opm-simulators
FlowProblemTPSA.hpp
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 2025 NORCE AS
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 
21  Consult the COPYING file in the top-level source directory of this
22  module for the precise wording of the license and the list of
23  copyright holders.
24 */
25 #ifndef FLOW_PROBLEM_TPSA_HPP
26 #define FLOW_PROBLEM_TPSA_HPP
27 
28 #include <dune/common/fvector.hh>
29 
30 #include <dune/grid/common/rangegenerators.hh>
31 
32 #include <opm/common/OpmLog/OpmLog.hpp>
33 
34 #include <opm/input/eclipse/EclipseState/Grid/FaceDir.hpp>
35 
36 #include <opm/material/common/MathToolbox.hpp>
37 #include <opm/material/materialstates/MaterialStateTPSA.hpp>
38 
39 #include <opm/models/io/vtktpsamodule.hpp>
40 #include <opm/models/tpsa/tpsabaseproperties.hpp>
41 #include <opm/models/tpsa/tpsamodel.hpp>
43 
44 #include <opm/simulators/flow/FacePropertiesTPSA.hpp>
46 
47 #include <cmath>
48 #include <memory>
49 #include <stdexcept>
50 #include <string>
51 #include <utility>
52 
53 #include <fmt/format.h>
54 
55 
56 namespace Opm {
57 
61 template <class TypeTag>
62 class FlowProblemTPSA : public FlowProblemBlackoil<TypeTag>
63 {
64 public:
65  using ParentType = FlowProblemBlackoil<TypeTag>;
66 
78 
79  enum { dimWorld = GridView::dimensionworld };
80  enum { enableMech = getPropValue<TypeTag, Properties::EnableMech>() };
81  enum { historySize = getPropValue<TypeTag, Properties::SolutionHistorySizeTPSA>() };
82  enum { numEq = getPropValue<TypeTag, Properties::NumEqTPSA>() };
83  enum { numPhases = FluidSystem::numPhases };
84 
85  enum { contiRotEqIdx = Indices::contiRotEqIdx };
86  enum { contiSolidPresEqIdx = Indices::contiSolidPresEqIdx };
87  enum { solidPres0Idx = Indices::solidPres0Idx };
88 
90  using DimVector = Dune::FieldVector<Scalar, dimWorld>;
92  using InitialMaterialState = MaterialStateTPSA<Scalar>;
93  using Toolbox = MathToolbox<Evaluation>;
94 
95  // ///
96  // Public functions
97  // ///
103  FlowProblemTPSA(Simulator& simulator)
104  : ParentType(simulator)
105  , faceProps_(simulator.vanguard().eclState(),
106  simulator.vanguard().gridView(),
107  simulator.vanguard().cartesianIndexMapper(),
108  simulator.vanguard().grid(),
109  simulator.vanguard().cellCentroids())
110  , geoMechModel_(simulator)
111  {
112  if constexpr(enableMech) {
113  // Add VTK TPSA to output module
114  this->model().addOutputModule(std::make_unique<VtkTpsaModule<TypeTag>>(simulator));
115 
116  // Sanity check
117  const auto& mechSolver = simulator.vanguard().eclState().runspec().mechSolver();
118  if (!mechSolver.tpsa()) {
119  std::string msg = "Simulator with Tpsa-geomechanics enabled compile time, but deck does not contain "
120  "TPSA keyword!";
121  OpmLog::error(msg);
122  throw std::runtime_error(msg);
123  }
124 
125  // Warn against using aquifers with TPSA
126  if (simulator.vanguard().eclState().aquifer().active()) {
127  OpmLog::warning("TPSA geomechanics does not handle numerical or analytical "
128  "aquifers, hence results may be inaccurate!");
129  }
130  }
131  else {
132  // Sanity check
133  const auto& mechSolver = simulator.vanguard().eclState().runspec().mechSolver();
134  if (mechSolver.tpsa()) {
135  std::string msg = "TPSA keyword in deck, but Tpsa-geomechanics disabled compile-time!";
136  OpmLog::error(msg);
137  throw std::runtime_error(msg);
138  }
139  }
140  }
141 
145  static void registerParameters()
146  {
147  // Register parameters for parent class
149 
150  // Geomech model parameters
151  GeomechModel::registerParameters();
152 
153  // VTK output parameters
155  }
156 
160  void finishInit()
161  {
162  // FlowProblemBlackoil::finishInit()
164 
165  // Read initial conditions and set material state
167 
168  // Calculate face properties
169  faceProps_.finishInit();
170 
171  // Set equation weights
173 
174  // Initialize the TPSA model
175  geoMechModel_.finishInit();
176  }
177 
183  void initialSolutionApplied() override
184  {
185  // Set up initial solution for the Flow model
187 
188  // Initialize soultions as zero
189  auto& uCur = geoMechModel_.solution(/*timeIdx=*/0);
190  uCur = Scalar(0.0);
191 
192  // Loop through grid and set initial solution from material state
193  ElementContext elemCtx(this->simulator());
194  for (const auto& elem : elements(this->gridView())) {
195  // Ignore everything which is not in the interior if the current process' piece of the grid
196  if (elem.partitionType() != Dune::InteriorEntity) {
197  continue;
198  }
199 
200  // Loop over sub control volumes and set initial solutions
201  elemCtx.updateStencil(elem);
202  for (unsigned dofIdx = 0; dofIdx < elemCtx.numPrimaryDof(/*timeIdx=*/0); ++dofIdx) {
203  const unsigned globalIdx = elemCtx.globalSpaceIndex(dofIdx, /*timeIdx=*/0);
204  auto& priVars = uCur[globalIdx];
205  priVars.assignNaive(initialMaterialState_[globalIdx]);
206  priVars.checkDefined();
207  }
208  }
209 
210  // synchronize the ghost/overlapping DOFs (if necessary)
211  geoMechModel_.syncOverlap();
212 
213  // Set history solutions to the initial solution.
214  for (unsigned timeIdx = 1; timeIdx < historySize; ++timeIdx) {
215  geoMechModel_.solution(timeIdx) = geoMechModel_.solution(/*timeIdx=*/0);
216  }
217 
218  // Set material state
219  geoMechModel_.updateMaterialState(/*timeIdx=*/0);
220  }
221 
226  {
227  // Average shear modulus over domain
228  Scalar avgSmodulus = 0.0;
229  const auto& gridView = this->gridView();
230  ElementContext elemCtx(this->simulator());
231  for(const auto& elem: elements(gridView, Dune::Partitions::interior)) {
232  elemCtx.updatePrimaryStencil(elem);
233  int elemIdx = elemCtx.globalSpaceIndex(/*spaceIdx=*/0, /*timeIdx=*/0);
234  avgSmodulus += this->shearModulus(elemIdx);
235  }
236  std::size_t numDof = this->model().numGridDof();
237  const auto& comm = this->simulator().vanguard().grid().comm();
238  avgSmodulus = comm.sum(avgSmodulus);
239  Scalar numTotalDof = comm.sum(numDof);
240  avgSmodulus /= numTotalDof;
241  avgSmodulus = std::sqrt(avgSmodulus);
242 
243  for (unsigned eqIdx = 0; eqIdx < numEq; ++eqIdx) {
244  if (eqIdx < contiRotEqIdx) {
245  geoMechModel_.setEqWeight(eqIdx, 1 / avgSmodulus);
246  }
247  else {
248  geoMechModel_.setEqWeight(eqIdx, avgSmodulus);
249  }
250  }
251  }
252 
256  void beginTimeStep() override
257  {
258  // Call parent class beginTimeStep()
260 
261  // Update mechanics boundary conditions.
262  // NOTE: Flow boundary conditions should be updated in ParentType::beginTimeStep()
263  if (this->nonTrivialBoundaryConditions()) {
264  geoMechModel_.linearizer().updateBoundaryConditionData();
265  }
266  }
267 
280  std::pair<BCMECHType, Dune::FieldVector<Evaluation, 3>>
281  mechBoundaryCondition(const unsigned int globalSpaceIdx, const int directionId)
282  {
283  // Default boundary conditions if BCCON/BCPROP not defined
284  if (!this->nonTrivialBoundaryConditions()) {
285  return { BCMECHType::NONE, Dune::FieldVector<Evaluation, 3>{0.0, 0.0, 0.0} };
286  }
287 
288  // Default for BCPROP index = 0 or no BCPROP defined at current episode
289  FaceDir::DirEnum dir = FaceDir::FromIntersectionIndex(directionId);
290  const auto& schedule = this->simulator().vanguard().schedule();
291  if (this->bcindex_(dir)[globalSpaceIdx] == 0 || schedule[this->episodeIndex()].bcprop.size() == 0) {
292  return { BCMECHType::NONE, Dune::FieldVector<Evaluation, 3>{0.0, 0.0, 0.0} };
293  }
294 
295  // Get current BC
296  const auto& bc = schedule[this->episodeIndex()].bcprop[this->bcindex_(dir)[globalSpaceIdx]];
297  if (bc.bcmechtype == BCMECHType::FREE) {
298  return { BCMECHType::FREE, Dune::FieldVector<Evaluation, 3>{0.0, 0.0, 0.0} };
299  }
300  else {
301  return { bc.bcmechtype, Dune::FieldVector<Evaluation, 3>{0.0, 0.0, 0.0} };
302  }
303  }
304 
314  void tpsaSource(Dune::FieldVector<Evaluation, numEq>& sourceTerm,
315  unsigned globalSpaceIdx,
316  unsigned timeIdx)
317  {
318  sourceTerm = 0.0;
319 
320  // Coupling term Flow -> TPSA
321  const auto biot = this->biotCoeff(globalSpaceIdx);
322  const auto lameParam = this->lame(globalSpaceIdx);
323 
324  const auto& iq = this->model().intensiveQuantities(globalSpaceIdx, timeIdx);
325  const auto& fs = iq.fluidState();
326  const auto pres = decay<Scalar>(fs.pressure(this->refPressurePhaseIdx_()));
327  const auto initPres = this->initialFluidState(globalSpaceIdx).pressure(this->refPressurePhaseIdx_());
328 
329  auto sourceFromFlow = -biot / lameParam * (pres - initPres);
330  sourceTerm[contiSolidPresEqIdx] += sourceFromFlow;
331  }
332 
342  Scalar rockMechPoroChange(unsigned elementIdx, unsigned timeIdx) const
343  {
344  // TODO: get timeIdx=1 solid pressure from a cached materialState (or intensiveQuantities) if/when implemented
345  assert (timeIdx <= historySize);
346  const auto solidPres = (timeIdx == 0) ?
347  decay<Scalar>( geoMechModel_.materialState(elementIdx, /*timeIdx=*/timeIdx).solidPressure()) :
348  geoMechModel_.solution(/*timeIdx=*/timeIdx)[elementIdx][solidPres0Idx];
349  const auto biot = this->biotCoeff(elementIdx);
350  const auto lameParam = this->lame(elementIdx);
351 
352  return biot / lameParam * solidPres;
353  }
354 
355  // ///
356  // Public get functions
357  // ///
365  Scalar weightAverage(unsigned globalElemIdxIn, unsigned globalElemIdxOut)
366  {
367  return faceProps_.weightAverage(globalElemIdxIn, globalElemIdxOut);
368  }
369 
377  Scalar weightAverageBoundary(unsigned globalElemIdxIn, unsigned boundaryFaceIdx) const
378  {
379  return faceProps_.weightAverageBoundary(globalElemIdxIn, boundaryFaceIdx);
380  }
381 
389  Scalar weightProduct(unsigned globalElemIdxIn, unsigned globalElemIdxOut) const
390  {
391  return faceProps_.weightProduct(globalElemIdxIn, globalElemIdxOut);
392  }
393 
401  Scalar normalDistance(unsigned globalElemIdxIn, unsigned globalElemIdxOut) const
402  {
403  return faceProps_.normalDistance(globalElemIdxIn, globalElemIdxOut);
404  }
405 
413  Scalar normalDistanceBoundary(unsigned globalElemIdxIn, unsigned boundaryFaceIdx) const
414  {
415  return faceProps_.normalDistanceBoundary(globalElemIdxIn, boundaryFaceIdx);
416  }
417 
425  DimVector cellFaceNormal(unsigned globalElemIdxIn, unsigned globalElemIdxOut)
426  {
427  return faceProps_.cellFaceNormal(globalElemIdxIn, globalElemIdxOut);
428  }
429 
437  const DimVector& cellFaceNormalBoundary(unsigned globalElemIdxIn, unsigned boundaryFaceIdx) const
438  {
439  return faceProps_.cellFaceNormalBoundary(globalElemIdxIn, boundaryFaceIdx);
440  }
441 
448  Scalar shearModulus(unsigned globalElemIdx) const
449  {
450  return faceProps_.shearModulus(globalElemIdx);
451  }
452 
458  bool laggedScheme() const
459  {
460  const auto& mechSolver = this->simulator().vanguard().eclState().runspec().mechSolver();
461  return mechSolver.laggedScheme();
462  }
463 
469  bool fixedStressScheme() const
470  {
471  const auto& mechSolver = this->simulator().vanguard().eclState().runspec().mechSolver();
472  return mechSolver.fixedStressScheme();
473  }
474 
480  const GeomechModel& geoMechModel() const
481  {
482  return geoMechModel_;
483  }
484 
490  GeomechModel& geoMechModel()
491  {
492  return geoMechModel_;
493  }
494 
500  std::pair<int, int> fixedStressParameters() const
501  {
502  const auto& mechSolver = this->simulator().vanguard().eclState().runspec().mechSolver();
503  return std::make_pair(mechSolver.fixedStressMinIter(), mechSolver.fixedStressMaxIter());
504  }
505 
506 protected:
507  // ///
508  // Protected functions
509  // ///
514  {
515  // ///
516  // OBS: No equilibration keywords (e.g., STREQUIL) implemented yet!
517  // ///
518 
519  // Set all initial material state variables to zero
520  std::size_t numDof = this->model().numGridDof();
521  initialMaterialState_.resize(numDof);
522  for (std::size_t dofIdx = 0; dofIdx < numDof; ++dofIdx) {
523  auto& dofMaterialState = initialMaterialState_[dofIdx];
524  for (unsigned dirIdx = 0; dirIdx < 3; ++dirIdx) {
525  dofMaterialState.setDisplacement(dirIdx, 0.0);
526  dofMaterialState.setRotation(dirIdx, 0.0);
527  }
528  dofMaterialState.setSolidPressure(0.0);
529  }
530  }
531 
532 private:
533  FaceProperties faceProps_;
534  GeomechModel geoMechModel_;
535 
536  std::vector<Scalar> biotcoeff_;
537  std::vector<InitialMaterialState> initialMaterialState_;
538 }; // class FlowProblemTPSA
539 
540 } // namespace Opm
541 
542 #endif
Scalar weightAverage(unsigned globalElemIdxIn, unsigned globalElemIdxOut)
Direct access to average (half-)weight at interface between two elements.
Definition: FlowProblemTPSA.hpp:365
This problem simulates an input file given in the data format used by the commercial ECLiPSE simulato...
Definition: FlowProblemBlackoil.hpp:86
VTK output module for TPSA quantities.
Definition: vtktpsamodule.hpp:48
Scalar weightProduct(unsigned elemIdx1, unsigned elemIdx2) const
Product of weights at interface between two elements.
Definition: FacePropertiesTPSA_impl.hpp:343
typename Properties::Detail::GetPropImpl< TypeTag, Property >::type::type GetPropType
get the type alias defined in the property (equivalent to old macro GET_PROP_TYPE(...))
Definition: propertysystem.hh:233
bool laggedScheme() const
Flow-TPSA lagged coupling scheme activated?
Definition: FlowProblemTPSA.hpp:458
bool fixedStressScheme() const
Flow-TPSA fixed-stress coupling scheme activated?
Definition: FlowProblemTPSA.hpp:469
const DimVector & cellFaceNormalBoundary(unsigned globalElemIdxIn, unsigned boundaryFaceIdx) const
Direct access to face normal at the boundary.
Definition: FlowProblemTPSA.hpp:437
const Scalar shearModulus(unsigned elemIdx) const
Return shear modulus of an element.
Definition: FacePropertiesTPSA.hpp:77
Scalar shearModulus(unsigned globalElemIdx) const
Direct access to shear modulus in an element.
Definition: FlowProblemTPSA.hpp:448
void readInitalConditionsTPSA_()
Read initial conditions and generate material state for TPSA model.
Definition: FlowProblemTPSA.hpp:513
Cell face properties needed in TPSA equation calculations.
Definition: FacePropertiesTPSA.hpp:48
This file provides the infrastructure to retrieve run-time parameters.
void beginTimeStep() override
Called by the simulator before each time integration.
Definition: FlowProblemBlackoil.hpp:315
Scalar weightProduct(unsigned globalElemIdxIn, unsigned globalElemIdxOut) const
Direct access to product of weights at interface between two elements.
Definition: FlowProblemTPSA.hpp:389
std::pair< BCMECHType, Dune::FieldVector< Evaluation, 3 > > mechBoundaryCondition(const unsigned int globalSpaceIdx, const int directionId)
Organize mechanics boundary conditions.
Definition: FlowProblemTPSA.hpp:281
FlowProblemTPSA(Simulator &simulator)
Constructor.
Definition: FlowProblemTPSA.hpp:103
static void registerParameters()
Register runtime parameters.
Definition: vtktpsamodule.hpp:88
Scalar weightAverageBoundary(unsigned elemIdx1, unsigned boundaryFaceIdx) const
Average (half-)weight at boundary interface.
Definition: FacePropertiesTPSA_impl.hpp:329
Structs needed for tpfalinearizer and its gpuparams struct extracted to be defined in one place that ...
Definition: blackoilbioeffectsmodules.hh:45
void tpsaSource(Dune::FieldVector< Evaluation, numEq > &sourceTerm, unsigned globalSpaceIdx, unsigned timeIdx)
Set mechanics source term, in particular coupling terms.
Definition: FlowProblemTPSA.hpp:314
static void registerParameters()
Register runtime parameters.
Definition: FlowProblemTPSA.hpp:145
void finishInit()
Called by the Opm::Simulator in order to initialize the problem.
Definition: FlowProblemBlackoil.hpp:324
This problem simulates an input file given in the data format used by the commercial ECLiPSE simulato...
DimVector cellFaceNormal(unsigned elemIdx1, unsigned elemIdx2)
Cell face normal at interface between two elements.
Definition: FacePropertiesTPSA_impl.hpp:402
Scalar biotCoeff(const Context &context, unsigned spaceIdx, unsigned timeIdx) const
Definition: FlowProblem.hpp:765
GeomechModel & geoMechModel()
Get TPSA model.
Definition: FlowProblemTPSA.hpp:490
static void registerParameters()
Registers all available parameters for the problem and the model.
Definition: FlowProblemBlackoil.hpp:175
void beginTimeStep() override
Called by the simulator before each time integration.
Definition: FlowProblemTPSA.hpp:256
std::pair< int, int > fixedStressParameters() const
Get fixed-stress iteration parameters.
Definition: FlowProblemTPSA.hpp:500
Problem for Flow-TPSA coupled simulations.
Definition: FlowProblemTPSA.hpp:62
Scalar normalDistance(unsigned globalElemIdxIn, unsigned globalElemIdxOut) const
Direct access to normal distance between two elements.
Definition: FlowProblemTPSA.hpp:401
void initialSolutionApplied() override
Set initial solution for the problem.
Definition: FlowProblemTPSA.hpp:183
DimVector cellFaceNormal(unsigned globalElemIdxIn, unsigned globalElemIdxOut)
Direct access to face normal between two elements.
Definition: FlowProblemTPSA.hpp:425
Scalar weightAverage(unsigned elemIdx1, unsigned elemIdx2) const
Average (half-)weight at interface between two elements.
Definition: FacePropertiesTPSA_impl.hpp:304
void initialSolutionApplied() override
Callback used by the model to indicate that the initial solution has been determined for all degrees ...
Definition: FlowProblemBlackoil.hpp:635
Scalar normalDistanceBoundary(unsigned elemIdx1, unsigned boundaryFaceIdx) const
Distance to boundary interface.
Definition: FacePropertiesTPSA_impl.hpp:385
Scalar normalDistanceBoundary(unsigned globalElemIdxIn, unsigned boundaryFaceIdx) const
Direct access to normal distance at the boundary.
Definition: FlowProblemTPSA.hpp:413
Scalar lame(const Context &context, unsigned spaceIdx, unsigned timeIdx) const
Definition: FlowProblem.hpp:755
void finishInit()
Initialize the problem.
Definition: FlowProblemTPSA.hpp:160
Definition: CollectDataOnIORank.hpp:50
Scalar rockMechPoroChange(unsigned elementIdx, unsigned timeIdx) const
Pore volume change due to geomechanics.
Definition: FlowProblemTPSA.hpp:342
const GeomechModel & geoMechModel() const
Get TPSA model.
Definition: FlowProblemTPSA.hpp:480
Scalar weightAverageBoundary(unsigned globalElemIdxIn, unsigned boundaryFaceIdx) const
Direct access to normal distance at the boundary.
Definition: FlowProblemTPSA.hpp:377
Scalar normalDistance(unsigned elemIdx1, unsigned elemIdx2) const
Distance between two elements.
Definition: FacePropertiesTPSA_impl.hpp:371
void computeAndSetEqWeights_()
Compute weights to rescale the TPSA equations.
Definition: FlowProblemTPSA.hpp:225
void finishInit()
Compute TPSA face properties.
Definition: FacePropertiesTPSA_impl.hpp:96
const DimVector & cellFaceNormalBoundary(unsigned elemIdx1, unsigned boundaryFaceIdx) const
Cell face normal of boundary interface.
Definition: FacePropertiesTPSA_impl.hpp:424