icbdyn.F90 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. MODULE icbdyn
  2. !!======================================================================
  3. !! *** MODULE icbdyn ***
  4. !! Iceberg: time stepping routine for iceberg tracking
  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) Replace broken grounding routine
  11. !! - ! with one of Gurvan's suggestions (just like
  12. !! - ! the broken one)
  13. !!----------------------------------------------------------------------
  14. USE par_oce ! NEMO parameters
  15. USE dom_oce ! NEMO ocean domain
  16. USE phycst ! NEMO physical constants
  17. !
  18. USE icb_oce ! define iceberg arrays
  19. USE icbutl ! iceberg utility routines
  20. USE icbdia ! iceberg budget routines
  21. IMPLICIT NONE
  22. PRIVATE
  23. PUBLIC icb_dyn ! routine called in icbstp.F90 module
  24. !!----------------------------------------------------------------------
  25. !! NEMO/OPA 3.3 , NEMO Consortium (2011)
  26. !! $Id: icbdyn.F90 2355 2015-05-20 07:11:50Z ufla $
  27. !! Software governed by the CeCILL licence (NEMOGCM/NEMO_CeCILL.txt)
  28. !!----------------------------------------------------------------------
  29. CONTAINS
  30. SUBROUTINE icb_dyn( kt )
  31. !!----------------------------------------------------------------------
  32. !! *** ROUTINE icb_dyn ***
  33. !!
  34. !! ** Purpose : iceberg evolution.
  35. !!
  36. !! ** Method : - See Martin & Adcroft, Ocean Modelling 34, 2010
  37. !!----------------------------------------------------------------------
  38. REAL(wp) :: zuvel1 , zvvel1 , zu1, zv1, zax1, zay1, zxi1 , zyj1
  39. REAL(wp) :: zuvel2 , zvvel2 , zu2, zv2, zax2, zay2, zxi2 , zyj2
  40. REAL(wp) :: zuvel3 , zvvel3 , zu3, zv3, zax3, zay3, zxi3 , zyj3
  41. REAL(wp) :: zuvel4 , zvvel4 , zu4, zv4, zax4, zay4, zxi4 , zyj4
  42. REAL(wp) :: zuvel_n, zvvel_n, zxi_n , zyj_n
  43. REAL(wp) :: zdt, zdt_2, zdt_6, ze1, ze2
  44. LOGICAL :: ll_bounced
  45. TYPE(iceberg), POINTER :: berg
  46. TYPE(point) , POINTER :: pt
  47. INTEGER :: kt
  48. !!----------------------------------------------------------------------
  49. ! 4th order Runge-Kutta to solve: d/dt X = V, d/dt V = A
  50. ! with I.C.'s: X=X1 and V=V1
  51. !
  52. ! ; A1=A(X1,V1)
  53. ! X2 = X1+dt/2*V1 ; V2 = V1+dt/2*A1 ; A2=A(X2,V2)
  54. ! X3 = X1+dt/2*V2 ; V3 = V1+dt/2*A2 ; A3=A(X3,V3)
  55. ! X4 = X1+ dt*V3 ; V4 = V1+ dt*A3 ; A4=A(X4,V4)
  56. !
  57. ! Xn = X1+dt*(V1+2*V2+2*V3+V4)/6
  58. ! Vn = V1+dt*(A1+2*A2+2*A3+A4)/6
  59. ! time steps
  60. zdt = berg_dt
  61. zdt_2 = zdt * 0.5_wp
  62. zdt_6 = zdt / 6._wp
  63. berg => first_berg ! start from the first berg
  64. !
  65. DO WHILE ( ASSOCIATED(berg) ) !== loop over all bergs ==!
  66. !
  67. pt => berg%current_point
  68. ll_bounced = .false.
  69. ! STEP 1 !
  70. ! ====== !
  71. zxi1 = pt%xi ; zuvel1 = pt%uvel !** X1 in (i,j) ; V1 in m/s
  72. zyj1 = pt%yj ; zvvel1 = pt%vvel
  73. ! !** A1 = A(X1,V1)
  74. CALL icb_accel( berg , zxi1, ze1, zuvel1, zuvel1, zax1, &
  75. & zyj1, ze2, zvvel1, zvvel1, zay1, zdt_2 )
  76. !
  77. zu1 = zuvel1 / ze1 !** V1 in d(i,j)/dt
  78. zv1 = zvvel1 / ze2
  79. ! STEP 2 !
  80. ! ====== !
  81. ! !** X2 = X1+dt/2*V1 ; V2 = V1+dt/2*A1
  82. ! position using di/dt & djdt ! V2 in m/s
  83. zxi2 = zxi1 + zdt_2 * zu1 ; zuvel2 = zuvel1 + zdt_2 * zax1
  84. zyj2 = zyj1 + zdt_2 * zv1 ; zvvel2 = zvvel1 + zdt_2 * zay1
  85. !
  86. CALL icb_ground( zxi2, zxi1, zu1, &
  87. & zyj2, zyj1, zv1, ll_bounced )
  88. ! !** A2 = A(X2,V2)
  89. CALL icb_accel( berg , zxi2, ze1, zuvel2, zuvel1, zax2, &
  90. & zyj2, ze2, zvvel2, zvvel1, zay2, zdt_2 )
  91. !
  92. zu2 = zuvel2 / ze1 !** V2 in d(i,j)/dt
  93. zv2 = zvvel2 / ze2
  94. !
  95. ! STEP 3 !
  96. ! ====== !
  97. ! !** X3 = X1+dt/2*V2 ; V3 = V1+dt/2*A2; A3=A(X3)
  98. zxi3 = zxi1 + zdt_2 * zu2 ; zuvel3 = zuvel1 + zdt_2 * zax2
  99. zyj3 = zyj1 + zdt_2 * zv2 ; zvvel3 = zvvel1 + zdt_2 * zay2
  100. !
  101. CALL icb_ground( zxi3, zxi1, zu3, &
  102. & zyj3, zyj1, zv3, ll_bounced )
  103. ! !** A3 = A(X3,V3)
  104. CALL icb_accel( berg , zxi3, ze1, zuvel3, zuvel1, zax3, &
  105. & zyj3, ze2, zvvel3, zvvel1, zay3, zdt )
  106. !
  107. zu3 = zuvel3 / ze1 !** V3 in d(i,j)/dt
  108. zv3 = zvvel3 / ze2
  109. ! STEP 4 !
  110. ! ====== !
  111. ! !** X4 = X1+dt*V3 ; V4 = V1+dt*A3
  112. zxi4 = zxi1 + zdt * zu3 ; zuvel4 = zuvel1 + zdt * zax3
  113. zyj4 = zyj1 + zdt * zv3 ; zvvel4 = zvvel1 + zdt * zay3
  114. CALL icb_ground( zxi4, zxi1, zu4, &
  115. & zyj4, zyj1, zv4, ll_bounced )
  116. ! !** A4 = A(X4,V4)
  117. CALL icb_accel( berg , zxi4, ze1, zuvel4, zuvel1, zax4, &
  118. & zyj4, ze2, zvvel4, zvvel1, zay4, zdt )
  119. zu4 = zuvel4 / ze1 !** V4 in d(i,j)/dt
  120. zv4 = zvvel4 / ze2
  121. ! FINAL STEP !
  122. ! ========== !
  123. ! !** Xn = X1+dt*(V1+2*V2+2*V3+V4)/6
  124. ! !** Vn = V1+dt*(A1+2*A2+2*A3+A4)/6
  125. zxi_n = pt%xi + zdt_6 * ( zu1 + 2.*(zu2 + zu3 ) + zu4 )
  126. zyj_n = pt%yj + zdt_6 * ( zv1 + 2.*(zv2 + zv3 ) + zv4 )
  127. zuvel_n = pt%uvel + zdt_6 * ( zax1 + 2.*(zax2 + zax3) + zax4 )
  128. zvvel_n = pt%vvel + zdt_6 * ( zay1 + 2.*(zay2 + zay3) + zay4 )
  129. CALL icb_ground( zxi_n, zxi1, zuvel_n, &
  130. & zyj_n, zyj1, zvvel_n, ll_bounced )
  131. pt%uvel = zuvel_n !** save in berg structure
  132. pt%vvel = zvvel_n
  133. pt%xi = zxi_n
  134. pt%yj = zyj_n
  135. ! update actual position
  136. pt%lon = icb_utl_bilin_x(glamt, pt%xi, pt%yj )
  137. pt%lat = icb_utl_bilin(gphit, pt%xi, pt%yj, 'T' )
  138. berg => berg%next ! switch to the next berg
  139. !
  140. END DO !== end loop over all bergs ==!
  141. !
  142. END SUBROUTINE icb_dyn
  143. SUBROUTINE icb_ground( pi, pi0, pu, &
  144. & pj, pj0, pv, ld_bounced )
  145. !!----------------------------------------------------------------------
  146. !! *** ROUTINE icb_ground ***
  147. !!
  148. !! ** Purpose : iceberg grounding.
  149. !!
  150. !! ** Method : - adjust velocity and then put iceberg back to start position
  151. !! NB two possibilities available one of which is hard-coded here
  152. !!----------------------------------------------------------------------
  153. REAL(wp), INTENT(inout) :: pi , pj ! current iceberg position
  154. REAL(wp), INTENT(in ) :: pi0, pj0 ! previous iceberg position
  155. REAL(wp), INTENT(inout) :: pu , pv ! current iceberg velocities
  156. LOGICAL , INTENT( out) :: ld_bounced ! bounced indicator
  157. !
  158. INTEGER :: ii, ii0
  159. INTEGER :: ij, ij0
  160. INTEGER :: ibounce_method
  161. !!----------------------------------------------------------------------
  162. !
  163. ld_bounced = .FALSE.
  164. !
  165. ii0 = INT( pi0+0.5 ) ; ij0 = INT( pj0+0.5 ) ! initial gridpoint position (T-cell)
  166. ii = INT( pi +0.5 ) ; ij = INT( pj +0.5 ) ! current - -
  167. !
  168. IF( ii == ii0 .AND. ij == ij0 ) RETURN ! berg remains in the same cell
  169. !
  170. ! map into current processor
  171. ii0 = mi1( ii0 )
  172. ij0 = mj1( ij0 )
  173. ii = mi1( ii )
  174. ij = mj1( ij )
  175. !
  176. IF( tmask(ii,ij,1) /= 0._wp ) RETURN ! berg reach a new t-cell, but an ocean one
  177. !
  178. ! From here, berg have reach land: treat grounding/bouncing
  179. ! -------------------------------
  180. ld_bounced = .TRUE.
  181. !! not obvious what should happen now
  182. !! if berg tries to enter a land box, the only location we can return it to is the start
  183. !! position (pi0,pj0), since it has to be in a wet box to do any melting;
  184. !! first option is simply to set whole velocity to zero and move back to start point
  185. !! second option (suggested by gm) is only to set the velocity component in the (i,j) direction
  186. !! of travel to zero; at a coastal boundary this has the effect of sliding the berg along the coast
  187. ibounce_method = 2
  188. SELECT CASE ( ibounce_method )
  189. CASE ( 1 )
  190. pi = pi0
  191. pj = pj0
  192. pu = 0._wp
  193. pv = 0._wp
  194. CASE ( 2 )
  195. IF( ii0 /= ii ) THEN
  196. pi = pi0 ! return back to the initial position
  197. pu = 0._wp ! zeroing of velocity in the direction of the grounding
  198. ENDIF
  199. IF( ij0 /= ij ) THEN
  200. pj = pj0 ! return back to the initial position
  201. pv = 0._wp ! zeroing of velocity in the direction of the grounding
  202. ENDIF
  203. END SELECT
  204. !
  205. END SUBROUTINE icb_ground
  206. SUBROUTINE icb_accel( berg , pxi, pe1, puvel, puvel0, pax, &
  207. & pyj, pe2, pvvel, pvvel0, pay, pdt )
  208. !!----------------------------------------------------------------------
  209. !! *** ROUTINE icb_accel ***
  210. !!
  211. !! ** Purpose : compute the iceberg acceleration.
  212. !!
  213. !! ** Method : - sum the terms in the momentum budget
  214. !!----------------------------------------------------------------------
  215. TYPE(iceberg ), POINTER, INTENT(in ) :: berg ! berg
  216. REAL(wp) , INTENT(in ) :: pxi , pyj ! berg position in (i,j) referential
  217. REAL(wp) , INTENT(in ) :: puvel , pvvel ! berg velocity [m/s]
  218. REAL(wp) , INTENT(in ) :: puvel0, pvvel0 ! initial berg velocity [m/s]
  219. REAL(wp) , INTENT( out) :: pe1, pe2 ! horizontal scale factor at (xi,yj)
  220. REAL(wp) , INTENT(inout) :: pax, pay ! berg acceleration
  221. REAL(wp) , INTENT(in ) :: pdt ! berg time step
  222. !
  223. REAL(wp), PARAMETER :: pp_alpha = 0._wp !
  224. REAL(wp), PARAMETER :: pp_beta = 1._wp !
  225. REAL(wp), PARAMETER :: pp_vel_lim =15._wp ! max allowed berg speed
  226. REAL(wp), PARAMETER :: pp_accel_lim = 1.e-2_wp ! max allowed berg acceleration
  227. REAL(wp), PARAMETER :: pp_Cr0 = 0.06_wp !
  228. !
  229. INTEGER :: itloop
  230. REAL(wp) :: zuo, zvo, zui, zvi, zua, zva, zuwave, zvwave, zssh_x, zssh_y, zsst, zcn, zhi
  231. REAL(wp) :: zff, zT, zD, zW, zL, zM, zF
  232. REAL(wp) :: zdrag_ocn, zdrag_atm, zdrag_ice, zwave_rad
  233. REAL(wp) :: z_ocn, z_atm, z_ice
  234. REAL(wp) :: zampl, zwmod, zCr, zLwavelength, zLcutoff, zLtop
  235. REAL(wp) :: zlambda, zdetA, zA11, zA12, zaxe, zaye, zD_hi
  236. REAL(wp) :: zuveln, zvveln, zus, zvs, zspeed, zloc_dx, zspeed_new
  237. !!----------------------------------------------------------------------
  238. ! Interpolate gridded fields to berg
  239. nknberg = berg%number(1)
  240. CALL icb_utl_interp( pxi, pe1, zuo, zui, zua, zssh_x, &
  241. & pyj, pe2, zvo, zvi, zva, zssh_y, zsst, zcn, zhi, zff )
  242. zM = berg%current_point%mass
  243. zT = berg%current_point%thickness ! total thickness
  244. zD = ( rn_rho_bergs / pp_rho_seawater ) * zT ! draught (keel depth)
  245. zF = zT - zD ! freeboard
  246. zW = berg%current_point%width
  247. zL = berg%current_point%length
  248. zhi = MIN( zhi , zD )
  249. zD_hi = MAX( 0._wp, zD-zhi )
  250. ! Wave radiation
  251. zuwave = zua - zuo ; zvwave = zva - zvo ! Use wind speed rel. to ocean for wave model
  252. zwmod = zuwave*zuwave + zvwave*zvwave ! The wave amplitude and length depend on the current;
  253. ! ! wind speed relative to the ocean. Actually wmod is wmod**2 here.
  254. zampl = 0.5 * 0.02025 * zwmod ! This is "a", the wave amplitude
  255. zLwavelength = 0.32 * zwmod ! Surface wave length fitted to data in table at
  256. ! ! http://www4.ncsu.edu/eos/users/c/ceknowle/public/chapter10/part2.html
  257. zLcutoff = 0.125 * zLwavelength
  258. zLtop = 0.25 * zLwavelength
  259. zCr = pp_Cr0 * MIN( MAX( 0., (zL-zLcutoff) / ((zLtop-zLcutoff)+1.e-30)) , 1.) ! Wave radiation coefficient
  260. ! ! fitted to graph from Carrieres et al., POAC Drift Model.
  261. zwave_rad = 0.5 * pp_rho_seawater / zM * zCr * grav * zampl * MIN( zampl,zF ) * (2.*zW*zL) / (zW+zL)
  262. zwmod = SQRT( zua*zua + zva*zva ) ! Wind speed
  263. IF( zwmod /= 0._wp ) THEN
  264. zuwave = zua/zwmod ! Wave radiation force acts in wind direction ... !!gm this should be the wind rel. to ocean ?
  265. zvwave = zva/zwmod
  266. ELSE
  267. zuwave = 0. ; zvwave=0. ; zwave_rad=0. ! ... and only when wind is present. !!gm wave_rad=0. is useless
  268. ENDIF
  269. ! Weighted drag coefficients
  270. z_ocn = pp_rho_seawater / zM * (0.5*pp_Cd_wv*zW*(zD_hi)+pp_Cd_wh*zW*zL)
  271. z_atm = pp_rho_air / zM * (0.5*pp_Cd_av*zW*zF +pp_Cd_ah*zW*zL)
  272. z_ice = pp_rho_ice / zM * (0.5*pp_Cd_iv*zW*zhi )
  273. IF( abs(zui) + abs(zvi) == 0._wp ) z_ice = 0._wp
  274. zuveln = puvel ; zvveln = pvvel ! Copy starting uvel, vvel
  275. !
  276. DO itloop = 1, 2 ! Iterate on drag coefficients
  277. !
  278. zus = 0.5 * ( zuveln + puvel )
  279. zvs = 0.5 * ( zvveln + pvvel )
  280. zdrag_ocn = z_ocn * SQRT( (zus-zuo)*(zus-zuo) + (zvs-zvo)*(zvs-zvo) )
  281. zdrag_atm = z_atm * SQRT( (zus-zua)*(zus-zua) + (zvs-zva)*(zvs-zva) )
  282. zdrag_ice = z_ice * SQRT( (zus-zui)*(zus-zui) + (zvs-zvi)*(zvs-zvi) )
  283. !
  284. ! Explicit accelerations
  285. !zaxe= zff*pvvel -grav*zssh_x +zwave_rad*zuwave &
  286. ! -zdrag_ocn*(puvel-zuo) -zdrag_atm*(puvel-zua) -zdrag_ice*(puvel-zui)
  287. !zaye=-zff*puvel -grav*zssh_y +zwave_rad*zvwave &
  288. ! -zdrag_ocn*(pvvel-zvo) -zdrag_atm*(pvvel-zva) -zdrag_ice*(pvvel-zvi)
  289. zaxe = -grav * zssh_x + zwave_rad * zuwave
  290. zaye = -grav * zssh_y + zwave_rad * zvwave
  291. IF( pp_alpha > 0._wp ) THEN ! If implicit, use time-level (n) rather than RK4 latest
  292. zaxe = zaxe + zff*pvvel0
  293. zaye = zaye - zff*puvel0
  294. ELSE
  295. zaxe = zaxe + zff*pvvel
  296. zaye = zaye - zff*puvel
  297. ENDIF
  298. IF( pp_beta > 0._wp ) THEN ! If implicit, use time-level (n) rather than RK4 latest
  299. zaxe = zaxe - zdrag_ocn*(puvel0-zuo) - zdrag_atm*(puvel0-zua) -zdrag_ice*(puvel0-zui)
  300. zaye = zaye - zdrag_ocn*(pvvel0-zvo) - zdrag_atm*(pvvel0-zva) -zdrag_ice*(pvvel0-zvi)
  301. ELSE
  302. zaxe = zaxe - zdrag_ocn*(puvel -zuo) - zdrag_atm*(puvel -zua) -zdrag_ice*(puvel -zui)
  303. zaye = zaye - zdrag_ocn*(pvvel -zvo) - zdrag_atm*(pvvel -zva) -zdrag_ice*(pvvel -zvi)
  304. endif
  305. ! Solve for implicit accelerations
  306. IF( pp_alpha + pp_beta > 0._wp ) THEN
  307. zlambda = zdrag_ocn + zdrag_atm + zdrag_ice
  308. zA11 = 1._wp + pp_beta *pdt*zlambda
  309. zA12 = pp_alpha*pdt*zff
  310. zdetA = 1._wp / ( zA11*zA11 + zA12*zA12 )
  311. pax = zdetA * ( zA11*zaxe + zA12*zaye )
  312. pay = zdetA * ( zA11*zaye - zA12*zaxe )
  313. else
  314. pax = zaxe ; pay = zaye
  315. endif
  316. zuveln = puvel0 + pdt*pax
  317. zvveln = pvvel0 + pdt*pay
  318. !
  319. END DO ! itloop
  320. IF( rn_speed_limit > 0._wp ) THEN ! Limit speed of bergs based on a CFL criteria (if asked)
  321. zspeed = SQRT( zuveln*zuveln + zvveln*zvveln ) ! Speed of berg
  322. IF( zspeed > 0._wp ) THEN
  323. zloc_dx = MIN( pe1, pe2 ) ! minimum grid spacing
  324. zspeed_new = zloc_dx / pdt * rn_speed_limit ! Speed limit as a factor of dx / dt
  325. IF( zspeed_new < zspeed ) THEN
  326. zuveln = zuveln * ( zspeed_new / zspeed ) ! Scale velocity to reduce speed
  327. zvveln = zvveln * ( zspeed_new / zspeed ) ! without changing the direction
  328. CALL icb_dia_speed()
  329. ENDIF
  330. ENDIF
  331. ENDIF
  332. ! ! check the speed and acceleration limits
  333. IF( ABS( zuveln ) > pp_vel_lim .OR. ABS( zvveln ) > pp_vel_lim ) &
  334. WRITE(numicb,'("pe=",i3,x,a)') narea,'Dump triggered by excessive velocity'
  335. IF( ABS( pax ) > pp_accel_lim .OR. ABS( pay ) > pp_accel_lim ) &
  336. WRITE(numicb,'("pe=",i3,x,a)') narea,'Dump triggered by excessive acceleration'
  337. !
  338. END SUBROUTINE icb_accel
  339. !!======================================================================
  340. END MODULE icbdyn