realistic_global
The realistic_global tasks in polaris.tasks.ocean.realistic_global use
realistic global ocean meshes, bathymetry and forcing. They fall into five
groups:
hydrography/woa23, a mesh-independent preprocessing task that builds a reusable hydrography product from the World Ocean Atlas 2023 on its native 0.25-degree latitude-longitude grid.forcing/jra55, a mesh-independent preprocessing task that builds a reusable wind-stress product from JRA55-do 10-m winds.init, which creates mesh-specific ocean initial conditions using that hydrography and forcing together with the culled mesh frome3sm/init.analysis_members, forward runs on realistic global meshes that exercise the analysis members: short runs in both MPAS-Ocean and Omega, and one-year Omega runs that write what the analysis tasks inpolaris.tasks.ocean.analysisread.restart, a short Omega run split across a restart, checking that the history output survives the restart intact.
Tasks are added to the ocean component by
polaris.tasks.ocean.realistic_global.add_realistic_global_tasks(),
which registers the woa23 and jra55 tasks, one init task per MPAS mesh,
an analysis_members task for each mesh in
its mesh_dict and each duration in
polaris.tasks.ocean.realistic_global.analysis_members.DURATIONS, and a
single restart task on QU.240km. Adding a new mesh to analysis_members
takes two things: an entry in mesh_dict giving the MPAS-Ocean and Omega
initial-condition IDs and the cell count, and a <mesh>.cfg file in the
analysis_members package setting the baroclinic and barotropic time steps
(dt and btr_dt), the length of the short run (short_run_duration),
the number of MOC latitude bins (moc_num_bins) and the momentum viscosities
(mom_del2 and mom_del4).
framework
The config options for these tasks are described in
realistic_global in the User’s Guide. The shared colormap
options for the viz step live in realistic_global.cfg, while the
analysis_members tasks add analysis_members.cfg.
forward
The class polaris.tasks.ocean.realistic_global.forward.Forward
is a shared polaris.ocean.model.OceanModelStep used by tasks in
this group. Unlike most Polaris forward steps, it does not build its own
mesh and initial condition; instead it downloads cached, model-specific files
from the realistic_global section of the Polaris input database.
Because the file layout differs between the two models, the input files are
added in setup() rather than __init__(), once config is available and
the target model is known:
For Omega, a single file is linked three times, as
mesh.nc,vert_coord.ncandinit.nc, because a single file contains the converted mesh, initial condition, and vertical coordinate from MPAS-Ocean.For MPAS-Ocean, a
zerovelfile is linked as bothmesh.ncandinit.nc, and thetime_integratortemplate replacement is rewritten fromSplitExplicitRK2to MPAS-Ocean’ssplit_explicit.
setup() also renders forward.yaml with the template replacements supplied
by the task, so the time step, run duration and output interval can be varied
per mesh.
The files the run writes are given as output_filenames, each of which is
declared as an output; the default is the single output.nc. A subclass
whose streams write one file per month, such as the 1_year
analysis_members forward step, passes the whole list.
The helper _make_stream_dirs() creates the directories Omega’s streams
write into, listed in stream_dirs: restart/ for the RestartWrite
stream, plus whatever a subclass appends. Omega does not create these
directories itself, so without them the restart write fails at the end of the
run. It is called from both setup() and runtime_setup() so the
directories exist whether or not setup and run happen in the same invocation.
MPAS-Ocean needs no equivalent because the MPAS framework creates stream
directories itself.
compute_cell_count() returns the cell count passed in by the task rather
than reading the mesh, since the mesh is not available at setup time.
viz
The class polaris.tasks.ocean.realistic_global.viz.Viz plots
global maps of each state variable at the start and end of the run, plus the
zonal and meridional wind stress from the initial condition. The list of
variables comes from the ocean component’s state_vars, with
normalVelocity replaced by kineticEnergyCell because the normal velocity
lives on edges and is not directly plottable as a cell field. Variables
missing from a given file are logged and skipped, so the step does not fail
when a model writes a different subset of fields.
analysis_members
The polaris.tasks.ocean.realistic_global.analysis_members.AnalysisMembers
task runs the ocean model with the global-statistics analysis member enabled
and plots the resulting time series. It sits at
spherical/realistic_global/<mesh>/analysis_members/<duration>, where the
duration is short or 1_year. The short task contains a forward step,
a global_stats step and a viz step. The 1_year task has no viz step,
since that step reads the single output.nc the short run writes. Only
forward runs by default.
Each task builds its own polaris.config.PolarisConfigParser from
realistic_global.cfg, analysis_members.cfg and <mesh>.cfg and shares it
with all its steps, so that a user editing the config file in the task work
directory affects the whole task.
forward
The class
polaris.tasks.ocean.realistic_global.analysis_members.forward.Forward
is a subclass of the shared framework
Forward, which supplies the cached initial condition and the shared
forward.yaml. Its setup() fills in the time steps, the short run’s
length and the number of MOC bins from the [analysis_members] config
options before the yaml files are added, and adds the mom_del2 and
mom_del4 config options as config_mom_del2 and config_mom_del4, which
are translated to Omega’s ViscDel2 and ViscDel4.
For the 1_year run, which supports only Omega, setup() also adds
monthly_output.yaml after the shared yaml file, so that it overrides it.
The fragment replaces the History stream with monthly snapshots in
output/, turns on the MonthlyAverages and MOC analysis groups, adds
kinetic energy and sea surface height to GlobalStats, and makes restarts
monthly. A stream’s options merge across yaml files, so the fragment has to
set the History stream’s FileFreq explicitly: the shared value of 9999
years would put every snapshot into one file. The step appends output to
stream_dirs, so that the directory exists before the run.
Every monthly file is declared as an output, so that a run whose analysis
groups did not write is reported as a missing output rather than found later
by the analysis. Omega names a time-mean file for the instant it was
finalized (E3SM-Project/Omega#554), so a run that starts on 0001-01-01
writes the files covering year 1 as 0001-02 through 0002-01;
_monthly_filenames() builds the names that way, and the snapshots, taken at
those instants, are named the same way.
global_stats
The class
polaris.tasks.ocean.realistic_global.analysis_members.stats_analysis.StatsAnalysis
plots, for each state variable, the minimum, maximum and mean over time along
with a shaded standard-deviation envelope, and a companion panel showing the
same quantities as anomalies relative to their initial values.
This step normalizes two differences between the models:
Output location. Omega writes the statistics to a separate
global_stats_1DayInstantsfile, whereas MPAS-Ocean writesglobal_stats.nc. The input file is therefore selected insetup(), once the model is known.Standard deviation. Omega writes the standard deviation directly in its
Rmsfield, while MPAS-Ocean writes a true root-mean-square, so the standard deviation is recovered as \(\sigma = \sqrt{\mathrm{rms}^2 - \mathrm{mean}^2}\).
restart
The polaris.tasks.ocean.realistic_global.restart.Restart task is a
regression test for
Omega #482. It runs the
same period twice, once in one go and once split across a restart, and checks
that the two histories agree.
The run lengths live as module-level constants in the task rather than in the config file, since they are chosen to make the failure visible – two history frames per segment, so that a segment that clobbered rather than appended would be obvious – rather than to be tuned by a user.
The per-mesh time steps and viscosity are module-level constants too. The
viscosity is passed to every step as a model config option rather than set in
the task’s forward.yaml, which only the segments of the restart chain read,
so that the full run and the chain run the same model.
restart_step
The class
polaris.tasks.ocean.realistic_global.restart.restart_step.RestartStep
extends the shared Forward step with what a segment of a restart chain
needs. setup() layers the task’s own forward.yaml on top of the shared
one; because model config data is processed in the order it was added, the
task’s overrides win. It also rejects any model other than Omega, since the
failure being tested cannot arise in MPAS-Ocean.
runtime_setup() creates the restart directory this segment writes into,
which Omega does not create itself, and, for a continuing segment, copies the
previous segment’s output.nc into place. Each segment gets a directory of
its own under the task’s restarts, named for the step, and a continuing
segment reads its predecessor’s. A single shared directory would mean a
segment’s own end-of-run restart replaced the pointer file it had just read
from, so re-running that step alone would silently continue from the wrong
time. The copy is what makes the test
meaningful: it reproduces what a continuation run finds on disk, a history
file that already holds the earlier frames. It is a copy rather than a
symlink so that appending to it cannot reach back into the previous segment’s
work directory.
The previous segment’s output is also added as an input file under the name
previous_output.nc, which both establishes the dependency between the two
steps and keeps the symlink out of the way of the copy.
the start time
The one subtlety in forward.yaml is that every step is given the same
StartTime, the start of the simulation, rather than the start of its own
segment. Omega measures the history time axis from the clock’s start time, so
a segment that moves the start time restarts that axis at zero, and its first
frame then collides with the first frame already in the file. That collision
is what silently overwrote the earlier frames in #482. On a Continue the
clock’s current time comes from the restart file instead, so the start time
does not need to move.
validate
The class
polaris.tasks.ocean.realistic_global.restart.validate.Validate
compares the history of the restart chain with that of the full run in two
ways. It reads the elapsed-time coordinate with xarray directly, rather
than through the Omega-to-MPAS-Ocean renaming in open_model_dataset(),
because that coordinate is the variable under test; it then compares the state
variables with polaris.validate.compare_variables() as any other
exact-restart test would. A chain that wrote fewer frames than the full run
is reported as the #482 failure specifically, since that is the shape the bug
takes.
hydrography/woa23
The polaris.tasks.ocean.realistic_global.hydrography.woa23.task.Woa23
task is the Polaris port of the WOA preprocessing part of the legacy Compass
utility/extrap_woa workflow.
The implementation is intentionally organized around reusable Polaris steps
rather than around the legacy multiprocessing workflow. One notable design
choice is that the task reuses the combined topography product from
e3sm/init rather than taking a raw topography filename as a task-specific
input.
cached topography dependency
polaris.tasks.ocean.realistic_global.hydrography.woa23.steps.get_woa23_steps()
internally creates a shared e3sm/init
polaris.tasks.e3sm.init.topo.combine.step.CombineStep
configured for a 0.25-degree lat-lon target grid. The
polaris.tasks.ocean.realistic_global.hydrography.woa23.task.Woa23
task adds this step with a symlink combine_topo.
Because CombineStep sets default_cached = True, the combine_topo step
is automatically treated as cached during setup — no explicit opt-in is
needed.
This keeps the expensive topography blending logic in one place and makes the
ocean hydrography preprocessing task consistent with the broader Polaris
approach to shared, cacheable preprocessing steps. See
Automatic cache use with default_cached and free_running_steps for a full description of the
default_cached / free_running_steps mechanism.
combine
The class
polaris.tasks.ocean.realistic_global.hydrography.woa23.combine.CombineStep
combines January and annual WOA23 temperature and salinity climatologies into
a single dataset. January values are used where they exist, and annual values
fill deeper levels where the monthly product is not available.
WOA23 supplies in-situ temperature and practical salinity, so this step uses
gsw to derive conservative temperature and absolute salinity for the
canonical woa_combined.nc product.
extrapolate
The class
polaris.tasks.ocean.realistic_global.hydrography.woa23.extrapolate.ExtrapolateStep
uses the cached combined-topography product on the WOA grid together with
woa_combined.nc to build a 3D ocean mask and then fill missing WOA values in
two stages:
Horizontal then vertical extrapolation within the ocean mask
Horizontal then vertical extrapolation into land and grounded-ice regions
The final output is woa23_decav_0.25_jan_extrap.nc.
viz
The class
polaris.tasks.ocean.realistic_global.hydrography.woa23.viz.Woa23VizStep
plots horizontal maps of the extrapolated temperature and salinity at the
depths given by the horizontal_plot_depths config option, along with
vertical sections through Filchner Trough and the Ross Ice Shelf cavity. It
is added with run_by_default=False.
forcing/jra55
The polaris.tasks.ocean.realistic_global.forcing.jra55.task.Jra55
task builds the reusable wind-stress product used by the init tasks. Its
shared steps come from
polaris.tasks.ocean.realistic_global.forcing.jra55.steps.get_jra55_steps().
stress
polaris.tasks.ocean.realistic_global.forcing.jra55.stress.Jra55StressStep
downloads the yearly JRA55-do uas/vas files through the
initial_condition_database mechanism, selects the configured month, and
computes the stress at every 3-hourly step before averaging. Averaging the
stress rather than the wind preserves the gust contribution, which is why the
3-hourly data is needed; the drag law is
wind_stress().
The time loop is chunked, since a month of 3-hourly TL319 winds is
248 x 320 x 640 per component.
The step is default_cached = True, with the product in the cache database,
so that the multi-GiB download happens only when the product is deliberately
regenerated. The standalone jra55 task marks the step free-running for
that purpose.
The product is deliberately not padded, in latitude or longitude. Bilinear
remapping is center-based for ESMF but corner-based for mbtempest, and padding
a lat-lon source so that either its corners or its centers reach the pole
aborts mbtempest; duplicating a longitude column makes the grid overlap itself
and breaks both map tools. The output is jra55_stress.nc.
viz
polaris.tasks.ocean.realistic_global.forcing.jra55.viz.Jra55VizStep
plots global maps of the stress components and magnitude plus a zonal-mean
taux curve, which is the diagnostic that confirms the bulk formula and air
density are right.
init
The init task family (whose steps live under
spherical/realistic_global/{mesh_name}/init) creates mesh-specific ocean
initial conditions using WOA23 hydrography and the culled mesh produced by
e3sm/init. One
polaris.tasks.ocean.realistic_global.init.task.RealisticGlobalInit
task is registered per MPAS mesh; the target ocean model is determined by the
[ocean] model config option at run time.
step dependency chain
polaris.tasks.ocean.realistic_global.init.steps.get_realistic_init_steps()
composes the full chain:
cull_topo (
CullTopoStep): reindexes remapped topography from the base mesh to the culled ocean mesh usingocean_map_culled_to_base.nc, producingtopography_culled.nc. The standard topography fields (seeTOPO_VARIABLES) are validated against a baseline when one is provided.woa23_map (
Woa23MapStep): apolaris.remap.MappingFileStepthat builds the bilinear mapping file from the 0.25-degree WOA23 lat-lon grid to the culled MPAS mesh. This is the only MPI step in the WOA23 chain (it runsmbtempestor ESMF). Its task count scales with the approximate culled ocean cell count via theremap_cells_per_taskandremap_min_cells_per_taskoptions in the[realistic_global_init]config section. It and jra55_map are thin subclasses ofLatLonMapStep, which links the source and culled mesh, sizes the task count and builds the weights; each subclass supplies only its source.remap_woa23 (
RemapWoa23Step): a serial step that applies the weights from woa23_map withncremap, remapping WOA23 conservative temperature and absolute salinity to the culled MPAS mesh and producingwoa23_on_mesh.nc. The remapper is retrieved from woa23_map through the step dependency mechanism, so it is resolved only after that step has run.jra55_map (
Jra55MapStep): the bilinear mapping file from the JRA55-do TL319 grid to the culled MPAS mesh, sized the same way as woa23_map. Bilinear rather than conservative, because the ocean responds to wind stress curl and first-order conservative remapping makes that curl grid-scale noise; pyremap’s moab path hard-codes--order 1.remap_jra55 (
RemapJra55Step): applies those weights withncremap, producingjra55_on_mesh.nc. The stress is remapped in Cartesian components (stress_to_cartesian()) and rotated back to zonal and meridional components at the cell centers (cartesian_to_stress()), since the east/north basis rotates with longitude. That also makes the result independent ofmap_tool: ESMF’s default pole handling averages the source’s outermost row, which would cancel a vector given in zonal and meridional components.ncremapwrites zero rather than a missing value to a cell the weights do not reach, socheck_no_missing_cells()reads each cell’s coverage (frac_b) from the mapping file and fails the step if any cell is not fully covered or has a non-finite stress.pstar_init (
RealisticPStarInitStep): subclass ofpolaris.ocean.vertical.pstar_init.PStarInitStepthat runs the fixed-point p-star iteration jointly with the vertical interpolation of the WOA23 tracers, writing a model-neutralpstar_init.ncwith converged geometric layer interfaces and CT/SA tracers. The target bathymetry is first clamped into the range of depths the reference grid can represent, and to no shallower thanmin_bottom_depthormin_vert_levelslayers. The column is anchored at the prescribed sea surface, sosshmatches its prescribed value (0 here) and any residual from partial-cell snapping goes intobottomDepthinstead. Isolated bathymetry holes, cells deeper than every ocean neighbor, are capped at their deepest neighbor’s level and re-solved viapolaris.ocean.vertical.bathymetry_holes.fill_max_level_holes().initial_state (
InitialStateStep): readspstar_init.ncand the model resolved from[ocean] modelto produce model-specific output files (init.ncfor both models;vert_coord.ncadditionally for Omega). Tracer fields are kept as CT/SA for Omega and converted to potential temperature / practical salinity for MPAS-Ocean; the conversion itself is the framework’s (see Initial state), with this step supplying the per-cell longitude and latitude from the culled mesh becausepstar_init.nchas no horizontal mesh fields. It also writesmesh.nc, adding the Coriolis fields viapolaris.coriolis.add_coriolis_to_dataset(). For Omega,write_horiz_mesh_dataset()merges in the cell-centered vector-reconstruction fields fromreconstruction_weights.nc, so the step links cull_mesh’sculled_ocean_reconstruction_weights.ncunder that name. The weights have to be the culled mesh’s, not the base mesh’s, since that is the mesh the initial condition is built on.forcing (
ForcingStep): writes the model-specificforcing.ncfromjra55_on_mesh.ncviapolaris.ocean.model.OceanIOStep.write_forcing_dataset(). Omega reads 1-D fields onNCells; MPAS-Ocean’s Registry declaresdimensions="nCells Time", so aTimedimension of one is added there.viz (
VizInitStep): visualizes and sanity-checks the initial condition, vertical-coordinate and forcing datasets (see below).
viz
The VizInitStep
step is a shared step that is only added to a task’s steps_to_run when
get_realistic_init_steps is called with include_viz=True (as the standalone
RealisticGlobalInit task does). Other consumers that reuse the init outputs
as dependencies leave it out of their run list so the plots are not
regenerated.
The step is model-agnostic. It reads through
open_model_dataset() — which maps
Omega variable names to their MPAS-Ocean equivalents — and
open_vert_coord_dataset(), so one
code path serves both models. The titles and labels, by contrast, use each
model’s own variable names (Temperature, BottomGeomDepth, SshCell, …
for Omega), and temperature and salinity also say which quantity the model
holds: conservative temperature and absolute salinity for Omega, potential
temperature and practical salinity for MPAS-Ocean. Output filenames keep the
MPAS-Ocean names for both models so that the two sets of plots line up.
Omega’s initial state has no SpecVol, which open_model_dataset needs to turn PseudoThickness
into a geometric layerThickness, so the step derives the specific volume from
the equation of state itself. It produces:
initial_state_summary.png: histograms of the initial condition (a port of Compass’plot_initial_statewithout its Haney-number panel). The prognostic layer-thickness panel shows each model’s native variable —layerThicknessfor MPAS-Ocean andPseudoThicknessfor Omega — read from the raw output file and, like the other 3D panels, masked below the seafloor.vertical_coordinate.png: the vertical-coordinate structure derived from the geometricrestingThicknessof the deepest column (there are norefMidDepth/refBottomDepthreference profiles in this workflow).global native-mesh maps (via
polaris.viz.plot_global_mpas_field()) of temperature and salinity at the depths listed in[realistic_global_init_viz] depths, plus surface and seafloor, andbottomDepth,ssh,maxLevelCelland column thickness. For Omega the more nativeSurfacePressureandBottomPressureare also plotted when present.vertical transects (via
mpas_toolscompute_transect/plot_transect) of temperature and salinity along each transect in[realistic_global_init_viz_transects]. Layer interfaces are not drawn, because the thin upper layers pack them into a band that hides the upper few hundred meters;vertical_coordinate.pngshows the layer structure.Omega only: a stratification check using the TEOS-10 in-situ
Density(global surface/seafloor maps and transects). Density is not plotted for MPAS-Ocean, whose equation of state differs and is not evaluated here.xdmf/init/and (Omega)xdmf/vert_coord/: XDMF/HDF5 exports for ParaView, produced withmpas_tools.viz.mpas_to_xdmf.MpasToXdmf. For Omega the native variable names are preserved and only the dimension names are renamed to their MPAS-Ocean equivalents, as required by the converter.
Colormaps come from the shared viz defaults in
polaris.viz.get_viz_defaults(), looked up by variable name, so a
variable gets the same colormap everywhere it is plotted; none are named in
the plotting code. Units come from the same defaults (Omega’s Density
falls back on density), except that MPAS-Ocean’s salinity is labeled PSU,
since it is practical rather than absolute salinity. The limits, by
contrast, are computed per plot from the data range and written into
[realistic_global_init_viz] just before each call. That is deliberate and
differs from the analysis_members viz step, which reads fixed limits from
realistic_global.cfg: fixed limits are what you want to compare runs or
times against each other, and the data range is what you want when the
question is whether a brand-new initial condition is sane. For a diverging
colormap the range is made symmetric about zero.