Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
348ec17
VertAdv module definition; creation, destruction, retrieval methods
brian-oneill Dec 27, 2025
9ddddce
Add optional per-team scratch space to parallelForOuter
brian-oneill Dec 27, 2025
a70b3af
Add computeVerticalVelocity
brian-oneill Dec 27, 2025
cfa28fd
Add computeThicknessVAdvTend
brian-oneill Dec 29, 2025
2671b0b
Add computeVelocityVAdvTend
brian-oneill Dec 29, 2025
fe72127
Add computeTracerVAdvTend dispatch function
brian-oneill Dec 29, 2025
554fffb
Add computeVerticalFluxes
brian-oneill Jan 1, 2026
a91a479
Add computeStdVAdvTend
brian-oneill Jan 1, 2026
2e47453
Add computeFCTVAdvTend
brian-oneill Jan 2, 2026
686640c
Add ProvThickness to auxvars (follow-up restructuring needed)
brian-oneill Jan 6, 2026
cc92f70
Add new config variables to default config
brian-oneill Jan 8, 2026
5587339
Add VertAdv unit tests
brian-oneill Jan 8, 2026
fd0ea9f
Init some previously missed pointers and add Field for ProvThickness
brian-oneill Jan 9, 2026
fd3fc35
Comments, bug fix, and add Fields for VertAdv arrays
brian-oneill Jan 9, 2026
17da3e4
Some bug fixes for running on GPU
brian-oneill Jan 9, 2026
becd4f2
Bug fixes: BottomDepth no longer in HorzMesh and typo fixes
brian-oneill Jan 10, 2026
0516858
Make host copies of VertAdv arrays
brian-oneill Jan 11, 2026
ece1a8c
Linting fix
brian-oneill Jan 11, 2026
e4d7484
Resize flux arrays in VertAdvTest
brian-oneill Feb 3, 2026
116f9d3
Rename config option from Enabled to Enable
brian-oneill Feb 5, 2026
6904333
Remove unnecessary header
brian-oneill Feb 5, 2026
c32fc97
Skip vertical tracer advection when not enabled
brian-oneill Feb 5, 2026
39b8783
Add TimeStep to AuxState for ProvThickness
brian-oneill Feb 10, 2026
e97befa
Remove commented out ssh calculation
brian-oneill Feb 11, 2026
1372e29
Add LayerThicknessAux.computeVarsOnCells to AuxState::computeAll
brian-oneill Feb 11, 2026
da4107d
Add VertAdv to Tendencies
brian-oneill Feb 11, 2026
16597a2
Disable VertAdv tends in TimeStepperTest
brian-oneill Feb 11, 2026
a8853d1
Add VertAdv to AuxState
brian-oneill Feb 11, 2026
d27bc79
Add computeVerticalVelocity to computeMomAux
brian-oneill Feb 11, 2026
7584bfb
Add VertAdv init and clear to OceanDriver methods
brian-oneill Feb 11, 2026
4828a49
Add computeThicknessVAdvTend to computeThicknessTendenciesOnly
brian-oneill Feb 11, 2026
005b96f
Add computeVelocityVAdvTend to computeVelocityTendenciesOnly
brian-oneill Feb 11, 2026
a92f9a6
Add computeTracerVAdvTend to computeTracerTendenciesOnly
brian-oneill Feb 11, 2026
36ec792
Make TimeStep nonoptional for computeTracerVAdvTend
brian-oneill Feb 12, 2026
6fa3aa0
Add VertAdv devGuide and userGuide
brian-oneill Feb 12, 2026
0a01a4c
Add early return to VertAdv methods when NVertLayers == 1
brian-oneill Feb 14, 2026
9461d88
Fix typos in comments and docs
brian-oneill Feb 19, 2026
578a35f
Update HorzMesh user doc
brian-oneill Feb 20, 2026
ad15ca6
Add sanity check for bottom depth
brian-oneill Feb 20, 2026
f9485b4
Fix linting fail
brian-oneill Feb 20, 2026
5c2be5e
Rename TracerTend to LocTracerTend in function call
brian-oneill Feb 20, 2026
9b49bf7
Remove commented-out line
brian-oneill Feb 24, 2026
5759e27
Rebase: remove TracerAux computeVarsOnEdge
brian-oneill Mar 10, 2026
520bdf8
Rebase: fix variable name passed to computeVelocityVAdvTend
brian-oneill Mar 10, 2026
b7fc515
Rebase: fix getLayerThickness usage
brian-oneill Mar 10, 2026
1815dba
Exchange halo for BottomDepth
brian-oneill Mar 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion components/omega/configs/Default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ Omega:
State:
NTimeLevels: 2
Advection:
Coef3rdOrder: 0.25
FluxThicknessType: Center
HorzTracerFluxOrder: 2
Coef3rdOrder: 0.25
FluxTracerType: Center
VerticalTracerFluxLimiterEnable: true
VerticalTracerFluxOrder: 3
WindStress:
InterpType: Isotropic
VertCoord:
Expand All @@ -51,6 +54,9 @@ Omega:
EddyDiff4: 0.0
UseCustomTendency: false
ManufacturedSolutionTendency: false
ThicknessVertAdvTendencyEnable: true
VelocityVertAdvTendencyEnable: true
TracerVertAdvTendencyEnable: true
Tracers:
Base: [Temperature, Salinity]
Debug: [Debug1, Debug2, Debug3]
Expand Down
103 changes: 103 additions & 0 deletions components/omega/doc/devGuide/VertAdv.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
(omega-dev-vert-adv)=

## Vertical Advection

The `VertAdv` class contains methods and arrays for calculating vertical
velocities and the tendencies of thickness, horizontal velocity, and tracers due
to vertical advection.

### Initialization

The default `VertAdv` instance is created by calling `VertAdv::init()` method.
The default `HorzMesh`, `VertCoord`, and `Tracers` objects must be initialized
first. The default instance is retrieved by:
```
VertAdv *DefVertAdv = VertAdv::getDefault();
```

Additional instances can be created with the `create` method, which calls the
constructor and places the new instance in a map identified with the label
`Name`:
```
VertAdv *VertAdv::create(const std::string &Name, ///< [in] name for new VertAdv
const HorzMesh *Mesh, ///< [in] associated HorzMesh
const VertCoord *VCoord, ///< [in] associated VertCoord
Config *Options ///< [in] configuration options
)
```
This instance is retrieved with the get method:
```
VertAdv *NewVertAdv = VertAdv::get("Name");
```
The constructor reads configuration options, stores some info from the
`HorzMesh`, `VertCoord`, and `Tracers` objects, allocates needed arrays,
and defines `Field` metadata.

### Variables

The following arrays are stored as members of the `VertAdv` class.

| Variable Name | Type | Dimensions |
| ------------- | ---- | ---------- |
| VerticalVelocity | Real | NCellsSize, NVertLayersP1 |
| TotalVerticalVelocity | Real | NCellsSize, NVertLayersP1 |
| VertFlux | Real | NTracers, NCellsSize, NVertLayersP1 |
| LowOrderVertFlux | Real | NTracers, NCellsSize, NVertLayersP1 |

The `VerticalVelocity` represents the raw vertical pseudovelocity through the
top interfaces of cell layers, computed from the divergence of the horizontal
velocity. The `TotalVerticalVelocity` is the transport velocity and includes
corrections applied to the `VerticalVelocity`. The `VertFlux` and
`LowOrderVertFlux` store tracer fluxes at layer interfaces. The specific
numerical algorithms to compute these fluxes are chosen by the user through
configuration options.

### Methods

The `VerticalVelocity` and `TotalVerticalVelocity` arrays are computed by
calling the `computeVerticalVelocity` method:
```
VertAdv::computeVerticalVelocity(NormalVelocity, FluxLayerThickEdge);
```
This method takes as input the `NormalVelocity` field from the `OceanState`
object, and the `FluxLayerThickEdge` field from the `AuxiliaryState`. At
present, `TotalVerticalVelocity` is equivalent to `VerticalVelocity`;
additional corrections will be added in subsequent updates. The
`TotalVerticalVelocity` array is used by each of the tendency methods,
therefore `computeVerticalVelocity` must be called before any tendency
computations.

There are three methods for computing vertical advection tendencies of
thickness, horizontal velocity and tracers that are called from the time
stepper: `computeThicknessVAdvTend`, `computeVelocityVAdvTend`, and
`computeTracerVAdvTend`. Each method updates the corresponding tendency array
in place (inout).

Only the thickness tendency array is passed to the `computeThicknessVAdvTend`
method:
```
VertAdv::computeThicknessVAdvTend(ThickTend);
```

The `computeVelocityVAdvTend` method requires the `NormalVelocity` and
`FluxLayerThickEdge` fields, in addition to the velocity tendency array:
```
VertAdv::computeVelocityVAdvTend(VelTend, NormalVelocity, FluxLayerThickEdge);
```

The tracer vertical advection tendency depends on the configured settings.
The `computeTracerVAdvTend` method takes as arguments the full 3D array of
tracers, a thickness array (selected based on the configured advection
algorithm) and a `TimeInterval` representing the time step, along with the
tracer tendency array:
```
VertAdv::computeTracerVAdvTend(TracerTend, TracerArray, Thickness, TimeStep);
```
For the standard advection algorithm, `LayerThickness` from the `OceanState` is
passed as the thickness argument; for the FCT algorithm, the `ProvThickness`
from the `AuxiliaryState` is used instead.

This method first calls `computeVerticalFluxes` to compute the tracer fluxes at
layer interfaces, using the order of accuracy specified in the configuration
file. It then dispatches to the selected advection algorithm (standard or FCT)
to compute the tracer tendencies.
2 changes: 2 additions & 0 deletions components/omega/doc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ userGuide/TridiagonalSolvers
userGuide/VertCoord
userGuide/Timing
userGuide/VerticalMixingCoeff
userGuide/VertAdv
```

```{toctree}
Expand Down Expand Up @@ -94,6 +95,7 @@ devGuide/TridiagonalSolvers
devGuide/VertCoord
devGuide/Timing
devGuide/VerticalMixingCoeff
devGuide/VertAdv
```

```{toctree}
Expand Down
1 change: 0 additions & 1 deletion components/omega/doc/userGuide/HorzMesh.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ This includes the following variables:
| XCell, YCell, ZCell | Cartesian coordinates of cell centers | m |
| XEdge, YEdge, ZEdge | Cartesian coordinates of edge centers | m |
| XVertex, YVertex, ZVertex | Cartesian coordinates of vertices | m |
| BottomDepth | Depth of the bottom of the ocean at cell centers | m |
| FCell, FEdge, FVertex | Coriolis parameter at cell centers/edges/vertices | radians/s |
| LonCell, LatCell | Longitude/latitude coordinates of cell centers | radians |
| LonEdge, LatEdge | Longitude/latitude coordinates of edge centers | radians |
Expand Down
47 changes: 47 additions & 0 deletions components/omega/doc/userGuide/VertAdv.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
(omega-user-vert-adv)=

## Vertical Advection

### Overview

In Omega, vertical advection represents the transport of mass, momentum, and
tracer quantities along the vertical coordinate due to vertical motion within a
water column. In the context of Omega's
[governing equations](omega-design-governing-eqns-omega1), vertical advection
contributes to the tendencies of pseudo-thickness, horizontal velocity, and
tracer fields. Vertical motion is inferred from the divergence of horizontal
flow through the continuity equation and is necessary for accurately
representing vertical transport in three-dimensional ocean simulations.

The `VertAdv` class contains variables and functions for computing the vertical
velocity and the tendencies of `LayerThickness`, `NormalVelocity`, and
`Tracers`. The algorithms used are determined by options specified in the
configuration file.

### Configuration Options

Within the `Tendencies` group of the configuration file, flags are used to
enable or disable the contribution of vertical advection to the tendencies of
thickness, velocity, and tracers:
| Configuration name | Options |
| ------------------ | ------- |
| ThicknessVertAdvTendencyEnable | true / false |
| VelocityVertAdvTendencyEnable | true / false |
| TracerVertAdvTendencyEnable | true / false |

Within the `Advection` group, options are provided to configure the algorithms
used to compute the tracer tendencies:
| Configuration name | Description | Options |
| ------------------ | ----------- | ------- |
| VerticalTracerFluxOrder | order of accuracy used for the tracer flux calculation | 2, 3, or 4 |
| VerticalTracerFluxLimiterEnable | selects the tracer tendency algorithm | true / false |
| Coef3rdOrder | coefficient used for blending 3rd- and 4th-order fluxes when `VerticalTracerFluxOrder == 3` | 0.0 - 1.0 |

For `VerticalTracerFluxLimiterEnable`, `true` enables flux-corrected transport,
and `false` selects the standard algorithm. For `Coef3rdOrder`, `1` corresponds
to purely 3rd-order, `0` purely 4th-order.

The `VertAdv` class provides four fields that can be written to output by
adding them to the contents of an output stream in the configuration file:
`VerticalVelocity`, `TotalVerticalVelocity`, `VertFlux`, and `LowOrderVertFlux`.
These fields collectively make up the `VertAdv` output group.
17 changes: 14 additions & 3 deletions components/omega/src/infra/OmegaKokkos.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ using ScratchMemSpace = ExecSpace::scratch_memory_space;
using Kokkos::MemoryUnmanaged;
using Kokkos::PerTeam;
using Kokkos::TeamThreadRange;
using RealScratchArray =
Kokkos::View<Real *, ScratchMemSpace, Kokkos::MemoryUnmanaged>;

/// team_size for hierarchical parallelism
#ifdef OMEGA_TARGET_DEVICE
Expand Down Expand Up @@ -321,7 +323,8 @@ KOKKOS_INLINE_FUNCTION void teamBarrier(const TeamMember &Team) {
// parallelForOuter: with label
template <int N, class F>
inline void parallelForOuter(const std::string &Label,
const int (&UpperBounds)[N], F &&Functor) {
const int (&UpperBounds)[N], F &&Functor,
int ScratchValsPerTeam = 0) {

auto LinFunctor = LinearIdxWrapper{std::forward<F>(Functor), UpperBounds};
int LinBound = 1;
Expand All @@ -330,6 +333,12 @@ inline void parallelForOuter(const std::string &Label,
}

auto Policy = TeamPolicy(LinBound, OMEGA_TEAMSIZE);

if (ScratchValsPerTeam > 0) {
Policy.set_scratch_size(
0, Kokkos::PerTeam(ScratchValsPerTeam * sizeof(Real)));
}

Kokkos::parallel_for(
Label, Policy, KOKKOS_LAMBDA(const TeamMember &Team) {
const int TeamId = Team.league_rank();
Expand All @@ -339,8 +348,10 @@ inline void parallelForOuter(const std::string &Label,

// parallelForOuter: without label
template <int N, class F>
inline void parallelForOuter(const int (&UpperBounds)[N], F &&Functor) {
parallelForOuter("", UpperBounds, std::forward<F>(Functor));
inline void parallelForOuter(const int (&UpperBounds)[N], F &&Functor,
int ScratchValsPerTeam = 0) {
parallelForOuter("", UpperBounds, std::forward<F>(Functor),
ScratchValsPerTeam);
}

// parallelForInner
Expand Down
72 changes: 55 additions & 17 deletions components/omega/src/ocn/AuxiliaryState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "Field.h"
#include "Logging.h"
#include "Pacer.h"
#include "TimeStepper.h"

namespace OMEGA {

Expand All @@ -19,9 +20,10 @@ static std::string stripDefault(const std::string &Name) {
// fields with IOStreams
AuxiliaryState::AuxiliaryState(const std::string &Name, const HorzMesh *Mesh,
Halo *MeshHalo, const VertCoord *VCoord,
int NTracers)
: Mesh(Mesh), MeshHalo(MeshHalo), VCoord(VCoord), Name(stripDefault(Name)),
KineticAux(stripDefault(Name), Mesh, VCoord),
VertAdv *VAdv, int NTracers,
TimeInterval TimeStep)
: Mesh(Mesh), MeshHalo(MeshHalo), VCoord(VCoord), VAdv(VAdv),
Name(stripDefault(Name)), KineticAux(stripDefault(Name), Mesh, VCoord),
LayerThicknessAux(stripDefault(Name), Mesh, VCoord),
VorticityAux(stripDefault(Name), Mesh, VCoord),
VelocityDel2Aux(stripDefault(Name), Mesh, VCoord),
Expand Down Expand Up @@ -80,6 +82,9 @@ void AuxiliaryState::computeMomAux(const OceanState *State, int ThickTimeLevel,
OMEGA_SCOPE(MaxLayerEdgeBot, VCoord->MaxLayerEdgeBot);
OMEGA_SCOPE(MaxLayerEdgeTop, VCoord->MaxLayerEdgeTop);

R8 TimeStepSeconds;
TimeStep.get(TimeStepSeconds, TimeUnits::Seconds);

Pacer::start("AuxState:computeMomAux", 1);

Pacer::start("AuxState:vertexAuxState1", 2);
Expand Down Expand Up @@ -194,12 +199,20 @@ void AuxiliaryState::computeMomAux(const OceanState *State, int ThickTimeLevel,

parallelForInner(
Team, KRange, INNER_LAMBDA(int KChunk) {
LocLayerThicknessAux.computeVarsOnCells(ICell, KChunk,
LayerThickCell);
LocLayerThicknessAux.computeVarsOnCells(
ICell, KChunk, LayerThickCell, NormalVelEdge,
TimeStepSeconds);
});
});
Pacer::stop("AuxState:cellAuxState3", 2);

Pacer::start("AuxState:computeVerticalVelocity", 2);

const auto &FluxLayerThickEdge = LayerThicknessAux.FluxLayerThickEdge;
VAdv->computeVerticalVelocity(NormalVelEdge, FluxLayerThickEdge);

Pacer::stop("AuxState:computeVerticalVelocity", 2);

Pacer::stop("AuxState:computeMomAux", 1);
}

Expand All @@ -212,16 +225,37 @@ void AuxiliaryState::computeAll(const OceanState *State,

const int NTracers = TracerArray.extent_int(0);

OMEGA_SCOPE(LocLayerThicknessAux, LayerThicknessAux);
OMEGA_SCOPE(LocTracerAux, TracerAux);
OMEGA_SCOPE(MinLayerCell, VCoord->MinLayerCell);
OMEGA_SCOPE(MaxLayerCell, VCoord->MaxLayerCell);
OMEGA_SCOPE(MinLayerEdgeBot, VCoord->MinLayerEdgeBot);
OMEGA_SCOPE(MaxLayerEdgeTop, VCoord->MaxLayerEdgeTop);

R8 TimeStepSeconds;
TimeStep.get(TimeStepSeconds, TimeUnits::Seconds);

Pacer::start("AuxState:computeAll", 1);

computeMomAux(State, ThickTimeLevel, VelTimeLevel);

Pacer::start("AuxState:cellAuxState3", 2);
parallelForOuter(
"cellAuxState3", {Mesh->NCellsAll},
KOKKOS_LAMBDA(int ICell, const TeamMember &Team) {
const int KMin = MinLayerCell(ICell);
const int KMax = MaxLayerCell(ICell);
const int KRange = vertRangeChunked(KMin, KMax);

parallelForInner(
Team, KRange, INNER_LAMBDA(int KChunk) {
LocLayerThicknessAux.computeVarsOnCells(
ICell, KChunk, LayerThickCell, NormalVelEdge,
TimeStepSeconds);
});
});
Pacer::stop("AuxState:cellAuxState3", 2);

const auto &MeanLayerThickEdge = LayerThicknessAux.MeanLayerThickEdge;

Pacer::start("AuxState:cellAuxState4", 2);
Expand Down Expand Up @@ -252,33 +286,37 @@ void AuxiliaryState::computeAll(const OceanState *State,
// Create a non-default auxiliary state
AuxiliaryState *AuxiliaryState::create(const std::string &Name,
const HorzMesh *Mesh, Halo *MeshHalo,
const VertCoord *VCoord,
const int NTracers) {
const VertCoord *VCoord, VertAdv *VAdv,
const int NTracers,
TimeInterval TimeStep) {
if (AllAuxStates.find(Name) != AllAuxStates.end()) {
LOG_ERROR("Attempted to create a new AuxiliaryState with name {} but it "
"already exists",
Name);
return nullptr;
}

auto *NewAuxState =
new AuxiliaryState(Name, Mesh, MeshHalo, VCoord, NTracers);
auto *NewAuxState = new AuxiliaryState(Name, Mesh, MeshHalo, VCoord, VAdv,
NTracers, TimeStep);
AllAuxStates.emplace(Name, NewAuxState);

return NewAuxState;
}

// Create the default auxiliary state. Assumes that HorzMesh, VertCoord and
// Halo have been initialized.
// Create the default auxiliary state. Assumes that HorzMesh, VertCoord,
// VertAdv, and Halo have been initialized.
void AuxiliaryState::init() {
const HorzMesh *DefMesh = HorzMesh::getDefault();
Halo *DefHalo = Halo::getDefault();
const VertCoord *DefVCoord = VertCoord::getDefault();
const HorzMesh *DefMesh = HorzMesh::getDefault();
Halo *DefHalo = Halo::getDefault();
const VertCoord *DefVCoord = VertCoord::getDefault();
VertAdv *DefVAdv = VertAdv::getDefault();
const TimeStepper *DefTimeStepper = TimeStepper::getDefault();

int NTracers = Tracers::getNumTracers();
int NTracers = Tracers::getNumTracers();
TimeInterval TimeStep = DefTimeStepper->getTimeStep();

AuxiliaryState::DefaultAuxState =
AuxiliaryState::create("Default", DefMesh, DefHalo, DefVCoord, NTracers);
AuxiliaryState::DefaultAuxState = AuxiliaryState::create(
"Default", DefMesh, DefHalo, DefVCoord, DefVAdv, NTracers, TimeStep);

Config *OmegaConfig = Config::getOmegaConfig();
DefaultAuxState->readConfigOptions(OmegaConfig);
Expand Down
Loading
Loading