Add velocity-component reconstruction for I/O - #525
Draft
xylar wants to merge 7 commits into
Draft
Conversation
Add an overload that reconstructs one layer of a multi-layer field. The reconstruction is independent in each layer, so this is the existing single-layer form with a vertical index added. Factor the Cartesian to local geographic rotation into a shared helper so the spherical and planar paths stay identical between the two overloads. Extend the operator unit test to exercise the multi-layer form. Each layer holds the exact field scaled by a layer-dependent factor so that a reconstruction which mixed layers would show up as an error. The error measures are normalized by the exact field, so the same expected errors apply as for the single-layer case. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The reconstruction stencil and weights are precomputed as a mesh preprocessing step rather than by Omega, so whether they are available is a property of the mesh file, not of whether the mesh is spherical. Detect their presence by attempting the read of NEdgesReconOnCell and treating a failure as "not present", and carry the answer as Decomp::HasVectorRecon and HorzMesh::HasVectorRecon. This lets planar meshes carry reconstruction data while leaving planar meshes that predate it readable, and makes VectorReconOnCell abort with a message naming the three arrays it needs rather than refusing every planar mesh. The planar branch already present in the operator becomes reachable as a result. Decomp no longer parses the OnSphere attribute, which existed only to gate these reads; HorzMesh still determines it from the full mesh stream as before. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add a VelocityReconAuxVars auxiliary variable class holding the zonal and meridional velocity components reconstructed at cell centers from the edge-normal velocity, and register both as fields in the AuxiliaryState field group so they can be requested by any IO stream. On a planar mesh the reconstructed vector already lies in the plane of the mesh, so the two components are the Cartesian x and y components. The reconstruction is written out here rather than calling VectorReconOnCell so that layers outside MinLayerCell/MaxLayerCell keep the fill value that Field::attachData wrote, which the operator, as a reference implementation, knows nothing about. Add AuxiliaryState::computeVelocityRecon to drive it. These are diagnostics that no tendency reads, so the computation is deliberately kept out of computeAll, which runs once per time stepper stage, and is over owned cells only. It aborts if the mesh supplied no reconstruction data. Nothing calls it yet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add IOStream::isFieldRequested, which reports whether a Field is in the contents of any defined stream, so a diagnostic that nothing will read or write can be skipped. Since validate replaces a group name in a stream's contents by its member Fields, this is only meaningful after the streams are validated; an unvalidated stream conservatively answers yes so an optional computation is done rather than skipped. Use it to gate AuxiliaryState::computeVelocityRecon, resolving the answer on the first call and caching it, since the streams are validated only after all Fields are defined and so well after the auxiliary state is constructed. Call it once per time step from both ocnRun loops, on the state the step just produced (time level 0), which is the state that updateTimeLevels has attached to the NormalVelocity field and so the one that any stream writing this step will write. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replace the hardcoded 1e-4 placeholder in OcnToCplFields::updateFields with the reconstructed zonal and meridional velocity in the surface layer, and drop the TODO it carried. Add AuxiliaryState::requireVelocityRecon so surface coupling can force the reconstruction to be computed every step regardless of what the IO streams ask for, and call it from SfcCoupling::init. That init also now checks the mesh supplies the reconstruction data, so a coupled run on a mesh without it fails at initialization rather than at the first time step. Initialize VertAdv and AuxiliaryState in SfcCouplingTest, which the coupling now depends on, and check the exported surface velocity against the reconstruction rather than skipping it. At NAccumSteps == 0 the running average is exactly the single sampled value, so the comparison is exact, as it already is for temperature and salinity. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add VelocityZonalCell and VelocityMeridionalCell to the auxiliary variable list in the user guide, with a section on the three ways they differ from the other auxiliary variables: they are diagnostic and so computed once per step, they are only computed when a stream asks for them or Omega is coupled, and they need a mesh file that supplies the reconstruction stencil and weights. Document computeVelocityRecon and requireVelocityRecon in the auxiliary state developer guide, and IOStream::isFieldRequested in the IOStreams developer guide. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Author
|
This work is motivated by the need for reconstructed velocity components for analysis work in Polaris before the Sept. 15th deadline. |
2 tasks
Author
|
Needs to have the velocity reconstruction coefficients added to planar meshes for CTests, following the fix from E3SM-Project/polaris#721. |
xylar
added a commit
to xylar/polaris
that referenced
this pull request
Aug 25, 2026
PlanarPeriodic48x48.omega_vars.260518.nc has no vector-reconstruction fields, so Omega's planar CTests cannot exercise the reconstruction. The two spherical CTest meshes gained them in the 260807 update, but the planar one was left behind because the weights it would have gotten were wrong. Point at PlanarPeriodic48x48.omega_vars.260825.nc, which is the same mesh with NEdgesReconOnCell, ReconStencilCell and ReconWeightsCell appended, computed after the two planar fixes earlier on this branch. No pre-existing variable changed. Omega will pick up the same datestamp in E3SM-Project/Omega#525.
PlanarPeriodic48x48.omega_vars.260825.nc is the first planar mesh to carry the vector reconstruction stencil and weights, so the CTests that use a planar mesh need it rather than the earlier datestamps. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Author
Early CTestingCTest unit tests:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add ZonalVelocity and MeridionalVelocity as fields available for I/O in Omega. Omega carries only the edge-normal component of the velocity, so the zonal and meridional components at cell centers are reconstructed from it using the least-squares stencil and weights already supplied by the mesh file. The two new fields are registered with the auxiliary state and can be requested by any I/O stream. They are computed once per time step, and only when something asks for them. This also replaces the hardcoded placeholder value that surface coupling was exporting to the coupler as the surface velocity.
What this addresses
Omega is a C-grid model and stores only the edge-normal velocity, so there has never been a way to look at the velocity as a vector at cell centers. That has two consequences. Analysis and comparison against MPAS-Ocean output are awkward, since MPAS-Ocean writes
velocityZonalandvelocityMeridional. And more concretely,OcnToCplFields::updateFieldshad a// TODO: Implement vector reconstruction for velocity fieldand exported a hardcodedConstSfcVelocity = 1e-4to the coupler in place of a real surface velocity.The machinery to fix this already landed in #480, which added the
VectorReconOnCelloperator plus the mesh-supplied stencil and weights (NEdgesReconOnCell,ReconStencilCell,ReconWeightsCell) that it needs. Nothing insrc/called it, and it only supported a single vertical layer on spherical meshes. This PR builds on that.Direction of the solution
VectorReconOnCellgains an overload that takes a vertical index, so it can reconstruct one layer of a multi-layer field. It is the existing single-layer form with an index added, since the reconstruction is independent in each layer. The Cartesian to local geographic rotation is factored into a helper shared by both overloads. Note that neither the new overload nor anything else added here uses vertical chunking, in line with the direction of #473.Whether the reconstruction is available is now treated as a property of the mesh file rather than of the geometry. The stencil and weights are precomputed as a mesh preprocessing step rather than by Omega, so
Decompdetects their presence by attempting the read ofNEdgesReconOnCelland treating a failure as "not present", and carries the answer asDecomp::HasVectorReconandHorzMesh::HasVectorRecon. Previously all of this was gated onOnSphere, which meant planar meshes could never have it and forcedDecompto parse theOnSphereattribute itself, duplicating whatHorzMeshdoes properly. That duplicate parsing is removed. The practical effect is that planar meshes work as soon as their mesh files carry the arrays, while planar meshes that predate them stay readable.The fields themselves live in a new
VelocityReconAuxVarsauxiliary variable class, following the pattern ofKineticAuxVars, and are registered into the existingAuxiliaryStatefield group. On a planar mesh the reconstructed vector already lies in the plane of the mesh, so the two variables hold the Cartesian x and y components instead of zonal and meridional ones.They are driven by a new
AuxiliaryState::computeVelocityRecon, deliberately kept out ofcomputeAll. Nothing in the Omega equations reads these, so computing them once per time stepper stage would be wasted work;ocnRuncalls them once per step instead, on the state the step just produced. They are also skipped entirely unless something needs them, which is decided by a newIOStream::isFieldRequestedthat reports whether a field is in the contents of any defined stream. Since validation is what expands a group name into its member fields, that answer is only meaningful after validation, so an unvalidated stream conservatively answers yes and the computation is done rather than skipped.Surface coupling needs the reconstruction regardless of what the streams say, so
SfcCoupling::initcallsAuxiliaryState::requireVelocityReconto force it on, and checks the mesh supplies the reconstruction data so a coupled run on a mesh without it fails at initialization rather than at the first time step.OcnToCplFields::updateFieldsthen averages the real surface values in place of the1e-4placeholder.Using the new fields
They are registered but are not in the contents of any stream by default, so add them to a stream to have them written:
Requesting them on a mesh whose file did not supply the reconstruction stencil and weights is an error, as is running coupled with such a mesh.
Tests
HORZ_OPERATORS_TESTgains a multi-layer check intestVectorRecon, alongside the existing single-layer one. Each layer holds the exact field scaled by a layer-dependent factor, so a reconstruction that mixed layers or reused a single layer shows up as an error. The error measures are normalized by the exact field, so a per-layer constant factor leaves them unchanged and the same expected error values apply as for the single-layer case.SFC_COUPLING_TESTnow checks the exported surface velocity instead of skipping it, comparingSo_uandSo_vagainst the reconstruction in the surface layer. AtNAccumSteps == 0the running average is exactly the single sampled value, so the comparison is exact, as it already is for temperature and salinity. This test now also initializesVertAdvandAuxiliaryState, which surface coupling depends on.DECOMP_TESTgates its existing reconstruction stencil checks onHasVectorReconrather thanOnSphere.Planar meshes
The code accepts planar meshes with reconstruction data, but no such mesh file exists yet, so the planar leg of
testVectorReconstill returns early and this has only been exercised on spherical meshes. Generating one requires a Polaris fix:construct_rotation_matrixinpolaris/mesh/reconstruct.pyreturned an all-ones matrix rather than the identity for planar meshes, which collapsed the local tangent frame and produced silently wrong weights. Reconstructing a uniform field onPlanarPeriodic48x48gave a mean of(0.729, 0.729)for an exact(1.0, 0.5); with the identity it is exact to 2.7e-13. Weights on a spherical mesh are bit-identical before and after. That fix, with tests, is on the Polaris branchfix-planar-velocity-reconstruction. Enabling planar meshes here is left to a follow-up once it has merged and planar mesh files have been regenerated.Checklist
Documentation:
Linting
Building
Testing
aurora, oneapi-ifx, mpich
chrysalis, oneapi-ifx, openmpi
frontier, craygnu, mpich
frontier, craygnu-mphipcc, mpich
pm-cpu, gnu, mpich
pm-gpu, gnugpu, mpich
Provide relevant details in a comment to the PR titled
Testingwith the following:have been run on and indicate that are all passing.
has passed, using the Polaris
e3sm_submodules/Omegabaseline-pfor both the baseline (Polarise3sm_submodules/Omega) and the PR buildPerformance related PRs: Please include a relevant PACE experiment link documenting performance before and after.
New tests:
Stealth Features