Skip to content

Conversation

@alicebarthel
Copy link

This PR constrains the P, Sa, T ocean state fields to the valid range as defined by the ''oceanographic funnel'' of the GSW function gsw_infunnel (originally based on McDougall et al. 2003).
The input values are pre-processed against the valid P,Sa,T bounds and the specific volume calculation is performed on the modified values (if necessary). The original state fields are not modified.
The functions to calculate the valid bounds include the polynomial approximation of the freezing temperature. It is faster but less accurate than the full GSW/Teos-10 freezing temperature calculation.

I included a unit test to check the freezing temperature against the equivalent function in GSW-C.
The code compiled and passed tests on pm-cpu (gnu) and pm-gpu.

Checklist

  • Documentation:
    • User's Guide has been updated
    • Developer's Guide has been updated
    • Documentation has been built locally and changes look as expected
  • Building
    • CMake build does not produce any new warnings from changes in this PR
  • Testing
    • A comment in the PR documents testing used to verify the changes including any tests that are added/modified/impacted.
    • Unit tests have passed. Please provide a relevant CDash build entry for verification.

@alicebarthel alicebarthel marked this pull request as draft September 18, 2025 21:56
@alicebarthel
Copy link
Author

It seems like a forgot to run the pre-commit checks. I'll update.

@alicebarthel alicebarthel marked this pull request as ready for review September 18, 2025 22:05
Copy link

@philipwjones philipwjones left a comment

Choose a reason for hiding this comment

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

This looks fine and also passes Ctests on Frontier cpu/gpu. I had one minor change but will go ahead and approve this.

@alicebarthel
Copy link
Author

Adding a comment for discussion here. @katsmith133 raised the good point of whether the T,S bounds should apply to the clamped values or the original values. I wrote everything assuming that we calculate, say the freezing temperature bounds, on the actual (P,S) fields.
The conundrum is: how strongly do we want to constrain our values to the original "funnel"? Its original definition limits it to P < 8,000 dbar, for example, though the Poly75t was evaluated down to 10,000dbar:

For context, the 75-t polynomial was tested against the full TEOS-10 in the funnel (which was used for the fit), but also in WOA and the "cube" range (SA=0-42; CT= -2-40 C; P=0-10,000 dbar) (Table3 of Roquet et al 2015)

Screenshot 2025-10-01 at 1 20 49 PM

@alicebarthel
Copy link
Author

As far as I understand, the TEOS-10 toolbox documents the range of validity (and offers a check) but does not restrict the range to the funnel.
e.g.

def specvol(SA, CT, p):
    """
    Calculates specific volume from Absolute Salinity, Conservative
    Temperature and pressure, using the computationally-efficient 75-term
    polynomial expression for specific volume (Roquet et al., 2015).

    Parameters
    ----------
    SA : array-like
        Absolute Salinity, g/kg
    CT : array-like
        Conservative Temperature (ITS-90), degrees C
    p : array-like
        Sea pressure (absolute pressure minus 10.1325 dbar), dbar

    Returns
    -------
    specvol : array-like, m^3/kg
        specific volume


    Notes
    -----
    Note that the 75-term equation has been fitted in a restricted range of
    parameter space, and is most accurate inside the "oceanographic funnel"
    described in McDougall et al. (2003).  The GSW library function
    "gsw_infunnel(SA,CT,p)" is available to be used if one wants to test if
    some of one's data lies outside this "funnel".

@vanroekel
Copy link
Collaborator

@alicebarthel I'm not sure I'm tracking what the discussion point is. bounds and clamping are confusing me. Let me see if I understand and please correct me where I'm off. Right now you compute bounds based on the state, but the question is if we should compute bounds on the valid range? If this is right it seems cyclical to me. Is this a question of allowing say P > 8000 in the calculation, which would loosen the T/S range some?

If I'm tracking, how different do we expect these options to be? The ranges you show are already quite broad it seems.

@alicebarthel
Copy link
Author

alicebarthel commented Oct 1, 2025

Thanks @vanroekel. It's a detail of the implementation.

The easiest example might be:
if S = 44 (above the 42 g/kg funnel bound), should the T bounds be based on gsw_ct_freezing(sa, p, 0) or gsw_ct_freezing(42, p, 0)?

In reality, I don't expect it to apply to many (or any?) locations at all, but perhaps some edge cases. We can have locations with S > 42 g/kg (e.g. the Red Sea and sometimes the Mediterranean), but they tend to be warm.
As I said, I see no indication in the gsw toolbox that they limited the T,S,P range when using the eos, except for documenting the validity range.

@vanroekel
Copy link
Collaborator

Thanks @alicebarthel I feel like I wasn't too far off in my understanding. My follow up question would be how different would those two results be in your example? Just trying to see how important of a consideration this distinction is.

@alicebarthel
Copy link
Author

@vanroekel For my example above, the difference in density is about O(0.01).
image

Similar order of magnitude than calculating rho based on the S bound (42) rather than S itself:
image

Copy link

@sbrus89 sbrus89 left a comment

Choose a reason for hiding this comment

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

This looks good to me, @alicebarthel and @katsmith133. I just have one suggestion.

Copy link

@katsmith133 katsmith133 left a comment

Choose a reason for hiding this comment

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

Approving based upon visual inspection and my own testing. Thanks, @alicebarthel!

Copy link
Collaborator

@vanroekel vanroekel left a comment

Choose a reason for hiding this comment

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

approving based on visual inspection. Discussion of clamp implication will be moved to a separate discussion.

Thanks @alicebarthel

## Removal of Eos
## Bounds check (and truncation) for the state variables (under TEOS-10)

The implemented 75-term polynomial for the calculation of the specific volume under TEOS-10 is considered valid for ocean states contained in the ''oceanographic funnel'' defined in [McDougall et al., 2003](https://journals.ametsoc.org/view/journals/atot/20/5/1520-0426_2003_20_730_aaceaf_2_0_co_2.xml). When using TEOS-10, the Eos uses member methods `calcSLimits(P)` and `calcTLimits(Sa, P)` to calculate the valid ranges of Sa and T given the pressure. The conservative temperature lower bound is set by the freezing temperature, using the member method `calcCtFreezing(Sa, P, SaturationFract)`. This method implements the polynomial approximation of the conservative freezing temperature (called `gsw_ct_freezing_poly` in the GSW package), which is known to produce erros in the (-5e-4 K, 6e-4 K) range. Once we calculate the upper and lower bounds of validity, the state variables are clipped to the valid range (if outside the bounds) before we run the specific volume calculation. The state fields themselves are not changed.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
The implemented 75-term polynomial for the calculation of the specific volume under TEOS-10 is considered valid for ocean states contained in the ''oceanographic funnel'' defined in [McDougall et al., 2003](https://journals.ametsoc.org/view/journals/atot/20/5/1520-0426_2003_20_730_aaceaf_2_0_co_2.xml). When using TEOS-10, the Eos uses member methods `calcSLimits(P)` and `calcTLimits(Sa, P)` to calculate the valid ranges of Sa and T given the pressure. The conservative temperature lower bound is set by the freezing temperature, using the member method `calcCtFreezing(Sa, P, SaturationFract)`. This method implements the polynomial approximation of the conservative freezing temperature (called `gsw_ct_freezing_poly` in the GSW package), which is known to produce erros in the (-5e-4 K, 6e-4 K) range. Once we calculate the upper and lower bounds of validity, the state variables are clipped to the valid range (if outside the bounds) before we run the specific volume calculation. The state fields themselves are not changed.
The implemented 75-term polynomial for the calculation of the specific volume under TEOS-10 is considered valid for ocean states contained in the ''oceanographic funnel'' defined in [McDougall et al., 2003](https://journals.ametsoc.org/view/journals/atot/20/5/1520-0426_2003_20_730_aaceaf_2_0_co_2.xml). When using TEOS-10, the Eos uses member methods `calcSLimits(P)` and `calcTLimits(Sa, P)` to calculate the valid ranges of Sa and T given the pressure. The conservative temperature lower bound is set by the freezing temperature, using the member method `calcCtFreezing(Sa, P, SaturationFract)`. This method implements the polynomial approximation of the conservative freezing temperature (called `gsw_ct_freezing_poly` in the GSW package), which is known to produce errors in the (-5e-4 K, 6e-4 K) range. Once we calculate the upper and lower bounds of validity, the state variables are clipped to the valid range (if outside the bounds) before we run the specific volume calculation. The state fields themselves are not changed.

Copy link
Collaborator

Choose a reason for hiding this comment

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

should this also be updated to say we will issue a warning? Or we have an option to clip?


In addition to `SpecVol`, the displaced specific volume `SpecVolDisplaced` is also calculated by the EOS. This calculates the density of a parcel of fluid that is adiabatically displaced by a relative `k` levels, capturing the effects of pressure/depth changes. This is primarily used to calculate quantities for determining the water column stability (i.e. the stratification) and the vertical mixing coefficients (viscosity and diffusivity). Note: when using the linear EOS, `SpecVolDisplaced` will be the same as `SpecVol` since the specific volume calculation is independent of pressure/depth.

When using TEOS-10, the state variables are checked against the range over which the polynomial is considered to be valid (see [Roquet et al. 2015](https://www.sciencedirect.com/science/article/pii/S1463500315000566)). If the values are outside of the accepted values, we use the valid bounds for the specific volume calculation. Note that the state variable values themselves are not modified, only that they are not used as is in the calculation.
Copy link
Collaborator

Choose a reason for hiding this comment

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

same comment here should we specify we warn and have options for clipping

Real Tt = Ct / CTu;
EosRange SRange = calcSLimits(P);
EosRange TRange = calcTLimits(Sa, P);
Real SaInFunnel = Kokkos::clamp(
Copy link
Collaborator

Choose a reason for hiding this comment

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

will this eventually become an option?

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.

5 participants