!======================================================================= !BOP ! ! !MODULE: ice_itd - initialize and redistribute ice in the ITD ! ! !DESCRIPTION: ! ! Routines to initialize the ice thickness distribution and ! utilities to redistribute ice among categories. These routines ! are not specific to a particular numerical implementation. ! ! See Bitz, C.M., and W.H. Lipscomb, 1999: ! An energy-conserving thermodynamic model of sea ice, ! J. Geophys. Res., 104, 15,669--15,677. ! ! See Bitz, C.M., M.M. Holland, A.J. Weaver, M. Eby, 2001: ! Simulating the ice-thickness distribution in a climate model, ! J. Geophys. Res., 106, 2441--2464. ! ! !REVISION HISTORY: ! SVN:$Id: ice_itd.F90 138 2008-07-08 20:39:37Z eclare $ ! ! authors: C. M. Bitz, UW ! William H. Lipscomb and Elizabeth C. Hunke, LANL ! ! 2003: Vectorized by Clifford Chen (Fujitsu) and William Lipscomb ! ! 2004 WHL: Added multiple snow layers, block structure, cleanup_itd ! 2006 ECH: Added WMO standard ice thickness categories as kcatbound=2 ! Streamlined for efficiency ! Converted to free source form (F90) ! ! !INTERFACE: ! module ice_itd 26,6 ! ! !USES: ! use ice_kinds_mod use ice_communicate, only: my_task, master_task use ice_domain_size use ice_constants use ice_fileunits use ice_exit ! !EOP ! implicit none save integer (kind=int_kind) :: & kitd , & ! type of itd conversions ! 0 = delta function ! 1 = linear remap kcatbound , & ! 0 = old category boundary formula ! 1 = new formula giving round numbers ! 2 = WMO standard ilyr1 (ncat), & ! array position of top ice layer in each cat ilyrn (ncat), & ! array position of bottom ice layer in each cat slyr1 (ncat), & ! array position of top snow layer in each cat slyrn (ncat) ! array position of bottom snow layer in each cat real (kind=dbl_kind), parameter :: & hi_min = p01 ! minimum ice thickness allowed (m) real (kind=dbl_kind) :: & hin_max(0:ncat) ! category limits (m) character (len=35) :: c_hi_range(ncat) !------------------------------------------------------------------- ! a note regarding hi_min and hin_max(0): ! both represent a minimum ice thickness. hin_max(0) is ! intended to be used for particular numerical implementations ! of category conversions in the ice thickness distribution. ! hi_min is a more general purpose parameter, but is specifically ! for maintaining stability in the thermodynamics. ! hin_max(0) = 0.1 m for the delta function itd ! hin_max(0) = 0.0 m for linear remapping ! ! Also note that the upper limit on the thickest category ! is only used for the linear remapping scheme ! and it is not a true upper limit on the thickness !------------------------------------------------------------------- !======================================================================= contains !======================================================================= !BOP ! ! !IROUTINE: init_itd - initalize area fraction and thickness boundaries for ITD ! ! !INTERFACE: ! subroutine init_itd 1 ! ! !DESCRIPTION: ! ! Initialize area fraction and thickness boundaries for the itd model ! ! !REVISION HISTORY: ! ! authors: William H. Lipscomb and Elizabeth C. Hunke, LANL ! C. M. Bitz, UW ! ! !USES: ! ! !INPUT/OUTPUT PARAMETERS: ! !EOP ! integer (kind=int_kind) :: & n ! thickness category index real (kind=dbl_kind) :: & cc1, cc2, cc3, & ! parameters for kcatbound = 0 x1 , & rn , & ! real(n) rncat , & ! real(ncat) d1 , & ! parameters for kcatbound = 1 (m) d2 real (kind=dbl_kind), dimension(5) :: wmo5 ! data for wmo itd real (kind=dbl_kind), dimension(6) :: wmo6 ! data for wmo itd real (kind=dbl_kind), dimension(7) :: wmo7 ! data for wmo itd character(len=8) :: c_hinmax1,c_hinmax2 character(len=2) :: c_nc rncat = real(ncat, kind=dbl_kind) d1 = 3.0_dbl_kind / rncat d2 = 0.5_dbl_kind / rncat !----------------------------------------------------------------- ! Choose category boundaries based on one of three options. ! ! The first formula (kcatbound = 0) was used in Lipscomb (2001) ! and in CICE versions 3.0 and 3.1. ! ! The second formula is more user-friendly in the sense that it ! is easy to obtain round numbers for category boundaries: ! ! H(n) = n * [d1 + d2*(n-1)] ! ! Default values are d1 = 300/ncat, d2 = 50/ncat. ! For ncat = 5, boundaries in cm are 60, 140, 240, 360, which are ! close to the standard values given by the first formula. ! For ncat = 10, boundaries in cm are 30, 70, 120, 180, 250, 330, ! 420, 520, 630. ! ! The third option provides support for World Meteorological ! Organization classification based on thickness. The full ! WMO thickness distribution is used if ncat = 7; if ncat=5 ! or ncat = 6, some of the thinner categories are combined. ! For ncat = 5, boundaries are 30, 70, 120, 200, >200 cm. ! For ncat = 6, boundaries are 15, 30, 70, 120, 200, >200 cm. ! For ncat = 7, boundaries are 10, 15, 30, 70, 120, 200, >200 cm. !----------------------------------------------------------------- if (kcatbound == 0) then ! original scheme if (kitd == 1) then ! linear remapping itd category limits cc1 = c3/rncat cc2 = c15*cc1 cc3 = c3 hin_max(0) = c0 ! minimum ice thickness, m else ! delta function itd category limits cc1 = max(1.1_dbl_kind/rncat,c1*hi_min) cc2 = c25*cc1 cc3 = 2.25_dbl_kind ! hin_max(0) should not be zero ! use some caution in making it less than 0.10 hin_max(0) = hi_min ! minimum ice thickness, m endif ! kitd do n = 1, ncat x1 = real(n-1,kind=dbl_kind) / rncat hin_max(n) = hin_max(n-1) & + cc1 + cc2*(c1 + tanh(cc3*(x1-c1))) enddo elseif (kcatbound == 1) then ! new scheme hin_max(0) = c0 do n = 1, ncat rn = real(n, kind=dbl_kind) hin_max(n) = rn * (d1 + (rn-c1)*d2) enddo elseif (kcatbound == 2) then ! WMO standard if (ncat == 5) then ! thinnest 3 categories combined data wmo5 / 0.30_dbl_kind, 0.70_dbl_kind, & 1.20_dbl_kind, 2.00_dbl_kind, & 999._dbl_kind / hin_max(0) = c0 do n = 1, ncat hin_max(n) = wmo5(n) enddo elseif (ncat == 6) then ! thinnest 2 categories combined data wmo6 / 0.15_dbl_kind, & 0.30_dbl_kind, 0.70_dbl_kind, & 1.20_dbl_kind, 2.00_dbl_kind, & 999._dbl_kind / hin_max(0) = c0 do n = 1, ncat hin_max(n) = wmo6(n) enddo elseif (ncat == 7) then ! all thickness categories data wmo7 / 0.10_dbl_kind, 0.15_dbl_kind, & 0.30_dbl_kind, 0.70_dbl_kind, & 1.20_dbl_kind, 2.00_dbl_kind, & 999._dbl_kind / hin_max(0) = c0 do n = 1, ncat hin_max(n) = wmo7(n) enddo else write (nu_diag,*) 'kcatbound=3 (WMO) must have ncat=5, 6 or 7' stop endif endif ! kcatbound if (my_task == master_task) then write (nu_diag,*) ' ' write (nu_diag,*) 'hin_max(n-1) < Cat n < hin_max(n)' do n = 1, ncat write (nu_diag,*) hin_max(n-1),' < Cat ',n, ' < ',hin_max(n) ! Write integer n to character string write (c_nc, '(i2)') n ! Write hin_max to character string write (c_hinmax1, '(f5.3)') hin_max(n-1) write (c_hinmax2, '(f5.3)') hin_max(n) ! Save character string to write to history file c_hi_range(n)=c_hinmax1//'m < hi Cat '//c_nc//' < '// & c_hinmax2//'m' enddo write (nu_diag,*) ' ' endif !----------------------------------------------------------------- ! vectors identifying first and last layer in each category !----------------------------------------------------------------- ilyr1(1) = 1 ! if nilyr = 4 ilyrn(1) = nilyr ! ilyr1 = { 1,5,9 } do n = 2, ncat ! ilyrn = { 4,8,12} etc ilyr1(n) = ilyrn(n-1) + 1 ilyrn(n) = ilyrn(n-1) + nilyr enddo slyr1(1) = 1 slyrn(1) = nslyr do n = 2, ncat slyr1(n) = slyrn(n-1) + 1 slyrn(n) = slyrn(n-1) + nslyr enddo end subroutine init_itd !======================================================================= !BOP ! ! !IROUTINE: aggregate - aggregate ice state variables ! ! !INTERFACE: ! subroutine aggregate (nx_block, ny_block, & 6,1 aicen, trcrn, & vicen, vsnon, & eicen, esnon, & aice, trcr, & vice, vsno, & eice, esno, & aice0, tmask, & ntrcr, trcr_depend) ! ! !DESCRIPTION: ! ! Aggregate ice state variables over thickness categories. ! ! !REVISION HISTORY: ! ! authors: C. M. Bitz, UW ! W. H. Lipscomb, LANL ! ! !USES: ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block, & ! block dimensions ntrcr ! number of tracers in use real (kind=dbl_kind), dimension (nx_block,ny_block,ncat), & intent(in) :: & aicen , & ! concentration of ice vicen , & ! volume per unit area of ice (m) vsnon ! volume per unit area of snow (m) real (kind=dbl_kind), dimension (nx_block,ny_block,max_ntrcr,ncat), & intent(in) :: & trcrn ! ice tracers real (kind=dbl_kind), dimension (nx_block,ny_block,ntilyr), & intent(in) :: & eicen ! energy of melting for each ice layer (J/m^2) real (kind=dbl_kind), dimension (nx_block,ny_block,ntslyr), & intent(in) :: & esnon ! energy of melting for each snow layer (J/m^2) logical (kind=log_kind), dimension (nx_block,ny_block), & intent(in) :: & tmask ! land/boundary mask, thickness (T-cell) integer (kind=int_kind), dimension (max_ntrcr), intent(in) :: & trcr_depend ! = 0 for aicen tracers, 1 for vicen, 2 for vsnon real (kind=dbl_kind), dimension (nx_block,ny_block), & intent(out) :: & aice , & ! concentration of ice vice , & ! volume per unit area of ice (m) vsno , & ! volume per unit area of snow (m) eice , & ! energy of melt. of ice (J/m^2) esno , & ! energy of melt. of snow layer (J/m^2) aice0 ! concentration of open water real (kind=dbl_kind), dimension (nx_block,ny_block,max_ntrcr), & intent(out) :: & trcr ! ice tracers ! !EOP ! integer (kind=int_kind) :: & icells ! number of ocean/ice cells integer (kind=int_kind), dimension (nx_block*ny_block) :: & indxi, & ! compressed indices in i/j directions indxj integer (kind=int_kind) :: & i, j, k, n, it, & ij ! combined i/j horizontal index real (kind=dbl_kind), dimension (:,:), allocatable :: & atrcr ! sum of aicen*trcrn or vicen*trcrn or vsnon*trcrn !----------------------------------------------------------------- ! Initialize !----------------------------------------------------------------- icells = 0 do j = 1, ny_block do i = 1, nx_block if (tmask(i,j)) then icells = icells + 1 indxi(icells) = i indxj(icells) = j endif ! tmask aice0(i,j) = c1 aice (i,j) = c0 vice (i,j) = c0 vsno (i,j) = c0 eice (i,j) = c0 esno (i,j) = c0 enddo enddo allocate (atrcr(icells,ntrcr)) !----------------------------------------------------------------- ! Aggregate !----------------------------------------------------------------- atrcr(:,:) = c0 do n = 1, ncat !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, icells i = indxi(ij) j = indxj(ij) aice(i,j) = aice(i,j) + aicen(i,j,n) vice(i,j) = vice(i,j) + vicen(i,j,n) vsno(i,j) = vsno(i,j) + vsnon(i,j,n) enddo ! ij do it = 1, ntrcr if (trcr_depend(it) == 0) then ! ice area tracer !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, icells i = indxi(ij) j = indxj(ij) atrcr(ij,it) = atrcr(ij,it) & + trcrn(i,j,it,n)*aicen(i,j,n) enddo ! ij elseif (trcr_depend(it) == 1) then ! ice volume tracer !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, icells i = indxi(ij) j = indxj(ij) atrcr(ij,it) = atrcr(ij,it) & + trcrn(i,j,it,n)*vicen(i,j,n) enddo ! ij elseif (trcr_depend(it) ==2) then ! snow volume tracer !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, icells i = indxi(ij) j = indxj(ij) atrcr(ij,it) = atrcr(ij,it) & + trcrn(i,j,it,n)*vsnon(i,j,n) enddo ! ij endif ! trcr_depend enddo ! ntrcr do k = 1, nilyr !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, icells i = indxi(ij) j = indxj(ij) eice(i,j) = eice(i,j) + eicen(i,j,ilyr1(n)+k-1) enddo enddo ! nilyr do k = 1, nslyr !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, icells i = indxi(ij) j = indxj(ij) esno(i,j) = esno(i,j) + esnon(i,j,slyr1(n)+k-1) enddo enddo ! nslyr enddo ! ncat ! Open water fraction do ij = 1, icells i = indxi(ij) j = indxj(ij) aice0(i,j) = max (c1 - aice(i,j), c0) enddo ! ij ! Tracers call compute_tracers (nx_block, ny_block, & icells, indxi, indxj, & ntrcr, trcr_depend, & atrcr(:,:), aice(:,:), & vice (:,:), vsno(:,:), & trcr(:,:,:)) deallocate (atrcr) end subroutine aggregate !======================================================================= !BOP ! ! !IROUTINE: aggregate_area - aggregate ice area ! ! !INTERFACE: ! subroutine aggregate_area (nx_block, ny_block, & 3 aicen, aice, aice0) ! ! !DESCRIPTION: ! ! Aggregate ice area (but not other state variables) over thickness ! categories. ! ! !REVISION HISTORY: ! ! authors: William H. Lipscomb, LANL ! modified Jan 2004 by Clifford Chen, Fujitsu ! ! !USES: ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block ! block dimensions real (kind=dbl_kind), dimension (:,:,:), intent(in) :: & aicen ! concentration of ice real (kind=dbl_kind), dimension (:,:), intent(inout) :: & aice, & ! concentration of ice aice0 ! concentration of open water ! !EOP ! integer (kind=int_kind) :: i, j, n !----------------------------------------------------------------- ! Aggregate !----------------------------------------------------------------- aice(:,:) = c0 do n = 1, ncat do j = 1, ny_block do i = 1, nx_block aice(i,j) = aice(i,j) + aicen(i,j,n) enddo ! i enddo ! j enddo ! n do j = 1, ny_block do i = 1, nx_block ! open water fraction aice0(i,j) = max (c1 - aice(i,j), c0) enddo ! i enddo ! j end subroutine aggregate_area !======================================================================= !BOP ! ! !IROUTINE: rebin - rebins thicknesses into defined categories ! ! !INTERFACE: ! subroutine rebin (nx_block, ny_block, & 3,2 icells, indxi, indxj, & ntrcr, trcr_depend, & aicen, trcrn, & vicen, vsnon, & eicen, esnon, & l_stop, & istop, jstop) ! ! !DESCRIPTION: ! ! Rebins thicknesses into defined categories ! ! !REVISION HISTORY: ! ! authors: William H. Lipscomb and Elizabeth C. Hunke, LANL ! ! !USES: ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block, & ! block dimensions icells , & ! number of grid cells with ice ntrcr ! number of tracers in use integer (kind=int_kind), dimension (nx_block*ny_block), & intent(in) :: & indxi, indxj ! compressed i/j indices integer (kind=int_kind), dimension (max_ntrcr), intent(in) :: & trcr_depend ! = 0 for aicen tracers, 1 for vicen, 2 for vsnon real (kind=dbl_kind), dimension (nx_block,ny_block,ncat), & intent(inout) :: & aicen , & ! concentration of ice vicen , & ! volume per unit area of ice (m) vsnon ! volume per unit area of snow (m) real (kind=dbl_kind), dimension (nx_block,ny_block,max_ntrcr,ncat), & intent(inout) :: & trcrn ! ice tracers real (kind=dbl_kind), dimension (nx_block,ny_block,ntilyr), & intent(inout) :: & eicen ! energy of melting for each ice layer (J/m^2) real (kind=dbl_kind), dimension (nx_block,ny_block,ntslyr), & intent(inout) :: & esnon ! energy of melting for each snow layer (J/m^2) logical (kind=log_kind), intent(out) :: & l_stop ! if true, abort on return integer (kind=int_kind), intent(out) :: & istop, jstop ! indices of grid cell where model aborts ! !EOP ! integer (kind=int_kind) :: & i,j , & ! horizontal indices n , & ! category index ij ! combined horizontal index logical (kind=log_kind) :: & shiftflag ! = .true. if ice must be shifted integer (kind=int_kind), dimension (icells,ncat) :: & donor ! donor category index real (kind=dbl_kind), dimension (icells,ncat) :: & daice , & ! ice area transferred dvice , & ! ice volume transferred hicen ! ice thickness for each cat (m) !----------------------------------------------------------------- ! Initialize !----------------------------------------------------------------- l_stop = .false. istop = 0 jstop = 0 do n = 1, ncat do ij = 1, icells ! aice(i,j) > puny i = indxi(ij) j = indxj(ij) donor(ij,n) = 0 daice(ij,n) = c0 dvice(ij,n) = c0 !----------------------------------------------------------------- ! Compute ice thickness. !----------------------------------------------------------------- if (aicen(i,j,n) > puny) then hicen(ij,n) = vicen(i,j,n) / aicen(i,j,n) else hicen(ij,n) = c0 endif enddo ! ij enddo ! n !----------------------------------------------------------------- ! make sure thickness of cat 1 is at least hin_max(0) !----------------------------------------------------------------- do ij = 1, icells ! aice(i,j) > puny i = indxi(ij) j = indxj(ij) if (aicen(i,j,1) > puny) then if (hicen(ij,1) <= hin_max(0) .and. hin_max(0) > c0 ) then aicen(i,j,1) = vicen(i,j,1) / hin_max(0) hicen(ij,1) = hin_max(0) endif endif enddo ! ij !----------------------------------------------------------------- ! If a category thickness is not in bounds, shift the ! entire area, volume, and energy to the neighboring category !----------------------------------------------------------------- !----------------------------------------------------------------- ! Move thin categories up !----------------------------------------------------------------- do n = 1, ncat-1 ! loop over category boundaries !----------------------------------------------------------------- ! identify thicknesses that are too big !----------------------------------------------------------------- shiftflag = .false. do ij = 1, icells ! aice(i,j) > puny i = indxi(ij) j = indxj(ij) if (aicen(i,j,n) > puny .and. & hicen(ij,n) > hin_max(n)) then shiftflag = .true. donor(ij,n) = n daice(ij,n) = aicen(i,j,n) dvice(ij,n) = vicen(i,j,n) endif enddo ! ij if (shiftflag) then !----------------------------------------------------------------- ! shift ice between categories !----------------------------------------------------------------- call shift_ice (nx_block, ny_block, & indxi, indxj, & icells, & ntrcr, trcr_depend, & aicen, trcrn, & vicen, vsnon, & eicen, esnon, & hicen, donor, & daice, dvice, & l_stop, & istop, jstop) if (l_stop) return !----------------------------------------------------------------- ! reset shift parameters !----------------------------------------------------------------- do ij = 1, icells ! aice(i,j) > puny donor(ij,n) = 0 daice(ij,n) = c0 dvice(ij,n) = c0 enddo endif ! shiftflag enddo ! n !----------------------------------------------------------------- ! Move thick categories down !----------------------------------------------------------------- do n = ncat-1, 1, -1 ! loop over category boundaries !----------------------------------------------------------------- ! identify thicknesses that are too small !----------------------------------------------------------------- shiftflag = .false. do ij = 1, icells ! aice(i,j) > puny i = indxi(ij) j = indxj(ij) if (aicen(i,j,n+1) > puny .and. & hicen(ij,n+1) <= hin_max(n)) then shiftflag = .true. donor(ij,n) = n+1 daice(ij,n) = aicen(i,j,n+1) dvice(ij,n) = vicen(i,j,n+1) endif enddo ! ij if (shiftflag) then !----------------------------------------------------------------- ! shift ice between categories !----------------------------------------------------------------- call shift_ice (nx_block, ny_block, & indxi, indxj, & icells, & ntrcr, trcr_depend, & aicen, trcrn, & vicen, vsnon, & eicen, esnon, & hicen, donor, & daice, dvice, & l_stop, & istop, jstop) if (l_stop) return !----------------------------------------------------------------- ! reset shift parameters !----------------------------------------------------------------- do ij = 1, icells ! aice(i,j) > puny donor(ij,n) = 0 daice(ij,n) = c0 dvice(ij,n) = c0 enddo endif ! shiftflag enddo ! n end subroutine rebin !======================================================================= !BOP ! ! !IROUTINE: reduce_area - reduce area when ice melts for special case ncat=1 ! ! !INTERFACE: ! subroutine reduce_area (nx_block, ny_block, & ilo, ihi, jlo, jhi, & tmask, & aicen, vicen, & aicen_init,vicen_init) ! ! !DESCRIPTION: ! ! Reduce area when ice melts for special case of ncat=1 ! ! Use CSM 1.0-like method of reducing ice area ! when melting occurs: assume only half the ice volume ! change goes to thickness decrease, the other half ! to reduction in ice fraction ! ! !REVISION HISTORY: ! ! authors: C. M. Bitz, UW ! modified by: Elizabeth C. Hunke, LANL ! ! !USES: ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block, & ! block dimensions ilo,ihi,jlo,jhi ! beginning and end of physical domain logical (kind=log_kind), dimension (nx_block,ny_block), & intent(in) :: & tmask ! land/boundary mask, thickness (T-cell) real (kind=dbl_kind), dimension (nx_block,ny_block), & intent(inout) :: & aicen , & ! concentration of ice vicen ! volume per unit area of ice (m) real (kind=dbl_kind), dimension(nx_block,ny_block), intent(in) :: & aicen_init, & ! old ice area for category 1 (m) vicen_init ! old ice volume for category 1 (m) ! !EOP ! integer (kind=int_kind) :: & i, j ! horizontal indices real (kind=dbl_kind) :: & hi0 , & ! initial hi hi1 , & ! current hi dhi ! hi1 - hi0 do j = jlo, jhi do i = ilo, ihi if (tmask(i,j)) then hi0 = c0 if (aicen_init(i,j) > c0) & hi0 = vicen_init(i,j) / aicen_init(i,j) hi1 = c0 if (aicen(i,j) > c0) & hi1 = vicen(i,j) / aicen(i,j) ! make sure thickness of cat 1 is at least hin_max(0) if (hi1 <= hin_max(0) .and. hin_max(0) > c0 ) then aicen(i,j) = vicen(i,j) / hin_max(0) hi1 = hin_max(0) endif if (aicen(i,j) > c0) then dhi = hi1 - hi0 if (dhi < c0) then hi1 = vicen(i,j) / aicen(i,j) aicen(i,j) = c2 * vicen(i,j) / (hi1 + hi0) endif endif endif ! tmask enddo ! i enddo ! j end subroutine reduce_area !======================================================================= !BOP ! ! !IROUTINE: shift_ice - shift ice across category boundaries ! ! !INTERFACE: ! subroutine shift_ice (nx_block, ny_block, & 3,1 indxi, indxj, & icells, & ntrcr, trcr_depend, & aicen, trcrn, & vicen, vsnon, & eicen, esnon, & hicen, donor, & daice, dvice, & l_stop, & istop, jstop) ! ! !DESCRIPTION: ! ! Shift ice across category boundaries, conserving area, volume, and ! energy. ! ! !REVISION HISTORY: ! ! authors: William H. Lipscomb and Elizabeth C. Hunke, LANL ! ! !USES: ! ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block, & ! block dimensions icells , & ! number of ocean/ice cells ntrcr ! number of tracers in use integer (kind=int_kind), dimension (nx_block*ny_block), & intent(in) :: & indxi , & ! compressed indices in i/j directions indxj integer (kind=int_kind), dimension (max_ntrcr), intent(in) :: & trcr_depend ! = 0 for aicen tracers, 1 for vicen, 2 for vsnon real (kind=dbl_kind), dimension (nx_block,ny_block,ncat), & intent(inout) :: & aicen , & ! concentration of ice vicen , & ! volume per unit area of ice (m) vsnon ! volume per unit area of snow (m) real (kind=dbl_kind), dimension (nx_block,ny_block,max_ntrcr,ncat), & intent(inout) :: & trcrn ! ice tracers real (kind=dbl_kind), dimension (nx_block,ny_block,ntilyr), & intent(inout) :: & eicen ! energy of melting for each ice layer (J/m^2) real (kind=dbl_kind), dimension (nx_block,ny_block,ntslyr), & intent(inout) :: & esnon ! energy of melting for each snow layer (J/m^2) ! NOTE: Third index of donor, daice, dvice should be ncat-1, ! except that compilers would have trouble when ncat = 1 integer (kind=int_kind), dimension(icells,ncat), & intent(in) :: & donor ! donor category index real (kind=dbl_kind), dimension(icells,ncat), & intent(inout) :: & daice , & ! ice area transferred across boundary dvice , & ! ice volume transferred across boundary hicen ! ice thickness for each cat (m) logical (kind=log_kind), intent(out) :: & l_stop ! if true, abort on return integer (kind=int_kind), intent(out) :: & istop, jstop ! indices of grid cell where model aborts ! !EOP ! integer (kind=int_kind) :: & i, j, m , & ! horizontal indices n , & ! thickness category index nr , & ! receiver category nd , & ! donor category k , & ! ice layer index it , & ! tracer index ilo,ihi,jlo,jhi ! beginning and end of physical domain real (kind=dbl_kind), dimension(icells,max_ntrcr,ncat) :: & atrcrn ! aicen*trcrn real (kind=dbl_kind) :: & dvsnow , & ! snow volume transferred desnow , & ! snow energy transferred deice , & ! ice energy transferred datrcr ! aicen*train transferred integer (kind=int_kind), dimension (icells) :: & indxii , & ! compressed indices for i/j directions indxjj , & indxij integer (kind=int_kind) :: & ishift , & ! number of cells with ice to transfer ij ! combined i/j horizontal index logical (kind=log_kind) :: & daice_negative , & ! true if daice < -puny dvice_negative , & ! true if dvice < -puny daice_greater_aicen, & ! true if daice > aicen dvice_greater_vicen ! true if dvice > vicen real (kind=dbl_kind), dimension (nx_block,ny_block) :: & worka, & workb !----------------------------------------------------------------- ! Initialize !----------------------------------------------------------------- l_stop = .false. istop = 0 jstop = 0 worka(:,:) = c0 workb(:,:) = c0 !----------------------------------------------------------------- ! Define variables equal to aicen*trcrn, vicen*trcrn, vsnon*trcrn !----------------------------------------------------------------- do n = 1, ncat do it = 1, ntrcr if (trcr_depend(it) == 0) then ! ice area tracer do ij = 1, icells i = indxi(ij) j = indxj(ij) atrcrn(ij,it,n) = aicen(i,j,n)*trcrn(i,j,it,n) enddo elseif (trcr_depend(it) ==1) then ! ice volume tracer do ij = 1, icells i = indxi(ij) j = indxj(ij) atrcrn(ij,it,n) = vicen(i,j,n)*trcrn(i,j,it,n) enddo elseif (trcr_depend(it) ==2) then ! snow volume tracer do ij = 1, icells i = indxi(ij) j = indxj(ij) atrcrn(ij,it,n) = vsnon(i,j,n)*trcrn(i,j,it,n) enddo endif enddo enddo !----------------------------------------------------------------- ! Check for daice or dvice out of range, allowing for roundoff error !----------------------------------------------------------------- do n = 1, ncat-1 daice_negative = .false. dvice_negative = .false. daice_greater_aicen = .false. dvice_greater_vicen = .false. do ij = 1, icells i = indxi(ij) j = indxj(ij) if (donor(ij,n) > 0) then nd = donor(ij,n) if (daice(ij,n) < c0) then if (daice(ij,n) > -puny*aicen(i,j,nd)) then daice(ij,n) = c0 ! shift no ice dvice(ij,n) = c0 else daice_negative = .true. endif endif if (dvice(ij,n) < c0) then if (dvice(ij,n) > -puny*vicen(i,j,nd)) then daice(ij,n) = c0 ! shift no ice dvice(ij,n) = c0 else dvice_negative = .true. endif endif if (daice(ij,n) > aicen(i,j,nd)*(c1-puny)) then if (daice(ij,n) < aicen(i,j,nd)*(c1+puny)) then daice(ij,n) = aicen(i,j,nd) dvice(ij,n) = vicen(i,j,nd) else daice_greater_aicen = .true. endif endif if (dvice(ij,n) > vicen(i,j,nd)*(c1-puny)) then if (dvice(ij,n) < vicen(i,j,nd)*(c1+puny)) then daice(ij,n) = aicen(i,j,nd) dvice(ij,n) = vicen(i,j,nd) else dvice_greater_vicen = .true. endif endif endif ! donor > 0 enddo ! ij !----------------------------------------------------------------- ! error messages !----------------------------------------------------------------- if (daice_negative) then do ij = 1, icells i = indxi(ij) j = indxj(ij) if (donor(ij,n) > 0 .and. & daice(ij,n) <= -puny*aicen(i,j,nd)) then write(nu_diag,*) ' ' write(nu_diag,*) 'shift_ice: negative daice' write(nu_diag,*) 'i, j:', i, j write(nu_diag,*) 'boundary, donor cat:', n, nd write(nu_diag,*) 'daice =', daice(ij,n) write(nu_diag,*) 'dvice =', dvice(ij,n) l_stop = .true. istop = i jstop = j endif enddo endif if (l_stop) return if (dvice_negative) then do ij = 1, icells i = indxi(ij) j = indxj(ij) if (donor(ij,n) > 0 .and. & dvice(ij,n) <= -puny*vicen(i,j,nd)) then write(nu_diag,*) ' ' write(nu_diag,*) 'shift_ice: negative dvice' write(nu_diag,*) 'i, j:', i, j write(nu_diag,*) 'boundary, donor cat:', n, nd write(nu_diag,*) 'daice =', daice(ij,n) write(nu_diag,*) 'dvice =', dvice(ij,n) l_stop = .true. istop = i jstop = j endif enddo endif if (l_stop) return if (daice_greater_aicen) then do ij = 1, icells i = indxi(ij) j = indxj(ij) if (donor(ij,n) > 0) then nd = donor(ij,n) if (daice(ij,n) >= aicen(i,j,nd)*(c1+puny)) then write(nu_diag,*) ' ' write(nu_diag,*) 'shift_ice: daice > aicen' write(nu_diag,*) 'i, j:', i, j write(nu_diag,*) 'boundary, donor cat:', n, nd write(nu_diag,*) 'daice =', daice(ij,n) write(nu_diag,*) 'aicen =', aicen(i,j,nd) l_stop = .true. istop = i jstop = j endif endif enddo endif if (l_stop) return if (dvice_greater_vicen) then do ij = 1, icells i = indxi(ij) j = indxj(ij) if (donor(ij,n) > 0) then nd = donor(ij,n) if (dvice(ij,n) >= vicen(i,j,nd)*(c1+puny)) then write(nu_diag,*) ' ' write(nu_diag,*) 'shift_ice: dvice > vicen' write(nu_diag,*) 'i, j:', i, j write(nu_diag,*) 'boundary, donor cat:', n, nd write(nu_diag,*) 'dvice =', dvice(ij,n) write(nu_diag,*) 'vicen =', vicen(i,j,nd) l_stop = .true. istop = i jstop = j endif endif enddo endif if (l_stop) return !----------------------------------------------------------------- ! transfer volume and energy between categories !----------------------------------------------------------------- ishift = 0 do ij = 1, icells i = indxi(ij) j = indxj(ij) if (daice(ij,n) > c0) then ! daice(n) can be < puny ishift = ishift + 1 indxii(ishift) = i indxjj(ishift) = j indxij(ishift) = ij endif ! tmask enddo !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, ishift i = indxii(ij) j = indxjj(ij) m = indxij(ij) nd = donor(m,n) worka(i,j) = dvice(m,n) / vicen(i,j,nd) if (nd == n) then nr = nd+1 else ! nd = n+1 nr = n endif aicen(i,j,nd) = aicen(i,j,nd) - daice(m,n) aicen(i,j,nr) = aicen(i,j,nr) + daice(m,n) vicen(i,j,nd) = vicen(i,j,nd) - dvice(m,n) vicen(i,j,nr) = vicen(i,j,nr) + dvice(m,n) dvsnow = vsnon(i,j,nd) * worka(i,j) vsnon(i,j,nd) = vsnon(i,j,nd) - dvsnow vsnon(i,j,nr) = vsnon(i,j,nr) + dvsnow workb(i,j) = dvsnow enddo ! ij do it = 1, ntrcr !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, ishift i = indxii(ij) j = indxjj(ij) m = indxij(ij) nd = donor(m,n) if (nd == n) then nr = nd+1 else ! nd = n+1 nr = n endif if (trcr_depend(it) == 0) then datrcr = daice(m,n)*trcrn(i,j,it,nd) elseif (trcr_depend(it) == 1) then datrcr = dvice(m,n)*trcrn(i,j,it,nd) elseif (trcr_depend(it) == 2) then datrcr = workb(i,j) *trcrn(i,j,it,nd) endif atrcrn(m,it,nd) = atrcrn(m,it,nd) - datrcr atrcrn(m,it,nr) = atrcrn(m,it,nr) + datrcr enddo ! ij enddo ! ntrcr do k = 1, nilyr !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, ishift i = indxii(ij) j = indxjj(ij) m = indxij(ij) nd = donor(m,n) if (nd == n) then nr = nd+1 else ! nd = n+1 nr = n endif deice = eicen(i,j,ilyr1(nd)+k-1) * worka(i,j) eicen(i,j,ilyr1(nd)+k-1) = & eicen(i,j,ilyr1(nd)+k-1) - deice eicen(i,j,ilyr1(nr)+k-1) = & eicen(i,j,ilyr1(nr)+k-1) + deice enddo ! ij enddo ! nilyr do k = 1, nslyr !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, ishift i = indxii(ij) j = indxjj(ij) m = indxij(ij) nd = donor(m,n) if (nd == n) then nr = nd+1 else ! nd = n+1 nr = n endif desnow = esnon(i,j,slyr1(nd)+k-1) * worka(i,j) esnon(i,j,slyr1(nd)+k-1) = & esnon(i,j,slyr1(nd)+k-1) - desnow esnon(i,j,slyr1(nr)+k-1) = & esnon(i,j,slyr1(nr)+k-1) + desnow enddo ! ij enddo ! nslyr enddo ! boundaries, 1 to ncat-1 !----------------------------------------------------------------- ! Update ice thickness and tracers !----------------------------------------------------------------- do n = 1, ncat do ij = 1, icells i = indxi(ij) j = indxj(ij) if (aicen(i,j,n) > puny) then hicen(ij,n) = vicen (i,j,n) / aicen(i,j,n) else hicen(ij,n) = c0 endif enddo call compute_tracers (nx_block, ny_block, & icells, indxi, indxj, & ntrcr, trcr_depend, & atrcrn(:,:,n), aicen(:,:, n), & vicen (:,:, n), vsnon(:,:, n), & trcrn(:,:,:,n)) enddo ! ncat end subroutine shift_ice !======================================================================= !BOP ! ! !IROUTINE: column_sum - sum field over all ice categories ! ! !INTERFACE: ! subroutine column_sum (nx_block, ny_block, & 18 icells, indxi, indxj, & nsum, & xin, xout) ! ! !DESCRIPTION: ! ! For each grid cell, sum field over all ice categories. ! ! !REVISION HISTORY: ! ! author: William H. Lipscomb, LANL ! ! !USES: ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block, & ! block dimensions nsum , & ! number of categories/layers icells ! number of ice/ocean grid cells integer (kind=int_kind), dimension (nx_block*ny_block), & intent(in) :: & indxi, indxj ! compressed i/j indices real (kind=dbl_kind), dimension (nx_block,ny_block,nsum), & intent(in) :: & xin ! input field real (kind=dbl_kind), dimension (icells), intent(out) :: & xout ! output field ! !EOP ! integer (kind=int_kind) :: & i, j, ij , & ! horizontal indices n ! category/layer index do ij = 1, icells xout(ij) = c0 enddo do n = 1, nsum do ij = 1, icells i = indxi(ij) j = indxj(ij) xout(ij) = xout(ij) + xin(i,j,n) enddo ! ij enddo ! n end subroutine column_sum !======================================================================= !BOP ! ! !IROUTINE: column_conservation_check ! ! !INTERFACE: ! subroutine column_conservation_check (nx_block, ny_block, & 9 icells, indxi, indxj, & fieldid, & x1, x2, & max_err, l_stop, & istop, jstop) ! ! !DESCRIPTION: ! ! For each physical grid cell, check that initial and final values ! of a conserved field are equal to within a small value. ! ! !REVISION HISTORY: ! ! author: William H. Lipscomb, LANL ! ! !USES: ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block, & ! block dimensions icells ! number of ice/ocean grid cells integer (kind=int_kind), dimension (nx_block*ny_block), & intent(in) :: & indxi, indxj ! compressed i/j indices real (kind=dbl_kind), dimension(icells), intent(in) :: & x1 , & ! initial field x2 ! final field real (kind=dbl_kind), intent(in) :: & max_err ! max allowed error character (len=char_len), intent(in) :: & fieldid ! field identifier logical (kind=log_kind), intent(inout) :: & l_stop ! if true, abort on return integer (kind=int_kind), intent(inout) :: & istop, jstop ! indices of grid cell where model aborts ! !EOP ! integer (kind=int_kind) :: & ij ! horizontal indices do ij = 1, icells if (abs (x2(ij)-x1(ij)) > max_err) then l_stop = .true. istop = indxi(ij) jstop = indxj(ij) write (nu_diag,*) ' ' write (nu_diag,*) 'Conservation error: ', trim(fieldid) write (nu_diag,*) 'i, j =', istop, jstop write (nu_diag,*) 'Initial value =', x1(ij) write (nu_diag,*) 'Final value =', x2(ij) write (nu_diag,*) 'Difference =', x2(ij) - x1(ij) endif enddo end subroutine column_conservation_check !======================================================================= !BOP ! ! !IROUTINE: compute_tracers - compute tracer fields ! ! !INTERFACE: ! subroutine compute_tracers (nx_block, ny_block, & 4,1 icells, indxi, indxj, & ntrcr, trcr_depend, & atrcrn, aicen, & vicen, vsnon, & trcrn) ! ! !DESCRIPTION: ! ! Compute tracer fields. ! Given atrcrn = aicen*trcrn (or vicen*trcrn, vsnon*trcrn), compute trcrn. ! ! !REVISION HISTORY: ! ! author: William H. Lipscomb, LANL ! ! !USES: ! use ice_state, only: nt_Tsfc ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block, & ! block dimensions icells , & ! number of ice/ocean grid cells ntrcr ! number of tracers in use integer (kind=int_kind), dimension (nx_block*ny_block), & intent(in) :: & indxi, indxj ! compressed i/j indices integer (kind=int_kind), dimension (max_ntrcr), intent(in) :: & trcr_depend ! = 0 for aicen tracers, 1 for vicen, 2 for vsnon real (kind=dbl_kind), dimension (icells,max_ntrcr), & intent(in) :: & atrcrn ! aicen*trcrn or vicen*trcrn or vsnon*trcrn real (kind=dbl_kind), dimension (nx_block,ny_block), & intent(in) :: & aicen , & ! concentration of ice vicen , & ! volume per unit area of ice (m) vsnon ! volume per unit area of snow (m) real (kind=dbl_kind), dimension (nx_block,ny_block,max_ntrcr), & intent(out) :: & trcrn ! ice tracers ! !EOP ! integer (kind=int_kind) :: & i, j, it, ij ! counting indices trcrn(:,:,:) = c0 !----------------------------------------------------------------- ! Compute new tracers !----------------------------------------------------------------- do it = 1, ntrcr if (it == nt_Tsfc) then ! surface temperature do ij = 1, icells i = indxi(ij) j = indxj(ij) if (aicen(i,j) > puny) then trcrn(i,j,it) = atrcrn(ij,it) / aicen(i,j) else trcrn(i,j,it) = Tocnfrz endif enddo elseif (trcr_depend(it) == 0) then ! ice area tracers do ij = 1, icells i = indxi(ij) j = indxj(ij) if (aicen(i,j) > puny) then trcrn(i,j,it) = atrcrn(ij,it) / aicen(i,j) else trcrn(i,j,it) = c0 endif enddo elseif (trcr_depend(it) == 1) then ! ice volume tracers do ij = 1, icells i = indxi(ij) j = indxj(ij) if (vicen(i,j) > puny) then trcrn(i,j,it) = atrcrn(ij,it) / vicen(i,j) else trcrn(i,j,it) = c0 endif enddo elseif (trcr_depend(it) == 2) then ! snow volume tracers do ij = 1, icells i = indxi(ij) j = indxj(ij) if (vsnon(i,j) > puny) then trcrn(i,j,it) = atrcrn(ij,it) / vsnon(i,j) else trcrn(i,j,it) = c0 endif enddo endif ! trcr_depend enddo ! ntrcr end subroutine compute_tracers !======================================================================= !BOP ! ! !IROUTINE: cleanup_itd - rebin if needed, eliminate small ice areas, ! and aggregate over categories ! ! !INTERFACE: ! subroutine cleanup_itd (nx_block, ny_block, & 2,4 ilo, ihi, jlo, jhi, & dt, ntrcr, & aicen, trcrn, & vicen, vsnon, & eicen, esnon, & aice0, aice, & trcr_depend, fresh, & fsalt, fhocn, & fsoot, tr_aero, & heat_capacity, l_stop, & istop, jstop, & limit_aice_in) ! ! !DESCRIPTION: ! ! Cleanup subroutine that rebins thickness categories if necessary, ! eliminates very small ice areas while conserving mass and energy, ! aggregates state variables, and does a boundary call. ! It is a good idea to call this subroutine after the thermodynamics ! (thermo_vertical/thermo_itd) and again after the dynamics ! (evp/transport/ridging). ! ! !REVISION HISTORY: ! ! author: William H. Lipscomb, LANL ! ! !USES: ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block, & ! block dimensions ilo,ihi,jlo,jhi , & ! beginning and end of physical domain ntrcr ! number of tracers in use real (kind=dbl_kind), intent(in) :: & dt ! time step real (kind=dbl_kind), dimension (nx_block,ny_block,ncat), & intent(inout) :: & aicen , & ! concentration of ice vicen , & ! volume per unit area of ice (m) vsnon ! volume per unit area of snow (m) real (kind=dbl_kind), dimension (nx_block,ny_block,max_ntrcr,ncat), & intent(inout) :: & trcrn ! ice tracers real (kind=dbl_kind), dimension (nx_block,ny_block,ntilyr), & intent(inout) :: & eicen ! energy of melting for each ice layer (J/m^2) real (kind=dbl_kind), dimension (nx_block,ny_block,ntslyr), & intent(inout) :: & esnon ! energy of melting for each snow layer (J/m^2) real (kind=dbl_kind), dimension (nx_block,ny_block), & intent(inout) :: & aice , & ! total ice concentration aice0 ! concentration of open water integer (kind=int_kind), dimension(max_ntrcr), intent(in) :: & trcr_depend ! tracer dependency information logical (kind=log_kind), intent(in) :: & tr_aero, & heat_capacity ! if false, ice and snow have zero heat capacity logical (kind=log_kind), intent(out) :: & l_stop ! if true, abort on return integer (kind=int_kind), intent(out) :: & istop, jstop ! indices of grid cell where model aborts ! ice-ocean fluxes (required for strict conservation) real (kind=dbl_kind), dimension (nx_block,ny_block), & intent(inout), optional :: & fresh , & ! fresh water flux to ocean (kg/m^2/s) fsalt , & ! salt flux to ocean (kg/m^2/s) fhocn ! net heat flux to ocean (W/m^2) real (kind=dbl_kind), dimension (nx_block,ny_block,n_aeromx), & intent(inout), optional :: & fsoot ! soot flux to ocean (kg/m^2/s) logical (kind=log_kind), intent(in), optional :: & limit_aice_in ! if false, allow aice to be out of bounds ! may want to allow this for unit tests ! !EOP ! integer (kind=int_kind) :: & i, j , & ! horizontal indices n , & ! category index icells ! number of grid cells with ice integer (kind=int_kind), dimension (nx_block*ny_block) :: & indxi, indxj ! compressed i/j indices real (kind=dbl_kind), dimension (nx_block,ny_block) :: & dfresh , & ! zapped fresh water flux (kg/m^2/s) dfsalt , & ! zapped salt flux (kg/m^2/s) dfhocn ! zapped energy flux ( W/m^2) real (kind=dbl_kind), dimension (nx_block,ny_block,n_aeromx) :: & dfsoot ! zapped soot flux (kg/m^2/s) logical (kind=log_kind) :: & limit_aice ! if true, check for aice out of bounds !----------------------------------------------------------------- ! Initialize !----------------------------------------------------------------- if (present(limit_aice_in)) then limit_aice = limit_aice_in else limit_aice = .true. endif l_stop = .false. istop = 0 jstop = 0 !----------------------------------------------------------------- ! Compute total ice area. !----------------------------------------------------------------- call aggregate_area (nx_block, ny_block, & aicen(:,:,:), & aice, aice0) if (limit_aice) then ! check for aice out of bounds do j = jlo,jhi do i = ilo,ihi if (aice(i,j) > c1+puny .or. aice(i,j) < -puny) then l_stop = .true. istop = i jstop = j endif enddo enddo if (l_stop) then ! area out of bounds i = istop j = jstop write(nu_diag,*) ' ' write(nu_diag,*) 'aggregate ice area out of bounds' write(nu_diag,*) 'my_task, i, j, aice:', & my_task, i, j, aice(i,j) do n = 1, ncat write(nu_diag,*) 'n, aicen:', n, aicen(i,j,n) enddo return endif ! l_stop endif ! limit_aice !----------------------------------------------------------------- ! Identify grid cells with ice. !----------------------------------------------------------------- icells = 0 do j = jlo,jhi do i = ilo,ihi if (aice(i,j) > puny) then icells = icells + 1 indxi(icells) = i indxj(icells) = j endif enddo enddo !----------------------------------------------------------------- ! Make sure ice in each category is within its thickness bounds. ! NOTE: The rebin subroutine is needed only in the rare cases ! when the linear_itd subroutine cannot transfer ice ! correctly (e.g., very fast ice growth). !----------------------------------------------------------------- call rebin (nx_block, ny_block, & icells, indxi, indxj, & ntrcr, trcr_depend, & aicen(:,:,:), trcrn(:,:,:,:), & vicen(:,:,:), vsnon(:,:,:), & eicen(:,:,:), esnon(:,:,:), & l_stop, & istop, jstop) if (l_stop) return !----------------------------------------------------------------- ! Zero out ice categories with very small areas. !----------------------------------------------------------------- if (limit_aice) then call zap_small_areas (nx_block, ny_block, & ilo, ihi, jlo, jhi, & dt, ntrcr, & aice, aice0, & aicen(:,:,:), trcrn(:,:,:,:), & vicen(:,:,:), vsnon(:,:,:), & eicen(:,:,:), esnon(:,:,:), & dfresh, dfsalt, & dfhocn, dfsoot, & tr_aero, & l_stop, & istop, jstop) if (l_stop) return endif ! l_limit_aice !------------------------------------------------------------------- ! Update ice-ocean fluxes for strict conservation !------------------------------------------------------------------- if (present(fresh)) & fresh (:,:) = fresh(:,:) + dfresh(:,:) if (present(fsalt)) & fsalt (:,:) = fsalt(:,:) + dfsalt(:,:) if (present(fhocn)) & fhocn (:,:) = fhocn(:,:) + dfhocn(:,:) if (present(fsoot)) & fsoot (:,:,:) = fsoot(:,:,:) + dfsoot(:,:,:) !---------------------------------------------------------------- ! If using zero-layer model (no heat capacity), check that the ! energy of snow and ice is correct. !---------------------------------------------------------------- if (.not. heat_capacity) then call zerolayer_check(nx_block, ny_block, & icells, indxi, indxj, & aicen, & vicen, vsnon, & eicen, esnon, & l_stop, & istop, jstop) endif end subroutine cleanup_itd !======================================================================= !BOP ! ! !IROUTINE: zap_small_areas - eliminate very small ice areas ! ! !INTERFACE: ! subroutine zap_small_areas (nx_block, ny_block, & 1,1 ilo, ihi, jlo, jhi, & dt, ntrcr, & aice, aice0, & aicen, trcrn, & vicen, vsnon, & eicen, esnon, & dfresh, dfsalt, & dfhocn, dfsoot, & tr_aero, & l_stop, & istop, jstop) ! ! !DESCRIPTION: ! ! For each ice category in each grid cell, remove ice if the fractional ! area is less than puny. ! ! !REVISION HISTORY: ! ! author: William H. Lipscomb, LANL ! ! !USES: ! use ice_state, only: nt_Tsfc, nt_aero ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block, & ! block dimensions ilo,ihi,jlo,jhi , & ! beginning and end of physical domain ntrcr ! number of tracers in use real (kind=dbl_kind), intent(in) :: & dt ! time step real (kind=dbl_kind), dimension (nx_block,ny_block), & intent(inout) :: & aice , & ! total ice concentration aice0 ! concentration of open water real (kind=dbl_kind), dimension(nx_block,ny_block,ncat), & intent(inout) :: & aicen , & ! concentration of ice vicen , & ! volume per unit area of ice (m) vsnon ! volume per unit area of snow (m) real (kind=dbl_kind), dimension (nx_block,ny_block,ntilyr), & intent(inout) :: & eicen ! energy of melting for each ice layer (J/m^2) real (kind=dbl_kind), dimension (nx_block,ny_block,ntslyr), & intent(inout) :: & esnon ! energy of melting for each snow layer (J/m^2) real (kind=dbl_kind), dimension (nx_block,ny_block,max_ntrcr,ncat), & intent(inout) :: & trcrn ! ice tracers real (kind=dbl_kind), dimension (nx_block,ny_block), & intent(out) :: & dfresh , & ! zapped fresh water flux (kg/m^2/s) dfsalt , & ! zapped salt flux (kg/m^2/s) dfhocn ! zapped energy flux ( W/m^2) real (kind=dbl_kind), dimension (nx_block,ny_block,n_aeromx), & intent(out) :: & dfsoot ! zapped soot flux (kg/m^2/s) logical (kind=log_kind), intent(in) :: & tr_aero logical (kind=log_kind), intent(out) :: & l_stop ! if true, abort on return integer (kind=int_kind), intent(out) :: & istop, jstop ! indices of grid cell where model aborts ! !EOP ! integer (kind=int_kind) :: & i,j, n, k, it , & ! counting indices icells , & ! number of cells with ice to zap ij ! combined i/j horizontal index integer (kind=int_kind), dimension (nx_block*ny_block) :: & indxi , & ! compressed indices for i/j directions indxj real (kind=dbl_kind) :: xtmp ! temporary variable !----------------------------------------------------------------- ! Initialize !----------------------------------------------------------------- l_stop = .false. istop = 0 jstop = 0 dfresh(:,:) = c0 dfsalt(:,:) = c0 dfhocn(:,:) = c0 dfsoot(:,:,:) = c0 !----------------------------------------------------------------- ! Zap categories with very small areas. !----------------------------------------------------------------- do n = 1, ncat !----------------------------------------------------------------- ! Count categories to be zapped. !----------------------------------------------------------------- icells = 0 do j = jlo, jhi do i = ilo, ihi if (aicen(i,j,n) < -puny) then write (nu_diag,*) 'Zap ice: negative ice area' write (nu_diag,*) 'i, j, n, aicen =', & i, j, n, aicen(i,j,n) l_stop = .true. istop = i jstop = j return elseif ((aicen(i,j,n) >= -puny .and. aicen(i,j,n) < c0) .or. & (aicen(i,j,n) > c0 .and. aicen(i,j,n) <= puny)) then icells = icells + 1 indxi(icells) = i indxj(icells) = j endif enddo enddo !----------------------------------------------------------------- ! Zap ice energy and use ocean heat to melt ice !----------------------------------------------------------------- do k = 1, nilyr !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, icells i = indxi(ij) j = indxj(ij) xtmp = eicen(i,j,ilyr1(n)+k-1) / dt ! < 0 dfhocn(i,j) = dfhocn(i,j) + xtmp eicen(i,j,ilyr1(n)+k-1) = c0 enddo ! ij enddo ! k !----------------------------------------------------------------- ! Zap snow energy and use ocean heat to melt snow !----------------------------------------------------------------- do k = 1, nslyr !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, icells i = indxi(ij) j = indxj(ij) xtmp = esnon(i,j,slyr1(n)+k-1) / dt ! < 0 dfhocn(i,j) = dfhocn(i,j) + xtmp esnon(i,j,slyr1(n)+k-1) = c0 enddo ! ij enddo ! k !----------------------------------------------------------------- ! Zap ice and snow volume, add water and salt to ocean !----------------------------------------------------------------- !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, icells i = indxi(ij) j = indxj(ij) xtmp = (rhoi*vicen(i,j,n) + rhos*vsnon(i,j,n)) / dt dfresh(i,j) = dfresh(i,j) + xtmp xtmp = rhoi*vicen(i,j,n)*ice_ref_salinity*p001 / dt dfsalt(i,j) = dfsalt(i,j) + xtmp aice0(i,j) = aice0(i,j) + aicen(i,j,n) aicen(i,j,n) = c0 vicen(i,j,n) = c0 vsnon(i,j,n) = c0 trcrn(i,j,nt_Tsfc,n) = Tocnfrz enddo ! ij if (tr_aero) then !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, icells i = indxi(ij) j = indxj(ij) do it=1,n_aero xtmp & = (vsnon(i,j,n)*(trcrn(i,j,nt_aero +4*(it-1),n) & +trcrn(i,j,nt_aero+1+4*(it-1),n)) & + vicen(i,j,n)*(trcrn(i,j,nt_aero+2+4*(it-1),n) & +trcrn(i,j,nt_aero+3+4*(it-1),n))) & / dt dfsoot(i,j,it) = dfsoot(i,j,it) + xtmp enddo ! n enddo ! ij endif !----------------------------------------------------------------- ! Zap tracers !----------------------------------------------------------------- if (ntrcr >= 2) then do it = 1, ntrcr ! this assumes nt_Tsfc = 1 do ij = 1, icells i = indxi(ij) j = indxj(ij) trcrn(i,j,it,n) = c0 enddo enddo endif enddo ! n !----------------------------------------------------------------- ! Count cells with excess ice (aice > c1) due to roundoff errors. ! Zap a little ice in each category so that aice = c1. !----------------------------------------------------------------- icells = 0 do j = jlo, jhi do i = ilo, ihi if (aice(i,j) > (c1+puny)) then write (nu_diag,*) 'Zap ice: excess ice area' write (nu_diag,*) 'i, j, aice =', & i, j, aice(i,j) l_stop = .true. istop = i jstop = j return elseif (aice(i,j) > c1 .and. aice(i,j) < (c1+puny)) then icells = icells + 1 indxi(icells) = i indxj(icells) = j endif enddo enddo do n = 1, ncat !----------------------------------------------------------------- ! Zap ice energy and use ocean heat to melt ice !----------------------------------------------------------------- do k = 1, nilyr !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, icells i = indxi(ij) j = indxj(ij) xtmp = eicen(i,j,ilyr1(n)+k-1) & * (aice(i,j)-c1)/aice(i,j) / dt ! < 0 dfhocn(i,j) = dfhocn(i,j) + xtmp eicen(i,j,ilyr1(n)+k-1) = eicen(i,j,ilyr1(n)+k-1) & * (c1/aice(i,j)) enddo ! ij enddo ! k !----------------------------------------------------------------- ! Zap snow energy and use ocean heat to melt snow !----------------------------------------------------------------- do k = 1, nslyr !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, icells i = indxi(ij) j = indxj(ij) xtmp = esnon(i,j,slyr1(n)+k-1) & * (aice(i,j)-c1)/aice(i,j) / dt ! < 0 dfhocn(i,j) = dfhocn(i,j) + xtmp esnon(i,j,slyr1(n)+k-1) = esnon(i,j,slyr1(n)+k-1) & *(c1/aice(i,j)) enddo ! ij enddo ! k !----------------------------------------------------------------- ! Zap ice and snow volume, add water and salt to ocean !----------------------------------------------------------------- !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, icells i = indxi(ij) j = indxj(ij) xtmp = (rhoi*vicen(i,j,n) + rhos*vsnon(i,j,n)) & * (aice(i,j)-c1)/aice(i,j) / dt dfresh(i,j) = dfresh(i,j) + xtmp xtmp = rhoi*vicen(i,j,n)*ice_ref_salinity*p001 & * (aice(i,j)-c1)/aice(i,j) / dt dfsalt(i,j) = dfsalt(i,j) + xtmp aicen(i,j,n) = aicen(i,j,n) * (c1/aice(i,j)) vicen(i,j,n) = vicen(i,j,n) * (c1/aice(i,j)) vsnon(i,j,n) = vsnon(i,j,n) * (c1/aice(i,j)) enddo ! ij ! Note: Tracers are unchanged. !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu if (tr_aero) then do ij = 1, icells i = indxi(ij) j = indxj(ij) do it=1,n_aero xtmp & = (vsnon(i,j,n)*(trcrn(i,j,nt_aero +4*(it-1),n) & +trcrn(i,j,nt_aero+1+4*(it-1),n)) & + vicen(i,j,n)*(trcrn(i,j,nt_aero+2+4*(it-1),n) & +trcrn(i,j,nt_aero+3+4*(it-1),n))) & * (aice(i,j)-c1)/aice(i,j) / dt dfsoot(i,j,it) = dfsoot(i,j,it) + xtmp enddo ! n enddo ! ij endif enddo ! n !----------------------------------------------------------------- ! Correct aice !----------------------------------------------------------------- !DIR$ CONCURRENT !Cray !cdir nodep !NEC !ocl novrec !Fujitsu do ij = 1, icells i = indxi(ij) j = indxj(ij) aice(i,j) = c1 aice0(i,j) = c0 enddo end subroutine zap_small_areas !======================================================================= !BOP ! ! !IROUTINE: zerolayer_check - check that snow and ice energy is ! correct when using zero layer thermodynamics ! ! !INTERFACE: ! subroutine zerolayer_check (nx_block, ny_block, & 1 icells, indxi, indxj, & aicen, & vicen, vsnon, & eicen, esnon, & l_stop, & istop, jstop) ! ! !DESCRIPTION: ! ! Checks that the snow and ice energy in the zero layer thermodynamics ! model still agrees with the snow and ice volume. ! If there is an error, the model will abort. ! This subroutine is only called if heat_capacity = .false. ! ! !REVISION HISTORY: ! ! author: Alison McLaren, Met Office ! ! !USES: ! ! ! !INPUT/OUTPUT PARAMETERS: ! integer (kind=int_kind), intent(in) :: & nx_block, ny_block, & ! block dimensions icells ! number of grid cells with ice integer (kind=int_kind), dimension (nx_block*ny_block), & intent(in) :: & indxi, indxj ! compressed i/j indices real (kind=dbl_kind), dimension (nx_block,ny_block,ncat), & intent(inout) :: & aicen , & ! concentration of ice vicen , & ! volume per unit area of ice (m) vsnon ! volume per unit area of snow (m) real (kind=dbl_kind), dimension (nx_block,ny_block,ntilyr), & intent(in) :: & eicen ! energy of melting for each ice layer (J/m^2) real (kind=dbl_kind), dimension (nx_block,ny_block,ntslyr), & intent(in) :: & esnon ! energy of melting for each snow layer (J/m^2) logical (kind=log_kind), intent(out) :: & l_stop ! if true, abort on return integer (kind=int_kind), intent(out) :: & istop, jstop ! indices of grid cell where model aborts ! !EOP ! integer (kind=int_kind) :: & i, j , & ! horizontal indices n , & ! category index ij ! combined horizontal index real (kind=dbl_kind), parameter :: & max_error = puny*Lfresh*rhos ! max error in zero layer energy check ! (so max volume error = puny) logical (kind=log_kind) :: & ice_energy_correct , & ! zero layer ice energy check snow_energy_correct ! zero layer snow energy check real (kind=dbl_kind), dimension (nx_block,ny_block) :: & worka, & workb !----------------------------------------------------------------- ! Initialize !----------------------------------------------------------------- l_stop = .false. istop = 0 jstop = 0 worka(:,:) = c0 workb(:,:) = c0 !---------------------------------------------------------------- ! Calculate difference between ice and snow energies and the ! energy values derived from the ice and snow volumes !---------------------------------------------------------------- ice_energy_correct = .true. snow_energy_correct = .true. do n=1,ncat do ij=1,icells i=indxi(ij) j=indxj(ij) worka(i,j) = eicen(i,j,n) + rhoi * Lfresh * vicen(i,j,n) workb(i,j) = esnon(i,j,n) + rhos * Lfresh * vsnon(i,j,n) if(abs(worka(i,j)) > max_error) then ice_energy_correct = .false. endif if(abs(workb(i,j)) > max_error) then snow_energy_correct = .false. endif enddo !---------------------------------------------------------------- ! If there is a problem, abort with error message !---------------------------------------------------------------- if (.not. ice_energy_correct) then do ij=1,icells i=indxi(ij) j=indxj(ij) if(abs(worka(i,j)) > max_error) then write(nu_diag,*) ' ' write(nu_diag,*) & 'zerolayer check - wrong ice energy' write(nu_diag,*) 'i, j, n:', i,j,n write(nu_diag,*) 'eicen =', eicen(i,j,n) write(nu_diag,*) 'error=', worka(i,j) write(nu_diag,*) 'vicen =', vicen(i,j,n) write(nu_diag,*) 'aicen =', aicen(i,j,n) l_stop = .true. istop = i jstop = j endif enddo endif if (l_stop) return if (.not. snow_energy_correct) then do ij=1,icells i=indxi(ij) j=indxj(ij) if(abs(workb(i,j)) > max_error) then write(nu_diag,*) ' ' write(nu_diag,*) & 'zerolayer_check - wrong snow energy' write(nu_diag,*) 'i, j, n:', i,j,n write(nu_diag,*) 'esnon =', esnon(i,j,n) write(nu_diag,*) 'error=', workb(i,j) write(nu_diag,*) 'vsnon =', vsnon(i,j,n) write(nu_diag,*) 'aicen =', aicen(i,j,n) l_stop = .true. istop = i jstop = j return endif enddo endif enddo ! ncat end subroutine zerolayer_check !======================================================================= end module ice_itd !=======================================================================