!=======================================================================
!BOP
!
! !MODULE: ice_therm_itd - thermo calculations after call to coupler
!
! !DESCRIPTION:
!
! Thermo calculations after call to coupler, related to ITD:
! ice thickness redistribution, lateral growth and melting.
!
! NOTE: The thermodynamic calculation is split in two for load balancing.
! First ice_therm_vertical computes vertical growth rates and coupler
! fluxes. Then ice_therm_itd does thermodynamic calculations not
! needed for coupling.
!
! !REVISION HISTORY:
! SVN:$Id: ice_therm_itd.F90 48 2007-01-09 23:57:33Z eclare $
!
! authors William H. Lipscomb, LANL
! C. M. Bitz, UW
! Elizabeth C. Hunke, LANL
!
! 2003: Vectorized by Clifford Chen (Fujitsu) and William Lipscomb
! 2004: Block structure added by William Lipscomb.
! 2006: Streamlined for efficiency by Elizabeth Hunke
!
! !INTERFACE:
!
module ice_therm_itd 3,5
!
! !USES:
!
use ice_kinds_mod
use ice_communicate
, only: my_task, master_task
use ice_domain_size
use ice_constants
use ice_fileunits
!
!EOP
!
implicit none
save
real (kind=dbl_kind), parameter, private :: &
hfrazilmin = 0.05_dbl_kind ! min thickness of new frazil ice (m)
!=======================================================================
contains
!=======================================================================
!BOP
!
! !IROUTINE: linear_itd - ITD scheme that shifts ice among categories
!
! !INTERFACE:
!
subroutine linear_itd (nx_block, ny_block, & 1,17
icells, indxi, indxj, &
ntrcr, trcr_depend, &
aicen_init, vicen_init, &
aicen, trcrn, &
vicen, vsnon, &
eicen, esnon, &
aice, aice0, &
l_stop, &
istop, jstop)
! See Lipscomb, W. H. Remapping the thickness distribution in sea
! ice models. 2001, J. Geophys. Res., Vol 106, 13989--14000.
!
! Using the thermodynamic "velocities", interpolate to find the
! velocities in thickness space at the category boundaries, and
! compute the new locations of the boundaries. Then for each
! category, compute the thickness distribution function, g(h),
! between hL and hR, the left and right boundaries of the category.
! Assume g(h) is a linear polynomial that satisfies two conditions:
!
! (1) The ice area implied by g(h) equals aicen(n).
! (2) The ice volume implied by g(h) equals aicen(n)*hicen(n).
!
! Given g(h), at each boundary compute the ice area and volume lying
! between the original and new boundary locations. Transfer area
! and volume across each boundary in the appropriate direction, thus
! restoring the original boundaries.
!
! !REVISION HISTORY:
!
! authors: William H. Lipscomb, LANL
! Elizabeth C. Hunke, LANL
!
! !USES:
!
use ice_itd
, only: hin_max, hi_min, aggregate_area, shift_ice, &
column_sum, column_conservation_check
!
! !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(in) :: &
aicen_init, & ! initial ice concentration (before vertical thermo)
vicen_init ! initial ice volume (m)
real (kind=dbl_kind), dimension (nx_block,ny_block,ncat), &
intent(inout) :: &
aicen , & ! ice concentration
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 , & ! concentration of ice
aice0 ! concentration of open water
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, nd , & ! category indices
k ! ice layer index
real (kind=dbl_kind) :: &
slope , & ! rate of change of dhice with hice
dh0 , & ! change in ice thickness at h = 0
da0 , & ! area melting from category 1
damax , & ! max allowed reduction in category 1 area
etamin, etamax,& ! left and right limits of integration
x1 , & ! etamax - etamin
x2 , & ! (etamax^2 - etamin^2) / 2
x3 , & ! (etamax^3 - etamin^3) / 3
wk1, wk2 ! temporary variables
real (kind=dbl_kind), dimension(icells,0:ncat) :: &
hbnew ! new boundary locations
real (kind=dbl_kind), dimension(icells) :: &
work ! temporary work array (for hbnew)
integer (kind=int_kind), dimension(icells) :: &
indxii, indxjj,& ! compressed i/j indices
indxij ! compressed 1D i/j indices
real (kind=dbl_kind), dimension(:,:), allocatable :: &
g0 , & ! constant coefficient in g(h)
g1 , & ! linear coefficient in g(h)
hL , & ! left end of range over which g(h) > 0
hR ! right end of range over which g(h) > 0
real (kind=dbl_kind), dimension(icells,ncat) :: &
hicen , & ! ice thickness for each cat (m)
hicen_init , & ! initial ice thickness for each cat (pre-thermo)
dhicen , & ! thickness change for remapping (m)
daice , & ! ice area transferred across boundary
dvice ! ice volume transferred across boundary
real (kind=dbl_kind), dimension(icells) :: &
vice_init, vice_final, & ! ice volume summed over categories
vsno_init, vsno_final, & ! snow volume summed over categories
eice_init, eice_final, & ! ice energy summed over categories
esno_init, esno_final ! snow energy summed over categories
! 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) :: &
donor ! donor category index
logical (kind=log_kind), dimension(icells) :: &
remap_flag ! remap ITD if remap_flag(ij) is true
character (len=char_len) :: &
fieldid ! field identifier
logical (kind=log_kind), parameter :: &
l_conservation_check = .true. ! if true, check conservation
! l_conservation_check = .false. ! if true, check conservation
! (useful for debugging)
integer (kind=int_kind) :: &
iflag , & ! number of grid cells with remap_flag = .true.
ij, m ! combined horizontal indices
!-----------------------------------------------------------------
! Initialize
!-----------------------------------------------------------------
l_stop = .false.
istop = 0
jstop = 0
hin_max(ncat) = 999.9_dbl_kind ! arbitrary big number
!-----------------------------------------------------------------
! Compute volume and energy sums that linear remapping should
! conserve.
!-----------------------------------------------------------------
if (l_conservation_check) then
call column_sum
(nx_block, ny_block, &
icells, indxi, indxj, &
ncat, &
vicen, vice_init)
call column_sum
(nx_block, ny_block, &
icells, indxi, indxj, &
ncat, &
vsnon, vsno_init)
call column_sum
(nx_block, ny_block, &
icells, indxi, indxj, &
ntilyr, &
eicen, eice_init)
call column_sum
(nx_block, ny_block, &
icells, indxi, indxj, &
ntslyr, &
esnon, esno_init)
endif
!-----------------------------------------------------------------
! Initialize remapping flag.
! Remapping is done wherever remap_flag = .true.
! In rare cases the category boundaries may shift too far for the
! remapping algorithm to work, and remap_flag is set to .false.
! In these cases the simpler 'rebin' subroutine will shift ice
! between categories if needed.
!-----------------------------------------------------------------
do ij = 1, icells
remap_flag(ij) = .true.
enddo
!-----------------------------------------------------------------
! Compute thickness change in each category.
!-----------------------------------------------------------------
do n = 1, ncat
do ij = 1, icells ! aice(i,j) > puny
i = indxi(ij)
j = indxj(ij)
if (aicen_init(i,j,n) > puny) then
hicen_init (ij,n) = vicen_init(i,j,n) / &
aicen_init(i,j,n)
else
hicen_init(ij,n) = c0
endif ! aicen_init > puny
if (aicen(i,j,n) > puny) then
hicen (ij,n) = vicen(i,j,n) / aicen(i,j,n)
dhicen(ij,n) = hicen(ij,n) - hicen_init(ij,n)
else
hicen (ij,n) = c0
dhicen(ij,n) = c0
endif ! aicen > puny
enddo ! ij
enddo ! n
!-----------------------------------------------------------------
! Compute new category boundaries, hbnew, based on changes in
! ice thickness from vertical thermodynamics.
!-----------------------------------------------------------------
do ij = 1, icells ! aice(i,j) > puny
hbnew(ij,0) = hin_max(0)
enddo
do n = 1, ncat-1
!DIR$ CONCURRENT !Cray
!cdir nodep !NEC
!ocl novrec !Fujitsu
do ij = 1, icells ! aice(i,j) > puny
i = indxi(ij)
j = indxj(ij)
if (hicen_init(ij,n) > puny .and. &
hicen_init(ij,n+1) > puny) then
! interpolate between adjacent category growth rates
slope = (dhicen(ij,n+1) - dhicen(ij,n)) / &
(hicen_init(ij,n+1) - hicen_init(ij,n))
hbnew(ij,n) = hin_max(n) + dhicen(ij,n) &
+ slope * (hin_max(n) - hicen_init(ij,n))
elseif (hicen_init(ij,n) > puny) then ! hicen_init(n+1)=0
hbnew(ij,n) = hin_max(n) + dhicen(ij,n)
elseif (hicen_init(ij,n+1) > puny) then ! hicen_init(n)=0
hbnew(ij,n) = hin_max(n) + dhicen(ij,n+1)
else
hbnew(ij,n) = hin_max(n)
endif
!-----------------------------------------------------------------
! Check that each boundary lies between adjacent values of hicen.
! If not, set remap_flag = .false.
! Write diagnosis outputs if remap_flag was changed to false
!-----------------------------------------------------------------
if (aicen(i,j,n) > puny .and. &
hicen(ij,n) >= hbnew(ij,n)) then
remap_flag(ij) = .false.
write(nu_diag,*) my_task,':',i,j, &
'ITD: hicen(n) > hbnew(n)'
write(nu_diag,*) 'cat ',n
write(nu_diag,*) my_task,':',i,j, &
'hicen(n) =', hicen(ij,n)
write(nu_diag,*) my_task,':',i,j, &
'hbnew(n) =', hbnew(ij,n)
elseif (aicen(i,j,n+1) > puny .and. &
hicen(ij,n+1) <= hbnew(ij,n)) then
remap_flag(ij) = .false.
write(nu_diag,*) my_task,':',i,j, &
'ITD: hicen(n+1) < hbnew(n)'
write(nu_diag,*) 'cat ',n
write(nu_diag,*) my_task,':',i,j, &
'hicen(n+1) =', hicen(ij,n+1)
write(nu_diag,*) my_task,':',i,j, &
'hbnew(n) =', hbnew(ij,n)
endif
!-----------------------------------------------------------------
! Check that hbnew(n) lies between hin_max(n-1) and hin_max(n+1).
! If not, set remap_flag = .false.
! (In principle we could allow this, but it would make the code
! more complicated.)
! Write diagnosis outputs if remap_flag was changed to false
!-----------------------------------------------------------------
if (hbnew(ij,n) > hin_max(n+1)) then
remap_flag(ij) = .false.
write(nu_diag,*) my_task,':',i,j, &
'ITD hbnew(n) > hin_max(n+1)'
write(nu_diag,*) 'cat ',n
write(nu_diag,*) my_task,':',i,j, &
'hbnew(n) =', hbnew(ij,n)
write(nu_diag,*) my_task,':',i,j, &
'hin_max(n+1) =', hin_max(n+1)
endif
if (hbnew(ij,n) < hin_max(n-1)) then
remap_flag(ij) = .false.
write(nu_diag,*) my_task,':',i,j, &
'ITD: hbnew(n) < hin_max(n-1)'
write(nu_diag,*) 'cat ',n
write(nu_diag,*) my_task,':',i,j, &
'hbnew(n) =', hbnew(ij,n)
write(nu_diag,*) my_task,':',i,j, &
'hin_max(n-1) =', hin_max(n-1)
endif
enddo ! ij
enddo ! boundaries, 1 to ncat-1
!-----------------------------------------------------------------
! Compute hbnew(ncat)
!-----------------------------------------------------------------
!DIR$ CONCURRENT !Cray
!cdir nodep !NEC
!ocl novrec !Fujitsu
do ij = 1, icells ! aice(i,j) > puny
i = indxi(ij)
j = indxj(ij)
if (aicen(i,j,ncat) > puny) then
hbnew(ij,ncat) = c3*hicen(ij,ncat) - c2*hbnew(ij,ncat-1)
else
hbnew(ij,ncat) = hin_max(ncat)
endif
hbnew(ij,ncat) = max(hbnew(ij,ncat),hin_max(ncat-1))
enddo
!-----------------------------------------------------------------
! Identify cells where the ITD is to be remapped
!-----------------------------------------------------------------
iflag = 0
do ij = 1, icells
i = indxi(ij)
j = indxj(ij)
if (remap_flag(ij)) then
iflag = iflag + 1
indxii(iflag) = i
indxjj(iflag) = j
indxij(iflag) = ij
endif
enddo
allocate(g0(iflag,ncat))
allocate(g1(iflag,ncat))
allocate(hL(iflag,ncat))
allocate(hR(iflag,ncat))
!-----------------------------------------------------------------
! Compute g(h) for category 1 at start of time step
! (hicen = hicen_init)
!-----------------------------------------------------------------
do ij = 1, icells ! aice(i,j) > puny
work(ij) = hin_max(1)
enddo
call fit_line
(nx_block, ny_block, &
iflag, icells, &
indxii, indxjj, indxij, &
aicen(:,:,1), hicen_init(:,1), &
hbnew(:,0), work (:), &
g0 (:,1), g1 (:,1), &
hL (:,1), hR (:,1))
!-----------------------------------------------------------------
! Find area lost due to melting of thin (category 1) ice
!-----------------------------------------------------------------
!DIR$ CONCURRENT !Cray
!cdir nodep !NEC
!ocl novrec !Fujitsu
do ij = 1, iflag ! remap_flag = .true.
i = indxii(ij)
j = indxjj(ij)
m = indxij(ij)
if (aicen(i,j,1) > puny) then
dh0 = dhicen(m,1)
if (dh0 < c0) then ! remove area from category 1
dh0 = min(-dh0,hin_max(1)) ! dh0 --> |dh0|
!-----------------------------------------------------------------
! Integrate g(1) from 0 to dh0 to estimate area melted
!-----------------------------------------------------------------
! right integration limit (left limit = 0)
etamax = min(dh0,hR(ij,1)) - hL(ij,1)
if (etamax > c0) then
x1 = etamax
x2 = p5 * etamax*etamax
da0 = g1(ij,1)*x2 + g0(ij,1)*x1 ! ice area removed
! constrain new thickness <= hicen_init
damax = aicen(i,j,1) &
* (c1-hicen(m,1)/hicen_init(m,1)) ! damax > 0
da0 = min (da0, damax)
! remove area, conserving volume
hicen(m,1) = hicen(m,1) &
* aicen(i,j,1) / (aicen(i,j,1)-da0)
aicen(i,j,1) = aicen(i,j,1) - da0
endif ! etamax > 0
else ! dh0 >= 0
hbnew(m,0) = min(dh0,hin_max(1)) ! shift hbnew(0) to right
endif
endif ! aicen(i,j,1) > puny
enddo ! ij
!-----------------------------------------------------------------
! Compute g(h) for each ice thickness category.
!-----------------------------------------------------------------
do n = 1, ncat
call fit_line
(nx_block, ny_block, &
iflag, icells, &
indxii, indxjj, indxij, &
aicen(:,:,n), hicen(:,n), &
hbnew(:,n-1), hbnew(:,n), &
g0 (:,n), g1 (:,n), &
hL (:,n), hR (:,n))
enddo
!-----------------------------------------------------------------
! Compute area and volume to be shifted across each boundary.
!-----------------------------------------------------------------
do n = 1, ncat
do ij = 1, icells
donor(ij,n) = 0
daice(ij,n) = c0
dvice(ij,n) = c0
enddo
enddo
do n = 1, ncat-1
!DIR$ CONCURRENT !Cray
!cdir nodep !NEC
!ocl novrec !Fujitsu
do ij = 1, iflag ! remap_flag = .true.
i = indxii(ij)
j = indxjj(ij)
m = indxij(ij)
if (hbnew(m,n) > hin_max(n)) then ! transfer from n to n+1
! left and right integration limits in eta space
etamin = max(hin_max(n), hL(ij,n)) - hL(ij,n)
etamax = min(hbnew(m,n), hR(ij,n)) - hL(ij,n)
donor(m,n) = n
else ! hbnew(n) <= hin_max(n); transfer from n+1 to n
! left and right integration limits in eta space
etamin = c0
etamax = min(hin_max(n), hR(ij,n+1)) - hL(ij,n+1)
donor(m,n) = n+1
endif ! hbnew(n) > hin_max(n)
if (etamax > etamin) then
x1 = etamax - etamin
wk1 = etamin*etamin
wk2 = etamax*etamax
x2 = p5 * (wk2 - wk1)
wk1 = wk1*etamin
wk2 = wk2*etamax
x3 = p333 * (wk2 - wk1)
nd = donor(m,n)
daice(m,n) = g1(ij,nd)*x2 + g0(ij,nd)*x1
dvice(m,n) = g1(ij,nd)*x3 + g0(ij,nd)*x2 &
+ daice(m,n)*hL(ij,nd)
endif ! etamax > etamin
! If daice or dvice is very small, shift no ice.
nd = donor(m,n)
if (daice(m,n) < aicen(i,j,nd)*puny) then
daice(m,n) = c0
dvice(m,n) = c0
donor(m,n) = 0
endif
if (dvice(m,n) < vicen(i,j,nd)*puny) then
daice(m,n) = c0
dvice(m,n) = c0
donor(m,n) = 0
endif
! If daice is close to aicen or dvice is close to vicen,
! shift entire category
if (daice(m,n) > aicen(i,j,nd)*(c1-puny)) then
daice(m,n) = aicen(i,j,nd)
dvice(m,n) = vicen(i,j,nd)
endif
if (dvice(m,n) > vicen(i,j,nd)*(c1-puny)) then
daice(m,n) = aicen(i,j,nd)
dvice(m,n) = vicen(i,j,nd)
endif
enddo ! ij
enddo ! boundaries, 1 to ncat-1
deallocate(g0)
deallocate(g1)
deallocate(hL)
deallocate(hR)
!-----------------------------------------------------------------
! Shift ice between categories as necessary
!-----------------------------------------------------------------
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
!-----------------------------------------------------------------
! Make sure hice(i,j,1) >= minimum ice thickness hi_min.
!-----------------------------------------------------------------
!DIR$ CONCURRENT !Cray
!cdir nodep !NEC
!ocl novrec !Fujitsu
do ij = 1, iflag ! remap_flag = .true.
i = indxii(ij)
j = indxjj(ij)
m = indxij(ij)
if (hi_min > c0 .and. &
aicen(i,j,1) > puny .and. hicen(m,1) < hi_min) then
aicen(i,j,1) = aicen(i,j,1) * hicen(m,1)/hi_min
hicen(m,1) = hi_min
endif
enddo ! ij
!-----------------------------------------------------------------
! Update fractional ice area in each grid cell.
!-----------------------------------------------------------------
call aggregate_area
(nx_block, ny_block, &
aicen, &
aice, aice0)
if (l_stop) return
!-----------------------------------------------------------------
! Check volume and energy conservation.
!-----------------------------------------------------------------
if (l_conservation_check) then
call column_sum
(nx_block, ny_block, &
icells, indxi, indxj, &
ncat, &
vicen, vice_final)
fieldid = 'vice, ITD remap'
call column_conservation_check
(nx_block, ny_block, &
icells, indxi, indxj, &
fieldid, &
vice_init, vice_final, &
puny, l_stop, &
istop, jstop)
if (l_stop) return
call column_sum
(nx_block, ny_block, &
icells, indxi, indxj, &
ncat, &
vsnon, vsno_final)
fieldid = 'vsno, ITD remap'
call column_conservation_check
(nx_block, ny_block, &
icells, indxi, indxj, &
fieldid, &
vsno_init, vsno_final, &
puny, l_stop, &
istop, jstop)
if (l_stop) return
call column_sum
(nx_block, ny_block, &
icells, indxi, indxj, &
ntilyr, &
eicen, eice_final)
fieldid = 'eice, ITD remap'
call column_conservation_check
(nx_block, ny_block, &
icells, indxi, indxj, &
fieldid, &
eice_init, eice_final, &
puny*Lfresh*rhoi, &
l_stop, &
istop, jstop)
if (l_stop) return
call column_sum
(nx_block, ny_block, &
icells, indxi, indxj, &
ntslyr, &
esnon, esno_final)
fieldid = 'esno, ITD remap'
call column_conservation_check
(nx_block, ny_block, &
icells, indxi, indxj, &
fieldid, &
esno_init, esno_final, &
puny*Lfresh*rhos, &
l_stop, &
istop, jstop)
if (l_stop) return
endif ! conservation check
end subroutine linear_itd
!=======================================================================
!BOP
!
! !IROUTINE: fit_line - fit g(h) with a line using area, volume constraints
!
! !INTERFACE:
!
subroutine fit_line (nx_block, ny_block, & 2
iflag, icells, &
indxii, indxjj, indxij, &
aicen, hice, &
hbL, hbR, &
g0, g1, &
hL, hR)
!
! !DESCRIPTION:
!
! Fit g(h) with a line, satisfying area and volume constraints.
! To reduce roundoff errors caused by large values of g0 and g1,
! we actually compute g(eta), where eta = h - hL, and hL is the
! left boundary.
!
! !REVISION HISTORY:
!
! authors: William H. Lipscomb, LANL
! 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
iflag ! number of grid cells with remap_flag = .true.
integer (kind=int_kind), dimension (icells), &
intent(in) :: &
indxii, indxjj, & ! compressed i/j indices (from iflag)
indxij ! compressed i/j indices (from icells)
real (kind=dbl_kind), dimension (nx_block,ny_block), intent(in) :: &
aicen ! concentration of ice
real (kind=dbl_kind), dimension (icells), intent(in) :: &
hbL, hbR , & ! left and right category boundaries
hice ! ice thickness
real (kind=dbl_kind), dimension (iflag), intent(out):: &
g0, g1 , & ! coefficients in linear equation for g(eta)
hL , & ! min value of range over which g(h) > 0
hR ! max value of range over which g(h) > 0
!
!EOP
!
integer (kind=int_kind) :: &
i,j , & ! horizontal indices
ij, m ! combined horizontal indices
real (kind=dbl_kind) :: &
h13 , & ! hbL + 1/3 * (hbR - hbL)
h23 , & ! hbL + 2/3 * (hbR - hbL)
dhr , & ! 1 / (hR - hL)
wk1, wk2 ! temporary variables
!-----------------------------------------------------------------
! Compute g0, g1, hL, and hR for each category to be remapped.
!-----------------------------------------------------------------
do ij = 1, iflag
i = indxii(ij)
j = indxjj(ij)
m = indxij(ij)
if (aicen(i,j) > puny .and. hbR(m) - hbL(m) > puny) then
! Initialize hL and hR
hL(ij) = hbL(m)
hR(ij) = hbR(m)
! Change hL or hR if hicen(n) falls outside central third of range
h13 = p333 * (c2*hL(ij) + hR(ij))
h23 = p333 * (hL(ij) + c2*hR(ij))
if (hice(m) < h13) then
hR(ij) = c3*hice(m) - c2*hL(ij)
elseif (hice(m) > h23) then
hL(ij) = c3*hice(m) - c2*hR(ij)
endif
! Compute coefficients of g(eta) = g0 + g1*eta
dhr = c1 / (hR(ij) - hL(ij))
wk1 = c6 * aicen(i,j) * dhr
wk2 = (hice(m) - hL(ij)) * dhr
g0(ij) = wk1 * (p666 - wk2)
g1(ij) = c2*dhr * wk1 * (wk2 - p5)
else
g0(ij) = c0
g1(ij) = c0
hL(ij) = c0
hR(ij) = c0
endif ! aicen > puny
enddo ! ij
end subroutine fit_line
!=======================================================================
!BOP
!
! !ROUTINE: add_new_ice - add frazil ice to ice thickness distribution
!
! !DESCRIPTION:
!
! Given the volume of new ice grown in open water, compute its area
! and thickness and add it to the appropriate category or categories.
!
! NOTE: Usually all the new ice is added to category 1. An exception is
! made if there is no open water or if the new ice is too thick
! for category 1, in which case ice is distributed evenly over the
! entire cell. Subroutine rebin should be called in case the ice
! thickness lies outside category bounds after new ice formation.
!
! !REVISION HISTORY:
!
! authors William H. Lipscomb, LANL
! Elizabeth C. Hunke, LANL
!
! !INTERFACE:
!
subroutine add_new_ice (nx_block, ny_block, & 1,6
ntrcr, icells, &
indxi, indxj, &
tmask, dt, &
aicen, trcrn, &
vicen, eicen, &
aice0, aice, &
frzmlt, frazil, &
frz_onset, yday, &
fresh, fsalt, &
Tf, l_stop, &
istop, jstop)
!
! !USES:
!
use ice_itd
, only: hin_max, ilyr1, column_sum, &
column_conservation_check
use ice_state
, only: nt_Tsfc, nt_iage, nt_FY, nt_aero, &
tr_iage, tr_FY, tr_aero, &
nt_alvl, nt_vlvl, tr_lvl
use ice_flux
, only: update_ocn_f
! !INPUT/OUTPUT PARAMETERS:
!
integer (kind=int_kind), intent(in) :: &
nx_block, ny_block, & ! block dimensions
ntrcr , & ! number of tracers in use
icells ! number of ice/ocean grid cells
integer (kind=int_kind), dimension (nx_block*ny_block), &
intent(in) :: &
indxi, indxj ! compressed i/j indices
logical (kind=log_kind), dimension (nx_block,ny_block), &
intent(in) :: &
tmask ! land/boundary mask, thickness (T-cell)
real (kind=dbl_kind), intent(in) :: &
dt ! time step (s)
real (kind=dbl_kind), dimension (nx_block,ny_block), intent(in) :: &
aice , & ! total concentration of ice
frzmlt, & ! freezing/melting potential (W/m^2)
Tf ! freezing temperature (C)
real (kind=dbl_kind), dimension (nx_block,ny_block,ncat), &
intent(inout) :: &
aicen , & ! concentration of ice
vicen ! volume per unit area of ice (m)
real (kind=dbl_kind), dimension (nx_block,ny_block,max_ntrcr,ncat), &
intent(inout) :: &
trcrn ! ice tracers
! 1: surface temperature
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), &
intent(inout) :: &
aice0 , & ! concentration of open water
frazil , & ! frazil ice growth (m/step-->cm/day)
fresh , & ! fresh water flux to ocean (kg/m^2/s)
fsalt ! salt flux to ocean (kg/m^2/s)
real (kind=dbl_kind), dimension (nx_block,ny_block), &
intent(inout), optional :: &
frz_onset ! day of year that freezing begins (congel or frazil)
real (kind=dbl_kind), intent(in), optional :: &
yday ! day of year
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 , & ! ice category index
k , & ! ice layer index
it ! aerosol tracer index
real (kind=dbl_kind), dimension (icells) :: &
ai0new , & ! area of new ice added to cat 1
vi0new , & ! volume of new ice added to cat 1
hsurp , & ! thickness of new ice added to each cat
vlyr ! ice layer volume
real (kind=dbl_kind), dimension (icells) :: &
vice_init, vice_final ! ice volume summed over categories
real (kind=dbl_kind) :: &
fnew , & ! heat flx to open water for new ice (W/m^2)
hi0new , & ! thickness of new ice
hi0max , & ! max allowed thickness of new ice
qi0(nilyr) , & ! frazil ice enthalpy
qi0av , & ! mean value of qi0 for new ice (J kg-1)
vsurp , & ! volume of new ice added to each cat
area1 , & ! starting fractional area of existing ice
vice1 , & ! starting volume of existing ice
rnilyr , & ! real(nilyr)
dfresh , & ! change in fresh
dfsalt , & ! change in fsalt
vtmp
integer (kind=int_kind) :: &
jcells, kcells , & ! grid cell counters
ij, m ! combined i/j horizontal indices
integer (kind=int_kind), dimension (icells) :: &
indxij2, indxij3 , & ! compressed i/j indices
indxi2, indxj2 , &
indxi3, indxj3
character (len=char_len) :: &
fieldid ! field identifier
l_stop = .false.
istop = 0
jstop = 0
jcells = 0
kcells = 0
if (ncat > 1) then
hi0max = hin_max(1)*0.9_dbl_kind ! not too close to boundary
else
hi0max = bignum ! big number
endif
! initial ice volume in each grid cell
call column_sum
(nx_block, ny_block, &
icells, indxi, indxj, &
ncat, &
vicen, vice_init)
!-----------------------------------------------------------------
! Compute average enthalpy of new ice.
!
! POP assumes new ice is fresh. Otherwise, it would be better
! to do something like this:
! qi0(i,j,k) = -rhoi * (cp_ice*(Tmlt(k)-Tf(i,j))
! + Lfresh*(1.-Tmlt(k)/Tf(i,j)) - cp_ocn*Tmlt(k))
!-----------------------------------------------------------------
rnilyr = real(nilyr,kind=dbl_kind)
qi0av = c0
do k = 1, nilyr
qi0(k) = -rhoi*Lfresh ! note sign convention, qi < 0
qi0av = qi0av + qi0(k)
enddo
qi0av = qi0av/rnilyr
!-----------------------------------------------------------------
! Compute the volume, area, and thickness of new ice.
!-----------------------------------------------------------------
!DIR$ CONCURRENT !Cray
!cdir nodep !NEC
!ocl novrec !Fujitsu
do ij = 1, icells
i = indxi(ij)
j = indxj(ij)
fnew = max (frzmlt(i,j), c0) ! fnew > 0 iff frzmlt > 0
vi0new(ij) = -fnew*dt / qi0av ! note sign convention, qi < 0
! increment ice volume
vice_init(ij) = vice_init(ij) + vi0new(ij)
! history diagnostics
frazil(i,j) = vi0new(ij)
if (present(frz_onset) .and. present(yday)) then
if (frazil(i,j) > puny .and. frz_onset(i,j) < puny) &
frz_onset(i,j) = yday
endif
!-----------------------------------------------------------------
! Update fresh water and salt fluxes.
!
! NOTE: POP assumes fresh water and salt flux due to frzmlt > 0
! is NOT included in fluxes fresh and fsalt.
!-----------------------------------------------------------------
if (update_ocn_f) then
dfresh = -rhoi*vi0new(ij)/dt
dfsalt = ice_ref_salinity*p001*dfresh
fresh(i,j) = fresh(i,j) + dfresh
fsalt(i,j) = fsalt(i,j) + dfsalt
endif
!-----------------------------------------------------------------
! Decide how to distribute the new ice.
!-----------------------------------------------------------------
hsurp(ij) = c0
ai0new(ij) = c0
if (vi0new(ij) > c0) then
! new ice area and thickness
! hin_max(0) < new ice thickness < hin_max(1)
if (aice0(i,j) > puny) then
hi0new = max(vi0new(ij)/aice0(i,j), hfrazilmin)
if (hi0new > hi0max .and. aice0(i,j)+puny < c1) then
! distribute excess volume over all categories (below)
hi0new = hi0max
ai0new(ij) = aice0(i,j)
vsurp = vi0new(ij) - ai0new(ij)*hi0new
hsurp(ij) = vsurp / aice(i,j)
vi0new(ij) = ai0new(ij)*hi0new
else
! put ice in a single category, with hsurp = 0
ai0new(ij) = vi0new(ij)/hi0new
endif
else ! aice0 < puny
hsurp(ij) = vi0new(ij)/aice(i,j) ! new thickness in each cat
vi0new(ij) = c0
endif ! aice0 > puny
endif ! vi0new > puny
!-----------------------------------------------------------------
! Identify grid cells receiving new ice.
!-----------------------------------------------------------------
i = indxi(ij)
j = indxj(ij)
if (vi0new(ij) > c0) then ! add ice to category 1
jcells = jcells + 1
indxi2(jcells) = i
indxj2(jcells) = j
indxij2(jcells) = ij
endif
if (hsurp(ij) > c0) then ! add ice to all categories
kcells = kcells + 1
indxi3(kcells) = i
indxj3(kcells) = j
indxij3(kcells) = ij
endif
enddo ! ij
!-----------------------------------------------------------------
! Distribute excess ice volume among ice categories by increasing
! ice thickness, leaving ice area unchanged.
!
! NOTE: If new ice contains globally conserved tracers
! (e.g., isotopes from seawater), code must be added here.
!-----------------------------------------------------------------
do n = 1, ncat
!DIR$ CONCURRENT !Cray
!cdir nodep !NEC
!ocl novrec !Fujitsu
do ij = 1, kcells
i = indxi3(ij)
j = indxj3(ij)
m = indxij3(ij)
vsurp = hsurp(m) * aicen(i,j,n)
! update ice age due to freezing (new ice age = dt)
vtmp = vicen(i,j,n) + vsurp
if (vtmp > puny) then
if (tr_iage) trcrn(i,j,nt_iage,n) &
= (trcrn(i,j,nt_iage,n)*vicen(i,j,n) + dt*vsurp) &
/ vtmp
if (tr_lvl) &
trcrn(i,j,nt_vlvl,n) = &
(trcrn(i,j,nt_vlvl,n)*vicen(i,j,n) + &
trcrn(i,j,nt_alvl,n)*vsurp) / vtmp
if (tr_aero) then
do it=1,n_aero
trcrn(i,j,nt_aero+2+4*(it-1),n) &
= trcrn(i,j,nt_aero+2+4*(it-1),n)*vicen(i,j,n) &
/ vtmp
trcrn(i,j,nt_aero+3+4*(it-1),n) &
= trcrn(i,j,nt_aero+3+4*(it-1),n)*vicen(i,j,n) &
/ vtmp
enddo
endif
endif
! update category volumes
vicen(i,j,n) = vicen(i,j,n) + vsurp
vlyr(m) = vsurp/rnilyr
enddo ! ij
do k = 1, nilyr
!DIR$ CONCURRENT !Cray
!cdir nodep !NEC
!ocl novrec !Fujitsu
do ij = 1, kcells
i = indxi3(ij)
j = indxj3(ij)
m = indxij3(ij)
eicen(i,j,ilyr1(n)+k-1) = &
eicen(i,j,ilyr1(n)+k-1) + qi0(k)*vlyr(m)
enddo ! ij
enddo ! k
enddo ! n
!-----------------------------------------------------------------
! Combine new ice grown in open water with category 1 ice.
! Assume that vsnon and esnon are unchanged.
!-----------------------------------------------------------------
!DIR$ CONCURRENT !Cray
!cdir nodep !NEC
!ocl novrec !Fujitsu
do ij = 1, jcells
i = indxi2(ij)
j = indxj2(ij)
m = indxij2(ij)
area1 = aicen(i,j,1) ! save
vice1 = vicen(i,j,1) ! save
aicen(i,j,1) = aicen(i,j,1) + ai0new(m)
aice0(i,j) = aice0(i,j) - ai0new(m)
vicen(i,j,1) = vicen(i,j,1) + vi0new(m)
trcrn(i,j,nt_Tsfc,1) = (Tf(i,j)*ai0new(m) + trcrn(i,j,nt_Tsfc,1)*area1) &
/ aicen(i,j,1)
trcrn(i,j,nt_Tsfc,1) = min (trcrn(i,j,nt_Tsfc,1), c0)
if (vicen(i,j,1) > puny) then
if (tr_iage) trcrn(i,j,nt_iage,1) = &
(trcrn(i,j,nt_iage,1)*vice1 + dt*vi0new(m))/vicen(i,j,1)
if (tr_aero) then
do it=1,n_aero
trcrn(i,j,nt_aero+2+4*(it-1),1) = &
trcrn(i,j,nt_aero+2+4*(it-1),1)*vice1/vicen(i,j,1)
trcrn(i,j,nt_aero+3+4*(it-1),1) = &
trcrn(i,j,nt_aero+3+4*(it-1),1)*vice1/vicen(i,j,1)
enddo
endif
endif
if (tr_lvl .and. aicen(i,j,1) > puny) then
trcrn(i,j,nt_alvl,1) = &
(trcrn(i,j,nt_alvl,1)*area1 + ai0new(m))/aicen(i,j,1)
trcrn(i,j,nt_vlvl,1) = &
(trcrn(i,j,nt_vlvl,1)*vice1 + vi0new(m))/vicen(i,j,1)
endif
if (tr_FY) &
trcrn(i,j,nt_FY,1) = (ai0new(m) + trcrn(i,j,nt_FY,1)*area1) &
/ aicen(i,j,1)
vlyr(m) = vi0new(m) / rnilyr
enddo ! ij
do k = 1, nilyr
!DIR$ CONCURRENT !Cray
!cdir nodep !NEC
!ocl novrec !Fujitsu
do ij = 1, jcells
i = indxi2(ij)
j = indxj2(ij)
m = indxij2(ij)
eicen(i,j,k) = eicen(i,j,k) + qi0(k)*vlyr(m)
enddo
enddo
call column_sum
(nx_block, ny_block, &
icells, indxi, indxj, &
ncat, &
vicen, vice_final)
fieldid = 'vice, add_new_ice'
call column_conservation_check
(nx_block, ny_block, &
icells, indxi, indxj, &
fieldid, &
vice_init, vice_final, &
puny, l_stop, &
istop, jstop)
if (l_stop) return
end subroutine add_new_ice
!=======================================================================
!BOP
!
! !ROUTINE: lateral_melt - melt ice laterally
!
! !DESCRIPTION:
!
! Given the fraction of ice melting laterally in each grid cell
! (computed in subroutine frzmlt_bottom_lateral), melt ice.
!
! !REVISION HISTORY:
!
! author: C. M. Bitz, UW
! 2003: Modified by William H. Lipscomb and Elizabeth C. Hunke, LANL
!
! !INTERFACE:
!
subroutine lateral_melt (nx_block, ny_block, & 1,2
ilo, ihi, jlo, jhi, &
dt, &
fresh, fsalt, &
fhocn, fsoot, &
rside, meltl, &
aicen, vicen, &
vsnon, eicen, &
esnon, trcrn)
!
! !USES:
!
use ice_itd
, only: ilyr1, slyr1
use ice_state
, only: nt_aero, tr_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
real (kind=dbl_kind), intent(in) :: &
dt ! time step (s)
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(in) :: &
trcrn ! energy of melting for each snow layer (J/m^2)
real (kind=dbl_kind), dimension(nx_block,ny_block), intent(in) :: &
rside ! fraction of ice that melts laterally
real (kind=dbl_kind), dimension(nx_block,ny_block), &
intent(inout) :: &
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)
meltl ! lateral ice melt (m/step-->cm/day)
real (kind=dbl_kind), dimension(nx_block,ny_block,n_aeromx), &
intent(inout) :: &
fsoot !
!
!EOP
!
integer (kind=int_kind) :: &
i, j , & ! horizontal indices
n , & ! thickness category index
k , & ! layer index
ij , & ! horizontal index, combines i and j loops
icells , & ! number of cells with aice > puny
it ! tracer index for aerosols
integer (kind=int_kind), dimension(nx_block*ny_block) :: &
indxi, indxj ! compressed indices for cells with aice > puny
real (kind=dbl_kind) :: &
dfhocn , & ! change in fhocn
dfresh , & ! change in fresh
dfsalt ! change in fsalt
do n = 1, ncat
!-----------------------------------------------------------------
! Identify grid cells with lateral melting.
!-----------------------------------------------------------------
icells = 0
do j = jlo, jhi
do i = ilo, ihi
if (rside(i,j) > c0) then
icells = icells + 1
indxi(icells) = i
indxj(icells) = j
endif
enddo ! i
enddo ! j
!-----------------------------------------------------------------
! Melt the ice and increment fluxes.
!-----------------------------------------------------------------
if (tr_aero) then
do k=1,n_aero
do ij = 1, icells
i = indxi(ij)
j = indxj(ij)
fsoot(i,j,k) = fsoot(i,j,k) &
+ (vsnon(i,j,n) &
*(trcrn(i,j,nt_aero +4*(k-1),n) &
+trcrn(i,j,nt_aero+1+4*(k-1),n)) &
+ vicen(i,j,n) &
*(trcrn(i,j,nt_aero+2+4*(k-1),n) &
+trcrn(i,j,nt_aero+3+4*(k-1),n))) &
* rside(i,j) / dt
enddo
enddo
endif
!DIR$ CONCURRENT !Cray
!cdir nodep !NEC
!ocl novrec !Fujitsu
do ij = 1, icells
i = indxi(ij)
j = indxj(ij)
! fluxes to coupler
! dfresh > 0, dfsalt > 0
dfresh = (rhos*vsnon(i,j,n) + rhoi*vicen(i,j,n)) &
* rside(i,j) / dt
dfsalt = rhoi*vicen(i,j,n)*ice_ref_salinity*p001 &
* rside(i,j) / dt
fresh(i,j) = fresh(i,j) + dfresh
fsalt(i,j) = fsalt(i,j) + dfsalt
! history diagnostics
meltl(i,j) = meltl(i,j) + vicen(i,j,n)*rside(i,j)
! state variables
aicen(i,j,n) = aicen(i,j,n) * (c1 - rside(i,j))
vicen(i,j,n) = vicen(i,j,n) * (c1 - rside(i,j))
vsnon(i,j,n) = vsnon(i,j,n) * (c1 - rside(i,j))
enddo ! ij
do k = 1, nilyr
!DIR$ CONCURRENT !Cray
!cdir nodep !NEC
!ocl novrec !Fujitsu
do ij = 1, icells
i = indxi(ij)
j = indxj(ij)
! heat flux to coupler for ice melt (dfhocn < 0)
dfhocn = eicen(i,j,ilyr1(n)+k-1)*rside(i,j) / dt
fhocn(i,j) = fhocn(i,j) + dfhocn
! ice energy
eicen(i,j,ilyr1(n)+k-1) = eicen(i,j,ilyr1(n)+k-1) &
* (c1 - rside(i,j))
enddo ! ij
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)
! heat flux to coupler for snow melt (dfhocn < 0)
dfhocn = esnon(i,j,slyr1(n)+k-1)*rside(i,j) / dt
fhocn(i,j) = fhocn(i,j) + dfhocn
! snow energy
esnon(i,j,slyr1(n)+k-1) = esnon(i,j,slyr1(n)+k-1) &
* (c1 - rside(i,j))
enddo ! ij
enddo ! nslyr
enddo ! n
end subroutine lateral_melt
!=======================================================================
end module ice_therm_itd
!=======================================================================