Higher Order Flux Corrected Horizontal Transport. - #447
Conversation
|
@overfelt I think in the meeting you mentioned success comparing the |
1403553 to
1b66468
Compare
ab09d81 to
9e7262f
Compare
|
Testing: Polaris omega_pr run against baselines on Chrysalis and passed. The TEND_PLANE_TEST and TEND_SPHERE_TEST test all of the new functions added for higher order FCT. There is no previous higher order FCT to compare against. |
Some notes from Claude Opus from testing horizontal FCT on a global configurationFeedback for the Motivation for trying it: Omega's unlimited horizontal tracer advection drives The configuration that worked: Omega:
Advection:
HorzTracerFluxOrder: 3
Coef3rdOrder: 0.25
HorzTracerFluxLimiterEnable: true
HorzTracerFluxLimiterBudgetsEnable: false
HorzTracerFluxLimiterMonotonicityCheckEnable: falseTwo things I hit that might be worth addressing before merge. 1.
|
81dab92 to
b9c2d57
Compare
|
@overfelt Are you able to post relevant plots generated by the |
4af06b6 to
d0434c3
Compare
|
@overfelt Can you rebase when you have a chance? Thanks! |
d0434c3 to
45ee3e9
Compare
|
@cbegeman , I rebased the branch. |
|
Closed by accident, I assume? |
|
oops
|
Build issues on aurora, gpu:Successful build with aurora, oneapi-ifxgpu, E3SM-Project/polaris@5546b58 + Omega submodule at c83fe0d Unsuccessful build with aurora, oneapi-ifxgpu, E3SM-Project/polaris@8aa9ccb, Omega this branch 5e38631 Build log can be found here: Claude attributes the fail to https://github.com/overfelt/Omega/blob/d31b35395899bf9ae21671a7fa4abbf725f4dc7e/components/omega/src/ocn/TendencyTerms.h#L729-L744:
|
|
I ran CTests on The seg faults happen because the vertical advection module is not initialized in these tests. I opened #503 to fix this issue and to add checks for module dependencies.
You probably know this, but the fix is to use |
Give LinearEos and ConstantEos the same calcSpecVolDerivs entry point the TEOS-10 functor now has, so the higher-order pressure gradient can be run with any of the three equations of state rather than only with TEOS-10. Both are closed form. For the linear EOS the derivatives with respect to conservative temperature and absolute salinity are -DRhodT and -DRhodS times the square of the specific volume, and there is no pressure dependence at all; for the constant EOS all three vanish. The idealized Polaris cases that use these options therefore get an exactly known reference to test the pressure gradient against. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add the array-level entry point that the higher-order pressure gradient will call, dispatching on the configured EOS to the calcSpecVolDerivs kernel of the corresponding functor and filling the specific volume and its three first derivatives in one pass. Eos owns the SpecVolDCt, SpecVolDSa and SpecVolDP arrays just as it owns SpecVol, allocating them in the constructor and registering them as fields in the Eos group so they can be written to a stream. Their valid range spans the full range of Real rather than starting at zero, since the salinity derivative is negative everywhere and the temperature derivative is negative in cold, nearly fresh water. Since computeSpecVolAndDerivs fills SpecVol as well, it replaces a call to computeSpecVol rather than accompanying one. The two are kept separate because the derivatives roughly double the TEOS-10 arithmetic per cell and layer, and only the higher-order pressure gradient needs them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Check the new derivatives against the unmodified GSW-C library over a cross product of 216 states spanning the oceanographic range and its corners, including fresh water, sub-zero temperatures and the full pressure range. Add an array-level test over the mesh with a state that varies with depth, so the device kernel and the vertical chunking are exercised over a range of values rather than a single one, and closed-form tests for the linear and constant options. Also compare the thermal expansion and haline contraction coefficients used by the Brunt-Vaisala frequency against gsw_specvol_alpha_beta. Those two functions were previously exercised only through a single hardcoded BruntVaisalaFreqSq value at a tolerance too loose to catch a mistake in either; the check is added before the following commit rewrites them. Measured agreement with GSW-C: 2.4e-14 for the specific volume, 3.1e-14 for the temperature derivative, 8.5e-15 for the salinity derivative, and 2.4e-14 for alpha and beta. The pressure derivative agrees only to 2.3e-12 and is gated separately at 1e-10. The discrepancy is GSW-C's: its v_P comes from a table of coefficients pre-multiplied by their pressure exponents and rounded, so it departs from the exact derivative of the 75-term polynomial by about 2e-12 at 10000 dbar, growing with pressure. Evaluating the exact derivative in 60-digit arithmetic puts the Omega value within 1e-16 of it and GSW-C's at 1.8e-12, and rounding the coefficient table to 11 digits reproduces GSW-C's error pattern. The looser gate therefore bounds GSW-C's rounding rather than ours. Add a finite-difference check as well. It is redundant with the GSW-C comparison while that library is present and correct, which is the point: it pins the unit convention of the Omega interface, per degC, per (g/kg) and per Pa, without reference to GSW, and would catch a pressure derivative that silently became per dbar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rewrite Teos10BruntVaisalaFreqSq::calcAlpha and calcBeta over the TEOS-10 derivative helpers and delete the A and B coefficient tables, which were a second copy of the same 100 constants. Alpha is the temperature derivative of the specific volume divided by the specific volume and beta is minus the salinity derivative divided by it, so both follow directly. The coefficient assemblers are made static, since they carry no state, so the frequency functor can reach them without holding an equation of state instance. This changes answers at roundoff level: the same polynomial is evaluated in a different Horner arrangement, and the normalized salinity is now formed as sqrt((Sa + DeltaS) / SaNorm) as calcPCoeffs does it rather than from a separately rounded reciprocal. Measured against the GSW-C library over the 216 test states, the maximum relative difference moves from 2.4e-14 to 3.1e-14 for alpha and from 2.4e-14 to 3.2e-14 for beta. BruntVaisalaFreqSq is gated at 1e-10 against a hardcoded value and continues to pass unchanged. The guard test added in the previous commit is what makes this safe: alpha and beta are now pinned to gsw_specvol_alpha_beta over the full state range, so a mistake in the rewrite fails there rather than hiding inside the single frequency value. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Describe computeSpecVolAndDerivs in the developer guide: the signature, the unit convention, that the results land in Eos members registered as fields in the Eos group, that it replaces rather than accompanies computeSpecVol since it fills SpecVol too, and that the Brunt-Vaisala expansion and contraction coefficients are now derived from them. Record the licensing position, since it is the reason the implementation looks the way it does, and the measured agreement with GSW-C, including why the pressure derivative agrees less well than the other two. Add a shorter user guide section noting that the derivatives exist for all three EOS options, that they carry no configuration of their own, that they cost nothing unless a scheme that needs them is enabled, and that they are available for output like SpecVol. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rename the single-state entry point to calcSpecVolAndDerivsAtPoint and the vertical-chunk entry point to calcSpecVolAndDerivsInChunk, so the two are told apart at the call site rather than by the presence of "And" in the name. Document what each is for.
Say explicitly that the higher-order pressure gradient will need the derivatives every time step and that computeSpecVolAndDerivs then replaces the computeSpecVol call, while configurations that do not use it keep the cheaper call. Note why there is no displaced counterpart and what adding one would take.
The Brunt-Vaisala frequency evaluates alpha and beta at the interface, from temperature, salinity and pressure averaged across the two adjacent layers, while SpecVolDCt and SpecVolDSa hold the derivatives at the layer centers. Record that in the calcAlpha and calcBeta comments so the duplication does not look accidental.
Both compare against GSW-C, but testEosTeos10Derivs covers the array-level machinery -- dispatch, chunking, layer masking, member arrays and field registration -- on one realistic profile, while checkValueGswcSpecVolDerivs covers the polynomial itself point by point at the corners of the oceanographic range. Say so in each test's comment.
AuxiliaryState::computeMomVertAux is the only caller of computeSpecVol; computeBruntVaisalaFreqSq consumes the SpecVol array rather than recomputing it, and computeSpecVolDisp is a separate evaluation. Describe the actual call graph, and give the two reasons the plain computeSpecVol is still wanted: the centered pressure gradient is the config default, and the VertMix refresh of SpecVol feeds nothing that reads the derivatives.
Vertical chunking is on its way out of Omega (PR E3SM-Project#473 removes it from the auxiliary variables and tendency terms), so this branch should not add more of it. The EOS is not touched by that PR, so remove chunking here across the board rather than leaving the new specific volume derivative code to be converted later. The functors now follow the same pattern as the de-chunked auxiliary variables: each takes a TeamMember and a cell index and loops over the active layers with parallelForInner(Team, Range{KMin, KMax}, INNER_LAMBDA(int K) { ... }); so the callers in Eos.cpp reduce to a single call inside parallelForOuter, with no vertRangeChunked and no inner loop over chunks. Dropping the chunk loop also removes VecLength from the TEOS-10 polynomial helpers. calcPCoeffs, calcPCoeffsDTt, calcPCoeffsDSs, calcDelta, calcDeltaDeriv and calcDeltaDP took arrays sized 6 * VecLength or 5 * VecLength together with a KVec index into them; they now take plain [6] and [5] arrays and no index. That storage was never shared between layers -- the coefficients are recomputed for every layer -- so nothing is lost, and the point-wise calcSpecVolAndDerivsAtPoint and the calcAlpha and calcBeta helpers no longer allocate VecLength times more stack than they use. With the chunk loop gone, the array-level derivative routine is exactly calcSpecVolAndDerivsAtPoint evaluated at each cell and layer, so it now calls it instead of repeating the polynomial evaluation. That leaves a single implementation of the TEOS-10 derivatives. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The InChunk suffix named an implementation detail that no longer exists, and it was flagged in review as unusual and potentially confusing. The auxiliary variables name these routines for the mesh element they act on rather than for the vertical loop structure -- computeVarsOnCell, computeVarsOnEdge, computeVarsOnVertex -- and keep those names through the removal of chunking, so follow that convention here. The point-wise entry point keeps the name calcSpecVolAndDerivsAtPoint; the pair now reads as scalars in and out versus arrays over the mesh. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
eb73410 to
87d8ead
Compare
|
@mwarusz - thanks for reviewing. Are you ready to approve, or do you have more suggestions? |
Co-authored-by: Maciej Waruszewski <mwarusz@igf.fuw.edu.pl>
|
@overfelt, it looks like this is getting close! It seems like the CI is picking up some linting issues. |
|
@sbrus89 It looks like the lint check has problems installing the environment. The "install dependencies" step fails when trying to install conda. |
The field used in MPAS-Ocean is vertAleTransportTop which maps to TotalVerticalPseudoVelocity in Omega.
sbrus89
left a comment
There was a problem hiding this comment.
@overfelt, I'm seeing 3 CTest fails on Frontier (craygnu). Most of these are probably just tolerances that need to be recalibrated, but there are some inf values reported.
TEND_PLANE_TEST
[error] [OceanTestCommon.h:676] TendencyTermsTest: FCTHProvInv LInf FAIL, expected 0, got inf
[error] [OceanTestCommon.h:681] TendencyTermsTest: FCTHProvInv L2 FAIL, expected 0, got inf
[error] [OceanTestCommon.h:676] TendencyTermsTest: FCTHNewInv LInf FAIL, expected 0, got inf
[error] [OceanTestCommon.h:681] TendencyTermsTest: FCTHNewInv L2 FAIL, expected 0, got inf
[error] [OceanTestCommon.h:676] TendencyTermsTest: FCTHighAndLowOrderFlux_High LInf FAIL, expected 6.938893903907228e-18, got 5.204170427930421e-18
[error] [OceanTestCommon.h:681] TendencyTermsTest: FCTHighAndLowOrderFlux_High L2 FAIL, expected 5.762546266224026e-18, got 3.645353341218854e-18
[error] [OceanTestCommon.h:676] TendencyTermsTest: FCTAccumulateHighOrderFlux_0 LInf FAIL, expected 1.2480435640527313e-15, got 1.1987786865243336e-15
[error] [OceanTestCommon.h:681] TendencyTermsTest: FCTAccumulateHighOrderFlux_0 L2 FAIL, expected 6.665476716753009e-16, got 5.663839253635848e-16
TEND_PLANE_SINGLE_PRECISION_TEST
[error] [OceanTestCommon.h:676] TendencyTermsTest: FCTHProv LInf FAIL, expected 0, got 1.5258789e-05
[error] [OceanTestCommon.h:681] TendencyTermsTest: FCTHProv L2 FAIL, expected 0, got 1.5258789e-05
[error] [OceanTestCommon.h:676] TendencyTermsTest: FCTHProvInv LInf FAIL, expected 0, got inf
[error] [OceanTestCommon.h:681] TendencyTermsTest: FCTHProvInv L2 FAIL, expected 0, got inf
[error] [OceanTestCommon.h:676] TendencyTermsTest: FCTHNewInv LInf FAIL, expected 0, got inf
[error] [OceanTestCommon.h:681] TendencyTermsTest: FCTHNewInv L2 FAIL, expected 0, got inf
[error] [OceanTestCommon.h:676] TendencyTermsTest: FCTHighAndLowOrderFlux_Low LInf FAIL, expected 1.0095554e-15, got 0
[error] [OceanTestCommon.h:681] TendencyTermsTest: FCTHighAndLowOrderFlux_Low L2 FAIL, expected 1.0095554e-15, got 0
[error] [OceanTestCommon.h:676] TendencyTermsTest: FCTHighAndLowOrderFlux_High LInf FAIL, expected 6.938894e-18, got 1.8626451e-09
[error] [OceanTestCommon.h:681] TendencyTermsTest: FCTHighAndLowOrderFlux_High L2 FAIL, expected 5.7625463e-18, got 1.643791e-09
[error] [OceanTestCommon.h:676] TendencyTermsTest: FCTAccumulateHighOrderFlux_0 LInf FAIL, expected 1.2480435e-15, got 7.405691e-07
[error] [OceanTestCommon.h:681] TendencyTermsTest: FCTAccumulateHighOrderFlux_0 L2 FAIL, expected 6.665477e-16, got 3.0637568e-07
TEND_SPHERE_TEST
[error] [OceanTestCommon.h:676] TendencyTermsTest: FCTHProvInv LInf FAIL, expected 3.0541724683419424e-05, got inf
[error] [OceanTestCommon.h:681] TendencyTermsTest: FCTHProvInv L2 FAIL, expected 1.0779233406323438e-06, got inf
[error] [OceanTestCommon.h:676] TendencyTermsTest: FCTHNewInv LInf FAIL, expected 3.0541724683419424e-05, got inf
[error] [OceanTestCommon.h:681] TendencyTermsTest: FCTHNewInv L2 FAIL, expected 1.0779233406323438e-06, got inf
[error] [OceanTestCommon.h:676] TendencyTermsTest: FCTAccumulateHighOrderFlux_0 LInf FAIL, expected 1.632639958580968e-15, got 1.521323597768628e-15
[error] [OceanTestCommon.h:681] TendencyTermsTest: FCTAccumulateHighOrderFlux_0 L2 FAIL, expected 7.582979124216783e-16, got 7.094408556615681e-16
|
@sbrus89 , I'll see if I can still access Frontier and try to replicate these failures. Some of the failing tests look like tolerances that can just be adjusted for Frontier but others are significant diffs. |
|
Sounds good @overfelt, I'm also seeing similar fails on Frontier with craygnu-mphicc in case it's helpful for adjusting tolerances: TEND_PLANE_TEST TEND_PLANE_SINGLE_PRECISION_TEST TEND_SPHERE_TEST I'll run on pm-cpu/gpu and post any fails I see as well. |






Checklist
Testingwith the following:have been run on and indicate that are all passing.
has passed, using the Polaris
e3sm_submodules/Omegabaseline-pfor both the baseline (Polarise3sm_submodules/Omega) and the PR build