Line data Source code
1 : !=========================================================================
2 : !
3 : ! This module contains the subroutines required to define
4 : ! a floe size distribution tracer for sea ice
5 : !
6 : ! Theory based on:
7 : !
8 : ! Horvat, C., & Tziperman, E. (2015). A prognostic model of the sea-ice
9 : ! floe size and thickness distribution. The Cryosphere, 9(6), 2119-2134.
10 : ! doi:10.5194/tc-9-2119-2015
11 : !
12 : ! and implementation described in:
13 : !
14 : ! Roach, L. A., Horvat, C., Dean, S. M., & Bitz, C. M. (2018). An emergent
15 : ! sea ice floe size distribution in a global coupled ocean--sea ice model.
16 : ! Journal of Geophysical Research: Oceans, 123(6), 4322-4337.
17 : ! doi:10.1029/2017JC013692
18 : !
19 : ! with some modifications.
20 : !
21 : ! For floe welding parameter and tensile mode parameter values, see
22 : !
23 : ! Roach, L. A., Smith, M. M., & Dean, S. M. (2018). Quantifying
24 : ! growth of pancake sea ice floes using images from drifting buoys.
25 : ! Journal of Geophysical Research: Oceans, 123(4), 2851-2866.
26 : ! doi: 10.1002/2017JC013693
27 : !
28 : ! Variable naming convention:
29 : ! for k = 1, nfsd and n = 1, ncat
30 : ! afsdn(k,n) = trcrn(:,:,nt_nfsd+k-1,n,:)
31 : ! afsd (k) is per thickness category or averaged over n
32 : ! afs is the associated scalar value for (k,n)
33 : !
34 : ! authors: Lettie Roach, VUW/NIWA
35 : ! C. M. Bitz, UW
36 : !
37 : ! 2016: CMB started
38 : ! 2016-8: LR worked on most of it
39 : ! 2019: ECH ported to Icepack
40 :
41 : !-----------------------------------------------------------------
42 :
43 : module icepack_fsd
44 :
45 : use icepack_kinds
46 : use icepack_parameters, only: c0, c1, c2, c4, p01, p1, p5, puny
47 : use icepack_parameters, only: pi, floeshape, wave_spec, bignum, gravit, rhoi
48 : use icepack_tracers, only: nt_fsd, tr_fsd
49 : use icepack_warnings, only: warnstr, icepack_warnings_add
50 : use icepack_warnings, only: icepack_warnings_setabort, icepack_warnings_aborted
51 :
52 : implicit none
53 :
54 : private
55 : public :: icepack_init_fsd_bounds, icepack_init_fsd, icepack_cleanup_fsd, &
56 : fsd_lateral_growth, fsd_add_new_ice, fsd_weld_thermo, get_subdt_fsd
57 :
58 : real(kind=dbl_kind), dimension(:), allocatable :: &
59 : floe_rad_h, & ! fsd size higher bound in m (radius)
60 : floe_area_l, & ! fsd area at lower bound (m^2)
61 : floe_area_h, & ! fsd area at higher bound (m^2)
62 : floe_area_c, & ! fsd area at bin centre (m^2)
63 : floe_area_binwidth ! floe area bin width (m^2)
64 :
65 : integer(kind=int_kind), dimension(:,:), allocatable, public :: &
66 : iweld ! floe size categories that can combine
67 : ! during welding (dimensionless)
68 :
69 : !=======================================================================
70 :
71 : contains
72 :
73 : !=======================================================================
74 : ! Note that radius widths cannot be larger than twice previous
75 : ! category width or floe welding will not have an effect
76 : !
77 : ! Note also that the bound of the lowest floe size category is used
78 : ! to define the lead region width and the domain spacing for wave fracture
79 : !
80 : !autodocument_start icepack_init_fsd_bounds
81 : ! Initialize ice fsd bounds (call whether or not restarting)
82 : ! Define the bounds, midpoints and widths of floe size
83 : ! categories in area and radius
84 : !
85 : ! authors: Lettie Roach, NIWA/VUW and C. M. Bitz, UW
86 :
87 4 : subroutine icepack_init_fsd_bounds(nfsd, &
88 4 : floe_rad_l, & ! fsd size lower bound in m (radius)
89 4 : floe_rad_c, & ! fsd size bin centre in m (radius)
90 4 : floe_binwidth, & ! fsd size bin width in m (radius)
91 4 : c_fsd_range, & ! string for history output
92 : write_diags ) ! flag for writing diagnostics
93 :
94 : integer (kind=int_kind), intent(in) :: &
95 : nfsd ! number of floe size categories
96 :
97 : real(kind=dbl_kind), dimension(:), intent(inout) :: &
98 : floe_rad_l, & ! fsd size lower bound in m (radius)
99 : floe_rad_c, & ! fsd size bin centre in m (radius)
100 : floe_binwidth ! fsd size bin width in m (radius)
101 :
102 : character (len=35), intent(out) :: &
103 : c_fsd_range(nfsd) ! string for history output
104 :
105 : logical (kind=log_kind), intent(in), optional :: &
106 : write_diags ! write diags flag
107 :
108 : !autodocument_end
109 :
110 : ! local variables
111 :
112 : integer (kind=int_kind) :: n, m, k
113 : integer (kind=int_kind) :: ierr
114 :
115 2 : real (kind=dbl_kind) :: test
116 :
117 : real (kind=dbl_kind), dimension (nfsd+1) :: &
118 : area_lims, area_lims_scaled
119 :
120 : real (kind=dbl_kind), dimension (0:nfsd) :: &
121 25 : floe_rad
122 :
123 : real (kind=dbl_kind), dimension(:), allocatable :: &
124 4 : lims
125 :
126 : logical (kind=log_kind) :: &
127 : l_write_diags ! local write diags
128 :
129 : character(len=8) :: c_fsd1,c_fsd2
130 : character(len=2) :: c_nf
131 : character(len=*), parameter :: subname='(icepack_init_fsd_bounds)'
132 :
133 4 : l_write_diags = .true.
134 4 : if (present(write_diags)) then
135 0 : l_write_diags = write_diags
136 : endif
137 :
138 4 : if (nfsd.eq.24) then
139 :
140 0 : allocate(lims(24+1))
141 :
142 : lims = (/ 6.65000000e-02, 5.31030847e+00, 1.42865861e+01, 2.90576686e+01, &
143 : 5.24122136e+01, 8.78691405e+01, 1.39518470e+02, 2.11635752e+02, &
144 : 3.08037274e+02, 4.31203059e+02, 5.81277225e+02, 7.55141047e+02, &
145 : 9.45812834e+02, 1.34354446e+03, 1.82265364e+03, 2.47261361e+03, &
146 : 3.35434988e+03, 4.55051413e+03, 6.17323164e+03, 8.37461170e+03, &
147 : 1.13610059e+04, 1.54123510e+04, 2.09084095e+04, 2.83643675e+04, &
148 0 : 3.84791270e+04 /)
149 :
150 4 : elseif (nfsd.eq.16) then
151 :
152 0 : allocate(lims(16+1))
153 :
154 : lims = (/ 6.65000000e-02, 5.31030847e+00, 1.42865861e+01, 2.90576686e+01, &
155 : 5.24122136e+01, 8.78691405e+01, 1.39518470e+02, 2.11635752e+02, &
156 : 3.08037274e+02, 4.31203059e+02, 5.81277225e+02, 7.55141047e+02, &
157 : 9.45812834e+02, 1.34354446e+03, 1.82265364e+03, 2.47261361e+03, &
158 0 : 3.35434988e+03 /)
159 :
160 4 : else if (nfsd.eq.12) then
161 :
162 3 : allocate(lims(12+1))
163 :
164 : lims = (/ 6.65000000e-02, 5.31030847e+00, 1.42865861e+01, 2.90576686e+01, &
165 : 5.24122136e+01, 8.78691405e+01, 1.39518470e+02, 2.11635752e+02, &
166 : 3.08037274e+02, 4.31203059e+02, 5.81277225e+02, 7.55141047e+02, &
167 42 : 9.45812834e+02 /)
168 :
169 1 : else if (nfsd.eq.1) then ! default case
170 :
171 1 : allocate(lims(1+1))
172 :
173 3 : lims = (/ 6.65000000e-02, 3.0e+02 /)
174 :
175 : else
176 :
177 : call icepack_warnings_add(subname//&
178 0 : ' floe size categories not defined for nfsd')
179 0 : call icepack_warnings_setabort(.true.,__FILE__,__LINE__)
180 0 : return
181 :
182 : end if
183 :
184 : allocate( &
185 : floe_rad_h (nfsd), & ! fsd size higher bound in m (radius)
186 : floe_area_l (nfsd), & ! fsd area at lower bound (m^2)
187 : floe_area_h (nfsd), & ! fsd area at higher bound (m^2)
188 : floe_area_c (nfsd), & ! fsd area at bin centre (m^2)
189 : floe_area_binwidth (nfsd), & ! floe area bin width (m^2)
190 : iweld (nfsd, nfsd), & ! fsd categories that can weld
191 4 : stat=ierr)
192 4 : if (ierr/=0) then
193 0 : call icepack_warnings_add(subname//' Out of Memory fsd')
194 0 : call icepack_warnings_setabort(.true.,__FILE__,__LINE__)
195 0 : return
196 : endif
197 :
198 41 : floe_rad_l = lims(1:nfsd )
199 41 : floe_rad_h = lims(2:nfsd+1)
200 41 : floe_rad_c = (floe_rad_h+floe_rad_l)/c2
201 :
202 41 : floe_area_l = c4*floeshape*floe_rad_l**2
203 41 : floe_area_c = c4*floeshape*floe_rad_c**2
204 41 : floe_area_h = c4*floeshape*floe_rad_h**2
205 :
206 41 : floe_binwidth = floe_rad_h - floe_rad_l
207 :
208 41 : floe_area_binwidth = floe_area_h - floe_area_l
209 :
210 : ! floe size categories that can combine during welding
211 474 : iweld(:,:) = -999
212 41 : do n = 1, nfsd
213 474 : do m = 1, nfsd
214 433 : test = floe_area_c(n) + floe_area_c(m)
215 5185 : do k = 1, nfsd-1
216 4752 : if ((test >= floe_area_l(k)) .and. (test < floe_area_h(k))) &
217 781 : iweld(n,m) = k
218 : end do
219 470 : if (test >= floe_area_l(nfsd)) iweld(n,m) = nfsd
220 : end do
221 : end do
222 :
223 4 : if (allocated(lims)) deallocate(lims)
224 :
225 : ! write fsd bounds
226 4 : floe_rad(0) = floe_rad_l(1)
227 41 : do n = 1, nfsd
228 37 : floe_rad(n) = floe_rad_h(n)
229 : ! Save character string to write to history file
230 37 : write (c_nf, '(i2)') n
231 37 : write (c_fsd1, '(f6.3)') floe_rad(n-1)
232 37 : write (c_fsd2, '(f6.3)') floe_rad(n)
233 41 : c_fsd_range(n)=c_fsd1//'m < fsd Cat '//c_nf//' < '//c_fsd2//'m'
234 : enddo
235 :
236 4 : if (l_write_diags) then
237 4 : write(warnstr,*) ' '
238 4 : call icepack_warnings_add(warnstr)
239 4 : write(warnstr,*) subname
240 4 : call icepack_warnings_add(warnstr)
241 4 : write(warnstr,*) 'floe_rad(n-1) < fsd Cat n < floe_rad(n)'
242 4 : call icepack_warnings_add(warnstr)
243 41 : do n = 1, nfsd
244 37 : write(warnstr,*) floe_rad(n-1),' < fsd Cat ',n, ' < ',floe_rad(n)
245 41 : call icepack_warnings_add(warnstr)
246 : enddo
247 4 : write(warnstr,*) ' '
248 4 : call icepack_warnings_add(warnstr)
249 : endif
250 :
251 4 : end subroutine icepack_init_fsd_bounds
252 :
253 : !=======================================================================
254 : ! When growing from no-ice conditions, initialize to zero.
255 : ! This allows the FSD to emerge, as described in Roach, Horvat et al. (2018)
256 : !
257 : ! Otherwise initalize with a power law, following Perovich
258 : ! & Jones (2014). The basin-wide applicability of such a
259 : ! prescribed power law has not yet been tested.
260 : !
261 : ! Perovich, D. K., & Jones, K. F. (2014). The seasonal evolution of
262 : ! sea ice floe size distribution. Journal of Geophysical Research: Oceans,
263 : ! 119(12), 8767–8777. doi:10.1002/2014JC010136
264 : !
265 : !autodocument_start icepack_init_fsd
266 : !
267 : ! Initialize the FSD
268 : !
269 : ! authors: Lettie Roach, NIWA/VUW
270 :
271 20 : subroutine icepack_init_fsd(nfsd, ice_ic, &
272 40 : floe_rad_c, & ! fsd size bin centre in m (radius)
273 40 : floe_binwidth, & ! fsd size bin width in m (radius)
274 40 : afsd) ! floe size distribution tracer
275 :
276 : integer(kind=int_kind), intent(in) :: &
277 : nfsd
278 :
279 : character(len=char_len_long), intent(in) :: &
280 : ice_ic ! method of ice cover initialization
281 :
282 : real(kind=dbl_kind), dimension(:), intent(inout) :: &
283 : floe_rad_c, & ! fsd size bin centre in m (radius)
284 : floe_binwidth ! fsd size bin width in m (radius)
285 :
286 : real (kind=dbl_kind), dimension (:), intent(inout) :: &
287 : afsd ! floe size tracer: fraction distribution of floes
288 :
289 : !autodocument_end
290 :
291 : ! local variables
292 :
293 40 : real (kind=dbl_kind) :: alpha, totfrac
294 :
295 : integer (kind=int_kind) :: k
296 :
297 : real (kind=dbl_kind), dimension (nfsd) :: &
298 190 : num_fsd ! number distribution of floes
299 :
300 40 : if (trim(ice_ic) == 'none') then
301 :
302 0 : afsd(:) = c0
303 :
304 : else ! Perovich (2014)
305 :
306 : ! fraction of ice in each floe size and thickness category
307 : ! same for ALL cells (even where no ice) initially
308 40 : alpha = 2.1_dbl_kind
309 40 : totfrac = c0 ! total fraction of floes
310 410 : do k = 1, nfsd
311 370 : num_fsd(k) = (2*floe_rad_c(k))**(-alpha-c1) ! number distribution of floes
312 370 : afsd (k) = num_fsd(k)*floe_area_c(k)*floe_binwidth(k) ! fraction distribution of floes
313 410 : totfrac = totfrac + afsd(k)
314 : enddo
315 410 : afsd = afsd/totfrac ! normalize
316 :
317 : endif ! ice_ic
318 :
319 40 : end subroutine icepack_init_fsd
320 :
321 : !=======================================================================
322 : !autodocument_start icepack_cleanup_fsd
323 : !
324 : ! Clean up small values and renormalize
325 : !
326 : ! authors: Elizabeth Hunke, LANL
327 : !
328 296028 : subroutine icepack_cleanup_fsd (ncat, nfsd, afsdn)
329 :
330 : integer (kind=int_kind), intent(in) :: &
331 : ncat , & ! number of thickness categories
332 : nfsd ! number of floe size categories
333 :
334 : real (kind=dbl_kind), dimension(:,:), intent(inout) :: &
335 : afsdn ! floe size distribution tracer
336 :
337 : !autodocument_end
338 : ! local variables
339 :
340 : integer (kind=int_kind) :: &
341 : n ! thickness category index
342 :
343 : character(len=*), parameter :: subname='(icepack_cleanup_fsd)'
344 :
345 :
346 296028 : if (tr_fsd) then
347 :
348 1776168 : do n = 1, ncat
349 1480140 : call icepack_cleanup_fsdn(nfsd, afsdn(:,n))
350 1776168 : if (icepack_warnings_aborted(subname)) return
351 : enddo
352 :
353 : endif ! tr_fsd
354 :
355 : end subroutine icepack_cleanup_fsd
356 :
357 : !=======================================================================
358 : !
359 : ! Clean up small values and renormalize -- per category
360 : !
361 : ! authors: Elizabeth Hunke, LANL
362 : !
363 :
364 2392698 : subroutine icepack_cleanup_fsdn (nfsd, afsd)
365 :
366 : integer (kind=int_kind), intent(in) :: &
367 : nfsd ! number of floe size categories
368 :
369 : real (kind=dbl_kind), dimension(:), intent(inout) :: &
370 : afsd ! floe size distribution tracer
371 :
372 : ! local variables
373 :
374 : integer (kind=int_kind) :: &
375 : k ! floe size category index
376 :
377 : real (kind=dbl_kind) :: &
378 1202062 : tot
379 :
380 25043612 : do k = 1, nfsd
381 25043612 : if (afsd(k) < puny) afsd(k) = c0
382 : enddo
383 :
384 25043612 : tot = sum(afsd(:))
385 2392698 : if (tot > puny) then
386 24058481 : do k = 1, nfsd
387 24058481 : afsd(k) = afsd(k) / tot ! normalize
388 : enddo
389 : else ! represents ice-free cell, so set to zero
390 985131 : afsd(:) = c0
391 : endif
392 :
393 2392698 : end subroutine icepack_cleanup_fsdn
394 :
395 : !=======================================================================
396 : !
397 : ! Given the joint ice thickness and floe size distribution, calculate
398 : ! the lead region and the total lateral surface area following Horvat
399 : ! and Tziperman (2015).
400 : !
401 : ! authors: Lettie Roach, NIWA/VUW
402 :
403 61115 : subroutine partition_area (ncat, nfsd, &
404 61115 : floe_rad_c, aice, &
405 122230 : aicen, vicen, &
406 61115 : afsdn, lead_area, &
407 : latsurf_area)
408 :
409 : integer (kind=int_kind), intent(in) :: &
410 : ncat , & ! number of thickness categories
411 : nfsd ! number of floe size categories
412 :
413 : real (kind=dbl_kind), dimension(:), intent(in) :: &
414 : floe_rad_c ! fsd size bin centre in m (radius)
415 :
416 : real (kind=dbl_kind), intent(in) :: &
417 : aice ! ice concentration
418 :
419 : real (kind=dbl_kind), dimension(:), intent(in) :: &
420 : aicen , & ! fractional area of ice
421 : vicen ! volume per unit area of ice (m)
422 :
423 : real (kind=dbl_kind), dimension(:,:), intent(in) :: &
424 : afsdn ! floe size distribution tracer
425 :
426 : real (kind=dbl_kind), intent(out) :: &
427 : lead_area , & ! the fractional area of the lead region
428 : latsurf_area ! the area covered by lateral surface of floes
429 :
430 : ! local variables
431 :
432 : integer (kind=int_kind) :: &
433 : n , & ! thickness category index
434 : k ! floe size index
435 :
436 : real (kind=dbl_kind) :: &
437 29663 : width_leadreg, & ! width of lead region (m)
438 29663 : thickness ! actual thickness of ice in thickness cat (m)
439 :
440 : !-----------------------------------------------------------------
441 : ! Initialize
442 : !-----------------------------------------------------------------
443 61115 : lead_area = c0
444 61115 : latsurf_area = c0
445 :
446 : ! Set the width of the lead region to be the smallest
447 : ! floe size category, as per Horvat & Tziperman (2015)
448 61115 : width_leadreg = floe_rad_c(1)
449 :
450 : ! Only calculate these areas where there is some ice
451 61115 : if (aice > puny) then
452 :
453 : ! lead area = sum of areas of annuli around all floes
454 366654 : do n = 1, ncat
455 3227004 : do k = 1, nfsd
456 973410 : lead_area = lead_area + aicen(n) * afsdn(k,n) &
457 973410 : * (c2*width_leadreg /floe_rad_c(k) &
458 3165895 : + width_leadreg**2/floe_rad_c(k)**2)
459 : enddo ! k
460 : enddo ! n
461 :
462 : ! cannot be greater than the open water fraction
463 61109 : lead_area=MIN(lead_area, c1-aice)
464 61109 : if (lead_area < c0) then
465 0 : if (lead_area < -puny) then
466 : ! stop 'lead_area lt0 in partition_area'
467 : else
468 0 : lead_area=MAX(lead_area,c0)
469 : end if
470 : end if
471 :
472 : ! Total fractional lateral surface area in each grid (per unit ocean area)
473 366654 : do n = 1, ncat
474 305545 : thickness = c0
475 305545 : if (aicen(n) > c0) thickness = vicen(n)/aicen(n)
476 :
477 3227004 : do k = 1, nfsd
478 : latsurf_area = latsurf_area &
479 3165895 : + afsdn(k,n) * aicen(n) * c2 * thickness/floe_rad_c(k)
480 : end do
481 : end do
482 :
483 : ! check
484 : ! if (latsurf_area < c0) stop 'negative latsurf_area'
485 : ! if (latsurf_area /= latsurf_area) stop 'latsurf_area NaN'
486 :
487 : end if ! aice
488 :
489 61115 : end subroutine partition_area
490 :
491 : !=======================================================================
492 : !
493 : ! Lateral growth at the edges of floes
494 : !
495 : ! Compute the portion of new ice growth that occurs at the edges of
496 : ! floes. The remainder will grow as new ice frazil ice in open water
497 : ! (away from floes).
498 : !
499 : ! See Horvat & Tziperman (2015) and Roach, Horvat et al. (2018).
500 : !
501 : ! authors: Lettie Roach, NIWA/VUW
502 : !
503 61115 : subroutine fsd_lateral_growth (ncat, nfsd, &
504 : dt, aice, &
505 61115 : aicen, vicen, &
506 : vi0new, &
507 61115 : frazil, floe_rad_c, &
508 61115 : afsdn, &
509 : lead_area, latsurf_area, &
510 61115 : G_radial, d_an_latg, &
511 : tot_latg)
512 :
513 : integer (kind=int_kind), intent(in) :: &
514 : ncat , & ! number of thickness categories
515 : nfsd ! number of floe size categories
516 :
517 : real (kind=dbl_kind), intent(in) :: &
518 : dt , & ! time step (s)
519 : aice ! total concentration of ice
520 :
521 : real (kind=dbl_kind), dimension (:), intent(in) :: &
522 : aicen , & ! concentration of ice
523 : vicen ! volume per unit area of ice (m)
524 :
525 : real (kind=dbl_kind), dimension(:,:), intent(in) :: &
526 : afsdn ! floe size distribution tracer
527 :
528 : real (kind=dbl_kind), intent(inout) :: &
529 : vi0new , & ! volume of new ice added to cat 1 (m)
530 : frazil ! frazil ice growth (m/step-->cm/day)
531 :
532 : ! floe size distribution
533 : real (kind=dbl_kind), dimension (:), intent(in) :: &
534 : floe_rad_c ! fsd size bin centre in m (radius)
535 :
536 : real (kind=dbl_kind), dimension(ncat), intent(out) :: &
537 : d_an_latg ! change in aicen occuring due to lateral growth
538 :
539 : real (kind=dbl_kind), intent(out) :: &
540 : G_radial , & ! lateral melt rate (m/s)
541 : tot_latg ! total change in aice due to
542 : ! lateral growth at the edges of floes
543 :
544 : ! local variables
545 : integer (kind=int_kind) :: &
546 : n, k ! ice category indices
547 :
548 : real (kind=dbl_kind) :: &
549 29663 : vi0new_lat ! volume of new ice added laterally to FSD (m)
550 :
551 : real (kind=dbl_kind), intent(out) :: &
552 : lead_area , & ! the fractional area of the lead region
553 : latsurf_area ! the area covered by lateral surface of floes
554 :
555 : character(len=*),parameter :: subname='(fsd_lateral_growth)'
556 :
557 61115 : lead_area = c0
558 61115 : latsurf_area = c0
559 61115 : G_radial = c0
560 61115 : tot_latg = c0
561 366690 : d_an_latg = c0
562 :
563 : ! partition volume into lateral growth and frazil
564 : call partition_area (ncat, nfsd, &
565 0 : floe_rad_c, aice, &
566 0 : aicen, vicen, &
567 0 : afsdn, lead_area, &
568 61115 : latsurf_area)
569 61115 : if (icepack_warnings_aborted(subname)) return
570 :
571 61115 : vi0new_lat = c0
572 61115 : if (latsurf_area > puny) then
573 61109 : vi0new_lat = vi0new * lead_area / (c1 + aice/latsurf_area)
574 : end if
575 :
576 : ! for history/diagnostics
577 61115 : frazil = vi0new - vi0new_lat
578 :
579 : ! lateral growth increment
580 61115 : if (vi0new_lat > puny) then
581 60936 : G_radial = vi0new_lat/dt
582 365616 : do n = 1, ncat
583 :
584 3223891 : do k = 1, nfsd
585 972055 : d_an_latg(n) = d_an_latg(n) &
586 3162955 : + c2*aicen(n)*afsdn(k,n)*G_radial*dt/floe_rad_c(k)
587 : end do
588 : end do ! n
589 :
590 : ! cannot expand ice laterally beyond lead region
591 365616 : if (SUM(d_an_latg(:)).ge.lead_area) then
592 0 : d_an_latg(:) = d_an_latg(:)/SUM(d_an_latg(:))
593 0 : d_an_latg(:) = d_an_latg(:)*lead_area
594 : end if
595 :
596 : endif ! vi0new_lat > 0
597 :
598 : ! Use remaining ice volume as in standard model,
599 : ! but ice cannot grow into the area that has grown laterally
600 61115 : vi0new = vi0new - vi0new_lat
601 366690 : tot_latg = SUM(d_an_latg(:))
602 :
603 : end subroutine fsd_lateral_growth
604 :
605 : !=======================================================================
606 : !
607 : ! Evolve the FSD subject to lateral growth and the growth of new ice
608 : ! in thickness category 1.
609 : !
610 : ! If ocean surface wave forcing is provided, the floe size of new ice
611 : ! (grown away from floe edges) can computed from the wave field
612 : ! based on the tensile (stretching) stress limitation following
613 : ! Shen et al. (2001). Otherwise, new floes all grow in the smallest
614 : ! floe size category, representing pancake ice formation.
615 : !
616 : ! Shen, H., Ackley, S., & Hopkins, M. (2001). A conceptual model
617 : ! for pancake-ice formation in a wave field.
618 : ! Annals of Glaciology, 33, 361-367. doi:10.3189/172756401781818239
619 : !
620 : ! authors: Lettie Roach, NIWA/VUW
621 : !
622 304808 : subroutine fsd_add_new_ice (ncat, n, nfsd, &
623 : dt, ai0new, &
624 609616 : d_an_latg, d_an_newi, &
625 304808 : floe_rad_c, floe_binwidth, &
626 304808 : G_radial, area2, &
627 : wave_sig_ht, &
628 304808 : wave_spectrum, &
629 304808 : wavefreq, &
630 304808 : dwavefreq, &
631 304808 : d_afsd_latg, &
632 304808 : d_afsd_newi, &
633 609616 : afsdn, aicen_init, &
634 609616 : aicen, trcrn)
635 :
636 : integer (kind=int_kind), intent(in) :: &
637 : n , & ! thickness category number
638 : ncat , & ! number of thickness categories
639 : nfsd ! number of floe size categories
640 :
641 : real (kind=dbl_kind), intent(in) :: &
642 : dt , & ! time step (s)
643 : ai0new , & ! area of new ice added to cat 1
644 : G_radial , & ! lateral melt rate (m/s)
645 : wave_sig_ht ! wave significant height (everywhere) (m)
646 :
647 : real (kind=dbl_kind), dimension(:), intent(in) :: &
648 : wave_spectrum ! ocean surface wave spectrum as a function of frequency
649 : ! power spectral density of surface elevation, E(f) (units m^2 s)
650 :
651 : real(kind=dbl_kind), dimension(:), intent(in) :: &
652 : wavefreq , & ! wave frequencies (s^-1)
653 : dwavefreq ! wave frequency bin widths (s^-1)
654 :
655 : real (kind=dbl_kind), dimension(:), intent(in) :: &
656 : d_an_latg , & ! change in aicen due to lateral growth
657 : d_an_newi ! change in aicen due to frazil ice formation
658 :
659 : real (kind=dbl_kind), dimension (:), intent(in) :: &
660 : aicen_init , & ! fractional area of ice
661 : aicen , & ! after update
662 : floe_rad_c , & ! fsd size bin centre in m (radius)
663 : floe_binwidth ! fsd size bin width in m (radius)
664 :
665 : real (kind=dbl_kind), dimension (:,:), intent(in) :: &
666 : afsdn ! floe size distribution tracer
667 :
668 : real (kind=dbl_kind), dimension (:), intent(in) :: &
669 : area2 ! area after lateral growth, before new ice formation
670 :
671 : real (kind=dbl_kind), dimension (:,:), intent(inout) :: &
672 : trcrn ! ice tracers
673 :
674 : real (kind=dbl_kind), dimension(:), intent(inout) :: &
675 : ! change in floe size distribution (area)
676 : d_afsd_latg, & ! due to fsd lateral growth
677 : d_afsd_newi ! new ice formation
678 :
679 : integer (kind=int_kind) :: &
680 : k, & ! floe size category index
681 : new_size, & ! index for floe size of new ice
682 : nsubt ! number of subtimesteps
683 :
684 : real (kind=dbl_kind) :: &
685 295274 : elapsed_t, subdt ! elapsed time, subtimestep (s)
686 :
687 : real (kind=dbl_kind), dimension (nfsd,ncat) :: &
688 6060874 : afsdn_latg ! fsd after lateral growth
689 :
690 : real (kind=dbl_kind), dimension (nfsd) :: &
691 1434121 : dafsd_tmp, & ! tmp FSD
692 1434121 : df_flx , & ! finite differences for fsd
693 1729395 : afsd_ni ! fsd after new ice added
694 :
695 : real (kind=dbl_kind), dimension(nfsd+1) :: &
696 1572224 : f_flx ! finite differences in floe size
697 :
698 : character(len=*),parameter :: subname='(fsd_add_new_ice)'
699 :
700 3163002 : afsdn_latg(:,n) = afsdn(:,n) ! default
701 :
702 304808 : if (d_an_latg(n) > puny) then ! lateral growth
703 :
704 : ! adaptive timestep
705 235375 : elapsed_t = c0
706 235375 : nsubt = 0
707 :
708 470750 : DO WHILE (elapsed_t.lt.dt)
709 :
710 235375 : nsubt = nsubt + 1
711 235375 : if (nsubt.gt.100) print *, 'latg not converging'
712 :
713 : ! finite differences
714 2941262 : df_flx(:) = c0 ! NB could stay zero if all in largest FS cat
715 3176637 : f_flx (:) = c0
716 2705887 : do k = 2, nfsd
717 2705887 : f_flx(k) = G_radial * afsdn_latg(k-1,n) / floe_binwidth(k-1)
718 : end do
719 2941262 : do k = 1, nfsd
720 2941262 : df_flx(k) = f_flx(k+1) - f_flx(k)
721 : end do
722 :
723 : ! if (abs(sum(df_flx)) > puny) print*,'fsd_add_new ERROR df_flx /= 0'
724 :
725 2941262 : dafsd_tmp(:) = c0
726 2941262 : do k = 1, nfsd
727 1747382 : dafsd_tmp(k) = (-df_flx(k) + c2 * G_radial * afsdn_latg(k,n) &
728 36166984 : * (c1/floe_rad_c(k) - SUM(afsdn_latg(:,n)/floe_rad_c(:))) )
729 :
730 : end do
731 :
732 : ! timestep required for this
733 235375 : subdt = get_subdt_fsd(nfsd, afsdn_latg(:,n), dafsd_tmp(:))
734 235375 : subdt = MIN(subdt, dt)
735 :
736 : ! update fsd and elapsed time
737 2941262 : afsdn_latg(:,n) = afsdn_latg(:,n) + subdt*dafsd_tmp(:)
738 470750 : elapsed_t = elapsed_t + subdt
739 :
740 : END DO
741 :
742 235375 : call icepack_cleanup_fsdn (nfsd, afsdn_latg(:,n))
743 235375 : if (icepack_warnings_aborted(subname)) return
744 2941262 : trcrn(nt_fsd:nt_fsd+nfsd-1,n) = afsdn_latg(:,n)
745 :
746 : end if ! lat growth
747 :
748 304808 : new_size = nfsd
749 304808 : if (n == 1) then
750 : ! add new frazil ice to smallest thickness
751 61115 : if (d_an_newi(n) > puny) then
752 :
753 633246 : afsd_ni(:) = c0
754 :
755 633246 : if (SUM(afsdn_latg(:,n)) > puny) then ! fsd exists
756 :
757 61106 : if (wave_spec) then
758 46449 : if (wave_sig_ht > puny) then
759 0 : call wave_dep_growth (nfsd, wave_spectrum, &
760 0 : wavefreq, dwavefreq, &
761 46449 : new_size)
762 46449 : if (icepack_warnings_aborted(subname)) return
763 : end if
764 :
765 : ! grow in new_size category
766 45003 : afsd_ni(new_size) = (afsdn_latg(new_size,n)*area2(n) + ai0new) &
767 76451 : / (area2(n) + ai0new)
768 46449 : do k = 1, new_size-1 ! diminish other floe cats accordingly
769 46449 : afsd_ni(k) = afsdn_latg(k,n)*area2(n) / (area2(n) + ai0new)
770 : end do
771 557388 : do k = new_size+1, nfsd ! diminish other floe cats accordingly
772 557388 : afsd_ni(k) = afsdn_latg(k,n)*area2(n) / (area2(n) + ai0new)
773 : end do
774 :
775 : else ! grow in smallest floe size category
776 43971 : afsd_ni(1) = (afsdn_latg(1,n)*area2(n) + ai0new) &
777 43971 : / (area2(n) + ai0new)
778 14657 : do k = 2, nfsd ! diminish other floe cats accordingly
779 14657 : afsd_ni(k) = afsdn_latg(k,n)*area2(n) / (area2(n)+ai0new)
780 : enddo
781 : end if ! wave spec
782 :
783 : else ! no fsd, so entirely new ice
784 :
785 9 : if (wave_spec) then
786 7 : if (wave_sig_ht > puny) then
787 0 : call wave_dep_growth (nfsd, wave_spectrum, &
788 0 : wavefreq, dwavefreq, &
789 7 : new_size)
790 7 : if (icepack_warnings_aborted(subname)) return
791 : end if
792 :
793 7 : afsd_ni(new_size) = c1
794 : else
795 2 : afsd_ni(1) = c1
796 : endif ! wave forcing
797 :
798 : endif ! entirely new ice or not
799 :
800 633246 : trcrn(nt_fsd:nt_fsd+nfsd-1,n) = afsd_ni(:)
801 61115 : call icepack_cleanup_fsdn (nfsd, trcrn(nt_fsd:nt_fsd+nfsd-1,n))
802 61115 : if (icepack_warnings_aborted(subname)) return
803 : endif ! d_an_newi > puny
804 : endif ! n = 1
805 :
806 : ! history/diagnostics
807 3163002 : do k = 1, nfsd
808 : ! sum over n
809 972142 : d_afsd_latg(k) = d_afsd_latg(k) &
810 1944284 : + area2(n)*afsdn_latg(k,n) & ! after latg
811 4802478 : - aicen_init(n)*afsdn(k,n) ! at start
812 972142 : d_afsd_newi(k) = d_afsd_newi(k) &
813 2916426 : + aicen(n)*trcrn(nt_fsd+k-1,n) & ! after latg and newi
814 6079428 : - area2(n)*afsdn_latg(k,n) ! after latg
815 : enddo ! k
816 :
817 : end subroutine fsd_add_new_ice
818 :
819 : !=======================================================================
820 : !
821 : ! Given a wave spectrum, calculate size of new floes based on
822 : ! tensile failure, following Shen et al. (2001)
823 : !
824 : ! The tensile mode parameter is based on in-situ measurements
825 : ! by Roach, Smith & Dean (2018).
826 : !
827 : ! authors: Lettie Roach, NIWA/VUW
828 : !
829 46456 : subroutine wave_dep_growth (nfsd, local_wave_spec, &
830 46456 : wavefreq, dwavefreq, &
831 : new_size)
832 :
833 : integer (kind=int_kind), intent(in) :: &
834 : nfsd ! number of floe size categories
835 :
836 : real (kind=dbl_kind), dimension(:), intent(in) :: &
837 : local_wave_spec ! ocean surface wave spectrum as a function of frequency
838 : ! power spectral density of surface elevation, E(f) (units m^2 s)
839 : ! dimension set in ice_forcing
840 :
841 : real(kind=dbl_kind), dimension(:), intent(in) :: &
842 : wavefreq, & ! wave frequencies (s^-1)
843 : dwavefreq ! wave frequency bin widths (s^-1)
844 :
845 : integer (kind=int_kind), intent(out) :: &
846 : new_size ! index of floe size category in which new floes will grow
847 :
848 : ! local variables
849 : real (kind=dbl_kind), parameter :: &
850 : tensile_param = 0.167_dbl_kind ! tensile mode parameter (kg m^-1 s^-2)
851 : ! value from Roach, Smith & Dean (2018)
852 :
853 : real (kind=dbl_kind) :: &
854 15004 : w_amp, & ! wave amplitude (m)
855 15004 : f_peak, & ! peak frequency (s^-1)
856 15004 : r_max ! floe radius (m)
857 :
858 : integer (kind=int_kind) :: k
859 :
860 1207856 : w_amp = c2* SQRT(SUM(local_wave_spec*dwavefreq)) ! sig wave amplitude
861 1207856 : f_peak = wavefreq(MAXLOC(local_wave_spec, DIM=1)) ! peak frequency
862 :
863 : ! tensile failure
864 46456 : if (w_amp > puny .and. f_peak > puny) then
865 46456 : r_max = p5*SQRT(tensile_param*gravit/(pi**5*rhoi*w_amp*2))/f_peak**2
866 : else
867 0 : r_max = bignum
868 : end if
869 :
870 46456 : new_size = nfsd
871 557472 : do k = nfsd-1, 1, -1
872 557472 : if (r_max <= floe_rad_h(k)) new_size = k
873 : end do
874 :
875 46456 : end subroutine wave_dep_growth
876 :
877 : !=======================================================================
878 : !
879 : ! Floes are perimitted to weld together in freezing conditions, according
880 : ! to their geometric probability of overlap if placed randomly on the
881 : ! domain. The rate per unit area c_weld is the total number
882 : ! of floes that weld with another, per square meter, per unit time, in the
883 : ! case of a fully covered ice surface (aice=1), equal to twice the reduction
884 : ! in total floe number. See Roach, Smith & Dean (2018).
885 : !
886 : !
887 : ! authors: Lettie Roach, NIWA/VUW
888 : !
889 :
890 98676 : subroutine fsd_weld_thermo (ncat, nfsd, &
891 : dt, frzmlt, &
892 98676 : aicen, trcrn, &
893 98676 : d_afsd_weld)
894 :
895 : integer (kind=int_kind), intent(in) :: &
896 : ncat , & ! number of thickness categories
897 : nfsd ! number of floe size categories
898 :
899 : real (kind=dbl_kind), intent(in) :: &
900 : dt ! time step (s)
901 :
902 : real (kind=dbl_kind), dimension (:), intent(in) :: &
903 : aicen ! ice concentration
904 :
905 : real (kind=dbl_kind), intent(in) :: &
906 : frzmlt ! freezing/melting potential (W/m^2)
907 :
908 : real (kind=dbl_kind), dimension (:,:), intent(inout) :: &
909 : trcrn ! ice tracers
910 :
911 : real (kind=dbl_kind), dimension (:), intent(inout) :: &
912 : d_afsd_weld ! change in fsd due to welding
913 :
914 : ! local variables
915 : real (kind=dbl_kind), parameter :: &
916 : aminweld = p1 ! minimum ice concentration likely to weld
917 :
918 : real (kind=dbl_kind), parameter :: &
919 : c_weld = 1.0e-8_dbl_kind
920 : ! constant of proportionality for welding
921 : ! total number of floes that weld with another, per square meter,
922 : ! per unit time, in the case of a fully covered ice surface
923 : ! units m^-2 s^-1, see documentation for details
924 :
925 : integer (kind=int_kind) :: &
926 : nt , & ! time step index
927 : n , & ! thickness category index
928 : k, kx, ky, i, j ! floe size category indices
929 :
930 : real (kind=dbl_kind), dimension(nfsd,ncat) :: &
931 2115792 : afsdn ! floe size distribution tracer
932 :
933 : real (kind=dbl_kind), dimension(nfsd,ncat) :: &
934 2115792 : d_afsdn_weld ! change in afsdn due to welding
935 :
936 : real (kind=dbl_kind), dimension(nfsd) :: &
937 486432 : stability , & ! check for stability
938 486432 : nfsd_tmp , & ! number fsd
939 591552 : afsd_init , & ! initial values
940 486432 : afsd_tmp , & ! work array
941 887076 : gain, loss ! welding tendencies
942 :
943 : real(kind=dbl_kind) :: &
944 52560 : prefac , & ! multiplies kernel
945 52560 : kern , & ! kernel
946 52560 : subdt , & ! subcycling time step for stability (s)
947 52560 : elapsed_t ! elapsed subcycling time
948 :
949 : character(len=*), parameter :: subname='(fsd_weld_thermo)'
950 :
951 :
952 5067216 : afsdn (:,:) = c0
953 993708 : afsd_init(:) = c0
954 993708 : stability = c0
955 98676 : prefac = p5
956 :
957 592056 : do n = 1, ncat
958 :
959 4968540 : d_afsd_weld (:) = c0
960 4968540 : d_afsdn_weld(:,n) = c0
961 4968540 : afsdn(:,n) = trcrn(nt_fsd:nt_fsd+nfsd-1,n)
962 493380 : call icepack_cleanup_fsdn (nfsd, afsdn(:,n))
963 493380 : if (icepack_warnings_aborted(subname)) return
964 :
965 : ! If there is some ice in the lower (nfsd-1) categories
966 : ! and there is freezing potential
967 : if ((frzmlt > puny) .and. & ! freezing potential
968 4737960 : (aicen(n) > aminweld) .and. & ! low concentrations unlikely to weld
969 361476 : (SUM(afsdn(1:nfsd-1,n)) > puny)) then ! some ice in nfsd-1 categories
970 :
971 1594944 : afsd_init(:) = afsdn(:,n) ! save initial values
972 1594944 : afsd_tmp (:) = afsd_init(:) ! work array
973 :
974 : ! in case of minor numerical errors
975 1594944 : WHERE(afsd_tmp < puny) afsd_tmp = c0
976 3067200 : afsd_tmp = afsd_tmp/SUM(afsd_tmp)
977 :
978 : ! adaptive sub-timestep
979 122688 : elapsed_t = c0
980 2190968 : DO WHILE (elapsed_t < dt)
981 :
982 : ! calculate sub timestep
983 26887640 : nfsd_tmp = afsd_tmp/floe_area_c
984 0 : WHERE (afsd_tmp > puny) &
985 26887640 : stability = nfsd_tmp/(c_weld*afsd_tmp*aicen(n))
986 26887640 : WHERE (stability < puny) stability = bignum
987 26887640 : subdt = MINVAL(stability)
988 2068280 : subdt = MIN(subdt,dt)
989 :
990 26887640 : loss(:) = c0
991 26887640 : gain(:) = c0
992 :
993 26887640 : do i = 1, nfsd ! consider loss from this category
994 324719960 : do j = 1, nfsd ! consider all interaction partners
995 297832320 : k = iweld(i,j) ! product of i+j
996 322651680 : if (k > i) then
997 165462400 : kern = c_weld * floe_area_c(i) * aicen(n)
998 165462400 : loss(i) = loss(i) + kern*afsd_tmp(i)*afsd_tmp(j)
999 165462400 : if (i.eq.j) prefac = c1 ! otherwise 0.5
1000 165462400 : gain(k) = gain(k) + prefac*kern*afsd_tmp(i)*afsd_tmp(j)
1001 : end if
1002 : end do
1003 : end do
1004 :
1005 : ! does largest category lose?
1006 : ! if (loss(nfsd) > puny) stop 'weld, largest cat losing'
1007 : ! if (gain(1) > puny) stop 'weld, smallest cat gaining'
1008 :
1009 : ! update afsd
1010 26887640 : afsd_tmp(:) = afsd_tmp(:) + subdt*(gain(:) - loss(:))
1011 :
1012 : ! in case of minor numerical errors
1013 26887640 : WHERE(afsd_tmp < puny) afsd_tmp = c0
1014 51707000 : afsd_tmp = afsd_tmp/SUM(afsd_tmp)
1015 :
1016 : ! update time
1017 2068280 : elapsed_t = elapsed_t + subdt
1018 :
1019 : ! stop if all in largest floe size cat
1020 2190968 : if (afsd_tmp(nfsd) > (c1-puny)) exit
1021 :
1022 : END DO ! time
1023 :
1024 122688 : call icepack_cleanup_fsdn (nfsd, afsdn(:,n))
1025 122688 : if (icepack_warnings_aborted(subname)) return
1026 :
1027 1594944 : do k = 1, nfsd
1028 1472256 : afsdn(k,n) = afsd_tmp(k)
1029 1472256 : trcrn(nt_fsd+k-1,n) = afsdn(k,n)
1030 : ! history/diagnostics
1031 1594944 : d_afsdn_weld(k,n) = afsdn(k,n) - afsd_init(k)
1032 : enddo
1033 : endif ! try to weld
1034 : enddo ! ncat
1035 :
1036 : ! history/diagnostics
1037 993708 : do k = 1, nfsd
1038 895032 : d_afsd_weld(k) = c0
1039 5468868 : do n = 1, ncat
1040 5370192 : d_afsd_weld(k) = d_afsd_weld(k) + aicen(n)*d_afsdn_weld(k,n)
1041 : end do ! n
1042 : end do ! k
1043 :
1044 : end subroutine fsd_weld_thermo
1045 :
1046 : !=======================================================================
1047 : !
1048 : ! Adaptive timestepping (process agnostic)
1049 : ! See reference: Horvat & Tziperman (2017) JGR, Appendix A
1050 : !
1051 : ! authors: 2018 Lettie Roach, NIWA/VUW
1052 : !
1053 : !
1054 398547 : function get_subdt_fsd(nfsd, afsd_init, d_afsd) &
1055 187686 : result(subdt)
1056 :
1057 : integer (kind=int_kind), intent(in) :: &
1058 : nfsd ! number of floe size categories
1059 :
1060 : real (kind=dbl_kind), dimension (nfsd), intent(in) :: &
1061 : afsd_init, d_afsd ! floe size distribution tracer
1062 :
1063 : ! output
1064 : real (kind=dbl_kind) :: &
1065 : subdt ! subcycle timestep (s)
1066 :
1067 : ! local variables
1068 : real (kind=dbl_kind), dimension (nfsd) :: &
1069 2122957 : check_dt ! to compute subcycle timestep (s)
1070 :
1071 : integer (kind=int_kind) :: k
1072 :
1073 4442428 : check_dt(:) = bignum
1074 4442428 : do k = 1, nfsd
1075 4043881 : if (d_afsd(k) > puny) check_dt(k) = (1-afsd_init(k))/d_afsd(k)
1076 4442428 : if (d_afsd(k) < -puny) check_dt(k) = afsd_init(k)/ABS(d_afsd(k))
1077 : end do
1078 :
1079 4442428 : subdt = MINVAL(check_dt)
1080 :
1081 398547 : end function get_subdt_fsd
1082 :
1083 :
1084 : !=======================================================================
1085 :
1086 : end module icepack_fsd
1087 :
1088 : !=======================================================================
1089 :
|