Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
5af4160
VertAdv module definition; creation, destruction, retrieval methods
brian-oneill Dec 27, 2025
f09b3ae
Add optional per-team scratch space to parallelForOuter
brian-oneill Dec 27, 2025
ff5fa57
Add computeVerticalVelocity
brian-oneill Dec 27, 2025
d9deb07
Add computeThicknessVAdvTend
brian-oneill Dec 29, 2025
93876a3
Add computeVelocityVAdvTend
brian-oneill Dec 29, 2025
43fa0c8
Add computeTracerVAdvTend dispatch function
brian-oneill Dec 29, 2025
56ca686
Add computeVerticalFluxes
brian-oneill Jan 1, 2026
42609bf
Add computeStdVAdvTend
brian-oneill Jan 1, 2026
bd522d7
Add computeFCTVAdvTend
brian-oneill Jan 2, 2026
421ab50
Add ProvThickness to auxvars (follow-up restructuring needed)
brian-oneill Jan 6, 2026
725210c
Add new config variables to default config
brian-oneill Jan 8, 2026
980fef8
Add VertAdv unit tests
brian-oneill Jan 8, 2026
9e9d606
Init some previously missed pointers and add Field for ProvThickness
brian-oneill Jan 9, 2026
d859c84
Comments, bug fix, and add Fields for VertAdv arrays
brian-oneill Jan 9, 2026
f811db1
Some bug fixes for running on GPU
brian-oneill Jan 9, 2026
5b40aa4
Bug fixes: BottomDepth no longer in HorzMesh and typo fixes
brian-oneill Jan 10, 2026
9bdf05d
Make host copies of VertAdv arrays
brian-oneill Jan 11, 2026
7f8f0ef
Linting fix
brian-oneill Jan 11, 2026
d625796
Resize flux arrays in VertAdvTest
brian-oneill Feb 3, 2026
ec916bf
Rename config option from Enabled to Enable
brian-oneill Feb 5, 2026
2ea99f1
Remove unnecessary header
brian-oneill Feb 5, 2026
60bc8bf
Skip vertical tracer advection when not enabled
brian-oneill Feb 5, 2026
f777c61
Add TimeStep to AuxState for ProvThickness
brian-oneill Feb 10, 2026
f7fe596
Remove commented out ssh calculation
brian-oneill Feb 11, 2026
91d6523
Add LayerThicknessAux.computeVarsOnCells to AuxState::computeAll
brian-oneill Feb 11, 2026
3d3f1a3
Add VertAdv to Tendencies
brian-oneill Feb 11, 2026
9616d8f
Disable VertAdv tends in TimeStepperTest
brian-oneill Feb 11, 2026
9a5ef3a
Add VertAdv to AuxState
brian-oneill Feb 11, 2026
3c06217
Add computeVerticalVelocity to computeMomAux
brian-oneill Feb 11, 2026
d2359ce
Add VertAdv init and clear to OceanDriver methods
brian-oneill Feb 11, 2026
1a2cb40
Add computeThicknessVAdvTend to computeThicknessTendenciesOnly
brian-oneill Feb 11, 2026
f4beb4e
Add computeVelocityVAdvTend to computeVelocityTendenciesOnly
brian-oneill Feb 11, 2026
a1a84d7
Add computeTracerVAdvTend to computeTracerTendenciesOnly
brian-oneill Feb 11, 2026
594604b
Make TimeStep nonoptional for computeTracerVAdvTend
brian-oneill Feb 12, 2026
913d9e3
Add VertAdv devGuide and userGuide
brian-oneill Feb 12, 2026
bf7f3f7
Add early return to VertAdv methods when NVertLayers == 1
brian-oneill Feb 14, 2026
4dac381
Merge 'Omega/develop' into omega/add-vertical-advection
brian-oneill Feb 14, 2026
e813b29
Fix typos in comments and docs
brian-oneill Feb 19, 2026
beeec71
Update HorzMesh user doc
brian-oneill Feb 20, 2026
44bd58a
Add sanity check for bottom depth
brian-oneill Feb 20, 2026
6a34375
Fix linting fail
brian-oneill Feb 20, 2026
bda6af4
Rename TracerTend to LocTracerTend in function call
brian-oneill Feb 20, 2026
10eec48
Remove commented-out line
brian-oneill Feb 24, 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
6 changes: 6 additions & 0 deletions components/omega/configs/Default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ Omega:
State:
NTimeLevels: 2
Advection:
Coef3rdOrder: 0.25
FluxThicknessType: Center
FluxTracerType: Center
VerticalTracerFluxLimiterEnable: true
VerticalTracerFluxOrder: 3
WindStress:
InterpType: Isotropic
VertCoord:
Expand All @@ -50,6 +53,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 @@ -82,6 +84,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 @@ -196,12 +201,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 @@ -216,16 +229,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);

Pacer::start("AuxState:edgeAuxState4", 2);
parallelForOuter(
"edgeAuxState4", {NTracers, Mesh->NEdgesAll},
Expand Down Expand Up @@ -272,33 +306,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