| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| logical, | public | :: | direct_stress | = | .false. |
MOM6 DIRECT_STRESS analogue. When |
|
| type(scratch_3d_buffer_t), | public | :: | du_stress |
Surface stress tendency at east faces, shape (nx+1, ny, nz). Only k=nz carries a non-zero value; k |
|||
| type(scratch_3d_buffer_t), | public | :: | dv_stress |
Surface stress tendency at north faces, shape (nx, ny+1, nz). |
|||
| real(kind=wp), | public | :: | h_min | = | 1.0e-3_wp |
Floor on the surface-layer thickness in the |
|
| logical, | public | :: | has_stress_mag | = | .false. |
True once |
|
| real(kind=wp), | public | :: | hmix_stress | = | 0.0_wp |
Thickness (m) of the surface slab over which the stress
is spread when |
|
| logical, | public | :: | is_init | = | .false. |
True between |
|
| real(kind=wp), | public | :: | rho0 | = | 1035.0_wp |
Boussinesq reference density (kg/m^3) used in the
ASSIGNED FROM CONFIG by |
|
| real(kind=wp), | public, | allocatable | :: | stress_mag(:,:) |
Cell-centred wind-stress magnitude Under an ice-shelf cavity the refresh is not enough on its
own: a |
||
| real(kind=wp), | public, | allocatable | :: | stress_shelf(:,:) |
Cell-centred magnitude of the stress an ICE-SHELF BASE
exerts on the ocean (N/m^2, NOT derived from Consumers: KPP ( |
||
| real(kind=wp), | public, | allocatable | :: | tau_x(:,:) |
Zonal wind stress (N/m^2) on east faces, shape |
||
| real(kind=wp), | public, | allocatable | :: | tau_y(:,:) |
Meridional wind stress (N/m^2) on north faces, shape
|
Counted allocatable footprint of the surface stress slot (0 when unallocated). One arr_bytes term per array — add a term here when a new allocatable joins the type.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_surface_stress_t), | intent(in) | :: | this |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_surface_stress_t), | intent(inout) | :: | this |
Type-bound wrapper — delegates to the non-polymorphic impl so the device-attach map base is the heap object, not a polymorphic stack box (AMD libomptarget cross-slot-overlap fix). Slot-header presence comes from the orchestrator’s root copyin(state); only the leaf arrays + scratch are attached here.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_surface_stress_t), | intent(inout) | :: | this |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_surface_stress_t), | intent(inout) | :: | this |
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_surface_stress_t), | intent(inout) | :: | this | |||
| type(hgrid_t), | intent(in) | :: | grid | |||
| integer, | intent(in), | optional | :: | nz_ml |
Fill tau_x with the MOM6 2gyre profile,
tau_x(i,j) = taux_mag · (1 − cos(2π · (y − y_south) / y_len)),
and zero tau_y. In Cartesian terms (y − y_south) / y_len
is the normalised position from the south wall of the physical
domain (0 at south, 1 at north), so the formula reduces to
taux_mag · (1 − cos(2π · ((j_phys − 0.5) / ny_phys))) with
j_phys = j − nghost. Physical-interior rows only; ghost rows
stay at zero so wall faces see no spurious stress. Host only —
call enter_data afterwards (or !$acc update device if
already mapped).
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_surface_stress_t), | intent(inout) | :: | this | |||
| type(hgrid_t), | intent(in) | :: | grid | |||
| real(kind=wp), | intent(in) | :: | taux_mag | |||
| integer, | intent(in), | optional | :: | j_offset | ||
| integer, | intent(in), | optional | :: | ny_global |
Fill tau_x / tau_y uniformly with the given scalar
values. Convenience helper for the spatially-constant
wind case (the Tier-1 default and most unit tests). Host
only — call enter_data afterwards (or !$acc update
device if already mapped) to sync to GPU. Refreshes
stress_mag in step (PR-12) so every existing caller —
production and unit test alike — gets a consistent
stress_mag with no separate call required.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_surface_stress_t), | intent(inout) | :: | this | |||
| real(kind=wp), | intent(in) | :: | tau_x_val | |||
| real(kind=wp), | intent(in) | :: | tau_y_val |
Fill tau_x with the Neverworld2 zonal wind-stress profile
(Marques et al. 2022, GMD; MOM6-inspired) and zero tau_y. τ_x is a
3-band piecewise function of the normalized meridional position
y = (j_phys − 0.5)/ny_phys ∈ [0,1] (which equals MOM6’s
(lat − south)/len_lat on a uniform grid), scaled by the peak stress
taux_mag (Pa), with off = 0.02:
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(ocean_surface_stress_t), | intent(inout) | :: | this | |||
| type(hgrid_t), | intent(in) | :: | grid | |||
| real(kind=wp), | intent(in) | :: | taux_mag | |||
| integer, | intent(in), | optional | :: | j_offset | ||
| integer, | intent(in), | optional | :: | ny_global |
type :: ocean_surface_stress_t logical :: is_init = .false. !! True between `init` and `destroy`. real(wp) :: rho0 = 1035.0_wp !! Boussinesq reference density (kg/m^3) used in the !! `tau / (rho_0 * h)` acceleration (top-layer and DIRECT_STRESS !! distributed forms alike). !! !! ASSIGNED FROM CONFIG by `configure_ocean_reference_density`, !! which copies the single rho0 of record (`&ocean_ic_nml rho_0` !! -> `eos%rho0`). The literal here is only the pre-configure !! type default. Host scalar: passed by value into the !! `surfstress_*_impl` kernels, so no `!$acc update device`. real(wp) :: h_min = 1.0e-3_wp !! Floor on the surface-layer thickness in the `1/h_top` !! division — keeps the kernel finite when the top layer !! pinches out (rare; e.g., wave breaking under ZSTAR_FULL). logical :: direct_stress = .false. !! MOM6 DIRECT_STRESS analogue. When `.true.` the wind !! stress is distributed across the top `hmix_stress` metres !! rather than concentrated in the surface-most layer. !! Mirrors HYCOM's approach: stress acts on a surface slab !! of fixed thickness, with each layer in that slab getting !! a proportional share. Has no effect when the top layer !! is already thinner than `hmix_stress` (bit-identical to !! the bed-only branch in that limit). real(wp) :: hmix_stress = 0.0_wp !! Thickness (m) of the surface slab over which the stress !! is spread when `direct_stress = .true.`. MOM6 production !! default 20 m. Zero (default) keeps the surface-layer-only !! behaviour even if `direct_stress` is flipped on. real(wp), allocatable :: tau_x(:, :) !! Zonal wind stress (N/m^2) on east faces, shape `(nx+1, ny)`. !! Fill via `set_wind_stress_const` for spatially-uniform !! wind, or assign the array directly for spatially-varying !! input. Default zero. real(wp), allocatable :: tau_y(:, :) !! Meridional wind stress (N/m^2) on north faces, shape !! `(nx, ny+1)`. logical :: has_stress_mag = .false. !! True once `ocean_surface_stress_set_derived` has filled !! `stress_mag` at least once. Informational — no kernel gates !! on it (`stress_mag` is always allocated and zero-safe). real(wp), allocatable :: stress_mag(:, :) !! Cell-centred wind-stress magnitude `|tau|` (Pa, !! `sqrt(tau_x_cell^2 + tau_y_cell^2)`), shape `(nx_total, !! ny_total)`. **Always allocated** (PR-12 §7.4: a pure τ !! property, unlike a shared `ustar` which would need a !! coherent `rho0` — deferred). Refreshed in step by every !! writer of the `tau` pair: the `set_wind_stress_*` setters and !! `ocean_surface_stress_set_derived` at configure, the !! data-forcing reader's seam refresh per bracket, and the !! sea-ice stress coupler's on-device blend every outer step !! (all via `ocean_surface_stress_refresh_mag`). KPP !! (`rdb_ocean_vmix`) !! and EPBL (`rdb_ocean_epbl`) both read it instead of !! re-deriving `tau_mag` inline (bit-identical dedup — same !! three lines, same FP op order, just computed once) — which is !! why a `tau` write that skips the refresh silently freezes !! BOTH schemes' `u_*` at the last refreshed stress. !! !! Under an ice-shelf cavity the refresh is not enough on its !! own: a `tau` writer must ALSO re-apply the cover mask, which !! is why `ocean_surface_stress_apply_cover` does both in one !! call (see the module docstring's cover contract). What the !! mask leaves behind under the ice — the ICE-OCEAN stress — is !! `stress_shelf` below, deliberately not folded in here. real(wp), allocatable :: stress_shelf(:, :) !! Cell-centred magnitude of the stress an ICE-SHELF BASE !! exerts on the ocean (N/m^2, `>= 0`), shape `(nx_total, !! ny_total)`, valid including ghosts. **Always allocated**, !! mapped and counted; exactly zero without a cavity, and !! exactly zero on every cell with `cover_frac = 0`. !! !! NOT derived from `tau` — the ice-ocean stress is a separate !! momentum tendency (`rdb_ocean_top_drag`), so this field is !! NOT touched by `ocean_surfstress_refresh_stress_mag` and NOT !! touched by `ocean_surface_stress_apply_cover`. It is !! refreshed by the split/unsplit RK2 drivers, inline, in the !! same stage that recomputes the top drag and strictly before !! `vmix_apply_in_stage` reads it (no lag), from !! `ocean_top_drag_t%stress_top`. When the top drag is OFF but !! `&ocean_cavity_melt_nml enable` is on, `engine_step_finalize` !! fills it instead from `rho_0*u_*^2` with the melt slot's own !! `u_*` — the SAME `C_d` under the one-drag-coefficient rule, !! but at the thermo cadence, so THAT path is lagged one outer !! step. Both are documented in the module docstring. !! !! Consumers: KPP (`rdb_ocean_vmix`) and EPBL !! (`rdb_ocean_epbl`), both as !! `u_* = sqrt((stress_mag + stress_shelf)/rho_0)`. type(scratch_3d_buffer_t) :: du_stress !! Surface stress tendency at east faces, shape !! (nx+1, ny, nz). Only k=nz carries a non-zero value; !! k<nz stays zero. type(scratch_3d_buffer_t) :: dv_stress !! Surface stress tendency at north faces, shape !! (nx, ny+1, nz). contains procedure, non_overridable :: init => ocean_surfstress_init procedure, non_overridable :: destroy => ocean_surfstress_destroy procedure, non_overridable :: enter_data => ocean_surfstress_enter_data procedure, non_overridable :: exit_data => ocean_surfstress_exit_data procedure, non_overridable :: set_wind_stress_const => ocean_surfstress_set_const procedure, non_overridable :: set_wind_stress_2gyre => ocean_surfstress_set_2gyre procedure, non_overridable :: set_wind_stress_neverworld2 => ocean_surfstress_set_neverworld2 procedure, non_overridable :: bytes => ocean_surface_stress_bytes end type ocean_surface_stress_t