icbthm.F90 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. MODULE icbthm
  2. !!======================================================================
  3. !! *** MODULE icbthm ***
  4. !! Icebergs: thermodynamics routines for icebergs
  5. !!======================================================================
  6. !! History : 3.3.1 ! 2010-01 (Martin&Adcroft) Original code
  7. !! - ! 2011-03 (Madec) Part conversion to NEMO form
  8. !! - ! Removal of mapping from another grid
  9. !! - ! 2011-04 (Alderson) Split into separate modules
  10. !! - ! 2011-05 (Alderson) Use tmask instead of tmask_i
  11. !!----------------------------------------------------------------------
  12. !!----------------------------------------------------------------------
  13. !! icb_thm : initialise
  14. !! reference for equations - M = Martin + Adcroft, OM 34, 2010
  15. !!----------------------------------------------------------------------
  16. USE par_oce ! NEMO parameters
  17. USE dom_oce ! NEMO domain
  18. USE in_out_manager ! NEMO IO routines, numout in particular
  19. USE lib_mpp ! NEMO MPI routines, ctl_stop in particular
  20. USE phycst ! NEMO physical constants
  21. USE sbc_oce
  22. USE eosbn2 ! equation of state
  23. USE lib_fortran, ONLY : DDPDD
  24. USE bdy_oce, ONLY : bdytmask,ln_bdy
  25. USE icb_oce ! define iceberg arrays
  26. USE icbutl ! iceberg utility routines
  27. USE icbdia ! iceberg budget routines
  28. IMPLICIT NONE
  29. PRIVATE
  30. PUBLIC icb_thm ! routine called in icbstp.F90 module
  31. !!----------------------------------------------------------------------
  32. !! NEMO/OCE 4.0 , NEMO Consortium (2018)
  33. !! $Id: icbthm.F90 15088 2021-07-06 13:03:34Z acc $
  34. !! Software governed by the CeCILL license (see ./LICENSE)
  35. !!----------------------------------------------------------------------
  36. CONTAINS
  37. SUBROUTINE icb_thm( kt )
  38. !!----------------------------------------------------------------------
  39. !! *** ROUTINE icb_thm ***
  40. !!
  41. !! ** Purpose : compute the iceberg thermodynamics.
  42. !!
  43. !! ** Method : - See Martin & Adcroft, Ocean Modelling 34, 2010
  44. !!----------------------------------------------------------------------
  45. INTEGER, INTENT(in) :: kt ! timestep number, just passed to icb_utl_print_berg
  46. !
  47. INTEGER :: ii, ij, jk, ikb
  48. REAL(wp) :: zM, zT, zW, zL, zSST, zVol, zLn, zWn, zTn, znVol, zIC, zDn, zD, zvb, zub, ztb
  49. REAL(wp) :: zMv, zMe, zMb, zmelt, zdvo, zdvob, zdva, zdM, zSs, zdMe, zdMb, zdMv
  50. REAL(wp) :: zSSS, zfzpt
  51. REAL(wp) :: zMnew, zMnew1, zMnew2, zheat_hcflux, zheat_latent, z1_12
  52. REAL(wp) :: zMbits, znMbits, zdMbitsE, zdMbitsM, zLbits, zAbits, zMbb
  53. REAL(wp) :: zxi, zyj, zff, z1_rday, z1_e1e2, zdt, z1_dt, z1_dt_e1e2, zdepw
  54. REAL(wp), DIMENSION(jpk) :: ztoce, zuoce, zvoce, ze3t, zzMv
  55. TYPE(iceberg), POINTER :: this, next
  56. TYPE(point) , POINTER :: pt
  57. !
  58. COMPLEX(dp), DIMENSION(jpi,jpj) :: cicb_melt, cicb_hflx
  59. !!----------------------------------------------------------------------
  60. !
  61. !! initialiaze cicb_melt and cicb_heat
  62. cicb_melt = CMPLX( 0.e0, 0.e0, dp )
  63. cicb_hflx = CMPLX( 0.e0, 0.e0, dp )
  64. !
  65. z1_rday = 1._wp / rday
  66. z1_12 = 1._wp / 12._wp
  67. zdt = berg_dt
  68. z1_dt = 1._wp / zdt
  69. !
  70. ! we're either going to ignore berg fresh water melt flux and associated heat
  71. ! or we pass it into the ocean, so at this point we set them both to zero,
  72. ! accumulate the contributions to them from each iceberg in the while loop following
  73. ! and then pass them (or not) to the ocean
  74. !
  75. berg_grid%floating_melt(:,:) = 0._wp
  76. ! calving_hflx re-used here as temporary workspace for the heat flux associated with melting
  77. berg_grid%calving_hflx(:,:) = 0._wp
  78. !
  79. ! virtual area set to 0
  80. virtual_area(:,:)= 0._wp
  81. virtual_area_e(:,:)= 0._wp
  82. !
  83. this => first_berg
  84. DO WHILE( ASSOCIATED(this) )
  85. !
  86. pt => this%current_point
  87. nknberg = this%number(1)
  88. CALL icb_utl_interp( pt%xi, pt%yj, & ! position
  89. & pssu=pt%ssu, pua=pt%ua, & ! oce/atm velocities
  90. & pssv=pt%ssv, pva=pt%va, & ! oce/atm velocities
  91. & psst=pt%sst, pcn=pt%cn, &
  92. & psss=pt%sss )
  93. IF ( nn_sample_rate > 0 .AND. MOD(kt-1,nn_sample_rate) == 0 ) THEN
  94. CALL icb_utl_interp( pt%xi, pt%yj, pe1=pt%e1, pe2=pt%e2, &
  95. & pui=pt%ui, pssh_i=pt%ssh_x, &
  96. & pvi=pt%vi, pssh_j=pt%ssh_y, &
  97. & phi=pt%hi, &
  98. & plat=pt%lat, plon=pt%lon )
  99. END IF
  100. !
  101. zSST = pt%sst
  102. zSSS = pt%sss
  103. CALL eos_fzp(zSSS,zfzpt) ! freezing point
  104. zIC = MIN( 1._wp, pt%cn + rn_sicn_shift ) ! Shift sea-ice concentration !!gm ???
  105. zM = pt%mass
  106. zT = pt%thickness ! total thickness
  107. zD = rho_berg_1_oce * zT ! draught (keel depth)
  108. zW = pt%width
  109. zL = pt%length
  110. zxi = pt%xi ! position in (i,j) referential
  111. zyj = pt%yj
  112. ii = INT( zxi + 0.5 ) ! T-cell of the berg
  113. ii = mi1( ii + (nn_hls-1) )
  114. ij = INT( zyj + 0.5 )
  115. ij = mj1( ij + (nn_hls-1) )
  116. zVol = zT * zW * zL
  117. ! Environment
  118. ! default sst, ssu and ssv
  119. ! ln_M2016: use temp, u and v profile
  120. IF ( ln_M2016 ) THEN
  121. ! load t, u, v and e3 profile at icb position
  122. CALL icb_utl_interp( pt%xi, pt%yj, ptoce=ztoce, puoce=zuoce, pvoce=zvoce, pe3t=ze3t )
  123. !compute bottom level
  124. CALL icb_utl_getkb( pt%kb, ze3t, zD )
  125. ikb = MIN(pt%kb,mbkt(ii,ij)) ! limit pt%kb by mbkt
  126. ! => bottom temperature used to fill ztoce(mbkt:jpk)
  127. ztb = ztoce(ikb) ! basal temperature
  128. zub = zuoce(ikb)
  129. zvb = zvoce(ikb)
  130. ELSE
  131. ztb = pt%sst
  132. zub = pt%ssu
  133. zvb = pt%ssv
  134. END IF
  135. zdvob = SQRT( (pt%uvel-zub)**2 + (pt%vvel-zvb)**2 ) ! relative basal velocity
  136. zdva = SQRT( (pt%ua -pt%ssu)**2 + (pt%va -pt%ssv)**2 ) ! relative wind
  137. zSs = 1.5_wp * SQRT( zdva ) + 0.1_wp * zdva ! Sea state (eqn M.A9)
  138. !
  139. ! Melt rates in m/s (i.e. division by rday)
  140. ! Buoyant convection at sides (eqn M.A10)
  141. IF ( ln_M2016 ) THEN
  142. ! averaging along all the iceberg draft
  143. zzMv(:) = MAX( 7.62d-3*ztoce(:)+1.29d-3*(ztoce(:)**2), 0._wp ) * z1_rday
  144. CALL icb_utl_zavg(zMv, zzMv, ze3t, zD, ikb )
  145. ELSE
  146. zMv = MAX( 7.62d-3*zSST+1.29d-3*(zSST**2), 0._wp ) * z1_rday
  147. END IF
  148. !
  149. ! Basal turbulent melting (eqn M.A7 )
  150. IF ( zSST > zfzpt ) THEN ! Calculate basal melting only if SST above freezing point
  151. zMb = MAX( 0.58_wp*(zdvob**0.8_wp)*(ztb+4.0_wp)/(zL**0.2_wp) , 0._wp ) * z1_rday
  152. ELSE
  153. zMb = 0._wp ! No basal melting if SST below freezing point
  154. ENDIF
  155. !
  156. ! Wave erosion (eqn M.A8 )
  157. zMe = MAX( z1_12*(zSST+2.)*zSs*(1._wp+COS(rpi*(zIC**3))) , 0._wp ) * z1_rday
  158. IF( ln_operator_splitting ) THEN ! Operator split update of volume/mass
  159. zTn = MAX( zT - zMb*zdt , 0._wp ) ! new total thickness (m)
  160. znVol = zTn * zW * zL ! new volume (m^3)
  161. zMnew1 = ( znVol / zVol ) * zM ! new mass (kg)
  162. zdMb = zM - zMnew1 ! mass lost to basal melting (>0) (kg)
  163. !
  164. zLn = MAX( zL - zMv*zdt , 0._wp ) ! new length (m)
  165. zWn = MAX( zW - zMv*zdt , 0._wp ) ! new width (m)
  166. znVol = zTn * zWn * zLn ! new volume (m^3)
  167. zMnew2 = ( znVol / zVol ) * zM ! new mass (kg)
  168. zdMv = zMnew1 - zMnew2 ! mass lost to buoyant convection (>0) (kg)
  169. !
  170. zLn = MAX( zLn - zMe*zdt , 0._wp ) ! new length (m)
  171. zWn = MAX( zWn - zMe*zdt , 0._wp ) ! new width (m)
  172. znVol = zTn * zWn * zLn ! new volume (m^3)
  173. zMnew = ( znVol / zVol ) * zM ! new mass (kg)
  174. zdMe = zMnew2 - zMnew ! mass lost to erosion (>0) (kg)
  175. zdM = zM - zMnew ! mass lost to all erosion and melting (>0) (kg)
  176. !
  177. ELSE ! Update dimensions of berg
  178. zLn = MAX( zL -(zMv+zMe)*zdt ,0._wp ) ! (m)
  179. zWn = MAX( zW -(zMv+zMe)*zdt ,0._wp ) ! (m)
  180. zTn = MAX( zT - zMb *zdt ,0._wp ) ! (m)
  181. ! Update volume and mass of berg
  182. znVol = zTn*zWn*zLn ! (m^3)
  183. zMnew = (znVol/zVol)*zM ! (kg)
  184. zdM = zM - zMnew ! (kg)
  185. zdMb = (zM/zVol) * (zW* zL ) *zMb*zdt ! approx. mass loss to basal melting (kg)
  186. zdMe = (zM/zVol) * (zT*(zW+zL)) *zMe*zdt ! approx. mass lost to erosion (kg)
  187. zdMv = (zM/zVol) * (zT*(zW+zL)) *zMv*zdt ! approx. mass loss to buoyant convection (kg)
  188. ENDIF
  189. IF( rn_bits_erosion_fraction > 0._wp ) THEN ! Bergy bits
  190. !
  191. zMbits = pt%mass_of_bits ! mass of bergy bits (kg)
  192. zdMbitsE = rn_bits_erosion_fraction * zdMe ! change in mass of bits (kg)
  193. znMbits = zMbits + zdMbitsE ! add new bergy bits to mass (kg)
  194. zLbits = MIN( zL, zW, zT, 40._wp ) ! assume bergy bits are smallest dimension or 40 meters
  195. zAbits = ( zMbits / rn_rho_bergs ) / zLbits ! Effective bottom area (assuming T=Lbits)
  196. zMbb = MAX( 0.58_wp*(zdvob**0.8_wp)*(zSST+2._wp) / &
  197. & ( zLbits**0.2_wp ) , 0._wp ) * z1_rday ! Basal turbulent melting (for bits)
  198. zMbb = rn_rho_bergs * zAbits * zMbb ! in kg/s
  199. zdMbitsM = MIN( zMbb*zdt , znMbits ) ! bergy bits mass lost to melting (kg)
  200. znMbits = znMbits-zdMbitsM ! remove mass lost to bergy bits melt
  201. IF( zMnew == 0._wp ) THEN ! if parent berg has completely melted then
  202. zdMbitsM = zdMbitsM + znMbits ! instantly melt all the bergy bits
  203. znMbits = 0._wp
  204. ENDIF
  205. ELSE ! No bergy bits
  206. zAbits = 0._wp
  207. zdMbitsE = 0._wp
  208. zdMbitsM = 0._wp
  209. znMbits = pt%mass_of_bits ! retain previous value incase non-zero
  210. ENDIF
  211. ! use tmask rather than tmask_i when dealing with icebergs
  212. IF( tmask(ii,ij,1) /= 0._wp ) THEN ! Add melting to the grid and field diagnostics
  213. z1_e1e2 = r1_e1e2t(ii,ij) * this%mass_scaling
  214. z1_dt_e1e2 = z1_dt * z1_e1e2
  215. !
  216. ! iceberg melt
  217. !! the use of DDPDD function for the cumulative sum is needed for reproducibility
  218. zmelt = ( zdM - ( zdMbitsE - zdMbitsM ) ) * z1_dt ! kg/s
  219. CALL DDPDD( CMPLX( zmelt * z1_e1e2, 0.e0, dp ), cicb_melt(ii,ij) )
  220. !
  221. ! iceberg heat flux
  222. !! the use of DDPDD function for the cumulative sum is needed for reproducibility
  223. !! NB. The src_calving_hflx field is currently hardwired to zero in icb_stp, which means that the
  224. !! heat density of the icebergs is zero and the heat content flux to the ocean from iceberg
  225. !! melting is always zero. Leaving the term in the code until such a time as this is fixed. DS.
  226. zheat_hcflux = zmelt * pt%heat_density ! heat content flux : kg/s x J/kg = J/s
  227. zheat_latent = - zmelt * rLfus ! latent heat flux: kg/s x J/kg = J/s
  228. CALL DDPDD( CMPLX( ( zheat_hcflux + zheat_latent ) * z1_e1e2, 0.e0, dp ), cicb_hflx(ii,ij) )
  229. !
  230. ! diagnostics
  231. CALL icb_dia_melt( ii, ij, zMnew, zheat_hcflux, zheat_latent, this%mass_scaling, &
  232. & zdM, zdMbitsE, zdMbitsM, zdMb, zdMe, &
  233. & zdMv, z1_dt_e1e2, z1_e1e2 )
  234. ELSE
  235. WRITE(numout,*) 'icb_thm: berg ',this%number(:),' appears to have grounded at ',narea,ii,ij
  236. CALL icb_utl_print_berg( this, kt )
  237. WRITE(numout,*) 'msk=',tmask(ii,ij,1), e1e2t(ii,ij)
  238. CALL ctl_stop('icb_thm', 'berg appears to have grounded!')
  239. ENDIF
  240. ! Rolling
  241. zDn = rho_berg_1_oce * zTn ! draught (keel depth)
  242. IF( zDn > 0._wp .AND. MAX(zWn,zLn) < SQRT( 0.92*(zDn**2) + 58.32*zDn ) ) THEN
  243. zT = zTn
  244. zTn = zWn
  245. zWn = zT
  246. ENDIF
  247. ! Store the new state of iceberg (with L>W)
  248. pt%mass = zMnew
  249. pt%mass_of_bits = znMbits
  250. pt%thickness = zTn
  251. pt%width = MIN( zWn , zLn )
  252. pt%length = MAX( zWn , zLn )
  253. next=>this%next
  254. !!gm add a test to avoid over melting ?
  255. !!pm I agree, over melting could break conservation (more melt than calving)
  256. IF( zMnew <= 0._wp ) THEN ! Delete the berg if completely melted
  257. CALL icb_utl_delete( first_berg, this )
  258. !
  259. ELSE IF (ln_bdy .AND. bdytmask(ii,ij)==0.) THEN !!pm + CK remove icb if at bdy
  260. CALL icb_utl_delete( first_berg, this )
  261. ELSE ! Diagnose mass distribution on grid
  262. z1_e1e2 = r1_e1e2t(ii,ij) * this%mass_scaling
  263. CALL icb_dia_size( ii, ij, zWn, zLn, zAbits, &
  264. & this%mass_scaling, zMnew, znMbits, z1_e1e2 )
  265. !
  266. ! ! diagnose virtual area on grid (ln_icb_area_mask or ln_icb_diag)
  267. virtual_area(ii,ij) = virtual_area(ii,ij) + ( zWn * zLn + zAbits ) * this%mass_scaling ! m^2
  268. !
  269. ENDIF
  270. !
  271. this=>next
  272. !
  273. END DO
  274. !
  275. berg_grid%floating_melt = REAL(cicb_melt,dp) ! kg/m2/s
  276. berg_grid%calving_hflx = REAL(cicb_hflx,dp)
  277. !
  278. ! now use melt and associated heat flux in ocean (or not)
  279. !
  280. IF(.NOT. ln_passive_mode ) THEN
  281. emp (:,:) = emp (:,:) - berg_grid%floating_melt(:,:)
  282. qns (:,:) = qns (:,:) + berg_grid%calving_hflx (:,:)
  283. ENDIF
  284. !
  285. END SUBROUTINE icb_thm
  286. !!======================================================================
  287. END MODULE icbthm