ocean_surface_stress_set_shelf_from_ustar Subroutine

public pure subroutine ocean_surface_stress_set_shelf_from_ustar(stress_shelf, ustar, rho0, nx, ny)

Publish the ice-base stress from a FRICTION VELOCITY: stress_shelf = rho_0 * u_*^2.

The melt-only route into the stress_shelf seam. When &ocean_tdrag_nml is on, the RK2 stage drivers fill stress_shelf inline from ocean_top_drag_t%stress_top and this is never called; when the top drag is OFF but &ocean_cavity_melt_nml is on, engine_step_finalize calls it with the melt slot’s own u_*, which was solved with the SAME C_d (the one-drag-coefficient rule refuses a disagreeing cdrag_top at configure). So this is a change of variable, not a second drag law.

Cadence, stated because it is a real cost: the melt u_* is refreshed at the THERMO cadence at the END of an outer step, so this path reaches KPP/EPBL ONE OUTER STEP LATE. The top-drag path has no such lag.

A CALL rather than an inline loop at the call site, deliberately: engine_step_finalize would have to walk engine%state%... inside a do concurrent, and ocean_engine_t is not a mapped object, so nvfortran emits a data clause for the whole engine and aborts with “partially present on the device”. Host-dereference at the call site, flat explicit-shape dummies here. The escaping-actual pessimisation CLAUDE.md warns about does not apply: engine_step_finalize owns no do concurrent of its own.

u_* is exactly zero on every uncovered column (cavity_melt_columns_2d), so the published field keeps stress_shelf’s “exactly zero off the cover” invariant and the disjoint-support argument in the module docstring still holds.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(inout) :: stress_shelf(nx,ny)

ocean_surface_stress_t%stress_shelf (N/m^2), overwritten.

real(kind=wp), intent(in) :: ustar(nx,ny)

ocean_cavity_flux_t%ustar (m/s).

real(kind=wp), intent(in) :: rho0

Reference density (kg/m^3) – the single rho0 of record, by value from ocean_surface_stress_t%rho0.

integer, intent(in) :: nx

Extents of BOTH arrays. The caller gates on the melt slot’s enable, which is exactly when ustar is full size, so an (1,1) placeholder can never reach these dummies.

integer, intent(in) :: ny

Extents of BOTH arrays. The caller gates on the melt slot’s enable, which is exactly when ustar is full size, so an (1,1) placeholder can never reach these dummies.


Called by

proc~~ocean_surface_stress_set_shelf_from_ustar~~CalledByGraph proc~ocean_surface_stress_set_shelf_from_ustar ocean_surface_stress_set_shelf_from_ustar proc~engine_step_finalize engine_step_finalize proc~engine_step_finalize->proc~ocean_surface_stress_set_shelf_from_ustar proc~driver_run_ocean driver_run_ocean proc~driver_run_ocean->proc~engine_step_finalize proc~rdb_ocean_step rdb_ocean_step proc~rdb_ocean_step->proc~engine_step_finalize proc~driver_run driver_run proc~driver_run->proc~driver_run_ocean

Variables

Type Visibility Attributes Name Initial
integer, private :: i
integer, private :: j

Source Code

   pure subroutine ocean_surface_stress_set_shelf_from_ustar(stress_shelf, ustar, &
                                                             rho0, nx, ny)
      !! Publish the ice-base stress from a FRICTION VELOCITY:
      !! `stress_shelf = rho_0 * u_*^2`.
      !!
      !! The melt-only route into the `stress_shelf` seam.  When
      !! `&ocean_tdrag_nml` is on, the RK2 stage drivers fill
      !! `stress_shelf` inline from `ocean_top_drag_t%stress_top` and
      !! this is never called; when the top drag is OFF but
      !! `&ocean_cavity_melt_nml` is on, `engine_step_finalize` calls it
      !! with the melt slot's own `u_*`, which was solved with the SAME
      !! `C_d` (the one-drag-coefficient rule refuses a disagreeing
      !! `cdrag_top` at configure).  So this is a change of variable, not
      !! a second drag law.
      !!
      !! **Cadence, stated because it is a real cost:** the melt `u_*` is
      !! refreshed at the THERMO cadence at the END of an outer step, so
      !! this path reaches KPP/EPBL ONE OUTER STEP LATE.  The top-drag
      !! path has no such lag.
      !!
      !! A CALL rather than an inline loop at the call site, deliberately:
      !! `engine_step_finalize` would have to walk `engine%state%...`
      !! inside a `do concurrent`, and `ocean_engine_t` is not a mapped
      !! object, so nvfortran emits a data clause for the whole engine and
      !! aborts with "partially present on the device".  Host-dereference
      !! at the call site, flat explicit-shape dummies here.  The
      !! escaping-actual pessimisation CLAUDE.md warns about does not
      !! apply: `engine_step_finalize` owns no `do concurrent` of its own.
      !!
      !! `u_*` is exactly zero on every uncovered column
      !! (`cavity_melt_columns_2d`), so the published field keeps
      !! `stress_shelf`'s "exactly zero off the cover" invariant and the
      !! disjoint-support argument in the module docstring still holds.
      integer, intent(in) :: nx, ny
         !! Extents of BOTH arrays.  The caller gates on the melt slot's
         !! `enable`, which is exactly when `ustar` is full size, so an
         !! `(1,1)` placeholder can never reach these dummies.
      real(wp), intent(in) :: rho0
         !! Reference density (kg/m^3) -- the single rho0 of record, by
         !! value from `ocean_surface_stress_t%rho0`.
      real(wp), intent(inout) :: stress_shelf(nx, ny)
         !! `ocean_surface_stress_t%stress_shelf` (N/m^2), overwritten.
      real(wp), intent(in) :: ustar(nx, ny)
         !! `ocean_cavity_flux_t%ustar` (m/s).

      integer :: i, j

      do concurrent(j=1:ny, i=1:nx)
         stress_shelf(i, j) = rho0*ustar(i, j)*ustar(i, j)
      end do
   end subroutine ocean_surface_stress_set_shelf_from_ustar