LCOV - code coverage report
Current view: top level - icepack/columnphysics - icepack_wavefracspec.F90 (source / functions) Coverage Total Hit
Test: 250115-172326:736c1771a8:7:first,quick,base,travis,io,gridsys,unittest Lines: 91.54 % 201 184
Test Date: 2025-01-15 16:42:12 Functions: 100.00 % 5 5

            Line data    Source code
       1              : !  This module contains the subroutines required to fracture sea ice
       2              : !  by ocean surface waves
       3              : !
       4              : !  Theory based on:
       5              : !
       6              : !    Horvat, C., & Tziperman, E. (2015). A prognostic model of the sea-ice
       7              : !    floe size and thickness distribution. The Cryosphere, 9(6), 2119–2134.
       8              : !    doi:10.5194/tc-9-2119-2015
       9              : !
      10              : !  and implementation described in:
      11              : !
      12              : !    Roach, L. A., Horvat, C., Dean, S. M., & Bitz, C. M. (2018). An emergent
      13              : !    sea ice floe size distribution in a global coupled ocean--sea ice model.
      14              : !    Journal of Geophysical Research: Oceans, 123(6), 4322–4337.
      15              : !    doi:10.1029/2017JC013692
      16              : !
      17              : !  now with some modifications to allow direct input of ocean surface wave spectrum.
      18              : !
      19              : !  We calculate the fractures that would occur if waves enter a fully ice-covered
      20              : !  region defined in one dimension in the direction of propagation, and then apply
      21              : !  the outcome proportionally to the ice-covered fraction in each grid cell. Assuming
      22              : !  that sea ice flexes with the sea surface height field, strains are computed on this
      23              : !  sub-grid-scale 1D domain. If the strain between successive extrema exceeds a critical
      24              : !  value new floes are formed with diameters equal to the distance between the extrema.
      25              : !
      26              : !  authors: 2016-8 Lettie Roach, NIWA/VUW
      27              : !
      28              : !
      29              :       module icepack_wavefracspec
      30              : 
      31              :       use icepack_kinds
      32              :       use icepack_parameters, only: p01, p5, c0, c1, c2, c3, c4, c10
      33              :       use icepack_parameters, only: bignum, puny, gravit, pi
      34              :       use icepack_tracers, only: nt_fsd, ncat, nfsd
      35              :       use icepack_warnings, only: warnstr, icepack_warnings_add,  icepack_warnings_aborted
      36              :       use icepack_fsd
      37              : 
      38              :       implicit none
      39              :       private
      40              :       public :: icepack_init_wave, icepack_step_wavefracture
      41              : 
      42              :       real (kind=dbl_kind), parameter  :: &
      43              :          swh_minval = 0.01_dbl_kind,  & ! minimum value of wave height (m)   ! LCOV_EXCL_LINE
      44              :          straincrit = 3.e-5_dbl_kind, & ! critical strain   ! LCOV_EXCL_LINE
      45              :          D          = 1.e4_dbl_kind,  & ! domain size   ! LCOV_EXCL_LINE
      46              :          dx         = c1,             & ! domain spacing   ! LCOV_EXCL_LINE
      47              :          threshold  = c10               ! peak-finding threshold -
      48              :                                         ! points are defined to be extrema if they
      49              :                                         ! are a local max or min over a distance
      50              :                                         ! of 10m on both sides, based on the
      51              :                                         ! observations of Toyota et al. (2011) who
      52              :                                         ! find this to be the order of the smallest
      53              :                                         ! floe size affected by wave fracture
      54              : 
      55              :       integer (kind=int_kind), parameter :: &
      56              :          nx = 10000         ! number of points in domain
      57              : 
      58              :       integer (kind=int_kind), parameter :: &
      59              :          max_no_iter = 100 ! max no of iterations to compute wave fracture
      60              : 
      61              : 
      62              : !=======================================================================
      63              : 
      64              :       contains
      65              : 
      66              : !=======================================================================
      67              : !autodocument_start icepack_init_wave
      68              : !  Initialize the wave spectrum and frequencies for the FSD
      69              : !
      70              : !  authors: 2018 Lettie Roach, NIWA/VUW
      71              : 
      72          154 :       subroutine icepack_init_wave(nfreq,                 &
      73          154 :                                    wave_spectrum_profile, &   ! LCOV_EXCL_LINE
      74          154 :                                    wavefreq, dwavefreq)
      75              : 
      76              :       integer(kind=int_kind), intent(in) :: &
      77              :          nfreq                    ! number of wave frequencies
      78              : 
      79              :       real(kind=dbl_kind), dimension(:), intent(out) :: &
      80              :          wave_spectrum_profile, & ! ocean surface wave spectrum as a function of frequency   ! LCOV_EXCL_LINE
      81              :                                   ! power spectral density of surface elevation, E(f) (units m^2 s)
      82              :          wavefreq,              & ! wave frequencies (s^-1)
      83              :          dwavefreq                ! wave frequency bin widths (s^-1)
      84              : 
      85              : !autodocument_end
      86              :       ! local variables
      87              :       integer (kind=int_kind) :: k
      88              : 
      89              :       real(kind=dbl_kind), dimension(100) :: &
      90              :          wave_spectrum_data       ! default values for nfreq profile
      91              : 
      92              :       ! set for 25 frequencies
      93              : 
      94          154 :       wave_spectrum_data = c0
      95              : 
      96              :       ! FOR TESTING ONLY - do not use for actual runs!!
      97          154 :       wave_spectrum_data(1) = 0.00015429197810590267
      98          154 :       wave_spectrum_data(2) = 0.002913531381636858
      99          154 :       wave_spectrum_data(3) = 0.02312942035496235
     100          154 :       wave_spectrum_data(4) = 0.07201970368623734
     101          154 :       wave_spectrum_data(5) = 0.06766948103904724
     102          154 :       wave_spectrum_data(6) = 0.005527883302420378
     103          154 :       wave_spectrum_data(7) = 3.326293881400488e-05
     104          154 :       wave_spectrum_data(8) = 6.815936703929992e-10
     105          154 :       wave_spectrum_data(9) = 2.419401186610744e-20
     106              : 
     107         4004 :       do k = 1, nfreq
     108         4004 :          wave_spectrum_profile(k) = wave_spectrum_data(k)
     109              :       enddo
     110              : 
     111              :       ! hardwired for wave coupling with NIWA version of Wavewatch
     112              :       ! From Wavewatch, f(n+1) = C*f(n) where C is a constant set by the user
     113              :       ! These freq are for C = 1.1
     114              :       wavefreq = (/0.04118,     0.045298,    0.0498278,   0.05481058,  0.06029164, &
     115              :                    0.06632081,  0.07295289,  0.08024818,  0.08827299,  0.09710029, &   ! LCOV_EXCL_LINE
     116              :                    0.10681032,  0.11749136,  0.1292405,   0.14216454,  0.15638101, &   ! LCOV_EXCL_LINE
     117              :                    0.17201911,  0.18922101,  0.20814312,  0.22895744,  0.25185317, &   ! LCOV_EXCL_LINE
     118         4004 :                    0.27703848,  0.30474234,  0.33521661,  0.36873826,  0.40561208/)
     119              : 
     120              :       ! boundaries of bin n are at f(n)*sqrt(1/C) and f(n)*sqrt(C)
     121         4004 :       dwavefreq(:) = wavefreq(:)*(SQRT(1.1_dbl_kind) - SQRT(c1/1.1_dbl_kind))
     122              : 
     123          154 :       end subroutine icepack_init_wave
     124              : 
     125              : !=======================================================================
     126              : !
     127              : !  Calculate the change in the FSD arising from wave fracture
     128              : !
     129              : !  authors: 2017 Lettie Roach, NIWA/VUW
     130              : !
     131       176435 :       function get_dafsd_wave(afsd_init, fracture_hist, frac) &
     132       176435 :                               result(d_afsd)
     133              : 
     134              :       real (kind=dbl_kind), dimension (:), intent(in) :: &
     135              :          afsd_init, fracture_hist
     136              : 
     137              :       real (kind=dbl_kind), dimension (:,:), intent(in) :: &
     138              :          frac
     139              : 
     140              :       ! output
     141              :       real (kind=dbl_kind), dimension (nfsd) :: &
     142              :          d_afsd
     143              : 
     144              :       ! local variables
     145              :       real (kind=dbl_kind), dimension (nfsd) :: &
     146       352870 :          loss, gain, omega
     147              : 
     148              :       integer (kind=int_kind) :: k
     149              : 
     150              :       character(len=*),parameter :: subname='(get_dafsd_wave)'
     151              : 
     152      2293655 :       do k = 1, nfsd
     153              :          ! fracture_hist is already normalized
     154     13938365 :          omega(k) = afsd_init(k)*SUM(fracture_hist(1:k-1))
     155              :       end do
     156              : 
     157      2293655 :       loss = omega
     158              : 
     159      2293655 :       do k =1,nfsd
     160     27700295 :          gain(k) = SUM(omega*frac(:,k))
     161              :       end do
     162              : 
     163      2293655 :       d_afsd(:) = gain(:) - loss(:)
     164              : 
     165      2293655 :       if (SUM(d_afsd(:)) > puny) then
     166            0 :          write(warnstr,*) subname, 'area not conserved, waves'
     167            0 :          call icepack_warnings_add(warnstr)
     168              :       endif
     169              : 
     170      2293655 :       WHERE (ABS(d_afsd).lt.puny) d_afsd = c0
     171              : 
     172       176435 :       end  function get_dafsd_wave
     173              : 
     174              : !=======================================================================
     175              : !autodocument_start icepack_step_wavefracture
     176              : !
     177              : !  Given fracture histogram computed from local wave spectrum, evolve
     178              : !  the floe size distribution
     179              : !
     180              : !  authors: 2018 Lettie Roach, NIWA/VUW
     181              : !
     182     22595640 :       subroutine icepack_step_wavefracture(wave_spec_type,   &
     183              :                   dt,            nfreq,                      &   ! LCOV_EXCL_LINE
     184     22595640 :                   aice,          vice,            aicen,     &   ! LCOV_EXCL_LINE
     185     22595640 :                   wave_spectrum, wavefreq,        dwavefreq, &   ! LCOV_EXCL_LINE
     186     22595640 :                   trcrn,         d_afsd_wave)
     187              : 
     188              : 
     189              :       character (len=char_len), intent(in) :: &
     190              :          wave_spec_type  ! type of wave spectrum forcing
     191              : 
     192              :       integer (kind=int_kind), intent(in) :: &
     193              :          nfreq           ! number of wave frequency categories
     194              : 
     195              :       real (kind=dbl_kind), intent(in) :: &
     196              :          dt,           & ! time step   ! LCOV_EXCL_LINE
     197              :          aice,         & ! ice area fraction   ! LCOV_EXCL_LINE
     198              :          vice            ! ice volume per unit area
     199              : 
     200              :       real (kind=dbl_kind), dimension(ncat), intent(in) :: &
     201              :          aicen           ! ice area fraction (categories)
     202              : 
     203              :       real (kind=dbl_kind), dimension (:), intent(in) :: &
     204              :          wavefreq,     & ! wave frequencies (s^-1)   ! LCOV_EXCL_LINE
     205              :          dwavefreq       ! wave frequency bin widths (s^-1)
     206              : 
     207              :       real (kind=dbl_kind), dimension(:), intent(in) :: &
     208              :          wave_spectrum   ! ocean surface wave spectrum as a function of frequency
     209              :                          ! power spectral density of surface elevation, E(f) (units m^2 s)
     210              : 
     211              :       real (kind=dbl_kind), dimension(:,:), intent(inout) :: &
     212              :          trcrn           ! tracer array
     213              : 
     214              :       real (kind=dbl_kind), dimension(:), intent(out) :: &
     215              :          d_afsd_wave     ! change in fsd due to waves
     216              : 
     217              :       real (kind=dbl_kind), dimension(nfsd,ncat) :: &
     218     45191280 :          d_afsdn_wave    ! change in fsd due to waves, per category
     219              : 
     220              : !autodocument_end
     221              :       ! local variables
     222              :       integer (kind=int_kind) :: &
     223              :          n, k,  &   ! LCOV_EXCL_LINE
     224              :          nsubt ! number of subcycles
     225              : 
     226              :       real (kind=dbl_kind), dimension (nfsd, nfsd) :: &
     227     45191280 :          frac
     228              : 
     229              :       real (kind=dbl_kind) :: &
     230              :          hbar         , & ! mean ice thickness   ! LCOV_EXCL_LINE
     231              :          elapsed_t    , & ! elapsed subcycling time   ! LCOV_EXCL_LINE
     232              :          subdt        , & ! subcycling time step   ! LCOV_EXCL_LINE
     233              :          cons_error       ! area conservation error
     234              : 
     235              :       real (kind=dbl_kind), dimension (nfsd) :: &
     236     45191280 :          fracture_hist, & ! fracture histogram   ! LCOV_EXCL_LINE
     237     22595640 :          afsd_init    , & ! tracer array   ! LCOV_EXCL_LINE
     238     45191280 :          afsd_tmp     , & ! tracer array   ! LCOV_EXCL_LINE
     239     22595640 :          d_afsd_tmp       ! change
     240              : 
     241              :       real (kind=dbl_kind) :: &
     242              :            local_sig_ht
     243              : 
     244              :       character(len=*),parameter :: &
     245              :          subname='(icepack_step_wavefracture)'
     246              : 
     247              :       !------------------------------------
     248              : 
     249              :       ! initialize
     250    293743320 :       d_afsd_wave    (:)   = c0
     251   1491312240 :       d_afsdn_wave   (:,:) = c0
     252    293743320 :       fracture_hist  (:)   = c0
     253              : 
     254              :       ! if all ice is not in first floe size category
     255     22636476 :       if (.NOT. ALL(trcrn(nt_fsd,:).ge.c1-puny)) then
     256              : 
     257    587274714 :       local_sig_ht = c4*SQRT(SUM(wave_spectrum(:)*dwavefreq(:)))
     258              :       ! do not try to fracture for minimal ice concentration or zero wave spectrum
     259              : !      if ((aice > p01).and.(MAXVAL(wave_spectrum(:)) > puny)) then
     260     22587489 :       if ((aice > p01).and.(local_sig_ht>0.1_dbl_kind)) then
     261              : 
     262        11417 :          hbar = vice / aice
     263              : 
     264              :          ! calculate fracture histogram
     265              :          call wave_frac(nfreq, wave_spec_type, &
     266              :                         wavefreq, dwavefreq, &   ! LCOV_EXCL_LINE
     267        11417 :                         hbar, wave_spectrum, fracture_hist)
     268              : 
     269        11417 :          if (icepack_warnings_aborted(subname)) return
     270              : 
     271              :          ! if fracture occurs
     272       159838 :          if (MAXVAL(fracture_hist) > puny) then
     273              :             ! protect against small numerical errors
     274         9329 :             call icepack_cleanup_fsd (trcrn(nt_fsd:nt_fsd+nfsd-1,:) )
     275         9329 :             if (icepack_warnings_aborted(subname)) return
     276              : 
     277        55974 :             do n = 1, ncat
     278              : 
     279       606385 :               afsd_init(:) = trcrn(nt_fsd:nt_fsd+nfsd-1,n)
     280              : 
     281              :               ! if there is ice, and a FSD, and not all ice is the smallest floe size
     282              :               if ((aicen(n) > puny) .and. (SUM(afsd_init(:)) > puny) &
     283       615714 :                                     .and.     (afsd_init(1) < c1)) then
     284              : 
     285       581113 :                  afsd_tmp =  afsd_init
     286              : 
     287              :                   ! frac does not vary within subcycle
     288      7018057 :                   frac(:,:) = c0
     289       536412 :                   do k = 2, nfsd
     290      3486678 :                      frac(k,1:k-1) = fracture_hist(1:k-1)
     291              :                   end do
     292       581113 :                   do k = 1, nfsd
     293     18218881 :                      if (SUM(frac(k,:)) > c0) frac(k,:) = frac(k,:)/SUM(frac(k,:))
     294              :                   end do
     295              : 
     296              :                   ! adaptive sub-timestep
     297        44701 :                   elapsed_t = c0
     298        44701 :                   cons_error = c0
     299        44701 :                   nsubt = 0
     300       221136 :                   DO WHILE (elapsed_t < dt)
     301       209511 :                      nsubt = nsubt + 1
     302              : 
     303              :                      ! if all floes in smallest category already, exit
     304       209511 :                      if (afsd_tmp(1).ge.c1-puny) EXIT
     305              : 
     306              :                      ! calculate d_afsd using current afstd
     307       176435 :                      d_afsd_tmp = get_dafsd_wave(afsd_tmp, fracture_hist, frac)
     308              : 
     309              :                      ! check in case wave fracture struggles to converge
     310       176435 :                      if (nsubt>100) then
     311            0 :                         write(warnstr,*) subname, &
     312            0 :                           'warning: step_wavefracture struggling to converge'
     313            0 :                         call icepack_warnings_add(warnstr)
     314              :                      endif
     315              : 
     316              :                      ! required timestep
     317       176435 :                      subdt = get_subdt_fsd(afsd_tmp, d_afsd_tmp)
     318       176435 :                      subdt = MIN(subdt, dt)
     319              : 
     320              :                      ! update afsd
     321      2293655 :                      afsd_tmp = afsd_tmp + subdt * d_afsd_tmp(:)
     322              : 
     323              :                      ! check conservation and negatives
     324      2470090 :                      if (MINVAL(afsd_tmp) < -puny) then
     325            0 :                         write(warnstr,*) subname, 'wb, <0 loop'
     326            0 :                         call icepack_warnings_add(warnstr)
     327              :                      endif
     328      2470090 :                      if (MAXVAL(afsd_tmp) > c1+puny) then
     329            0 :                         write(warnstr,*) subname, 'wb, >1 loop'
     330            0 :                         call icepack_warnings_add(warnstr)
     331              :                      endif
     332              : 
     333              :                      ! update time
     334       176435 :                      elapsed_t = elapsed_t + subdt
     335              : 
     336              :                   END DO ! elapsed_t < dt
     337              : 
     338              :                   ! In some cases---particularly for strong fracturing---the equation
     339              :                   ! for wave fracture does not quite conserve area.
     340              :                   ! With the dummy wave forcing, this happens < 2% of the time (in
     341              :                   ! 1997) and is always less than 10^-7.
     342              :                   ! Simply renormalizing may cause the first floe size
     343              :                   ! category to reduce, which is not physically allowed
     344              :                   ! to happen. So we adjust here
     345       581113 :                   cons_error = SUM(afsd_tmp) - c1
     346              : 
     347              :                   ! area loss: add to first category
     348        44701 :                   if (cons_error.lt.c0) then
     349         7536 :                       afsd_tmp(1) = afsd_tmp(1) - cons_error
     350              :                   else
     351              :                   ! area gain: take it from the largest possible category
     352       393643 :                   do k = nfsd, 1, -1
     353       393643 :                      if (afsd_tmp(k).gt.cons_error) then
     354        37165 :                         afsd_tmp(k) = afsd_tmp(k) - cons_error
     355        37165 :                         EXIT
     356              :                      end if
     357              :                   end do
     358              :                   end if
     359              : 
     360              :                   ! update trcrn
     361      1117525 :                   trcrn(nt_fsd:nt_fsd+nfsd-1,n) = afsd_tmp/SUM(afsd_tmp)
     362        44701 :                   call icepack_cleanup_fsd (trcrn(nt_fsd:nt_fsd+nfsd-1,:) )
     363        44701 :                   if (icepack_warnings_aborted(subname)) return
     364              : 
     365              :                   ! for diagnostics
     366       581113 :                   d_afsdn_wave(:,n) = afsd_tmp(:) - afsd_init(:)
     367       581113 :                   d_afsd_wave (:)   = d_afsd_wave(:) + aicen(n)*d_afsdn_wave(:,n)
     368              :                endif ! aicen > puny
     369              :             enddo    ! n
     370              :         endif ! fracture hist > 0
     371              : 
     372              :       endif          ! aice > p01
     373              :       endif         ! all small floes
     374              : 
     375              :       end subroutine icepack_step_wavefracture
     376              : 
     377              : !=======================================================================
     378              : !
     379              : !  Calculates functions to describe the change in the FSD when waves
     380              : !  fracture ice, given a wave spectrum (1D frequency, nfreq (default 25)
     381              : !  frequency bins)
     382              : !
     383              : !  We calculate extrema and if these are successive maximum,
     384              : !  minimum, maximum or vice versa, and have strain greater than a
     385              : !  critical strain, break ice and create new floes with lengths equal
     386              : !  to these distances. Based on MatLab code written by Chris Horvat,
     387              : !  from Horvat & Tziperman (2015).
     388              : !
     389              : !  Note that a realization of sea surface height requires a random phase.
     390              : !
     391              : !  authors: 2018 Lettie Roach, NIWA/VUW
     392              : 
     393        11417 :       subroutine wave_frac(nfreq, wave_spec_type, &
     394        22834 :                            wavefreq, dwavefreq, &   ! LCOV_EXCL_LINE
     395        11417 :                            hbar, spec_efreq, frac_local)
     396              : 
     397              :       integer (kind=int_kind), intent(in) :: &
     398              :          nfreq         ! number of wave frequency categories
     399              : 
     400              :       character (len=char_len), intent(in) :: &
     401              :         wave_spec_type ! type of wave spectrum forcing
     402              : 
     403              :       real (kind=dbl_kind),  intent(in) :: &
     404              :          hbar          ! mean ice thickness (m)
     405              : 
     406              :       real (kind=dbl_kind), dimension (:), intent(in) :: &
     407              :          wavefreq,   & ! wave frequencies (s^-1)   ! LCOV_EXCL_LINE
     408              :          dwavefreq,  & ! wave frequency bin widths (s^-1)   ! LCOV_EXCL_LINE
     409              :          spec_efreq    ! wave spectrum (m^2 s)
     410              : 
     411              :       real (kind=dbl_kind), dimension (nfsd), intent(out) :: &
     412              :          frac_local    ! fracturing histogram
     413              : 
     414              :       ! local variables
     415              : 
     416              :       integer (kind=int_kind) :: j, k, iter, loop_max_iter
     417              : 
     418              :       real (kind=dbl_kind) :: &
     419              :          fracerror ! difference between successive histograms
     420              : 
     421              :       real (kind=dbl_kind), parameter :: &
     422              :          errortol = 6.5e-4  ! tolerance in error between successive histograms
     423              : 
     424              :       real (kind=dbl_kind), dimension(nfreq) :: &
     425        22834 :          lambda,                   & ! wavelengths (m)   ! LCOV_EXCL_LINE
     426        22834 :          spec_coeff,               &   ! LCOV_EXCL_LINE
     427        22834 :          phi, rand_array, summand
     428              : 
     429              :       real (kind=dbl_kind), dimension(nx) :: &
     430              :          fraclengths
     431              : 
     432              :       real (kind=dbl_kind), dimension(nx) :: &
     433              :          X,  &    ! spatial domain (m)   ! LCOV_EXCL_LINE
     434              :          eta      ! sea surface height field (m)
     435              : 
     436              :       real (kind=dbl_kind), dimension(nfsd) :: &
     437        11417 :          frachistogram, & ! histogram   ! LCOV_EXCL_LINE
     438        11417 :          prev_frac_local  ! previous histogram
     439              : 
     440              :       character(len=*),parameter :: &
     441              :          subname='(wave_frac)'
     442              : 
     443              : 
     444        11417 :       if (trim(wave_spec_type).eq.'random') then
     445              :           ! run wave fracture to convergence
     446            0 :           loop_max_iter = max_no_iter
     447              :       else
     448        11417 :           loop_max_iter = 1
     449              :       end if
     450              : 
     451              :       ! spatial domain
     452    114181417 :       do j = 1, nx
     453    114181417 :          X(j)= j*dx
     454              :       end do
     455              : 
     456              :       ! dispersion relation
     457       296842 :       lambda (:) = gravit/(c2*pi*wavefreq (:)**2)
     458              : 
     459              :       ! spectral coefficients
     460       296842 :       spec_coeff = sqrt(c2*spec_efreq*dwavefreq)
     461              : 
     462              :       ! initialize frac lengths
     463        11417 :       fraclengths(:) = c0
     464       148421 :       prev_frac_local(:) = c0
     465       148421 :       frachistogram(:) = c0
     466        11417 :       fracerror = bignum
     467              : 
     468              :       ! loop while fracerror greater than error tolerance
     469        11417 :       iter = 0
     470        22834 :       do while (iter < loop_max_iter .and. fracerror > errortol)
     471        11417 :          iter = iter + 1
     472              : 
     473              :          ! Phase for each Fourier component may be constant or
     474              :          ! a random phase that varies in each i loop
     475              :          ! See documentation for discussion
     476        11417 :          if (trim(wave_spec_type).eq.'random') then
     477            0 :             call RANDOM_NUMBER(rand_array)
     478            0 :             if (icepack_warnings_aborted(subname)) return
     479              :          else
     480       296842 :             rand_array(:) = p5
     481              :          endif
     482       296842 :          phi = c2*pi*rand_array
     483              : 
     484    114181417 :          do j = 1, nx
     485              :             ! SSH field in space (sum over wavelengths, no attenuation)
     486   2968420000 :             summand = spec_coeff*COS(2*pi*X(j)/lambda+phi)
     487   2968431417 :             eta(j)  = SUM(summand)
     488              :          end do
     489              : 
     490        11417 :          fraclengths(:) = c0
     491    114181417 :          if ((SUM(ABS(eta)) > puny).and.(hbar > puny)) then
     492        11417 :             call get_fraclengths(X, eta, fraclengths, hbar)
     493        11417 :             if (icepack_warnings_aborted(subname)) return
     494              :          end if
     495              : 
     496              :          ! convert from diameter to radii
     497    114181417 :          fraclengths(:) = fraclengths(:)/c2
     498              : 
     499     20891417 :          if (ALL(fraclengths.lt.floe_rad_l(1))) then
     500        27144 :             frac_local(:) = c0
     501              :          else
     502              :             ! bin into FS cats
     503              :             ! accumulate the frac histogram each iteration
     504     93299329 :             do j = 1, size(fraclengths)
     505     93299329 :                if (fraclengths(j).gt.floe_rad_l(1)) then
     506     39743520 :                   do k = 1, nfsd-1
     507     36431560 :                      if ((fraclengths(j) >= floe_rad_l(k)) .and. &
     508      3311960 :                          (fraclengths(j) < floe_rad_l(k+1))) then
     509      3311716 :                         frachistogram(k) = frachistogram(k) + 1
     510              :                      end if
     511              :                   end do
     512      3311960 :                if (fraclengths(j)>floe_rad_l(nfsd)) frachistogram(nfsd) = frachistogram(nfsd) + 1
     513              :                end if
     514              :             end do
     515              : 
     516       121277 :             do k = 1, nfsd
     517       121277 :                frac_local(k) = floe_rad_c(k)*frachistogram(k)
     518              :             end do
     519              : 
     520              :             ! normalize
     521       345173 :             if (SUM(frac_local) /= c0) frac_local(:) = frac_local(:) / SUM(frac_local(:))
     522              : 
     523              :          end if
     524              : 
     525              :          ! wave fracture run to convergence
     526        11417 :          if (trim(wave_spec_type).eq.'random') then
     527              : 
     528              :              ! check avg frac local against previous iteration
     529            0 :              fracerror = SUM(ABS(frac_local - prev_frac_local))/nfsd
     530              : 
     531              :              ! save histogram for next iteration
     532            0 :              prev_frac_local = frac_local
     533              : 
     534              :          end if
     535              : 
     536              :       END DO
     537              : 
     538        11417 :       if (iter >= max_no_iter) then
     539            0 :          write(warnstr,*) subname,'warning: wave_frac struggling to converge'
     540            0 :          call icepack_warnings_add(warnstr)
     541              :       endif
     542              : 
     543              :       end subroutine wave_frac
     544              : 
     545              : !===========================================================================
     546              : !
     547              : !  Given the (attenuated) sea surface height, find the strain across triplets
     548              : !  of max, min, max or min, max, min (local extrema within 10m).
     549              : !  If this strain is greater than the  critical strain, ice can fracture
     550              : !  and new floes are formed with sizes equal to the distances between
     551              : !  extrema. Based on MatLab code written by Chris Horvat,
     552              : !  from Horvat & Tziperman (2015).
     553              : !
     554              : !  authors: 2016 Lettie Roach, NIWA/VUW
     555              : !
     556        11417 :       subroutine get_fraclengths(X, eta, fraclengths, hbar)
     557              : 
     558              :       real (kind=dbl_kind), intent(in) :: &
     559              :          hbar             ! mean thickness (m)
     560              : 
     561              :       real (kind=dbl_kind), intent(in), dimension (nx) :: &
     562              :          X, &              ! spatial domain (m)   ! LCOV_EXCL_LINE
     563              :          eta               ! sea surface height field (m)
     564              : 
     565              :       real (kind=dbl_kind), intent(inout), dimension (nx) :: &
     566              :          fraclengths      ! The distances between fracture points
     567              :                           ! Size cannot be greater than nx.
     568              :                           ! In practice, will be much less
     569              : 
     570              :       ! local variables
     571              :       integer (kind=int_kind) :: &
     572              :          spcing,        & ! distance over which to search for extrema on each side of point   ! LCOV_EXCL_LINE
     573              :          j, k,          & ! indices to iterate over domain   ! LCOV_EXCL_LINE
     574              :          first, last,   & ! indices over which to search for extrema   ! LCOV_EXCL_LINE
     575              :          j_neg,         & ! nearest extrema backwards   ! LCOV_EXCL_LINE
     576              :          j_pos,         & ! nearest extrema forwards   ! LCOV_EXCL_LINE
     577              :          n_above          ! number of points where strain is above critical
     578              : 
     579              :       real (kind=dbl_kind), dimension(nx) :: &
     580              :          fracdistances, & ! distances in space where fracture has occurred   ! LCOV_EXCL_LINE
     581              :          strain           ! the strain between triplets of extrema
     582              : 
     583              :       logical (kind=log_kind), dimension(nx) :: &
     584              :          is_max, is_min,& ! arrays to hold whether each point is a local max or min   ! LCOV_EXCL_LINE
     585              :          is_extremum,   & ! or extremum   ! LCOV_EXCL_LINE
     586              :          is_triplet       ! or triplet of extrema
     587              : 
     588              :       real (kind=dbl_kind) :: &
     589              :          denominator,   & ! denominator in strain equation   ! LCOV_EXCL_LINE
     590              :          delta,         & ! difference in x between current and prev extrema   ! LCOV_EXCL_LINE
     591              :          delta_pos        ! difference in x between next and current extrema
     592              : 
     593              :       integer (kind=int_kind), dimension(1) :: &
     594              :          maxj, minj       ! indices of local max and min
     595              : 
     596              :       ! ------- equivalent of peakfinder2
     597              :       ! given eta and spcing, compute extremelocs in ascending order
     598        11417 :       spcing = nint(threshold/dx)
     599              : 
     600        11417 :       is_max = .false.
     601        11417 :       is_min = .false.
     602        11417 :       is_extremum = .false.
     603        11417 :       is_triplet = .false.
     604        11417 :       strain = c0
     605        11417 :       j_neg = 0
     606        11417 :       j_pos = 0
     607        11417 :       fraclengths(:) = c0
     608              : 
     609              :       ! search for local max and min within spacing
     610              :       ! on either side of each point
     611              : 
     612    114181417 :       do j = 1, nx
     613              : 
     614              :          ! indices within which to search for local max and min
     615    114170000 :          first = MAX(1,j-spcing)
     616    114170000 :          last  = MIN(nx,j+spcing)
     617              : 
     618              :          ! location of max and min within spacing
     619    114170000 :          maxj = MAXLOC(eta(first:last))
     620    114170000 :          minj = MINLOC(eta(first:last))
     621              : 
     622              :          ! current j is the max or the min, save it
     623    114170000 :          if (maxj(1)+first-1 == j) is_max(j) = .true.
     624    114170000 :          if (minj(1)+first-1 == j) is_min(j) = .true.
     625              : 
     626              :          ! save whether max or min in one array
     627    114181417 :          if (is_min(j).or.is_max(j)) is_extremum(j) = .true.
     628              :       end do
     629              : 
     630              :       ! loop over points
     631              :       ! nothing can happen at the first or last
     632    114158583 :       do j = 2, nx-1
     633    114158583 :          if (is_extremum(j)) then
     634      4076547 :             if (j == 2) then
     635            0 :                if (is_extremum(1)) j_neg = 1
     636              :             else
     637    113846393 :                do k = j-1, 1, -1
     638    113846393 :                   if (is_extremum(k)) then
     639      4076547 :                      j_neg = k
     640      4076547 :                      EXIT
     641              :                   end if
     642              :                end do
     643              :             end if
     644              : 
     645    112955371 :             do k = j+1, nx
     646    112955371 :                if (is_extremum(k)) then
     647      4070738 :                   j_pos = k
     648      4070738 :                   EXIT
     649              :                end if
     650              :             end do
     651              : 
     652              :             ! find triplets of max and min
     653      4076547 :             if ((j_neg > 0).and.(j_pos > 0)) then
     654      4076547 :                if (is_max(j_neg).and.is_min(j).and.is_max(j_pos)) &
     655      1760070 :                   is_triplet(j) = .true.
     656      4076547 :                if (is_min(j_neg).and.is_max(j).and.is_min(j_pos)) &
     657      1763554 :                   is_triplet(j) = .true.
     658              :             end if
     659              : 
     660              :             ! calculate strain
     661      4076547 :             if (is_triplet(j)) then
     662              : 
     663              :                ! finite differences
     664      3523624 :                delta_pos = X(j_pos) - X(j    )
     665      3523624 :                delta     = X(j    ) - X(j_neg)
     666              : 
     667              :                ! This equation differs from HT2015 by a factor 2 in numerator
     668              :                ! and eta(j_pos). This is the correct form of the equation.
     669              : 
     670      3523624 :                denominator = delta*delta_pos*(delta+delta_pos)
     671              : 
     672      3523624 :                if (denominator.ne.c0) &
     673              :                    strain(j) = ABS(hbar*(eta(j_neg)* delta_pos &   ! LCOV_EXCL_LINE
     674              :                                 - eta(j    )*(delta_pos+delta) &   ! LCOV_EXCL_LINE
     675              :                                 + eta(j_pos)*           delta) &   ! LCOV_EXCL_LINE
     676      3523624 :                                 / denominator)
     677              : 
     678              :             end if ! is triplet
     679              :          end if ! is extremum
     680              : 
     681              :       end do ! loop over j
     682              : 
     683    114181417 :       n_above = COUNT(strain > straincrit)
     684        11417 :       fracdistances(:) = c0
     685              : 
     686              :       ! only do if we have some strains exceeding strain crit
     687        11417 :       if (n_above>0) then
     688              : 
     689         9334 :           k = 0
     690     93349334 :           do j = 1, nx
     691     93349334 :             if (strain(j) > straincrit) then
     692      3321294 :               k = k + 1
     693      3321294 :               fracdistances(k) = X(j)
     694              :             end if
     695              :           end do
     696              : 
     697      3321294 :           do j = 1, n_above-1
     698      3321294 :               fraclengths(j) = fracdistances(j+1) - fracdistances(j)
     699              :           end do
     700              : 
     701              : 
     702              :       end if ! n_above
     703              : 
     704        11417 :       end subroutine get_fraclengths
     705              : 
     706              : !=======================================================================
     707              : 
     708              :       end module icepack_wavefracspec
     709              : 
     710              : !=======================================================================
     711              : 
     712              : 
        

Generated by: LCOV version 2.0-1