Binds time-varying NetCDF surface fields onto the ocean C-grid
forcing slots: wind stress (ocean_surface_stress_t%tau_x/tau_y)
and the surface heat / freshwater / salt fluxes
(ocean_surface_flux_t). Owns the (file, variable) -> slot
mapping that rdb_ocean_data_input deliberately does not; owns no
file handles, no time axis and no buffers of its own — those all
live in the reader.
Two-call contract.ocean_data_forcing_configure runs once in
the driver’s configure phase, BEFORE ocean_state_enter_data
(registration opens each file and allocates the reader’s f0/f1
bracket buffers, which enter_data then maps).
ocean_data_forcing_apply runs once per outer step, immediately
after the driver’s ocean_data_input_update_all(t_current) — the
reader’s fail-loud freshness check enforces that ordering rather
than letting a stale bracket blend silently.
This slot maps nothing. It holds registration ids and
logicals that are read HOST-side only, so it adds no term to
ocean_state_enter_data — stated explicitly because the standing
rule is that a new ocean slot does wire in, and a silent omission
there is a 150-1500x memcpy bug. The arrays it writes into are
mapped by their own owning slots.
Ghost cells are EXCHANGED, never extrapolated. The reader
fills physical cells only and the consumer owns the halo. For the
stress pair that halo is filled by
ocean_seam_refresh_surface_stress — MPI exchange, then periodic
wrap on any axis the exchange did not own, then the tripolar fold —
which also re-derives stress_mag (read by KPP/EPBL for u_*,
and otherwise stale from the configure-time wind).
An earlier revision zero-gradient-extended the blend into its
ghosts. That is wrong under decomposition and worth recording so
it is not reintroduced: at an MPI seam the ghost belongs to the
neighbour rank, so copying this rank’s edge value there produces a
decomposition-dependent answer that no single-rank test can see.
Extrapolating forcing into a halo is a pattern MOM6 does not use
anywhere; it exchanges the stress pair and relies on masking at
true domain edges.
The thermodynamic flux tags (heat, salt, evap, lprec) get
NO ghost treatment at all, deliberately: they are applied strictly
column-locally, so no kernel ever reads them in a ghost cell. A
future kernel that takes a horizontal gradient of one of them owns
adding the exchange.
The namelist is the expert-level surface, not the intended one.&ocean_dataovr_nml is a flat, fixed set of tags because that is
what the strict schema can express; it is not the ergonomic way to
describe a forcing dataset. The binding itself is programmatic —
register_tag is a thin wrapper over
ocean_data_input_register_2d returning an opaque id — so a
future Python/C driver should call the registration path DIRECTLY
with its own field table rather than synthesising namelist text.
Keep ocean_data_forcing_configure a pure translation of config
to registrations, with no logic that a non-namelist caller would
have to re-implement.
Heat/salt destination depends on use_components — see
resolve_flux_targets. ocean_surface_flux_assemble fully
overwrites Q_heat/Q_salt from the component set every thermo
step, so under components the file must feed a component instead.
Nodes of different colours represent the following:
Solid arrows point from a submodule to the (sub)module which it is
descended from. Dashed arrows point from a module or program unit to
modules which it uses.
Where possible, edges connecting nodes are
given different colours to make them easier to distinguish in
large graphs.
Nodes of different colours represent the following:
Solid arrows point from a submodule to the (sub)module which it is
descended from. Dashed arrows point from a module or program unit to
modules which it uses.
Where possible, edges connecting nodes are
given different colours to make them easier to distinguish in
large graphs.
Registration bookkeeping for the file-driven surface tags. A
zero id_* means that tag is not file-driven (blank file in
&ocean_dataovr_nml) and its slot keeps whatever the
configure-time scalar/formula path seeded.
Components
Type
Visibility
Attributes
Name
Initial
logical,
public
::
active
=
.false.
.true. once at least one tag registered. .false. makes
ocean_data_forcing_apply an immediate return.
logical,
public
::
heat_to_component
=
.false.
.true. => the heat tag blends into heat_added (components
on); .false. => straight into Q_heat. Resolved once at
configure so the per-step path carries no mode test.
Blend every active tag’s current bracket into its slot. t must
be the same model time ocean_data_input_update_all was just
called with — the reader enforces this and aborts otherwise.
Register every configured tag against the shared reader and
resolve the heat/salt destinations. A no-op when
enable = .false. — nothing registers, so
ocean_data_input_update_all stays a no-op and the run is
bit-identical.
Non-zero (OCEAN_STATUS_ERR_SETUP/OCEAN_STATUS_ERR_IO, see
rdb_ocean_status) on a malformed &ocean_dataovr_nml group
or a registration failure (bad file/variable/dims) when
present; absent behaves as today (error stop).
Fail loud on a cyclic group with no period. The reader makes
the same check per field; catching it once here names the
namelist group the user actually edited.