Skip to content

Add SfcCoupling class as an interface layer to the coupler #458

Open
andrewdnolan wants to merge 27 commits into
E3SM-Project:developfrom
andrewdnolan:omega/surface-coupling
Open

Add SfcCoupling class as an interface layer to the coupler #458
andrewdnolan wants to merge 27 commits into
E3SM-Project:developfrom
andrewdnolan:omega/surface-coupling

Conversation

@andrewdnolan

@andrewdnolan andrewdnolan commented Jul 2, 2026

Copy link
Copy Markdown

Adds SfcCoupling, which manages the variables exchanged to (o2x) and from (x2o) the coupler for a sub-domain of the global mesh.

Changes

  • New SfcCoupling class supporting both MCT and MOAB coupling layouts
  • CouplingLayout Enum allows for CTesting of MCT (column major) and MOAB (row major) layouts.
  • Import/export of raw coupler data via unmanaged Kokkos views
  • applyImportFields() to push imported fields into Forcing
  • updateExportFields() to accumulate running averages of export fields on-device

Known limitations

  • Exported velocities use a placeholder constant pending vector reconstruction
    • A small non-zero value is used to prevent infinite fluxes in the coupler

Checklist

  • Documentation:
  • Linting
  • Building
    • CMake build does not produce any new warnings from changes in this PR
  • Testing
    • Add a comment to the PR titled Testing with the following:
      • Which machines CTest unit tests
        have been run on and indicate that are all passing.
      • The Polaris omega_pr test suite
        has passed, using the Polaris e3sm_submodules/Omega baseline
      • Document machine(s), compiler(s), and the build path(s) used for -p for both the baseline (Polaris e3sm_submodules/Omega) and the PR build
      • Indicate "All tests passed" or document failing tests
      • Document testing used to verify the changes including any tests that are added/modified/impacted.
    • New tests:
      • CTest unit tests for new features have been added per the approved design.

@cbegeman

cbegeman commented Jul 6, 2026

Copy link
Copy Markdown

@andrewdnolan Besides the ctest, is testing this as a part of e3sm also possible (with cpl history output)?

@andrewdnolan

andrewdnolan commented Jul 6, 2026

Copy link
Copy Markdown
Author

@cbegeman Not quite yet, there will be some work to get the coupled driver branch hook up to use this branch. I can try to get that working, but that testing would be pretty ad-hoc. I think it'd be best to get this in with just the CTest testing, with the understanding that this PR is just a first pass, and a follow on will be needed to fully "hook things up".

That being said, I tried to be very thorough in the CTest and have mock pointers that mimic the raw attribute vectors passed by the coupler. I have tests for both unpacking (import) and packing (export) with both MCT and MOAB layouts. The data varies per-field and per cell, so a pretty reasonable test. I feel fairly confident in the code mechanics here, given the CTest, though there will absolutely be more work to get physically meaningful coupling. (But that will be dependent on a coupled driver).

@cbegeman

cbegeman commented Jul 6, 2026

Copy link
Copy Markdown

@andrewdnolan Thanks for clarifying! That all sounds good.

Comment thread components/omega/src/ocn/SfcCoupling.h Outdated

// Kokkok views created with a label are zero-initialized by default.
// We reset the fields here anyway to be explicit about the fact that the
// OcnToCpl fields need to being a coupling interval with all zeros.

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 missing word here in the comment?

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.

Whoops, it should be "begin" not "being". With that I think the sentence makes sense. Thanks for catching that!

Comment thread components/omega/test/ocn/SfcCouplingTest.cpp Outdated
Comment thread components/omega/test/ocn/SfcCouplingTest.cpp Outdated
deepCopy(InstSshCellH, SSHCellOwned);
}

// OcnToCpl fields need to being a coupling interval with all values set to 0.

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 confused on what this is supposed to say... is it "...need to bring..."?

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.

Whoops, "begin" not "being". Fixed. Thanks for catching that!

}

// OcnToCpl fields need to being a coupling interval with all values set to 0.
void OcnToCplFields::resetFields() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Is resetting everything to 0 the safest thing here? Maybe I am misunderstanding, but is the a chance that these zeros could not be overwritten and get sent to the coupler?

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, we need to be able to reset all field to zero. The field contained in OcnToCpl are averaged fields, so each ocean time step (within the coupling interval) we calculate the update average. Given the average function: if we did not have a way to reset values to zero at the end of a coupling interval, these values would continue accumulating over the whole simulation, which is no good.

So this should only be called at then end of coupling interval, after the SfcCoupling::exportToCoupler has been called.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Ok, that makes sense. Now I understand the need to reset. I guess is there a change nothing gets accumulated in those averages and we send 0 to the coupler? I think for ssh or velocity that would be fine, but not so much for temp or salt.

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.

The checks we have in place ensure zero values will never be exported to the coupler.

A rough sketch of what the coupled version of ocnRun will look like is in the design doc. We check to make sure the ocean timestep is less than the coupling interval and that they are evenly divisible (see here). So we are guaranteed that at least one iteration of the while loop in the coupled version of ocnRun will run, so at least one call to SfcCoupling::updateExportFields will happen (i.e. non zero export fields).

And OcnToCplField.resetFields() is called at the end of SfcCoupling::exportToCoupler(), so there's no confusion about when it should be called.

The coupled version of ocnRun will be implemented in the coupled driver PR, in the coming week(s), but this should set us up nicely for it.

return Array;
}

// Shaed index formula for packing/unpacking from rae coupler buffer

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

"Shaed" = "Shared"? and "rae"?

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.

Thanks for catching that. "rae" was meant to be "raw".

Comment thread components/omega/src/ocn/SfcCoupling.cpp
Comment thread components/omega/src/ocn/SfcCoupling.h
@katsmith133

Copy link
Copy Markdown

Ran CTests on PM-CPU and GPU. All tests passed on CPU, but SFCCOUPLING_TEST failed on GPU. I don't see any error message pertaining to this failure and I don't see any compile messages that have to do with the files touched in this PR.

mark-petersen pushed a commit that referenced this pull request Jul 6, 2026
Rename SurfaceCoupling -> SfcCoupling throughout; document split
CplToOcnFields/OcnToCplFields containers, Welford running-average
accumulation, attachData's unmanaged strided views, and the
import/apply/update/export method flow to match the implementation
in #458.

Document the once-per-interval unit conversion added in #458: device
averages stay in native units for correct Welford math, conversion
(temperature only; salinity is a TODO) happens once per interval in
copyToHost via a local Teos10Eos, and the device arrays are private
on OcnToCplFields so external code can't read unconverted data.
Co-authored-by: Carolyn Begeman <cbegeman@lanl.gov>
@andrewdnolan andrewdnolan force-pushed the omega/surface-coupling branch from 6e673da to 1e8058c Compare July 6, 2026 21:06
@cbegeman

cbegeman commented Jul 6, 2026

Copy link
Copy Markdown

@andrewdnolan Let me know if it would be helpful to run the ctests on a particular machine. I imagine I should perhaps wait until we have a fix for the pm-gpu fail.

@andrewdnolan

andrewdnolan commented Jul 6, 2026

Copy link
Copy Markdown
Author

@cbegeman I will work on debugging the pm-gpu fails and running the full omega_pr suite first thing tomorrow. I've gotten a little more caught up testing other PR's than I had originally planned.

But agreed, probably not worth testing on another machine till that's addressed.

@andrewdnolan

Copy link
Copy Markdown
Author

Testing

CTest unit tests

  • Machine: pm-gpu
  • Compiler: gnugpu
  • Build type: Release
  • Result: All tests passed

Polaris omega_pr suite

  • Baseline workdir: /pscratch/sd/a/anolan/omega_PR-458/baseline
  • Baseline build: /pscratch/sd/a/anolan/omega_PR-458/build/pm-cpu/baseline
  • PR build: /pscratch/sd/a/anolan/omega_PR-458/build/pm-cpu/surface-coupling
  • PR workdir: /pscratch/sd/a/anolan/omega_PR-458/surface-coupling
  • Machine: pm-cpu
  • Compiler: gnu
  • Build type: Release
  • Log: not found
  • Result: All tests passed

Tests added/modified/impacted

  • CTest: SFCCOUPLING_TEST — mocks unpacking (import) and packing (export) from raw pointers, validates averaging functionality, and applying coupler fields to the Forcing class

Comment thread components/omega/src/ocn/SfcCoupling.cpp Outdated
Comment thread components/omega/src/ocn/SfcCoupling.cpp Outdated
KOKKOS_INLINE_FUNCTION Real updateAverage(const Real OldAvg,
const Real NewValue,
const I4 NAccumSteps) {
return OldAvg + (NewValue - OldAvg) / (NAccumSteps + 1);

@alicebarthel alicebarthel Jul 7, 2026

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 had to check:
(NAccumSteps + 1) makes sense because NAccumSteps is updated after updateAverage() is called (via updateFields()).

@andrewdnolan andrewdnolan Jul 8, 2026

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.

Yeah NAccumSteps starts a coupling interval with a value of zero and is incremented at the end of the ocean timestep, so the +1 is necessary. This is following the MPAS-Ocean approach (see here).

@alicebarthel alicebarthel 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.

Approving after visual inspection.

@alicebarthel alicebarthel 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.

Approving after visual inspection.

@cbegeman

cbegeman commented Jul 8, 2026

Copy link
Copy Markdown

@andrewdnolan Is this now ready to test on another machine? Any recommendations as to which?

@sbrus89

sbrus89 commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

I'll test on Frontier today.

Comment thread components/omega/test/CMakeLists.txt Outdated
Co-authored-by: alicebarthel <36741014+alicebarthel@users.noreply.github.com>
@andrewdnolan andrewdnolan force-pushed the omega/surface-coupling branch from 3dfd90f to d0ac984 Compare July 8, 2026 20:38
parallelFor(
{(int)AvgSfcTemperature.extent(0)}, KOKKOS_LAMBDA(int Cell) {
const Real Ct = LocAvgSfcTemp(Cell);
const Real Sa = LocAvgSfcSalinity(Cell);

@alicebarthel alicebarthel Jul 9, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@andrewdnolan It doesn't have to be added now but a first order absolute salinity --> practical salinity can be done using the factor I put in GlobalConstants.h:

Suggested change
const Real Sa = LocAvgSfcSalinity(Cell);
const Real Sa = LocAvgSfcSalinity(Cell) / Psu2Gpkg; // AbsSal to Practical conversion

The port that @katsmith133 was working on adds the spatial variability of the conversion, but that's second order. I'd recommend punting the full port to later.
Thoughts?

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'm inclined to hold off until a follow up PR. This first one is primarily focused on code mechanics so that we can build out the coupled driver. Once the first pass at the coupled driver is finished, we can return to this and work improve the physical fidelity of the coupling.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

That's totally fine by me. I just wanted to flag this since I was talking about it to you and Kat both.

@jonbob jonbob 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.

We verified that the sequence changes in the initial routine are valid, by making ocn_comp_mct in mpaso work in a similar way and passing ERS tests

@andrewdnolan

Copy link
Copy Markdown
Author

Testing

CTest unit tests

  • Machine:pm-cpu / pm-gpu
  • Compiler: gnu / gnugpu
  • Build type: Release
  • Result: All tests passed (on both machines)

Re-ran CTests on both pm-cpu and pm-gpu just to be double safe after the miscellaneous commits from code review.

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.

7 participants