MOM6
MOM_surface_forcing_gfdl.F90
1 module mom_surface_forcing_gfdl
2 
3 ! This file is part of MOM6. See LICENSE.md for the license.
4 
5 !#CTRL# use MOM_controlled_forcing, only : apply_ctrl_forcing, register_ctrl_forcing_restarts
6 !#CTRL# use MOM_controlled_forcing, only : controlled_forcing_init, controlled_forcing_end
7 !#CTRL# use MOM_controlled_forcing, only : ctrl_forcing_CS
8 use mom_coms, only : reproducing_sum
9 use mom_constants, only : hlv, hlf
10 use mom_cpu_clock, only : cpu_clock_id, cpu_clock_begin, cpu_clock_end
11 use mom_cpu_clock, only : clock_subcomponent
12 use mom_diag_mediator, only : diag_ctrl, safe_alloc_ptr, time_type
14 use mom_domains, only : agrid, bgrid_ne, cgrid_ne, to_all
15 use mom_domains, only : to_north, to_east, omit_corners
16 use mom_error_handler, only : mom_error, warning, fatal, is_root_pe, mom_mesg
19 use mom_forcing_type, only : forcing_diags, mech_forcing_diags, register_forcing_type_diags
20 use mom_forcing_type, only : allocate_forcing_type, deallocate_forcing_type
21 use mom_forcing_type, only : allocate_mech_forcing, deallocate_mech_forcing
22 use mom_get_input, only : get_mom_input, directories
23 use mom_grid, only : ocean_grid_type
24 use mom_io, only : slasher, write_version_number, mom_read_data
25 use mom_restart, only : register_restart_field, restart_init, mom_restart_cs
26 use mom_restart, only : restart_init_end, save_restart, restore_state
27 use mom_string_functions, only : uppercase
28 use mom_spatial_means, only : adjust_area_mean_to_zero
30 use mom_variables, only : surface
31 use user_revise_forcing, only : user_alter_forcing, user_revise_forcing_init
33 
34 use coupler_types_mod, only : coupler_2d_bc_type, coupler_type_write_chksums
35 use coupler_types_mod, only : coupler_type_initialized, coupler_type_spawn
36 use coupler_types_mod, only : coupler_type_copy_data
37 use data_override_mod, only : data_override_init, data_override
38 use fms_mod, only : stdout
39 use mpp_mod, only : mpp_chksum
40 use time_interp_external_mod, only : init_external_field, time_interp_external
41 use time_interp_external_mod, only : time_interp_external_init
42 
43 implicit none ; private
44 
45 #include <MOM_memory.h>
46 
47 public convert_iob_to_fluxes, convert_iob_to_forces
48 public surface_forcing_init
49 public ice_ocn_bnd_type_chksum
50 public forcing_save_restart
51 
52 ! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional
53 ! consistency testing. These are noted in comments with units like Z, H, L, and T, along with
54 ! their mks counterparts with notation like "a velocity [Z T-1 ~> m s-1]". If the units
55 ! vary with the Boussinesq approximation, the Boussinesq variant is given first.
56 
57 !> surface_forcing_CS is a structure containing pointers to the forcing fields
58 !! which may be used to drive MOM. All fluxes are positive downward.
59 type, public :: surface_forcing_cs ; private
60  integer :: wind_stagger !< AGRID, BGRID_NE, or CGRID_NE (integer values
61  !! from MOM_domains) to indicate the staggering of
62  !! the winds that are being provided in calls to
63  !! update_ocean_model.
64  logical :: use_temperature !< If true, temp and saln used as state variables
65  real :: wind_stress_multiplier !< A multiplier applied to incoming wind stress [nondim].
66 
67  real :: rho0 !< Boussinesq reference density [R ~> kg m-3]
68  real :: area_surf = -1.0 !< Total ocean surface area [m2]
69  real :: latent_heat_fusion !< Latent heat of fusion [J kg-1]
70  real :: latent_heat_vapor !< Latent heat of vaporization [J kg-1]
71 
72  real :: max_p_surf !< The maximum surface pressure that can be exerted by
73  !! the atmosphere and floating sea-ice [R L2 T-2 ~> Pa].
74  !! This is needed because the FMS coupling structure
75  !! does not limit the water that can be frozen out
76  !! of the ocean and the ice-ocean heat fluxes are
77  !! treated explicitly.
78  logical :: use_limited_p_ssh !< If true, return the sea surface height with
79  !! the correction for the atmospheric (and sea-ice)
80  !! pressure limited by max_p_surf instead of the
81  !! full atmospheric pressure. The default is true.
82  logical :: approx_net_mass_src !< If true, use the net mass sources from the ice-ocean boundary
83  !! type without any further adjustments to drive the ocean dynamics.
84  !! The actual net mass source may differ due to corrections.
85 
86  real :: gust_const !< Constant unresolved background gustiness for ustar [R L Z T-1 ~> Pa]
87  logical :: read_gust_2d !< If true, use a 2-dimensional gustiness supplied from an input file.
88  real, pointer, dimension(:,:) :: &
89  tke_tidal => null() !< Turbulent kinetic energy introduced to the bottom boundary layer
90  !! by drag on the tidal flows [R Z3 T-3 ~> W m-2].
91  real, pointer, dimension(:,:) :: &
92  gust => null() !< A spatially varying unresolved background gustiness that
93  !! contributes to ustar [R L Z T-1 ~> Pa]. gust is used when read_gust_2d is true.
94  real, pointer, dimension(:,:) :: &
95  ustar_tidal => null() !< Tidal contribution to the bottom friction velocity [Z T-1 ~> m s-1]
96  real :: cd_tides !< Drag coefficient that applies to the tides (nondimensional)
97  real :: utide !< Constant tidal velocity to use if read_tideamp is false [Z T-1 ~> m s-1].
98  logical :: read_tideamp !< If true, spatially varying tidal amplitude read from a file.
99 
100  logical :: rigid_sea_ice !< If true, sea-ice exerts a rigidity that acts to damp surface
101  !! deflections (especially surface gravity waves). The default is false.
102  real :: g_earth !< Gravitational acceleration [L2 Z-1 T-2 ~> m s-2]
103  real :: kv_sea_ice !< Viscosity in sea-ice that resists sheared vertical motions [L4 Z-2 T-1 ~> m2 s-1]
104  real :: density_sea_ice !< Typical density of sea-ice [R ~> kg m-3]. The value is only used to convert
105  !! the ice pressure into appropriate units for use with Kv_sea_ice.
106  real :: rigid_sea_ice_mass !< A mass per unit area of sea-ice beyond which sea-ice viscosity
107  !! becomes effective [R Z ~> kg m-2], typically of order 1000 kg m-2.
108  logical :: allow_flux_adjustments !< If true, use data_override to obtain flux adjustments
109 
110  logical :: restore_salt !< If true, the coupled MOM driver adds a term to restore surface
111  !! salinity to a specified value.
112  logical :: restore_temp !< If true, the coupled MOM driver adds a term to restore sea
113  !! surface temperature to a specified value.
114  real :: flux_const !< Piston velocity for surface restoring [Z T-1 ~> m s-1]
115  logical :: salt_restore_as_sflux !< If true, SSS restore as salt flux instead of water flux
116  logical :: adjust_net_srestore_to_zero !< Adjust srestore to zero (for both salt_flux or vprec)
117  logical :: adjust_net_srestore_by_scaling !< Adjust srestore w/o moving zero contour
118  logical :: adjust_net_fresh_water_to_zero !< Adjust net surface fresh-water (with restoring) to zero
119  logical :: use_net_fw_adjustment_sign_bug !< Use the wrong sign when adjusting net FW
120  logical :: adjust_net_fresh_water_by_scaling !< Adjust net surface fresh-water w/o moving zero contour
121  logical :: mask_srestore_under_ice !< If true, use an ice mask defined by frazil criteria
122  !! for salinity restoring.
123  real :: ice_salt_concentration !< Salt concentration for sea ice [kg/kg]
124  logical :: mask_srestore_marginal_seas !< If true, then mask SSS restoring in marginal seas
125  real :: max_delta_srestore !< Maximum delta salinity used for restoring
126  real :: max_delta_trestore !< Maximum delta sst used for restoring
127  real, pointer, dimension(:,:) :: basin_mask => null() !< Mask for surface salinity restoring by basin
128  logical :: answers_2018 !< If true, use the order of arithmetic and expressions that recover
129  !! the answers from the end of 2018. Otherwise, use a simpler
130  !! expression to calculate gustiness.
131  logical :: fix_ustar_gustless_bug !< If true correct a bug in the time-averaging of the
132  !! gustless wind friction velocity.
133  logical :: check_no_land_fluxes !< Return warning if IOB flux over land is non-zero
134 
135  type(diag_ctrl), pointer :: diag => null() !< Structure to regulate diagnostic output timing
136  character(len=200) :: inputdir !< Directory where NetCDF input files are
137  character(len=200) :: salt_restore_file !< Filename for salt restoring data
138  character(len=30) :: salt_restore_var_name !< Name of surface salinity in salt_restore_file
139  logical :: mask_srestore !< If true, apply a 2-dimensional mask to the surface
140  !! salinity restoring fluxes. The masking file should be
141  !! in inputdir/salt_restore_mask.nc and the field should
142  !! be named 'mask'
143  real, pointer, dimension(:,:) :: srestore_mask => null() !< mask for SSS restoring
144  character(len=200) :: temp_restore_file !< Filename for sst restoring data
145  character(len=30) :: temp_restore_var_name !< Name of surface temperature in temp_restore_file
146  logical :: mask_trestore !< If true, apply a 2-dimensional mask to the surface
147  !! temperature restoring fluxes. The masking file should be
148  !! in inputdir/temp_restore_mask.nc and the field should
149  !! be named 'mask'
150  real, pointer, dimension(:,:) :: trestore_mask => null() !< Mask for SST restoring
151  integer :: id_srestore = -1 !< An id number for time_interp_external.
152  integer :: id_trestore = -1 !< An id number for time_interp_external.
153 
154  type(forcing_diags), public :: handles !< Diagnostics handles
155 
156 !#CTRL# type(ctrl_forcing_CS), pointer :: ctrl_forcing_CSp => NULL()
157  type(mom_restart_cs), pointer :: restart_csp => null() !< A pointer to the restart control structure
158  type(user_revise_forcing_cs), pointer :: urf_cs => null() !< A control structure for user forcing revisions
159 end type surface_forcing_cs
160 
161 
162 !> ice_ocean_boundary_type is a structure corresponding to forcing, but with the elements, units,
163 !! and conventions that exactly conform to the use for MOM6-based coupled models.
164 type, public :: ice_ocean_boundary_type
165  real, pointer, dimension(:,:) :: u_flux =>null() !< i-direction wind stress [Pa]
166  real, pointer, dimension(:,:) :: v_flux =>null() !< j-direction wind stress [Pa]
167  real, pointer, dimension(:,:) :: t_flux =>null() !< sensible heat flux [W m-2]
168  real, pointer, dimension(:,:) :: q_flux =>null() !< specific humidity flux [kg m-2 s-1]
169  real, pointer, dimension(:,:) :: salt_flux =>null() !< salt flux [kg m-2 s-1]
170  real, pointer, dimension(:,:) :: lw_flux =>null() !< long wave radiation [W m-2]
171  real, pointer, dimension(:,:) :: sw_flux_vis_dir =>null() !< direct visible sw radiation [W m-2]
172  real, pointer, dimension(:,:) :: sw_flux_vis_dif =>null() !< diffuse visible sw radiation [W m-2]
173  real, pointer, dimension(:,:) :: sw_flux_nir_dir =>null() !< direct Near InfraRed sw radiation [W m-2]
174  real, pointer, dimension(:,:) :: sw_flux_nir_dif =>null() !< diffuse Near InfraRed sw radiation [W m-2]
175  real, pointer, dimension(:,:) :: lprec =>null() !< mass flux of liquid precip [kg m-2 s-1]
176  real, pointer, dimension(:,:) :: fprec =>null() !< mass flux of frozen precip [kg m-2 s-1]
177  real, pointer, dimension(:,:) :: runoff =>null() !< mass flux of liquid runoff [kg m-2 s-1]
178  real, pointer, dimension(:,:) :: calving =>null() !< mass flux of frozen runoff [kg m-2 s-1]
179  real, pointer, dimension(:,:) :: stress_mag =>null() !< The time-mean magnitude of the stress on the ocean [Pa]
180  real, pointer, dimension(:,:) :: ustar_berg =>null() !< frictional velocity beneath icebergs [m s-1]
181  real, pointer, dimension(:,:) :: area_berg =>null() !< fractional area covered by icebergs [m2 m-2]
182  real, pointer, dimension(:,:) :: mass_berg =>null() !< mass of icebergs per unit ocean area [kg m-2]
183  real, pointer, dimension(:,:) :: runoff_hflx =>null() !< heat content of liquid runoff [W m-2]
184  real, pointer, dimension(:,:) :: calving_hflx =>null() !< heat content of frozen runoff [W m-2]
185  real, pointer, dimension(:,:) :: p =>null() !< pressure of overlying ice and atmosphere
186  !< on ocean surface [Pa]
187  real, pointer, dimension(:,:) :: mi =>null() !< mass of ice per unit ocean area [kg m-2]
188  real, pointer, dimension(:,:) :: ice_rigidity =>null() !< rigidity of the sea ice, sea-ice and
189  !! ice-shelves, expressed as a coefficient
190  !! for divergence damping, as determined
191  !! outside of the ocean model [m3 s-1]
192  integer :: xtype !< The type of the exchange - REGRID, REDIST or DIRECT
193  type(coupler_2d_bc_type) :: fluxes !< A structure that may contain an array of named fields
194  !! used for passive tracer fluxes.
195  integer :: wind_stagger = -999 !< A flag indicating the spatial discretization of wind stresses.
196  !! This flag may be set by the flux-exchange code, based on what
197  !! the sea-ice model is providing. Otherwise, the value from
198  !! the surface_forcing_CS is used.
200 
201 integer :: id_clock_forcing !< A CPU time clock
202 
203 contains
204 
205 !> This subroutine translates the Ice_ocean_boundary_type into a MOM
206 !! thermodynamic forcing type, including changes of units, sign conventions,
207 !! and putting the fields into arrays with MOM-standard halos.
208 subroutine convert_iob_to_fluxes(IOB, fluxes, index_bounds, Time, valid_time, G, US, CS, sfc_state)
209  type(ice_ocean_boundary_type), &
210  target, intent(in) :: iob !< An ice-ocean boundary type with fluxes to drive
211  !! the ocean in a coupled model
212  type(forcing), intent(inout) :: fluxes !< A structure containing pointers to all
213  !! possible mass, heat or salt flux forcing fields.
214  !! Unused fields have NULL ptrs.
215  integer, dimension(4), intent(in) :: index_bounds !< The i- and j- size of the arrays in IOB.
216  type(time_type), intent(in) :: time !< The time of the fluxes, used for interpolating the
217  !! salinity to the right time, when it is being restored.
218  real, intent(in) :: valid_time !< The amount of time over which these fluxes
219  !! should be applied [s].
220  type(ocean_grid_type), intent(inout) :: g !< The ocean's grid structure
221  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
222  type(surface_forcing_cs),pointer :: cs !< A pointer to the control structure returned by a
223  !! previous call to surface_forcing_init.
224  type(surface), intent(in) :: sfc_state !< A structure containing fields that describe the
225  !! surface state of the ocean.
226 
227  real, dimension(SZI_(G),SZJ_(G)) :: &
228  data_restore, & ! The surface value toward which to restore [ppt] or [degC]
229  sst_anom, & ! Instantaneous sea surface temperature anomalies from a target value [degC]
230  sss_anom, & ! Instantaneous sea surface salinity anomalies from a target value [ppt]
231  sss_mean, & ! A (mean?) salinity about which to normalize local salinity
232  ! anomalies when calculating restorative precipitation anomalies [ppt]
233  pme_adj, & ! The adjustment to PminusE that will cause the salinity
234  ! to be restored toward its target value [kg m-1 s-1]
235  net_fw, & ! The area integrated net freshwater flux into the ocean [kg s-1]
236  net_fw2, & ! The net freshwater flux into the ocean [kg m-2 s-1]
237  work_sum, & ! A 2-d array that is used as the work space for global sums [m2] or [kg s-1]
238  open_ocn_mask ! a binary field indicating where ice is present based on frazil criteria [nondim]
239 
240  integer :: i, j, is, ie, js, je, isq, ieq, jsq, jeq, i0, j0
241  integer :: isd, ied, jsd, jed, isdb, iedb, jsdb, jedb, isr, ier, jsr, jer
242  integer :: isc_bnd, iec_bnd, jsc_bnd, jec_bnd
243 
244  real :: delta_sss ! temporary storage for sss diff from restoring value [ppt]
245  real :: delta_sst ! temporary storage for sst diff from restoring value [degC]
246 
247  real :: kg_m2_s_conversion ! A combination of unit conversion factors for rescaling
248  ! mass fluxes [R Z s m2 kg-1 T-1 ~> 1].
249  real :: rhoxcp ! Reference density times heat capacity times unit scaling
250  ! factors [Q R degC-1 ~> J m-3 degC-1]
251  real :: sign_for_net_fw_bug ! Should be +1. but an old bug can be recovered by using -1.
252 
253  call cpu_clock_begin(id_clock_forcing)
254 
255  isc_bnd = index_bounds(1) ; iec_bnd = index_bounds(2)
256  jsc_bnd = index_bounds(3) ; jec_bnd = index_bounds(4)
257  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
258  isq = g%IscB ; ieq = g%IecB ; jsq = g%JscB ; jeq = g%JecB
259  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed
260  isdb = g%IsdB ; iedb = g%IedB ; jsdb = g%JsdB ; jedb = g%JedB
261  isr = is-isd+1 ; ier = ie-isd+1 ; jsr = js-jsd+1 ; jer = je-jsd+1
262 
263  kg_m2_s_conversion = us%kg_m2s_to_RZ_T
264  if (cs%restore_temp) rhoxcp = cs%Rho0 * fluxes%C_p
265  open_ocn_mask(:,:) = 1.0
266  pme_adj(:,:) = 0.0
267  fluxes%vPrecGlobalAdj = 0.0
268  fluxes%vPrecGlobalScl = 0.0
269  fluxes%saltFluxGlobalAdj = 0.0
270  fluxes%saltFluxGlobalScl = 0.0
271  fluxes%netFWGlobalAdj = 0.0
272  fluxes%netFWGlobalScl = 0.0
273 
274  ! allocation and initialization if this is the first time that this
275  ! flux type has been used.
276  if (fluxes%dt_buoy_accum < 0) then
277  call allocate_forcing_type(g, fluxes, water=.true., heat=.true., ustar=.true., press=.true., &
278  fix_accum_bug=cs%fix_ustar_gustless_bug)
279 
280  call safe_alloc_ptr(fluxes%sw_vis_dir,isd,ied,jsd,jed)
281  call safe_alloc_ptr(fluxes%sw_vis_dif,isd,ied,jsd,jed)
282  call safe_alloc_ptr(fluxes%sw_nir_dir,isd,ied,jsd,jed)
283  call safe_alloc_ptr(fluxes%sw_nir_dif,isd,ied,jsd,jed)
284 
285  call safe_alloc_ptr(fluxes%p_surf,isd,ied,jsd,jed)
286  call safe_alloc_ptr(fluxes%p_surf_full,isd,ied,jsd,jed)
287  if (cs%use_limited_P_SSH) then
288  fluxes%p_surf_SSH => fluxes%p_surf
289  else
290  fluxes%p_surf_SSH => fluxes%p_surf_full
291  endif
292 
293  call safe_alloc_ptr(fluxes%salt_flux,isd,ied,jsd,jed)
294  call safe_alloc_ptr(fluxes%salt_flux_in,isd,ied,jsd,jed)
295  call safe_alloc_ptr(fluxes%salt_flux_added,isd,ied,jsd,jed)
296 
297  call safe_alloc_ptr(fluxes%TKE_tidal,isd,ied,jsd,jed)
298  call safe_alloc_ptr(fluxes%ustar_tidal,isd,ied,jsd,jed)
299 
300  if (cs%allow_flux_adjustments) then
301  call safe_alloc_ptr(fluxes%heat_added,isd,ied,jsd,jed)
302  call safe_alloc_ptr(fluxes%salt_flux_added,isd,ied,jsd,jed)
303  endif
304 
305  do j=js-2,je+2 ; do i=is-2,ie+2
306  fluxes%TKE_tidal(i,j) = cs%TKE_tidal(i,j)
307  fluxes%ustar_tidal(i,j) = cs%ustar_tidal(i,j)
308  enddo ; enddo
309 
310  if (cs%restore_temp) call safe_alloc_ptr(fluxes%heat_added,isd,ied,jsd,jed)
311 
312  endif ! endif for allocation and initialization
313 
314 
315  if (((associated(iob%ustar_berg) .and. (.not.associated(fluxes%ustar_berg))) &
316  .or. (associated(iob%area_berg) .and. (.not.associated(fluxes%area_berg)))) &
317  .or. (associated(iob%mass_berg) .and. (.not.associated(fluxes%mass_berg)))) &
318  call allocate_forcing_type(g, fluxes, iceberg=.true.)
319 
320  if ((.not.coupler_type_initialized(fluxes%tr_fluxes)) .and. &
321  coupler_type_initialized(iob%fluxes)) &
322  call coupler_type_spawn(iob%fluxes, fluxes%tr_fluxes, &
323  (/is,is,ie,ie/), (/js,js,je,je/))
324  ! It might prove valuable to use the same array extents as the rest of the
325  ! ocean model, rather than using haloless arrays, in which case the last line
326  ! would be: ( (/isd,is,ie,ied/), (/jsd,js,je,jed/))
327 
328  ! allocation and initialization on first call to this routine
329  if (cs%area_surf < 0.0) then
330  do j=js,je ; do i=is,ie
331  work_sum(i,j) = us%L_to_m**2*g%areaT(i,j) * g%mask2dT(i,j)
332  enddo ; enddo
333  cs%area_surf = reproducing_sum(work_sum, isr, ier, jsr, jer)
334  endif ! endif for allocation and initialization
335 
336 
337  ! Indicate that there are new unused fluxes.
338  fluxes%fluxes_used = .false.
339  fluxes%dt_buoy_accum = us%s_to_T*valid_time
340 
341  if (cs%allow_flux_adjustments) then
342  fluxes%heat_added(:,:) = 0.0
343  fluxes%salt_flux_added(:,:) = 0.0
344  endif
345 
346  do j=js,je ; do i=is,ie
347  fluxes%salt_flux(i,j) = 0.0
348  fluxes%vprec(i,j) = 0.0
349  enddo ; enddo
350 
351  ! Salinity restoring logic
352  if (cs%restore_salt) then
353  call time_interp_external(cs%id_srestore,time,data_restore)
354  ! open_ocn_mask indicates where to restore salinity (1 means restore, 0 does not)
355  open_ocn_mask(:,:) = 1.0
356  if (cs%mask_srestore_under_ice) then ! Do not restore under sea-ice
357  do j=js,je ; do i=is,ie
358  if (sfc_state%SST(i,j) <= -0.0539*sfc_state%SSS(i,j)) open_ocn_mask(i,j)=0.0
359  enddo ; enddo
360  endif
361  if (cs%salt_restore_as_sflux) then
362  do j=js,je ; do i=is,ie
363  delta_sss = data_restore(i,j)- sfc_state%SSS(i,j)
364  delta_sss = sign(1.0,delta_sss)*min(abs(delta_sss),cs%max_delta_srestore)
365  fluxes%salt_flux(i,j) = 1.e-3*g%mask2dT(i,j) * (cs%Rho0*cs%Flux_const)* &
366  (cs%basin_mask(i,j)*open_ocn_mask(i,j)*cs%srestore_mask(i,j)) *delta_sss ! R Z T-1 ~> kg Salt m-2 s-1
367  enddo ; enddo
368  if (cs%adjust_net_srestore_to_zero) then
369  if (cs%adjust_net_srestore_by_scaling) then
370  call adjust_area_mean_to_zero(fluxes%salt_flux, g, fluxes%saltFluxGlobalScl, &
371  unit_scale=us%RZ_T_to_kg_m2s)
372  fluxes%saltFluxGlobalAdj = 0.
373  else
374  work_sum(is:ie,js:je) = us%L_to_m**2*us%RZ_T_to_kg_m2s * &
375  g%areaT(is:ie,js:je)*fluxes%salt_flux(is:ie,js:je)
376  fluxes%saltFluxGlobalAdj = reproducing_sum(work_sum(:,:), isr,ier, jsr,jer)/cs%area_surf
377  fluxes%salt_flux(is:ie,js:je) = fluxes%salt_flux(is:ie,js:je) - kg_m2_s_conversion * fluxes%saltFluxGlobalAdj
378  endif
379  endif
380  fluxes%salt_flux_added(is:ie,js:je) = fluxes%salt_flux(is:ie,js:je) ! Diagnostic
381  else
382  do j=js,je ; do i=is,ie
383  if (g%mask2dT(i,j) > 0.5) then
384  delta_sss = sfc_state%SSS(i,j) - data_restore(i,j)
385  delta_sss = sign(1.0,delta_sss)*min(abs(delta_sss),cs%max_delta_srestore)
386  fluxes%vprec(i,j) = (cs%basin_mask(i,j)*open_ocn_mask(i,j)*cs%srestore_mask(i,j))* &
387  (cs%Rho0*cs%Flux_const) * &
388  delta_sss / (0.5*(sfc_state%SSS(i,j) + data_restore(i,j)))
389  endif
390  enddo ; enddo
391  if (cs%adjust_net_srestore_to_zero) then
392  if (cs%adjust_net_srestore_by_scaling) then
393  call adjust_area_mean_to_zero(fluxes%vprec, g, fluxes%vPrecGlobalScl, &
394  unit_scale=us%RZ_T_to_kg_m2s)
395  fluxes%vPrecGlobalAdj = 0.
396  else
397  work_sum(is:ie,js:je) = us%L_to_m**2*g%areaT(is:ie,js:je) * &
398  us%RZ_T_to_kg_m2s*fluxes%vprec(is:ie,js:je)
399  fluxes%vPrecGlobalAdj = reproducing_sum(work_sum(:,:), isr, ier, jsr, jer) / cs%area_surf
400  do j=js,je ; do i=is,ie
401  fluxes%vprec(i,j) = ( fluxes%vprec(i,j) - kg_m2_s_conversion*fluxes%vPrecGlobalAdj ) * g%mask2dT(i,j)
402  enddo ; enddo
403  endif
404  endif
405  endif
406  endif
407 
408  ! SST restoring logic
409  if (cs%restore_temp) then
410  call time_interp_external(cs%id_trestore,time,data_restore)
411  do j=js,je ; do i=is,ie
412  delta_sst = data_restore(i,j)- sfc_state%SST(i,j)
413  delta_sst = sign(1.0,delta_sst)*min(abs(delta_sst),cs%max_delta_trestore)
414  fluxes%heat_added(i,j) = g%mask2dT(i,j) * cs%trestore_mask(i,j) * &
415  rhoxcp * delta_sst * cs%Flux_const ! W m-2
416  enddo ; enddo
417  endif
418 
419 
420  ! obtain fluxes from IOB; note the staggering of indices
421  i0 = is - isc_bnd ; j0 = js - jsc_bnd
422  do j=js,je ; do i=is,ie
423 
424  if (associated(iob%lprec)) then
425  fluxes%lprec(i,j) = kg_m2_s_conversion * iob%lprec(i-i0,j-j0) * g%mask2dT(i,j)
426  if (cs%check_no_land_fluxes) &
427  call check_mask_val_consistency(iob%lprec(i-i0,j-j0), g%mask2dT(i,j), i, j, 'lprec', g)
428  endif
429 
430  if (associated(iob%fprec)) then
431  fluxes%fprec(i,j) = kg_m2_s_conversion * iob%fprec(i-i0,j-j0) * g%mask2dT(i,j)
432  if (cs%check_no_land_fluxes) &
433  call check_mask_val_consistency(iob%fprec(i-i0,j-j0), g%mask2dT(i,j), i, j, 'fprec', g)
434  endif
435 
436  if (associated(iob%q_flux)) then
437  fluxes%evap(i,j) = - kg_m2_s_conversion * iob%q_flux(i-i0,j-j0) * g%mask2dT(i,j)
438  if (cs%check_no_land_fluxes) &
439  call check_mask_val_consistency(iob%q_flux(i-i0,j-j0), g%mask2dT(i,j), i, j, 'q_flux', g)
440  endif
441 
442  if (associated(iob%runoff)) then
443  fluxes%lrunoff(i,j) = kg_m2_s_conversion * iob%runoff(i-i0,j-j0) * g%mask2dT(i,j)
444  if (cs%check_no_land_fluxes) &
445  call check_mask_val_consistency(iob%runoff(i-i0,j-j0), g%mask2dT(i,j), i, j, 'runoff', g)
446  endif
447 
448  if (associated(iob%calving)) then
449  fluxes%frunoff(i,j) = kg_m2_s_conversion * iob%calving(i-i0,j-j0) * g%mask2dT(i,j)
450  if (cs%check_no_land_fluxes) &
451  call check_mask_val_consistency(iob%calving(i-i0,j-j0), g%mask2dT(i,j), i, j, 'calving', g)
452  endif
453 
454  if (associated(iob%ustar_berg)) then
455  fluxes%ustar_berg(i,j) = us%m_to_Z*us%T_to_s * iob%ustar_berg(i-i0,j-j0) * g%mask2dT(i,j)
456  if (cs%check_no_land_fluxes) &
457  call check_mask_val_consistency(iob%ustar_berg(i-i0,j-j0), g%mask2dT(i,j), i, j, 'ustar_berg', g)
458  endif
459 
460  if (associated(iob%area_berg)) then
461  fluxes%area_berg(i,j) = iob%area_berg(i-i0,j-j0) * g%mask2dT(i,j)
462  if (cs%check_no_land_fluxes) &
463  call check_mask_val_consistency(iob%area_berg(i-i0,j-j0), g%mask2dT(i,j), i, j, 'area_berg', g)
464  endif
465 
466  if (associated(iob%mass_berg)) then
467  fluxes%mass_berg(i,j) = us%m_to_Z*us%kg_m3_to_R * iob%mass_berg(i-i0,j-j0) * g%mask2dT(i,j)
468  if (cs%check_no_land_fluxes) &
469  call check_mask_val_consistency(iob%mass_berg(i-i0,j-j0), g%mask2dT(i,j), i, j, 'mass_berg', g)
470  endif
471 
472  if (associated(iob%runoff_hflx)) then
473  fluxes%heat_content_lrunoff(i,j) = us%W_m2_to_QRZ_T * iob%runoff_hflx(i-i0,j-j0) * g%mask2dT(i,j)
474  if (cs%check_no_land_fluxes) &
475  call check_mask_val_consistency(iob%runoff_hflx(i-i0,j-j0), g%mask2dT(i,j), i, j, 'runoff_hflx', g)
476  endif
477 
478  if (associated(iob%calving_hflx)) then
479  fluxes%heat_content_frunoff(i,j) = us%W_m2_to_QRZ_T * iob%calving_hflx(i-i0,j-j0) * g%mask2dT(i,j)
480  if (cs%check_no_land_fluxes) &
481  call check_mask_val_consistency(iob%calving_hflx(i-i0,j-j0), g%mask2dT(i,j), i, j, 'calving_hflx', g)
482  endif
483 
484  if (associated(iob%lw_flux)) then
485  fluxes%LW(i,j) = us%W_m2_to_QRZ_T * iob%lw_flux(i-i0,j-j0) * g%mask2dT(i,j)
486  if (cs%check_no_land_fluxes) &
487  call check_mask_val_consistency(iob%lw_flux(i-i0,j-j0), g%mask2dT(i,j), i, j, 'lw_flux', g)
488  endif
489 
490  if (associated(iob%t_flux)) then
491  fluxes%sens(i,j) = -us%W_m2_to_QRZ_T* iob%t_flux(i-i0,j-j0) * g%mask2dT(i,j)
492  if (cs%check_no_land_fluxes) &
493  call check_mask_val_consistency(iob%t_flux(i-i0,j-j0), g%mask2dT(i,j), i, j, 't_flux', g)
494  endif
495 
496  fluxes%latent(i,j) = 0.0
497  if (associated(iob%fprec)) then
498  fluxes%latent(i,j) = fluxes%latent(i,j) - &
499  iob%fprec(i-i0,j-j0)*us%W_m2_to_QRZ_T*cs%latent_heat_fusion
500  fluxes%latent_fprec_diag(i,j) = -g%mask2dT(i,j) * iob%fprec(i-i0,j-j0)*us%W_m2_to_QRZ_T*cs%latent_heat_fusion
501  endif
502  if (associated(iob%calving)) then
503  fluxes%latent(i,j) = fluxes%latent(i,j) - &
504  iob%calving(i-i0,j-j0)*us%W_m2_to_QRZ_T*cs%latent_heat_fusion
505  fluxes%latent_frunoff_diag(i,j) = -g%mask2dT(i,j) * iob%calving(i-i0,j-j0)*us%W_m2_to_QRZ_T*cs%latent_heat_fusion
506  endif
507  if (associated(iob%q_flux)) then
508  fluxes%latent(i,j) = fluxes%latent(i,j) - &
509  iob%q_flux(i-i0,j-j0)*us%W_m2_to_QRZ_T*cs%latent_heat_vapor
510  fluxes%latent_evap_diag(i,j) = -g%mask2dT(i,j) * iob%q_flux(i-i0,j-j0)*us%W_m2_to_QRZ_T*cs%latent_heat_vapor
511  endif
512 
513  fluxes%latent(i,j) = g%mask2dT(i,j) * fluxes%latent(i,j)
514 
515  if (associated(iob%sw_flux_vis_dir)) then
516  fluxes%sw_vis_dir(i,j) = g%mask2dT(i,j) * us%W_m2_to_QRZ_T * iob%sw_flux_vis_dir(i-i0,j-j0)
517  if (cs%check_no_land_fluxes) &
518  call check_mask_val_consistency(iob%sw_flux_vis_dir(i-i0,j-j0), g%mask2dT(i,j), i, j, 'sw_flux_vis_dir', g)
519  endif
520  if (associated(iob%sw_flux_vis_dif)) then
521  fluxes%sw_vis_dif(i,j) = g%mask2dT(i,j) * us%W_m2_to_QRZ_T * iob%sw_flux_vis_dif(i-i0,j-j0)
522  if (cs%check_no_land_fluxes) &
523  call check_mask_val_consistency(iob%sw_flux_vis_dif(i-i0,j-j0), g%mask2dT(i,j), i, j, 'sw_flux_vis_dif', g)
524  endif
525  if (associated(iob%sw_flux_nir_dir)) then
526  fluxes%sw_nir_dir(i,j) = g%mask2dT(i,j) * us%W_m2_to_QRZ_T * iob%sw_flux_nir_dir(i-i0,j-j0)
527  if (cs%check_no_land_fluxes) &
528  call check_mask_val_consistency(iob%sw_flux_nir_dir(i-i0,j-j0), g%mask2dT(i,j), i, j, 'sw_flux_nir_dir', g)
529  endif
530  if (associated(iob%sw_flux_nir_dif)) then
531  fluxes%sw_nir_dif(i,j) = g%mask2dT(i,j) * us%W_m2_to_QRZ_T * iob%sw_flux_nir_dif(i-i0,j-j0)
532  if (cs%check_no_land_fluxes) &
533  call check_mask_val_consistency(iob%sw_flux_nir_dif(i-i0,j-j0), g%mask2dT(i,j), i, j, 'sw_flux_nir_dif', g)
534  endif
535  if (cs%answers_2018) then
536  fluxes%sw(i,j) = fluxes%sw_vis_dir(i,j) + fluxes%sw_vis_dif(i,j) + &
537  fluxes%sw_nir_dir(i,j) + fluxes%sw_nir_dif(i,j)
538  else
539  fluxes%sw(i,j) = (fluxes%sw_vis_dir(i,j) + fluxes%sw_vis_dif(i,j)) + &
540  (fluxes%sw_nir_dir(i,j) + fluxes%sw_nir_dif(i,j))
541  endif
542 
543  enddo ; enddo
544 
545  ! applied surface pressure from atmosphere and cryosphere
546  if (associated(iob%p)) then
547  if (cs%max_p_surf >= 0.0) then
548  do j=js,je ; do i=is,ie
549  fluxes%p_surf_full(i,j) = g%mask2dT(i,j) * us%kg_m3_to_R*us%m_s_to_L_T**2*iob%p(i-i0,j-j0)
550  fluxes%p_surf(i,j) = min(fluxes%p_surf_full(i,j),cs%max_p_surf)
551  if (cs%check_no_land_fluxes) &
552  call check_mask_val_consistency(iob%p(i-i0,j-j0), g%mask2dT(i,j), i, j, 'p', g)
553  enddo ; enddo
554  else
555  do j=js,je ; do i=is,ie
556  fluxes%p_surf_full(i,j) = g%mask2dT(i,j) * us%kg_m3_to_R*us%m_s_to_L_T**2*iob%p(i-i0,j-j0)
557  fluxes%p_surf(i,j) = fluxes%p_surf_full(i,j)
558  if (cs%check_no_land_fluxes) &
559  call check_mask_val_consistency(iob%p(i-i0,j-j0), g%mask2dT(i,j), i, j, 'p', g)
560  enddo ; enddo
561  endif
562  fluxes%accumulate_p_surf = .true. ! Multiple components may contribute to surface pressure.
563  endif
564 
565  ! more salt restoring logic
566  if (associated(iob%salt_flux)) then
567  do j=js,je ; do i=is,ie
568  fluxes%salt_flux(i,j) = g%mask2dT(i,j)*(fluxes%salt_flux(i,j) - kg_m2_s_conversion*iob%salt_flux(i-i0,j-j0))
569  fluxes%salt_flux_in(i,j) = g%mask2dT(i,j)*( -kg_m2_s_conversion*iob%salt_flux(i-i0,j-j0) )
570  if (cs%check_no_land_fluxes) &
571  call check_mask_val_consistency(iob%salt_flux(i-i0,j-j0), g%mask2dT(i,j), i, j, 'salt_flux', g)
572  enddo ; enddo
573  endif
574 
575 !#CTRL# if (associated(CS%ctrl_forcing_CSp)) then
576 !#CTRL# do j=js,je ; do i=is,ie
577 !#CTRL# SST_anom(i,j) = sfc_state%SST(i,j) - CS%T_Restore(i,j)
578 !#CTRL# SSS_anom(i,j) = sfc_state%SSS(i,j) - CS%S_Restore(i,j)
579 !#CTRL# SSS_mean(i,j) = 0.5*(sfc_state%SSS(i,j) + CS%S_Restore(i,j))
580 !#CTRL# enddo ; enddo
581 !#CTRL# call apply_ctrl_forcing(SST_anom, SSS_anom, SSS_mean, fluxes%heat_restore, &
582 !#CTRL# fluxes%vprec, day, dt, G, CS%ctrl_forcing_CSp)
583 !#CTRL# endif
584 
585  ! adjust the NET fresh-water flux to zero, if flagged
586  if (cs%adjust_net_fresh_water_to_zero) then
587  sign_for_net_fw_bug = 1.
588  if (cs%use_net_FW_adjustment_sign_bug) sign_for_net_fw_bug = -1.
589  do j=js,je ; do i=is,ie
590  net_fw(i,j) = us%RZ_T_to_kg_m2s* &
591  (((fluxes%lprec(i,j) + fluxes%fprec(i,j)) + &
592  (fluxes%lrunoff(i,j) + fluxes%frunoff(i,j))) + &
593  (fluxes%evap(i,j) + fluxes%vprec(i,j)) ) * us%L_to_m**2*g%areaT(i,j)
594  ! The following contribution appears to be calculating the volume flux of sea-ice
595  ! melt. This calculation is clearly WRONG if either sea-ice has variable
596  ! salinity or the sea-ice is completely fresh.
597  ! Bob thinks this is trying ensure the net fresh-water of the ocean + sea-ice system
598  ! is constant.
599  ! To do this correctly we will need a sea-ice melt field added to IOB. -AJA
600  if (associated(iob%salt_flux) .and. (cs%ice_salt_concentration>0.0)) &
601  net_fw(i,j) = net_fw(i,j) + sign_for_net_fw_bug * us%L_to_m**2*g%areaT(i,j) * &
602  (iob%salt_flux(i-i0,j-j0) / cs%ice_salt_concentration)
603  net_fw2(i,j) = net_fw(i,j) / (us%L_to_m**2*g%areaT(i,j))
604  enddo ; enddo
605 
606  if (cs%adjust_net_fresh_water_by_scaling) then
607  call adjust_area_mean_to_zero(net_fw2, g, fluxes%netFWGlobalScl)
608  do j=js,je ; do i=is,ie
609  fluxes%vprec(i,j) = fluxes%vprec(i,j) + us%kg_m2s_to_RZ_T * &
610  (net_fw2(i,j) - net_fw(i,j)/(us%L_to_m**2*g%areaT(i,j))) * g%mask2dT(i,j)
611  enddo ; enddo
612  else
613  fluxes%netFWGlobalAdj = reproducing_sum(net_fw(:,:), isr, ier, jsr, jer) / cs%area_surf
614  do j=js,je ; do i=is,ie
615  fluxes%vprec(i,j) = ( fluxes%vprec(i,j) - kg_m2_s_conversion * fluxes%netFWGlobalAdj ) * g%mask2dT(i,j)
616  enddo ; enddo
617  endif
618 
619  endif
620 
621  ! Set the wind stresses and ustar.
622  if (associated(fluxes%ustar) .and. associated(fluxes%ustar_gustless)) then
623  call extract_iob_stresses(iob, index_bounds, time, g, us, cs, ustar=fluxes%ustar, &
624  gustless_ustar=fluxes%ustar_gustless)
625  elseif (associated(fluxes%ustar)) then
626  call extract_iob_stresses(iob, index_bounds, time, g, us, cs, ustar=fluxes%ustar)
627  elseif (associated(fluxes%ustar_gustless)) then
628  call extract_iob_stresses(iob, index_bounds, time, g, us, cs, gustless_ustar=fluxes%ustar_gustless)
629  endif
630 
631  if (coupler_type_initialized(fluxes%tr_fluxes) .and. &
632  coupler_type_initialized(iob%fluxes)) &
633  call coupler_type_copy_data(iob%fluxes, fluxes%tr_fluxes)
634 
635  if (cs%allow_flux_adjustments) then
636  ! Apply adjustments to fluxes
637  call apply_flux_adjustments(g, us, cs, time, fluxes)
638  endif
639 
640  ! Allow for user-written code to alter fluxes after all the above
641  call user_alter_forcing(sfc_state, fluxes, time, g, cs%urf_CS)
642 
643  call cpu_clock_end(id_clock_forcing)
644 
645 end subroutine convert_iob_to_fluxes
646 
647 !> This subroutine translates the Ice_ocean_boundary_type into a MOM
648 !! mechanical forcing type, including changes of units, sign conventions,
649 !! and putting the fields into arrays with MOM-standard halos.
650 subroutine convert_iob_to_forces(IOB, forces, index_bounds, Time, G, US, CS, dt_forcing, reset_avg)
651  type(ice_ocean_boundary_type), &
652  target, intent(in) :: iob !< An ice-ocean boundary type with fluxes to drive
653  !! the ocean in a coupled model
654  type(mech_forcing), intent(inout) :: forces !< A structure with the driving mechanical forces
655  integer, dimension(4), intent(in) :: index_bounds !< The i- and j- size of the arrays in IOB.
656  type(time_type), intent(in) :: time !< The time of the fluxes, used for interpolating the
657  !! salinity to the right time, when it is being restored.
658  type(ocean_grid_type), intent(inout) :: g !< The ocean's grid structure
659  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
660  type(surface_forcing_cs),pointer :: cs !< A pointer to the control structure returned by a
661  !! previous call to surface_forcing_init.
662  real, optional, intent(in) :: dt_forcing !< A time interval over which to apply the
663  !! current value of ustar as a weighted running
664  !! average [s], or if 0 do not average ustar.
665  !! Missing is equivalent to 0.
666  logical, optional, intent(in) :: reset_avg !< If true, reset the time average.
667 
668  ! Local variables
669  real, dimension(SZI_(G),SZJ_(G)) :: &
670  rigidity_at_h, & ! Ice rigidity at tracer points [L4 Z-1 T-1 ~> m3 s-1]
671  net_mass_src, & ! A temporary of net mass sources [kg m-2 s-1].
672  ustar_tmp ! A temporary array of ustar values [Z T-1 ~> m s-1].
673 
674  real :: i_gearth ! The inverse of the gravitational acceleration [T2 Z L-2 ~> s2 m-1]
675  real :: kv_rho_ice ! (CS%Kv_sea_ice / CS%density_sea_ice) [L4 Z-2 T-1 R-1 ~> m5 s-1 kg-1]
676  real :: mass_ice ! mass of sea ice at a face [R Z ~> kg m-2]
677  real :: mass_eff ! effective mass of sea ice for rigidity [R Z ~> kg m-2]
678  real :: wt1, wt2 ! Relative weights of previous and current values of ustar, ND.
679 
680  integer :: i, j, is, ie, js, je, isq, ieq, jsq, jeq, i0, j0
681  integer :: isd, ied, jsd, jed, isdb, iedb, jsdb, jedb, isr, ier, jsr, jer
682  integer :: isc_bnd, iec_bnd, jsc_bnd, jec_bnd
683 
684  call cpu_clock_begin(id_clock_forcing)
685 
686  isc_bnd = index_bounds(1) ; iec_bnd = index_bounds(2)
687  jsc_bnd = index_bounds(3) ; jec_bnd = index_bounds(4)
688  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
689  isq = g%IscB ; ieq = g%IecB ; jsq = g%JscB ; jeq = g%JecB
690  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed
691  isdb = g%IsdB ; iedb = g%IedB ; jsdb = g%JsdB ; jedb = g%JedB
692  isr = is-isd+1 ; ier = ie-isd+1 ; jsr = js-jsd+1 ; jer = je-jsd+1
693  i0 = is - isc_bnd ; j0 = js - jsc_bnd
694 
695  ! allocation and initialization if this is the first time that this
696  ! mechanical forcing type has been used.
697  if (.not.forces%initialized) then
698  call allocate_mech_forcing(g, forces, stress=.true., ustar=.true., &
699  press=.true.)
700 
701  call safe_alloc_ptr(forces%p_surf,isd,ied,jsd,jed)
702  call safe_alloc_ptr(forces%p_surf_full,isd,ied,jsd,jed)
703  if (cs%use_limited_P_SSH) then
704  forces%p_surf_SSH => forces%p_surf
705  else
706  forces%p_surf_SSH => forces%p_surf_full
707  endif
708 
709  if (cs%rigid_sea_ice) then
710  call safe_alloc_ptr(forces%rigidity_ice_u,isdb,iedb,jsd,jed)
711  call safe_alloc_ptr(forces%rigidity_ice_v,isd,ied,jsdb,jedb)
712  endif
713 
714  forces%initialized = .true.
715  endif
716 
717  if ( (associated(iob%area_berg) .and. (.not. associated(forces%area_berg))) .or. &
718  (associated(iob%mass_berg) .and. (.not. associated(forces%mass_berg))) ) &
719  call allocate_mech_forcing(g, forces, iceberg=.true.)
720 
721  if (associated(iob%ice_rigidity)) then
722  rigidity_at_h(:,:) = 0.0
723  call safe_alloc_ptr(forces%rigidity_ice_u,isdb,iedb,jsd,jed)
724  call safe_alloc_ptr(forces%rigidity_ice_v,isd,ied,jsdb,jedb)
725  endif
726 
727  forces%accumulate_rigidity = .true. ! Multiple components may contribute to rigidity.
728  if (associated(forces%rigidity_ice_u)) forces%rigidity_ice_u(:,:) = 0.0
729  if (associated(forces%rigidity_ice_v)) forces%rigidity_ice_v(:,:) = 0.0
730 
731  ! Set the weights for forcing fields that use running time averages.
732  if (present(reset_avg)) then ; if (reset_avg) forces%dt_force_accum = 0.0 ; endif
733  wt1 = 0.0 ; wt2 = 1.0
734  if (present(dt_forcing)) then
735  if ((forces%dt_force_accum > 0.0) .and. (dt_forcing > 0.0)) then
736  wt1 = forces%dt_force_accum / (forces%dt_force_accum + dt_forcing)
737  wt2 = 1.0 - wt1
738  endif
739  if (dt_forcing > 0.0) then
740  forces%dt_force_accum = max(forces%dt_force_accum, 0.0) + dt_forcing
741  else
742  forces%dt_force_accum = 0.0 ! Reset the averaging time interval.
743  endif
744  else
745  forces%dt_force_accum = 0.0 ! Reset the averaging time interval.
746  endif
747 
748  ! applied surface pressure from atmosphere and cryosphere
749  if (associated(iob%p)) then
750  if (cs%max_p_surf >= 0.0) then
751  do j=js,je ; do i=is,ie
752  forces%p_surf_full(i,j) = g%mask2dT(i,j) * us%kg_m3_to_R*us%m_s_to_L_T**2*iob%p(i-i0,j-j0)
753  forces%p_surf(i,j) = min(forces%p_surf_full(i,j),cs%max_p_surf)
754  enddo ; enddo
755  else
756  do j=js,je ; do i=is,ie
757  forces%p_surf_full(i,j) = g%mask2dT(i,j) * us%kg_m3_to_R*us%m_s_to_L_T**2*iob%p(i-i0,j-j0)
758  forces%p_surf(i,j) = forces%p_surf_full(i,j)
759  enddo ; enddo
760  endif
761  else
762  do j=js,je ; do i=is,ie
763  forces%p_surf_full(i,j) = 0.0
764  forces%p_surf(i,j) = 0.0
765  enddo ; enddo
766  endif
767  forces%accumulate_p_surf = .true. ! Multiple components may contribute to surface pressure.
768 
769  ! Set the wind stresses and ustar.
770  if (wt1 <= 0.0) then
771  call extract_iob_stresses(iob, index_bounds, time, g, us, cs, taux=forces%taux, tauy=forces%tauy, &
772  ustar=forces%ustar, tau_halo=1)
773  else
774  call extract_iob_stresses(iob, index_bounds, time, g, us, cs, taux=forces%taux, tauy=forces%tauy, &
775  ustar=ustar_tmp, tau_halo=1)
776  do j=js,je ; do i=is,ie
777  forces%ustar(i,j) = wt1*forces%ustar(i,j) + wt2*ustar_tmp(i,j)
778  enddo ; enddo
779  endif
780 
781  ! Find the net mass source in the input forcing without other adjustments.
782  if (cs%approx_net_mass_src .and. associated(forces%net_mass_src)) then
783  net_mass_src(:,:) = 0.0
784  i0 = is - isc_bnd ; j0 = js - jsc_bnd
785  do j=js,je ; do i=is,ie ; if (g%mask2dT(i,j) > 0.0) then
786  if (associated(iob%lprec)) &
787  net_mass_src(i,j) = net_mass_src(i,j) + iob%lprec(i-i0,j-j0)
788  if (associated(iob%fprec)) &
789  net_mass_src(i,j) = net_mass_src(i,j) + iob%fprec(i-i0,j-j0)
790  if (associated(iob%runoff)) &
791  net_mass_src(i,j) = net_mass_src(i,j) + iob%runoff(i-i0,j-j0)
792  if (associated(iob%calving)) &
793  net_mass_src(i,j) = net_mass_src(i,j) + iob%calving(i-i0,j-j0)
794  if (associated(iob%q_flux)) &
795  net_mass_src(i,j) = net_mass_src(i,j) - iob%q_flux(i-i0,j-j0)
796  endif ; enddo ; enddo
797  if (wt1 <= 0.0) then
798  do j=js,je ; do i=is,ie
799  forces%net_mass_src(i,j) = wt2*net_mass_src(i,j)
800  enddo ; enddo
801  else
802  do j=js,je ; do i=is,ie
803  forces%net_mass_src(i,j) = wt1*forces%net_mass_src(i,j) + wt2*net_mass_src(i,j)
804  enddo ; enddo
805  endif
806  forces%net_mass_src_set = .true.
807  else
808  forces%net_mass_src_set = .false.
809  endif
810 
811  ! Obtain optional ice-berg related fluxes from the IOB type:
812  if (associated(iob%area_berg)) then ; do j=js,je ; do i=is,ie
813  forces%area_berg(i,j) = iob%area_berg(i-i0,j-j0) * g%mask2dT(i,j)
814  enddo ; enddo ; endif
815 
816  if (associated(iob%mass_berg)) then ; do j=js,je ; do i=is,ie
817  forces%mass_berg(i,j) = us%m_to_Z*us%kg_m3_to_R * iob%mass_berg(i-i0,j-j0) * g%mask2dT(i,j)
818  enddo ; enddo ; endif
819 
820  ! Obtain sea ice related dynamic fields
821  if (associated(iob%ice_rigidity)) then
822  do j=js,je ; do i=is,ie
823  rigidity_at_h(i,j) = us%m_to_L**3*us%Z_to_L*us%T_to_s * iob%ice_rigidity(i-i0,j-j0) * g%mask2dT(i,j)
824  enddo ; enddo
825  call pass_var(rigidity_at_h, g%Domain, halo=1)
826  do i=is-1,ie ; do j=js,je
827  forces%rigidity_ice_u(i,j) = forces%rigidity_ice_u(i,j) + &
828  min(rigidity_at_h(i,j), rigidity_at_h(i+1,j))
829  enddo ; enddo
830  do i=is,ie ; do j=js-1,je
831  forces%rigidity_ice_v(i,j) = forces%rigidity_ice_v(i,j) + &
832  min(rigidity_at_h(i,j), rigidity_at_h(i,j+1))
833  enddo ; enddo
834  endif
835 
836  if (cs%rigid_sea_ice) then
837  call pass_var(forces%p_surf_full, g%Domain, halo=1)
838  i_gearth = 1.0 / cs%g_Earth
839  kv_rho_ice = (cs%Kv_sea_ice / cs%density_sea_ice)
840  do i=is-1,ie ; do j=js,je
841  mass_ice = min(forces%p_surf_full(i,j), forces%p_surf_full(i+1,j)) * i_gearth
842  mass_eff = 0.0
843  if (mass_ice > cs%rigid_sea_ice_mass) then
844  mass_eff = (mass_ice - cs%rigid_sea_ice_mass)**2 / (mass_ice + cs%rigid_sea_ice_mass)
845  endif
846  forces%rigidity_ice_u(i,j) = forces%rigidity_ice_u(i,j) + kv_rho_ice * mass_eff
847  enddo ; enddo
848  do i=is,ie ; do j=js-1,je
849  mass_ice = min(forces%p_surf_full(i,j), forces%p_surf_full(i,j+1)) * i_gearth
850  mass_eff = 0.0
851  if (mass_ice > cs%rigid_sea_ice_mass) then
852  mass_eff = (mass_ice - cs%rigid_sea_ice_mass)**2 / (mass_ice + cs%rigid_sea_ice_mass)
853  endif
854  forces%rigidity_ice_v(i,j) = forces%rigidity_ice_v(i,j) + kv_rho_ice * mass_eff
855  enddo ; enddo
856  endif
857 
858  if (cs%allow_flux_adjustments) then
859  ! Apply adjustments to forces
860  call apply_force_adjustments(g, us, cs, time, forces)
861  endif
862 
863 !### ! Allow for user-written code to alter fluxes after all the above
864 !### call user_alter_mech_forcing(forces, Time, G, CS%urf_CS)
865 
866  call cpu_clock_end(id_clock_forcing)
867 end subroutine convert_iob_to_forces
868 
869 
870 !> This subroutine extracts the wind stresses and related fields like ustar from an
871 !! Ice_ocean_boundary_type into optional argument arrays, including changes of units, sign
872 !! conventions, and putting the fields into arrays with MOM-standard sized halos.
873 subroutine extract_iob_stresses(IOB, index_bounds, Time, G, US, CS, taux, tauy, ustar, &
874  gustless_ustar, tau_halo)
875  type(ice_ocean_boundary_type), &
876  target, intent(in) :: iob !< An ice-ocean boundary type with fluxes to drive
877  !! the ocean in a coupled model
878  integer, dimension(4), intent(in) :: index_bounds !< The i- and j- size of the arrays in IOB.
879  type(time_type), intent(in) :: time !< The time of the fluxes, used for interpolating the
880  !! salinity to the right time, when it is being restored.
881  type(ocean_grid_type), intent(inout) :: g !< The ocean's grid structure
882  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
883  type(surface_forcing_cs),pointer :: cs !< A pointer to the control structure returned by a
884  !! previous call to surface_forcing_init.
885  real, dimension(SZIB_(G),SZJ_(G)), &
886  optional, intent(inout) :: taux !< The zonal wind stresses on a C-grid [R Z L T-2 ~> Pa].
887  real, dimension(SZI_(G),SZJB_(G)), &
888  optional, intent(inout) :: tauy !< The meridional wind stresses on a C-grid [R Z L T-2 ~> Pa].
889  real, dimension(SZI_(G),SZJ_(G)), &
890  optional, intent(inout) :: ustar !< The surface friction velocity [Z T-1 ~> m s-1].
891  real, dimension(SZI_(G),SZJ_(G)), &
892  optional, intent(out) :: gustless_ustar !< The surface friction velocity without
893  !! any contributions from gustiness [Z T-1 ~> m s-1].
894  integer, optional, intent(in) :: tau_halo !< The halo size of wind stresses to set, 0 by default.
895 
896  ! Local variables
897  real, dimension(SZI_(G),SZJ_(G)) :: taux_in_a ! Zonal wind stresses [R Z L T-2 ~> Pa] at h points
898  real, dimension(SZI_(G),SZJ_(G)) :: tauy_in_a ! Meridional wind stresses [R Z L T-2 ~> Pa] at h points
899  real, dimension(SZIB_(G),SZJ_(G)) :: taux_in_c ! Zonal wind stresses [Pa] at u points
900  real, dimension(SZI_(G),SZJB_(G)) :: tauy_in_c ! Meridional wind stresses [R Z L T-2 ~> Pa] at v points
901  real, dimension(SZIB_(G),SZJB_(G)) :: taux_in_b ! Zonal wind stresses [Pa] at q points
902  real, dimension(SZIB_(G),SZJB_(G)) :: tauy_in_b ! Meridional wind stresses [R Z L T-2 ~> Pa] at q points
903 
904  real :: gustiness ! unresolved gustiness that contributes to ustar [R Z L T-2 ~> Pa]
905  real :: irho0 ! Inverse of the mean density rescaled to [Z L-1 R-1 ~> m3 kg-1]
906  real :: taux2, tauy2 ! squared wind stresses [R2 Z2 L2 T-4 ~> Pa2]
907  real :: tau_mag ! magnitude of the wind stress [R Z L T-2 ~> Pa]
908  real :: pa_conversion ! A unit conversion factor from Pa to the internal wind stress units [R Z L T-2 Pa-1 ~> 1]
909  real :: stress_conversion ! A unit conversion factor from Pa times any stress multiplier [R Z L T-2 Pa-1 ~> 1]
910 
911  logical :: do_ustar, do_gustless
912  integer :: wind_stagger ! AGRID, BGRID_NE, or CGRID_NE (integers from MOM_domains)
913  integer :: i, j, is, ie, js, je, ish, ieh, jsh, jeh, isqh, ieqh, jsqh, jeqh, i0, j0, halo
914 
915  halo = 0 ; if (present(tau_halo)) halo = tau_halo
916  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec
917  ish = g%isc-halo ; ieh = g%iec+halo ; jsh = g%jsc-halo ; jeh = g%jec+halo
918  isqh = g%IscB-halo ; ieqh = g%IecB+halo ; jsqh = g%JscB-halo ; jeqh = g%JecB+halo
919  i0 = is - index_bounds(1) ; j0 = js - index_bounds(3)
920 
921  irho0 = us%L_to_Z / cs%Rho0
922  pa_conversion = us%kg_m3_to_R*us%m_s_to_L_T**2*us%L_to_Z
923  stress_conversion = pa_conversion * cs%wind_stress_multiplier
924 
925  do_ustar = present(ustar) ; do_gustless = present(gustless_ustar)
926 
927  wind_stagger = cs%wind_stagger
928  if ((iob%wind_stagger == agrid) .or. (iob%wind_stagger == bgrid_ne) .or. &
929  (iob%wind_stagger == cgrid_ne)) wind_stagger = iob%wind_stagger
930 
931  if (associated(iob%u_flux).neqv.associated(iob%v_flux)) call mom_error(fatal,"extract_IOB_stresses: "//&
932  "associated(IOB%u_flux) /= associated(IOB%v_flux !!!")
933  if (present(taux).neqv.present(tauy)) call mom_error(fatal,"extract_IOB_stresses: "//&
934  "present(taux) /= present(tauy) !!!")
935 
936  ! Set surface momentum stress related fields as a function of staggering.
937  if (present(taux) .or. present(tauy) .or. &
938  ((do_ustar.or.do_gustless) .and. .not.associated(iob%stress_mag)) ) then
939 
940  if (wind_stagger == bgrid_ne) then
941  taux_in_b(:,:) = 0.0 ; tauy_in_b(:,:) = 0.0
942  if (associated(iob%u_flux).and.associated(iob%v_flux)) then
943  do j=js,je ; do i=is,ie
944  taux_in_b(i,j) = iob%u_flux(i-i0,j-j0) * stress_conversion
945  tauy_in_b(i,j) = iob%v_flux(i-i0,j-j0) * stress_conversion
946  enddo ; enddo
947  endif
948 
949  if (g%symmetric) call fill_symmetric_edges(taux_in_b, tauy_in_b, g%Domain, stagger=bgrid_ne)
950  call pass_vector(taux_in_b, tauy_in_b, g%Domain, stagger=bgrid_ne, halo=max(1,halo))
951 
952  if (present(taux).and.present(tauy)) then
953  do j=jsh,jeh ; do i=isqh,ieqh
954  taux(i,j) = 0.0
955  if ((g%mask2dBu(i,j) + g%mask2dBu(i,j-1)) > 0) &
956  taux(i,j) = (g%mask2dBu(i,j)*taux_in_b(i,j) + g%mask2dBu(i,j-1)*taux_in_b(i,j-1)) / &
957  (g%mask2dBu(i,j) + g%mask2dBu(i,j-1))
958  enddo ; enddo
959  do j=jsqh,jeqh ; do i=ish,ieh
960  tauy(i,j) = 0.0
961  if ((g%mask2dBu(i,j) + g%mask2dBu(i-1,j)) > 0) &
962  tauy(i,j) = (g%mask2dBu(i,j)*tauy_in_b(i,j) + g%mask2dBu(i-1,j)*tauy_in_b(i-1,j)) / &
963  (g%mask2dBu(i,j) + g%mask2dBu(i-1,j))
964  enddo ; enddo
965  endif
966  elseif (wind_stagger == agrid) then
967  taux_in_a(:,:) = 0.0 ; tauy_in_a(:,:) = 0.0
968  if (associated(iob%u_flux).and.associated(iob%v_flux)) then
969  do j=js,je ; do i=is,ie
970  taux_in_a(i,j) = iob%u_flux(i-i0,j-j0) * stress_conversion
971  tauy_in_a(i,j) = iob%v_flux(i-i0,j-j0) * stress_conversion
972  enddo ; enddo
973  endif
974 
975  if (halo == 0) then
976  call pass_vector(taux_in_a, tauy_in_a, g%Domain, to_all+omit_corners, stagger=agrid, halo=1)
977  else
978  call pass_vector(taux_in_a, tauy_in_a, g%Domain, stagger=agrid, halo=max(1,halo))
979  endif
980 
981  if (present(taux)) then ; do j=jsh,jeh ; do i=isqh,ieqh
982  taux(i,j) = 0.0
983  if ((g%mask2dT(i,j) + g%mask2dT(i+1,j)) > 0) &
984  taux(i,j) = (g%mask2dT(i,j)*taux_in_a(i,j) + g%mask2dT(i+1,j)*taux_in_a(i+1,j)) / &
985  (g%mask2dT(i,j) + g%mask2dT(i+1,j))
986  enddo ; enddo ; endif
987 
988  if (present(tauy)) then ; do j=jsqh,jeqh ; do i=ish,ieh
989  tauy(i,j) = 0.0
990  if ((g%mask2dT(i,j) + g%mask2dT(i,j+1)) > 0) &
991  tauy(i,j) = (g%mask2dT(i,j)*tauy_in_a(i,j) + g%mask2dT(i,j+1)*tauy_in_a(i,j+1)) / &
992  (g%mask2dT(i,j) + g%mask2dT(i,j+1))
993  enddo ; enddo ; endif
994 
995  else ! C-grid wind stresses.
996  taux_in_c(:,:) = 0.0 ; tauy_in_c(:,:) = 0.0
997  if (associated(iob%u_flux).and.associated(iob%v_flux)) then
998  do j=js,je ; do i=is,ie
999  taux_in_c(i,j) = iob%u_flux(i-i0,j-j0) * stress_conversion
1000  tauy_in_c(i,j) = iob%v_flux(i-i0,j-j0) * stress_conversion
1001  enddo ; enddo
1002  endif
1003 
1004  if (g%symmetric) call fill_symmetric_edges(taux_in_c, tauy_in_c, g%Domain)
1005  call pass_vector(taux_in_c, tauy_in_c, g%Domain, halo=max(1,halo))
1006 
1007  if (present(taux).and.present(tauy)) then
1008  do j=jsh,jeh ; do i=isqh,ieqh
1009  taux(i,j) = g%mask2dCu(i,j)*taux_in_c(i,j)
1010  enddo ; enddo
1011  do j=jsqh,jeqh ; do i=ish,ieh
1012  tauy(i,j) = g%mask2dCv(i,j)*tauy_in_c(i,j)
1013  enddo ; enddo
1014  endif
1015  endif ! endif for extracting wind stress fields with various staggerings
1016  endif
1017 
1018  if (do_ustar .or. do_gustless) then
1019  ! Set surface friction velocity directly or as a function of staggering.
1020  ! ustar is required for the bulk mixed layer formulation and other turbulent mixing
1021  ! parametizations. The background gustiness (for example with a relatively small value
1022  ! of 0.02 Pa) is intended to give reasonable behavior in regions of very weak winds.
1023  if (associated(iob%stress_mag)) then
1024  if (do_ustar) then ; do j=js,je ; do i=is,ie
1025  gustiness = cs%gust_const
1026  if (cs%read_gust_2d) then
1027  if ((wind_stagger == cgrid_ne) .or. &
1028  ((wind_stagger == agrid) .and. (g%mask2dT(i,j) > 0)) .or. &
1029  ((wind_stagger == bgrid_ne) .and. &
1030  (((g%mask2dBu(i,j) + g%mask2dBu(i-1,j-1)) + &
1031  (g%mask2dBu(i,j-1) + g%mask2dBu(i-1,j))) > 0)) ) &
1032  gustiness = cs%gust(i,j)
1033  endif
1034  ustar(i,j) = sqrt(gustiness*irho0 + irho0*pa_conversion*iob%stress_mag(i-i0,j-j0))
1035  enddo ; enddo ; endif
1036  if (cs%answers_2018) then
1037  if (do_gustless) then ; do j=js,je ; do i=is,ie
1038  gustless_ustar(i,j) = sqrt(pa_conversion*us%L_to_Z*iob%stress_mag(i-i0,j-j0) / cs%Rho0)
1039  enddo ; enddo ; endif
1040  else
1041  if (do_gustless) then ; do j=js,je ; do i=is,ie
1042  gustless_ustar(i,j) = sqrt(irho0 * pa_conversion*iob%stress_mag(i-i0,j-j0))
1043  enddo ; enddo ; endif
1044  endif
1045  elseif (wind_stagger == bgrid_ne) then
1046  do j=js,je ; do i=is,ie
1047  tau_mag = 0.0 ; gustiness = cs%gust_const
1048  if (((g%mask2dBu(i,j) + g%mask2dBu(i-1,j-1)) + &
1049  (g%mask2dBu(i,j-1) + g%mask2dBu(i-1,j))) > 0) then
1050  tau_mag = sqrt(((g%mask2dBu(i,j)*(taux_in_b(i,j)**2 + tauy_in_b(i,j)**2) + &
1051  g%mask2dBu(i-1,j-1)*(taux_in_b(i-1,j-1)**2 + tauy_in_b(i-1,j-1)**2)) + &
1052  (g%mask2dBu(i,j-1)*(taux_in_b(i,j-1)**2 + tauy_in_b(i,j-1)**2) + &
1053  g%mask2dBu(i-1,j)*(taux_in_b(i-1,j)**2 + tauy_in_b(i-1,j)**2)) ) / &
1054  ((g%mask2dBu(i,j) + g%mask2dBu(i-1,j-1)) + (g%mask2dBu(i,j-1) + g%mask2dBu(i-1,j))) )
1055  if (cs%read_gust_2d) gustiness = cs%gust(i,j)
1056  endif
1057  if (do_ustar) ustar(i,j) = sqrt(gustiness*irho0 + irho0 * tau_mag)
1058  if (cs%answers_2018) then
1059  if (do_gustless) gustless_ustar(i,j) = sqrt(us%L_to_Z*tau_mag / cs%Rho0)
1060  else
1061  if (do_gustless) gustless_ustar(i,j) = sqrt(irho0 * tau_mag)
1062  endif
1063  enddo ; enddo
1064  elseif (wind_stagger == agrid) then
1065  do j=js,je ; do i=is,ie
1066  tau_mag = g%mask2dT(i,j) * sqrt(taux_in_a(i,j)**2 + tauy_in_a(i,j)**2)
1067  gustiness = cs%gust_const
1068  if (cs%read_gust_2d .and. (g%mask2dT(i,j) > 0)) gustiness = cs%gust(i,j)
1069  if (do_ustar) ustar(i,j) = sqrt(gustiness*irho0 + irho0 * tau_mag)
1070  if (cs%answers_2018) then
1071  if (do_gustless) gustless_ustar(i,j) = sqrt(us%L_to_Z*tau_mag / cs%Rho0)
1072  else
1073  if (do_gustless) gustless_ustar(i,j) = sqrt(irho0 * tau_mag)
1074  endif
1075  enddo ; enddo
1076  else ! C-grid wind stresses.
1077  do j=js,je ; do i=is,ie
1078  taux2 = 0.0 ; tauy2 = 0.0
1079  if ((g%mask2dCu(i-1,j) + g%mask2dCu(i,j)) > 0) &
1080  taux2 = (g%mask2dCu(i-1,j)*taux_in_c(i-1,j)**2 + g%mask2dCu(i,j)*taux_in_c(i,j)**2) / &
1081  (g%mask2dCu(i-1,j) + g%mask2dCu(i,j))
1082  if ((g%mask2dCv(i,j-1) + g%mask2dCv(i,j)) > 0) &
1083  tauy2 = (g%mask2dCv(i,j-1)*tauy_in_c(i,j-1)**2 + g%mask2dCv(i,j)*tauy_in_c(i,j)**2) / &
1084  (g%mask2dCv(i,j-1) + g%mask2dCv(i,j))
1085  tau_mag = sqrt(taux2 + tauy2)
1086 
1087  gustiness = cs%gust_const
1088  if (cs%read_gust_2d) gustiness = cs%gust(i,j)
1089 
1090  if (do_ustar) ustar(i,j) = sqrt(gustiness*irho0 + irho0 * tau_mag)
1091  if (cs%answers_2018) then
1092  if (do_gustless) gustless_ustar(i,j) = sqrt(us%L_to_Z*tau_mag / cs%Rho0)
1093  else
1094  if (do_gustless) gustless_ustar(i,j) = sqrt(irho0 * tau_mag)
1095  endif
1096  enddo ; enddo
1097  endif ! endif for wind friction velocity fields
1098  endif
1099 
1100 end subroutine extract_iob_stresses
1101 
1102 
1103 !> Adds thermodynamic flux adjustments obtained via data_override
1104 !! Component name is 'OCN'
1105 !! Available adjustments are:
1106 !! - hflx_adj (Heat flux into the ocean [W m-2])
1107 !! - sflx_adj (Salt flux into the ocean [kg salt m-2 s-1])
1108 !! - prcme_adj (Fresh water flux into the ocean [kg m-2 s-1])
1109 subroutine apply_flux_adjustments(G, US, CS, Time, fluxes)
1110  type(ocean_grid_type), intent(inout) :: g !< Ocean grid structure
1111  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
1112  type(surface_forcing_cs), pointer :: cs !< Surface forcing control structure
1113  type(time_type), intent(in) :: time !< Model time structure
1114  type(forcing), intent(inout) :: fluxes !< Surface fluxes structure
1115 
1116  ! Local variables
1117  real, dimension(SZI_(G),SZJ_(G)) :: temp_at_h ! Various fluxes at h points [W m-2] or [kg m-2 s-1]
1118 
1119  integer :: isc, iec, jsc, jec, i, j
1120  logical :: overrode_h
1121 
1122  isc = g%isc; iec = g%iec ; jsc = g%jsc; jec = g%jec
1123 
1124  overrode_h = .false.
1125  call data_override('OCN', 'hflx_adj', temp_at_h(isc:iec,jsc:jec), time, override=overrode_h)
1126 
1127  if (overrode_h) then ; do j=jsc,jec ; do i=isc,iec
1128  fluxes%heat_added(i,j) = fluxes%heat_added(i,j) + us%W_m2_to_QRZ_T*temp_at_h(i,j)* g%mask2dT(i,j)
1129  enddo ; enddo ; endif
1130  ! Not needed? ! if (overrode_h) call pass_var(fluxes%heat_added, G%Domain)
1131 
1132  overrode_h = .false.
1133  call data_override('OCN', 'sflx_adj', temp_at_h(isc:iec,jsc:jec), time, override=overrode_h)
1134 
1135  if (overrode_h) then ; do j=jsc,jec ; do i=isc,iec
1136  fluxes%salt_flux_added(i,j) = fluxes%salt_flux_added(i,j) + &
1137  us%kg_m2s_to_RZ_T * temp_at_h(i,j)* g%mask2dT(i,j)
1138  enddo ; enddo ; endif
1139  ! Not needed? ! if (overrode_h) call pass_var(fluxes%salt_flux_added, G%Domain)
1140 
1141  overrode_h = .false.
1142  call data_override('OCN', 'prcme_adj', temp_at_h(isc:iec,jsc:jec), time, override=overrode_h)
1143 
1144  if (overrode_h) then ; do j=jsc,jec ; do i=isc,iec
1145  fluxes%vprec(i,j) = fluxes%vprec(i,j) + us%kg_m2s_to_RZ_T * temp_at_h(i,j)* g%mask2dT(i,j)
1146  enddo ; enddo ; endif
1147  ! Not needed? ! if (overrode_h) call pass_var(fluxes%vprec, G%Domain)
1148 end subroutine apply_flux_adjustments
1149 
1150 !> Adds mechanical forcing adjustments obtained via data_override
1151 !! Component name is 'OCN'
1152 !! Available adjustments are:
1153 !! - taux_adj (Zonal wind stress delta, positive to the east [Pa])
1154 !! - tauy_adj (Meridional wind stress delta, positive to the north [Pa])
1155 subroutine apply_force_adjustments(G, US, CS, Time, forces)
1156  type(ocean_grid_type), intent(inout) :: g !< Ocean grid structure
1157  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
1158  type(surface_forcing_cs), pointer :: cs !< Surface forcing control structure
1159  type(time_type), intent(in) :: time !< Model time structure
1160  type(mech_forcing), intent(inout) :: forces !< A structure with the driving mechanical forces
1161 
1162  ! Local variables
1163  real, dimension(SZI_(G),SZJ_(G)) :: tempx_at_h ! Delta to zonal wind stress at h points [R Z L T-2 ~> Pa]
1164  real, dimension(SZI_(G),SZJ_(G)) :: tempy_at_h ! Delta to meridional wind stress at h points [R Z L T-2 ~> Pa]
1165 
1166  integer :: isc, iec, jsc, jec, i, j
1167  real :: dlondx, dlondy, rdlon, cosa, sina, zonal_tau, merid_tau
1168  real :: pa_conversion ! A unit conversion factor from Pa to the internal units [R Z L T-2 Pa-1 ~> 1]
1169  logical :: overrode_x, overrode_y
1170 
1171  isc = g%isc; iec = g%iec ; jsc = g%jsc; jec = g%jec
1172  pa_conversion = us%kg_m3_to_R*us%m_s_to_L_T**2*us%L_to_Z
1173 
1174  tempx_at_h(:,:) = 0.0 ; tempy_at_h(:,:) = 0.0
1175  ! Either reads data or leaves contents unchanged
1176  overrode_x = .false. ; overrode_y = .false.
1177  call data_override('OCN', 'taux_adj', tempx_at_h(isc:iec,jsc:jec), time, override=overrode_x)
1178  call data_override('OCN', 'tauy_adj', tempy_at_h(isc:iec,jsc:jec), time, override=overrode_y)
1179 
1180  if (overrode_x .or. overrode_y) then
1181  if (.not. (overrode_x .and. overrode_y)) call mom_error(fatal,"apply_flux_adjustments: "//&
1182  "Both taux_adj and tauy_adj must be specified, or neither, in data_table")
1183 
1184  ! Rotate winds
1185  call pass_vector(tempx_at_h, tempy_at_h, g%Domain, to_all, agrid, halo=1)
1186  do j=jsc-1,jec+1 ; do i=isc-1,iec+1
1187  dlondx = g%geoLonCu(i,j) - g%geoLonCu(i-1,j)
1188  dlondy = g%geoLonCv(i,j) - g%geoLonCv(i,j-1)
1189  rdlon = sqrt( dlondx * dlondx + dlondy * dlondy )
1190  if (rdlon > 0.) rdlon = 1. / rdlon
1191  cosa = dlondx * rdlon
1192  sina = dlondy * rdlon
1193  zonal_tau = pa_conversion * tempx_at_h(i,j)
1194  merid_tau = pa_conversion * tempy_at_h(i,j)
1195  tempx_at_h(i,j) = cosa * zonal_tau - sina * merid_tau
1196  tempy_at_h(i,j) = sina * zonal_tau + cosa * merid_tau
1197  enddo ; enddo
1198 
1199  ! Average to C-grid locations
1200  do j=jsc,jec ; do i=isc-1,iec
1201  forces%taux(i,j) = forces%taux(i,j) + 0.5 * ( tempx_at_h(i,j) + tempx_at_h(i+1,j) )
1202  enddo ; enddo
1203 
1204  do j=jsc-1,jec ; do i=isc,iec
1205  forces%tauy(i,j) = forces%tauy(i,j) + 0.5 * ( tempy_at_h(i,j) + tempy_at_h(i,j+1) )
1206  enddo ; enddo
1207  endif ! overrode_x .or. overrode_y
1208 
1209 end subroutine apply_force_adjustments
1210 
1211 !> Save any restart files associated with the surface forcing.
1212 subroutine forcing_save_restart(CS, G, Time, directory, time_stamped, &
1213  filename_suffix)
1214  type(surface_forcing_cs), pointer :: cs !< A pointer to the control structure returned
1215  !! by a previous call to surface_forcing_init
1216  type(ocean_grid_type), intent(inout) :: g !< The ocean's grid structure
1217  type(time_type), intent(in) :: time !< The current model time
1218  character(len=*), intent(in) :: directory !< The directory into which to write the
1219  !! restart files
1220  logical, optional, intent(in) :: time_stamped !< If true, the restart file names include
1221  !! a unique time stamp. The default is false.
1222  character(len=*), optional, intent(in) :: filename_suffix !< An optional suffix (e.g., a time-
1223  !! stamp) to append to the restart file names.
1224 
1225  if (.not.associated(cs)) return
1226  if (.not.associated(cs%restart_CSp)) return
1227  call save_restart(directory, time, g, cs%restart_CSp, time_stamped)
1228 
1229 end subroutine forcing_save_restart
1230 
1231 !> Initialize the surface forcing, including setting parameters and allocating permanent memory.
1232 subroutine surface_forcing_init(Time, G, US, param_file, diag, CS, wind_stagger)
1233  type(time_type), intent(in) :: time !< The current model time
1234  type(ocean_grid_type), intent(in) :: g !< The ocean's grid structure
1235  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
1236  type(param_file_type), intent(in) :: param_file !< A structure to parse for run-time parameters
1237  type(diag_ctrl), target, intent(inout) :: diag !< A structure that is used to regulate
1238  !! diagnostic output
1239  type(surface_forcing_cs), pointer :: cs !< A pointer that is set to point to the control
1240  !! structure for this module
1241  integer, optional, intent(in) :: wind_stagger !< If present, the staggering of the winds that are
1242  !! being provided in calls to update_ocean_model
1243 
1244  ! Local variables
1245  real :: utide ! The RMS tidal velocity [Z T-1 ~> m s-1].
1246  type(directories) :: dirs
1247  logical :: new_sim, iceberg_flux_diags
1248  logical :: default_2018_answers
1249  type(time_type) :: time_frc
1250  character(len=200) :: tideamp_file, gust_file, salt_file, temp_file ! Input file names.
1251  ! This include declares and sets the variable "version".
1252 # include "version_variable.h"
1253  character(len=40) :: mdl = "MOM_surface_forcing" ! This module's name.
1254  character(len=48) :: stagger
1255  character(len=48) :: flnam
1256  character(len=240) :: basin_file
1257  integer :: i, j, isd, ied, jsd, jed
1258 
1259  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed
1260 
1261  if (associated(cs)) then
1262  call mom_error(warning, "surface_forcing_init called with an associated "// &
1263  "control structure.")
1264  return
1265  endif
1266  allocate(cs)
1267 
1268  id_clock_forcing=cpu_clock_id('Ocean surface forcing', grain=clock_subcomponent)
1269  call cpu_clock_begin(id_clock_forcing)
1270 
1271  cs%diag => diag
1272 
1273  call write_version_number(version)
1274  ! Read all relevant parameters and write them to the model log.
1275  call log_version(param_file, mdl, version, "", log_to_all=.true., debugging=.true.)
1276 
1277  call get_param(param_file, mdl, "INPUTDIR", cs%inputdir, &
1278  "The directory in which all input files are found.", &
1279  default=".")
1280  cs%inputdir = slasher(cs%inputdir)
1281  call get_param(param_file, mdl, "ENABLE_THERMODYNAMICS", cs%use_temperature, &
1282  "If true, Temperature and salinity are used as state "//&
1283  "variables.", default=.true.)
1284  call get_param(param_file, mdl, "RHO_0", cs%Rho0, &
1285  "The mean ocean density used with BOUSSINESQ true to "//&
1286  "calculate accelerations and the mass for conservation "//&
1287  "properties, or with BOUSSINSEQ false to convert some "//&
1288  "parameters from vertical units of m to kg m-2.", &
1289  units="kg m-3", default=1035.0, scale=us%kg_m3_to_R)
1290  call get_param(param_file, mdl, "LATENT_HEAT_FUSION", cs%latent_heat_fusion, &
1291  "The latent heat of fusion.", units="J/kg", default=hlf)
1292  call get_param(param_file, mdl, "LATENT_HEAT_VAPORIZATION", cs%latent_heat_vapor, &
1293  "The latent heat of fusion.", units="J/kg", default=hlv)
1294  call get_param(param_file, mdl, "MAX_P_SURF", cs%max_p_surf, &
1295  "The maximum surface pressure that can be exerted by the "//&
1296  "atmosphere and floating sea-ice or ice shelves. This is "//&
1297  "needed because the FMS coupling structure does not "//&
1298  "limit the water that can be frozen out of the ocean and "//&
1299  "the ice-ocean heat fluxes are treated explicitly. No "//&
1300  "limit is applied if a negative value is used.", &
1301  units="Pa", default=-1.0, scale=us%kg_m3_to_R*us%m_s_to_L_T**2)
1302  call get_param(param_file, mdl, "RESTORE_SALINITY", cs%restore_salt, &
1303  "If true, the coupled driver will add a globally-balanced "//&
1304  "fresh-water flux that drives sea-surface salinity "//&
1305  "toward specified values.", default=.false.)
1306  call get_param(param_file, mdl, "RESTORE_TEMPERATURE", cs%restore_temp, &
1307  "If true, the coupled driver will add a "//&
1308  "heat flux that drives sea-surface temperature "//&
1309  "toward specified values.", default=.false.)
1310  call get_param(param_file, mdl, "ADJUST_NET_SRESTORE_TO_ZERO", &
1311  cs%adjust_net_srestore_to_zero, &
1312  "If true, adjusts the salinity restoring seen to zero "//&
1313  "whether restoring is via a salt flux or virtual precip.",&
1314  default=cs%restore_salt)
1315  call get_param(param_file, mdl, "ADJUST_NET_SRESTORE_BY_SCALING", &
1316  cs%adjust_net_srestore_by_scaling, &
1317  "If true, adjustments to salt restoring to achieve zero net are "//&
1318  "made by scaling values without moving the zero contour.",&
1319  default=.false.)
1320  call get_param(param_file, mdl, "ADJUST_NET_FRESH_WATER_TO_ZERO", &
1321  cs%adjust_net_fresh_water_to_zero, &
1322  "If true, adjusts the net fresh-water forcing seen "//&
1323  "by the ocean (including restoring) to zero.", default=.false.)
1324  if (cs%adjust_net_fresh_water_to_zero) &
1325  call get_param(param_file, mdl, "USE_NET_FW_ADJUSTMENT_SIGN_BUG", &
1326  cs%use_net_FW_adjustment_sign_bug, &
1327  "If true, use the wrong sign for the adjustment to "//&
1328  "the net fresh-water.", default=.false.)
1329  call get_param(param_file, mdl, "ADJUST_NET_FRESH_WATER_BY_SCALING", &
1330  cs%adjust_net_fresh_water_by_scaling, &
1331  "If true, adjustments to net fresh water to achieve zero net are "//&
1332  "made by scaling values without moving the zero contour.",&
1333  default=.false.)
1334  call get_param(param_file, mdl, "ICE_SALT_CONCENTRATION", &
1335  cs%ice_salt_concentration, &
1336  "The assumed sea-ice salinity needed to reverse engineer the "//&
1337  "melt flux (or ice-ocean fresh-water flux).", &
1338  units="kg/kg", default=0.005)
1339  call get_param(param_file, mdl, "USE_LIMITED_PATM_SSH", cs%use_limited_P_SSH, &
1340  "If true, return the sea surface height with the "//&
1341  "correction for the atmospheric (and sea-ice) pressure "//&
1342  "limited by max_p_surf instead of the full atmospheric "//&
1343  "pressure.", default=.true.)
1344  call get_param(param_file, mdl, "APPROX_NET_MASS_SRC", cs%approx_net_mass_src, &
1345  "If true, use the net mass sources from the ice-ocean "//&
1346  "boundary type without any further adjustments to drive "//&
1347  "the ocean dynamics. The actual net mass source may differ "//&
1348  "due to internal corrections.", default=.false.)
1349 
1350  if (present(wind_stagger)) then
1351  if (wind_stagger == agrid) then ; stagger = 'AGRID'
1352  elseif (wind_stagger == bgrid_ne) then ; stagger = 'BGRID_NE'
1353  elseif (wind_stagger == cgrid_ne) then ; stagger = 'CGRID_NE'
1354  else ; stagger = 'UNKNOWN' ; call mom_error(fatal,"surface_forcing_init: WIND_STAGGER = "// &
1355  trim(stagger)// "is invalid."); endif
1356  call log_param(param_file, mdl, "WIND_STAGGER", stagger, &
1357  "The staggering of the input wind stress field "//&
1358  "from the coupler that is actually used.")
1359  cs%wind_stagger = wind_stagger
1360  else
1361  call get_param(param_file, mdl, "WIND_STAGGER", stagger, &
1362  "A case-insensitive character string to indicate the "//&
1363  "staggering of the input wind stress field. Valid "//&
1364  "values are 'A', 'B', or 'C'.", default="C")
1365  if (uppercase(stagger(1:1)) == 'A') then ; cs%wind_stagger = agrid
1366  elseif (uppercase(stagger(1:1)) == 'B') then ; cs%wind_stagger = bgrid_ne
1367  elseif (uppercase(stagger(1:1)) == 'C') then ; cs%wind_stagger = cgrid_ne
1368  else ; call mom_error(fatal,"surface_forcing_init: WIND_STAGGER = "// &
1369  trim(stagger)//" is invalid.") ; endif
1370  endif
1371 
1372  call get_param(param_file, mdl, "WIND_STRESS_MULTIPLIER", cs%wind_stress_multiplier, &
1373  "A factor multiplying the wind-stress given to the ocean by the "//&
1374  "coupler. This is used for testing and should be =1.0 for any "//&
1375  "production runs.", units="nondim", default=1.0)
1376 
1377  if (cs%restore_salt) then
1378  call get_param(param_file, mdl, "FLUXCONST", cs%Flux_const, &
1379  "The constant that relates the restoring surface fluxes to the relative "//&
1380  "surface anomalies (akin to a piston velocity). Note the non-MKS units.", &
1381  default=0.0, units="m day-1", scale=us%m_to_Z*us%T_to_s)
1382  ! Convert CS%Flux_const from m day-1 to m s-1.
1383  cs%Flux_const = cs%Flux_const / 86400.0
1384  call get_param(param_file, mdl, "SALT_RESTORE_FILE", cs%salt_restore_file, &
1385  "A file in which to find the surface salinity to use for restoring.", &
1386  default="salt_restore.nc")
1387  call get_param(param_file, mdl, "SALT_RESTORE_VARIABLE", cs%salt_restore_var_name, &
1388  "The name of the surface salinity variable to read from "//&
1389  "SALT_RESTORE_FILE for restoring salinity.", &
1390  default="salt")
1391 
1392  call get_param(param_file, mdl, "SRESTORE_AS_SFLUX", cs%salt_restore_as_sflux, &
1393  "If true, the restoring of salinity is applied as a salt "//&
1394  "flux instead of as a freshwater flux.", default=.false.)
1395  call get_param(param_file, mdl, "MAX_DELTA_SRESTORE", cs%max_delta_srestore, &
1396  "The maximum salinity difference used in restoring terms.", &
1397  units="PSU or g kg-1", default=999.0)
1398  call get_param(param_file, mdl, "MASK_SRESTORE_UNDER_ICE", &
1399  cs%mask_srestore_under_ice, &
1400  "If true, disables SSS restoring under sea-ice based on a frazil "//&
1401  "criteria (SST<=Tf). Only used when RESTORE_SALINITY is True.", &
1402  default=.false.)
1403  call get_param(param_file, mdl, "MASK_SRESTORE_MARGINAL_SEAS", &
1404  cs%mask_srestore_marginal_seas, &
1405  "If true, disable SSS restoring in marginal seas. Only used when "//&
1406  "RESTORE_SALINITY is True.", default=.false.)
1407  call get_param(param_file, mdl, "BASIN_FILE", basin_file, &
1408  "A file in which to find the basin masks, in variable 'basin'.", &
1409  default="basin.nc")
1410  basin_file = trim(cs%inputdir) // trim(basin_file)
1411  call safe_alloc_ptr(cs%basin_mask,isd,ied,jsd,jed) ; cs%basin_mask(:,:) = 1.0
1412  if (cs%mask_srestore_marginal_seas) then
1413  call mom_read_data(basin_file,'basin',cs%basin_mask,g%domain, timelevel=1)
1414  do j=jsd,jed ; do i=isd,ied
1415  if (cs%basin_mask(i,j) >= 6.0) then ; cs%basin_mask(i,j) = 0.0
1416  else ; cs%basin_mask(i,j) = 1.0 ; endif
1417  enddo ; enddo
1418  endif
1419  call get_param(param_file, mdl, "MASK_SRESTORE", cs%mask_srestore, &
1420  "If true, read a file (salt_restore_mask) containing "//&
1421  "a mask for SSS restoring.", default=.false.)
1422  endif
1423 
1424  if (cs%restore_temp) then
1425  call get_param(param_file, mdl, "FLUXCONST", cs%Flux_const, &
1426  "The constant that relates the restoring surface fluxes to the relative "//&
1427  "surface anomalies (akin to a piston velocity). Note the non-MKS units.", &
1428  default=0.0, units="m day-1", scale=us%m_to_Z*us%T_to_s)
1429  ! Convert CS%Flux_const from m day-1 to m s-1.
1430  cs%Flux_const = cs%Flux_const / 86400.0
1431  call get_param(param_file, mdl, "SST_RESTORE_FILE", cs%temp_restore_file, &
1432  "A file in which to find the surface temperature to use for restoring.", &
1433  default="temp_restore.nc")
1434  call get_param(param_file, mdl, "SST_RESTORE_VARIABLE", cs%temp_restore_var_name, &
1435  "The name of the surface temperature variable to read from "//&
1436  "SST_RESTORE_FILE for restoring sst.", &
1437  default="temp")
1438 
1439  call get_param(param_file, mdl, "MAX_DELTA_TRESTORE", cs%max_delta_trestore, &
1440  "The maximum sst difference used in restoring terms.", &
1441  units="degC ", default=999.0)
1442  call get_param(param_file, mdl, "MASK_TRESTORE", cs%mask_trestore, &
1443  "If true, read a file (temp_restore_mask) containing "//&
1444  "a mask for SST restoring.", default=.false.)
1445 
1446  endif
1447 
1448 ! Optionally read tidal amplitude from input file [m s-1] on model grid.
1449 ! Otherwise use default tidal amplitude for bottom frictionally-generated
1450 ! dissipation. Default cd_tides is chosen to yield approx 1 TWatt of
1451 ! work done against tides globally using OSU tidal amplitude.
1452  call get_param(param_file, mdl, "CD_TIDES", cs%cd_tides, &
1453  "The drag coefficient that applies to the tides.", &
1454  units="nondim", default=1.0e-4)
1455  call get_param(param_file, mdl, "READ_TIDEAMP", cs%read_TIDEAMP, &
1456  "If true, read a file (given by TIDEAMP_FILE) containing "//&
1457  "the tidal amplitude with INT_TIDE_DISSIPATION.", default=.false.)
1458  if (cs%read_TIDEAMP) then
1459  call get_param(param_file, mdl, "TIDEAMP_FILE", tideamp_file, &
1460  "The path to the file containing the spatially varying "//&
1461  "tidal amplitudes with INT_TIDE_DISSIPATION.", &
1462  default="tideamp.nc")
1463  cs%utide=0.0
1464  else
1465  call get_param(param_file, mdl, "UTIDE", cs%utide, &
1466  "The constant tidal amplitude used with INT_TIDE_DISSIPATION.", &
1467  units="m s-1", default=0.0, scale=us%m_to_Z*us%T_to_s)
1468  endif
1469 
1470  call safe_alloc_ptr(cs%TKE_tidal,isd,ied,jsd,jed)
1471  call safe_alloc_ptr(cs%ustar_tidal,isd,ied,jsd,jed)
1472 
1473  if (cs%read_TIDEAMP) then
1474  tideamp_file = trim(cs%inputdir) // trim(tideamp_file)
1475  call mom_read_data(tideamp_file,'tideamp',cs%TKE_tidal,g%domain,timelevel=1, scale=us%m_to_Z*us%T_to_s)
1476  do j=jsd, jed; do i=isd, ied
1477  utide = cs%TKE_tidal(i,j)
1478  cs%TKE_tidal(i,j) = g%mask2dT(i,j)*cs%Rho0*cs%cd_tides*(utide*utide*utide)
1479  cs%ustar_tidal(i,j) = sqrt(cs%cd_tides)*utide
1480  enddo ; enddo
1481  else
1482  do j=jsd,jed; do i=isd,ied
1483  utide = cs%utide
1484  cs%TKE_tidal(i,j) = cs%Rho0*cs%cd_tides*(utide*utide*utide)
1485  cs%ustar_tidal(i,j) = sqrt(cs%cd_tides)*utide
1486  enddo ; enddo
1487  endif
1488 
1489  call time_interp_external_init
1490 
1491  ! Optionally read a x-y gustiness field in place of a global constant.
1492  call get_param(param_file, mdl, "READ_GUST_2D", cs%read_gust_2d, &
1493  "If true, use a 2-dimensional gustiness supplied from "//&
1494  "an input file", default=.false.)
1495  call get_param(param_file, mdl, "GUST_CONST", cs%gust_const, &
1496  "The background gustiness in the winds.", &
1497  units="Pa", default=0.0, scale=us%kg_m3_to_R*us%m_s_to_L_T**2*us%L_to_Z)
1498  if (cs%read_gust_2d) then
1499  call get_param(param_file, mdl, "GUST_2D_FILE", gust_file, &
1500  "The file in which the wind gustiness is found in "//&
1501  "variable gustiness.")
1502 
1503  call safe_alloc_ptr(cs%gust,isd,ied,jsd,jed)
1504  gust_file = trim(cs%inputdir) // trim(gust_file)
1505  call mom_read_data(gust_file, 'gustiness', cs%gust, g%domain, timelevel=1, &
1506  scale=us%kg_m3_to_R*us%m_s_to_L_T**2*us%L_to_Z) ! units in file should be Pa
1507  endif
1508  call get_param(param_file, mdl, "DEFAULT_2018_ANSWERS", default_2018_answers, &
1509  "This sets the default value for the various _2018_ANSWERS parameters.", &
1510  default=.false.)
1511  call get_param(param_file, mdl, "SURFACE_FORCING_2018_ANSWERS", cs%answers_2018, &
1512  "If true, use the order of arithmetic and expressions that recover the answers "//&
1513  "from the end of 2018. Otherwise, use a simpler expression to calculate gustiness.", &
1514  default=default_2018_answers)
1515  call get_param(param_file, mdl, "FIX_USTAR_GUSTLESS_BUG", cs%fix_ustar_gustless_bug, &
1516  "If true correct a bug in the time-averaging of the gustless wind friction velocity", &
1517  default=.true.)
1518 
1519 ! See whether sufficiently thick sea ice should be treated as rigid.
1520  call get_param(param_file, mdl, "USE_RIGID_SEA_ICE", cs%rigid_sea_ice, &
1521  "If true, sea-ice is rigid enough to exert a "//&
1522  "nonhydrostatic pressure that resist vertical motion.", &
1523  default=.false.)
1524  if (cs%rigid_sea_ice) then
1525  call get_param(param_file, mdl, "G_EARTH", cs%g_Earth, &
1526  "The gravitational acceleration of the Earth.", &
1527  units="m s-2", default = 9.80, scale=us%Z_to_m*us%m_s_to_L_T**2)
1528  call get_param(param_file, mdl, "SEA_ICE_MEAN_DENSITY", cs%density_sea_ice, &
1529  "A typical density of sea ice, used with the kinematic "//&
1530  "viscosity, when USE_RIGID_SEA_ICE is true.", &
1531  units="kg m-3", default=900.0, scale=us%kg_m3_to_R)
1532  call get_param(param_file, mdl, "SEA_ICE_VISCOSITY", cs%Kv_sea_ice, &
1533  "The kinematic viscosity of sufficiently thick sea ice "//&
1534  "for use in calculating the rigidity of sea ice.", &
1535  units="m2 s-1", default=1.0e9, scale=us%Z_to_L**2*us%m_to_L**2*us%T_to_s)
1536  call get_param(param_file, mdl, "SEA_ICE_RIGID_MASS", cs%rigid_sea_ice_mass, &
1537  "The mass of sea-ice per unit area at which the sea-ice "//&
1538  "starts to exhibit rigidity", &
1539  units="kg m-2", default=1000.0, scale=us%kg_m3_to_R*us%m_to_Z)
1540  endif
1541 
1542  call get_param(param_file, mdl, "ALLOW_ICEBERG_FLUX_DIAGNOSTICS", iceberg_flux_diags, &
1543  "If true, makes available diagnostics of fluxes from icebergs "//&
1544  "as seen by MOM6.", default=.false.)
1545  call register_forcing_type_diags(time, diag, us, cs%use_temperature, cs%handles, &
1546  use_berg_fluxes=iceberg_flux_diags)
1547 
1548  call get_param(param_file, mdl, "ALLOW_FLUX_ADJUSTMENTS", cs%allow_flux_adjustments, &
1549  "If true, allows flux adjustments to specified via the "//&
1550  "data_table using the component name 'OCN'.", default=.false.)
1551 
1552  call get_param(param_file, mdl, "CHECK_NO_LAND_FLUXES", cs%check_no_land_fluxes, &
1553  "If true, checks that values from IOB fluxes are zero "//&
1554  "above land points (i.e. G%mask2dT = 0).", default=.false., &
1555  debuggingparam=.true.)
1556 
1557  call data_override_init(ocean_domain_in=g%Domain%mpp_domain)
1558 
1559  if (cs%restore_salt) then
1560  salt_file = trim(cs%inputdir) // trim(cs%salt_restore_file)
1561  cs%id_srestore = init_external_field(salt_file, cs%salt_restore_var_name, domain=g%Domain%mpp_domain)
1562  call safe_alloc_ptr(cs%srestore_mask,isd,ied,jsd,jed); cs%srestore_mask(:,:) = 1.0
1563  if (cs%mask_srestore) then ! read a 2-d file containing a mask for restoring fluxes
1564  flnam = trim(cs%inputdir) // 'salt_restore_mask.nc'
1565  call mom_read_data(flnam,'mask', cs%srestore_mask, g%domain, timelevel=1)
1566  endif
1567  endif
1568 
1569  if (cs%restore_temp) then
1570  temp_file = trim(cs%inputdir) // trim(cs%temp_restore_file)
1571  cs%id_trestore = init_external_field(temp_file, cs%temp_restore_var_name, domain=g%Domain%mpp_domain)
1572  call safe_alloc_ptr(cs%trestore_mask,isd,ied,jsd,jed); cs%trestore_mask(:,:) = 1.0
1573  if (cs%mask_trestore) then ! read a 2-d file containing a mask for restoring fluxes
1574  flnam = trim(cs%inputdir) // 'temp_restore_mask.nc'
1575  call mom_read_data(flnam, 'mask', cs%trestore_mask, g%domain, timelevel=1)
1576  endif
1577  endif
1578 
1579  ! Set up any restart fields associated with the forcing.
1580  call restart_init(param_file, cs%restart_CSp, "MOM_forcing.res")
1581 !#CTRL# call register_ctrl_forcing_restarts(G, param_file, CS%ctrl_forcing_CSp, &
1582 !#CTRL# CS%restart_CSp)
1583  call restart_init_end(cs%restart_CSp)
1584 
1585  if (associated(cs%restart_CSp)) then
1586  call get_mom_input(dirs=dirs)
1587 
1588  new_sim = .false.
1589  if ((dirs%input_filename(1:1) == 'n') .and. &
1590  (len_trim(dirs%input_filename) == 1)) new_sim = .true.
1591  if (.not.new_sim) then
1592  call restore_state(dirs%input_filename, dirs%restart_input_dir, time_frc, &
1593  g, cs%restart_CSp)
1594  endif
1595  endif
1596 
1597 !#CTRL# call controlled_forcing_init(Time, G, param_file, diag, CS%ctrl_forcing_CSp)
1598 
1599  call user_revise_forcing_init(param_file, cs%urf_CS)
1600 
1601  call cpu_clock_end(id_clock_forcing)
1602 end subroutine surface_forcing_init
1603 
1604 !> Clean up and deallocate any memory associated with this module and its children.
1605 subroutine surface_forcing_end(CS, fluxes)
1606  type(surface_forcing_cs), pointer :: cs !< A pointer to the control structure returned by
1607  !! a previous call to surface_forcing_init, it will
1608  !! be deallocated here.
1609  type(forcing), optional, intent(inout) :: fluxes !< A structure containing pointers to all
1610  !! possible mass, heat or salt flux forcing fields.
1611  !! If present, it will be deallocated here.
1612 
1613  if (present(fluxes)) call deallocate_forcing_type(fluxes)
1614 
1615 !#CTRL# call controlled_forcing_end(CS%ctrl_forcing_CSp)
1616 
1617  if (associated(cs)) deallocate(cs)
1618  cs => null()
1619 
1620 end subroutine surface_forcing_end
1621 
1622 !> Write out a set of messages with checksums of the fields in an ice_ocen_boundary type
1623 subroutine ice_ocn_bnd_type_chksum(id, timestep, iobt)
1624 
1625  character(len=*), intent(in) :: id !< An identifying string for this call
1626  integer, intent(in) :: timestep !< The number of elapsed timesteps
1627  type(ice_ocean_boundary_type), &
1628  intent(in) :: iobt !< An ice-ocean boundary type with fluxes to drive the
1629  !! ocean in a coupled model whose checksums are reported
1630  integer :: n,m, outunit
1631 
1632  outunit = stdout()
1633 
1634  write(outunit,*) "BEGIN CHECKSUM(ice_ocean_boundary_type):: ", id, timestep
1635  write(outunit,100) 'iobt%u_flux ', mpp_chksum( iobt%u_flux )
1636  write(outunit,100) 'iobt%v_flux ', mpp_chksum( iobt%v_flux )
1637  write(outunit,100) 'iobt%t_flux ', mpp_chksum( iobt%t_flux )
1638  write(outunit,100) 'iobt%q_flux ', mpp_chksum( iobt%q_flux )
1639  write(outunit,100) 'iobt%salt_flux ', mpp_chksum( iobt%salt_flux )
1640  write(outunit,100) 'iobt%lw_flux ', mpp_chksum( iobt%lw_flux )
1641  write(outunit,100) 'iobt%sw_flux_vis_dir', mpp_chksum( iobt%sw_flux_vis_dir)
1642  write(outunit,100) 'iobt%sw_flux_vis_dif', mpp_chksum( iobt%sw_flux_vis_dif)
1643  write(outunit,100) 'iobt%sw_flux_nir_dir', mpp_chksum( iobt%sw_flux_nir_dir)
1644  write(outunit,100) 'iobt%sw_flux_nir_dif', mpp_chksum( iobt%sw_flux_nir_dif)
1645  write(outunit,100) 'iobt%lprec ', mpp_chksum( iobt%lprec )
1646  write(outunit,100) 'iobt%fprec ', mpp_chksum( iobt%fprec )
1647  write(outunit,100) 'iobt%runoff ', mpp_chksum( iobt%runoff )
1648  write(outunit,100) 'iobt%calving ', mpp_chksum( iobt%calving )
1649  write(outunit,100) 'iobt%p ', mpp_chksum( iobt%p )
1650  if (associated(iobt%ustar_berg)) &
1651  write(outunit,100) 'iobt%ustar_berg ', mpp_chksum( iobt%ustar_berg )
1652  if (associated(iobt%area_berg)) &
1653  write(outunit,100) 'iobt%area_berg ', mpp_chksum( iobt%area_berg )
1654  if (associated(iobt%mass_berg)) &
1655  write(outunit,100) 'iobt%mass_berg ', mpp_chksum( iobt%mass_berg )
1656 100 FORMAT(" CHECKSUM::",a20," = ",z20)
1657 
1658  call coupler_type_write_chksums(iobt%fluxes, outunit, 'iobt%')
1659 
1660 end subroutine ice_ocn_bnd_type_chksum
1661 
1662 !> Check the values passed by IOB over land are zero
1663 subroutine check_mask_val_consistency(val, mask, i, j, varname, G)
1664 
1665  real, intent(in) :: val !< value of flux/variable passed by IOB
1666  real, intent(in) :: mask !< value of ocean mask
1667  integer, intent(in) :: i !< model grid cell indices
1668  integer, intent(in) :: j !< model grid cell indices
1669  character(len=*), intent(in) :: varname !< variable name
1670  type(ocean_grid_type), intent(in) :: g !< The ocean's grid structure
1671  ! Local variables
1672  character(len=48) :: ci, cj !< model local grid cell indices as strings
1673  character(len=48) :: ciglo, cjglo !< model global grid cell indices as strings
1674  character(len=48) :: cval !< value to be displayed
1675  character(len=256) :: error_message !< error message to be displayed
1676 
1677  if ((mask == 0.) .and. (val /= 0.)) then
1678  write(ci, '(I8)') i
1679  write(cj, '(I8)') j
1680  write(ciglo, '(I8)') i + g%HI%idg_offset
1681  write(cjglo, '(I8)') j + g%HI%jdg_offset
1682  write(cval, '(E22.16)') val
1683  error_message = "MOM_surface_forcing: found non-zero value (="//trim(cval)//") over land "//&
1684  "for variable "//trim(varname)//" at local point (i, j) = ("//trim(ci)//", "//trim(cj)//&
1685  ", global point (iglo, jglo) = ("//trim(ciglo)//", "//trim(cjglo)//")"
1686  call mom_error(warning, error_message)
1687  endif
1688 
1689 end subroutine
1690 
1691 end module mom_surface_forcing_gfdl
Pointers to various fields which may be used describe the surface state of MOM, and which will be ret...
Functions and routines to take area, volume, mass-weighted, layerwise, zonal or meridional means.
This module implements boundary forcing for MOM6.
Control structure for user_revise_forcing.
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:26
The following data type a list of diagnostic fields an their variants, as well as variables that cont...
Reads the only Fortran name list needed to boot-strap the model.
Allocate the fields of a mechanical forcing type, based on either a set of input flags for each group...
A structure that can be parsed to read and document run-time parameters.
Provides the ocean grid type.
Definition: MOM_grid.F90:2
Wraps the MPP cpu clock functions.
Register fields for restarts.
This module contains I/O framework code.
Definition: MOM_io.F90:2
The MOM6 facility to parse input files for runtime parameters.
Do a set of halo updates that fill in the values at the duplicated edges of a staggered symmetric mem...
Definition: MOM_domains.F90:93
An overloaded interface to log the values of various types of parameters.
Do a halo update on a pair of arrays representing the two components of a vector.
Definition: MOM_domains.F90:59
Provides a few physical constants.
surface_forcing_CS is a structure containing pointers to the forcing fields which may be used to driv...
Structure that contains pointers to the mechanical forcing at the surface used to drive the liquid oc...
Interfaces to non-domain-oriented communication subroutines, including the MOM6 reproducing sums faci...
Definition: MOM_coms.F90:3
Allocate the fields of a (flux) forcing type, based on either a set of input flags for each group of ...
ice_ocean_boundary_type is a structure corresponding to forcing, but with the elements,...
A restart registry and the control structure for restarts.
Definition: MOM_restart.F90:75
Describes various unit conversion factors.
Provides a transparent unit rescaling type to facilitate dimensional consistency testing.
Describes the decomposed MOM domain and has routines for communications across PEs.
Definition: MOM_domains.F90:2
Provides a template for users to code updating the forcing fluxes.
The subroutines here provide convenient wrappers to the fms diag_manager interfaces with additional d...
Routines for error handling and I/O management.
The MOM6 facility for reading and writing restart files, and querying what has been read.
Definition: MOM_restart.F90:2
Structure that contains pointers to the boundary forcing used to drive the liquid ocean simulated by ...
An overloaded interface to log version information about modules.
Container for paths and parameter file names.
Find an accurate and order-invariant sum of a distributed 2d or 3d field.
Definition: MOM_coms.F90:53
Handy functions for manipulating strings.
Provides transparent structures with groups of MOM6 variables and supporting routines.
Structure that defines the id handles for the forcing type.
Do a halo update on an array.
Definition: MOM_domains.F90:54
Read a data field from a file.
Definition: MOM_io.F90:74
An overloaded interface to read and log the values of various types of parameters.