LCOV - code coverage report
Current view: top level - dynamics/fv - trac2d.F90 (source / functions) Hit Total Coverage
Test: coverage.info Lines: 160 162 98.8 %
Date: 2025-03-14 01:23:43 Functions: 1 1 100.0 %

          Line data    Source code
       1             : !-----------------------------------------------------------------------
       2             : !BOP
       3             : ! !ROUTINE: trac2d --- Remap Lagrangian to fixed coordinates
       4             : !
       5             : ! !INTERFACE:
       6       64512 :  subroutine trac2d( grid,    dp1,  tracer, cx,     cy,      &
       7       64512 :                     mfx,     mfy,   iord,  jord,   fill,    &
       8       64512 :                     nlo,     nhi,   va,    flx )
       9             :  
      10             : ! !USES:
      11             : 
      12             :    use shr_kind_mod, only  : r8 => shr_kind_r8, r4 => shr_kind_r4
      13             :    use tp_core, only       : tp2c
      14             :    use fill_module, only   : fillxy
      15             :    use dynamics_vars, only : T_FVDYCORE_GRID
      16             :    use FVperf_module, only : FVstartclock, FVstopclock, FVbarrierclock
      17             : 
      18             : #if defined( SPMD )
      19             :    use parutilitiesmodule, only: maxop, parcollective
      20             :    use mod_comm, only : mp_send4d_ns, mp_recv4d_ns,  &
      21             :                         mp_send4d_ns_r4, mp_recv4d_ns_r4,        &
      22             :                         mp_send3d_2, mp_recv3d_2
      23             : #endif
      24             : 
      25             :    implicit none
      26             : 
      27             : ! !INPUT PARAMETERS:
      28             : 
      29             :    type (T_FVDYCORE_GRID), intent(inout) :: grid
      30             :    integer, intent(in):: iord,  jord
      31             : 
      32             :    logical, intent(in):: fill
      33             :    integer, intent(in):: nlo,  nhi   ! Tracer index range
      34             : 
      35             : ! !INPUT/OUTPUT PARAMETERS:
      36             :    real(r8), intent(inout):: dp1(grid%im,grid%jfirst:grid%jlast,grid%kfirst:grid%klast)
      37             :    real(r8), intent(inout):: cx(grid%im,grid%jfirst-grid%ng_d:grid%jlast+grid%ng_d,grid%kfirst:grid%klast)
      38             :    real(r8), intent(inout):: cy(grid%im,grid%jfirst:grid%jlast+1,grid%kfirst:grid%klast)
      39             :    real(r8), intent(inout):: mfx(grid%im,grid%jfirst:grid%jlast,grid%kfirst:grid%klast)
      40             :    real(r8), intent(inout):: mfy(grid%im,grid%jfirst:grid%jlast+1,grid%kfirst:grid%klast)
      41             :    real(r8), intent(inout):: tracer(grid%im,grid%jfirst:grid%jlast,grid%kfirst:grid%klast,grid%ntotq)
      42             : 
      43             : ! !OUTPUT PARAMETERS:
      44             :    real(r8), intent(out):: va(grid%im,grid%jfirst:grid%jlast,grid%kfirst:grid%klast)
      45             :    real(r8), intent(out):: flx(grid%im,grid%jfirst:grid%jlast,grid%kfirst:grid%klast)
      46             : 
      47             : ! !DESCRIPTION:
      48             : !
      49             : !  Perform large-time-step tracer transport using accumulated Courant
      50             : !  numbers (cx, cy) and the mass fluxes (mfx, mfy) within the Lagrangian
      51             : !  layers.  This routine is 100\% parallel in the vertical direction
      52             : !  (with SMP).  Merdional Courant number will be further split, if
      53             : !  necessary, to ensure stability.  Cy <= 1 away from poles; Cy $\le$
      54             : !  1/2 at the latitudes closest to the poles.
      55             : !
      56             : ! !REVISION HISTORY:
      57             : !
      58             : !   SJL 99.04.13:  Delivery
      59             : !   WS  99.05.26:  Added jfirst:jlast concept; im, jm, km as parameters
      60             : !                  replaced IMR, JMR, JNP, NL with IM, JM-1, JM and KM
      61             : !   WS  99.09.27:  Documentation; indentation; jfirst:jlast 
      62             : !   WS  99.09.30:  Ghosting; loop limits; full parallelization; tested
      63             : !   SJL 99.10.15:  nsplt migrated to outermost loop to remove bug
      64             : !   SJL 99.12.19:  Local 2D arrays trimmed!
      65             : !   WS  00.05.14:  Renamed ghost indices as per Kevin's definitions
      66             : !   WS  00.07.13:  Changed PILGRIM API
      67             : !   AAM 00.08.29:  Added kfirst, klast
      68             : !   AAM 01.06.27:  Added y communicators
      69             : !   SJL 30.07.01:  MPI optimization/simplification
      70             : !   WS  02.04.24:  New mod_comm interfaces
      71             : !   WS  02.07.04:  Fixed 2D decomposition bug dest/src for mp_send3d
      72             : !   WS  03.11.19:  Merged in CAM changes by Mirin
      73             : !   WS  03.12.03:  Added GRID as argument, dynamics_vars removed
      74             : !   WS  04.08.25:  Simplification of interface with GRID
      75             : !   WS  04.10.07:  Removed dependency on spmd_dyn; info now in GRID
      76             : !   WS  05.04.04:  Transitioned to type T_TRACERS (supports r4 and r8)
      77             : !   WS  05.04.09:  Each tracer now ghosted individually (save buffers)
      78             : !   WS  05.04.12:  Full support for either r4 or r8 tracers
      79             : !   WS  05.05.25:  Merged CAM and GEOS5, e.g. nsplt(k) opt. in CAM
      80             : !   PW  05.10.12:  Changes for Cray X1(E), alternative implementation
      81             : !                  of double buffering logic
      82             : !   WS  06.09.08:  Magic numbers are now F90 parameters
      83             : !
      84             : !EOP
      85             : !---------------------------------------------------------------------
      86             : !BOC
      87             : 
      88             :    real(r8), parameter ::  D1EM10                  =  1.0e-10_r8
      89             :    real(r8), parameter ::  D1_0                    =  1.0_r8
      90             :    real(r8), parameter ::  D0_0                    =  0.0_r8
      91             : 
      92             : ! Local variables:
      93             : ! 2d arrays
      94      129024 :    real(r8)  a2(grid%im,grid%jfirst:grid%jlast)
      95      129024 :    real(r8)  fx(grid%im,grid%jfirst:grid%jlast)
      96      129024 :    real(r8)  fy(grid%im,grid%jfirst:grid%jlast+1)
      97      129024 :    real(r8)  cymax(grid%kfirst:grid%klast)
      98             : ! Temporary r8 array for Q
      99             :    real(r8)  ::  &
     100      129024 :       q_r8(grid%im,grid%jfirst-grid%ng_d:grid%jlast+grid%ng_d,grid%kfirst:grid%klast,1:2)
     101             : 
     102      129024 :    real(r8) dp2(grid%im,grid%jfirst:grid%jlast,grid%kfirst:grid%klast)
     103      129024 :    logical ffsl(grid%jm,grid%kfirst:grid%klast)
     104      129024 :    integer :: nsplt(grid%kfirst:grid%klast)
     105             : 
     106             :    integer :: im, jm, km                    ! Dimensions
     107             :    integer :: ng                            ! Max number of ghost latitudes
     108             :    integer :: jfirst, jlast, kfirst, klast  ! YZ decomposition limits
     109             :    integer :: cur, nxt                      ! current and next q_r8 buffer indices
     110             : 
     111             :    integer i, j, k
     112             :    integer it, iq, kq, max_nsplt
     113             :    integer :: k_courant, kend
     114             :    integer ktot
     115             :    integer js1gd, js2g0, js2gd, jn2g0,jn2gd,jn1g1,jn1gd
     116             : #if defined( SPMD )
     117             :    integer :: dest, src
     118             : #endif
     119             : 
     120             :    real(r8) cy_global
     121             :    real(r8) frac
     122             :    real(r8) cmax
     123             :    real(r8) sum1, sum2
     124             : 
     125       64512 :    cur     = 1
     126       64512 :    nxt     = 2
     127             : 
     128       64512 :    im      = grid%im
     129       64512 :    jm      = grid%jm
     130       64512 :    km      = grid%km
     131       64512 :    ng      = grid%ng_d
     132             : 
     133       64512 :    jfirst  = grid%jfirst
     134       64512 :    jlast   = grid%jlast
     135       64512 :    kfirst  = grid%kfirst
     136       64512 :    klast   = grid%klast
     137             : 
     138       64512 :    ktot  = klast - kfirst + 1
     139       64512 :    js2g0 = max(2,jfirst)
     140       64512 :    jn2g0 = min(jm-1,jlast)
     141       64512 :    jn1g1 = min(jm,jlast+1)
     142       64512 :    js1gd = max(1,jfirst-ng)     ! NG latitudes on S (starting at 1)
     143       64512 :    js2gd = max(2,jfirst-ng)     ! NG latitudes on S (starting at 2)
     144       64512 :    jn2gd = min(jm-1,jlast+ng)   ! NG latitudes on S (ending at jm-1)
     145       64512 :    jn1gd = min(jm,jlast+ng)     ! NG latitudes on N (ending at jm)
     146             : 
     147             : #if defined( SPMD )
     148       64512 :       call FVstartclock(grid,'---TRAC2D_COMM')
     149             :       call mp_send4d_ns( grid%commyz, im, jm, km,                      &
     150             :                          1, jfirst, jlast, kfirst, klast,             &
     151       64512 :                          ng, ng, cx )
     152             : ! Send one latitude of both cy and mfy to the south
     153       64512 :       dest = grid%iam-1
     154       64512 :       src  = grid%iam+1
     155       64512 :       if ( mod(grid%iam,grid%npr_y) == 0 ) dest = -1
     156       64512 :       if ( mod(grid%iam+1,grid%npr_y) == 0 ) src = -1
     157             :       call mp_send3d_2( grid%commyz, dest, src, im, jm, km,            &
     158             :                         1, im, jfirst, jlast+1, kfirst, klast,        &
     159       64512 :                         1, im, jfirst, jfirst, kfirst, klast, cy, mfy)
     160       64512 :       call FVstopclock(grid,'---TRAC2D_COMM')
     161             : #endif
     162             : 
     163             : !$omp parallel do default(shared) private(i,j,k,cmax)
     164      440832 :    do k=kfirst,klast
     165      376320 :         cymax(k) = D0_0
     166     1563912 :        do j=js2g0,jlast
     167     1123080 :             cmax = D0_0
     168   324570120 :           do i=1,im
     169   324570120 :             cmax = max( abs(cy(i,j,k)), cmax)
     170             :           enddo
     171     1499400 :             cymax(k) = max(cymax(k), cmax*(D1_0 + grid%sine(j)**16) )
     172             :        enddo
     173             :    enddo
     174             : 
     175             : #if defined( SPMD )
     176       64512 :    call FVstartclock(grid,'---TRAC2D_COMM')
     177             :    call mp_recv4d_ns( grid%commyz, im, jm, km,                         &
     178             :                       1, jfirst, jlast, kfirst, klast,                &
     179       64512 :                       ng, ng, cx )
     180             :    call mp_recv3d_2( grid%commyz, src, im, jm, km,                     &
     181             :                      1, im, jfirst, jlast+1, kfirst, klast,           &
     182       64512 :                      1, im, jlast+1, jlast+1, kfirst, klast, cy, mfy)
     183             : 
     184       64512 :    call parcollective( grid%comm_y, MAXOP, ktot, cymax )
     185       64512 :    call FVstopclock(grid,'---TRAC2D_COMM')
     186             : #endif
     187             : 
     188             : !---------------------------------------------------------------------
     189             : ! Determine the required value of nsplt for each level
     190             : !---------------------------------------------------------------------
     191      440832 :     nsplt(:)  = int( D1_0 + cymax(:) )
     192      440832 :     max_nsplt = maxval( nsplt(:) )
     193             : #if defined( SPMD )
     194       64512 :     call FVstartclock(grid,'---TRAC2D_COMM')
     195       64512 :     call parcollective( grid%comm_z, MAXOP, max_nsplt )  ! Find global max
     196       64512 :     call FVstopclock(grid,'---TRAC2D_COMM')
     197             : #endif
     198       64512 :     if (grid%ptop>1._r8) then
     199           0 :        nsplt(:)  = max_nsplt
     200             :     endif
     201      417792 :     do k_courant = klast,kfirst,-1
     202      417792 :        if( nsplt(k_courant) > 1 ) then
     203             :           exit
     204             :        end if
     205             :     end do
     206       64512 :     k_courant = max( kfirst,k_courant )
     207             : !!!    if (max_nsplt /= 1) write(iulog,*) 'trac2d: max_nsplt,k_courant = ', max_nsplt,k_courant
     208             : !!!    write(iulog,*) "max_nsplt", max_nsplt, "k_cour", k_courant, "nsplt", nsplt(:)
     209             : 
     210             : !$omp  parallel do default(shared) private(i,j,k,frac) schedule(dynamic,1)
     211             : 
     212             : #if !defined(USE_OMP)
     213             : !CSD$ PARALLEL DO PRIVATE (I, J, K, FRAC)
     214             : #endif
     215      440832 :  do 4000 k=kfirst,klast
     216             : 
     217      376320 :     if( nsplt(k) .ne. 1 ) then
     218       23040 :        frac = D1_0 / nsplt(k)
     219      226800 :        do j=js2gd,jn2gd                  
     220    58909680 :           do i=1,im
     221    58886640 :             cx(i,j,k) =  cx(i,j,k) * frac      ! cx ghosted on N*ng S*ng
     222             :           enddo
     223             :        enddo
     224             : 
     225       91440 :        do j=js2g0,jn2g0
     226    19790640 :           do i=1,im
     227    19767600 :             mfx(i,j,k) = mfx(i,j,k) * frac
     228             :           enddo
     229             :        enddo
     230             : 
     231      114480 :        do j=js2g0,jn1g1                     
     232    26449200 :           do i=1,im
     233    26334720 :              cy(i,j,k) =  cy(i,j,k) * frac    ! cy ghosted on N
     234    26426160 :             mfy(i,j,k) = mfy(i,j,k) * frac    ! mfy ghosted on N
     235             :           enddo
     236             :        enddo
     237             :     endif
     238             : 
     239     1493520 :        do j=js2g0,jn2g0
     240   323247120 :           do i=1,im
     241   322870800 :              if(cy(i,j,k)*cy(i,j+1,k) > D0_0) then
     242   307823709 :                 if( cy(i,j,k) > D0_0) then
     243   157313008 :                    va(i,j,k) = cy(i,j,k)
     244             :                 else
     245   150510701 :                    va(i,j,k) = cy(i,j+1,k)      ! cy ghosted on N
     246             :                 endif
     247             :              else
     248    13929891 :                 va(i,j,k) = D0_0
     249             :              endif
     250             :           enddo
     251             :        enddo
     252             : 
     253             : ! Check if FFSL extension is needed.
     254             : 
     255     3704400 :        do j=js2gd,jn2gd             ! flux needed on N*ng S*ng
     256     3328080 :           ffsl(j,k) = .false.
     257   932492467 :           do i=1,im
     258   932116147 :             if( abs(cx(i,j,k)) > D1_0 ) then  ! cx ghosted on N*ng S*ng
     259      128662 :               ffsl(j,k) = .true.
     260      128662 :               exit
     261             :             endif
     262             :           enddo
     263             :        enddo
     264             : 
     265             : ! Scale E-W mass fluxes by CX if FFSL
     266     1493520 :        do j=js2g0,jn2g0
     267     1493520 :           if( ffsl(j,k) ) then
     268    13987600 :             do i=1,im
     269    27878400 :               flx(i,j,k) = mfx(i,j,k) / sign( max(abs(cx(i,j,k)), D1EM10), &
     270    27926800 :                                         cx(i,j,k) )
     271             :             enddo
     272             :           else
     273   308883200 :             do i=1,im
     274   308883200 :               flx(i,j,k) = mfx(i,j,k)
     275             :             enddo
     276             :           endif
     277             :        enddo
     278       64512 : 4000  continue
     279             : #if !defined(USE_OMP)
     280             : !CSD$ END PARALLEL DO
     281             : #endif
     282             : 
     283       64512 :  call FVbarrierclock(grid,'sync_trac2d_tracer',grid%commyz)
     284       64512 :  call FVstartclock(grid,'---TRAC2D_TRACER')
     285             : 
     286      268032 :  do 6000 it=1, max_nsplt
     287      203520 :     if ( it == 1 ) then
     288       64512 :        kend = klast       !  The entire vertical slab needs to be considered
     289             :     else
     290      139008 :        kend = k_courant   !  Only the subset including courant # > 1 considered
     291             :     endif
     292             : ! WS 05.04.06:  send only the first tracer the rest at end of do iq loop
     293             : !               NOTE: there is per definition at least one tracer
     294           0 :     q_r8(1:im,jfirst:jlast,kfirst:kend,1) = &
     295   480894976 :                tracer(1:im,jfirst:jlast,kfirst:kend,nlo)
     296             : #if defined( SPMD )
     297      203520 :     call FVstartclock(grid,'---TRAC2D_TRACER_COMM')
     298             :     call mp_send4d_ns( grid%commyz, im, jm, km,                        &
     299             :                        1, jfirst, jlast, kfirst, kend,                &
     300      203520 :                        ng, ng, q_r8(1,jfirst-ng,kfirst,1) )
     301      203520 :     call FVstopclock(grid,'---TRAC2D_TRACER_COMM')
     302             : #endif
     303             : 
     304             : !$omp parallel do default(shared) private(i,j,k,sum1,sum2)
     305             : 
     306      757312 :   do 3000 k=kfirst,kend
     307      553792 :      if (it <= nsplt(k)) then
     308     1629156 :         do j=js2g0,jn2g0
     309   350974080 :            do i=1,im-1
     310  1049266260 :               dp2(i,j,k) =  dp1(i,j,k) + mfx(i,j,k) - mfx(i+1,j,k) +  &
     311  1400240340 :                         (mfy(i,j,k) - mfy(i,j+1,k)) * grid%acosp(j)
     312             :            enddo
     313     2437320 :            dp2(im,j,k) = dp1(im,j,k) + mfx(im,j,k) - mfx(1,j,k) +  &
     314     4066476 :                          (mfy(im,j,k) - mfy(im,j+1,k)) * grid%acosp(j)
     315             :         enddo
     316             : 
     317      410496 :         if ( jfirst == 1  ) then
     318        6414 :            sum1 = D0_0
     319     1853646 :            do i=1,im
     320     1853646 :               sum1 = sum1 + mfy(i,2,k)
     321             :            end do
     322             : 
     323        6414 :            sum1 = - sum1 * grid%rcap
     324     1853646 :            do i=1,im
     325     1853646 :               dp2(i,1,k) = dp1(i,1,k) +  sum1
     326             :            enddo
     327             :         endif
     328             : 
     329      410496 :         if ( jlast == jm ) then
     330        6414 :            sum2 = D0_0
     331     1853646 :            do i=1,im
     332     1853646 :               sum2 = sum2 + mfy(i,jm,k)
     333             :            end do
     334             : 
     335        6414 :               sum2 = sum2 * grid%rcap
     336     1853646 :            do i=1,im
     337     1853646 :               dp2(i,jm,k) = dp1(i,jm,k) +  sum2
     338             :            enddo
     339             :         endif
     340             :      endif
     341      203520 : 3000  continue
     342             : 
     343    41314560 :       do iq = nlo, nhi
     344             : #if defined( SPMD )
     345    41111040 :          call FVstartclock(grid,'---TRAC2D_TRACER_COMM')
     346             : !
     347             : ! The buffer indices are exchanged, so that cur points to the current buffer,
     348             : ! while nxt points to the one which will be used next.
     349             : !
     350    41111040 :         if ( mod(iq-nlo+1,2) == 0 ) then
     351             :           cur = 2
     352             :           nxt = 1
     353             :         else
     354    20555520 :           cur = 1
     355    20555520 :           nxt = 2
     356             :         endif
     357             :         call mp_recv4d_ns( grid%commyz, im, jm, km,                    &
     358             :                            1, jfirst, jlast, kfirst, kend,            &
     359    41111040 :                            ng, ng, q_r8(1,jfirst-ng,kfirst,cur) )
     360             : 
     361             : !
     362             : ! Pre-send the next tracer
     363             : !
     364    41111040 :         if ( iq < nhi ) then
     365    40907520 :           q_r8(1:im,jfirst:jlast,kfirst:kend,nxt) = &
     366 96700797696 :              tracer(1:im,jfirst:jlast,kfirst:kend,iq+1)
     367             :           call mp_send4d_ns( grid%commyz, im, jm, km,                  &
     368             :                              1, jfirst, jlast, kfirst, kend,          &
     369    40907520 :                              ng, ng, q_r8(1,jfirst-ng,kfirst,nxt) )
     370             :         endif
     371    41111040 :         call FVstopclock(grid,'---TRAC2D_TRACER_COMM')
     372             : #else
     373             : !
     374             : ! No message passing -- simply copy the tracer into q_r8
     375             : !
     376             :         q_r8(1:im,jfirst:jlast,kfirst:kend,cur) = &
     377             :               tracer(1:im,jfirst:jlast,kfirst:kend,iq)
     378             : #endif
     379             : 
     380             : #if (!defined USE_OMP) 
     381             : !CSD$ PARALLEL DO PRIVATE (I, J, K, KQ, FX, FY, A2)
     382             : #endif
     383   153180544 :         do 5000 k=kfirst,kend
     384   111865984 :            if ( it <= nsplt(k) ) then
     385    82920192 :               call tp2c(a2, va(1,jfirst,k), q_r8(1:,jfirst-ng:,k,cur),  &
     386    82920192 :                         cx(1,jfirst-ng,k) , cy(1,jfirst,k),           &
     387             :                         im, jm, iord, jord, ng,                       &
     388    82920192 :                         fx, fy, ffsl(1,k), grid%rcap, grid%acosp,     &
     389             :                         flx(1,jfirst,k), mfy(1,jfirst,k),             &
     390   248760576 :                         grid%cosp, 1, jfirst, jlast )
     391             : 
     392   331680768 :               do j=jfirst,jlast
     393 71974726656 :                  do i=1,im
     394 71891806464 :                     q_r8(i,j,k,cur) = q_r8(i,j,k,cur)*dp1(i,j,k) + a2(i,j)
     395             :                  enddo
     396             :               enddo
     397             : 
     398    82920192 :               if (fill) call fillxy (q_r8(1:,jfirst:,k,cur), im, jm, jfirst, &
     399    82920192 :                                      jlast, grid%acap, grid%cosp, grid%acosp)
     400             : 
     401   331680768 :               do j=jfirst,jlast
     402 71974726656 :                  do i=1,im
     403 71891806464 :                     tracer(i,j,k,iq) = q_r8(i,j,k,cur) / dp2(i,j,k)
     404             :                  enddo
     405             :               enddo
     406             :            endif
     407    41111040 : 5000    continue
     408             : #if (!defined USE_OMP) 
     409             : !CSD$ END PARALLEL DO
     410             : #endif
     411             : 
     412             :       enddo  ! End of do iq=nlo, nhi
     413             : 
     414             : !$omp parallel do private(i, j, k) schedule( dynamic,1 )
     415      757312 :       do k=kfirst,kend
     416      757312 :          if ( it < nsplt(k) ) then
     417      136704 :             do j=jfirst,jlast
     418    29664768 :                do i=1,im
     419    29630592 :                   dp1(i,j,k) = dp2(i,j,k)
     420             :                enddo
     421             :             enddo
     422             :          endif
     423             :       enddo
     424             : 
     425       64512 : 6000  continue
     426       64512 :  call FVstopclock(grid,'---TRAC2D_TRACER')
     427             : 
     428       64512 :       return
     429             : !EOC
     430       64512 :  end subroutine trac2d
     431             : !-----------------------------------------------------------------------
     432             : 
     433             : 

Generated by: LCOV version 1.14