flashcompositionstep.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3/*
4 Copyright 2026 SINTEF Digital
5
6 This file is part of the Open Porous Media project (OPM).
7
8 OPM is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
12
13 OPM is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with OPM. If not, see <http://www.gnu.org/licenses/>.
20*/
26#ifndef OPM_FLASH_COMPOSITION_STEP_HH
27#define OPM_FLASH_COMPOSITION_STEP_HH
28
29#include <algorithm>
30#include <array>
31#include <cmath>
32#include <cstddef>
33
34namespace Opm {
35
36namespace detail {
37
40template <class Scalar, std::size_t numComponents>
41void keepCompositionFloor(std::array<Scalar, numComponents>& z, const Scalar compositionFloor)
42{
43 Scalar excessSum = 0.0;
44 for (auto& zc : z) {
45 zc = std::max(zc - compositionFloor, Scalar{0});
46 excessSum += zc;
47 }
48 // Fractions summing to one leave a positive excessSum.
49 const Scalar excessTotal = 1 - numComponents * compositionFloor;
50 for (auto& zc : z) {
51 zc = compositionFloor + excessTotal * zc / excessSum;
52 }
53}
54
56template <class Scalar, std::size_t numComponents>
57Scalar largestAmountChange(const Scalar share,
58 const std::array<Scalar, numComponents>& z,
59 const Scalar otherShare,
60 const std::array<Scalar, numComponents>& otherZ)
61{
62 Scalar change = std::abs(otherShare - share);
63 for (std::size_t compIdx = 0; compIdx < numComponents; ++compIdx) {
64 change = std::max(std::abs(otherShare * otherZ[compIdx] - share * z[compIdx]), change);
65 }
66 return change;
67}
68
73template <class Scalar, std::size_t numComponents>
74void shortenStep(std::array<Scalar, numComponents>& z,
75 Scalar& sw,
76 const std::array<Scalar, numComponents>& newZ,
77 const Scalar newShare,
78 const Scalar compositionFloor,
79 const Scalar hydrocarbonFloor,
80 const Scalar maxAmountChange)
81{
82 const Scalar share = std::max(1 - sw, hydrocarbonFloor);
83 std::array<Scalar, numComponents> startZ = z;
84 keepCompositionFloor(startZ, compositionFloor);
85 const Scalar startShare = std::max(1 - std::clamp(sw, Scalar{0}, Scalar{1}),
86 hydrocarbonFloor);
87
88 // A start that uses up the limit is kept.
89 const Scalar offset = largestAmountChange(share, z, startShare, startZ);
90 const Scalar length = largestAmountChange(startShare, startZ, newShare, newZ);
91 const Scalar scale = offset < maxAmountChange ? (maxAmountChange - offset) / length
92 : Scalar{0};
93
94 const Scalar finalShare = startShare + scale * (newShare - startShare);
95 for (std::size_t compIdx = 0; compIdx < numComponents; ++compIdx) {
96 const Scalar startAmount = startShare * startZ[compIdx];
97 const Scalar newAmount = newShare * newZ[compIdx];
98 z[compIdx] = (startAmount + scale * (newAmount - startAmount)) / finalShare;
99 }
100 // Dividing by a small share amplifies roundoff in the amounts, so the floors and the
101 // sum are restored.
102 keepCompositionFloor(z, compositionFloor);
103 // Sw follows from the share: interpolated separately, the two would disagree where
104 // the floor on h applies.
105 sw = 1 - finalShare;
106}
107
108} // namespace detail
109
138template <class Scalar, std::size_t numComponents>
139void applyFlashCompositionStep(std::array<Scalar, numComponents>& z,
140 Scalar& sw,
141 const std::array<Scalar, numComponents>& dz,
142 const Scalar dSw,
143 const Scalar compositionFloor,
144 const Scalar hydrocarbonFloor,
145 const Scalar maxAmountChange)
146{
147 const Scalar hydrocarbonShare = std::max(1 - sw, hydrocarbonFloor);
148 const Scalar dHydrocarbonShare = -dSw;
149
150 // One damping factor limits the linearized changes in h and h z. The changes after
151 // clamping Sw and enforcing the floors are checked at the end.
152 Scalar maxChange = std::abs(dHydrocarbonShare);
153 for (std::size_t compIdx = 0; compIdx < numComponents; ++compIdx) {
154 const Scalar dAmount = hydrocarbonShare * dz[compIdx] + z[compIdx] * dHydrocarbonShare;
155 maxChange = std::max(std::abs(dAmount), maxChange);
156 }
157 const Scalar alpha = maxChange > maxAmountChange ? maxAmountChange / maxChange : 1.0;
158
159 const Scalar newSw = std::clamp(sw + alpha * dSw, Scalar{0}, Scalar{1});
160 const Scalar newHydrocarbonShare = std::max(1 - newSw, hydrocarbonFloor);
161
162 // Back from amounts to fractions: z moves by hydrocarbonShare/newHydrocarbonShare of
163 // the damped step. A vanished hydrocarbon keeps its composition: the equations see z
164 // only through the floor of h then, so the step would mostly carry linear-solver error.
165 const bool vanished = newHydrocarbonShare <= hydrocarbonFloor;
166 const Scalar zStepScale = vanished ? Scalar{0}
167 : alpha * hydrocarbonShare / newHydrocarbonShare;
168 std::array<Scalar, numComponents> newZ{};
169 for (std::size_t compIdx = 0; compIdx < numComponents; ++compIdx) {
170 newZ[compIdx] = z[compIdx] + zStepScale * dz[compIdx];
171 }
172 detail::keepCompositionFloor(newZ, compositionFloor);
173
174 // Clamping Sw and enforcing the floors can lengthen the step beyond the limit.
175 const Scalar change = detail::largestAmountChange(hydrocarbonShare, z,
176 newHydrocarbonShare, newZ);
177 if (change <= maxAmountChange) {
178 z = newZ;
179 sw = newSw;
180 }
181 else {
182 detail::shortenStep(z, sw, newZ, newHydrocarbonShare,
183 compositionFloor, hydrocarbonFloor, maxAmountChange);
184 }
185}
186
187} // namespace Opm
188
189#endif
Scalar largestAmountChange(const Scalar share, const std::array< Scalar, numComponents > &z, const Scalar otherShare, const std::array< Scalar, numComponents > &otherZ)
Largest change of the hydrocarbon share h or of an amount h z between two states.
Definition: flashcompositionstep.hh:57
void shortenStep(std::array< Scalar, numComponents > &z, Scalar &sw, const std::array< Scalar, numComponents > &newZ, const Scalar newShare, const Scalar compositionFloor, const Scalar hydrocarbonFloor, const Scalar maxAmountChange)
Definition: flashcompositionstep.hh:74
void keepCompositionFloor(std::array< Scalar, numComponents > &z, const Scalar compositionFloor)
Definition: flashcompositionstep.hh:41
Definition: blackoilbioeffectsmodules.hh:45
void applyFlashCompositionStep(std::array< Scalar, numComponents > &z, Scalar &sw, const std::array< Scalar, numComponents > &dz, const Scalar dSw, const Scalar compositionFloor, const Scalar hydrocarbonFloor, const Scalar maxAmountChange)
Applies a Newton step to the overall composition and the water saturation.
Definition: flashcompositionstep.hh:139