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
user_revise_forcing::user_revise_forcing_cs
Control structure for user_revise_forcing.
Definition: user_revise_forcing.F90:23
mom_forcing_type::mech_forcing
Structure that contains pointers to the mechanical forcing at the surface used to drive the liquid oc...
Definition: MOM_forcing_type.F90:204
mom_variables::surface
Pointers to various fields which may be used describe the surface state of MOM, and which will be ret...
Definition: MOM_variables.F90:41
mom_spatial_means
Functions and routines to take area, volume, mass-weighted, layerwise, zonal or meridional means.
Definition: MOM_spatial_means.F90:2
mom_surface_forcing_gfdl::surface_forcing_cs
surface_forcing_CS is a structure containing pointers to the forcing fields which may be used to driv...
Definition: MOM_surface_forcing_gfdl.F90:59
mom_file_parser::log_version
An overloaded interface to log version information about modules.
Definition: MOM_file_parser.F90:109
mom_constants
Provides a few physical constants.
Definition: MOM_constants.F90:2
user_revise_forcing
Provides a template for users to code updating the forcing fluxes.
Definition: user_revise_forcing.F90:2
mom_diag_mediator
The subroutines here provide convenient wrappers to the fms diag_manager interfaces with additional d...
Definition: MOM_diag_mediator.F90:3
mom_get_input::directories
Container for paths and parameter file names.
Definition: MOM_get_input.F90:20
mom_string_functions
Handy functions for manipulating strings.
Definition: MOM_string_functions.F90:2
mom_domains::pass_var
Do a halo update on an array.
Definition: MOM_domains.F90:54
mom_file_parser::param_file_type
A structure that can be parsed to read and document run-time parameters.
Definition: MOM_file_parser.F90:54
mom_file_parser::get_param
An overloaded interface to read and log the values of various types of parameters.
Definition: MOM_file_parser.F90:102
mom_forcing_type::allocate_mech_forcing
Allocate the fields of a mechanical forcing type, based on either a set of input flags for each group...
Definition: MOM_forcing_type.F90:50
mom_io
This module contains I/O framework code.
Definition: MOM_io.F90:2
mom_restart::mom_restart_cs
A restart registry and the control structure for restarts.
Definition: MOM_restart.F90:75
mom_get_input
Reads the only Fortran name list needed to boot-strap the model.
Definition: MOM_get_input.F90:6
mom_unit_scaling::unit_scale_type
Describes various unit conversion factors.
Definition: MOM_unit_scaling.F90:14
mom_surface_forcing_gfdl::ice_ocean_boundary_type
ice_ocean_boundary_type is a structure corresponding to forcing, but with the elements,...
Definition: MOM_surface_forcing_gfdl.F90:164
mom_coms
Interfaces to non-domain-oriented communication subroutines, including the MOM6 reproducing sums faci...
Definition: MOM_coms.F90:3
mom_domains::pass_vector
Do a halo update on a pair of arrays representing the two components of a vector.
Definition: MOM_domains.F90:59
mom_forcing_type
This module implements boundary forcing for MOM6.
Definition: MOM_forcing_type.F90:2
mom_forcing_type::allocate_forcing_type
Allocate the fields of a (flux) forcing type, based on either a set of input flags for each group of ...
Definition: MOM_forcing_type.F90:43
mom_restart
The MOM6 facility for reading and writing restart files, and querying what has been read.
Definition: MOM_restart.F90:2
mom_domains
Describes the decomposed MOM domain and has routines for communications across PEs.
Definition: MOM_domains.F90:2
mom_variables
Provides transparent structures with groups of MOM6 variables and supporting routines.
Definition: MOM_variables.F90:2
mom_io::mom_read_data
Read a data field from a file.
Definition: MOM_io.F90:74
mom_cpu_clock
Wraps the MPP cpu clock functions.
Definition: MOM_cpu_clock.F90:2
mom_file_parser
The MOM6 facility to parse input files for runtime parameters.
Definition: MOM_file_parser.F90:2
mom_grid
Provides the ocean grid type.
Definition: MOM_grid.F90:2
mom_restart::register_restart_field
Register fields for restarts.
Definition: MOM_restart.F90:111
mom_unit_scaling
Provides a transparent unit rescaling type to facilitate dimensional consistency testing.
Definition: MOM_unit_scaling.F90:2
mom_forcing_type::forcing
Structure that contains pointers to the boundary forcing used to drive the liquid ocean simulated by ...
Definition: MOM_forcing_type.F90:66
mom_forcing_type::forcing_diags
Structure that defines the id handles for the forcing type.
Definition: MOM_forcing_type.F90:256
mom_coms::reproducing_sum
Find an accurate and order-invariant sum of a distributed 2d or 3d field.
Definition: MOM_coms.F90:53
mom_file_parser::log_param
An overloaded interface to log the values of various types of parameters.
Definition: MOM_file_parser.F90:96
mom_error_handler
Routines for error handling and I/O management.
Definition: MOM_error_handler.F90:2
mom_grid::ocean_grid_type
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:26
mom_domains::fill_symmetric_edges
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
mom_diag_mediator::diag_ctrl
The following data type a list of diagnostic fields an their variants, as well as variables that cont...
Definition: MOM_diag_mediator.F90:240