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

          Line data    Source code
       1             : !-----------------------------------------------------------------------
       2             : !BOP
       3             : ! !ROUTINE: trac2d --- Remap Lagrangian to fixed coordinates
       4             : !
       5             : ! !INTERFACE:
       6       32256 :  subroutine trac2d( grid,    dp1,  tracer, cx,     cy,      &
       7       32256 :                     mfx,     mfy,   iord,  jord,   fill,    &
       8       32256 :                     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       64512 :    real(r8)  a2(grid%im,grid%jfirst:grid%jlast)
      95       64512 :    real(r8)  fx(grid%im,grid%jfirst:grid%jlast)
      96       64512 :    real(r8)  fy(grid%im,grid%jfirst:grid%jlast+1)
      97       64512 :    real(r8)  cymax(grid%kfirst:grid%klast)
      98             : ! Temporary r8 array for Q
      99             :    real(r8)  ::  &
     100       64512 :       q_r8(grid%im,grid%jfirst-grid%ng_d:grid%jlast+grid%ng_d,grid%kfirst:grid%klast,1:2)
     101             : 
     102       64512 :    real(r8) dp2(grid%im,grid%jfirst:grid%jlast,grid%kfirst:grid%klast)
     103       64512 :    logical ffsl(grid%jm,grid%kfirst:grid%klast)
     104       64512 :    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       32256 :    cur     = 1
     126       32256 :    nxt     = 2
     127             : 
     128       32256 :    im      = grid%im
     129       32256 :    jm      = grid%jm
     130       32256 :    km      = grid%km
     131       32256 :    ng      = grid%ng_d
     132             : 
     133       32256 :    jfirst  = grid%jfirst
     134       32256 :    jlast   = grid%jlast
     135       32256 :    kfirst  = grid%kfirst
     136       32256 :    klast   = grid%klast
     137             : 
     138       32256 :    ktot  = klast - kfirst + 1
     139       32256 :    js2g0 = max(2,jfirst)
     140       32256 :    jn2g0 = min(jm-1,jlast)
     141       32256 :    jn1g1 = min(jm,jlast+1)
     142       32256 :    js1gd = max(1,jfirst-ng)     ! NG latitudes on S (starting at 1)
     143       32256 :    js2gd = max(2,jfirst-ng)     ! NG latitudes on S (starting at 2)
     144       32256 :    jn2gd = min(jm-1,jlast+ng)   ! NG latitudes on S (ending at jm-1)
     145       32256 :    jn1gd = min(jm,jlast+ng)     ! NG latitudes on N (ending at jm)
     146             : 
     147             : #if defined( SPMD )
     148       32256 :       call FVstartclock(grid,'---TRAC2D_COMM')
     149             :       call mp_send4d_ns( grid%commyz, im, jm, km,                      &
     150             :                          1, jfirst, jlast, kfirst, klast,             &
     151       32256 :                          ng, ng, cx )
     152             : ! Send one latitude of both cy and mfy to the south
     153       32256 :       dest = grid%iam-1
     154       32256 :       src  = grid%iam+1
     155       32256 :       if ( mod(grid%iam,grid%npr_y) == 0 ) dest = -1
     156       32256 :       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       32256 :                         1, im, jfirst, jfirst, kfirst, klast, cy, mfy)
     160       32256 :       call FVstopclock(grid,'---TRAC2D_COMM')
     161             : #endif
     162             : 
     163             : !$omp parallel do default(shared) private(i,j,k,cmax)
     164      118272 :    do k=kfirst,klast
     165       86016 :         cymax(k) = D0_0
     166      374976 :        do j=js2g0,jlast
     167      256704 :             cmax = D0_0
     168    74187456 :           do i=1,im
     169    74187456 :             cmax = max( abs(cy(i,j,k)), cmax)
     170             :           enddo
     171      342720 :             cymax(k) = max(cymax(k), cmax*(D1_0 + grid%sine(j)**16) )
     172             :        enddo
     173             :    enddo
     174             : 
     175             : #if defined( SPMD )
     176       32256 :    call FVstartclock(grid,'---TRAC2D_COMM')
     177             :    call mp_recv4d_ns( grid%commyz, im, jm, km,                         &
     178             :                       1, jfirst, jlast, kfirst, klast,                &
     179       32256 :                       ng, ng, cx )
     180             :    call mp_recv3d_2( grid%commyz, src, im, jm, km,                     &
     181             :                      1, im, jfirst, jlast+1, kfirst, klast,           &
     182       32256 :                      1, im, jlast+1, jlast+1, kfirst, klast, cy, mfy)
     183             : 
     184       32256 :    call parcollective( grid%comm_y, MAXOP, ktot, cymax )
     185       32256 :    call FVstopclock(grid,'---TRAC2D_COMM')
     186             : #endif
     187             : 
     188             : !---------------------------------------------------------------------
     189             : ! Determine the required value of nsplt for each level
     190             : !---------------------------------------------------------------------
     191      118272 :     nsplt(:)  = int( D1_0 + cymax(:) )
     192      118272 :     max_nsplt = maxval( nsplt(:) )
     193             : #if defined( SPMD )
     194       32256 :     call FVstartclock(grid,'---TRAC2D_COMM')
     195       32256 :     call parcollective( grid%comm_z, MAXOP, max_nsplt )  ! Find global max
     196       32256 :     call FVstopclock(grid,'---TRAC2D_COMM')
     197             : #endif
     198       32256 :     if (grid%ptop>1._r8) then
     199      118272 :        nsplt(:)  = max_nsplt
     200             :     endif
     201       32256 :     do k_courant = klast,kfirst,-1
     202       32256 :        if( nsplt(k_courant) > 1 ) then
     203             :           exit
     204             :        end if
     205             :     end do
     206       32256 :     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      118272 :  do 4000 k=kfirst,klast
     216             : 
     217       86016 :     if( nsplt(k) .ne. 1 ) then
     218       86016 :        frac = D1_0 / nsplt(k)
     219      846720 :        do j=js2gd,jn2gd                  
     220   219929472 :           do i=1,im
     221   219843456 :             cx(i,j,k) =  cx(i,j,k) * frac      ! cx ghosted on N*ng S*ng
     222             :           enddo
     223             :        enddo
     224             : 
     225      341376 :        do j=js2g0,jn2g0
     226    73885056 :           do i=1,im
     227    73799040 :             mfx(i,j,k) = mfx(i,j,k) * frac
     228             :           enddo
     229             :        enddo
     230             : 
     231      427392 :        do j=js2g0,jn1g1                     
     232    98743680 :           do i=1,im
     233    98316288 :              cy(i,j,k) =  cy(i,j,k) * frac    ! cy ghosted on N
     234    98657664 :             mfy(i,j,k) = mfy(i,j,k) * frac    ! mfy ghosted on N
     235             :           enddo
     236             :        enddo
     237             :     endif
     238             : 
     239      341376 :        do j=js2g0,jn2g0
     240    73885056 :           do i=1,im
     241    73799040 :              if(cy(i,j,k)*cy(i,j+1,k) > D0_0) then
     242    70407564 :                 if( cy(i,j,k) > D0_0) then
     243    34032985 :                    va(i,j,k) = cy(i,j,k)
     244             :                 else
     245    36374579 :                    va(i,j,k) = cy(i,j+1,k)      ! cy ghosted on N
     246             :                 endif
     247             :              else
     248     3136116 :                 va(i,j,k) = D0_0
     249             :              endif
     250             :           enddo
     251             :        enddo
     252             : 
     253             : ! Check if FFSL extension is needed.
     254             : 
     255      846720 :        do j=js2gd,jn2gd             ! flux needed on N*ng S*ng
     256      760704 :           ffsl(j,k) = .false.
     257   215595885 :           do i=1,im
     258   215509869 :             if( abs(cx(i,j,k)) > D1_0 ) then  ! cx ghosted on N*ng S*ng
     259       22729 :               ffsl(j,k) = .true.
     260       22729 :               exit
     261             :             endif
     262             :           enddo
     263             :        enddo
     264             : 
     265             : ! Scale E-W mass fluxes by CX if FFSL
     266      341376 :        do j=js2g0,jn2g0
     267      341376 :           if( ffsl(j,k) ) then
     268     2576435 :             do i=1,im
     269     5135040 :               flx(i,j,k) = mfx(i,j,k) / sign( max(abs(cx(i,j,k)), D1EM10), &
     270     5143955 :                                         cx(i,j,k) )
     271             :             enddo
     272             :           else
     273    71222605 :             do i=1,im
     274    71222605 :               flx(i,j,k) = mfx(i,j,k)
     275             :             enddo
     276             :           endif
     277             :        enddo
     278       32256 : 4000  continue
     279             : #if !defined(USE_OMP)
     280             : !CSD$ END PARALLEL DO
     281             : #endif
     282             : 
     283       32256 :  call FVbarrierclock(grid,'sync_trac2d_tracer',grid%commyz)
     284       32256 :  call FVstartclock(grid,'---TRAC2D_TRACER')
     285             : 
     286       96768 :  do 6000 it=1, max_nsplt
     287       64512 :     if ( it == 1 ) then
     288       32256 :        kend = klast       !  The entire vertical slab needs to be considered
     289             :     else
     290       32256 :        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   149388288 :                tracer(1:im,jfirst:jlast,kfirst:kend,nlo)
     296             : #if defined( SPMD )
     297       64512 :     call FVstartclock(grid,'---TRAC2D_TRACER_COMM')
     298             :     call mp_send4d_ns( grid%commyz, im, jm, km,                        &
     299             :                        1, jfirst, jlast, kfirst, kend,                &
     300       64512 :                        ng, ng, q_r8(1,jfirst-ng,kfirst,1) )
     301       64512 :     call FVstopclock(grid,'---TRAC2D_TRACER_COMM')
     302             : #endif
     303             : 
     304             : !$omp parallel do default(shared) private(i,j,k,sum1,sum2)
     305             : 
     306      236544 :   do 3000 k=kfirst,kend
     307      172032 :      if (it <= nsplt(k)) then
     308      682752 :         do j=js2g0,jn2g0
     309   147087360 :            do i=1,im-1
     310   439729920 :               dp2(i,j,k) =  dp1(i,j,k) + mfx(i,j,k) - mfx(i+1,j,k) +  &
     311   586817280 :                         (mfy(i,j,k) - mfy(i,j+1,k)) * grid%acosp(j)
     312             :            enddo
     313     1021440 :            dp2(im,j,k) = dp1(im,j,k) + mfx(im,j,k) - mfx(1,j,k) +  &
     314     1704192 :                          (mfy(im,j,k) - mfy(im,j+1,k)) * grid%acosp(j)
     315             :         enddo
     316             : 
     317      172032 :         if ( jfirst == 1  ) then
     318        2688 :            sum1 = D0_0
     319      776832 :            do i=1,im
     320      776832 :               sum1 = sum1 + mfy(i,2,k)
     321             :            end do
     322             : 
     323        2688 :            sum1 = - sum1 * grid%rcap
     324      776832 :            do i=1,im
     325      776832 :               dp2(i,1,k) = dp1(i,1,k) +  sum1
     326             :            enddo
     327             :         endif
     328             : 
     329      172032 :         if ( jlast == jm ) then
     330        2688 :            sum2 = D0_0
     331      776832 :            do i=1,im
     332      776832 :               sum2 = sum2 + mfy(i,jm,k)
     333             :            end do
     334             : 
     335        2688 :               sum2 = sum2 * grid%rcap
     336      776832 :            do i=1,im
     337      776832 :               dp2(i,jm,k) = dp1(i,jm,k) +  sum2
     338             :            enddo
     339             :         endif
     340             :      endif
     341       64512 : 3000  continue
     342             : 
     343    15547392 :       do iq = nlo, nhi
     344             : #if defined( SPMD )
     345    15482880 :          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    15482880 :         if ( mod(iq-nlo+1,2) == 0 ) then
     351             :           cur = 2
     352             :           nxt = 1
     353             :         else
     354     7741440 :           cur = 1
     355     7741440 :           nxt = 2
     356             :         endif
     357             :         call mp_recv4d_ns( grid%commyz, im, jm, km,                    &
     358             :                            1, jfirst, jlast, kfirst, kend,            &
     359    15482880 :                            ng, ng, q_r8(1,jfirst-ng,kfirst,cur) )
     360             : 
     361             : !
     362             : ! Pre-send the next tracer
     363             : !
     364    15482880 :         if ( iq < nhi ) then
     365    15418368 :           q_r8(1:im,jfirst:jlast,kfirst:kend,nxt) = &
     366 35719219200 :              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    15418368 :                              ng, ng, q_r8(1,jfirst-ng,kfirst,nxt) )
     370             :         endif
     371    15482880 :         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    56835072 :         do 5000 k=kfirst,kend
     384    41287680 :            if ( it <= nsplt(k) ) then
     385    41287680 :               call tp2c(a2, va(1,jfirst,k), q_r8(1:,jfirst-ng:,k,cur),  &
     386    41287680 :                         cx(1,jfirst-ng,k) , cy(1,jfirst,k),           &
     387             :                         im, jm, iord, jord, ng,                       &
     388    41287680 :                         fx, fy, ffsl(1,k), grid%rcap, grid%acosp,     &
     389             :                         flx(1,jfirst,k), mfy(1,jfirst,k),             &
     390   123863040 :                         grid%cosp, 1, jfirst, jlast )
     391             : 
     392   165150720 :               do j=jfirst,jlast
     393 35837706240 :                  do i=1,im
     394 35796418560 :                     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    41287680 :               if (fill) call fillxy (q_r8(1:,jfirst:,k,cur), im, jm, jfirst, &
     399    41287680 :                                      jlast, grid%acap, grid%cosp, grid%acosp)
     400             : 
     401   165150720 :               do j=jfirst,jlast
     402 35837706240 :                  do i=1,im
     403 35796418560 :                     tracer(i,j,k,iq) = q_r8(i,j,k,cur) / dp2(i,j,k)
     404             :                  enddo
     405             :               enddo
     406             :            endif
     407    15482880 : 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      236544 :       do k=kfirst,kend
     416      236544 :          if ( it < nsplt(k) ) then
     417      344064 :             do j=jfirst,jlast
     418    74661888 :                do i=1,im
     419    74575872 :                   dp1(i,j,k) = dp2(i,j,k)
     420             :                enddo
     421             :             enddo
     422             :          endif
     423             :       enddo
     424             : 
     425       32256 : 6000  continue
     426       32256 :  call FVstopclock(grid,'---TRAC2D_TRACER')
     427             : 
     428       32256 :       return
     429             : !EOC
     430       32256 :  end subroutine trac2d
     431             : !-----------------------------------------------------------------------
     432             : 
     433             : 

Generated by: LCOV version 1.14