Global Ocean Forward Runs

Creation date: 2026/07/03

Contributors: Xylar Asay-Davis, Claude

Summary

This design document describes a new Polaris capability: a reusable framework for realistic, global-ocean forward runs with either MPAS-Ocean or Omega, selected by the [ocean] model config option. The framework belongs to the ocean/spherical/realistic_global family, alongside the initial-condition workflow described in global_ocean_init.md. Where that workflow produces a model initial condition, this framework runs the configured model forward from such a condition.

The intent is to replace and generalize the forward-run machinery of the legacy Compass global_ocean test family (compass/ocean/tests/global_ocean/forward.py and its test cases) using Polaris’ shared-step design and its existing ocean model abstraction. The single deliverable of the first phase is a reusable forward step plus one runnable “simple forward” task (a run whose duration and cadence come from config). The framework is deliberately shaped so that the two other forward run types from Compass — a restart test and the staged dynamic adjustment described in global_ocean_dynamic_adjustment.md — are straightforward to build on top of it later.

That first phase has landed, and so has more than it promised. Two runnable task families exist rather than one — forward on a mesh the init workflow builds, and cached_forward on an initial condition downloaded from the Polaris input-file database — and both run the same physics, with comparing MPAS-Ocean against Omega left as an opt-in config. Restart chaining landed as well and dynamic adjustment is built on it without modifying the forward step, exactly as this document called for, as are a restart task and a one-year Omega run that writes what the ocean analysis reads. Now that Omega has a split-explicit time stepper, it is the default for both models, and every short run also comes in an RK4 variant.

A forward run must be able to obtain its model inputs (horizontal mesh, vertical coordinate, initial state, surface forcing, and graph) from either of two sources: a file staged in the Polaris input-file database and referenced by name, or the outputs of the upstream realistic_global/init steps. It must express the per-run controls — run duration, output and restart cadence, time step, damping, and the mixing and parameterization settings that vary with the mesh — in a model-agnostic form and translate them, in a single place, to the configuration options of the configured model. It must also write restart files, so that a sequence of forward runs can be chained.

The primary software challenge is not the forward integration itself, which Polaris’ OceanModelStep already supports, but the workflow abstractions around it: a clean separation between where inputs come from, what a run should do, and how that maps onto MPAS-Ocean versus Omega. This design is successful if Polaris can run a realistic global forward simulation for either model from either initial-condition source, if the per-run settings are described once and mapped to the model in one isolated location, and if restart and staged dynamic-adjustment workflows can reuse the same forward step without modifying it.

Requirements

Requirement: A reusable forward-run capability runs a realistic global ocean simulation

Date last modified: 2026/07/03

Contributors: Xylar Asay-Davis, Claude

Polaris shall provide a reusable capability to run a realistic, global-ocean simulation forward in time for the ocean model selected by the [ocean] model config option (resolved to omega or mpas-ocean during component setup). A given task targets exactly one model.

The capability shall include at least one runnable task: a simple forward run whose duration and output cadence are set through config options rather than Python source code. The task shall run to completion on at least one coarse global mesh and produce a model output file.

The capability shall size its own parallel resources from the mesh so that the same task can run on meshes of very different sizes without per-mesh code changes.

Requirement: Forward runs can start from a database initial condition or from the init workflow

Date last modified: 2026/10/21

Contributors: Xylar Asay-Davis, Claude

A forward run shall be able to obtain the model inputs it consumes (horizontal mesh, vertical coordinate for Omega, initial state, surface forcing, and, for MPAS-Ocean, a graph partition file) from either of two sources:

  1. A file staged in the Polaris input-file database and referenced by name.

  2. The outputs of the upstream realistic_global/init steps for the same mesh and configured model.

The choice of source shall be a property of how the task is assembled, not a branch inside the forward step.

Requirement: Comparing MPAS-Ocean with Omega is opt-in

Date last modified: 2026/10/21

Contributors: Xylar Asay-Davis, Claude

Every forward run shall be as close to production as each model allows, whatever its initial condition. MPAS-Ocean therefore runs physics Omega lacks, and a user who wants to compare the two models shall be able to turn that physics off through config, without any code or task knowing that a comparison is being made.

The cached initial conditions are E3SM’s, for E3SM meshes, converted for Omega. They let Omega run realistic global configurations, and be compared with MPAS-Ocean, before Polaris could build its own initial conditions. The tasks that use them are temporary.

Requirement: Per-run settings are described in a common, model-agnostic form

Date last modified: 2026/10/15

Contributors: Xylar Asay-Davis, Claude

The controls that define a single forward run — run duration, output interval, restart interval, time-step selection, and damping strength — shall be described in a single, model-agnostic representation. Polaris shall translate that representation into the configuration options appropriate for the configured model in exactly one place, so that MPAS-Ocean and Omega differences are isolated rather than scattered through the workflow.

Run, output, and restart durations shall be expressible as duration strings compatible with the staged-adjustment schedule format so that a simple forward run and a dynamic-adjustment stage can share the same representation. The time step shall be derivable from mesh resolution (a value per kilometer scaled by the mesh’s minimum resolution) while still allowing an explicit override.

Split-explicit time stepping shall be the default for both models, since RK4 is too expensive for long runs. Every short forward task shall also be available with RK4 in both models, for occasional testing.

Requirement: The framework composes into restart and dynamic-adjustment workflows

Date last modified: 2026/07/03

Contributors: Xylar Asay-Davis, Claude

The forward step shall be general enough that a restart test and the staged dynamic-adjustment workflow can be built from it without changing the step itself. In particular, the step shall be able to write a restart file at a configured cadence, and it shall accept the settings needed to continue from an existing restart (a restart flag, a start time, and a restart input file).

The framework itself is not required to implement the restart test or dynamic adjustment in this phase; it is required only to leave clean seams for them, consistent with global_ocean_dynamic_adjustment.md, which decomposes dynamic adjustment into one restart-chained forward step per stage.

Requirement: Forward steps produce inspectable outputs and support basic validation

Date last modified: 2026/07/03

Contributors: Xylar Asay-Davis, Claude

A forward step shall produce clearly named, inspectable outputs — at least a model output file and, when a restart cadence is configured, restart files — that users and developers can examine for sanity checking and debugging.

The step shall support basic validation of a small set of output variables against a baseline and, where applicable, conservation-property checks, so that regressions such as numerical blow-up or broken restart chaining can be detected.

Requirement: Physics and mixing options vary with the mesh

Date last modified: 2026/10/17

Contributors: Xylar Asay-Davis, Claude

The horizontal mixing coefficients, the eddy parameterizations, and the way mixing is scaled across a variable-resolution mesh all depend on the mesh a forward run uses. A single, mesh-independent set of model config options is not sufficient: a 240 km mesh needs a Leith closure and a reference cell width, while a 6-to-18 km mesh needs a much smaller biharmonic viscosity, mesh-density scaling, and no eddy parameterization at all.

These settings shall therefore be Polaris config options rather than fixed values in the model-config template, with defaults in the task’s own config file and per-mesh overrides in mesh_configs/<mesh_name>.cfg, using the mechanism that already carries the vertical grid and the ocean-culled cell count. Each mesh’s values shall follow how that mesh is configured in E3SM, rather than being chosen to make MPAS-Ocean and Omega agree; the two models are expected to diverge where one lacks a parameterization the other has.

Options that both models support shall be expressed once, in model-neutral form, and translated automatically. Options with no counterpart in the other model shall be emitted only for the model that has them, which leaves the other model at its own default. An option shall therefore move to the model-neutral form as soon as both models have it.

Algorithm Design

Algorithm Design: A reusable forward-run capability runs a realistic global ocean simulation

Date last modified: 2026/07/03

Contributors: Xylar Asay-Davis, Claude

The forward run should be a single reusable step built on Polaris’ existing ocean model abstraction (OceanModelStep), which already selects between MPAS-Ocean namelists/streams and an Omega YAML configuration, partitions the graph for MPAS-Ocean, and computes the number of MPI tasks dynamically from a mesh cell count. The closest existing precedent is the spherical convergence forward (polaris.ocean.convergence.forward.ConvergenceForward and its spherical subclass), which renders a task-level YAML template with run duration, output cadence, and time step drawn from a config section and overrides a compute_cell_count method to size resources. The realistic global forward step follows the same shape, but sources its cell count from the actual global mesh rather than a convergence resolution heuristic.

Resource sizing should use the exact number of cells from the mesh when that file is available (at run time) and fall back to a configured approximate cell count during setup, before the initial condition has been produced. This mirrors the approach used in the legacy Compass forward step.

Algorithm Design: Forward runs can start from a database initial condition or from the init workflow

Date last modified: 2026/07/03

Contributors: Xylar Asay-Davis, Claude

The two initial-condition sources should be captured by a small abstraction — an “initial condition” object — whose responsibility is to add the required input files to a forward step and to point the step at the correct graph file. The forward step holds one such object and defers all questions of provenance to it.

Two concrete implementations are needed:

  1. A source backed by the upstream realistic_global/init steps, which links the mesh, vertical coordinate, initial state, forcing and graph files from the initialization steps’ work directories. This is the source used by the first runnable task, since those outputs already exist in the same run.

  2. A source backed by the Polaris input-file database, which constructs the input filename from the mesh, the configured model, and the equation of state, and registers it as a database input.

Because the required filenames and the model-specific handling of the vertical coordinate and graph depend on the configured model, the source applies its input files when the model is known (during step setup) rather than at construction time.

Algorithm Design: Comparing MPAS-Ocean with Omega is opt-in

Date last modified: 2026/10/21

Contributors: Xylar Asay-Davis, Claude

The settings that separate a production run from a comparison are already config options (see “Physics and mixing options vary with the mesh”), so a comparison is just a set of values for them, which a user layers on like any other config. The forward package ships those values as a reference file that Polaris never reads on its own, so that the list is written down, documented and tested in one place. It describes what Omega lacks today and should shrink as Omega gains capabilities.

Algorithm Design: Per-run settings are described in a common, model-agnostic form

Date last modified: 2026/10/17

Contributors: Xylar Asay-Davis, Claude

A single, model-agnostic settings structure should describe one forward run: its name, run duration, output interval, restart interval, time integrator, an optional explicit time step (barotropic and baroclinic), per-kilometer time-step scalings, a damping strength, and the fields needed to continue from a restart (a restart flag, a start time, and a restart input file). A simple forward run fills this structure from a config section; a future dynamic-adjustment workflow fills a list of them from a schedule file. This is the “common internal representation” called for in global_ocean_dynamic_adjustment.md.

The mapping from this structure onto model configuration should happen in one place. Durations are formatted as model duration strings; the time integrator name is translated where the models differ (for example RK4 to RungeKutta4). Controls that exist only for one model, such as Rayleigh damping for MPAS-Ocean, are emitted only under that model’s section of the template. Where a model lacks a direct equivalent (for example Omega damping), the mapping is the natural place to apply the closest available control as the capability matures.

Algorithm Design: The framework composes into restart and dynamic-adjustment workflows

Date last modified: 2026/07/03

Contributors: Xylar Asay-Davis, Claude

Restart chaining should be expressed through the same settings structure and the same template. Writing a restart is a matter of the restart interval; continuing from a restart is a matter of the restart flag, start time, and a restart input file. A restart test then becomes two forward steps — a full run that writes a restart partway through and a restart run that resumes from it — compared for bit-for-bit agreement. A dynamic-adjustment workflow becomes an ordered list of settings structures, each producing a restart consumed by the next, exactly as laid out in global_ocean_dynamic_adjustment.md.

The forward step does not need to know which of these workflows it participates in; it only needs to honor the settings it is given. Keeping start and stop times derived from cumulative durations, as in Compass, lets the higher-level workflow compute restart filenames deterministically.

Algorithm Design: Forward steps produce inspectable outputs and support basic validation

Date last modified: 2026/07/03

Contributors: Xylar Asay-Davis, Claude

The output and restart streams should be declared in the task-level YAML template so that the set of written variables is easy to inspect and change. The step should register its output file as a Polaris output with an optional list of variables to validate against a baseline and optional conservation-property checks, reusing the validation and property-check machinery already provided by the ocean model step base class.

Algorithm Design: Physics and mixing options vary with the mesh

Date last modified: 2026/10/17

Contributors: Xylar Asay-Davis, Claude

The per-mesh settings should join the same ForwardStage structure that already carries the per-run settings, so that a forward run continues to have a single description of what it should do. They are read from the same [realistic_global_forward] section, which means a per-mesh config file overrides them by the ordinary config-precedence rules and a user can override them again in a user config file.

A coefficient and its enable flag should be a single Polaris option: blank turns the term off, a value turns it on with that coefficient. This follows the convention the section already uses for the explicit time step and the damping coefficient, and it removes the possibility of a config that enables a term without a coefficient or sets a coefficient that is never used.

Where the model offers several independent booleans for what is really one choice — MPAS-Ocean’s reference-cell-width and scale-with-mesh flags for horizontal mixing — Polaris should expose a single option whose value names the scaling, so that the combinations that are not meaningful cannot be expressed.

The stage should sort the resulting model options into buckets: those both models support, emitted in neutral form and translated by the existing MPAS-Ocean-to-Omega map, and those only one model has, emitted for that model alone. “Both models support” means a one-to-one translation exists, not just that each model has something in the same role: two different boundary-layer schemes would each go in their own model’s bucket. Most model-specific options are MPAS-Ocean’s; Omega’s bucket holds its horizontal-mixing scaling switch, which is not a translation of MPAS-Ocean’s. Gent-McWilliams and Redi stay in the MPAS-Ocean bucket for good, since Omega has neither and is not expected to gain them. Other options move to the neutral bucket as Omega gains them, as KPP has.

Implementation

Implementation: A reusable forward-run capability runs a realistic global ocean simulation

Date last modified: 2026/08/11

Contributors: Xylar Asay-Davis, Claude

The framework lives under polaris/tasks/ocean/realistic_global/forward/. The forward step, Forward, subclasses polaris.ocean.model.OceanModelStep and is modeled closely on polaris.ocean.convergence.forward.ConvergenceForward. Its constructor adds the shared double-precision output YAML (polaris.ocean.config/output.yaml), stores an initial-condition object and a settings structure, and registers the output file. It does not read config in its constructor, because the target model is not known until setup.

Resource sizing overrides compute_cell_count to return the exact nCells read from the mesh input file when it exists, and otherwise an approx_cell_count; the base class turns this into ntasks/min_tasks using the existing goal_cells_per_core/max_cells_per_core options. The approximate count is a property of the initial condition rather than a config option, since it is the initial condition that knows which mesh it is for: a step-backed source takes it from mesh_info.estimate_ocean_cell_count, and a database-backed one from the count recorded for that cached mesh.

The runnable task is RealisticGlobalForward, added once per supported mesh by a factory add_realistic_global_forward_tasks, following the structure of the init factory in polaris/tasks/ocean/realistic_global/init/tasks.py. Its work directory is mesh-first, spherical/realistic_global/{mesh_name}/{subdir_name}, matching the init layout, with subdir_name distinguishing forward from cached_forward. The factory is registered from polaris/tasks/ocean/realistic_global/__init__.py.

The task holds three steps rather than one. The forward run itself is named short rather than forward, because that is what it is: a brief run that checks the model is stable on this mesh and initial condition, not a simulation worth interpreting. Two diagnostic steps follow it, neither run by default: global_stats plots time series of the run’s global statistics, and viz plots global maps of its state.

Implementation: Forward runs can start from a database initial condition or from the init workflow

Date last modified: 2026/08/11

Contributors: Xylar Asay-Davis, Claude

The initial-condition abstraction lives in forward/initial_condition.py as an InitialCondition base class with two subclasses. StepInitialCondition wraps the initial_state step returned by get_realistic_init_steps (in polaris/tasks/ocean/realistic_global/init/steps.py) and, during the forward step’s setup, calls the mixin helpers add_horiz_mesh_input_file, add_vert_coord_input_file, and add_init_input_file with work_dir_target pointing into that step’s work directory, and sets the forward step’s graph_target to the init step’s exported graph file. The vertical-coordinate input and the graph are handled correctly per model by the existing OceanModelFilesMixin and OceanModelStep machinery: for MPAS-Ocean the graph is linked and partitioned; for Omega the separate vertical-coordinate file is used and the graph is not partitioned.

DatabaseInitialCondition constructs the input filename from the mesh name, the configured model, and the equation of state, and registers it with add_init_input_file(database=..., target=...). It is now wired into the cached_forward tasks described above. The MPAS-Ocean graph question was settled in favor of downloading a prebuilt graph partition file alongside the initial condition rather than building one from the mesh; Omega partitions internally and needs neither.

Two things the design did not anticipate fell out of this abstraction once surface forcing existed. The initial condition also answers where the wind stress lives, through get_forcing_filename(): a source that stages a forcing file of its own names it, and a source whose stress travels inside the initial-condition file names that file instead, so the forcing streams are pointed at the right place without the forward step knowing which case it is in. And StepInitialCondition links the initial state only when the run actually reads one, since a stage continuing from a restart does not — a detail that belongs to the source rather than to the step for the same reason.

Implementation: Comparing MPAS-Ocean with Omega is opt-in

Date last modified: 2026/10/23

Contributors: Xylar Asay-Davis, Claude

compare_with_omega.cfg in the forward package turns off use_GM and use_Redi and sets pressure_gradient_type = pressure_and_zmid, the ordinary counterpart to Omega’s Centered. A user passes it, or its lines, with polaris setup -f, or sets the same options in a step’s forward.cfg before running; the stage is rebuilt from config at run time, so either works. The User’s Guide includes the file verbatim. use_KPP and use_submesoscale were on the list until Omega gained them.

add_realistic_global_cached_forward_tasks registers one task per entry of forward/tasks.py’s CACHED_MESHES, with a subdir_name of cached_forward. Each entry gives the MPAS-Ocean and Omega initial-condition IDs on the Polaris server, the minimum resolution and the cell count. The two meshes are E3SM’s QU240 and EC30to60E2r2, called e3sm-qu240 and e3sm-ec30to60e2r2 here so that they are lower case like the meshes Polaris builds and cannot be mistaken for them; their initial conditions were converted from E3SM’s by utils/omega/convert_mpaso_ic_to_omega.py and uploaded, and the files on the server keep E3SM’s names, which each entry gives as database_name. A cached mesh’s mesh_configs/<mesh_name>.cfg gives its split and RK4 time steps, its run duration and the momentum viscosities the one-year Omega runs settled on, since none of these follows from a mesh Polaris did not build. Its physics is the default, as for every other mesh.

The cached tasks are the only members of this family cheap enough for a PR suite: both variants of ocean/spherical/realistic_global/e3sm-qu240/cached_forward are in omega_pr, and the split_explicit one is in mpaso_pr. Nothing built on the init chain belongs in a suite, since that chain is hours of preprocessing before the model starts.

cached_forward is temporary: now that Polaris builds realistic initial conditions, the cached tasks will be removed.

Implementation: Per-run settings are described in a common, model-agnostic form

Date last modified: 2026/10/17

Contributors: Xylar Asay-Davis, Claude

The settings structure is a small dataclass, ForwardStage, in forward/stage.py, with a class method that builds one from a [realistic_global_forward] config section. Its fields cover the run duration, output interval, restart interval, statistics interval, the two time integrators, optional explicit dt/btr_dt, dt_per_km/btr_dt_per_km, a damping (Rayleigh) coefficient, the mixing and parameterization options described further below, and the restart fields (do_restart, start_time, restart_in, restart_out). Durations are duration strings of the form DDDD_HH:MM:SS, matching the schedule format in global_ocean_dynamic_adjustment.md.

The time integrator turned out to be the one setting that cannot be shared, so there are two fields rather than one: mpaso_time_integrator, defaulting to split_explicit_ab2, and omega_time_integrator, defaulting to split_explicit (Omega’s SplitExplicitRK2). Omega’s default was RK4 until it had a split time stepper. The time step follows from that choice rather than being chosen separately: a split integrator advances on the long baroclinic step and subcycles the barotropic mode, and a non-split integrator has to advance on the short barotropic step.

Every short task is registered twice, as <mesh>/<forward|cached_forward>/split_explicit and <mesh>/<forward|cached_forward>/rk4, each holding its own steps. The rk4 variant adds rk4.cfg, which sets both integrators to RK4. A mesh config can need a split step and an RK4 step at once, so the RK4 step has an explicit option of its own, rk4_dt, which falls back to btr_dt_per_km like the barotropic step.

The mapping onto model configuration happens only in the forward step’s dynamic_model_config, which renders forward/forward.yaml with template replacements derived from the stage. Durations are formatted with polaris.ocean.model.get_time_interval_string; where the time step is not given explicitly it is computed as the per-kilometer value times the mesh minimum resolution. The time-integrator name is mapped to Omega where needed, exactly as ConvergenceForward does. The template’s model-neutral ocean: block is auto-translated to Omega through the existing mpaso_to_omega.yaml map, while Rayleigh damping appears under the mpas-ocean: section. The barotropic time step moved to the neutral block once Omega had a split time stepper. The restart controls did gain an Omega counterpart, in restart_streams.yaml rather than in the main template; see below.

Where a model cannot honor a setting, the mapping raises rather than dropping it: an omega_time_integrator Omega does not support, and a non-blank damping under Omega, which has no Rayleigh damping at all (Omega#495). Both raise at run time rather than at setup, so that a task can be set up and its config changed before it is run. Reporting success on a run that quietly dropped the damping it was asked for is the failure mode this avoids.

def dynamic_model_config(self, at_setup):
    super().dynamic_model_config(at_setup=at_setup)
    replacements = self.stage.model_replacements(self.config, self.min_res)
    self.add_yaml_file(self.package, self.yaml_filename,
                       template_replacements=replacements)

Implementation: The framework composes into restart and dynamic-adjustment workflows

Date last modified: 2026/10/21

Contributors: Xylar Asay-Davis, Claude

The restart stream is declared in restart_streams.yaml, added only for a stage that is part of a restart chain, and the model-neutral time block carries the start time and restart flag. The simple forward task leaves the restart fields unset, so it starts from the initial condition and writes a single restart at the end of the run.

Dynamic adjustment is built on exactly this and needed no change to the forward step: it builds a list of ForwardStages from a per-mesh YAML schedule and sets each one’s restart_in and restart_out so that consecutive stages share a restarts directory. For MPAS-Ocean the restart stream is both an input and an output stream, so one filename_template serves both directions and the read side is just config_do_restart and config_start_time; the start time is stated explicitly rather than using a restart-pointer file. Omega decides whether to read a restart from its StartType and finds it through a pointer file. The Omega block of restart_streams.yaml requests the RestartRead stream, and ForwardStage.restart_stream_replacements fills in the start type and the directories: each stage writes its restart and pointer into a directory under restarts named for its restart time, and a restarting stage reads its predecessor’s. A shared directory would let a stage’s own restart replace the pointer it had just read. ForwardStage.restart_files gives the restart each model reads and writes, MPAS-Ocean’s in the shared directory and Omega’s in the stage’s own, and the forward step declares them as inputs and outputs.

Two more tasks are built on the forward step without changing it. The restart task takes the shape of CIME’s ERS test, so that Omega’s restarts are tested here the way E3SM will test them: a full_run writes a restart partway through, a restart_run continues from it to the end, and the two histories are compared. It reads that restart by name rather than through restart_in, since the full run writes another at its end. The restart run appends to a copy of the full run’s history up to the restart, which the full run writes as a second history stream with an end time, so the task is also a regression test for Omega #482, where appending to such a file overwrote the earlier frames. The analysis_output/1_year task runs Omega for a year on every mesh and writes the monthly means, overturning streamfunction and daily global statistics the ocean analysis reads. It adds a second yaml file for the monthly streams and declares every monthly file as an output, so a year whose analysis groups did not write fails in Polaris rather than in the analysis.

Implementation: Forward steps produce inspectable outputs and support basic validation

Date last modified: 2026/09/24

Contributors: Xylar Asay-Davis, Claude

The 3-D output stream is declared in forward/forward.yaml — an MPAS-Ocean output stream and the corresponding Omega History IOStream — and the restart streams in restart_streams.yaml. The Forward step registers its output file through add_output_file with an optional validate_vars list (temperature, salinity, layerThickness, normalVelocity) and optional check_properties, reusing the baseline-comparison and conservation checks already implemented in OceanModelStep.

The output stream carries more than the prognostic state: kinetic energy and relative vorticity for MPAS-Ocean, and for Omega the AuxiliaryState, SshCell and Eos groups, which add the vorticity, divergence and del2 terms, the free surface and the specific volume and buoyancy frequency. The extra fields cost file size and are worth it, because a run that goes wrong can otherwise be seen to have gone wrong but not diagnosed, and these runs exist partly to be compared against each other. Density is the one field in that stream with a switch of its own (output_density), because it is 3-D and roughly 13% of the output volume, which starts to matter once a workflow writes the stream many times over.

Global statistics are the other output, and the more useful one. Both models write a time series of the global minimum, maximum, mean and RMS of the state variables — MPAS-Ocean through its globalStats analysis member and Omega through its GlobalStats analysis group, plus the global CFL number for MPAS-Ocean — on a stats_interval cadence deliberately separate from the 3-D output_interval. They are a handful of scalars, so they can be written far more often than 3-D fields, which is what makes an excursion within a run visible rather than only at its end. Two model differences are handled once, in ForwardStage.stats_filename, so that everything reading a stage’s statistics asks the same question the same way: Omega treats the configured name as a prefix and appends its analysis period and the kind of output with no .nc extension, and Omega’s statistics are taken as instantaneous snapshots rather than temporal reductions, both because a snapshot’s name carries no averaging period and because an averaging period would constrain the restart interval. Each model’s statistics names are built from the field and the statistic by polaris.ocean.global_stats_names rather than mapped pair by pair in mpaso_to_omega.yaml, which no longer lists them.

Surface forcing is no longer out of scope, and it is not optional either. Every realistic global forward run is forced by the time-invariant JRA55-do wind stress the init workflow produces: forcing.yaml is always added, and there is no config option to run without it. forcing_streams.yaml points each model’s input stream at whichever file the initial condition names. There is still no surface restoring and there are no thermal or freshwater fluxes, so the tracers spin down from the initial condition while the momentum input holds up a circulation.

Two diagnostic steps, neither run by default, read these outputs: global_stats (StatsAnalysis) plots the statistics as time series with a standard-deviation envelope and an anomaly panel, and viz plots global maps of each state variable at the start and end of the run along with the wind stress that forced it. global_stats plots the fields named by [realistic_global_forward_stats] fields, or every field the run wrote statistics for when that is left empty; the option came with the step from the analysis_members task this framework replaced, where it was [analysis_members] fields.

Implementation: Physics and mixing options vary with the mesh

Date last modified: 2026/10/23

Contributors: Xylar Asay-Davis, Claude

forward.cfg gains the mixing and parameterization options: mom_del2, mom_del4, tracer_del2 and tracer_del4 (blank meaning off), mom_del4_div_factor, use_Leith_del2, hmix_scaling with hmix_ref_cell_width, use_GM with GM_closure and GM_constant_kappa, use_Redi, use_KPP, use_submesoscale, pressure_gradient_type, use_frazil_ice_formation and output_density. The list grew past what this section first named once the forward tasks had to reproduce E3SM’s configuration rather than a Compass baseline: the defaults are now E3SM’s, and comparing with Omega turns some of them off (see “Comparing MPAS-Ocean with Omega is opt-in” above). Several are off in MPAS-Ocean itself, so leaving them alone would not have given E3SM’s configuration either.

hmix_scaling has two values, none and ref_cell_width, not the three this section anticipated. MPAS-Ocean’s third combination — scaleWithMesh with use_ref_cell_width false — scales by the legacy meshDensity field, which every E3SM v4 mesh writes as uniformly 1.0, so that branch silently applies no scaling at all. Offering it would only let a user ask for scaling and get none. The two flags MPAS-Ocean does need are set together by ref_cell_width, since config_hmix_use_ref_cell_width is read only inside if (config_hmix_scaleWithMesh) and setting the first alone has the same silent failure.

Omega scales its coefficients too, but from the global median cell width (2*sqrt(areaCell/pi) over every cell) rather than a reference width, with the same per-edge width and powers as MPAS-Ocean. Scaling is the default in both, and so is Omega’s reference: a blank hmix_ref_cell_width is the median, which Forward.dynamic_model_config computes from the mesh and gives MPAS-Ocean as config_hmix_ref_cell_width. A mesh whose coefficients come from E3SM states its finest resolution, which E3SM’s per-grid values are referenced to, and for Omega horiz_mixing_options then multiplies the coefficients by W_med/hmix_ref_cell_width for del2 and by its cube for del4. So hmix_scaling is not a one-to-one translation: MPAS-Ocean’s two flags stay MPAS-Ocean-only, and Omega gets MeshScalingEnable from a new omega_physics_options bucket, always stated. W_med depends on the culled mesh, which an init-chain task does not have at setup, so it is computed at run time and logged with the coefficients and the range of scaling factors Omega should report.

Settings that should not vary are pinned in forward.yaml rather than made options, purely so that the two models agree: horizontal tracer advection order 3, implicit constant bottom drag at 1e-3, the CVMix convection, shear-mixing and KPP parameters, and config_Redi_min_layers_diag_terms = 0, which computes the Redi diagnostic terms in the top layers the Registry default skips. Where the two models’ defaults differ, the MPAS-Ocean default wins, which turns KPP’s Langmuir turbulence off for Omega. Omega’s advection order and bottom drag defaults have since come to match MPAS-Ocean’s; stating them keeps the two from drifting apart again.

ForwardStage gains the corresponding fields and four methods beside the existing bottom_drag_options: horiz_mixing_options returns the neutral del2/del4 options for momentum and tracers and the biharmonic divergence factor, vert_mixing_options the neutral KPP switch, submesoscale_options the neutral submesoscale switch, and mpaso_physics_options the MPAS-Ocean-only options — the Leith closure, horizontal-mixing scaling, Gent-McWilliams, Redi, the pressure-gradient formulation and frazil ice. Forward.dynamic_model_config adds the first three with config_model='ocean', so OceanModelStep.map_yaml_options translates them for Omega, and the fourth with config_model='mpas-ocean'. KPP and the submesoscale parameterization began in the fourth bucket and moved once Omega gained them. Left there, KPP never reached Omega, which ran KPP, its default, in tasks that had turned it off.

Per-mesh overrides live in mesh_configs/<mesh_name>.cfg under the same [realistic_global_forward] section, applied by add_realistic_global_mesh_config after the task config so that they take precedence. The values are ported from the per-mesh namelist.split_explicit_ab2 files of the corresponding Compass meshes: qu for u-oi30-lr10, rrs6to18 for u-oi6to18-lr6to10, and so12to30 for u-oi-so12to30-lr10. The three 240 km meshes (u-oi240-lr240, qu240km and icos240km, which are qualitatively the same mesh and share a block) follow E3SM’s oQU240 instead: a biharmonic momentum viscosity of 2.0e14 m^4/s alone, referenced to 240 km, and a 1 h / 3 min split time step. Compass’s qu240 coefficients were never scaled there, and at 240 km they are almost no viscosity; scaled from the shared 30 km reference, they would be about 500 times too large.

Two exceptions to that port are deliberate. The Compass so12to30 time step is not carried over: it is far more conservative than its resolution alone justifies, almost certainly because the Compass mesh has ice-shelf cavities, and u-oi-so12to30-lr10 is for now a proof of concept for regional refinement rather than a mesh run in earnest. And Compass’s run durations and debug-tracer settings are test-harness details that Polaris expresses through its own config options.

Because the neutral bucket can carry tracer diffusivities, the MPAS-Ocean-to-Omega map also gains the two entries it was missing, config_tracer_del2 to EddyDiff2 and config_tracer_del4 to EddyDiff4; the corresponding enable flags were already mapped, so without these a tracer diffusivity set from Polaris would have been dropped for Omega with only a warning. It gains config_mom_del4_div_factor to DivFactor as well, which is what lets the divergence factor be neutral.

Testing

Testing and Validation: A reusable forward-run capability runs a realistic global ocean simulation

Date last modified: 2026/10/21

Contributors: Xylar Asay-Davis, Claude

A polaris setup smoke test on a coarse global mesh, run for both [ocean] model = mpas-ocean and omega, should confirm that the forward work directory contains the expected input symlinks and that the generated namelist and streams (MPAS-Ocean) or omega.yml (Omega) contain the expected run duration, cadence, time step, and time integrator, without running the model. Where a model build is available, a short end-to-end run on a coarse mesh should confirm that the output file and a restart file are written.

What landed is tests/ocean/realistic_global/test_forward.py, which renders the model config for both models and checks it rather than requiring a setup, plus test_tasks.py, which checks that a task is registered per mesh with the step list this design calls for. Resource sizing is covered by tests that compute_cell_count falls back to the initial condition’s estimate before the mesh exists, and raises rather than guessing when there is no estimate. The end-to-end run is the cached e3sm-qu240 task in the two PR suites.

Testing and Validation: Forward runs can start from a database initial condition or from the init workflow

Date last modified: 2026/08/11

Contributors: Xylar Asay-Davis, Claude

Unit tests confirm that StepInitialCondition registers its inputs and sets the graph target from the init step’s work directory, that it skips the initial state on a stage continuing from a restart, and that it requires a forcing step and wires the forcing file; and that DatabaseInitialCondition constructs the expected database filename and target for each configured model, raising when an Omega run has no Omega initial-condition ID. A further pair of tests checks the seam between the two: that the forcing streams point at the staged file for one source and at the initial condition itself for the other.

Testing and Validation: Comparing MPAS-Ocean with Omega is opt-in

Date last modified: 2026/10/21

Contributors: Xylar Asay-Davis, Claude

Unit tests confirm that every cached mesh has a per-mesh config, that the cached tasks run the same physics as every other forward run, that the model config a cached task renders is what those options imply, that compare_with_omega.cfg layered on as a user config turns off what Omega lacks and names only real options, and that the task’s config carries what the database source needs — the equation of state above all, since a cached run never sets the init workflow’s config up at all and still has to name the equation of state to find the right file.

The end-to-end check is the PR suites, which run the cached e3sm-qu240 task on every pull request: split-explicit in both models, and RK4 in Omega.

Testing and Validation: Per-run settings are described in a common, model-agnostic form

Date last modified: 2026/10/17

Contributors: Xylar Asay-Davis, Claude

Unit tests should cover building a ForwardStage from config and its mapping onto template replacements: correct duration strings, the time-integrator translation for Omega, the derivation of the time step from dt_per_km and the mesh minimum resolution versus an explicit override (rk4_dt for RK4), and the presence of Rayleigh damping only for MPAS-Ocean.

Testing and Validation: The framework composes into restart and dynamic-adjustment workflows

Date last modified: 2026/10/04

Contributors: Xylar Asay-Davis, Claude

Tests confirm that the restart-in settings render the expected restart flag and start time, that restart_stream_replacements gives a first stage Omega’s StartUp and a restarting one Continue with its predecessor’s restart directory, that restart_streams.yaml requests Omega’s RestartRead stream through a pointer file, and that restart_files names each model’s restarts. Which files a step declares is checked from the dynamic-adjustment side, where the chain is built: that a stage in a chain declares both ends of it and that a lone stage declares neither.

Testing and Validation: Forward steps produce inspectable outputs and support basic validation

Date last modified: 2026/08/11

Contributors: Xylar Asay-Davis, Claude

Tests should confirm that the output file is registered with the expected validation variables and that, when property checks are requested, they are wired to the existing conservation checks. Baseline comparison of the validation variables should be exercised through the standard Polaris validation path once a baseline is available.

Two classes of test landed that this section did not anticipate, both guarding against a failure that is silent by construction. An option in the neutral ocean: block with no entry in mpaso_to_omega.yaml only warns, so a check that every neutral option in forward.yaml, in forcing.yaml and in the horizontal-mixing bucket has an Omega counterpart is what keeps the two models from quietly running different physics. And the statistics filename moves for Omega but not for MPAS-Ocean, and a name that is not found reads as a stage that wrote no statistics, so tests pin the filename each model actually writes, that it follows the statistics interval for Omega, and that Omega’s statistics are snapshots rather than reductions.

Testing and Validation: Physics and mixing options vary with the mesh

Date last modified: 2026/10/17

Contributors: Xylar Asay-Davis, Claude

Unit tests should confirm that ForwardStage.from_config reads the new options, that a blank coefficient turns its term off while a value turns it on, and that the neutral and MPAS-Ocean-only buckets contain the options they are supposed to. Because the coefficients are what keep a run stable, a test should also confirm that each per-mesh config produces the expected values, and that the three qualitatively identical 240 km meshes agree with one another so they cannot drift apart unnoticed.

Two further tests guard against failures that are silent by construction. One fails if an option in the MPAS-Ocean-only bucket has an Omega counterpart in the map, which is how KPP was left behind. It cannot see an Omega option that is on by default and not yet mapped. The other requires every mesh that scales its coefficients to reference its own finest resolution, which the 240 km meshes did not.

The end-to-end check is that a short forward run on each mesh completes without producing NaNs, which is the failure mode these options exist to prevent.