CPR: compute the true-IMPES weights per degree of freedom - #7276
Draft
hnil wants to merge 3 commits into
Draft
Conversation
Two facts that code outside a model has no way to ask for, and that it must know
before it can walk the degrees of freedom instead of the elements:
- whether the residual's computeStorage() can be given a degree of freedom's
intensive quantities directly. BlackOilLocalResidualTPFA has that overload;
the element-by-element residual does not, so the base states false and the TPFA
one overrides it. The model carries the answer through.
- whether the intensive quantities can be had by index at all, which is what the
cache is. storeIntensiveQuantities() does not answer that, being true also
when only the thermodynamic hints are kept.
No behaviour change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The weight is a function of the degree of freedom's own fluid state and primary variables -- the formation volume factors and the dissolved and vaporised ratios -- and of nothing else. No neighbours, no geometry. Walking the grid's elements to find it is therefore not necessary, and it is the only thing tying these weights to the grid: a degree of freedom that has no element cannot be given one at all. Walk the degrees of freedom when their intensive quantities can be had by index, and the elements when they cannot. The per-degree-of-freedom expression is written once and shared between the two walks so they cannot drift apart. Both walks cover the same degrees of freedom -- the element chunks are built over Partitions::all -- and read the same fluid states, so the weights are unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…esidual allows Same argument as for the analytic variant, with one requirement more. The accumulation term of a degree of freedom is a function of its own intensive quantities and its own volume; nothing about it needs an element. Forming it without one takes a storage term computed from those quantities alone, which the blackoil TPFA residual has and the element-by-element residual does not -- so the choice is made on that, and the element walk is taken whenever it or the indexed lookup is unavailable. The volume is the one substitution that is not a rename: dofTotalVolume() is the same quantity the element walk reads off the stencil -- accumulated once per degree of freedom and completed over process boundaries by the sum handle -- but without the extrusion factor, which is applied here explicitly. The two walks share the expression that turns an accumulation term into a weight, so they cannot drift apart. On a deck run under cpr_trueimpes the 252 summary vectors are identical to 1e-12. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
hnil
force-pushed
the
pr/cpr-dof-weights
branch
from
August 4, 2026 12:37
180de88 to
c9ede47
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Both true-IMPES weight formulas depend only on a degree of freedom's own intensive
quantities -- and, for the non-analytic one, its own volume. No neighbours, no geometry.
Walking the grid's elements to evaluate them is therefore not necessary, and it is the
only thing tying these weights to the grid.
The choice between the two walks is made on two facts a model can now be asked for
directly:
formsStorageFromIntensiveQuantitieson the residual --BlackOilLocalResidualTPFAhas the
computeStorage()overload taking intensive quantities alone, theelement-by-element residual does not.
intensiveQuantityCacheEnabled()on the model -- the indexed lookup is valid onlywhen the cache is on.
storeIntensiveQuantities()does not answer that, being truealso when only the thermodynamic hints are kept.
The element walk is kept and taken whenever either is missing, so
flow_blackoil_legacyassemblyand anything else on the element-based residual isunaffected.
dofTotalVolume()is the same quantity the element walk reads off the stencil --accumulated once per degree of freedom and completed over process boundaries by the sum
handle -- minus the extrusion factor, which is applied explicitly.
Verified on SIMPLE_MECH_NX_11_NY_11_NZ_25_FRAC_SEQ: 252 summary vectors identical to
1e-12 under
cpr_trueimpes, and a 13-test local regression set unchanged undercpr_trueimpesanalytic. Draft: I have not run the full opm-tests suite.Motivation is work on flow degrees of freedom that have no grid element (numerical
aquifers and embedded fractures as auxiliary DOFs), which cannot be given a weight at all
today -- but the change stands on its own.
🤖 Generated with Claude Code