Skip to content

SPE10-MOD02-01 from opm-tests aborts at initialisation: SOGCR = -5.55e-17 fails an exact < 0 end-point check #5382

Description

@whahnsr

Summary

SPE10-MOD02-01.DATA, which ships in opm-tests, cannot be run by flow 2026.04. Initialisation aborts because the critical oil-in-gas saturation for SATNUM 1 evaluates to -5.551115e-17 — one ULP of floating-point round-off below zero — and the saturation function consistency check compares it against exactly 0.

A tolerance-free comparison against a bound is the underlying problem: the end-point is computed as a difference of nearly equal quantities, so a result a few ULPs outside [0,1] is arithmetic noise rather than inconsistent input.

Environment

  • flow 2026.04, built from source at release/2026.04/final
    (opm-common 0ea62974, opm-simulators b82f21d)
  • Linux Mint 22.3 (Ubuntu 24.04 base), gcc 13.3.0, cmake 3.28.3, DUNE 2.11.0 (OPM PPA)
  • RelWithDebInfo, -march=native -fno-omit-frame-pointer, BUILD_TESTING=OFF

Reproduction

cd opm-tests/spe10
mpirun -np 4 flow SPE10-MOD02-01.DATA --parsing-strictness=low --output-dir=/tmp/spe10

(--parsing-strictness=low is needed for unrelated unsupported keywords in this deck —
OLDTRAN, NSTACK, ECHO/NOECHO, and item 10 of MESSAGES. The abort below happens
regardless, and also occurs in serial.)

Output, after the grid is built and partitioned successfully:

Processing grid
Total number of active cells: 1094421 / total pore volume: 13636601 RB
ZOLTAN Load balancing method = 9 (GRAPH)
...
Error: Saturation Function End-point Consistency Failures
Consistency Problem:
  Non-negative critical oil saturation in G/O system
  0 <= SOGCR < 1
  Total Violations: 4

List of Violations
+--------+---------------+
| SATNUM | SOGCR         |
+--------+---------------+
| 1      | -5.551115e-17 |
| 1      | -5.551115e-17 |
| 1      | -5.551115e-17 |
| 1      | -5.551115e-17 |
+--------+---------------+

Simulation aborted as program threw an unexpected exception: Could not initialize the
problem: Saturation function end-points do not meet requisite consistency conditions

Analysis

Where the value comes from — opm-common, opm/input/eclipse/EclipseState/Grid/SatfuncPropertyInitializers.cpp:750, in findCriticalOilGas():

return critical_oil_gas(sgofTables.getTable<Opm::SgofTable>(i), tolcrit) - swco[i];

For this deck the two operands are equal to within representation error, so the subtraction
yields -5.551115e-17 rather than 0.

Where it becomes fatal — opm-simulators, opm/simulators/utils/satfunc/OilPhaseConsistencyChecks.cpp:43-44, in SOcr_GO::testImpl():

const auto low  = this->sogcr_ < Scalar{0};
const auto high = ! (this->sogcr_ < Scalar{1});

if (low || high) {
    this->setViolated();
    this->setCritical();
}

The comparison is exact, and setCritical() makes the violation fatal.

This is not specific to the oil phase: Oil, Gas and Water PhaseConsistencyChecks contain
17 comparisons of end-points against Scalar{0} / Scalar{1} in the same style, each written
out in its own testImpl.

Both the computation and the check are unchanged on master as of 2026-09-18.

Suggested fix

Rather than adding a tolerance to each of the 17 comparisons, the attached patch sanitises the
end-points once, where they enter EclEpsScalingPointsInfo — opm-common,
opm/material/fluidmatrixinteractions/EclEpsScalingPoints.cpp, extractUnscaled() (line 83).
One helper defines the tolerance; a loop applies it to Swl, Sgl, Swcr, Sgcr, Sowcr,
Sogcr, Swu, Sgu.

    /// Snap a saturation end-point onto the unit interval when it lies
    /// outside by no more than a few rounding errors.
    template <typename Scalar>
    Scalar snapToUnitInterval(const Scalar s)
    {
        const auto tol = 10 * std::numeric_limits<Scalar>::epsilon();

        if ((s < Scalar{0}) && (s > -tol)) { return Scalar{0}; }
        if ((s > Scalar{1}) && (s < Scalar{1} + tol)) { return Scalar{1}; }

        return s;
    }

The intent is explicitly not to clamp: end-points outside [0,1] by more than the tolerance
are returned unchanged, so genuinely inconsistent input is still detected and reported exactly
as before. Verified against -5.551115e-17 and -2.3e-16 (snapped to 0) versus -1e-14,
-0.3 and 1.3 (unchanged, still reported).

The patch applies cleanly to both release/2026.04/final and current master.

Result

With the patch applied, SPE10 model 2 (1,094,421 active cells) initialises and runs to
completion — all 33 report steps, 2000 days — on 4 MPI ranks:

Number of MPI processes:         4
Threads per MPI process:         1
Setup time:                     45.96 s
Number of timesteps:            85
Simulation time:              2060.81 s
  Assembly time:               253.57 s
  Linear solve time:          1693.18 s
Overall Newton Iterations:     327
Overall Linear Iterations:    2447

Points for maintainers

Offered as a starting point rather than a finished solution — three things we were unsure of:

  1. Tolerance magnitude. 10 * epsilon (~2e-15 for doubles) is a guess with no basis in your
    conventions. A relative tolerance, or one derived from tolcrit, may suit better.
  2. Where to sanitise. Correcting the stored end-point affects every downstream consumer, not
    only the consistency checks. If you would rather the checks tolerate noise while the stored
    value stays untouched, the same helper could be applied in PhaseCheckBase instead — at the
    cost of touching all 17 comparison sites, or refactoring them through a shared predicate.
  3. Regression data. This changes a computed value, so stored reference results may shift in
    their last digits.

An alternative fix, avoiding the tolerance question entirely, would be to make the subtraction in
findCriticalOilGas() not produce sub-zero noise in the first place — though that addresses only
this one end-point, and leaves the exact comparisons in place for others to trip over.

snap-endpoints.patch

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions