LCOV - code coverage report
Current view: | top level - dynamics/se/dycore - derivative_mod.F90 (source / functions) |  |  | Hit | Total | Coverage
Test: | coverage.info |  | Lines: | 357 | 758 | 47.1 %
Date: | 2025-01-13 21:54:50 |  | Functions: | 18 | 41 | 43.9 %

              Line data    Source code
           1             : module derivative_mod
           2             :   use shr_kind_mod,       only: r8=>shr_kind_r8
           3             :   use cam_abortutils,     only: endrun
           4             :   use dimensions_mod, only : np, nc, npdg, nelemd, nlev
           5             :   use quadrature_mod, only : quadrature_t, gauss, gausslobatto,legendre, jacobi
           6             :   ! needed for spherical differential operators:
           7             :   use physconst, only: ra
           8             :   use element_mod, only : element_t
           9             :   use control_mod, only : hypervis_scaling, hypervis_power
          10             :   use perf_mod, only : t_startf, t_stopf
          11             :
          12             : implicit none
          13             : private
          14             :
          15             :   type, public :: derivative_t
          16             :      real (kind=r8) :: Dvv(np,np)
          17             :      real (kind=r8) :: Dvv_diag(np,np)
          18             :      real (kind=r8) :: Dvv_twt(np,np)
          19             :      real (kind=r8) :: Mvv_twt(np,np)  ! diagonal matrix of GLL weights
          20             :      real (kind=r8) :: Mfvm(np,nc+1)
          21             :      real (kind=r8) :: Cfvm(np,nc)
          22             :      real (kind=r8) :: legdg(np,np)
          23             :   end type derivative_t
          24             :
          25             :   type, public :: derivative_stag_t
          26             :      real (kind=r8) :: D(np,np)
          27             :      real (kind=r8) :: M(np,np)
          28             :      real (kind=r8) :: Dpv(np,np)
          29             :      real (kind=r8) :: D_twt(np,np)
          30             :      real (kind=r8) :: M_twt(np,np)
          31             :      real (kind=r8) :: M_t(np,np)
          32             :   end type derivative_stag_t
          33             :
          34             :   real (kind=r8), allocatable :: integration_matrix(:,:)
          35             :   real (kind=r8), allocatable :: integration_matrix_physgrid(:,:)
          36             :   real (kind=r8), allocatable :: boundary_interp_matrix(:,:,:)
          37             :
          38             : ! ======================================
          39             : ! Public Interfaces
          40             : ! ======================================
          41             :
          42             :   public :: subcell_integration
          43             :   public :: subcell_dss_fluxes
          44             :   public :: subcell_div_fluxes
          45             :   public :: subcell_Laplace_fluxes
          46             :   public :: allocate_subcell_integration_matrix_cslam    !for consistent se-cslam algorithm
          47             :   public :: allocate_subcell_integration_matrix_physgrid !for integration se basis functions over physgrid control volumes
          48             :
          49             :   public :: derivinit
          50             :
          51             :   public :: gradient
          52             :   public :: gradient_wk
          53             :   public :: vorticity
          54             :   public :: divergence
          55             :
          56             :   public :: interpolate_gll2fvm_corners
          57             :   public :: interpolate_gll2fvm_points
          58             :   public :: remap_phys2gll
          59             :
          60             :
          61             :   interface divergence
          62             :       module procedure divergence_nonstag
          63             :       module procedure divergence_stag
          64             :   end interface
          65             :
          66             :   interface gradient
          67             :       module procedure gradient_str_nonstag
          68             :       module procedure gradient_str_stag
          69             :   end interface
          70             :
          71             :   interface gradient_wk
          72             :       module procedure gradient_wk_nonstag
          73             :       module procedure gradient_wk_stag
          74             :   end interface
          75             :
          76             :   public :: v2pinit
          77             :
          78             :   private :: dmatinit
          79             :   private :: dvvinit
          80             :   private :: dpvinit
          81             :
          82             : ! these routines compute spherical differential operators as opposed to
          83             : ! the gnomonic coordinate operators above.  Vectors (input or output)
          84             : ! are always expressed in lat-lon coordinates
          85             : !
          86             : ! note that weak derivatives (integrated by parts form) can be defined using
          87             : ! contra or co-variant test functions, so
          88             : !
          89             :   public  :: gradient_sphere
          90             :   public  :: gradient_sphere_wk_testcov
          91             :   public  :: gradient_sphere_wk_testcontra   ! only used for debugging
          92             :   public  :: ugradv_sphere
          93             :   public  :: vorticity_sphere
          94             :   public  :: vorticity_sphere_diag
          95             :   public  :: divergence_sphere
          96             :   public  :: curl_sphere
          97             :   public  :: curl_sphere_wk_testcov
          98             :   public  :: divergence_sphere_wk
          99             :   public  :: laplace_sphere_wk
         100             :   public  :: vlaplace_sphere_wk
         101             :   public  :: vlaplace_sphere_wk_mol
         102             :   public  :: element_boundary_integral
         103             :   public  :: edge_flux_u_cg
         104             :   public  :: gll_to_dgmodal
         105             :   public  :: dgmodal_to_gll
         106             :
         107             :   public  :: limiter_optim_iter_full
         108             :
         109             : contains
         110             :
         111             : ! ==========================================
         112             : ! derivinit:
         113             : !
         114             : ! Initialize the matrices for taking
         115             : ! derivatives and interpolating
         116             : ! ==========================================
         117             :
         118        1536 :   subroutine derivinit(deriv,fvm_corners, fvm_points)
         119             :     type (derivative_t)      :: deriv
         120             :     real (kind=r8),optional :: fvm_corners(nc+1)
         121             :     real (kind=r8),optional :: fvm_points(nc)
         122             :
         123             :     ! Local variables
         124             :     type (quadrature_t) :: gp   ! Quadrature points and weights on pressure grid
         125             :
         126             :     real (kind=r8) :: dmat(np,np)
         127             :     real (kind=r8) :: dpv(np,np)
         128             :     real (kind=r8) :: v2p(np,np)
         129             :     real (kind=r8) :: p2v(np,np)
         130             :     real (kind=r8) :: dvv(np,np)
         131             :     real (kind=r8) :: dvv_diag(np,np)
         132             :     real (kind=r8) :: v2v(np,np)
         133             :     real (kind=r8) :: xnorm
         134             :     integer i,j
         135             :
         136             :     ! ============================================
         137             :     ! initialize matrices in r8 precision
         138             :     ! and transfer results into r8
         139             :     ! floating point precision
         140             :     ! ============================================
         141             :
         142        1536 :     gp=gausslobatto(np)
         143             :
         144             :     ! Legendre polynomials of degree npdg-1, on the np GLL grid:
         145        1536 :     if (npdg>np) call endrun( 'FATAL ERROR: npdg>np')
         146        1536 :     if (npdg>0 .and. npdg<np) then
         147             :        ! in this case, we use a DG basis of Legendre polynomials
         148             :        ! stored at the GLL points.
         149           0 :        do i=1,np
         150           0 :           deriv%legdg(1:npdg,i) = legendre(gp%points(i),npdg-1)
         151             :        end do
         152             :        ! normalize
         153           0 :        do j=1,npdg
         154           0 :           xnorm=sqrt(sum(deriv%legdg(j,:)*deriv%legdg(j,:)*gp%weights(:)))
         155           0 :           deriv%legdg(j,:)=deriv%legdg(j,:)/xnorm
         156             :        enddo
         157             :     endif
         158        1536 :     call dvvinit(dvv,gp)
         159       32256 :     deriv%Dvv(:,:)   = dvv(:,:)
         160             :
         161        7680 :     do i=1,np
         162       32256 :        do j=1,np
         163       30720 :           if (i.eq.j) then
         164        6144 :              deriv%dvv_diag(i,j)   = dvv(i,j)
         165             :           else
         166       18432 :              deriv%dvv_diag(i,j) = 0.0_r8
         167             :           endif
         168             :         end do
         169             :      end do
         170             :
         171        1536 :     v2v = 0.0_r8
         172        7680 :     do i=1,np
         173        7680 :        v2v(i,i) = gp%weights(i)
         174             :     end do
         175             :
         176        7680 :     do i=1,np
         177       32256 :        do j=1,np
         178       30720 :           dvv(j,i) = dvv(j,i)*gp%weights(i)
         179             :        end do
         180             :     end do
         181             :
         182       32256 :     deriv%Dvv_twt = TRANSPOSE(dvv)
         183       32256 :     deriv%Mvv_twt = v2v
         184        1536 :     if (present(fvm_corners)) &
         185        1536 :          call v2pinit(deriv%Mfvm,gp%points,fvm_corners,np,nc+1)
         186             :
         187        1536 :     if (present(fvm_points)) &
         188        1536 :          call v2pinit(deriv%Cfvm,gp%points,fvm_points,np,nc)
         189             :
         190             :     ! notice we deallocate this memory here even though it was allocated
         191             :     ! by the call to gausslobatto.
         192        1536 :     deallocate(gp%points)
         193        1536 :     deallocate(gp%weights)
         194             :
         195        1536 :   end subroutine derivinit
         196             :
         197             : ! =======================================
         198             : ! dmatinit:
         199             : !
         200             : ! Compute rectangular v->p
         201             : ! derivative matrix (dmat)
         202             : ! =======================================
         203             :
         204             :   subroutine dmatinit(dmat)
         205             :
         206             :     real (kind=r8) :: dmat(np,np)
         207             :
         208             :     ! Local variables
         209             :
         210             :     type (quadrature_t) :: gll
         211             :     type (quadrature_t) :: gs
         212             :
         213             :     integer i,j
         214             :     real(kind=r8)  fact,f1,f2
         215             :     real(kind=r8)  func0,func1
         216             :     real(kind=r8)  dis,c0,c1
         217             :
         218             :     real(kind=r8)  :: leg(np,np)
         219             :     real(kind=r8)  ::  jac(0:np-1)
         220             :     real(kind=r8)  :: djac(0:np-1)
         221             :
         222             :     c0 = 0.0_r8
         223             :     c1 = 1.0_r8
         224             :
         225             :     gll= gausslobatto(np)
         226             :     gs = gauss(np)
         227             :
         228             :     ! =============================================================
         229             :     ! Compute Legendre polynomials on Gauss-Lobatto grid (velocity)
         230             :     ! =============================================================
         231             :
         232             :     do i=1,np
         233             :        leg(:,i) = legendre(gll%points(i),np-1)
         234             :     end do
         235             :
         236             :     ! ================================================================
         237             :     !  Derivatives of velocity cardinal functions on pressure grid
         238             :     !  d(i,j) = D(j,i) = D' (D-transpose) since D(i,j) = dh_j(x_i)/dx
         239             :     ! ================================================================
         240             :
         241             :     fact = np*(np-1)
         242             :
         243             :     do j=1,np
         244             :        call jacobi(np-1,gs%points(j),c0,c0,jac(0:np-1),djac(0:np-1))
         245             :        func0 =  jac(np-1)
         246             :        func1 = djac(np-1)
         247             :        f1 = fact*func0
         248             :        f2 = (c1 - gs%points(j))*(c1 + gs%points(j)) * func1
         249             :        do i = 1, np
         250             :           if ( gs%points(j) /= gll%points(i) ) then
         251             :              dis = gs%points(j) - gll%points(i)
         252             :              dmat(i,j) = func0 / ( leg(np,i)*dis ) + f2 / (fact*leg(np,i)*dis*dis)
         253             :           else
         254             :              dmat(i,j) = c0
         255             :           endif
         256             :        end do
         257             :     end do
         258             :
         259             :     deallocate(gll%points)
         260             :     deallocate(gll%weights)
         261             :
         262             :     deallocate(gs%points)
         263             :     deallocate(gs%weights)
         264             :
         265             : end subroutine dmatinit
         266             :
         267             : ! =======================================
         268             : ! dpvinit:
         269             : !
         270             : ! Compute rectangular p->v
         271             : ! derivative matrix (dmat)
         272             : ! for strong gradients
         273             : ! =======================================
         274             :
         275             : subroutine dpvinit(dmat)
         276             :
         277             : real (kind=r8) :: dmat(np,np)
         278             :
         279             : ! Local variables
         280             :
         281             : type (quadrature_t) :: gll
         282             : type (quadrature_t) :: gs
         283             :
         284             : integer i,j
         285             : real(kind=r8)  dis,c0,c1
         286             :
         287             : real(kind=r8)  :: legv(0:np,np)
         288             : real(kind=r8)  :: dlegv(0:np,np)
         289             :
         290             : real(kind=r8)  :: leg(0:np)
         291             : real(kind=r8)  :: dleg(0:np)
         292             :
         293             : c0 = 0.0_r8
         294             : c1 = 1.0_r8
         295             :
         296             : gll= gausslobatto(np)
         297             : gs = gauss(np)
         298             :
         299             : ! =============================================================
         300             : ! Compute Legendre polynomials on Gauss-Lobatto grid (velocity)
         301             : ! =============================================================
         302             :
         303             : do i=1,np
         304             : call jacobi(np,gll%points(i),c0,c0,legv(0:np,i),dlegv(0:np,i))
         305             : end do
         306             :
         307             : ! ================================================================
         308             : !  Derivatives of velocity cardinal functions on pressure grid
         309             : !  d(i,j) = D(j,i) = D' (D-transpose) since D(i,j) = dh_j(x_i)/dx
         310             : ! ================================================================
         311             :
         312             :     do j=1,np
         313             :        call jacobi(np,gs%points(j),c0,c0,leg(0:np),dleg(0:np))
         314             :        do i = 1, np
         315             :           if ( gs%points(j) /= gll%points(i) ) then
         316             :              dis = gll%points(i) - gs%points(j)
         317             :              dmat(j,i) = dlegv(np,i)/( dleg(np)*dis ) -  legv(np,i)/ (dleg(np)*dis*dis)
         318             :           else
         319             :              dmat(j,i) = c0
         320             :           endif
         321             :        end do
         322             :     end do
         323             :
         324             :     deallocate(gll%points)
         325             :     deallocate(gll%weights)
         326             :
         327             :     deallocate(gs%points)
         328             :     deallocate(gs%weights)
         329             :
         330             :   end subroutine dpvinit
         331             :
         332             : ! =======================================
         333             : ! v2pinit:
         334             : ! Compute interpolation matrix from gll(1:n1) -> gs(1:n2)
         335             : ! =======================================
         336        3072 :   subroutine v2pinit(v2p,gll,gs,n1,n2)
         337             :     integer :: n1,n2
         338             :     real(kind=r8)  ::  v2p(n1,n2)
         339        6144 :     real(kind=r8)  ::  v2p_new(n1,n2)
         340             :     real(kind=r8)  ::  gll(n1),gs(n2)
         341             :     ! Local variables
         342             :
         343             :     integer i,j,k,m,l
         344             :     real(kind=r8)  fact,f1, sum
         345             :     real(kind=r8)  func0,func1
         346             :
         347        6144 :     real(kind=r8)  :: leg(n1,n1)
         348             :     real(kind=r8)  ::  jac(0:n1-1)
         349             :     real(kind=r8)  :: djac(0:n1-1)
         350             :     real(kind=r8)  :: c0,c1
         351             :
         352             :     type(quadrature_t) :: gll_pts
         353        6144 :     real(kind=r8)  :: leg_out(n1,n2)
         354        6144 :     real(kind=r8)  :: gamma(n1)
         355             :
         356        3072 :     c0 = 0.0_r8
         357        3072 :     c1 = 1.0_r8
         358             :
         359             :     ! ==============================================================
         360             :     ! Compute Legendre polynomials on Gauss-Lobatto grid (velocity)
         361             :     ! ==============================================================
         362             :
         363        3072 :     fact = -n1*(n1-1)
         364       15360 :     do i=1,n1
         365       12288 :        leg(:,i) = legendre(gll(i),n1-1)
         366       15360 :        leg(n1,i) = fact * leg(n1,i)
         367             :     end do
         368             :
         369             :     ! ===================================================
         370             :     !  Velocity cardinal functions on pressure grid
         371             :     ! ===================================================
         372             :     ! NEW VERSION, with no division by (gs(j)-gll(i)):
         373             :
         374             :     ! compute legendre polynomials at output points:
         375        3072 :     gll_pts = gausslobatto(n1)
         376             :
         377        3072 :     fact = -n1*(n1-1)
         378       13824 :     do i=1,n2
         379       10752 :        leg_out(:,i) = legendre(gs(i),n1-1)
         380       13824 :        leg_out(n1,i) = fact * leg_out(n1,i)
         381             :     end do
         382             :
         383             :
         384             :     ! compute gamma: (normalization factor for inv(leg)
         385       15360 :     do m=1,n1
         386       12288 :        gamma(m)=0
         387       61440 :        do i=1,n1
         388       61440 :           gamma(m)=gamma(m)+leg(m,i)*leg(m,i)*gll_pts%weights(i)
         389             :        enddo
         390       15360 :        gamma(m)=1/gamma(m)
         391             :     enddo
         392             :
         393             :     ! compute product of leg_out * inv(leg):
         394       13824 :     do j=1,n2   ! this should be fvm points
         395       56832 :        do l=1,n1   ! this should be GLL points
         396       43008 :           sum=0
         397      215040 :           do k=1,n1  ! number of polynomials = number of GLL points
         398      215040 :              sum=sum + leg_out(k,j)*gamma(k)*leg(k,l)
         399             :           enddo
         400       53760 :           v2p_new(l,j) = gll_pts%weights(l)*sum
         401             :        enddo
         402             :     enddo
         403        3072 :     deallocate(gll_pts%points)
         404        3072 :     deallocate(gll_pts%weights)
         405             :
         406       56832 :     v2p=v2p_new
         407        3072 :   end subroutine v2pinit
         408             :
         409             :
         410             :
         411             : ! =======================================
         412             : ! dvvinit:
         413             : !
         414             : ! Compute rectangular v->v
         415             : ! derivative matrix (dvv)
         416             : ! =======================================
         417             :
         418        1536 :   subroutine dvvinit(dvv,gll)
         419             :
         420             :     real(kind=r8)  ::  dvv(np,np)
         421             :     type (quadrature_t)   :: gll
         422             :
         423             :     ! Local variables
         424             :
         425             :     real(kind=r8)  :: leg(np,np)
         426             :     real(kind=r8)  :: c0,c1,c4
         427             :
         428             :     integer i,j
         429             :
         430        1536 :     c0 = 0.0_r8
         431        1536 :     c1 = 1.0_r8
         432        1536 :     c4 = 4.0_r8
         433             :
         434        7680 :     do i=1,np
         435        7680 :        leg(:,i) = legendre(gll%points(i),np-1)
         436             :     end do
         437             :
         438       32256 :     dvv(:,:) = c0
         439        7680 :     do j=1,np
         440       15360 :        do i=1,j-1
         441       15360 :           dvv(j,i) = (c1/(gll%points(i)-gll%points(j)))*leg(np,i)/leg(np,j)
         442             :        end do
         443        6144 :        dvv(j,j) = c0
         444       16896 :        do i=j+1,np
         445       15360 :           dvv(j,i) = (c1/(gll%points(i)-gll%points(j)))*leg(np,i)/leg(np,j)
         446             :        end do
         447             :     end do
         448             :
         449             :
         450        1536 :     dvv(np,np) = + np*(np-1)/c4
         451        1536 :     dvv(1,1)   = - np*(np-1)/c4
         452             :
         453        1536 :   end subroutine dvvinit
         454             :
         455             : !  ================================================
         456             : !  divergence_stag:
         457             : !
         458             : !  Compute divergence (maps v grid -> p grid)
         459             : !  ================================================
         460             :
         461           0 :   subroutine divergence_stag(v,deriv,div)
         462             :
         463             :     real(kind=r8), intent(in) :: v(np,np,2)
         464             :     type (derivative_stag_t), intent(in) :: deriv
         465             :     real(kind=r8), intent(out) :: div(np,np)
         466             :
         467             :     ! Local
         468             :
         469             :     integer i
         470             :     integer j
         471             :     integer l
         472             :
         473             :     real(kind=r8)  sumx00
         474             :     real(kind=r8)  sumy00
         475             :
         476             :     real(kind=r8) :: vtemp(np,np,2)
         477             :
         478             :
         479           0 :      do j=1,np
         480           0 :         do l=1,np
         481             :
         482             :            sumx00=0.0d0
         483             :            sumy00=0.0d0
         484             : !DIR$ UNROLL(NP)
         485           0 :            do i=1,np
         486           0 :               sumx00 = sumx00 + deriv%D(i,l  )*v(i,j  ,1)
         487           0 :               sumy00 = sumy00 + deriv%M(i,l  )*v(i,j  ,2)
         488             :            enddo
         489           0 :           vtemp(j  ,l  ,1) = sumx00
         490           0 :           vtemp(j  ,l  ,2) = sumy00
         491             :        enddo
         492             :     enddo
         493           0 :     do j=1,np
         494           0 :        do i=1,np
         495             :           sumx00=0.0d0
         496             :           sumy00=0.0d0
         497             : !DIR$ UNROLL(NP)
         498           0 :           do l=1,np
         499           0 :              sumx00 = sumx00 +  deriv%M(l,j  )*vtemp(l,i  ,1)
         500           0 :              sumy00 = sumy00 +  deriv%D(l,j  )*vtemp(l,i  ,2)
         501             :           enddo
         502           0 :           div(i  ,j  ) = sumx00 + sumy00
         503             :
         504             :        enddo
         505             :     enddo
         506             :
         507           0 :   end subroutine divergence_stag
         508             :
         509             : !  ================================================
         510             : !  divergence_nonstag:
         511             : !
         512             : !  Compute divergence (maps v->v)
         513             : !  ================================================
         514             :
         515           0 :   subroutine divergence_nonstag(v,deriv,div)
         516             :
         517             :     real(kind=r8), intent(in) :: v(np,np,2)
         518             :     type (derivative_t), intent(in) :: deriv
         519             :
         520             :     real(kind=r8), intent(out) :: div(np,np)
         521             :
         522             :     ! Local
         523             :
         524             :     integer i
         525             :     integer j
         526             :     integer l
         527             :
         528             :     real(kind=r8) ::  dudx00
         529             :     real(kind=r8) ::  dvdy00
         530             :
         531             :     real(kind=r8) ::  vvtemp(np,np)
         532             :
         533           0 :      do j=1,np
         534           0 :         do l=1,np
         535             :            dudx00=0.0d0
         536             :            dvdy00=0.0d0
         537             : !DIR$ UNROLL(NP)
         538           0 :            do i=1,np
         539           0 :               dudx00 = dudx00 + deriv%Dvv(i,l  )*v(i,j  ,1)
         540           0 :               dvdy00 = dvdy00 + deriv%Dvv(i,l  )*v(j  ,i,2)
         541             :            end do
         542             :
         543           0 :            div(l  ,j  ) = dudx00
         544           0 :            vvtemp(j  ,l  ) = dvdy00
         545             :         end do
         546             :     end do
         547           0 :     do j=1,np
         548           0 :        do i=1,np
         549           0 :           div(i,j)=div(i,j)+vvtemp(i,j)
         550             :        end do
         551             :     end do
         552             :
         553           0 :   end subroutine divergence_nonstag
         554             :
         555             : !  ================================================
         556             : !  gradient_wk_stag:
         557             : !
         558             : !  Compute the weak form gradient:
         559             : !  maps scalar field on the pressure grid to the
         560             : !  velocity grid
         561             : !  ================================================
         562             :
         563           0 :   function gradient_wk_stag(p,deriv) result(dp)
         564             :
         565             :     type (derivative_stag_t), intent(in) :: deriv
         566             :     real(kind=r8), intent(in) :: p(np,np)
         567             :
         568             :     real(kind=r8)             :: dp(np,np,2)
         569             :
         570             :     ! Local
         571             :
         572             :     integer i
         573             :     integer j
         574             :     integer l
         575             :
         576             :     real(kind=r8)  sumx00,sumx01
         577             :     real(kind=r8)  sumy00,sumy01
         578             :
         579             :     real(kind=r8)  :: vtempt(np,np,2)
         580             :
         581           0 :     do j=1,np
         582           0 :        do l=1,np
         583             :           sumx00=0.0d0
         584             :           sumy00=0.0d0
         585             : !DIR$ UNROLL(NP)
         586           0 :            do i=1,np
         587           0 :               sumx00 = sumx00 + deriv%D_twt(i,l  )*p(i,j  )
         588           0 :               sumy00 = sumy00 + deriv%M_twt(i,l  )*p(i,j  )
         589             :            enddo
         590           0 :            vtempt(j  ,l  ,1) = sumx00
         591           0 :            vtempt(j  ,l  ,2) = sumy00
         592             :         enddo
         593             :     enddo
         594           0 :     do j=1,np
         595           0 :        do i=1,np
         596             :           sumx00=0.0d0
         597             :           sumy00=0.0d0
         598             : !DIR$ UNROLL(NP)
         599           0 :           do l=1,np
         600           0 :              sumx00 = sumx00 +  deriv%M_twt(l,j  )*vtempt(l,i  ,1)
         601           0 :              sumy00 = sumy00 +  deriv%D_twt(l,j  )*vtempt(l,i  ,2)
         602             :           enddo
         603           0 :           dp(i  ,j  ,1) = sumx00
         604           0 :           dp(i  ,j  ,2) = sumy00
         605             :       enddo
         606             :     enddo
         607             :
         608             :
         609           0 :   end function gradient_wk_stag
         610             :
         611             : !  ================================================
         612             : !  gradient_wk_nonstag:
         613             : !
         614             : !  Compute the weak form gradient:
         615             : !  maps scalar field on the Gauss-Lobatto grid to the
         616             : !  weak gradient on the Gauss-Lobbatto grid
         617             : !  ================================================
         618             :
         619           0 :   function gradient_wk_nonstag(p,deriv) result(dp)
         620             :
         621             :     type (derivative_t), intent(in) :: deriv
         622             :     real(kind=r8), intent(in) :: p(np,np)
         623             :
         624             :     real(kind=r8)             :: dp(np,np,2)
         625             :
         626             :     ! Local
         627             :
         628             :     integer i
         629             :     integer j
         630             :     integer l
         631             :
         632             :     real(kind=r8)  sumx00
         633             :     real(kind=r8)  sumy00
         634             :
         635             :     real(kind=r8)  :: vvtempt(np,np,2)
         636             :
         637           0 :        do j=1,np
         638           0 :           do l=1,np
         639             :              sumx00=0.0d0
         640             :              sumy00=0.0d0
         641             : !DIR$ UNROLL(NP)
         642           0 :              do i=1,np
         643           0 :                 sumx00 = sumx00 + deriv%Dvv_twt(i,l  )*p(i,j  )
         644           0 :                 sumy00 = sumy00 + deriv%Mvv_twt(i,l  )*p(i,j  )
         645             :              end do
         646           0 :              vvtempt(j  ,l  ,1) = sumx00
         647           0 :              vvtempt(j  ,l  ,2) = sumy00
         648             :           end do
         649             :        end do
         650             :
         651           0 :        do j=1,np
         652           0 :           do i=1,np
         653             :              sumx00=0.0d0
         654             :              sumy00=0.0d0
         655             : !DIR$ UNROLL(NP)
         656           0 :              do l=1,np
         657           0 :                 sumx00 = sumx00 +  deriv%Mvv_twt(l,j  )*vvtempt(l,i  ,1)
         658           0 :                 sumy00 = sumy00 +  deriv%Dvv_twt(l,j  )*vvtempt(l,i  ,2)
         659             :              end do
         660           0 :              dp(i  ,j  ,1) = sumx00
         661           0 :              dp(i  ,j  ,2) = sumy00
         662             :           end do
         663             :        end do
         664           0 :   end function gradient_wk_nonstag
         665             :
         666             : !  ================================================
         667             : !  gradient_str_stag:
         668             : !
         669             : !  Compute the *strong* form gradient:
         670             : !  maps scalar field on the pressure grid to the
         671             : !  velocity grid
         672             : !  ================================================
         673             :
         674           0 :   subroutine  gradient_str_stag(p,deriv,dp)
         675             :
         676             :     type (derivative_stag_t), intent(in) :: deriv
         677             :     real(kind=r8), intent(in) :: p(np,np)
         678             :
         679             :     real(kind=r8), intent(out) :: dp(np,np,2)
         680             :
         681             :     ! Local
         682             :
         683             :     integer i
         684             :     integer j
         685             :     integer l
         686             :
         687             :     real(kind=r8)  sumx00
         688             :     real(kind=r8)  sumy00
         689             :
         690             :     real(kind=r8)  :: vtempt(np,np,2)
         691           0 :     do j=1,np
         692           0 :        do l=1,np
         693             :           sumx00=0.0d0
         694             :           sumy00=0.0d0
         695             : !DIR$ UNROLL(NP)
         696           0 :           do i=1,np
         697           0 :              sumx00 = sumx00 + deriv%Dpv(i,l  )*p(i,j  )
         698           0 :              sumy00 = sumy00 + deriv%M_t(i,l  )*p(i,j  )
         699             :           enddo
         700           0 :           vtempt(j  ,l  ,1) = sumx00
         701           0 :           vtempt(j  ,l  ,2) = sumy00
         702             :        enddo
         703             :     enddo
         704           0 :     do j=1,np
         705           0 :        do i=1,np
         706             :           sumx00=0.0d0
         707             :           sumy00=0.0d0
         708             : !DIR$ UNROLL(NP)
         709           0 :           do l=1,np
         710           0 :              sumx00 = sumx00 +  deriv%M_t(l,j  )*vtempt(l,i  ,1)
         711           0 :              sumy00 = sumy00 +  deriv%Dpv(l,j  )*vtempt(l,i  ,2)
         712             :           enddo
         713           0 :           dp(i  ,j  ,1) = sumx00
         714           0 :           dp(i  ,j  ,2) = sumy00
         715             :        enddo
         716             :     enddo
         717             :
         718           0 :   end subroutine gradient_str_stag
         719             :
         720             : !  ================================================
         721             : !  gradient_str_nonstag:
         722             : !
         723             : !  Compute the *strong* gradient on the velocity grid
         724             : !  of a scalar field on the velocity grid
         725             : !  ================================================
         726             :
         727           0 :   subroutine gradient_str_nonstag(s,deriv,ds)
         728             :
         729             :     type (derivative_t), intent(in) :: deriv
         730             :     real(kind=r8), intent(in) :: s(np,np)
         731             :     real(kind=r8), intent(out) :: ds(np,np,2)
         732             :
         733             :     integer i
         734             :     integer j
         735             :     integer l
         736             :     real(kind=r8) ::  dsdx00,dsdx01
         737             :     real(kind=r8) ::  dsdy00,dsdy01
         738           0 :        do j=1,np
         739           0 :           do l=1,np
         740             :              dsdx00=0.0d0
         741             :              dsdy00=0.0d0
         742             : !DIR$ UNROLL(NP)
         743           0 :              do i=1,np
         744           0 :                 dsdx00 = dsdx00 + deriv%Dvv(i,l  )*s(i,j  )
         745           0 :                 dsdy00 = dsdy00 + deriv%Dvv(i,l  )*s(j  ,i)
         746             :              end do
         747           0 :              ds(l  ,j  ,1) = dsdx00
         748           0 :              ds(j  ,l  ,2) = dsdy00
         749             :           end do
         750             :        end do
         751           0 :   end subroutine  gradient_str_nonstag
         752             :
         753             : !  ================================================
         754             : !  vorticity:
         755             : !
         756             : !  Compute the vorticity of the velocity field on the
         757             : !  velocity grid
         758             : !  ================================================
         759             :
         760           0 :   subroutine vorticity(v,deriv,vort)
         761             :
         762             :     type (derivative_t), intent(in) :: deriv
         763             :     real(kind=r8), intent(in) :: v(np,np,2)
         764             :
         765             :     real(kind=r8), intent(out) :: vort(np,np)
         766             :
         767             :     integer i
         768             :     integer j
         769             :     integer l
         770             :
         771             :     real(kind=r8) ::  dvdx00,dvdx01
         772             :     real(kind=r8) ::  dudy00,dudy01
         773             :
         774             :     real(kind=r8)  :: vvtemp(np,np)
         775           0 :     do j=1,np
         776           0 :        do l=1,np
         777             :           dudy00=0.0d0
         778             :           dvdx00=0.0d0
         779             : !DIR$ UNROLL(NP)
         780           0 :           do i=1,np
         781           0 :              dvdx00 = dvdx00 + deriv%Dvv(i,l  )*v(i,j  ,2)
         782           0 :              dudy00 = dudy00 + deriv%Dvv(i,l  )*v(j  ,i,1)
         783             :           enddo
         784           0 :           vort(l  ,j  ) = dvdx00
         785           0 :           vvtemp(j  ,l  ) = dudy00
         786             :        enddo
         787             :     enddo
         788           0 :     do j=1,np
         789           0 :        do i=1,np
         790           0 :           vort(i,j)=vort(i,j)-vvtemp(i,j)
         791             :        end do
         792             :     end do
         793             :
         794           0 :   end subroutine vorticity
         795             :
         796             : !  ================================================
         797             : !  interpolate_gll2fvm_points:
         798             : !
         799             : !  shape funtion interpolation from data on GLL grid to cellcenters on physics grid
         800             : !  Author: Christoph Erath
         801             : !  ================================================
         802           0 :   function interpolate_gll2fvm_points(v,deriv) result(p)
         803             :
         804             :     real(kind=r8), intent(in) :: v(np,np)
         805             :     type (derivative_t)         :: deriv
         806             :     real(kind=r8) :: p(nc,nc)
         807             :
         808             :     ! Local
         809             :     integer i
         810             :     integer j
         811             :     integer l
         812             :
         813             :     real(kind=r8)  sumx00,sumx01
         814             :     real(kind=r8)  sumx10,sumx11
         815             :     real(kind=r8)  vtemp(np,nc)
         816             :
         817           0 :     do j=1,np
         818           0 :        do l=1,nc
         819             :           sumx00=0.0d0
         820             : !DIR$ UNROLL(NP)
         821           0 :           do i=1,np
         822           0 :              sumx00 = sumx00 + deriv%Cfvm(i,l  )*v(i,j  )
         823             :           enddo
         824           0 :           vtemp(j  ,l) = sumx00
         825             :         enddo
         826             :     enddo
         827           0 :     do j=1,nc
         828           0 :        do i=1,nc
         829             :           sumx00=0.0d0
         830             : !DIR$ UNROLL(NP)
         831           0 :           do l=1,np
         832           0 :              sumx00 = sumx00 + deriv%Cfvm(l,j  )*vtemp(l,i)
         833             :           enddo
         834           0 :           p(i  ,j  ) = sumx00
         835             :        enddo
         836             :     enddo
         837           0 :   end function interpolate_gll2fvm_points
         838             : !  ================================================
         839             : !  interpolate_gll2fvm_corners:
         840             : !
         841             : !  shape funtion interpolation from data on GLL grid to physics grid
         842             : !
         843             : !  ================================================
         844           0 :   function interpolate_gll2fvm_corners(v,deriv) result(p)
         845             :
         846             :     real(kind=r8), intent(in) :: v(np,np)
         847             :     type (derivative_t), intent(in) :: deriv
         848             :     real(kind=r8) :: p(nc+1,nc+1)
         849             :
         850             :     ! Local
         851             :     integer i
         852             :     integer j
         853             :     integer l
         854             :
         855             :     real(kind=r8)  sumx00,sumx01
         856             :     real(kind=r8)  sumx10,sumx11
         857             :     real(kind=r8)  vtemp(np,nc+1)
         858             :
         859           0 :     do j=1,np
         860           0 :        do l=1,nc+1
         861             :           sumx00=0.0d0
         862             : !DIR$ UNROLL(NP)
         863           0 :           do i=1,np
         864           0 :              sumx00 = sumx00 + deriv%Mfvm(i,l  )*v(i,j  )
         865             :           enddo
         866           0 :           vtemp(j  ,l) = sumx00
         867             :         enddo
         868             :     enddo
         869           0 :     do j=1,nc+1
         870           0 :        do i=1,nc+1
         871             :           sumx00=0.0d0
         872             : !DIR$ UNROLL(NP)
         873           0 :           do l=1,np
         874           0 :              sumx00 = sumx00 + deriv%Mfvm(l,j  )*vtemp(l,i)
         875             :           enddo
         876           0 :           p(i  ,j  ) = sumx00
         877             :        enddo
         878             :     enddo
         879           0 :   end function interpolate_gll2fvm_corners
         880             :
         881             :
         882             : !  ================================================
         883             : !  remap_phys2gll:
         884             : !
         885             : !  interpolate to an equally spaced (in reference element coordinate system)
         886             : !  "physics" grid to the GLL grid
         887             : !
         888             : !  1st order, monotone, conservative
         889             : !  MT initial version 2013
         890             : !  ================================================
         891           0 :   function remap_phys2gll(pin,nphys) result(pout)
         892             :     integer :: nphys
         893             :     real(kind=r8), intent(in) :: pin(nphys*nphys)
         894             :     real(kind=r8) :: pout(np,np)
         895             :
         896             :     ! Local
         897             :     integer, save  :: nphys_init=0
         898             :     integer, save  :: nintersect
         899             :     real(kind=r8),save,pointer :: acell(:)  ! arrivial cell index of i'th intersection
         900             :     real(kind=r8),save,pointer :: dcell(:)  ! departure cell index of i'th intersection
         901             :     real(kind=r8),save,pointer :: delta(:)  ! length of i'th intersection
         902             :     real(kind=r8),save,pointer :: delta_a(:)  ! length of arrival cells
         903             :     integer in_i,in_j,ia,ja,id,jd,count,i,j
         904             :     logical :: found
         905             :
         906             :     real(kind=r8) :: tol = 1.0e-13_r8
         907             :     real(kind=r8) :: weight,x1,x2,dx
         908           0 :     real(kind=r8) :: gll_edges(np+1),phys_edges(nphys+1)
         909             :     type(quadrature_t) :: gll_pts
         910           0 :     if (nphys_init/=nphys) then
         911             :        ! setup (most be done on masterthread only) since all data is static
         912             :        ! MT: move barrier inside if loop - we dont want a barrier every regular call
         913             : !OMP BARRIER
         914             : !OMP MASTER
         915           0 :        nphys_init=nphys
         916             :        ! find number of intersections
         917           0 :        nintersect = np+nphys-1  ! max number of possible intersections
         918           0 :        allocate(acell(nintersect))
         919           0 :        allocate(dcell(nintersect))
         920           0 :        allocate(delta(nintersect))
         921           0 :        allocate(delta_a(np))
         922             :
         923             :        ! compute phys grid cell edges on [-1,1]
         924           0 :        do i=1,nphys+1
         925           0 :           dx = 2d0/nphys
         926           0 :           phys_edges(i)=-1 + (i-1)*dx
         927             :        enddo
         928             :
         929             :        ! compute GLL cell edges on [-1,1]
         930           0 :        gll_pts = gausslobatto(np)
         931           0 :        gll_edges(1)=-1
         932           0 :        do i=2,np
         933           0 :           gll_edges(i) = gll_edges(i-1) + gll_pts%weights(i-1)
         934             :        enddo
         935           0 :        gll_edges(np+1)=1
         936           0 :        delta_a=gll_pts%weights
         937           0 :        deallocate(gll_pts%points)
         938           0 :        deallocate(gll_pts%weights)
         939             :
         940           0 :        count=0
         941           0 :        x1=-1
         942           0 :        do while ( abs(x1-1) > tol )
         943             :           ! find point x2 closet to x1 and x2>x1:
         944           0 :           x2 = 1.1_r8
         945           0 :           do ia=2,np+1
         946           0 :              if (gll_edges(ia)>x1) then
         947           0 :                 if ( ( gll_edges(ia)-x1) < (x2-x1) ) then
         948           0 :                    x2=gll_edges(ia)
         949             :                 endif
         950             :              endif
         951             :           enddo
         952           0 :           do id=2,nphys+1
         953           0 :              if (phys_edges(id)>x1) then
         954           0 :                 if ( ( phys_edges(id)-x1) < (x2-x1) ) then
         955           0 :                    x2=phys_edges(id)
         956             :                 endif
         957             :              endif
         958             :           enddo
         959           0 :           print *,'x2=',x2
         960           0 :           if (x2>1+tol) call endrun('ERROR: did not find next intersection point')
         961           0 :           if (x2<=x1) call endrun('ERROR: next intersection point did not advance')
         962           0 :           count=count+1
         963           0 :           if (count>nintersect) call endrun('ERROR: search failuer: nintersect was too small')
         964           0 :           delta(count)=x2-x1
         965             :
         966           0 :           found=.false.
         967           0 :           do ia=1,np
         968           0 :              if (gll_edges(ia) <= x1+tol  .and.  x2-tol <= gll_edges(ia+1)) then
         969           0 :                 found=.true.
         970           0 :                 acell(count)=ia
         971             :              endif
         972             :           enddo
         973           0 :           if (.not. found) call endrun('ERROR: interval search problem')
         974             :
         975           0 :           found=.false.
         976           0 :           do id=1,nphys
         977           0 :              if (phys_edges(id) <= x1+tol .and.  x2-tol <= phys_edges(id+1)) then
         978           0 :                 found=.true.
         979           0 :                 dcell(count)=id
         980             :              endif
         981             :           enddo
         982           0 :           if (.not. found) call endrun('ERROR: interval search problem')
         983           0 :           x1=x2
         984             :        enddo
         985             :        ! reset to actual number of intersections
         986           0 :        nintersect=count
         987             : !OMP END MASTER
         988             : !OMP BARRIER
         989             :     endif
         990             :
         991           0 :     pout=0
         992           0 :     do in_i = 1,nintersect
         993           0 :        do in_j = 1,nintersect
         994           0 :           ia = acell(in_i)
         995           0 :           ja = acell(in_j)
         996           0 :           id = dcell(in_i)
         997           0 :           jd = dcell(in_j)
         998             :           ! mass in intersection region:  value*area_intersect
         999             :           ! value_arrival = value*area_intersect/area_arrival
        1000           0 :           weight = (  delta(in_i)*delta(in_j) ) / ( delta_a(ia)*delta_a(ja))
        1001             :           ! accumulate contribution from each intersection region:
        1002           0 :           pout(ia,ja) = pout(ia,ja) + weight*pin(id+(jd-1)*nphys)
        1003             :        enddo
        1004             :     enddo
        1005             :
        1006           0 :     end function remap_phys2gll
        1007             :
        1008             : !----------------------------------------------------------------
        1009             :
        1010             : !DIR$ ATTRIBUTES FORCEINLINE :: gradient_sphere
        1011 16277274000 :   subroutine gradient_sphere(s,deriv,Dinv,ds)
        1012             : !
        1013             : !   input s:  scalar
        1014             : !   output  ds: spherical gradient of s, lat-lon coordinates
        1015             : !
        1016             :
        1017             :     type (derivative_t), intent(in) :: deriv
        1018             :     real(kind=r8), intent(in), dimension(np,np,2,2) :: Dinv
        1019             :     real(kind=r8), intent(in) :: s(np,np)
        1020             :     real(kind=r8), intent(out) :: ds(np,np,2)
        1021             :
        1022             :     integer i
        1023             :     integer j
        1024             :     integer l
        1025             :
        1026             :     real(kind=r8) ::  dsdx00, dsdy00
        1027             :     real(kind=r8) ::  v1(np,np),v2(np,np)
        1028             :
        1029 81386370000 :     do j=1,np
        1030 >34182*10^7 :        do l=1,np
        1031             :           dsdx00=0.0d0
        1032             :           dsdy00=0.0d0
        1033             : !DIR$ UNROLL(NP)
        1034 >13021*10^8 :           do i=1,np
        1035 >10417*10^8 :              dsdx00 = dsdx00 + deriv%Dvv(i,l  )*s(i,j  )
        1036 >13021*10^8 :              dsdy00 = dsdy00 + deriv%Dvv(i,l  )*s(j  ,i)
        1037             :           end do
        1038 >26043*10^7 :           v1(l  ,j  ) = dsdx00*ra
        1039 >32554*10^7 :           v2(j  ,l  ) = dsdy00*ra
        1040             :        end do
        1041             :     end do
        1042             :     ! convert covarient to latlon
        1043 81386370000 :     do j=1,np
        1044 >34182*10^7 :        do i=1,np
        1045 >26043*10^7 :           ds(i,j,1)=Dinv(i,j,1,1)*v1(i,j) + Dinv(i,j,2,1)*v2(i,j)
        1046 >32554*10^7 :           ds(i,j,2)=Dinv(i,j,1,2)*v1(i,j) + Dinv(i,j,2,2)*v2(i,j)
        1047             :        enddo
        1048             :     enddo
        1049             :
        1050 16277274000 :     end subroutine gradient_sphere
        1051             :
        1052             :
        1053  2477919600 :   function curl_sphere_wk_testcov(s,deriv,elem) result(ds)
        1054             : !
        1055             : !   integrated-by-parts gradient, w.r.t. COVARIANT test functions
        1056             : !   input s:  scalar  (assumed to be s*khat)
        1057             : !   output  ds: weak curl, lat/lon coordinates
        1058             : !
        1059             : ! starting with:
        1060             : !   PHIcov1 = (PHI,0)  covariant vector
        1061             : !   PHIcov2 = (0,PHI)  covariant vector
        1062             : !
        1063             : !   ds1 = integral[ PHIcov1 dot curl(s*khat) ]
        1064             : !   ds2 = integral[ PHIcov2 dot curl(s*khat) ]
        1065             : ! integrate by parts:
        1066             : !   ds1 = integral[ vor(PHIcov1) * s ]
        1067             : !   ds2 = integral[ vor(PHIcov1) * s ]
        1068             : !
        1069             : !     PHIcov1 = (PHI^mn,0)
        1070             : !     PHIcov2 = (0,PHI^mn)
        1071             : !  vorticity() acts on covariant vectors:
        1072             : !   ds1 = sum wij g  s_ij 1/g (  (PHIcov1_2)_x  - (PHIcov1_1)_y )
        1073             : !       = -sum wij s_ij  d/dy (PHI^mn )
        1074             : ! for d/dy component, only sum over i=m
        1075             : !       = -sum  w_mj s_mj   d( PHI^n)(j)
        1076             : !           j
        1077             : !
        1078             : !   ds2 = sum wij g  s_ij 1/g (  (PHIcov2_2)_x  - (PHIcov2_1)_y )
        1079             : !       = +sum wij s_ij  d/dx (PHI^mn )
        1080             : ! for d/dx component, only sum over j=n
        1081             : !       = +sum  w_in s_in  d( PHI^m)(i)
        1082             : !           i
        1083             : !
        1084             :     type (derivative_t), intent(in) :: deriv
        1085             :     type (element_t), intent(in) :: elem
        1086             :     real(kind=r8), intent(in) :: s(np,np)
        1087             :
        1088             :     real(kind=r8) :: ds(np,np,2)
        1089             :
        1090             :     integer i,j,l,m,n
        1091             :     real(kind=r8) ::  dscontra(np,np,2)
        1092             :
        1093  2477919600 :     dscontra=0
        1094 12389598000 :     do n=1,np
        1095 52036311600 :        do m=1,np
        1096             : !DIR$ UNROLL(NP)
        1097 >20814*10^7 :           do j=1,np
        1098             :              ! phi(n)_y  sum over second index, 1st index fixed at m
        1099 >15858*10^7 :              dscontra(m,n,1)=dscontra(m,n,1)-(elem%mp(m,j)*s(m,j)*deriv%Dvv(n,j) )*ra
        1100             :              ! phi(m)_x  sum over first index, second index fixed at n
        1101 >19823*10^7 :              dscontra(m,n,2)=dscontra(m,n,2)+(elem%mp(j,n)*s(j,n)*deriv%Dvv(m,j) )*ra
        1102             :           enddo
        1103             :        enddo
        1104             :     enddo
        1105             :
        1106             :     ! convert contra -> latlon
        1107 12389598000 :     do j=1,np
        1108 52036311600 :        do i=1,np
        1109 39646713600 :           ds(i,j,1)=(elem%D(i,j,1,1)*dscontra(i,j,1) + elem%D(i,j,1,2)*dscontra(i,j,2))
        1110 49558392000 :           ds(i,j,2)=(elem%D(i,j,2,1)*dscontra(i,j,1) + elem%D(i,j,2,2)*dscontra(i,j,2))
        1111             :        enddo
        1112             :     enddo
        1113  2477919600 :     end function curl_sphere_wk_testcov
        1114             :
        1115             :
        1116  2477919600 :   function gradient_sphere_wk_testcov(s,deriv,elem) result(ds)
        1117             : !
        1118             : !   integrated-by-parts gradient, w.r.t. COVARIANT test functions
        1119             : !   input s:  scalar
        1120             : !   output  ds: weak gradient, lat/lon coordinates
        1121             : !   ds = - integral[ div(PHIcov) s ]
        1122             : !
        1123             : !     PHIcov1 = (PHI^mn,0)
        1124             : !     PHIcov2 = (0,PHI^mn)
        1125             : !   div() acts on contra components, so convert test function to contra:
        1126             : !     PHIcontra1 =  metinv PHIcov1  = (a^mn,b^mn)*PHI^mn
        1127             : !                                     a = metinv(1,1)  b=metinv(2,1)
        1128             : !
        1129             : !   ds1 = sum wij g  s_ij 1/g ( g a PHI^mn)_x  + ( g b PHI^mn)_y )
        1130             : !       = sum  wij s_ij  ag(m,n)  d/dx( PHI^mn ) + bg(m,n) d/dy( PHI^mn)
        1131             : !          i,j
        1132             : ! for d/dx component, only sum over j=n
        1133             : !       = sum  w_in s_in  ag(m,n)  d( PHI^m)(i)
        1134             : !          i
        1135             : ! for d/dy component, only sum over i=m
        1136             : !       = sum  w_mj s_mj  bg(m,n)  d( PHI^n)(j)
        1137             : !          j
        1138             : !
        1139             : !
        1140             : ! This formula is identical to gradient_sphere_wk_testcontra, except that
        1141             : !    g(m,n) is replaced by a(m,n)*g(m,n)
        1142             : !  and we have two terms for each componet of ds
        1143             : !
        1144             : !
        1145             :     type (derivative_t), intent(in) :: deriv
        1146             :     type (element_t), intent(in) :: elem
        1147             :     real(kind=r8), intent(in) :: s(np,np)
        1148             :
        1149             :     real(kind=r8) :: ds(np,np,2)
        1150             :
        1151             :     integer i,j,l,m,n
        1152             :     real(kind=r8) ::  dscontra(np,np,2)
        1153             :
        1154             :
        1155  2477919600 :     dscontra=0
        1156 12389598000 :     do n=1,np
        1157 52036311600 :        do m=1,np
        1158             : !DIR$ UNROLL(NP)
        1159 >20814*10^7 :           do j=1,np
        1160 >31717*10^7 :              dscontra(m,n,1)=dscontra(m,n,1)-(&
        1161 >15858*10^7 :                   (elem%mp(j,n)*elem%metinv(m,n,1,1)*elem%metdet(m,n)*s(j,n)*deriv%Dvv(m,j) ) +&
        1162             :                   (elem%mp(m,j)*elem%metinv(m,n,2,1)*elem%metdet(m,n)*s(m,j)*deriv%Dvv(n,j) ) &
        1163 >47576*10^7 :                   ) *ra
        1164             :
        1165             :              dscontra(m,n,2)=dscontra(m,n,2)-(&
        1166             :                   (elem%mp(j,n)*elem%metinv(m,n,1,2)*elem%metdet(m,n)*s(j,n)*deriv%Dvv(m,j) ) +&
        1167             :                   (elem%mp(m,j)*elem%metinv(m,n,2,2)*elem%metdet(m,n)*s(m,j)*deriv%Dvv(n,j) ) &
        1168 >19823*10^7 :                   ) *ra
        1169             :           enddo
        1170             :        enddo
        1171             :     enddo
        1172             :     ! convert contra -> latlon
        1173 12389598000 :     do j=1,np
        1174 52036311600 :        do i=1,np
        1175 39646713600 :           ds(i,j,1)=(elem%D(i,j,1,1)*dscontra(i,j,1) + elem%D(i,j,1,2)*dscontra(i,j,2))
        1176 49558392000 :           ds(i,j,2)=(elem%D(i,j,2,1)*dscontra(i,j,1) + elem%D(i,j,2,2)*dscontra(i,j,2))
        1177             :        enddo
        1178             :     enddo
        1179             :
        1180  2477919600 :     end function gradient_sphere_wk_testcov
        1181             :
        1182             :
        1183           0 :   function gradient_sphere_wk_testcontra(s,deriv,elem) result(ds)
        1184             : !
        1185             : !   integrated-by-parts gradient, w.r.t. CONTRA test functions
        1186             : !   input s:  scalar
        1187             : !   output  ds: weak gradient, lat/lon coordinates
        1188             : !
        1189             : !   integral[ div(phivec) s ] = sum  spheremp()* divergence_sphere(phivec) *s
        1190             : !   ds1 = above formual with phivec=(PHI,0) in CONTRA coordinates
        1191             : !   ds2 = above formual with phivec=(0,PHI) in CONTRA coordinates
        1192             : !
        1193             : ! PHI = (phi,0)
        1194             : !   s1 =  sum w_ij s_ij g_ij 1/g_ij ( g_ij PHI^mn )x
        1195             : !      =  sum w_ij s_ij g_mn dx(PHI^mn)_ij
        1196             : !         ij
        1197             : ! because x derivative is zero for j<>n, only have to sum over j=n
        1198             : !   s1(m,n)  =  sum w_i,n g_mn dx(PHI^m)_i,n s_i,n
        1199             : !                i
        1200             : !
        1201             :     type (derivative_t), intent(in) :: deriv
        1202             :     type (element_t), intent(in) :: elem
        1203             :     real(kind=r8), intent(in) :: s(np,np)
        1204             :
        1205             :     real(kind=r8) :: ds(np,np,2)
        1206             :
        1207             :     integer i,j,l,m,n
        1208             :     real(kind=r8) ::  dscov(np,np,2)
        1209             :
        1210           0 :     dscov=0
        1211           0 :     do n=1,np
        1212           0 :        do m=1,np
        1213             : !DIR$ UNROLL(NP)
        1214           0 :           do j=1,np
        1215             :              ! phi(m)_x  sum over first index, second index fixed at n
        1216           0 :              dscov(m,n,1)=dscov(m,n,1)-(elem%mp(j,n)*elem%metdet(m,n)*s(j,n)*deriv%Dvv(m,j) )*ra
        1217             :              ! phi(n)_y  sum over second index, 1st index fixed at m
        1218           0 :              dscov(m,n,2)=dscov(m,n,2)-(elem%mp(m,j)*elem%metdet(m,n)*s(m,j)*deriv%Dvv(n,j) )*ra
        1219             :           enddo
        1220             :        enddo
        1221             :     enddo
        1222             :
        1223             :     ! convert covariant -> latlon
        1224           0 :     ds(:,:,1)=elem%Dinv(:,:,1,1)*dscov(:,:,1) + elem%Dinv(:,:,2,1)*dscov(:,:,2)
        1225           0 :     ds(:,:,2)=elem%Dinv(:,:,1,2)*dscov(:,:,1) + elem%Dinv(:,:,2,2)*dscov(:,:,2)
        1226             :
        1227           0 :     end function gradient_sphere_wk_testcontra
        1228             :
        1229           0 :   function ugradv_sphere(u,v,deriv,elem) result(ugradv)
        1230             : !
        1231             : !   input:  vectors u and v  (latlon coordinates)
        1232             : !   output: vector  [ u dot grad ] v  (latlon coordinates)
        1233             : !
        1234             :     type (derivative_t), intent(in) :: deriv
        1235             :     type (element_t), intent(in) :: elem
        1236             :     real(kind=r8), intent(in) :: u(np,np,2)
        1237             :     real(kind=r8), intent(in) :: v(np,np,2)
        1238             :
        1239             :     real(kind=r8) :: ugradv(np,np,2)
        1240             :     real(kind=r8) :: dum_cart(np,np,3)
        1241             :     real(kind=r8) :: temp(np,np,2)
        1242             :
        1243             :     integer :: component
        1244             :
        1245             :     ! latlon -> cartesian
        1246           0 :     do component=1,3
        1247             :        ! Summing along the third dimension is a sum over components for each point.
        1248             :        ! (This is just a faster way of doing a dot product for each grid point,
        1249             :        ! since reindexing the inputs to use the intrinsic effectively would be
        1250             :        ! just asking for trouble.)
        1251           0 :        dum_cart(:,:,component)=sum( elem%vec_sphere2cart(:,:,component,:)*v(:,:,:) ,3)
        1252             :     end do
        1253             :
        1254             :     ! Do ugradv on the cartesian components.
        1255           0 :     do component=1,3
        1256             :        ! Dot u with the gradient of each component
        1257           0 :        call gradient_sphere(dum_cart(:,:,component),deriv,elem%Dinv,temp)
        1258           0 :        dum_cart(:,:,component) = sum( u(:,:,:) * temp,3)
        1259             :     enddo
        1260             :
        1261             :     ! cartesian -> latlon
        1262           0 :     do component=1,2
        1263             :        ! vec_sphere2cart is its own pseudoinverse.
        1264           0 :        ugradv(:,:,component) = sum(dum_cart(:,:,:)*elem%vec_sphere2cart(:,:,:,component), 3)
        1265             :     end do
        1266             :
        1267           0 :   end function ugradv_sphere
        1268             :
        1269             :
        1270             :
        1271           0 :   function curl_sphere(s,deriv,elem) result(ds)
        1272             : !
        1273             : !   input s:  scalar  (assumed to be  s khat)
        1274             : !   output  curl(s khat) vector in lat-lon coordinates
        1275             : !
        1276             : !   This subroutine can be used to compute divergence free velocity fields,
        1277             : !   since div(ds)=0
        1278             : !
        1279             : !    first compute:
        1280             : !    curl(s khat) = (1/jacobian) ( ds/dy, -ds/dx ) in contra-variant coordinates
        1281             : !    then map to lat-lon
        1282             : !
        1283             :     type (derivative_t), intent(in) :: deriv
        1284             :     type (element_t), intent(in) :: elem
        1285             :     real(kind=r8), intent(in) :: s(np,np)
        1286             :
        1287             :     real(kind=r8) :: ds(np,np,2)
        1288             :
        1289             :     integer i
        1290             :     integer j
        1291             :     integer l
        1292             :
        1293             :     real(kind=r8) ::  dsdx00
        1294             :     real(kind=r8) ::  dsdy00
        1295             :     real(kind=r8) ::  v1(np,np),v2(np,np)
        1296             :
        1297           0 :     do j=1,np
        1298           0 :        do l=1,np
        1299             :           dsdx00=0.0d0
        1300             :           dsdy00=0.0d0
        1301             : !DIR$ UNROLL(NP)
        1302           0 :           do i=1,np
        1303           0 :              dsdx00 = dsdx00 + deriv%Dvv(i,l  )*s(i,j  )
        1304           0 :              dsdy00 = dsdy00 + deriv%Dvv(i,l  )*s(j  ,i)
        1305             :           end do
        1306           0 :           v2(l  ,j  ) = -dsdx00*ra
        1307           0 :           v1(j  ,l  ) =  dsdy00*ra
        1308             :        end do
        1309             :     end do
        1310             :     ! convert contra -> latlon *and* divide by jacobian
        1311           0 :     do j=1,np
        1312           0 :        do i=1,np
        1313           0 :           ds(i,j,1)=(elem%D(i,j,1,1)*v1(i,j) + elem%D(i,j,1,2)*v2(i,j))/elem%metdet(i,j)
        1314           0 :           ds(i,j,2)= (elem%D(i,j,2,1)*v1(i,j) + elem%D(i,j,2,2)*v2(i,j))/elem%metdet(i,j)
        1315             :        enddo
        1316             :     enddo
        1317             :
        1318           0 :     end function curl_sphere
        1319             :
        1320             :
        1321             : !--------------------------------------------------------------------------
        1322             :
        1323             :
        1324             :
        1325  5362718400 :   subroutine divergence_sphere_wk(v,deriv,elem,div)
        1326             : !
        1327             : !   input:  v = velocity in lat-lon coordinates
        1328             : !   ouput:  div(v)  spherical divergence of v, integrated by parts
        1329             : !
        1330             : !   Computes  -< grad(psi) dot v >
        1331             : !   (the integrated by parts version of < psi div(v) > )
        1332             : !
        1333             : !   note: after DSS, divergence_sphere() and divergence_sphere_wk()
        1334             : !   are identical to roundoff, as theory predicts.
        1335             : !
        1336             :     real(kind=r8), intent(in) :: v(np,np,2)  ! in lat-lon coordinates
        1337             :     type (derivative_t), intent(in) :: deriv
        1338             :     type (element_t), intent(in) :: elem
        1339             :     real(kind=r8),intent(out)  :: div(np,np)
        1340             :
        1341             :     ! Local
        1342             :
        1343             :     integer i,j,m,n
        1344             :
        1345             :     real(kind=r8) ::  vtemp(np,np,2)
        1346             :     real(kind=r8) ::  ggtemp(np,np,2)
        1347             :     real(kind=r8) ::  gtemp(np,np,2)
        1348             :     real(kind=r8) ::  psi(np,np)
        1349             :     real(kind=r8) :: xtmp
        1350             :
        1351             :     ! latlon- > contra
        1352 26813592000 :     do j=1,np
        1353 >11261*10^7 :        do i=1,np
        1354 85803494400 :           vtemp(i,j,1)=(elem%Dinv(i,j,1,1)*v(i,j,1) + elem%Dinv(i,j,1,2)*v(i,j,2))
        1355 >10725*10^7 :           vtemp(i,j,2)=(elem%Dinv(i,j,2,1)*v(i,j,1) + elem%Dinv(i,j,2,2)*v(i,j,2))
        1356             :        enddo
        1357             :     enddo
        1358             :
        1359 26813592000 :     do n=1,np
        1360 >11261*10^7 :        do m=1,np
        1361             :
        1362 85803494400 :           div(m,n)=0
        1363             : !DIR$ UNROLL(NP)
        1364 >45046*10^7 :           do j=1,np
        1365 >34321*10^7 :              div(m,n)=div(m,n)-(elem%spheremp(j,n)*vtemp(j,n,1)*deriv%Dvv(m,j) &
        1366             :                               +elem%spheremp(m,j)*vtemp(m,j,2)*deriv%Dvv(n,j)) &
        1367 >77223*10^7 :                               * ra
        1368             :           enddo
        1369             :
        1370             :        end do
        1371             :     end do
        1372             :
        1373  5362718400 :   end subroutine divergence_sphere_wk
        1374             :
        1375             :
        1376             :
        1377           0 :   function element_boundary_integral(v,deriv,elem) result(result)
        1378             : !
        1379             : !   input:  v = velocity in lat-lon coordinates
        1380             : !   ouput:  result(i,j) = contour integral of PHI_ij * v dot normal
        1381             : !           where PHI_ij = cardinal function at i,j GLL point
        1382             : !
        1383             : !   this routine is used just to check spectral element integration by parts identities
        1384             : !
        1385             :     real(kind=r8), intent(in) :: v(np,np,2)  ! in lat-lon coordinates
        1386             :     type (derivative_t), intent(in) :: deriv
        1387             :     type (element_t), intent(in) :: elem
        1388             :     real(kind=r8) :: result(np,np)
        1389             :
        1390             :     ! Local
        1391             :     real(kind=r8) :: ucontra(np,np,2)  ! in lat-lon coordinates
        1392             :     integer i,j
        1393             :
        1394             :     ! latlon->contra
        1395           0 :     do j=1,np
        1396           0 :        do i=1,np
        1397           0 :           ucontra(i,j,1)=(elem%Dinv(i,j,1,1)*v(i,j,1) + elem%Dinv(i,j,1,2)*v(i,j,2))
        1398           0 :           ucontra(i,j,2)=(elem%Dinv(i,j,2,1)*v(i,j,1) + elem%Dinv(i,j,2,2)*v(i,j,2))
        1399             :        enddo
        1400             :     enddo
        1401             :
        1402             :     ! note: GLL weights  weight(i) = Mvv_twt(i,i)
        1403           0 :     result=0
        1404           0 :     j=1
        1405           0 :     do i=1,np
        1406           0 :        result(i,j)=result(i,j)-deriv%Mvv_twt(i,i)*elem%metdet(i,j)*ucontra(i,j,2)*ra
        1407             :     enddo
        1408             :
        1409           0 :     j=np
        1410           0 :     do i=1,np
        1411           0 :        result(i,j)=result(i,j)+deriv%Mvv_twt(i,i)*elem%metdet(i,j)*ucontra(i,j,2)*ra
        1412             :     enddo
        1413             :
        1414           0 :     i=1
        1415           0 :     do j=1,np
        1416           0 :        result(i,j)=result(i,j)-deriv%Mvv_twt(j,j)*elem%metdet(i,j)*ucontra(i,j,1)*ra
        1417             :     enddo
        1418             :
        1419           0 :     i=np
        1420           0 :     do j=1,np
        1421           0 :        result(i,j)=result(i,j)+deriv%Mvv_twt(j,j)*elem%metdet(i,j)*ucontra(i,j,1)*ra
        1422             :     enddo
        1423           0 :   end function element_boundary_integral
        1424             :
        1425             :
        1426             :
        1427           0 :   function edge_flux_u_cg( v,p,pedges, deriv, elem, u_is_contra) result(result)
        1428             : !
        1429             : !
        1430             : !   input:  v = velocity in contra or lat-lon coordinates (CONTINUIOUS)
        1431             : !           p      = scalar on this element
        1432             : !           pedges = scalar edge data from neighbor elements
        1433             : !
        1434             : !   ouput:  result(i,j) = contour integral of PHI_ij * pstar * v dot normal
        1435             : !           where PHI_ij = cardinal function at i,j GLL point
        1436             : !           pstar = centered or other flux
        1437             : !
        1438             :     real(kind=r8), intent(in) :: v(np,np,2)
        1439             :     real(kind=r8), intent(in) :: p(np,np)
        1440             :     real(kind=r8), intent(in) :: pedges(0:np+1,0:np+1)
        1441             :     type (derivative_t), intent(in) :: deriv
        1442             :     type (element_t), intent(in) :: elem
        1443             :     real(kind=r8) :: result(np,np)
        1444             :     logical :: u_is_contra
        1445             :
        1446             :     ! Local
        1447             :     real(kind=r8) :: ucontra(np,np,2)  ! in lat-lon coordinates
        1448             :     real(kind=r8) :: flux,pstar
        1449             :     integer i,j
        1450             :
        1451             :
        1452           0 :     result=0
        1453             :
        1454             :
        1455           0 :     if (u_is_contra) then
        1456           0 :        ucontra=v
        1457             :     else
        1458             :        ! latlon->contra
        1459           0 :        do j=1,np
        1460           0 :           do i=1,np
        1461           0 :              ucontra(i,j,1)=(elem%Dinv(i,j,1,1)*v(i,j,1) + elem%Dinv(i,j,1,2)*v(i,j,2))
        1462           0 :              ucontra(i,j,2)=(elem%Dinv(i,j,2,1)*v(i,j,1) + elem%Dinv(i,j,2,2)*v(i,j,2))
        1463             :           enddo
        1464             :        enddo
        1465             :     endif
        1466             :     ! upwind
        1467           0 :     do i=1,np
        1468           0 :        j=1
        1469           0 :        pstar=p(i,j)
        1470           0 :        if (ucontra(i,j,2)>0) pstar=pedges(i,0)
        1471           0 :        flux = -pstar*ucontra(i,j,2)*( deriv%Mvv_twt(i,i)*elem%metdet(i,j)*ra)
        1472           0 :        result(i,j)=result(i,j)+flux
        1473             :
        1474           0 :        j=np
        1475           0 :        pstar=p(i,j)
        1476           0 :        if (ucontra(i,j,2)<0) pstar=pedges(i,np+1)
        1477           0 :        flux = pstar*ucontra(i,j,2)* ( deriv%Mvv_twt(i,i)*elem%metdet(i,j)*ra)
        1478           0 :        result(i,j)=result(i,j)+flux
        1479             :     enddo
        1480             :
        1481           0 :     do j=1,np
        1482           0 :        i=1
        1483           0 :        pstar=p(i,j)
        1484           0 :        if (ucontra(i,j,1)>0) pstar=pedges(0,j)
        1485           0 :        flux = -pstar*ucontra(i,j,1)* ( deriv%Mvv_twt(j,j)*elem%metdet(i,j)*ra)
        1486           0 :        result(i,j)=result(i,j)+flux
        1487             :
        1488           0 :        i=np
        1489           0 :        pstar=p(i,j)
        1490           0 :        if (ucontra(i,j,1)<0) pstar=pedges(np+1,j)
        1491           0 :        flux = pstar*ucontra(i,j,1)* ( deriv%Mvv_twt(j,j)*elem%metdet(i,j)*ra)
        1492           0 :        result(i,j)=result(i,j)+flux
        1493             :     end do
        1494             :
        1495           0 :   end function edge_flux_u_cg
        1496             :
        1497             :
        1498             : !DIR$ ATTRIBUTES FORCEINLINE :: vorticity_sphere
        1499  4503891600 :   subroutine vorticity_sphere(v,deriv,elem,vort)
        1500             : !
        1501             : !   input:  v = velocity in lat-lon coordinates
        1502             : !   ouput:  spherical vorticity of v
        1503             : !
        1504             :
        1505             :     type (derivative_t), intent(in) :: deriv
        1506             :     type (element_t), intent(in) :: elem
        1507             :     real(kind=r8), intent(in) :: v(np,np,2)
        1508             :
        1509             :     real(kind=r8), intent(out) :: vort(np,np)
        1510             :
        1511             :     integer i
        1512             :     integer j
        1513             :     integer l
        1514             :
        1515             :     real(kind=r8) ::  dvdx00,dudy00
        1516             :     real(kind=r8) ::  vco(np,np,2)
        1517             :     real(kind=r8) ::  vtemp(np,np)
        1518             :
        1519             :     ! convert to covariant form
        1520 22519458000 :     do j=1,np
        1521 94581723600 :        do i=1,np
        1522 72062265600 :           vco(i,j,1)=(elem%D(i,j,1,1)*v(i,j,1) + elem%D(i,j,2,1)*v(i,j,2))
        1523 90077832000 :           vco(i,j,2)=(elem%D(i,j,1,2)*v(i,j,1) + elem%D(i,j,2,2)*v(i,j,2))
        1524             :        enddo
        1525             :     enddo
        1526             :
        1527 22519458000 :     do j=1,np
        1528 94581723600 :        do l=1,np
        1529             :
        1530             :           dudy00=0.0d0
        1531             :           dvdx00=0.0d0
        1532             :
        1533             : !DIR$ UNROLL(NP)
        1534 >36031*10^7 :           do i=1,np
        1535 >28824*10^7 :              dvdx00 = dvdx00 + deriv%Dvv(i,l  )*vco(i,j  ,2)
        1536 >36031*10^7 :              dudy00 = dudy00 + deriv%Dvv(i,l  )*vco(j  ,i,1)
        1537             :           enddo
        1538             :
        1539 72062265600 :           vort(l  ,j  ) = dvdx00
        1540 90077832000 :           vtemp(j  ,l  ) = dudy00
        1541             :        enddo
        1542             :     enddo
        1543             :
        1544 22519458000 :     do j=1,np
        1545 94581723600 :        do i=1,np
        1546 90077832000 :           vort(i,j)=(vort(i,j)-vtemp(i,j))*(elem%rmetdet(i,j)*ra)
        1547             :        end do
        1548             :     end do
        1549             :
        1550  4503891600 :   end subroutine vorticity_sphere
        1551             :
        1552           0 :   function vorticity_sphere_diag(v,deriv,elem) result(vort)
        1553             :   !
        1554             :   !   input:  v = velocity in lat-lon coordinates
        1555             :   !   ouput:  diagonal component of spherical vorticity of v
        1556             :   !
        1557             :
        1558             :       type (derivative_t), intent(in) :: deriv
        1559             :       type (element_t), intent(in) :: elem
        1560             :       real(kind=r8), intent(in) :: v(np,np,2)
        1561             :
        1562             :       real(kind=r8) :: vort(np,np)
        1563             :
        1564             :       integer i
        1565             :       integer j
        1566             :       integer l
        1567             :
        1568             :       real(kind=r8) :: dvdx00,dudy00
        1569             :       real(kind=r8) :: vco(np,np,2)
        1570             :       real(kind=r8) :: vtemp(np,np)
        1571             :       real(kind=r8) :: rdx
        1572             :       real(kind=r8) :: rdy
        1573             :
        1574             :       ! convert to covariant form
        1575             :
        1576           0 :       do j=1,np
        1577           0 :          do i=1,np
        1578           0 :             vco(i,j,1)=(elem%D(i,j,1,1)*v(i,j,1) + elem%D(i,j,2,1)*v(i,j,2))
        1579           0 :             vco(i,j,2)=(elem%D(i,j,1,2)*v(i,j,1) + elem%D(i,j,2,2)*v(i,j,2))
        1580             :          enddo
        1581             :       enddo
        1582             :
        1583             :
        1584           0 :       do j=1,np
        1585           0 :          do l=1,np
        1586             :             dudy00=0.0d0
        1587             :             dvdx00=0.0d0
        1588             : !DIR$ UNROLL(NP)
        1589           0 :             do i=1,np
        1590           0 :                dvdx00 = dvdx00 + deriv%Dvv_diag(i,l)*vco(i,j ,2)
        1591           0 :                dudy00 = dudy00 + deriv%Dvv_diag(i,l)*vco(j ,i,1)
        1592             :             enddo
        1593           0 :             vort(l ,j) = dvdx00
        1594           0 :             vtemp(j ,l) = dudy00
        1595             :          enddo
        1596             :       enddo
        1597             :
        1598           0 :       do j=1,np
        1599           0 :          do i=1,np
        1600           0 :           vort(i,j)=(vort(i,j)-vtemp(i,j))*(elem%rmetdet(i,j)*ra)
        1601             :          end do
        1602             :       end do
        1603             :
        1604           0 :   end function vorticity_sphere_diag
        1605             :
        1606             : !DIR$ ATTRIBUTES FORCEINLINE :: divergence_sphere
        1607  6597676800 :   subroutine divergence_sphere(v,deriv,elem,div)
        1608             : !
        1609             : !   input:  v = velocity in lat-lon coordinates
        1610             : !   ouput:  div(v)  spherical divergence of v
        1611             : !
        1612             :
        1613             :
        1614             :     real(kind=r8), intent(in) :: v(np,np,2)  ! in lat-lon coordinates
        1615             :     type (derivative_t), intent(in) :: deriv
        1616             :     type (element_t), intent(in) :: elem
        1617             :     real(kind=r8), intent(out) :: div(np,np)
        1618             :
        1619             :     ! Local
        1620             :
        1621             :     integer i
        1622             :     integer j
        1623             :     integer l
        1624             :
        1625             :     real(kind=r8) ::  dudx00
        1626             :     real(kind=r8) ::  dvdy00
        1627             :     real(kind=r8) ::  gv(np,np,2),vvtemp(np,np)
        1628             :
        1629             :     ! convert to contra variant form and multiply by g
        1630 32988384000 :     do j=1,np
        1631 >13855*10^7 :        do i=1,np
        1632 >10556*10^7 :           gv(i,j,1)=elem%metdet(i,j)*(elem%Dinv(i,j,1,1)*v(i,j,1) + elem%Dinv(i,j,1,2)*v(i,j,2))
        1633 >13195*10^7 :           gv(i,j,2)=elem%metdet(i,j)*(elem%Dinv(i,j,2,1)*v(i,j,1) + elem%Dinv(i,j,2,2)*v(i,j,2))
        1634             :        enddo
        1635             :     enddo
        1636             :
        1637             :     ! compute d/dx and d/dy
        1638 32988384000 :     do j=1,np
        1639 >13855*10^7 :        do l=1,np
        1640             :           dudx00=0.0d0
        1641             :           dvdy00=0.0d0
        1642             : !DIR$ UNROLL(NP)
        1643 >52781*10^7 :           do i=1,np
        1644 >42225*10^7 :              dudx00 = dudx00 + deriv%Dvv(i,l  )*gv(i,j  ,1)
        1645 >52781*10^7 :              dvdy00 = dvdy00 + deriv%Dvv(i,l  )*gv(j  ,i,2)
        1646             :           end do
        1647 >10556*10^7 :           div(l  ,j  ) = dudx00
        1648 >13195*10^7 :           vvtemp(j  ,l  ) = dvdy00
        1649             :        end do
        1650             :     end do
        1651             :
        1652 32988384000 :     do j=1,np
        1653 >13855*10^7 :        do i=1,np
        1654 >13195*10^7 :           div(i,j)=(div(i,j)+vvtemp(i,j))*(elem%rmetdet(i,j)*ra)
        1655             :        end do
        1656             :     end do
        1657             :
        1658  6597676800 :   end subroutine divergence_sphere
        1659             :
        1660             :
        1661             : !DIR$ ATTRIBUTES FORCEINLINE :: laplace_sphere_wk
        1662  5362718400 :   subroutine laplace_sphere_wk(s,deriv,elem,laplace,var_coef,mol_nu)
        1663             : !
        1664             : !   input:  s = scalar
        1665             : !   ouput:  -< grad(PHI), grad(s) >   = weak divergence of grad(s)
        1666             : !     note: for this form of the operator, grad(s) does not need to be made C0
        1667             : !
        1668             :     real(kind=r8), intent(in) :: s(np,np)
        1669             :     type (derivative_t), intent(in)     :: deriv
        1670             :     type (element_t), intent(in)        :: elem
        1671             :     real(kind=r8)                       :: laplace(np,np)
        1672             :     logical,       intent(in)           :: var_coef
        1673             :     real(kind=r8), intent(in), optional :: mol_nu(np,np)  !variable nu (e.g. molecular diffusion)
        1674             :
        1675             :     real(kind=r8)             :: laplace2(np,np)
        1676             :     integer i,j
        1677             :
        1678             :     ! Local
        1679             :     real(kind=r8) :: grads(np,np,2), oldgrads(np,np,2)
        1680             :
        1681  5362718400 :     call gradient_sphere(s,deriv,elem%Dinv,grads)
        1682             :     !
        1683             :     ! molecular diffusion coefficient
        1684             :     !
        1685  5362718400 :     if (present(mol_nu)) then
        1686           0 :       if (var_coef) &
        1687           0 :            call endrun('ERROR: this option is only for non-grid dependent variable viscosity')
        1688           0 :       grads(:,:,1) = grads(:,:,1)*mol_nu(:,:)
        1689           0 :       grads(:,:,2) = grads(:,:,2)*mol_nu(:,:)
        1690             :     end if
        1691             :
        1692  5362718400 :     if (var_coef) then
        1693  5269212000 :        if (hypervis_power/=0 ) then
        1694             :           ! scalar viscosity with variable coefficient
        1695           0 :           grads(:,:,1) = grads(:,:,1)*elem%variable_hyperviscosity(:,:)
        1696           0 :           grads(:,:,2) = grads(:,:,2)*elem%variable_hyperviscosity(:,:)
        1697  5269212000 :        else if (hypervis_scaling /=0 ) then
        1698             :           ! tensor hv, (3)
        1699           0 :           oldgrads=grads
        1700           0 :           do j=1,np
        1701           0 :              do i=1,np
        1702           0 :                 grads(i,j,1) = oldgrads(i,j,1)*elem%tensorVisc(i,j,1,1) + &
        1703           0 :                                oldgrads(i,j,2)*elem%tensorVisc(i,j,1,2)
        1704             :                 grads(i,j,2) = oldgrads(i,j,1)*elem%tensorVisc(i,j,2,1) + &
        1705           0 :                                oldgrads(i,j,2)*elem%tensorVisc(i,j,2,2)
        1706             :              end do
        1707             :           end do
        1708             :        else
        1709             :           ! do nothing: constant coefficient viscosity
        1710             :        endif
        1711             :     endif
        1712             :
        1713             :     ! note: divergnece_sphere and divergence_sphere_wk are identical *after* bndry_exchange
        1714             :     ! if input is C_0.  Here input is not C_0, so we should use divergence_sphere_wk().
        1715             :     ! laplace=divergence_sphere_wk(grads,deriv,elem)
        1716  5362718400 :     call divergence_sphere_wk(grads,deriv,elem,laplace)
        1717             :
        1718  5362718400 :   end subroutine laplace_sphere_wk
        1719             :
        1720             : !DIR$ ATTRIBUTES FORCEINLINE :: vlaplace_sphere_wk
        1721  2477919600 :   subroutine vlaplace_sphere_wk(v,deriv,elem,undamprrcart,laplace,var_coef,nu_ratio)
        1722             : !
        1723             : !   input:  v = vector in lat-lon coordinates
        1724             : !   ouput:  weak laplacian of v, in lat-lon coordinates
        1725             : !
        1726             : !   logic:
        1727             : !      tensorHV:     requires cartesian
        1728             : !      nu_div/=nu:   requires contra formulatino
        1729             : !
        1730             : !   One combination NOT supported:  tensorHV and nu_div/=nu then abort
        1731             : !
        1732             :     real(kind=r8),           intent(in)  :: v(np,np,2)
        1733             :     type (derivative_t),     intent(in)  :: deriv
        1734             :     type (element_t),        intent(in)  :: elem
        1735             :     logical,                 intent(in)  :: undamprrcart
        1736             :     real(kind=r8),           intent(out) :: laplace(np,np,2)
        1737             :     logical,       optional, intent(in)  :: var_coef
        1738             :     real(kind=r8), optional, intent(in)  :: nu_ratio
        1739             :
        1740             :
        1741  2477919600 :     if (hypervis_scaling/=0 .and. var_coef) then
        1742             :        ! tensorHV is turned on - requires cartesian formulation
        1743           0 :        if (present(nu_ratio)) then
        1744           0 :           if (nu_ratio /= 1._r8) then
        1745           0 :              call endrun('ERROR: tensorHV can not be used with nu_div/=nu')
        1746             :           endif
        1747             :        endif
        1748           0 :        laplace=vlaplace_sphere_wk_cartesian(v,deriv,elem,var_coef,undamprrcart)
        1749             :     else
        1750             :        ! all other cases, use contra formulation:
        1751  2477919600 :        laplace=vlaplace_sphere_wk_contra(v,deriv,elem,var_coef,undamprrcart,nu_ratio)
        1752             :     endif
        1753             :
        1754  2477919600 :   end subroutine vlaplace_sphere_wk
        1755             :   !
        1756             :   ! version of vlaplace_sphere_wk for molecular diffusion
        1757             :   !
        1758           0 :   subroutine vlaplace_sphere_wk_mol(v,deriv,elem,undamprrcart,mol_nu,laplace)
        1759             :     !
        1760             :     !   input:  v = vector in lat-lon coordinates
        1761             :     !   ouput:  weak laplacian of v, in lat-lon coordinates
        1762             :     !
        1763             :     real(kind=r8), intent(in)      :: v(np,np,2)
        1764             :     type (derivative_t), intent(in):: deriv
        1765             :     type (element_t), intent(in)   :: elem
        1766             :     logical,             intent(in) :: undamprrcart
        1767             :     real(kind=r8), intent(in)      :: mol_nu(np,np)
        1768             :     real(kind=r8), intent(out)     :: laplace(np,np,2)
        1769             :
        1770             :     real(kind=r8) :: vor(np,np),div(np,np)
        1771             :
        1772             :     integer :: n,m
        1773             :
        1774           0 :     call divergence_sphere(v,deriv,elem,div)
        1775           0 :     call vorticity_sphere(v,deriv,elem,vor)
        1776             :
        1777           0 :     div = div*mol_nu(:,:)
        1778           0 :     vor = vor*mol_nu(:,:)
        1779             :
        1780             :     laplace = gradient_sphere_wk_testcov(div,deriv,elem) - &
        1781           0 :          curl_sphere_wk_testcov(vor,deriv,elem)
        1782             :
        1783           0 :     if (undamprrcart) then
        1784           0 :       do n=1,np
        1785           0 :         do m=1,np
        1786             :           ! add in correction so we dont damp rigid rotation
        1787           0 :           laplace(m,n,1)=laplace(m,n,1) + 2*elem%spheremp(m,n)*v(m,n,1)*(ra**2)
        1788           0 :           laplace(m,n,2)=laplace(m,n,2) + 2*elem%spheremp(m,n)*v(m,n,2)*(ra**2)
        1789             :         enddo
        1790             :       enddo
        1791             :     end if
        1792             :
        1793             :
        1794           0 :   end subroutine vlaplace_sphere_wk_mol
        1795             :
        1796             :
        1797             :
        1798           0 :   function vlaplace_sphere_wk_cartesian(v,deriv,elem,var_coef,undamprrcart) result(laplace)
        1799             : !
        1800             : !   input:  v = vector in lat-lon coordinates
        1801             : !   ouput:  weak laplacian of v, in lat-lon coordinates
        1802             :
        1803             :     real(kind=r8),       intent(in) :: v(np,np,2)
        1804             :     type (derivative_t), intent(in) :: deriv
        1805             :     type (element_t),    intent(in) :: elem
        1806             :     logical,             intent(in) :: var_coef
        1807             :     logical,             intent(in) :: undamprrcart
        1808             :     real(kind=r8)                   :: laplace(np,np,2)
        1809             :     ! Local
        1810             :
        1811             :     integer component
        1812             :     real(kind=r8) :: dum_cart(np,np,3)
        1813             :     real(kind=r8) :: dum_cart2(np,np)
        1814             :
        1815             :
        1816             :     ! latlon -> cartesian
        1817           0 :     do component=1,3
        1818           0 :        dum_cart2(:,:) = elem%vec_sphere2cart(:,:,component,1)*v(:,:,1) + &
        1819           0 :                                 elem%vec_sphere2cart(:,:,component,2)*v(:,:,2)
        1820             :        ! Do laplace on cartesian comps
        1821           0 :        call laplace_sphere_wk(dum_cart2,deriv,elem,dum_cart(:,:,component),var_coef)
        1822             :     enddo
        1823             :
        1824             :     ! cartesian -> latlon
        1825           0 :     do component=1,2
        1826             :        ! vec_sphere2cart is its own pseudoinverse.
        1827           0 :        laplace(:,:,component) = dum_cart(:,:,1)*elem%vec_sphere2cart(:,:,1,component) + &
        1828             :                                 dum_cart(:,:,2)*elem%vec_sphere2cart(:,:,2,component) + &
        1829           0 :                                 dum_cart(:,:,3)*elem%vec_sphere2cart(:,:,3,component)
        1830             :     end do
        1831             :
        1832           0 :     if (undamprrcart) then
        1833             :       ! add in correction so we dont damp rigid rotation
        1834           0 :       laplace(:,:,1)=laplace(:,:,1) + 2*elem%spheremp(:,:)*v(:,:,1)*(ra**2)
        1835           0 :       laplace(:,:,2)=laplace(:,:,2) + 2*elem%spheremp(:,:)*v(:,:,2)*(ra**2)
        1836             :     end if
        1837             :
        1838           0 :   end function vlaplace_sphere_wk_cartesian
        1839             :
        1840             :
        1841             :
        1842  2477919600 :   function vlaplace_sphere_wk_contra(v,deriv,elem,var_coef,undamprrcart,nu_ratio) result(laplace)
        1843             : !
        1844             : !   input:  v = vector in lat-lon coordinates
        1845             : !   ouput:  weak laplacian of v, in lat-lon coordinates
        1846             : !
        1847             : !   du/dt = laplace(u) = grad(div) - curl(vor)
        1848             : !   < PHI du/dt > = < PHI laplace(u) >        PHI = covariant, u = contravariant
        1849             : !                 = < PHI grad(div) >  - < PHI curl(vor) >
        1850             : !                 = grad_wk(div) - curl_wk(vor)
        1851             : !
        1852             :     real(kind=r8),           intent(in) :: v(np,np,2)
        1853             :     logical,                 intent(in) :: var_coef
        1854             :     type (derivative_t),     intent(in) :: deriv
        1855             :     type (element_t),        intent(in) :: elem
        1856             :     logical,                 intent(in) :: undamprrcart
        1857             :     real(kind=r8), optional, intent(in) :: nu_ratio
        1858             :
        1859             :     real(kind=r8)                       :: laplace(np,np,2)
        1860             :
        1861             :     ! Local
        1862             :
        1863             :     integer i,j,l,m,n
        1864             :     real(kind=r8) :: vor(np,np),div(np,np)
        1865             :     real(kind=r8) :: v1,v2,div1,div2,vor1,vor2,phi_x,phi_y
        1866             :
        1867  2477919600 :     call divergence_sphere(v,deriv,elem,div)
        1868  2477919600 :     call vorticity_sphere(v,deriv,elem,vor)
        1869             :
        1870  2477919600 :     if (var_coef .and. hypervis_power/=0 ) then
        1871             :           ! scalar viscosity with variable coefficient
        1872           0 :           div = div*elem%variable_hyperviscosity(:,:)
        1873           0 :           vor = vor*elem%variable_hyperviscosity(:,:)
        1874             :     endif
        1875             :
        1876 52036311600 :     if (present(nu_ratio)) div = nu_ratio*div
        1877             :
        1878             :     laplace = gradient_sphere_wk_testcov(div,deriv,elem) - &
        1879 >10655*10^7 :          curl_sphere_wk_testcov(vor,deriv,elem)
        1880             :
        1881  2477919600 :     if (undamprrcart) then
        1882 12389598000 :       do n=1,np
        1883 52036311600 :         do m=1,np
        1884             :           ! add in correction so we dont damp rigid rotation
        1885 39646713600 :           laplace(m,n,1)=laplace(m,n,1) + 2*elem%spheremp(m,n)*v(m,n,1)*(ra**2)
        1886 49558392000 :           laplace(m,n,2)=laplace(m,n,2) + 2*elem%spheremp(m,n)*v(m,n,2)*(ra**2)
        1887             :         enddo
        1888             :       enddo
        1889             :     end if
        1890  2477919600 :   end function vlaplace_sphere_wk_contra
        1891             :
        1892           0 :   function gll_to_dgmodal(p,deriv) result(phat)
        1893             : !
        1894             : !   input:  v = velocity in lat-lon coordinates
        1895             : !   ouput:  phat = Legendre coefficients
        1896             : !
        1897             : !   Computes  < g dot p  > = SUM  g(i,j) p(i,j) w(i) w(j)
        1898             : !   (the quadrature approximation on the *reference element* of the integral of p against
        1899             : !    all Legendre polynomials up to degree npdg
        1900             : !
        1901             : !   for npdg < np, this routine gives the (exact) modal expansion of p/spheremp()
        1902             : !
        1903             :     real(kind=r8), intent(in) :: p(np,np)
        1904             :     type (derivative_t), intent(in) :: deriv
        1905             :     real(kind=r8) :: phat(npdg,npdg)
        1906             :
        1907             :     ! Local
        1908             :     integer i,j,m,n
        1909           0 :     real(kind=r8) :: A(np,npdg)
        1910           0 :     A=0
        1911           0 :     phat=0
        1912             :
        1913             :     ! N^3 tensor product formulation:
        1914           0 :     do m=1,npdg
        1915           0 :     do j=1,np
        1916             : !DIR$ UNROLL(NP)
        1917           0 :     do i=1,np
        1918           0 :        A(j,m)=A(j,m)+( p(i,j)*deriv%Mvv_twt(i,i)*deriv%Mvv_twt(j,j)  )*deriv%legdg(m,i)
        1919             :     enddo
        1920             :     enddo
        1921             :     enddo
        1922             :
        1923           0 :     do n=1,npdg
        1924           0 :     do m=1,npdg
        1925             : !DIR$ UNROLL(NP)
        1926           0 :     do j=1,np
        1927           0 :        phat(m,n)=phat(m,n)+A(j,m)*deriv%legdg(n,j)
        1928             :     enddo
        1929             :     enddo
        1930             :     enddo
        1931             :
        1932           0 :   end function
        1933             :
        1934           0 :   function dgmodal_to_gll(phat,deriv) result(p)
        1935             :     !
        1936             :     !   input:  phat = coefficients of Legendre expansion
        1937             :     !   ouput:  p    = sum expansion to evaluate phat at GLL points
        1938             :     !
        1939             :     real(kind=r8) :: p(np,np)
        1940             :     type (derivative_t), intent(in) :: deriv
        1941             :     real(kind=r8) :: phat(npdg,npdg)
        1942             :     ! Local
        1943             :     integer i,j,m,n
        1944           0 :     real(kind=r8) :: A(npdg,np)
        1945             :
        1946           0 :     p(:,:)=0
        1947             :     ! tensor product version
        1948           0 :     A=0
        1949           0 :     do i=1,np
        1950           0 :       do n=1,npdg
        1951           0 :         do m=1,npdg
        1952           0 :           A(n,i)=A(n,i)+phat(m,n)*deriv%legdg(m,i)
        1953             :         enddo
        1954             :       enddo
        1955             :     enddo
        1956           0 :     do j=1,np
        1957           0 :       do i=1,np
        1958           0 :         do n=1,npdg
        1959           0 :           p(i,j) = p(i,j)+A(n,i)*deriv%legdg(n,j)
        1960             :         enddo
        1961             :       enddo
        1962             :     enddo
        1963             :
        1964           0 :   end function dgmodal_to_gll
        1965             :
        1966  2072725200 :   subroutine subcell_dss_fluxes(dss, p, n, metdet, C, fluxes)
        1967             :
        1968             :     integer              , intent(in)  :: p
        1969             :     integer              , intent(in)  :: n
        1970             :     real (kind=r8), intent(in)  :: dss     (p,p)
        1971             :     real (kind=r8), intent(in)  :: metdet  (p,p)
        1972             :     real (kind=r8), intent(in)  :: C       (2,2,2)
        1973             :
        1974             :     real (kind=r8)              :: fluxes  (n,n,4)
        1975             :
        1976  4145450400 :     real (kind=r8)              :: Bp(p,p)
        1977  4145450400 :     real (kind=r8)              :: Tp(p,p)
        1978  4145450400 :     real (kind=r8)              :: Lp(p,p)
        1979  4145450400 :     real (kind=r8)              :: Rp(p,p)
        1980             :
        1981  4145450400 :     real (kind=r8)              :: B(n,n)
        1982  4145450400 :     real (kind=r8)              :: T(n,n)
        1983  4145450400 :     real (kind=r8)              :: L(n,n)
        1984  2072725200 :     real (kind=r8)              :: R(n,n)
        1985             :
        1986             :     integer            :: i,j
        1987             :
        1988 >10985*10^7 :     fluxes  = 0
        1989             :
        1990 43527229200 :     Bp = 0
        1991 43527229200 :     Tp = 0
        1992 43527229200 :     Rp = 0
        1993 43527229200 :     Lp = 0
        1994             :
        1995 12436351200 :     Bp(:,1)  = dss(:,1)  ! bottom
        1996 10363626000 :     Tp(:,p)  = dss(:,p)  ! top
        1997 10363626000 :     Rp(p,:)  = dss(p,:)  ! right
        1998 10363626000 :     Lp(1,:)  = dss(1,:)  ! left
        1999             :
        2000  2072725200 :     Bp(1,1)  = C(1,1,2)
        2001  2072725200 :     Lp(1,1)  = C(1,1,1)
        2002  2072725200 :     Bp(p,1)  = C(2,1,2)
        2003  2072725200 :     Rp(p,1)  = C(2,1,1)
        2004             :
        2005  2072725200 :     Tp(1,p)  = C(1,2,2)
        2006  2072725200 :     Lp(1,p)  = C(1,2,1)
        2007  2072725200 :     Tp(p,p)  = C(2,2,2)
        2008  2072725200 :     Rp(p,p)  = C(2,2,1)
        2009             :
        2010             :
        2011  2072725200 :     call subcell_integration(Bp, p, n, metdet,B)
        2012  2072725200 :     call subcell_integration(Tp, p, n, metdet,T)
        2013  2072725200 :     call subcell_integration(Lp, p, n, metdet,L)
        2014  2072725200 :     call subcell_integration(Rp, p, n, metdet,R)
        2015             :
        2016  8290900800 :     do i = 1,n
        2017 26945427600 :     do j = 1,n
        2018 18654526800 :       if (1<j) T(i,j) = T(i,j) + T(i,j-1)
        2019 24872702400 :       if (1<i) R(i,j) = R(i,j) + R(i-1,j)
        2020             :     end do
        2021             :     end do
        2022             :
        2023  8290900800 :     do i = n,1,-1
        2024 26945427600 :     do j = n,1,-1
        2025 18654526800 :       if (j<n) B(i,j) = B(i,j) + B(i,j+1)
        2026 24872702400 :       if (i<n) L(i,j) = L(i,j) + L(i+1,j)
        2027             :     end do
        2028             :     end do
        2029             :
        2030  8290900800 :     do i = 1,n
        2031 26945427600 :       do j = 1,n
        2032 18654526800 :         if (1==j) fluxes(i,j,1) =  B(i,j)
        2033 18654526800 :         if (n==i) fluxes(i,j,2) =  R(i,j)
        2034 18654526800 :         if (j==n) fluxes(i,j,3) =  T(i,j)
        2035 18654526800 :         if (1==i) fluxes(i,j,4) =  L(i,j)
        2036             :
        2037 18654526800 :         if (1< j) fluxes(i,j,1) =   B(i,j) - T(i,j-1)
        2038 18654526800 :         if (i< n) fluxes(i,j,2) =   R(i,j) - L(i+1,j)
        2039 18654526800 :         if (j< n) fluxes(i,j,3) =   T(i,j) - B(i,j+1)
        2040 24872702400 :         if (1< i) fluxes(i,j,4) =   L(i,j) - R(i-1,j)
        2041             :       end do
        2042             :     end do
        2043             :
        2044  2072725200 :   end subroutine subcell_dss_fluxes
        2045             :
        2046   810388800 :   subroutine subcell_div_fluxes(u, p, n, metdet,fluxes)
        2047             :
        2048             :     implicit none
        2049             :
        2050             :     integer              , intent(in)  :: p
        2051             :     integer              , intent(in)  :: n
        2052             :     real (kind=r8), intent(in)  :: u(p,p,2)
        2053             :     real (kind=r8), intent(in)  :: metdet(p,p)
        2054             :
        2055  1620777600 :     real (kind=r8)              :: v(p,p,2)
        2056             :     real (kind=r8)              :: fluxes(n,n,4)
        2057  1620777600 :     real (kind=r8)              :: tb(n,p)
        2058  1620777600 :     real (kind=r8)              :: lr(p,n)
        2059  1620777600 :     real (kind=r8)              :: flux_l(n,n)
        2060  1620777600 :     real (kind=r8)              :: flux_r(n,n)
        2061  1620777600 :     real (kind=r8)              :: flux_b(n,n)
        2062  1620777600 :     real (kind=r8)              :: flux_t(n,n)
        2063             :
        2064             :     integer i,j
        2065             :
        2066             :     if (.not.ALLOCATED(integration_matrix)      .or. &
        2067   810388800 :         SIZE(integration_matrix,1).ne.n .or. &
        2068             :         SIZE(integration_matrix,2).ne.p) then
        2069           0 :       call endrun( 'FATAL ERROR: allocate_subcell_integration_matrix not called')
        2070             :     end if
        2071             :
        2072 17018164800 :     v(:,:,1) = u(:,:,1)*metdet(:,:)
        2073 17018164800 :     v(:,:,2) = u(:,:,2)*metdet(:,:)
        2074             :
        2075   810388800 :     tb = MATMUL(integration_matrix, v(:,:,2))
        2076   810388800 :     flux_b(:,:) = MATMUL(tb,TRANSPOSE(boundary_interp_matrix(:,1,:)))
        2077   810388800 :     flux_t(:,:) = MATMUL(tb,TRANSPOSE(boundary_interp_matrix(:,2,:)))
        2078             :
        2079   810388800 :     lr = MATMUL(v(:,:,1),TRANSPOSE(integration_matrix))
        2080   810388800 :     flux_l(:,:) = MATMUL(boundary_interp_matrix(:,1,:),lr)
        2081   810388800 :     flux_r(:,:) = MATMUL(boundary_interp_matrix(:,2,:),lr)
        2082             :
        2083 10535054400 :     fluxes(:,:,1) = -flux_b(:,:)*ra
        2084 10535054400 :     fluxes(:,:,2) =  flux_r(:,:)*ra
        2085 10535054400 :     fluxes(:,:,3) =  flux_t(:,:)*ra
        2086 10535054400 :     fluxes(:,:,4) = -flux_l(:,:)*ra
        2087             :
        2088   810388800 :   end subroutine subcell_div_fluxes
        2089             :
        2090  1262336400 :   subroutine subcell_Laplace_fluxes(u, deriv, elem, p, n, fluxes)
        2091             :
        2092             :     implicit none
        2093             :
        2094             :     integer              , intent(in)  :: p
        2095             :     integer              , intent(in)  :: n
        2096             :     type (derivative_t)  , intent(in)  :: deriv
        2097             :     type (element_t)     , intent(in)  :: elem
        2098             :     real (kind=r8), intent(in)  :: u(p,p)
        2099             :
        2100  2524672800 :     real (kind=r8)              :: g(p,p,2)
        2101  2524672800 :     real (kind=r8)              :: v(p,p,2)
        2102  2524672800 :     real (kind=r8)              :: div(p,p,2)
        2103  2524672800 :     real (kind=r8)              :: sub_int(n,n,2)
        2104             :     real (kind=r8)              :: fluxes(n,n,4)
        2105             :
        2106             :     integer i,j
        2107             :
        2108  1262336400 :     call gradient_sphere(u,deriv,elem%Dinv,g)
        2109             :
        2110 26509064400 :     v(:,:,1) = elem%Dinv(:,:,1,1)*g(:,:,1) + elem%Dinv(:,:,1,2)*g(:,:,2)
        2111 26509064400 :     v(:,:,2) = elem%Dinv(:,:,2,1)*g(:,:,1) + elem%Dinv(:,:,2,2)*g(:,:,2)
        2112             :
        2113  6311682000 :     do j=1,p
        2114 26509064400 :     do i=1,p
        2115 >10098*10^7 :        div(i,j,1) = -SUM(elem%spheremp(:,j)*v(:,j,1)*deriv%Dvv(i,:))
        2116 >10603*10^7 :        div(i,j,2) = -SUM(elem%spheremp(i,:)*v(i,:,2)*deriv%Dvv(j,:))
        2117             :     end do
        2118             :     end do
        2119 54280465200 :     div = div * ra
        2120             :
        2121 26509064400 :     div(:,:,1) = div(:,:,1) / elem%spheremp(:,:)
        2122 26509064400 :     div(:,:,2) = div(:,:,2) / elem%spheremp(:,:)
        2123             :
        2124  1262336400 :     call subcell_integration(div(:,:,1), p, n, elem%metdet, sub_int(:,:,1))
        2125  1262336400 :     call subcell_integration(div(:,:,2), p, n, elem%metdet, sub_int(:,:,2))
        2126             :
        2127  5049345600 :     do i=1,n
        2128 12623364000 :     do j=2,n
        2129  7574018400 :       sub_int(j,i,1) = sub_int(j,i,1) + sub_int(j-1,i,1)
        2130 11361027600 :       sub_int(i,j,2) = sub_int(i,j,2) + sub_int(i,j-1,2)
        2131             :     end do
        2132             :     end do
        2133             :
        2134 66903829200 :     fluxes = 0
        2135  5049345600 :     do i=1,n
        2136 16410373200 :     do j=1,n
        2137 11361027600 :       if (1.lt.j) fluxes(i,j,1) = -sub_int(i,  j-1,2)
        2138 11361027600 :       if (i.lt.n) fluxes(i,j,2) =  sub_int(i,  j,  1)
        2139 11361027600 :       if (j.lt.n) fluxes(i,j,3) =  sub_int(i,  j,  2)
        2140 15148036800 :       if (1.lt.i) fluxes(i,j,4) = -sub_int(i-1,j,  1)
        2141             :     end do
        2142             :     end do
        2143             :
        2144  1262336400 :   end subroutine subcell_Laplace_fluxes
        2145             :
        2146             :
        2147             :   ! Given a field defined on the unit element, [-1,1]x[-1,1]
        2148             :   ! sample values, sampled_val, premultiplied by integration weights,
        2149             :   ! and a number, np, of Gauss-Lobatto-Legendre points. Divide
        2150             :   ! the square up into intervals by intervals sub-squares so that
        2151             :   ! there are now intervals**2 sub-cells.  Integrate the
        2152             :   ! function defined by sampled_val over each of these
        2153             :   ! sub-cells and return the integrated values as an
        2154             :   ! intervals by intervals matrix.
        2155             :   !
        2156             :   ! Efficiency is obtained by computing and caching the appropriate
        2157             :   ! integration matrix the first time the function is called.
        2158 11024812800 :   subroutine subcell_integration(sampled_val, np, intervals, metdet,values,separate_physics_grid)
        2159             :
        2160             :     implicit none
        2161             :
        2162             :     integer              , intent(in)  :: np
        2163             :     integer              , intent(in)  :: intervals
        2164             :     real (kind=r8), intent(in)  :: sampled_val(np,np)
        2165             :     real (kind=r8), intent(in)  :: metdet(np,np)
        2166 22049625600 :     real (kind=r8)              :: val(np,np)
        2167             :     real (kind=r8)              :: values(intervals,intervals)
        2168             :
        2169             :     logical, optional :: separate_physics_grid
        2170             :
        2171             :     logical :: ltmp
        2172             :     integer i,j
        2173             :
        2174 11024812800 :     if (present(separate_physics_grid)) then
        2175   208656000 :       ltmp = separate_physics_grid
        2176             :     else
        2177             :       ltmp = .false.
        2178             :     end if
        2179             :
        2180             :
        2181   208656000 :     if (ltmp) then
        2182             :         if (.not.ALLOCATED(integration_matrix_physgrid)      .or. &
        2183           0 :              SIZE(integration_matrix_physgrid,1).ne.intervals .or. &
        2184             :              SIZE(integration_matrix_physgrid,2).ne.np) then
        2185           0 :           call endrun( 'FATAL ERROR: allocate_subcell_integration_matrix_physgrid not called')
        2186             :         end if
        2187           0 :         val = sampled_val * metdet
        2188             :         values = MATMUL(integration_matrix_physgrid, &
        2189           0 :              MATMUL(val,TRANSPOSE(integration_matrix_physgrid)))
        2190             :     else
        2191             :       if (.not.ALLOCATED(integration_matrix)      .or. &
        2192 11024812800 :            SIZE(integration_matrix,1).ne.intervals .or. &
        2193             :            SIZE(integration_matrix,2).ne.np) then
        2194           0 :         call endrun( 'FATAL ERROR: allocate_subcell_integration_matrix not called')
        2195             :       end if
        2196             :       ! Multiply sampled values by spectral element weights
        2197 >23152*10^7 :       val = sampled_val * metdet
        2198             :
        2199             :       ! Multiply the sampled values by the weighted jacobians.
        2200             :       ! Symmetry allows us to write this as J^t V J
        2201             :       ! where J is a vector.
        2202             :
        2203             :       values = MATMUL(integration_matrix, &
        2204 11024812800 :            MATMUL(val,TRANSPOSE(integration_matrix)))
        2205             :
        2206             :     end if
        2207 11024812800 :   end subroutine subcell_integration
        2208             :
        2209             :
        2210             :   ! Helper subroutine that will fill in a matrix needed to
        2211             :   ! integrate a function defined on the GLL points of a unit
        2212             :   ! square on sub-cells.  So np is the number of integration
        2213             :   ! GLL points defined on the unit square (actually [-1,1]x[-1,1])
        2214             :   ! and intervals is the number to cut it up into, say a 3 by 3
        2215             :   ! set of uniform sub-cells.  This function will fill the
        2216             :   ! subcell_integration matrix with the correct coefficients
        2217             :   ! to integrate over each subcell.
        2218        1536 :   subroutine allocate_subcell_integration_matrix_cslam(np, intervals)
        2219             :     !-----------------
        2220             :     !-----------------
        2221             :     use quadrature_mod, only : gausslobatto, quadrature_t
        2222             :
        2223             :     implicit none
        2224             :
        2225             :     integer              , intent(in)  :: np
        2226             :     integer              , intent(in)  :: intervals
        2227             :     real (kind=r8)              :: values(intervals,intervals)
        2228             :
        2229             :
        2230             :     real(kind=r8), parameter :: zero = 0.0_r8, one=1.0_r8, two=2.0_r8
        2231             :
        2232             :
        2233        3072 :     real (kind=r8) :: sub_gll        (intervals,np)
        2234             :
        2235        3072 :     real (kind=r8) :: Lagrange_interp(intervals,np,np)
        2236             :     type (quadrature_t)   :: gll
        2237             :
        2238        3072 :     real (kind=r8) :: legrange_div(np)
        2239             :     real (kind=r8) :: a,b,x,y, x_j, x_i
        2240             :     real (kind=r8) :: r(1)
        2241             :     integer i,j,n,m
        2242             :
        2243        1536 :     if (ALLOCATED(integration_matrix)) deallocate(integration_matrix)
        2244        6144 :     allocate(integration_matrix(intervals,np))
        2245        1536 :     if (ALLOCATED(boundary_interp_matrix)) deallocate(boundary_interp_matrix)
        2246        6144 :     allocate(boundary_interp_matrix(intervals,2,np))
        2247             :
        2248        1536 :     gll = gausslobatto(np)
        2249             :
        2250             :     ! The GLL (Gauss-Lobatto-Legendre) points are from [-1,1],
        2251             :     ! we have a bunch of sub-intervals defined by intervals that
        2252             :     ! go from [a,b] so we need to linearly map [-1,1] -> [a,b]
        2253             :     ! all the  GLL points by  y = (a/2)(1-x) + (b/2)(1+x)
        2254        6144 :     do i=1,intervals
        2255        4608 :       a = -one + (i-one)*two/intervals
        2256        4608 :       b = -one +  i     *two/intervals
        2257       24576 :       sub_gll(i,:) = (a+b)/two + gll%points(:)/intervals
        2258             :     end do
        2259             :
        2260             :     ! Now to interpolate from the values at the input GLL
        2261             :     ! points to the sub-GLL points.  Do this by Lagrange
        2262             :     ! interpolation.  The jth Lagrange interpolating polynomial
        2263             :     ! for points x_i is
        2264             :     !              \prod_{i\ne j} (x-x_i)/(x_j-x_i)
        2265             :     ! These are then multiplied by the sampled values y_i
        2266             :     ! and summed.
        2267             :
        2268             :     ! Save some time by pre-computing the denominitor. I think
        2269             :     ! this is OK since all the points are of order 1 so should
        2270             :     ! be well behaved.
        2271        7680 :     do n = 1,np
        2272        6144 :       x_j = gll%points(n)
        2273        6144 :       x   = one
        2274       30720 :       do m = 1,np
        2275       30720 :         if (m.ne.n) then
        2276       18432 :           x_i = gll%points(m)
        2277       18432 :           x = x * (x_j-x_i)
        2278             :         endif
        2279             :       end do
        2280        7680 :       legrange_div(n)= x
        2281             :     end do
        2282        6144 :     do i=1,intervals
        2283       24576 :       do n=1,np
        2284       18432 :         x = sub_gll(i,n)
        2285       96768 :         do j = 1,np
        2286             :           y = one
        2287      368640 :           do m = 1,np
        2288      368640 :             if (m.ne.j) then
        2289      221184 :               x_i = gll%points(m)
        2290      221184 :               y = y * (x-x_i)
        2291             :             end if
        2292             :           end do
        2293       92160 :           Lagrange_interp(i,n,j) = y/legrange_div(j)
        2294             :         end do
        2295             :       end do
        2296             :     end do
        2297             :
        2298             :     ! Integration is the GLL weights times Jacobians times
        2299             :     ! the interpolated values:
        2300             :     !                   w^t I Y I^t w
        2301             :     ! where
        2302             :     ! w is GLL weights and Jacobians,
        2303             :     ! I is the Lagrange_interp matrix, and
        2304             :     ! Y is the coefficient matrix, sampled_val.
        2305             :     ! This can be written  J Y J^t where
        2306             :     !                       J = w^t I
        2307             :     ! J is integration_matrix
        2308        6144 :     do i=1,intervals
        2309       24576 :       integration_matrix(i,:) = MATMUL(gll%weights(:),Lagrange_interp(i,:,:))
        2310             :     end do
        2311             :
        2312             :     ! There is still the Jacobian to consider.  We are
        2313             :     ! integrating over [a,b] x [c,d] where
        2314             :     !        |b-a| = |d-c| = 2/Intervals
        2315             :     ! Multiply the weights appropriately given that
        2316             :     ! they are defined for a 2x2 square
        2317       26112 :     integration_matrix = integration_matrix/intervals
        2318             :
        2319       26112 :     boundary_interp_matrix(:,1,:) = Lagrange_interp(:,1,:)
        2320       26112 :     boundary_interp_matrix(:,2,:) = Lagrange_interp(:,np,:)
        2321        1536 :   end subroutine allocate_subcell_integration_matrix_cslam
        2322             :
        2323        1536 :   subroutine allocate_subcell_integration_matrix_physgrid(np, intervals)
        2324             :     !-----------------
        2325             :     !-----------------
        2326             :     use quadrature_mod, only : gausslobatto, quadrature_t
        2327             :
        2328             :     implicit none
        2329             :
        2330             :     integer              , intent(in)  :: np
        2331             :     integer              , intent(in)  :: intervals
        2332             :     real (kind=r8)              :: values(intervals,intervals)
        2333             :
        2334             :
        2335             :     real(kind=r8), parameter :: zero = 0.0_r8, one=1.0_r8, two=2.0_r8
        2336             :
        2337             :
        2338        3072 :     real (kind=r8) :: sub_gll        (intervals,np)
        2339             :
        2340        3072 :     real (kind=r8) :: Lagrange_interp(intervals,np,np)
        2341             :     type (quadrature_t)   :: gll
        2342             :
        2343        3072 :     real (kind=r8) :: legrange_div(np)
        2344             :     real (kind=r8) :: a,b,x,y, x_j, x_i
        2345             :     real (kind=r8) :: r(1)
        2346             :     integer i,j,n,m
        2347             :
        2348        1536 :     if (ALLOCATED(integration_matrix_physgrid)) deallocate(integration_matrix_physgrid)
        2349        6144 :     allocate(integration_matrix_physgrid(intervals,np))
        2350             :
        2351        1536 :     gll = gausslobatto(np)
        2352             :
        2353             :     ! The GLL (Gauss-Lobatto-Legendre) points are from [-1,1],
        2354             :     ! we have a bunch of sub-intervals defined by intervals that
        2355             :     ! go from [a,b] so we need to linearly map [-1,1] -> [a,b]
        2356             :     ! all the  GLL points by  y = (a/2)(1-x) + (b/2)(1+x)
        2357        6144 :     do i=1,intervals
        2358        4608 :       a = -one + (i-one)*two/intervals
        2359        4608 :       b = -one +  i     *two/intervals
        2360       24576 :       sub_gll(i,:) = (a+b)/two + gll%points(:)/intervals
        2361             :     end do
        2362             :
        2363             :     ! Now to interpolate from the values at the input GLL
        2364             :     ! points to the sub-GLL points.  Do this by Lagrange
        2365             :     ! interpolation.  The jth Lagrange interpolating polynomial
        2366             :     ! for points x_i is
        2367             :     !              \prod_{i\ne j} (x-x_i)/(x_j-x_i)
        2368             :     ! These are then multiplied by the sampled values y_i
        2369             :     ! and summed.
        2370             :
        2371             :     ! Save some time by pre-computing the denominitor. I think
        2372             :     ! this is OK since all the points are of order 1 so should
        2373             :     ! be well behaved.
        2374        7680 :     do n = 1,np
        2375        6144 :       x_j = gll%points(n)
        2376        6144 :       x   = one
        2377       30720 :       do m = 1,np
        2378       30720 :         if (m.ne.n) then
        2379       18432 :           x_i = gll%points(m)
        2380       18432 :           x = x * (x_j-x_i)
        2381             :         endif
        2382             :       end do
        2383        7680 :       legrange_div(n)= x
        2384             :     end do
        2385        6144 :     do i=1,intervals
        2386       24576 :       do n=1,np
        2387       18432 :         x = sub_gll(i,n)
        2388       96768 :         do j = 1,np
        2389             :           y = one
        2390      368640 :           do m = 1,np
        2391      368640 :             if (m.ne.j) then
        2392      221184 :               x_i = gll%points(m)
        2393      221184 :               y = y * (x-x_i)
        2394             :             end if
        2395             :           end do
        2396       92160 :           Lagrange_interp(i,n,j) = y/legrange_div(j)
        2397             :         end do
        2398             :       end do
        2399             :     end do
        2400        6144 :     do i=1,intervals
        2401       24576 :       integration_matrix_physgrid(i,:) = MATMUL(gll%weights(:),Lagrange_interp(i,:,:))
        2402             :     end do
        2403       26112 :     integration_matrix_physgrid = integration_matrix_physgrid/intervals
        2404        1536 :   end subroutine allocate_subcell_integration_matrix_physgrid
        2405             :
        2406             :
        2407             :
        2408           0 :   subroutine limiter_optim_iter_full(ptens,sphweights,minp,maxp,dpmass,kbeg,kend)
        2409             :     !
        2410             :     !The idea here is the following: We need to find a grid field which is closest
        2411             :     !to the initial field (in terms of weighted sum), but satisfies the min/max constraints.
        2412             :     !So, first we find values which do not satisfy constraints and bring these values
        2413             :     !to a closest constraint. This way we introduce some mass change (addmass),
        2414             :     !so, we redistribute addmass in the way that l2 error is smallest.
        2415             :     !This redistribution might violate constraints thus, we do a few iterations.
        2416             :     !
        2417             :     ! O. Guba ~2012                    Documented in Guba, Taylor & St-Cyr, JCP 2014
        2418             :     ! I. Demeshko & M. Taylor 7/2015:  Removed indirect addressing.
        2419             :     ! N. Lopez & M. Taylor 8/2015:     Mass redistributon tweak which is better at
        2420             :     !                                  linear coorelation preservation
        2421             :     !
        2422             :     use dimensions_mod, only : np, np, nlev
        2423             :
        2424             :     real (kind=r8), dimension(nlev), intent(inout)   :: minp, maxp
        2425             :     real (kind=r8), dimension(np*np,nlev), intent(inout)   :: ptens
        2426             :     real (kind=r8), dimension(np*np,nlev), intent(in), optional  :: dpmass
        2427             :     real (kind=r8), dimension(np*np), intent(in)   :: sphweights
        2428             :     integer, intent(in) :: kbeg, kend
        2429             :
        2430             :     real (kind=r8), dimension(np,np) :: ptens_mass
        2431             :     integer  k1, k, i, j, iter, weightsnum
        2432             :     real (kind=r8) :: addmass, weightssum, mass, sumc
        2433             :     real (kind=r8) :: x(np*np),c(np*np)
        2434             :     integer :: maxiter = np*np-1
        2435             :     real (kind=r8) :: tol_limiter = 5.0e-14_r8
        2436             :
        2437           0 :     do k = kbeg, kend
        2438             :
        2439           0 :      do k1=1,np*np
        2440           0 :        c(k1)=sphweights(k1)*dpmass(k1,k)
        2441           0 :        x(k1)=ptens(k1,k)/dpmass(k1,k)
        2442             :      enddo
        2443             :
        2444           0 :      sumc=sum(c)
        2445           0 :      if (sumc <= 0 ) CYCLE   ! this should never happen, but if it does, dont limit
        2446           0 :      mass=sum(c*x)
        2447             :
        2448             :
        2449             :
        2450             :       ! relax constraints to ensure limiter has a solution:
        2451             :       ! This is only needed if runnign with the SSP CFL>1 or
        2452             :       ! due to roundoff errors
        2453           0 :       if( mass < minp(k)*sumc ) then
        2454           0 :         minp(k) = mass / sumc
        2455             :       endif
        2456           0 :       if( mass > maxp(k)*sumc ) then
        2457           0 :         maxp(k) = mass / sumc
        2458             :       endif
        2459             :
        2460             :
        2461             :
        2462           0 :      do iter=1,maxiter
        2463             :
        2464             :       addmass=0.0d0
        2465             :
        2466           0 :        do k1=1,np*np
        2467           0 :          if((x(k1)>maxp(k))) then
        2468           0 :            addmass=addmass+(x(k1)-maxp(k))*c(k1)
        2469           0 :            x(k1)=maxp(k)
        2470             :          endif
        2471           0 :          if((x(k1)<minp(k))) then
        2472           0 :            addmass=addmass-(minp(k)-x(k1))*c(k1)
        2473           0 :            x(k1)=minp(k)
        2474             :          endif
        2475             :        enddo !k1
        2476             :
        2477           0 :        if(abs(addmass)<=tol_limiter*abs(mass)) exit
        2478             :
        2479           0 :        weightssum=0.0d0
        2480           0 :        if(addmass>0)then
        2481           0 :         do k1=1,np*np
        2482           0 :           if(x(k1)<maxp(k))then
        2483           0 :             weightssum=weightssum+c(k1)
        2484             :           endif
        2485             :         enddo !k1
        2486           0 :         do k1=1,np*np
        2487           0 :           if(x(k1)<maxp(k))then
        2488           0 :               x(k1)=x(k1)+addmass/weightssum
        2489             :           endif
        2490             :         enddo
        2491             :       else
        2492           0 :         do k1=1,np*np
        2493           0 :           if(x(k1)>minp(k))then
        2494           0 :             weightssum=weightssum+c(k1)
        2495             :           endif
        2496             :         enddo
        2497           0 :         do k1=1,np*np
        2498           0 :           if(x(k1)>minp(k))then
        2499           0 :             x(k1)=x(k1)+addmass/weightssum
        2500             :           endif
        2501             :         enddo
        2502             :       endif
        2503             :
        2504             :
        2505             :    enddo!end of iteration
        2506             :
        2507           0 :    do k1=1,np*np
        2508           0 :       ptens(k1,k)=x(k1)
        2509             :    enddo
        2510             :
        2511             :   enddo
        2512             :
        2513           0 :   do k = kbeg, kend
        2514           0 :     do k1=1,np*np
        2515           0 :       ptens(k1,k)=ptens(k1,k)*dpmass(k1,k)
        2516             :     enddo
        2517             :   enddo
        2518             :
        2519           0 :   end subroutine limiter_optim_iter_full
        2520             :
        2521             :
        2522             :
        2523             :
        2524             :
        2525           0 : end module derivative_mod

Generated by: LCOV version 1.14