Skip to content

implements the forcing class (wind only)#426

Merged
sbrus89 merged 12 commits into
E3SM-Project:developfrom
alicebarthel:omega/add-forcing-class
Jun 24, 2026
Merged

implements the forcing class (wind only)#426
sbrus89 merged 12 commits into
E3SM-Project:developfrom
alicebarthel:omega/add-forcing-class

Conversation

@alicebarthel

@alicebarthel alicebarthel commented Jun 2, 2026

Copy link
Copy Markdown

Adds the new Forcing class (with minimal changes).
Wind Forcing has been moved to the Forcing class and renamed a more general "SrfStress" to account for sea ice stress.
The docs have been updated accordingly.

The longer term vision is that Forcing will own 3 classes of AuxVars for momentum forcing, surface restoring, and surface tracer forcing. This PR is 1/3 to make #418 more digestible.

A key design question I'd like review on is the ownership/dependencies across AuxState vs Forcing.
I have moved all Forcing out of the AuxState (in the code and ctest). Instead, Forcing is initialized at the beginning of the timestep and used by tendencies directly.
Ultimately, the forcing compute will be called at the beginning of a coupling timestep.
The new/modified ctests include:

  • TendencyTest comparing velocity tendencies with and without SrfStress to ensure that SrfStress can be read it and is utilized by the tendency class.
  • ForcingTest now tests the computeStressOnEdge capability.

Checklist

@alicebarthel

alicebarthel commented Jun 2, 2026

Copy link
Copy Markdown
Author

There has been a call to rename the momentum-related AuxVars from MomForcingAuxVars (or MomentumForcingAuxVars) to the more specific SfcStressForcingAuxVars to make it more understandable to non-ocean modelers. Any objections before I do that? Other naming discussion/suggestions should happen here and will be folded in the full review so that I don't make too many updates.

@alicebarthel

Copy link
Copy Markdown
Author

waiting for omega_pr job 53810182 (set up using polaris/rename-wind)

@alicebarthel alicebarthel changed the title Omega/add forcing class implement the forcing class (for wind only) Jun 3, 2026
@alicebarthel alicebarthel changed the title implement the forcing class (for wind only) implements the forcing class (wind only) Jun 3, 2026
@alicebarthel

Copy link
Copy Markdown
Author

Update: omega_pr suite passes on pm-cpu (gnu) using the polaris/rename-wind branch

Polaris omega_pr suite

  • PR build: /global/homes/a/abarthel/pscratch/omega_scratch/develop-cpu
  • PR workdir: /global/homes/a/abarthel/pscratch/polaris-scratch/add-forcing-class-cpu
  • Machine: pm-cpu
  • Compiler: gnu
  • Build type: Debug
  • Log: /global/homes/a/abarthel/pscratch/polaris-scratch/add-forcing-class-cpu/polaris_omega_pr.o53863290
  • Result: All tests passed

@alicebarthel

Copy link
Copy Markdown
Author

After the last commit (renaming), I simply did a build and ctest on pm-cpu (gnu). All pass.
Reviewers, it'd be great if you run tests on other machines. Note that you need the new polaris dev branch to run the omega_pr suite.

@alicebarthel alicebarthel marked this pull request as ready for review June 4, 2026 01:33

@katsmith133 katsmith133 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alicebarthel I took a quick look at this and just had mostly clarification questions. I guess I am a bit confused that you've pulled the surface stress forcing out of the Aux stuff, but its still kind of an auxiliary variable? Can you explain why the partial move? Perhaps you've hashed this out with others, so sorry for the repetition.

Also, there is almost no commenting. I think it would be helpful to add commenting to the new files you've added and the new testing.

@@ -1,24 +1,24 @@
#ifndef OMEGA_AUX_WIND_H
#define OMEGA_AUX_WIND_H
#ifndef OMEGA_AUX_MOM_H

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we wanted to stay away from mom? Maybe this was skipped.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, thanks!

Err += OmegaConfig->get(SfcStressConfig);

std::string SfcStressInterpTypeStr;
Err += SfcStressConfig.get("InterpType", SfcStressInterpTypeStr);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the InterpType will stay the same for all forcing? Or rather if it doesn't this will be modified when those things are added?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, at this stage, this interp is only used for the surface stress, not the other fluxes which are digested straight from the cell-centered values.

@@ -0,0 +1,138 @@
#include "Forcing.h"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little bit of comments here like in AuxiliaryState.cpp would be good

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, but let me know if you need more detail.

Comment on lines 312 to 323

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming that as we add more forcing this will become more generic?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@katsmith133 What other kinds of stress forcing are you thinking we would add?

@@ -0,0 +1,277 @@
//===-- ocn/ForcingTest.cpp - Forcing Unit Test ---------------*- C++ -*-===//

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not entirely clear to me why some of the testing is in AuxVars and some is here?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, there is almost no commenting in this file. I would prefer if there were at least a little bit more. Helps to understand what you are trying to test and what the expected outcome is.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@katsmith133 this was historical. Since the Stress was still in AuxVars, I had left the testing in AuxVars. I have now moved the SfcStress testing to the ForcingTest. That set-up is much cleaner now, thanks for your feedback!

@alicebarthel

Copy link
Copy Markdown
Author

Thanks @katsmith133 for your review. I can definitely add comments for clarification.
@sbrus89 @brian-oneill could you give me a sense of your timeline for this review?

@cbegeman

Copy link
Copy Markdown

@alicebarthel I noticed you have a polaris branch. Feel free to open the Polaris PR with the Omega PR required tag.

@xylar

xylar commented Jun 12, 2026

Copy link
Copy Markdown

@cbegeman, my understanding is that's E3SM-Project/polaris#602.

@cbegeman

Copy link
Copy Markdown

@cbegeman, my understanding is that's E3SM-Project/polaris#602.

Thanks for pointing that out @xylar. Sorry, @alicebarthel, I didn't see the PR linked.

Comment thread components/omega/src/ocn/forcingVars/SfcStressForcingVars.cpp
Comment on lines 312 to 323

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@katsmith133 What other kinds of stress forcing are you thinking we would add?

@brian-oneill brian-oneill left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran the ctests and omega_pr suite successfully on Frontier with craygnu and craygnu-mphipcc. Found a few other instances, mostly comments where might want to change "wind" to "surface stress".

My take on some of the other comments above:

  • Instead of having the surface stress and other future forcing contributions mixed in with the other aux vars, maybe they should go in their own forcingVars directory. Or if there are just going to be a small number of them, they could just be defined in Forcing.h and Forcing.cpp
  • A small header in Forcing.h and Forcing.cpp, similar to what's in most other header/source files, along with some comments, go a long way towards making the code look cleaner and more readable.

| TracerDiffOnCell | horizontal diffusion of thickness-weighted tracers
| TracerHyperDiffOnCell | biharmonic horizontal mixing of thickness-weighted tracers
| WindForcingOnEdge | forcing by wind stress, defined on edges
| SfcStressForcingOnEdge | forcing by wind stress, defined on edges

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| SfcStressForcingOnEdge | forcing by wind stress, defined on edges
| SfcStressForcingOnEdge | forcing by surface stress, defined on edges

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"zonal surface stress", // long name/describe

// Meridional wind stress
auto MeridStressCellField =
Field::create(MeridStressCell.label(), // field name
"meridional wind stress", // long Name or description

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"meridional wind stress", // long Name or description
"meridional surface stress", // long Name or description

Comment thread components/omega/src/ocn/Tendencies.cpp Outdated
@@ -620,23 +622,25 @@ void Tendencies::computeVelocityTendenciesOnly(
Pacer::stop("Tend:computeVelocityVAdvTend", 2);

// Compute wind forcing

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Compute wind forcing
// Compute surface stress forcing

@@ -301,12 +301,12 @@ class VelocityHyperDiffOnEdge {
};

/// Wind forcing

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Wind forcing
/// Surface stress forcing

SfcStressForcingOnEdge(const HorzMesh *Mesh, const VertCoord *VCoord);

/// The functor takes the edge index, vertical chunk index, and arrays for
/// normal wind stress and edge pseudo-thickness, outputs tendency array

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// normal wind stress and edge pseudo-thickness, outputs tendency array
/// normal surface stress and edge pseudo-thickness, outputs tendency array

Array2DReal ExactSfcStressForcing("ExactSfcStressForcing", Mesh->NEdgesOwned,
NVertLayers);

// Note: this computes wind forcing at every layer

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Note: this computes wind forcing at every layer
// Note: this computes surface stress forcing at every layer

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am less worried about the comments in the test - since in the test, we can just decide that our simple case only has wind forcing. But for clarity/consistency, I can change it here too.

ExactSfcStressForcing, EdgeComponent::Normal, Geom, Mesh,
ExchangeHalos::No);

// Reset wind forcing to zero below the surface

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Reset wind forcing to zero below the surface
// Reset surface stress forcing to zero below the surface

Comment thread components/omega/src/ocn/.Forcing.h.swo Outdated
Comment thread components/omega/src/ocn/.Forcing.h.swp Outdated
Comment thread components/omega/src/ocn/forcingVars/SfcStressForcingVars.h
@alicebarthel

Copy link
Copy Markdown
Author

I think I have addressed all comments.
Ctests pass on pm-cpu (gnu).
I have run out of time for other tests on pm due to maintenance.

@alicebarthel

Copy link
Copy Markdown
Author

of course I just noticed I forgot a couple of comment changes suggested by brian in the Tests...

@sbrus89

sbrus89 commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

@alicebarthel, I'm seeing compile errors in the ForcingTest on Frontier:

/autofs/nccs-svm1_proj/cli115/sbrus/polaris_worktrees/testing/e3sm_submodules/Omega/components/omega/test/ocn/ForcingTest.cpp: In function ‘int {anonymous}::testForcingInitAndConfig()’:/autofs/nccs-svm1_proj/cli115/sbrus/polaris_worktrees/testing/e3sm_submodules/Omega/components/omega/test/ocn/ForcingTest.cpp:147:20: error: ‘class OMEGA::Forcing’ has no member named ‘SfcStressForcingAux’; did you mean ‘SfcStressForcing’?
  147 |    if (DefForcing->SfcStressForcingAux.InterpChoice != ExpectedChoice) {
      |                    ^~~~~~~~~~~~~~~~~~~
      |                    SfcStressForcing
/autofs/nccs-svm1_proj/cli115/sbrus/polaris_worktrees/testing/e3sm_submodules/Omega/components/omega/test/ocn/ForcingTest.cpp: In function ‘int {anonymous}::testForcingComputeAll()’:
/autofs/nccs-svm1_proj/cli115/sbrus/polaris_worktrees/testing/e3sm_submodules/Omega/components/omega/test/ocn/ForcingTest.cpp:171:40: error: ‘class OMEGA::Forcing’ has no member named ‘SfcStressForcingAux’; did you mean ‘SfcStressForcing’?
  171 |    auto &ZonalStressCell = DefForcing->SfcStressForcingAux.ZonalStressCell;
      |                                        ^~~~~~~~~~~~~~~~~~~
      |                                        SfcStressForcing
/autofs/nccs-svm1_proj/cli115/sbrus/polaris_worktrees/testing/e3sm_submodules/Omega/components/omega/test/ocn/ForcingTest.cpp:172:40: error: ‘class OMEGA::Forcing’ has no member named ‘SfcStressForcingAux’; did you mean ‘SfcStressForcing’?
  172 |    auto &MeridStressCell = DefForcing->SfcStressForcingAux.MeridStressCell;
      |                                        ^~~~~~~~~~~~~~~~~~~
      |                                        SfcStressForcing
/autofs/nccs-svm1_proj/cli115/sbrus/polaris_worktrees/testing/e3sm_submodules/Omega/components/omega/test/ocn/ForcingTest.cpp:173:40: error: ‘class OMEGA::Forcing’ has no member named ‘SfcStressForcingAux’; did you mean ‘SfcStressForcing’?
  173 |    auto &NormalStress    = DefForcing->SfcStressForcingAux.NormalStressEdge;
      |                                        ^~~~~~~~~~~~~~~~~~~
      |                                        SfcStressForcing

@alicebarthel

Copy link
Copy Markdown
Author

@sbrus89 I am sorry! I know I corrected these but somehow must have forgotten to push the changes.
Let me check out that branch on Chrysalis and correct these.

@alicebarthel alicebarthel force-pushed the omega/add-forcing-class branch 2 times, most recently from 8049c5b to 94de041 Compare June 19, 2026 17:57
@alicebarthel

Copy link
Copy Markdown
Author

I think this new version addresses all the comments. The design is a cleaner separation of ForcingVars outside of AuxiliaryVars. I mirrored the existence of a PLANE and SPHERE test for the forcing test (based on AuxiliaryVarsTest) to maintain the same test coverage of WindForcingAux-->SfcStressForcing.

Currently, ctests:

  • pm-cpu(gnu): 100% tests passed, 0 tests failed out of 42
  • pm-gpu: 100% tests passed, 0 tests failed out of 42

@alicebarthel

Copy link
Copy Markdown
Author

Polaris omega_pr suite

  • PR build: /global/homes/a/abarthel/pscratch/omega_scratch/add-forcing-class-cpu-gnu2
  • PR workdir: /global/homes/a/abarthel/pscratch/polaris-scratch/add-forcing-class-cpu3
  • Machine: pm-cpu
  • Compiler: gnu
  • Build type: Debug
  • Log: not found
  • Result: All tests passed

@alicebarthel

Copy link
Copy Markdown
Author

@mwarusz I was able to pass the testWindForcingAuxVars (moved to ForcingTest and renamed testSfcStressForcingVars) by following the structure in AuxiliaryVarsTest.cpp producing a SPHERE and PLANE test. Hopefully that was an ok plan? Could you review the test section to confirm?

@alicebarthel alicebarthel requested a review from sbrus89 June 19, 2026 19:14
@alicebarthel

alicebarthel commented Jun 19, 2026

Copy link
Copy Markdown
Author

omega_pr on pm-gpu
(set up using polaris/rename-wind with the fix from E3SM-Project/polaris#618)

Polaris omega_pr suite

  • PR build: /global/homes/a/abarthel/pscratch/omega_scratch/add-forcing-class-gpu2
  • PR workdir: /global/homes/a/abarthel/pscratch/polaris-scratch/add-forcing-class-gpu
  • Machine: pm-gpu
  • Compiler: gnugpu
  • Build type: Debug
  • Log: /global/homes/a/abarthel/pscratch/polaris-scratch/add-forcing-class-gpu/polaris_omega_pr.o54715042
  • Result: All tests passed

@cbegeman

Copy link
Copy Markdown

@alicebarthel Is this PR expected to be BFB with develop for wind-forced tests?

@cbegeman

Copy link
Copy Markdown

Polaris omega_pr suite

  • Baseline workdir: /lcrc/group/e3sm/ac.cbegeman/scratch/MPAS-Ocean-output/chrys/polaris-main-omega-20260512
  • Baseline build: /lcrc/group/e3sm/ac.cbegeman/scratch/MPAS-Ocean-output/chrys/polaris-main-omega-20260512/build
  • PR build: /lcrc/group/e3sm/ac.cbegeman/scratch/MPAS-Ocean-output/chrys/polaris-wind-omega-wind-20260620/build
  • PR workdir: /lcrc/group/e3sm/ac.cbegeman/scratch/MPAS-Ocean-output/chrys/polaris-wind-omega-wind-20260620
  • Machine: chrysalis
  • Partition: compute
  • Compiler: oneapi-ifx
  • Build type: Release
  • Log: /lcrc/group/e3sm/ac.cbegeman/scratch/MPAS-Ocean-output/chrys/polaris-wind-omega-wind-20260620/polaris_omega_pr.o1238760
  • Result: All tests passed

@alicebarthel

Copy link
Copy Markdown
Author

@alicebarthel Is this PR expected to be BFB with develop for wind-forced tests?

Yes, I would hope so. The functions have been moved but the implementation should be the same. Let me know if it does not come out as BFB.

@cbegeman

Copy link
Copy Markdown

@alicebarthel I didn't get a chance to check the velocity field of the barotropic_gyre case to make sure that this branch and develop are still reading in the forcing field. Do you have time to do this?

@alicebarthel

Copy link
Copy Markdown
Author

@cbegeman can you share the path to the long forward? I can have a look at the outout.

@cbegeman

Copy link
Copy Markdown

@alicebarthel I didn't have time to look at it before chrys went down so it would be best if you just ran it as setup with your latest suite on perlmutter.

@sbrus89 sbrus89 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested a local merge of this branch (in conjuction with a local merge of E3SM-Project/polaris#602) on Frontier with craygnu-mphipcc. Both the omega_pr suite and CTests pass.

Polaris omega_pr suite

  • Baseline workdir: /ccs/home/brus/run/polaris_omega_pr_baseline_craygnu-mphipcc_mpich
  • Baseline build: /ccs/home/brus/run/polaris_omega_pr_baseline_craygnu-mphipcc_mpich/build
  • PR build: /ccs/home/brus/run/polaris_forcing_class_omega_pr_craygnu-mphipcc_mpich/build
  • PR workdir: /ccs/home/brus/run/polaris_forcing_class_omega_pr_craygnu-mphipcc_mpich
  • Machine: frontier
  • Partition: batch
  • Compiler: craygnu-mphipcc
  • Build type: Release
  • Log: not found
  • Result: All tests passed

@sbrus89

sbrus89 commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

@alicebarthel, let me know when you're able to verify the state of the long_forward barotropic gyre case and I'll merge.

@alicebarthel

alicebarthel commented Jun 24, 2026

Copy link
Copy Markdown
Author

Thanks to @cbegeman for running a barotropic_gyre/long_forward.
comparison-pr-long
To me, this confirms that the wind forcing is adequately read in as forcing for this case.
@cbegeman?
@sbrus89 I am ready for a merge.

@sbrus89 sbrus89 merged commit 0c58868 into E3SM-Project:develop Jun 24, 2026
1 of 2 checks passed
@cbegeman

Copy link
Copy Markdown

@alicebarthel Thank you so much for taking the time to confirm the behavior! And for this very important development

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants