Skip to content

Multiphase equilibrium solver fixes#2116

Merged
ischoegl merged 10 commits into
Cantera:mainfrom
speth:equil-fixes2
May 16, 2026
Merged

Multiphase equilibrium solver fixes#2116
ischoegl merged 10 commits into
Cantera:mainfrom
speth:equil-fixes2

Conversation

@speth
Copy link
Copy Markdown
Member

@speth speth commented May 12, 2026

Changes proposed in this pull request

This PR fixes the known issues with constant TP equilibrium solves using the MultiPhaseEquil solver (that is, the one used when calling mix.equilibrate("TP", solver="gibbs").

  • Fix errors due to bad logic and an incorrect tolerance related to trace species;
  • Allow initial phases to be outside the defined temperature limits. For constant TP equilibrium, the initial composition is usually only used to determine the element abundances, so which phases these elements come from doesn't matter.
  • Fix an error in the VCS solver involving phases where an element has zero abundance
  • Protect against normalizing all-zero mass/mole fractions, which resulted in the composition turning into all NaNs.
  • Fix reaction step calculation for multiphase mixtures by using correct formula for Hessian elements for species in other phases.
  • Fix element conservation for trace species in MultiPhaseEquil::step. This requires some thresholds depending on the direction of the step (for favorable or unfavorable trace species) and depending on the amount of the trace species present.
  • Relax convergence criterion for thermodynamically negligible reactions, where these reactions don't have a significant effect on the composition.
  • Add tests covering these failure modes that test that the resulting mixture state satisfies equilibrium and conserves the initial element abundances.
  • Fix handling of mixtures dominated by species with linearly dependent elemental compositions

AI Statement (required)

  • Extensive use of generative AI. Significant portions of code or documentation were generated with AI, including logic and implementation decisions. All generated code and documentation were reviewed and understood by the contributor. These fixes and tests were developed using OpenAI Codex / GPT-5.5, Claude Code / Sonnet 4.6 and Claude Code / Opus 4.7.

Checklist

  • The pull request includes a clear description of this code change
  • Commit messages have short titles and reference relevant issues
  • Build passes (scons build & scons test) and unit tests address code coverage
  • Style & formatting of contributed code follows contributing guidelines
  • AI Statement is included
  • The pull request is ready for review

@codecov
Copy link
Copy Markdown

codecov Bot commented May 12, 2026

Codecov Report

❌ Patch coverage is 91.04478% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.68%. Comparing base (4be08e6) to head (c2861a1).

Files with missing lines Patch % Lines
src/equil/MultiPhaseEquil.cpp 93.33% 1 Missing and 3 partials ⚠️
src/thermo/Phase.cpp 50.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2116      +/-   ##
==========================================
- Coverage   77.69%   77.68%   -0.01%     
==========================================
  Files         452      452              
  Lines       53203    53253      +50     
  Branches     8864     8880      +16     
==========================================
+ Hits        41335    41370      +35     
- Misses       8867     8876       +9     
- Partials     3001     3007       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

speth and others added 10 commits May 12, 2026 22:32
Trace species were intended to be ignored in the convergence error metric,
but that branch was being overwritten by the following if/else.

This fix allows two tests previously marked as "xfail" to pass.

Fixes Cantera#130.

Fixes Cantera#425.

Co-authored-by: OpenAI Codex <codex@openai.com>
…siton

For constant TP equilibrium, the initial composition is usually only used
to determine the element abundances, so which phases these species come
from doesn't matter.

Partially addresses Cantera#160

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes Cantera#160

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
One of the terms here is supposed to be (sum_k nu_k)^2, not sum_k nu_k^2.

Partially fixes Cantera#1023

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eEquil::step

When a minor non-component species uses the exponential update formula
(moles_new = |m_moles[k]| * exp(-dG/RT), capped at 10x growth) instead of
the Newton step, the change in moles differs from what the component update
above assumed (omega*deltaN[k]). For modestly-trace species the resulting
element-balance error is O(m_moles[k]) and absorbed by subsequent iterations
without ill effect, but for catastrophic cases — when the Newton step expects
a large change but the exponential formula caps the actual change at a small
fraction — the imbalance can produce wildly wrong final compositions.

The catastrophic case showed up most clearly when one phase has effectively
zero total moles: chemical potentials in that phase are dominated by
numerical artifacts in the ratios of trace amounts, making other species in
the same phase look favorable to form. The Newton step would then debit a
large amount from the component (e.g., N2 in the first phase), while the
exponential formula left the trace noncomponent unchanged, draining
the component.

Fixed by correcting the components by m_N(n, j) * excess when the excess is
catastrophic (|excess| > 10*|moles_new - m_moles[k]|). Applying the correction
routinely destabilizes convergence by perturbing trace components, so the
gating threshold is essential.

Partially fixes Cantera#1023

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…reactions

MultiPhaseEquil checks every formation reaction's |dG/RT| against the
convergence tolerance, regardless of how much the reaction could affect
the composition. For trace minor species whose mole fraction is dominated
by floating-point noise, |dG/RT| can be pinned at a small but nonzero
value (e.g., 1e-5) indefinitely, preventing convergence even though no
further composition change is possible.

For an unfavorable formation reaction (dG/RT > 0), the maximum extent in
a single step is bounded by the noncomponent's own moles plus the moles
of any component the reaction would produce (m_N(n,j) > 0). When this
maximum extent times |dG/RT| is far below the mixture scale, the reaction's
contribution to the Gibbs free energy is below floating-point significance,
and it should not block convergence.

The bound is not applied to favorable reactions (dG/RT < 0), since those
can still meaningfully grow a species from trace levels via the
exponential update formula's 10x-per-step growth, even when the current
extent is small.

This fixes pre-existing convergence failures at higher temperatures
where minor species like H, O, OH have to be resolved.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sweeps parametrized cases for a two-phase mixture where one phase is
the full h2o2 mechanism initially defined as a stoichiometric H2/O2
mixture with some N2 and the second is an ideal-gas phase containing
a subset of the mechanism's species, starting with zero moles. The
combinations and conditions are chosen to exercise the problematic
regimes that were originally reported in issue Cantera#1023.

To verify equilibrium, the chemical potential of each species must satisfy
mu_k = sum_e n_{e,k} lambda_e for some element potentials lambda_e. We
solve for lambda by weighted least squares (weighting by moles so trace
species do not dominate) and require the per-species residual to be
below 1e-6 of RT for all species with non-trivial moles. Element
conservation between initial and final states is checked separately.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…a#425)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ments

Fixes Cantera#245

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@speth speth marked this pull request as ready for review May 13, 2026 14:04
ischoegl

This comment was marked as off-topic.

Copy link
Copy Markdown
Member

@ischoegl ischoegl left a comment

Choose a reason for hiding this comment

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

Thanks, @speth!

@ischoegl ischoegl merged commit cc3f89d into Cantera:main May 16, 2026
49 of 50 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants