Repository navigation
Conversation
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unresolved moderate issues remain in halo exchange and regression-test coverage.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 2
Open (3)
What changed in this PR
Adds configurable, depth-dependent three-band penetrating shortwave heating to the ocean.
Changes:
- Adds shortwave tendency and extinction-coefficient auxiliary fields.
- Imports coupled
Foxx_swnetforcing. - Updates configuration, tests, and documentation.
| File | Reviewed change / findings |
|---|---|
components/omega/test/ocn/TendencyTermsTest.cpp |
Adds conservation coverage. Moderate (3 votes): strengthen assertions to validate per-layer attenuation. |
components/omega/test/ocn/SfcCouplingTest.cpp |
Updates coupling fixtures. Moderate (2 votes): verify Foxx_swnet in both coupling paths. |
components/omega/src/ocn/TendencyTerms.h |
Defines the shortwave penetration kernel. |
components/omega/src/ocn/TendencyTerms.cpp |
Constructs the new kernel. |
components/omega/src/ocn/Tendencies.h |
Registers the new tendency. |
components/omega/src/ocn/Tendencies.cpp |
Applies the tendency. Moderate (1 vote): exchange extinction-field halos or restrict processing to owned cells. |
components/omega/src/ocn/SfcCoupling.h |
Adds coupled shortwave storage. |
components/omega/src/ocn/SfcCoupling.cpp |
Imports coupled shortwave flux. Moderate (1 vote): add Foxx_swnet coupling assertions. |
components/omega/src/ocn/OceanInit.cpp |
Reads extinction inputs. Nit (2 votes): remove redundant shadowed declarations at lines 153 and 242. |
components/omega/src/ocn/Forcing.cpp |
Enables and exchanges shortwave forcing. |
components/omega/src/ocn/auxiliaryVars/ShortwavePenAuxVars.h |
Declares extinction auxiliary fields. |
components/omega/src/ocn/auxiliaryVars/ShortwavePenAuxVars.cpp |
Registers extinction auxiliary fields. |
components/omega/src/ocn/AuxiliaryState.h |
Adds shortwave auxiliary state. Moderate (1 vote): exchange both extinction arrays in exchangeHalo(). |
components/omega/src/ocn/AuxiliaryState.cpp |
Initializes auxiliary fields. Moderate (1 vote): exchange extinction halos; Nit (1 vote): reorder the ShortwavePenAux initializer. |
components/omega/src/drivers/coupled/omega_cpl_indices.F90 |
Adds the coupled shortwave import index. |
components/omega/doc/userGuide/TendencyTerms.md |
Documents the tendency. |
components/omega/doc/userGuide/Forcing.md |
Documents forcing configuration and behavior. |
components/omega/doc/userGuide/AuxiliaryVariables.md |
Documents extinction fields. |
components/omega/doc/devGuide/Forcing.md |
Documents implementation details. |
components/omega/doc/devGuide/AuxiliaryVariables.md |
Documents auxiliary-field handling. |
components/omega/configs/Default.yml |
Adds feature defaults and input configuration. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
ca8d98a to
828ae13
Compare
katsmith133
left a comment
There was a problem hiding this comment.
Just a quick first round of reviewing. Nice job on this @vanroekel!
I haven't tested it, but I can do that on Frontier and post the results.
| BottomDragCoeff: 1.0e-3 | ||
| SfcThicknessForcingTendencyEnable: false | ||
| SfcTracerForcingTendencyEnable: false | ||
| PenetratingShortwaveTendencyEnable: false |
There was a problem hiding this comment.
This is bigger than just this PR, but I feel like we can organize this section a bit better. Perhaps more like the BottomDragTendency section above, where the parameters that go with that tendency are a subsection of that tendency flag. That way its clear what parameters go with what tendencies and it keeps it a bit cleaner? To start, I would suggest making NearIrFraction, NearIrCoeff, BlueFraction, and RedFraction sub parameters under a header of PenetratingShortwaveTendency along with an Enable parameter. And then one of us can do a clean up PR that tidies up the other ones too?
There was a problem hiding this comment.
This is fixed in the new version and reflected in the docs as well
| Omega: | ||
| Tendencies: | ||
| SfcTracerForcingTendencyEnable: true | ||
| PenetratingShortwaveTendencyEnable: true |
There was a problem hiding this comment.
Is there a warning or something for if the user sets PenetratingShortwaveTendencyEnable: true, but SfcTracerForcingTendencyEnable: false?
There was a problem hiding this comment.
Also, need to add BlueFraction: 0.19 and RedFraction: 0.23 here too?
There was a problem hiding this comment.
Added the Blue/Red fractions and added an error if PenetratingShortwaveTendencyEnable: true and SfcTracerForcingTendencyEnable: false. See L144 of OceanInit.cpp
|
|
||
| The near-infrared fraction and extinction coefficient can be changed under | ||
| `Omega.Tendencies` with `NearIrFraction` and `NearIrCoeff`. The red and | ||
| blue/green band fractions remain fixed at 0.23 and 0.19, respectively. |
There was a problem hiding this comment.
This can lead to totals less than or greater than 1, yes? Is that a problem we anticipate and/or should mitigate somehow?
There was a problem hiding this comment.
Also, where did these fractions/values come from? I don't seem them in the Manizza 2008 paper.
I see the 0.58, but not the 0.23 and 0.19.
There was a problem hiding this comment.
The values were a mistake they should have both been 0.21 as per Manizza et al, that is fixed. There is also now a check that the NearIRFraction and BlueFraction and RedFraction sum to 1, see L365 of Tendencies.cpp
| VorticityAux.registerFields(GroupName, AuxMeshName); | ||
| VelocityDel2Aux.registerFields(GroupName, AuxMeshName); | ||
| SurfTracerRestAux.registerFields(GroupName, AuxMeshName); | ||
| // Only the Default state's fields belong to the shared IO group created |
There was a problem hiding this comment.
I am not sure I understand why ShortwavePenAux is treated differently here. Can you elaborate?
There was a problem hiding this comment.
This is removed, not sure why it was different
| } | ||
|
|
||
| OMEGA_SCOPE(LocPenetratingShortwave, PenetratingShortwave); | ||
| if (LocPenetratingShortwave.Enabled) { |
There was a problem hiding this comment.
Does not SfcTracerForcingTendencyEnable also have to be enabled for this to go through? Or is that checked elsewhere
|
|
||
| TracerForcingFieldsEnabled = | ||
| SfcThicknessForcingEnabled || SfcTracerForcingEnabled; | ||
| bool PenetratingShortwaveEnabled = false; |
There was a problem hiding this comment.
I guess perhaps this is where we can double up and check if SfcTracerForcingTendencyEnable is also on and set PenetratingShortwaveEnabled based on that too?
There was a problem hiding this comment.
Or is it L186 below that is what is really needed? Really we need TracerForcingFieldsEnabled enabled, right? Or do we also need SfcTracerForcingTendencyEnable enabled as well?
There was a problem hiding this comment.
Actually looking here I think we need to strip all of the penetrating shortwave. Copilot loaded all this to check if tracer forcing is on at L189, but we don't want tracer forcing enabled if only Penetrating Shortwave is on. I'm going to remove it. I had co pilot add an error out in a different place if the two aren't on.
| const Real Kb = ExtinctionCoeffBlue(ICell); | ||
| const Real ZSurface = GeomZInterface(ICell, KTop); | ||
|
|
||
| Real FluxAtTop = SurfaceFlux; |
There was a problem hiding this comment.
Can we amend these to be FluxAtLayerTop and FluxAtLayerBottom? I think that helps make this read clearer.
There was a problem hiding this comment.
Done, fixed it here and in TendenciesTest as well
| const auto Mesh = HorzMesh::getDefault(); | ||
| const auto VCoord = VertCoord::getDefault(); | ||
|
|
||
| // Arbitrary test values for the incident surface shortwave flux and the |
There was a problem hiding this comment.
Does this test the no penetrative flux beyond the bottom most layer part? I feel like with 60 x 10m layers, the test probably doesn't have any extra leftover flux by the time it gets to the bottom.
There was a problem hiding this comment.
This was a mistake it should have been much shorter, column should be 16m then.
828ae13 to
585728b
Compare
Polaris
|
Polaris
|
Polaris
|
Polaris
|
| OnCell); | ||
| } | ||
|
|
||
| if (TracerForcingFieldsEnabled) { |
There was a problem hiding this comment.
Do we need a Halo exchange here? I'd think running on CellOwned would be enough since this is cell to cell?
There was a problem hiding this comment.
I think we may? The tracer tends iterate over nCellsAll so we'd have to make sure missing shortwave isn't needed. Can we punt on this until a later PR? Or @brian-oneill do you have thoughts on this one?
There was a problem hiding this comment.
actually thinking further on this I think it can be removed. I'm doing the surface tracer forcing over NCellsOwned and removing this exchange
There was a problem hiding this comment.
Yes, I agree. It doesn't look like this exchange was necessary
| Err, | ||
| "Tendencies: SfcTracerForcingTendencyEnable not found in TendConfig"); | ||
|
|
||
| Config PenSWConfig("PenetratingShortwaveTendency"); |
There was a problem hiding this comment.
Very minor formatting but using SW capitalized as a shorthand for Shortwave seems strange.
| Config PenSWConfig("PenetratingShortwaveTendency"); | |
| Config PenSwConfig("PenetratingShortwaveTendency"); |
| Real NearIrFraction = 0.58_Real; | ||
| Real NearIrCoeff = 2.86_Real; | ||
| Real RedFraction = 0.21_Real; | ||
| Real BlueFraction = 0.21_Real; |
There was a problem hiding this comment.
I'd prefer if we don't carry default values within the code. Default values should be in the Default.yml file. I'd rather expose that the value was not passed properly than run with a hard-coded value.
There was a problem hiding this comment.
I don't agree with the proposed change. We do this approach in other places of the code (e.g. Vmix, the submeso PR) it's also what MPAS did too. I don't understand the need for another check within classes.
There was a problem hiding this comment.
I am not saying to add a check. I am saying that the fail should be triggered by the check when initializing/creating the class. So a default value here is not necessary except for ctests, which in this case already set the values.
There was a problem hiding this comment.
I still don't understand. What check do you mean? Also how is the approach here different than what I see in other classes?
There was a problem hiding this comment.
I'm confused because I thought the yaml checks we have are if the config is in the yaml and not a value check. Or maybe this is what you mean? You want to have a check that it's in the yaml? I would guess from the code, but maybe I misunderstand that even with those values it will fail. So I think these values may be redundant.
So maybe I'm understanding your reply. but let me know
There was a problem hiding this comment.
Agreed, this is broader than just this class. I tend to code things up with default values too. I just wonder if once the development is done, it is better to remove the default value assignment to ensure that the value is actually passed from the yaml file rather than inadvertently relying on the hard-coded default value, that's all. I don't expect it to be an issue, just redundant.
There was a problem hiding this comment.
Are you basically advocating this section should be
bool Enabled;
Real NearIrFraction;
Real NearIrCoeff;
Real RedFraction;
Real BlueFraction;
?
If so I think I'd still prefer to leave the code as is. The model will still fail if the parameters aren't in the yaml file, I feel having the defaults is nice for clarity.
There was a problem hiding this comment.
Basically if the parameter is not in the yaml file, the code should fail. Which I expect it to do given the checks at the class creation. So there should always be a value passed, so a local default value is unnecessary (and potentially confusing if we change default values down the line).
There was a problem hiding this comment.
Leaving the code as is is fine by me, it is a preference rather than a requirement.
There was a problem hiding this comment.
Fair point. I'd prefer though this not be changed here alone. Can you create an issue for discussion? I like having the default, but I definitely see your point about changing of defaults. it would be good to get input from the broader team.
|
|
||
| Real FluxAtLayerTop = SurfaceFlux; | ||
| for (I4 K = KTop; K <= KBot; ++K) { | ||
| Real FluxAtLayerBottom; |
There was a problem hiding this comment.
Declaring inside the loop seemed strange to me but it seems like for a double/float type it makes no difference to modern compilers.
There was a problem hiding this comment.
Yes that is odd. I'll fix it momentarily
|
@vanroekel The I'm happy to do the refactor, do I have your permission to push a change to this branch? |
| const Real TotalFraction = this->PenetratingShortwave.NearIrFraction + | ||
| this->PenetratingShortwave.RedFraction + | ||
| this->PenetratingShortwave.BlueFraction; | ||
| if (std::abs(TotalFraction - 1.0_Real) > 1.0e-5_Real) { |
There was a problem hiding this comment.
| if (std::abs(TotalFraction - 1.0_Real) > 1.0e-5_Real) { | |
| if (Kokkos::abs(TotalFraction - 1.0_Real) > 1.0e-5_Real) { |
| #include "VertCoord.h" | ||
| #include "auxiliaryVars/KineticAuxVars.h" | ||
| #include "auxiliaryVars/PseudoThicknessAuxVars.h" | ||
| #include "auxiliaryVars/ShortwavePenAuxVars.h" |
There was a problem hiding this comment.
Does it make more sense to use forcingVars rather than auxiliaryVars? I know surface restoring is still in Aux but that was left over from the early design. Not a deal breaker, just a scope question.
alicebarthel
left a comment
There was a problem hiding this comment.
This looks great @vanroekel !
Approving based on visual inspection, my comments are all minor. Is this in need of additional testing in polaris?
The ctests coverage looks good but let me know if you need anything else on this.
|
Thanks for the review @alicebarthel I'll address your comments now. I think the test coverage is good. I just need to test aurora. I'm turning on the polaris solar test I'm adding (E3SM-Project/polaris#796) in the pr suite so it's getting tested on every machine. |
|
@brian-oneill I'd be happy to have you do that. Let me address and push fixes to @alicebarthel comments then you can take on the refactor. I'll let you know when ready |
|
The move from aux state to forcing was causing a failure in the |
TestingRan the ctests at cbc3a06 with all tests passing using the following configurations:
|
TestingThe following results are from tests using the Chrysalis,
|
cbc3a06 to
9cc4be9
Compare
|
@brian-oneill and @katsmith133 I've rebased and retested on frontier. let me know if you have any further changes you'd like. Otherwise I think this is ready to go. |
brian-oneill
left a comment
There was a problem hiding this comment.
I was very thorough in testing the changes I pushed (which helped me catch a couple bugs along the way). So other than one minor cleanup below, I approve.
| #include <cmath> | ||
| #include <iostream> |
There was a problem hiding this comment.
These includes don't look necessary, probably a vestige of developing/debugging?



This allows shortwave radiation to enter the subsurface ocean instead of simply being deposited in the top layer. The penetration with depth follows the implementation of Manizza et al (2008), their equation 4. It splits the incident solar into three components, near IR, 'blue' band, and 'red' band. The solar flux at the bottom of the ocean is set to zero to ensure there are no energy leaks, i.e. the column integrated heating is identical for penetrating shortwave and when deposited in the top layer.
A unit test has been added with a very shallow column and it tests equivalence of heating in the penetrating case and top layer only case.
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
New tests: