opm-simulators
tpsamodel.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 TPSA_MODEL_HPP
26 #define TPSA_MODEL_HPP
27 
28 #include <dune/grid/common/gridenums.hh>
29 
30 #include <dune/common/dynmatrix.hh>
31 #include <dune/common/dynvector.hh>
32 
33 #include <opm/grid/utility/ElementChunks.hpp>
34 
35 #include <opm/material/common/MathToolbox.hpp>
36 
38 #include <opm/models/tpsa/tpsabaseproperties.hpp>
39 #include <opm/models/utils/linearleastsquares.hpp>
41 
42 #include <array>
43 #include <memory>
44 #include <unordered_map>
45 
46 
47 namespace Opm {
48 
54 template <class TypeTag>
55 class TpsaModel
56 {
68 
69  enum { dimWorld = GridView::dimensionworld };
70  enum { historySize = getPropValue<TypeTag, Properties::SolutionHistorySizeTPSA>() };
71  enum { numEq = getPropValue<TypeTag, Properties::NumEqTPSA>() };
72 
73  enum { disp0Idx = Indices::disp0Idx };
74  enum { rot0Idx = Indices::rot0Idx };
75  enum { solidPres0Idx = Indices::solidPres0Idx };
76 
77  using MaterialState = MaterialStateTPSA<Evaluation>;
78 
79  using DimVector = Dune::FieldVector<Scalar, dimWorld>;
80  using SymTensor = Dune::FieldVector<Scalar, 6>;
81 
82 public:
87  {
88  protected:
89  SolutionVector blockVector_;
90  public:
97  TpsaBlockVectorWrapper(const std::string&, const std::size_t size)
98  : blockVector_(size)
99  {}
100 
104  TpsaBlockVectorWrapper() = default;
105 
110  {
111  TpsaBlockVectorWrapper result("dummy", 3);
112  result.blockVector_[0] = 1.0;
113  result.blockVector_[1] = 2.0;
114  result.blockVector_[2] = 3.0;
115 
116  return result;
117  }
118 
124  SolutionVector& blockVector()
125  { return blockVector_; }
126 
132  const SolutionVector& blockVector() const
133  { return blockVector_; }
134 
141  bool operator==(const TpsaBlockVectorWrapper& wrapper) const
142  {
143  return std::equal(this->blockVector_.begin(), this->blockVector_.end(),
144  wrapper.blockVector_.begin(), wrapper.blockVector_.end());
145  }
146 
152  template<class Serializer>
153  void serializeOp(Serializer& serializer)
154  {
155  serializer(blockVector_);
156  }
157  };
158 
159  // ///
160  // Public functions
161  // ///
167  explicit TpsaModel(Simulator& simulator)
168  : linearizer_(std::make_unique<Linearizer>())
169  , newtonMethod_(simulator)
170  , simulator_(simulator)
171  , element_chunks_(simulator.gridView(), Dune::Partitions::all, ThreadManager::maxThreads())
172  {
173  // Initialize equation weights to 1.0
174  eqWeights_.resize(numEq, 1.0);
175 
176  // Initialize historic solution vectors
177  // OBS: need at least history size = 2, due to time-derivative of solid-pressure in Flow coupling term
178  const std::size_t numDof = simulator_.model().numGridDof();
179  for (unsigned timeIdx = 0; timeIdx < historySize; ++timeIdx) {
180  solution_[timeIdx] = std::make_unique<TpsaBlockVectorWrapper>("solution", numDof);
181  }
182  }
183 
187  void finishInit()
188  {
189  // Initialize the linearizer
190  linearizer_->init(simulator_);
191 
192  // Resize material state vector
194  }
195 
199  static void registerParameters()
200  {
201  // Newton method parameters
203  }
204 
208  void prepareTPSA()
209  {
210  // Update historic solution
211  solution(/*timeIdx=*/1) = solution(/*timeIdx=*/0);
212  }
213 
219  void syncOverlap()
220  {
221  // Syncronize the solution on the ghost and overlap elements
222  using GhostSyncHandle = GridCommHandleGhostSync<PrimaryVariables,
223  SolutionVector,
224  DofMapper,
225  /*commCodim=*/0>;
226 
227  auto ghostSync = GhostSyncHandle(solution(/*timeIdx=*/0),
228  simulator_.model().dofMapper());
229 
230  simulator_.gridView().communicate(ghostSync,
231  Dune::InteriorBorder_All_Interface,
232  Dune::ForwardCommunication);
233  }
234 
242  void updateMaterialState(const unsigned /*timeIdx*/)
243  {
244  // Loop over all elements chuncks and update material state from current solution
245  const auto& elementMapper = simulator_.model().elementMapper();
246 #ifdef _OPENMP
247 #pragma omp parallel for
248 #endif
249  for (const auto& chunk : element_chunks_) {
250  for (const auto& elem : chunk) {
251  const unsigned globalIdx = elementMapper.index(elem);
252  auto& currSol = solution(/*timeIdx=*/0)[globalIdx];
253  setMaterialState_(globalIdx, /*timeIdx=*/0, currSol);
254  }
255  }
256  }
257 
258  // ///
259  // Public get and set functions
260  // ///
266  const Linearizer& linearizer() const
267  {
268  return *linearizer_;
269  }
270 
276  Linearizer& linearizer()
277  {
278  return *linearizer_;
279  }
280 
286  const NewtonMethod& newtonMethod() const
287  {
288  return newtonMethod_;
289  }
290 
296  NewtonMethod& newtonMethod()
297  {
298  return newtonMethod_;
299  }
300 
307  const SolutionVector& solution(unsigned timeIdx) const
308  {
309  return solution_[timeIdx]->blockVector();
310  }
311 
318  SolutionVector& solution(unsigned timeIdx)
319  {
320  return solution_[timeIdx]->blockVector();
321  }
322 
327  std::size_t numGridDof() const
328  {
329  return simulator_.model().numGridDof();
330  }
331 
336  std::size_t numTotalDof() const
337  {
338  return numGridDof() + numAuxiliaryDof();
339  }
340 
347  Scalar dofTotalVolume(unsigned globalIdx) const
348  {
349  return simulator_.model().dofTotalVolume(globalIdx);
350  }
351 
359  Scalar eqWeight(unsigned /*dofIdx*/, unsigned eqIdx) const
360  {
361  return eqWeights_[eqIdx];
362  }
363 
370  void setEqWeight(unsigned eqIdx, Scalar value)
371  {
372  eqWeights_[eqIdx] = value;
373  }
374 
379  std::size_t numAuxiliaryModules() const
380  {
381  return 0;
382  }
383 
388  std::size_t numAuxiliaryDof() const
389  {
390  return 0;
391  }
392 
402  const MaterialState& materialState(const unsigned globalIdx, unsigned /*timeIdx*/) const
403  {
404  return materialState_[globalIdx];
405  }
406 
416  DimVector disp(const unsigned globalIdx, const bool /*with_fracture*/) const
417  {
418  DimVector d;
419  for (std::size_t i = 0; i < 3; ++i) {
420  d[i] = decay<Scalar>(materialState_[globalIdx].displacement(i));
421  }
422  return d;
423  }
424 
433  SymTensor delstress(const unsigned /*globalIdx*/) const
434  {
435  SymTensor val;
436  return val;
437  }
438 
447  SymTensor fractureStress(const unsigned /*globalIdx*/) const
448  {
449  SymTensor val;
450  return val;
451  }
452 
461  SymTensor linstress(const unsigned /*globalIdx*/) const
462  {
463  SymTensor val;
464  return val;
465  }
466 
477  SymTensor stress(const unsigned globalIdx, const bool /*with_fracture*/) const
478  {
479  SymTensor stressOutput;
480  const auto& stressInfo = linearizer_->getStressInfo();
481  if (!stressInfo.empty()) {
482  const auto& stressInfoGlobI = stressInfo[globalIdx];
483 
484  // Setup least-squares matrix of face normals and right-hand side of traction vectors
485  // per faces. Matrix shape = 3 rows per face x 6 columns for each (symmetric) stress
486  // tensor component.
487  std::unordered_multimap<int, std::size_t> faceIdMap;
488  std::size_t mapSize = 0;
489  for (std::size_t sInfoIdx = 0; sInfoIdx < stressInfoGlobI.size(); ++sInfoIdx) {
490  const auto faceId = stressInfoGlobI[sInfoIdx].faceId;
491 
492  // OBS: NNC faces are not added to least squares system, since TPSA does not handle
493  // NNC connections, like numerical aquifers, yet!
494  if (faceId < 0 || stressInfoGlobI[sInfoIdx].faceArea == 0.0) {
495  continue;
496  }
497  if (!faceIdMap.contains(faceId)) {
498  ++mapSize;
499  }
500  faceIdMap.insert({faceId, sInfoIdx});
501  }
502 
503  // If no valid faces exist for cell, return zero SymTensor
504  if (mapSize == 0) {
505  return stressOutput;
506  }
507 
508  Dune::DynamicMatrix<Scalar> mat(3 * mapSize, 6);
509  Dune::DynamicVector<Scalar> rhs(3 * mapSize);
510  std::size_t rowIdx = 0;
511  for (auto it = faceIdMap.begin(); it != faceIdMap.end();) {
512  auto [first, last] = faceIdMap.equal_range(it->first);
513 
514  // Loop over possible multiple of the same face
515  Scalar sumFaceArea = 0.0;
516  for (auto inner = first; inner != last; ++inner) {
517  const auto sInfoIdx = inner->second;
518  const auto& faceStressInfo = stressInfoGlobI[sInfoIdx];
519 
520  // One normal per face
521  if (inner == first) {
522  const auto& fNormal = faceStressInfo.faceNormal;
523  mat[3*rowIdx][0] = fNormal[0];
524  mat[3*rowIdx][3] = fNormal[1];
525  mat[3*rowIdx][4] = fNormal[2];
526 
527  mat[3*rowIdx + 1][1] = fNormal[1];
528  mat[3*rowIdx + 1][3] = fNormal[0];
529  mat[3*rowIdx + 1][5] = fNormal[2];
530 
531  mat[3*rowIdx + 2][2] = fNormal[2];
532  mat[3*rowIdx + 2][4] = fNormal[0];
533  mat[3*rowIdx + 2][5] = fNormal[1];
534  }
535 
536  // Add traction vectors for same faces
537  const auto& fTraction = faceStressInfo.traction;
538  rhs[3*rowIdx] += fTraction[0];
539  rhs[3*rowIdx + 1] += fTraction[1];
540  rhs[3*rowIdx + 2] += fTraction[2];
541 
542  // Sum face area
543  sumFaceArea += faceStressInfo.faceArea;
544  }
545  // Divide rhs for (unique) face by sum of face areas
546  rhs[3*rowIdx] /= sumFaceArea;
547  rhs[3*rowIdx + 1] /= sumFaceArea;
548  rhs[3*rowIdx + 2] /= sumFaceArea;
549 
550  // Move to next unique face
551  ++rowIdx;
552  it = last;
553  }
554 
555  // Use least squares solver for underdetermined system
556  LinearLeastSquares<Scalar> lsq(mat, rhs);
557  lsq.solve();
558 
559  // Reconstructed stress tensor at cell center
560  const auto& stress = lsq.x();
561  stressOutput[0] = stress[0]; // XX
562  stressOutput[1] = stress[1]; // YY
563  stressOutput[2] = stress[2]; // ZZ
564  stressOutput[3] = stress[5]; // YZ
565  stressOutput[4] = stress[4]; // XZ
566  stressOutput[5] = stress[3]; // XY
567  }
568  return stressOutput;
569  }
570 
580  SymTensor strain(const unsigned /*globalIdx*/, const bool /*with_fracture*/) const
581  {
582  SymTensor val;
583  return val;
584  }
585 
594  Scalar mechPotentialForce(unsigned /*globalIdx*/) const
595  {
596  return Scalar(0.0);
597  }
598 
607  Scalar mechPotentialPressForce(unsigned /*globalIdx*/) const
608  {
609  return Scalar(0.0);
610  }
611 
620  Scalar mechPotentialTempForce(unsigned /*globalIdx*/) const
621  {
622  return Scalar(0.0);
623  }
624 
625 protected:
626  // ///
627  // Protected functions
628  // ///
635  {
636  const std::size_t numDof = simulator_.model().numGridDof();
637  materialState_.resize(numDof);
638  }
639 
640 private:
641  // ///
642  // Private functions
643  // ///
653  void setMaterialState_(const unsigned globalIdx, const unsigned /*timeIdx*/, PrimaryVariables& values)
654  {
655  auto& dofMaterialState = materialState_[globalIdx];
656  for (unsigned dirIdx = 0; dirIdx < 3; ++dirIdx) {
657  dofMaterialState.setDisplacement(dirIdx, values.makeEvaluation(disp0Idx + dirIdx, 0));
658  dofMaterialState.setRotation(dirIdx, values.makeEvaluation(rot0Idx + dirIdx, 0));
659  }
660  dofMaterialState.setSolidPressure(values.makeEvaluation(solidPres0Idx, 0));
661  }
662 
663  std::unique_ptr<Linearizer> linearizer_;
664  NewtonMethod newtonMethod_;
665  Simulator& simulator_;
666  ElementChunks<GridView, Dune::Partitions::All> element_chunks_;
667 
668  std::array<std::unique_ptr<TpsaBlockVectorWrapper>, historySize> solution_;
669  std::vector<Scalar> eqWeights_;
670  std::vector<MaterialState> materialState_;
671 }; // class TpsaModel
672 
673 } // namespace Opm
674 
675 
676 #endif
Data handle for parallel communication which can be used to set the values values of ghost and overla...
Definition: gridcommhandles.hh:105
Simplifies multi-threaded capabilities.
Definition: threadmanager.hpp:35
static void registerParameters()
Register all run-time parameters for the Newton method.
Definition: newtonmethod.hh:135
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
std::size_t numGridDof() const
Return number of degrees of freedom in the grid from the Flow model.
Definition: tpsamodel.hpp:327
const SolutionVector & solution(unsigned timeIdx) const
Get reference to history solution vector.
Definition: tpsamodel.hpp:307
Linear least squares calculations and properties.
Definition: linearleastsquares.hpp:44
Scalar mechPotentialTempForce(unsigned) const
Output potential temparature forces.
Definition: tpsamodel.hpp:620
const NewtonMethod & newtonMethod() const
Return the Newton method.
Definition: tpsamodel.hpp:286
void resizeMaterialState_()
Resize material state vector.
Definition: tpsamodel.hpp:634
Definition: fvbaseprimaryvariables.hh:161
Scalar eqWeight(unsigned, unsigned eqIdx) const
Return equation weights.
Definition: tpsamodel.hpp:359
Scalar mechPotentialForce(unsigned) const
Output potential forces.
Definition: tpsamodel.hpp:594
std::size_t numAuxiliaryModules() const
Return number of auxillary modules.
Definition: tpsamodel.hpp:379
bool operator==(const TpsaBlockVectorWrapper &wrapper) const
Check if incoming block vector is the same as current.
Definition: tpsamodel.hpp:141
Small block vector wrapper class for model solutions.
Definition: tpsamodel.hpp:86
void setEqWeight(unsigned eqIdx, Scalar value)
Set weights for equation.
Definition: tpsamodel.hpp:370
Structs needed for tpfalinearizer and its gpuparams struct extracted to be defined in one place that ...
Definition: blackoilbioeffectsmodules.hh:45
SolutionVector & blockVector()
Get reference of block vector.
Definition: tpsamodel.hpp:124
static TpsaBlockVectorWrapper serializationTestObject()
Test function for serialization.
Definition: tpsamodel.hpp:109
const SolutionVector & blockVector() const
Get const reference of block vector.
Definition: tpsamodel.hpp:132
TpsaModel(Simulator &simulator)
Constructor.
Definition: tpsamodel.hpp:167
std::size_t numAuxiliaryDof() const
Return number of auxillary degrees of freedom.
Definition: tpsamodel.hpp:388
TpsaBlockVectorWrapper()=default
Default constructor.
void updateMaterialState(const unsigned)
Update material state for all cells.
Definition: tpsamodel.hpp:242
const Linearizer & linearizer() const
Return the linearizer.
Definition: tpsamodel.hpp:266
void solve()
Solve linear least squares system.
Definition: linearleastsquares.hpp:66
SymTensor delstress(const unsigned) const
Output (del?)stress tensor.
Definition: tpsamodel.hpp:433
const Vector & x() const
Read-only vector of calculated coefficient vector.
Definition: linearleastsquares.hpp:76
SymTensor stress(const unsigned globalIdx, const bool) const
Output stress tensor.
Definition: tpsamodel.hpp:477
SolutionVector & solution(unsigned timeIdx)
Get reference to history solution vector.
Definition: tpsamodel.hpp:318
SymTensor strain(const unsigned, const bool) const
Output strain tensor.
Definition: tpsamodel.hpp:580
NewtonMethod & newtonMethod()
Return the Newton method.
Definition: tpsamodel.hpp:296
void syncOverlap()
Sync primary variables in overlapping cells.
Definition: tpsamodel.hpp:219
void finishInit()
Initialize TPSA model.
Definition: tpsamodel.hpp:187
SymTensor fractureStress(const unsigned) const
Output fracture stress tensor.
Definition: tpsamodel.hpp:447
The Opm property system, traits with inheritance.
std::size_t numTotalDof() const
Return the total number of degrees of freedom.
Definition: tpsamodel.hpp:336
Simplifies multi-threaded capabilities.
DimVector disp(const unsigned globalIdx, const bool) const
Output displacement vector.
Definition: tpsamodel.hpp:416
void prepareTPSA()
Prepare TPSA model for coupled Flow-TPSA scheme.
Definition: tpsamodel.hpp:208
Scalar mechPotentialPressForce(unsigned) const
Output potential pressure forces.
Definition: tpsamodel.hpp:607
SymTensor linstress(const unsigned) const
Output linear stress tensor.
Definition: tpsamodel.hpp:461
TpsaBlockVectorWrapper(const std::string &, const std::size_t size)
Constructor.
Definition: tpsamodel.hpp:97
const MaterialState & materialState(const unsigned globalIdx, unsigned) const
Return current material state.
Definition: tpsamodel.hpp:402
void serializeOp(Serializer &serializer)
Serializing operation.
Definition: tpsamodel.hpp:153
Linearizer & linearizer()
Return the linearizer.
Definition: tpsamodel.hpp:276
TPSA geomechanics model.
Definition: tpsamodel.hpp:55
static void registerParameters()
Register runtime parameters.
Definition: tpsamodel.hpp:199
Scalar dofTotalVolume(unsigned globalIdx) const
Return the total grid volume from the Flow model.
Definition: tpsamodel.hpp:347