Skip to content

Add general realistic-global forward runs#643

Draft
xylar wants to merge 49 commits into
E3SM-Project:mainfrom
xylar:add-realistic-global-ocean-general-forward
Draft

Add general realistic-global forward runs#643
xylar wants to merge 49 commits into
E3SM-Project:mainfrom
xylar:add-realistic-global-ocean-general-forward

Conversation

@xylar

@xylar xylar commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

(Full description to come)

This PR extends #612 and generalizes #593 to support both initial conditions from the Polaris workflow and from the Polaris database.

It also provides support for future restart tests and dynamic adjustment workflows.

Checklist

  • User's Guide has been updated
  • Developer's Guide has been updated
  • API documentation in the Developer's Guide (api.md) has any new or modified class, method and/or functions listed
  • Documentation has been built locally and changes look as expected
  • Testing comment in the PR documents testing used to verify the changes
  • New tests have been added to a test suite

@xylar

xylar commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator Author

I have successfully run short (1-day) forward runs for the 240 km unified mesh for both MPAS-Ocean and Omega. Omega requires manually changing the time integrator to RK4.

@xylar
xylar force-pushed the add-realistic-global-ocean-general-forward branch 2 times, most recently from 1fe3f28 to 64a105c Compare July 5, 2026 15:46
@xylar
xylar force-pushed the add-realistic-global-ocean-general-forward branch 4 times, most recently from dffb364 to 4493593 Compare July 21, 2026 11:10
xylar and others added 22 commits July 21, 2026 06:18
Introduces docs/design_docs/global_ocean_init.md for the realistic global
ocean initialization workflow. Key changes from the earlier draft:

- Each task initializes the single model selected by `[ocean] model`; no
  single task produces both MPAS-Ocean and Omega outputs.
- The p-star iteration step is model-independent; running the task for
  different models on the same mesh and hydrography yields the same geometric
  vertical grid, enabling comparison (by running separately).
- Renames the intermediate step and its output from `shared_vert_coord` /
  `shared_vert_coord.nc` to `pstar_init` / `pstar_init.nc`; the
  implementing class is `RealisticPStarInitStep(PStarInitStep)`.
- Replaces all `ztilde_init.md` cross-references with `pstar_init.md`.
- Updates pstar_init.md with three cross-references to global_ocean_init.md
  and renames the planned realistic subclass to `RealisticPStarInitStep`.
- Adds global_ocean_init to docs/design_docs/index.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces docs/design_docs/global_ocean_dynamic_adjustment.md for the staged
global-ocean dynamic adjustment workflow. Key changes from the earlier draft:

- Each task targets exactly one ocean model, selected by `[ocean] model`; the
  workflow does not attempt to run both MPAS-Ocean and Omega in a single task.
- The requirement, algorithm design, implementation, and testing sections for
  "supports both models" are updated to "supports either model", reflecting
  that model-specific differences are isolated in the translation layer and
  each task instance runs for the configured model only.
- The summary and diagnostics descriptions are updated to match.
- Adds global_ocean_dynamic_adjustment to docs/design_docs/index.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces docs/design_docs/e3sm_init_component_inputs.md describing the
e3sm/init component_inputs task family. The document already uses a
model-gated product matrix (ocean_model = mpas-ocean | omega | none,
seaice_model = mpas-seaice | none) that naturally supports single-model
tasks. Two targeted updates reflect the single-model ocean init design:

- Summary: clarifies that the upstream ocean init and dynamic-adjustment
  tasks each target a single ocean model (selected by [ocean] model); the
  component-inputs workflow consumes that model's products and does not
  build initial conditions for multiple models in one task.
- Implementation: notes that the upstream ocean init output (step 2) and
  dynamic-adjustment restart (step 3) correspond to the single configured
  model.

Adds e3sm_init_component_inputs to docs/design_docs/index.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Set default_cached = True on CombineStep and ExtrapolateStep so that the
expensive WOA23 preprocessing steps are cached by default and not re-run
when re-running tasks that depend on them.

Refactor get_woa23_steps() to self-contain the topo combine step lookup
(removing the separate get_woa23_topography_step() helper) and add an
include_viz parameter so callers can opt into the visualization step.
The standalone Woa23 task keeps include_viz=True and marks combine and
extrapolate as free-running so the standalone task always runs them
rather than restoring from cache.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add polaris/tasks/ocean/realistic_global/init/CullTopoStep, which
reindexes remapped topography from the base mesh onto the culled ocean
mesh cells using the cell-index map produced by the upstream e3sm/init
CullMeshStep. Inputs are topography_remapped.nc (from RemapTopoStep)
and ocean_map_culled_to_base.nc (from CullMeshStep); output is
topography_culled.nc.

Also adds the package __init__.py files for
polaris/tasks/ocean/realistic_global/init/ and
tests/ocean/realistic_global/init/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add RemapWoa23Step, which uses pyremap to horizontally remap the
extrapolated WOA23 conservative temperature and absolute salinity
product from the 0.25-degree lat-lon grid to the culled MPAS ocean
mesh. The step scales its task count from the approximate mesh cell
count configured in [unified_mesh] approximate_cell_count. Output is
woa23_on_mesh.nc. Includes unit tests in
tests/ocean/realistic_global/init/test_remap_woa23.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add polaris/tasks/ocean/realistic_global/init/pstar_init.py with
RealisticPStarInitStep(PStarInitStep), which jointly initializes the
p-star vertical coordinate and WOA23-derived tracer fields for a global
MPAS mesh.

RealisticPStarInitStep subclasses PStarInitStep (from
polaris/ocean/vertical/pstar_init.py) and implements init_tracers() by
interpolating conservative temperature and absolute salinity from the
pre-remapped WOA23 product (woa23_on_mesh.nc) to the current p-star
midpoints at each fixed-point iteration. The run() method calls
run_pstar_init() and writes the model-neutral intermediate product
pstar_init.nc. The step is renamed from the earlier SharedVertCoordStep
design; the output file is renamed from shared_vert_coord.nc to
pstar_init.nc.

Includes unit tests in tests/ocean/realistic_global/init/test_pstar_init.py
covering _geom_z_bot_from_topo, _fill_tracer_columns constant/linear/valid-
level/deep-extrapolation behavior.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- No `model` constructor arg; reads `config.get('ocean', 'model')` at
  run time (concrete after Ocean.setup() resolves 'detect').
- Inputs: `pstar_init.nc` (was `shared_vert_coord.nc`) from
  `RealisticPStarInitStep`, plus culled mesh and graph files.
- Calls `add_output_files_for_ocean_model_input` to register
  model-gated outputs (vert_coord.nc for Omega, culled_graph.info for
  MPAS-Ocean).
- Tracer conversion (CT/SA -> pot-T/prac-S via GSW) still only applies
  for MPAS-Ocean.
- Tests drop the obsolete `model`-constructor and `_effective_model()`
  tests; keep helper-function tests for `_add_layer_thickness`,
  `_add_normal_velocity`, and `_convert_tracers_mpas_ocean`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add get_realistic_init_steps(component, mesh_name) in steps.py that
  composes the full shared-step dependency chain:
  get_cull_topo_steps -> WOA23 steps -> CullTopoStep -> RemapWoa23Step
  -> RealisticPStarInitStep -> InitialStateStep; all new steps use
  get_or_create_shared_step for reuse across tasks on the same mesh
- Add RealisticGlobalInit task in task.py with subdir
  'spherical/realistic_global/init/{mesh_name}' (no /{model} segment);
  calls set_shared_config and adds all returned steps
- Add add_realistic_global_init_tasks in tasks.py: one task per mesh,
  no model loop; target model resolved from config at run time
- Register add_realistic_global_init_tasks in realistic_global/__init__.py
- Add realistic_global_init.cfg placeholder config
- Add approximate_cell_count to all four unified mesh cfg files for use
  by RemapWoa23Step._estimate_cell_count
- Update tests to expect one task per mesh with pstar_init/initial_state
  step names; drop per-model and shared-step-reuse tests
- Remove stale get_woa23_topography_step from api.md; add init package
  entries; update realistic_global.md to describe the init step chain

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Carolyn Begeman <cbegeman@lanl.gov>
- Add VizInitStep in viz.py, a model-agnostic OceanIOStep that reads
  through open_model_dataset / open_vert_coord_dataset (mapping Omega
  names to MPAS-Ocean and reconstructing geometric layerThickness) so
  the maps and transects work for both models. It produces:
  * initial_state_summary.png: histograms ported from Compass'
    plot_initial_state with the Haney (rx1Edge) panel dropped; the
    prognostic-thickness panel shows each model's native variable
    (layerThickness for MPAS-Ocean, PseudoThickness for Omega) read
    from the raw output file
  * vertical_coordinate.png: vertical structure from the geometric
    restingThickness of the deepest column (no ref* profiles exist in
    this workflow)
  * global native-mesh maps (plot_global_mpas_field) of temperature and
    salinity at configured depths plus surface/seafloor, and
    bottomDepth, ssh, maxLevelCell and column thickness; for Omega also
    native surfacePressure/bottomPressure and a TEOS-10 in-situ Density
    (stratification) check
  * multi-basin vertical transects (compute_transect / plot_transect)
    of temperature and salinity (and Density for Omega)
  * xdmf/init and (Omega) xdmf/vert_coord exports via MpasToXdmf,
    keeping Omega variable names but renaming dimensions to MPAS-Ocean
- Create the viz step as a shared step in get_realistic_init_steps via
  get_or_create_shared_step; add an include_viz flag so it is only
  added to steps_to_run when requested (keeps it out of other
  consumers' run lists)
- Pass include_viz=True from the standalone RealisticGlobalInit task so
  viz runs by default there
- Add [realistic_global_init_viz] and
  [realistic_global_init_viz_transects] config sections (projection,
  central_longitude, depths, and four default basin transects)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add viz.VizInitStep entries (setup, run) to the ocean developer API
- Describe the viz step in the developer guide: its shared/include_viz
  behavior, model-agnostic reads, and the full set of outputs (summary
  and vertical-coordinate figures, global maps, transects, Omega-only
  pressure and stratification plots, and ParaView XDMF exports)
- Add a user-guide init section covering the visualization outputs and
  the [realistic_global_init_viz] / [realistic_global_init_viz_transects]
  config options

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The realistic_global/init p-star coordinate produced a spurious non-zero
ssh (down to -1590 m) and a runaway BottomPressure (up to ~2.2e8 Pa,
a pseudo-depth of ~21,700 m) in ~420 deep cells.  The 80layerE3SMv1
reference grid defines pseudo-thicknesses whose deepest interface is
~5549.7 m (pseudo), but run_pstar_init targets the geometric seafloor
depth from base_elevation (up to 6761 m).  Since geometric thickness =
RhoSw * specVol * pseudoThickness and deep water is denser than RhoSw,
the deepest representable geometric column is only ~5171-5475 m; where
base_elevation is deeper, the geometric column saturates, the fixed-point
iteration diverges, and the residual shows up as negative ssh and an
inflated BottomPressure inconsistent with the clipped PseudoThickness.

Clamp the target seafloor into the representable range [-D_max, -D_min]
before calling run_pstar_init, so the existing loop converges to a
resting state (ssh = 0), BottomPressure = P_max (no runaway), and
bottomDepth = the clamped geometric thickness:

- Add RealisticPStarInitStep._clamp_to_representable_depth(), which builds
  one saturated full-grid column (inherited base _build_pstar_coord_ds +
  this step's init_tracers), and the pure helper _clamp_geom_z_bot(),
  which computes D_max (geometric thickness of the saturated column) and
  D_min (the deeper of min_bottom_depth and the depth needed for
  min_vert_levels layers, capped at D_max) and clamps geom_z_bot.
- ocean.cfg: add neutral default min_bottom_depth = 0.0 (clamp disabled;
  min_vert_levels stays 1) so other p-star users (e.g. horiz_press_grad)
  and the base run_pstar_init tests are unaffected.
- realistic_global_init.cfg: set min_bottom_depth = 10.0 and
  min_vert_levels = 3 (three layers is the practical minimum for
  baroclinic dynamics).
- Add unit tests for _clamp_geom_z_bot (deep/shallow/in-range clamp,
  min_bottom_depth floor, density-reduced D_max, attribute passthrough).

No changes to the shared base run_pstar_init (pstar_init.py) or pstar.py,
so horiz_press_grad's p-star path is untouched.  Verified end-to-end on
icos240 (Omega): max|ssh| = 1.9e-8 m (was -1590 m), BottomPressure <=
P_max for all cells, bottomDepth in [10, 5475] m, maxLevelCell >= 5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The p-star initialization path bypassed init_vertical_coord/add_1d_grid,
so pstar_init.nc lacked the 1D reference vertical coordinate variables
(refBottomDepth, refZMid, refTopDepth, refInterfaces).  MPAS-Ocean
requires refBottomDepth as an input on its mesh stream.

Reuse the existing add_1d_grid() in the base PStarInitStep output
assembly so every p-star init step produces these fields, and expose a
shared REF_COORD_VARS constant for downstream routing/exclusion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The base p-star init now emits refBottomDepth/refZMid/refTopDepth/
refInterfaces for MPAS-Ocean.  Omega uses RefPseudoThickness instead and
ignores these, so drop them from the Omega initial state file to keep
its output minimal and model-appropriate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Assert the base PStarInitStep output contains refBottomDepth and its
companions, verify they are kept for MPAS-Ocean but dropped for Omega in
write_initial_state_dataset, and guard that REF_COORD_VARS stays in sync
with add_1d_grid.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reorder the realistic_global init work-directory layout from
spherical/realistic_global/init/{mesh_name}/... to
spherical/realistic_global/{mesh_name}/init/... so that a mesh's
tasks are grouped under the mesh rather than scattered under sibling
family directories as more task families (forward, dynamic_adjustment,
restart, ...) are added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror the reordered work-directory layout
spherical/realistic_global/{mesh_name}/init/... in the task and step
subdir assertions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reflect the reordered work-directory layout
spherical/realistic_global/{mesh_name}/init in the developers guide
and the pstar_init design doc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The realistic_global init step left mesh.nc as a bare symlink to the
culled mesh, which lacks the Coriolis parameter (fCell/fEdge/fVertex).
The ocean model requires these in its horizontal mesh stream, so Omega
aborted reading FVertex/FEdge/FCell from HorzMeshIn.

Add the Coriolis fields via the shared add_coriolis_to_dataset helper
(as every other ocean task does) and write a real mesh.nc, reading the
culled mesh from a renamed culled_mesh.nc input. A [coriolis] section
with type = spherical is added to the init cfg so a realistic Earth mesh
gets f = 2 * omega * sin(latitude).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ocean-model resource and time-step sizing for the realistic_global meshes
should reflect the ocean + sea-ice culled domain that MPAS-Ocean actually
runs, not the full mesh (which includes finer land/river refinement).

Add ``get_unified_ocean_finest_cell_width`` which returns the finest ocean
background cell width, ignoring the land and river-channel widths, and add a
``culled_ocean_cell_count`` option to the unified mesh configs.  For the
constant 30 km ``u.oi30.lr10`` mesh this is ~470000 cells (~0.71 * 6e8 /
30**2); the variable meshes carry a commented placeholder to be filled in
from a trial run, falling back to ``approximate_cell_count`` until then.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The private ``_estimate_cell_count()`` helper lived in the WOA23 remapping
step, but sizing a step from the mesh name is generally useful and will be
needed by other steps (e.g. the forward run) as well.

Move it into a new ``realistic_global/mesh_info.py`` as public
``estimate_cell_count()``, and add ``estimate_ocean_cell_count()``, which
reads the ``culled_ocean_cell_count`` option added to the unified mesh
configs (falling back to the full-mesh estimate when it is unset).

WOA23 is remapped onto the culled ocean mesh, so ``remap_woa23`` now sizes
itself from the ocean-culled count rather than the full unified-mesh
estimate, which includes the finer land and river-channel refinement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
xylar and others added 27 commits July 21, 2026 06:18
Only u.oi30.lr10 had culled_ocean_cell_count set, so the other three meshes
fell back to approximate_cell_count, which includes the finer land and
river-channel refinement that is culled away before the ocean model runs.
That badly over-estimated the ocean domain and led to unreasonable node
counts for realistic_global init steps.

Set the ocean-culled counts for the remaining meshes: 7500 for
u.oi240.lr240, 4000000 for u.oi6to18.lr6to10 and 800000 for
u.oi.so12to30.lr10.  Add a test that every unified mesh defines the option
and that it is smaller than the full-mesh count.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the standard topography variables from the remapped topography
dataset as validation variables for topography_culled.nc so that the
culled fields are compared against a baseline when one is provided.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a package to hold per-mesh config overrides for realistic_global
ocean tasks, one optional <mesh_name>.cfg per mesh.

These are deliberately separate from the per-mesh configs in
polaris.mesh.spherical.unified: those belong to the mesh component and
describe the mesh itself, while options specific to the ocean (vertical
grid, ocean-culled cell count) belong here in the ocean component. The
two are joined by the mesh name.

Meshes with no config file here simply use the task defaults.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds per-mesh configs giving qu240km, icos240km and u.oi240.lr240 a
16-level tanh_dz vertical grid with a 3000 m bottom depth, matching the
Compass configuration for these meshes. The shallow bottom depth is
intentional: these coarse meshes are for cheap smoke-testing rather than
physical realism.

Only the five options that differ are overridden; coord_type,
min_vert_levels, min_bottom_depth and the rest are inherited from
realistic_global_init.cfg.

Wires the overrides into _get_init_config(), which now takes mesh_name
and applies them last so they take precedence over the task defaults.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ocean-culled cell count is an ocean concept that was living in the
mesh component's unified-mesh configs. It moves to the new per-mesh
configs under realistic_global/mesh_configs, in a new
[realistic_global_mesh] section, so the mesh component no longer carries
options specific to the ocean. approximate_cell_count stays behind: it
is a property of the mesh itself.

estimate_ocean_cell_count() also gains an optional config argument. Its
caller in remap_woa23 has the task config in hand, so passing it means a
user override of culled_ocean_cell_count now affects task sizing;
reading the package file directly ignored user overrides. Without a
config the function still falls back to the package file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Covers the vertical-grid override on the 240 km meshes, that unset
options are still inherited from realistic_global_init.cfg, and that a
mesh with no per-mesh config keeps the defaults.

Also checks that every config file in the package is named after a mesh
that exists. A misnamed file would otherwise be silently ignored, since
a mesh without a config file is a valid and common case.

Adds coverage for the new config argument to estimate_ocean_cell_count.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a users guide section describing the mesh_configs override
mechanism, which meshes currently override what, and the rule for
deciding whether a per-mesh option belongs to the mesh component or the
ocean component. Adds the new module and mesh_info to the ocean API
docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pyremap's Remapper defaults to use_tmp=True, which puts the SCRIP files
and the MOAB .h5m files in a TemporaryDirectory() under /tmp.  On
Chrysalis (and many other HPC machines) /tmp is a node-local disk, so
the MPI tasks running mbtempest on other nodes cannot see the files and
abort during the parallel read.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
RemapWoa23Step reimplemented mapping-file creation that
polaris.remap.MappingFileStep already provides, and it ran both an MPI
operation (mbtempest) and a serial one (ncremap) in a single step, which
will not schedule well under task parallelism.

Add Woa23MapStep, a MappingFileStep subclass that builds the bilinear
weights and carries the ntasks sizing from the estimated ocean cell
count.  RemapWoa23Step becomes a serial step that depends on it and
applies the weights with ncremap.  Getting the remapper from
MappingFileStep also picks up parallel_executable from config, which
pyremap's default of bare 'mpirun' does not honor.

The bilinear method is now passed explicitly rather than relying on
pyremap's default.  The woa23_on_mesh.nc output is unchanged, so
pstar_init needs no modification.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
MappingFileStep reads [mapping] map_tool from polaris/remap/mapping.cfg,
so add that package config and drop the duplicated map_tool option from
[realistic_global_init].

Raise remap_cells_per_task from 500 to 10000 and
remap_min_cells_per_task from 5000 to 50000.  The old values asked for
926 MPI tasks for u.oi30.lr10 (462,924 culled ocean cells), which only
ran at 512 because constrain_resources clipped it to the allocation.

Timings on Chrysalis show that more tasks is actively worse here: the
mbtempest weight computation takes only 5-8 s at any task count, while
the serial mbpart partitioning that precedes it gets more expensive as
the number of partitions grows.  512 tasks took 97 s, 128 took 52 s, 93
took 45 s and 64 took 44 s.  The new values give ntasks=47 and
min_tasks=9 for that mesh, halving the core reservation relative to
5000 cells per task at no measured cost in runtime.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cover that Woa23MapStep uses bilinear remapping and does not put SCRIP
files in /tmp, that its task counts follow the culled ocean cell count
and stay modest for a coarse mesh, and that RemapWoa23Step is serial and
reaches the remapper through the step dependency mechanism.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pyremap partitions the SCRIP files with 'mbpart <ntasks>' before running
mbtempest, and mbpart rejects a request for one partition:

    ** Please specify #parts = 1 to be greater than 1.

A serial test run confirmed this fails outright.  With the cell counts
per task raised in the previous commit, icos480km resolved to ntasks=1
and icos240km/icos120km to min_tasks=1, all of which would have hit
this.  Floor both at 2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add docs/design_docs/global_ocean_forward.md describing a reusable
framework for realistic global-ocean forward runs with MPAS-Ocean or
Omega: a Forward step built on OceanModelStep, two initial-condition
sources (Polaris database file and the realistic_global/init outputs),
a model-agnostic per-run settings structure mapped to the configured
model in one place, and restart in/out seams that leave clean paths to
restart tests and the staged dynamic-adjustment workflow. Register the
doc in the design-docs index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the model-agnostic pieces the forward step will use, not yet wired
to a step:

- ForwardStage (forward/stage.py): a dataclass of per-run settings
  (durations, time step, damping, restart-in fields) with from_config
  and a single model_replacements mapping onto MPAS-Ocean/Omega template
  values, plus bottom_drag_options for MPAS-Ocean Rayleigh damping.
- InitialCondition sources (forward/initial_condition.py): a base class
  with StepInitialCondition (links mesh/vert_coord/init/graph from an
  upstream realistic_global/init step) and DatabaseInitialCondition
  (constructs a database filename; not yet registered).
- realistic_global_forward.cfg: the [realistic_global_forward] section.

Co-locate the DDDD_HH:MM:SS string helpers so a function and its inverse
live together: move get_time_interval_string into polaris.mpas.time next
to the new duration_to_seconds parser, and re-export both from
polaris.mpas and polaris.ocean.model so existing imports are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add Forward(OceanModelStep), which wires its model inputs from an
InitialCondition and renders forward.yaml from a ForwardStage in a single
dynamic_model_config mapping site.  It sizes resources from the exact
nCells in the mesh at run time (config approx_cell_count fallback during
setup) and derives the mesh minimum resolution from dcEdge for per-km
time-step scaling.

forward.yaml sets the neutral time block plus the MPAS-Ocean restart/
output streams and Omega History/RestartWrite streams; input streams come
from the shared model_inputs.yaml.

Restrict Omega to the RK4 (RungeKutta4) time integrator, raising a clear
error if split_explicit_ab2 is selected for Omega, since the two are not
equivalent and an Omega split-explicit integrator is still in
development.  Add restart_freq to the stage's model replacements for the
Omega restart stream.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a runnable simple-forward task: RealisticGlobalForward (task.py) runs
the shared realistic_global/init steps for a mesh, then a single Forward
step wired to their outputs via StepInitialCondition, with its run
settings coming from [realistic_global_forward].  add_realistic_global_
forward_tasks (tasks.py) registers one task per base and unified mesh,
mirroring the init factory, and add_realistic_global_tasks now calls it.

Make the Forward step's stage optional: when a task does not pass an
explicit ForwardStage (the simple-forward case), the step builds one from
config in dynamic_model_config so user config overrides take effect;
dynamic adjustment will pass explicit per-stage settings instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add tests/ocean/realistic_global/test_forward.py covering the ForwardStage
mapping (per-km vs explicit time step, RK4->RungeKutta4, the Omega
split_explicit_ab2 error, output/restart freq, Rayleigh damping options,
restart_interval defaulting) and the InitialCondition sources
(StepInitialCondition input/graph wiring and DatabaseInitialCondition
filename construction for both models).

Extend test_tasks.py to confirm a RealisticGlobalForward task is
registered per mesh and that its forward step and shared init chain land
in the expected work dirs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The forward stage previously set config_dt from dt_per_km (the long
baroclinic step) for every integrator.  That is only correct for split
time stepping (split_explicit_ab2), which subcycles the barotropic mode
on config_btr_dt.  Non-split integrators (RK4, and Omega's RungeKutta4 --
the only Omega option) have no barotropic subcycling and must advance on
the short barotropic step, so they now use btr_dt_per_km for config_dt.
Without this, Omega runs would take the far-too-large baroclinic step and
go unstable.

Add a test covering the non-split short-step behavior and clarify the
dt_per_km / btr_dt_per_km cfg comments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The forward step previously read the mesh minimum resolution from a
[realistic_global_forward] min_res config option (default 30 km), an
unsafe guess, and otherwise from the mesh file at run time.  Instead,
min_res is now a property of the InitialCondition:

- StepInitialCondition takes min_res, which the task looks up from the
  existing mesh definitions -- get_base_mesh_definition().min_res for base
  meshes and get_unified_finest_cell_width() for unified meshes.
- DatabaseInitialCondition takes min_res as a required argument, since a
  database mesh has no init pipeline to derive it from and it cannot be
  guessed.

Remove the min_res config option and the run-time dcEdge read.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
An unsupported Omega time integrator was raised at setup time, which
prevented setting up the step and changing the config option before
running.  Thread at_setup through model_replacements so the neutral
integrator name is left in place at setup and validated only at run time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The forward step sized setup-time resources from a fixed
[realistic_global_forward] approx_cell_count = 100000 whenever the mesh
file did not yet exist.  That one number was used for every mesh, so it
badly over-provisioned coarse meshes (e.g. u.oi240.lr240 targeted 500 /
required 48 tasks instead of the mesh-appropriate count), which in turn
oversized the batch job.

Instead, derive the estimate per mesh, mirroring the earlier min_res
refactor:

- Move the base-vs-unified mesh-property lookups into a shared
  realistic_global/mesh_info.py, exposing public estimate_cell_count()
  (6e8 / min_res**2 for base meshes; [unified_mesh] approximate_cell_count
  for unified meshes) and min_res_for_mesh().  remap_woa23 and forward/task
  both use it.
- Carry approx_cell_count on the InitialCondition, set from the mesh
  definition at task registration.
- Forward.compute_cell_count() falls back to that estimate (and raises a
  clear error if it is unknown) instead of the config option.
- Remove the now-unused approx_cell_count config option.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The forward step lived at .../{mesh}/forward/forward: the per-mesh
"forward" group directory (holding the task at forward/task) plus a step
also named "forward".  The doubled name was confusing and said nothing
about what the run is.

Rename the step (and its work-dir leaf) to "short", so the brief 1-day
smoke run lands at .../{mesh}/forward/short, beside .../{mesh}/forward/task
and consistent with the init layout (init/task + init/<step>).  This
leaves room for other forward runs under the same forward/ group later.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The realistic_global forward step sized its resources and time step from the
full unified-mesh estimate, which includes finer land/river refinement that is
culled away before MPAS-Ocean runs.  For u.oi30.lr10 that made the setup-time
estimate (2,000,000 cells, and a 10 km min resolution from the land/river
width) far larger than the ocean domain the model actually runs.

Add ``estimate_ocean_cell_count``, which reads the new ``culled_ocean_cell_count``
option (falling back to the full estimate when unset), and use it for the
forward step.  Switch ``min_res_for_mesh`` to the ocean-only finest cell width
so unified meshes report their ocean resolution (30 km for u.oi30.lr10) rather
than the land/river refinement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The forward task built its shared config from the task's own
realistic_global_forward.cfg only, so per-mesh overrides in the
mesh_configs package were ignored.  Call
add_realistic_global_mesh_config() after the task config is added, as
the init steps already do.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@xylar
xylar force-pushed the add-realistic-global-ocean-general-forward branch from 4493593 to 3c30fa5 Compare July 21, 2026 11:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant