component_inputs
The polaris.tasks.e3sm.init.component_inputs package stages the input files
an E3SM run needs from one unified mesh. See
Component Input Tasks for what it produces and how to run it.
Import direction
This package lives in e3sm/init and imports from polaris.tasks.ocean,
while polaris.tasks.ocean.realistic_global imports from
polaris.tasks.e3sm.init.topo.cull. That is acyclic only in one direction,
and it works because polaris.tasks fully imports
polaris.tasks.e3sm.init before any add_*_tasks function runs.
Nothing under polaris.tasks.ocean may import this package. Reversing
the direction is the kind of change a well-meaning refactor makes, and it
fails as an import error that is hard to place.
Steps
get_component_inputs_steps(mesh_name, target) follows the same pattern as
get_cull_topo_steps and get_realistic_dynamic_adjustment_steps: every step
comes from Component.get_or_create_shared_step, so the three tasks that
share most of these steps get the same instances.
It builds only what target needs. Asking for seaice does not create
the dynamic-adjustment steps at all, so a sea-ice task never waits for a model
run whose output it does not use. Each task includes all the upstream steps
it needs, from the base mesh onward, so it can be set up on its own.
Step |
Reads |
Writes |
|---|---|---|
|
|
|
|
the cull step’s three SCRIP files |
|
|
the initial state’s |
|
|
the final adjustment stage’s restart, once that stage’s check has passed |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
the QU60km-to-mesh mapping file |
|
|
|
|
the above, plus the cull step’s |
|
The sea-ice partitioning is two steps rather than one because building the
remapping weights is an MPI job sized for the mapping tool, while using them
is serial. SeaicePartitionMapStep descends from
polaris.remap.MappingFileStep; SeaiceGraphPartitionStep depends
on it and reads the weights off its remapper, since the mapping file’s path is
not known until it has run. This is the same division realistic_global uses
between Woa23MapStep and RemapWoa23Step.
Work directories are e3sm/init/<mesh>/component_inputs/<step_name>, and the
tasks are at e3sm/init/<mesh>/component_inputs/tasks/{ocean,seaice,all}.
Putting the tasks under tasks/ keeps a task subdirectory from colliding with
a step subdirectory, which is a real risk when three tasks share most of their
steps.
The land mesh has no step of its own. Unlike the SCRIP files it carries no
mesh name to restamp, and unlike the ocean mesh it needs no vertical
coordinate, so there is nothing to compute between the cull step and the
staged tree; ComponentInputsAssembleStep declares it directly.
What is not recomputed
The cull step already writes the SCRIP descriptions, the ocean graph and the culled-to-base index maps, and the initial-state step already writes every vertical-coordinate field. Several steps that were computations in Compass are therefore staging or inversion here:
BaseMeshStepinverts the existing forward maps rather than recomputing them. They came from a nearest-element query far more expensive than a scatter, and inverting is what guarantees the two directions agree.ScripSteponly restampsmesh_name, usingncattedrather than an xarray round-trip. These areNETCDF3_64BIT_DATAfiles the cull step already found prohibitively slow to write directly.OceanMeshStepselects rather than computes; onlyzMidandrefLayerThicknessare built here.
What comes from the framework
The graph partitioning, the general inputdata paths and the mechanics of
building the staged tree are in polaris.e3sm.init, so other workflows that
stage files for E3SM can use them (see Framework). Both
partition steps descend from GraphPartitionStep and read their options from
[component_inputs]; ComponentInputsAssembleStep descends from
AssembleStep and supplies its own README. What stays here is particular to
unified meshes: their short names, the shared mesh directory, the SCRIP and
culled-mesh regions, and which products each target stages.
Leaf modules
names.py, maps.py and models.py open no file and construct no step, so
the naming rules, the index inversion and the model gate are all unit testable
without a work directory.
names.py— the staged paths that follow from a unified mesh’s short name, andocean_grid_name, which gives E3SM’s name for the ocean and sea-ice grid that the framework’s paths take.maps.py—map_base_to_culledandbase_to_culled_maps. Both check that every forward-map value is a valid base index and that no base index appears twice; a violation means a stale file from a different mesh, which would otherwise scatter into a plausible-looking wrong answer.models.py— the ocean model gate, which each ocean staging step calls at the start of setup. It reads the model the ocean component was configured with, so the staged files always match the model the dynamic adjustment ran. It exists even though only MPAS-Ocean is supported, so that adding Omega means filling in a gate that is already there rather than adding model selection to steps that assumed MPAS-Ocean. Sea ice needs no gate: which task is set up decides whether sea-ice files are staged.
The MOC masks borrow behavior without borrowing a component
MocMasksStep builds the one mask file MPAS-Ocean reads at run time. The
masks themselves come from the mesh component’s
polaris.tasks.mesh.spherical.feature_masks.compute.ComputeFeatureMasksStep,
and the two MOC-specific pieces — the mocBasinsAndTransects filename and the
southern-boundary transects appended to the masks — come from
polaris.tasks.mesh.spherical.feature_masks.moc, which the ocean
feature-mask step uses as well.
Subclassing the ocean feature-mask step would have been the obvious route
and does not work. That class inherits OceanIOStep to translate to and from
Omega’s variable names, and OceanIOStep.process_inputs_and_outputs reads
[ocean] model. A step owned by e3sm/init has no [ocean] section — every
config belongs to exactly one component — so setup fails with
NoSectionError. Overriding the two I/O hooks is not enough either, since
[ocean] model is read in four places along that inheritance path.
So the MOC-specific pieces live in a module that does not depend on the ocean
model, where any component can call them. This step and the ocean one then
share them without e3sm/init depending on the ocean component.
Sea-ice does not touch the ocean
Compass copied fCell, fEdge and fVertex out of an ocean restart and
partitioned that restart’s mesh. Both are removed here: the Coriolis fields
are computed from latitude with add_spherical_coriolis, and the partitioning
reads SeaiceMeshStep’s output.
add_spherical_coriolis is called directly rather than through
add_coriolis_to_dataset. The latter reads [coriolis] type, a config
section that has nothing to do with sea ice, and its default of zero would
give the wrong Coriolis fields without any error.
Staging happens only in the assembly step
Product steps write only their own outputs; ComponentInputsAssembleStep
declares those outputs as inputs and materializes the tree. Compass scatters
symlink() calls through the product steps, which is what makes its
assembled_files tree hard to reason about.
Graph partitions are the exception to declaring outputs: which core counts exist follows from a cell count that is not known until the graph file has been written, so they cannot be named at setup. The assembly step lists what the partition step produced and raises if it produced nothing.