tpsamodel.hpp
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 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/common/utility/SymmTensor.hpp>
36#include <opm/common/utility/VoigtArray.hpp>
37#include <opm/material/common/MathToolbox.hpp>
38
43
44#include <array>
45#include <memory>
46#include <unordered_map>
47
48
49namespace Opm {
50
56template <class TypeTag>
58{
70
71 enum { dimWorld = GridView::dimensionworld };
72 enum { historySize = getPropValue<TypeTag, Properties::SolutionHistorySizeTPSA>() };
73 enum { numEq = getPropValue<TypeTag, Properties::NumEqTPSA>() };
74
75 enum { disp0Idx = Indices::disp0Idx };
76 enum { rot0Idx = Indices::rot0Idx };
77 enum { solidPres0Idx = Indices::solidPres0Idx };
78
79 using MaterialState = MaterialStateTPSA<Evaluation>;
80
81 using DimVector = Dune::FieldVector<Scalar, dimWorld>;
82 using SymTensor = SymmTensor<Scalar>;
83 using PotForceVector = Dune::BlockVector<Scalar>;
84
85public:
90 {
91 protected:
92 SolutionVector blockVector_;
93 public:
100 TpsaBlockVectorWrapper(const std::string&, const std::size_t size)
101 : blockVector_(size)
102 {}
103
108
113 {
114 TpsaBlockVectorWrapper result("dummy", 3);
115 result.blockVector_[0] = 1.0;
116 result.blockVector_[1] = 2.0;
117 result.blockVector_[2] = 3.0;
118
119 return result;
120 }
121
127 SolutionVector& blockVector()
128 { return blockVector_; }
129
135 const SolutionVector& blockVector() const
136 { return blockVector_; }
137
144 bool operator==(const TpsaBlockVectorWrapper& wrapper) const
145 {
146 return std::equal(this->blockVector_.begin(), this->blockVector_.end(),
147 wrapper.blockVector_.begin(), wrapper.blockVector_.end());
148 }
149
155 template<class Serializer>
156 void serializeOp(Serializer& serializer)
157 {
158 serializer(blockVector_);
159 }
160 };
161
162 // ///
163 // Public functions
164 // ///
170 explicit TpsaModel(Simulator& simulator)
171 : linearizer_(std::make_unique<Linearizer>())
172 , newtonMethod_(simulator)
173 , simulator_(simulator)
174 , element_chunks_(simulator.gridView(), Dune::Partitions::all, ThreadManager::maxThreads())
175 {
176 // Initialize equation weights to 1.0
177 eqWeights_.resize(numEq, 1.0);
178
179 // Initialize historic solution vectors
180 // OBS: need at least history size = 2, due to time-derivative of solid-pressure in Flow coupling term
181 const std::size_t numDof = simulator_.model().numGridDof();
182 for (unsigned timeIdx = 0; timeIdx < historySize; ++timeIdx) {
183 solution_[timeIdx] = std::make_unique<TpsaBlockVectorWrapper>("solution", numDof);
184 }
185
186 // Initialize potential force vectors
187 mechPotPresForce_.resize(numDof);
188 }
189
194 {
195 // Initialize the linearizer
196 linearizer_->init(simulator_);
197
198 // Resize material state vector
200 }
201
205 static void registerParameters()
206 {
207 // Newton method parameters
209 }
210
215 {
216 // Update historic solution
217 solution(/*timeIdx=*/1) = solution(/*timeIdx=*/0);
218 }
219
226 {
227 // Syncronize the solution on the ghost and overlap elements
228 using GhostSyncHandle = GridCommHandleGhostSync<PrimaryVariables,
229 SolutionVector,
230 DofMapper,
231 /*commCodim=*/0>;
232
233 auto ghostSync = GhostSyncHandle(solution(/*timeIdx=*/0),
234 simulator_.model().dofMapper());
235
236 simulator_.gridView().communicate(ghostSync,
237 Dune::InteriorBorder_All_Interface,
238 Dune::ForwardCommunication);
239 }
240
248 void updateMaterialState(const unsigned /*timeIdx*/)
249 {
250 // Loop over all elements chuncks and update material state from current solution
251 const auto& elementMapper = simulator_.model().elementMapper();
252#ifdef _OPENMP
253#pragma omp parallel for
254#endif
255 for (const auto& chunk : element_chunks_) {
256 for (const auto& elem : chunk) {
257 const unsigned globalIdx = elementMapper.index(elem);
258 auto& currSol = solution(/*timeIdx=*/0)[globalIdx];
259 setMaterialState_(globalIdx, /*timeIdx=*/0, currSol);
260 }
261 }
262 }
263
264 // ///
265 // Public get and set functions
266 // ///
272 const Linearizer& linearizer() const
273 {
274 return *linearizer_;
275 }
276
282 Linearizer& linearizer()
283 {
284 return *linearizer_;
285 }
286
292 const NewtonMethod& newtonMethod() const
293 {
294 return newtonMethod_;
295 }
296
302 NewtonMethod& newtonMethod()
303 {
304 return newtonMethod_;
305 }
306
313 const SolutionVector& solution(unsigned timeIdx) const
314 {
315 return solution_[timeIdx]->blockVector();
316 }
317
324 SolutionVector& solution(unsigned timeIdx)
325 {
326 return solution_[timeIdx]->blockVector();
327 }
328
333 std::size_t numGridDof() const
334 {
335 return simulator_.model().numGridDof();
336 }
337
342 std::size_t numTotalDof() const
343 {
344 return numGridDof() + numAuxiliaryDof();
345 }
346
353 Scalar dofTotalVolume(unsigned globalIdx) const
354 {
355 return simulator_.model().dofTotalVolume(globalIdx);
356 }
357
365 Scalar eqWeight(unsigned /*dofIdx*/, unsigned eqIdx) const
366 {
367 return eqWeights_[eqIdx];
368 }
369
376 void setEqWeight(unsigned eqIdx, Scalar value)
377 {
378 eqWeights_[eqIdx] = value;
379 }
380
385 std::size_t numAuxiliaryModules() const
386 {
387 return 0;
388 }
389
394 std::size_t numAuxiliaryDof() const
395 {
396 return 0;
397 }
398
408 const MaterialState& materialState(const unsigned globalIdx, unsigned /*timeIdx*/) const
409 {
410 return materialState_[globalIdx];
411 }
412
422 DimVector disp(const unsigned globalIdx, const bool /*with_fracture*/) const
423 {
424 DimVector d;
425 for (std::size_t i = 0; i < 3; ++i) {
426 d[i] = decay<Scalar>(materialState_[globalIdx].displacement(i));
427 }
428 return d;
429 }
430
437 DimVector rotation(const unsigned globalIdx) const
438 {
439 DimVector rot;
440 for (std::size_t i = 0; i < 3; ++i) {
441 rot[i] = decay<Scalar>(materialState_[globalIdx].rotation(i));
442 }
443 return rot;
444 }
445
452 Scalar solidPressure(const unsigned globalIdx) const
453 {
454 return decay<Scalar>(materialState_[globalIdx].solidPressure());
455 }
456
463 SymTensor delstress(const unsigned globalIdx) const
464 {
465 return stress(globalIdx, false);
466 }
467
476 SymTensor fractureStress(const unsigned /*globalIdx*/) const
477 {
478 SymTensor val;
479 return val;
480 }
481
490 SymTensor linstress(const unsigned globalIdx) const
491 {
492 // Subtract the potential forces from pressure and temperature from the main diagonal of
493 // the total stress
494 SymTensor linStressTensor = stress(globalIdx, false);
495 const auto potForce = mechPotentialForce(globalIdx);
496 for (const auto& dirIdx : SymTensor::diag_indices) {
497 linStressTensor[dirIdx] -= potForce;
498 }
499 return -1.0 * linStressTensor;
500 }
501
512 SymTensor stress(const unsigned globalIdx, const bool /*with_fracture*/) const
513 {
514 SymTensor stressOutput;
515 const auto& stressInfo = linearizer_->getStressInfo();
516 if (!stressInfo.empty()) {
517 const auto& stressInfoGlobI = stressInfo[globalIdx];
518
519 // Setup least-squares matrix of face normals and right-hand side of traction vectors
520 // per faces. Matrix shape = 3 rows per face x 6 columns for each (symmetric) stress
521 // tensor component.
522 std::unordered_multimap<int, std::size_t> faceIdMap;
523 std::size_t mapSize = 0;
524 for (std::size_t sInfoIdx = 0; sInfoIdx < stressInfoGlobI.size(); ++sInfoIdx) {
525 const auto faceId = stressInfoGlobI[sInfoIdx].faceId;
526
527 // OBS: NNC faces are not added to least squares system, since TPSA does not handle
528 // NNC connections, like numerical aquifers, yet!
529 if (faceId < 0 || stressInfoGlobI[sInfoIdx].faceArea == 0.0) {
530 continue;
531 }
532 if (!faceIdMap.contains(faceId)) {
533 ++mapSize;
534 }
535 faceIdMap.insert({faceId, sInfoIdx});
536 }
537
538 // If no valid faces exist for cell, return zero SymTensor
539 if (mapSize == 0) {
540 return stressOutput;
541 }
542
543 Dune::DynamicMatrix<Scalar> mat(3 * mapSize, 6);
544 Dune::DynamicVector<Scalar> rhs(3 * mapSize);
545 std::size_t rowIdx = 0;
546 for (auto it = faceIdMap.begin(); it != faceIdMap.end();) {
547 auto [first, last] = faceIdMap.equal_range(it->first);
548
549 // Loop over possible multiple of the same face
550 Scalar sumFaceArea = 0.0;
551 for (auto inner = first; inner != last; ++inner) {
552 const auto sInfoIdx = inner->second;
553 const auto& faceStressInfo = stressInfoGlobI[sInfoIdx];
554
555 // One normal per face
556 if (inner == first) {
557 const auto& fNormal = faceStressInfo.faceNormal;
558 mat[3*rowIdx][0] = fNormal[0];
559 mat[3*rowIdx][3] = fNormal[1];
560 mat[3*rowIdx][4] = fNormal[2];
561
562 mat[3*rowIdx + 1][1] = fNormal[1];
563 mat[3*rowIdx + 1][3] = fNormal[0];
564 mat[3*rowIdx + 1][5] = fNormal[2];
565
566 mat[3*rowIdx + 2][2] = fNormal[2];
567 mat[3*rowIdx + 2][4] = fNormal[0];
568 mat[3*rowIdx + 2][5] = fNormal[1];
569 }
570
571 // Add traction vectors for same faces
572 const auto& fTraction = faceStressInfo.traction;
573 rhs[3*rowIdx] += fTraction[0];
574 rhs[3*rowIdx + 1] += fTraction[1];
575 rhs[3*rowIdx + 2] += fTraction[2];
576
577 // Sum face area
578 sumFaceArea += faceStressInfo.faceArea;
579 }
580 // Divide rhs for (unique) face by sum of face areas
581 rhs[3*rowIdx] /= sumFaceArea;
582 rhs[3*rowIdx + 1] /= sumFaceArea;
583 rhs[3*rowIdx + 2] /= sumFaceArea;
584
585 // Move to next unique face
586 ++rowIdx;
587 it = last;
588 }
589
590 // Use least squares solver for underdetermined system
591 LinearLeastSquares<Scalar> lsq(mat, rhs);
592 lsq.solve();
593
594 // Reconstructed stress tensor at cell center
595 const auto& stress = lsq.x();
596 stressOutput[VoigtIndex::XX] = stress[0]; // XX
597 stressOutput[VoigtIndex::YY] = stress[1]; // YY
598 stressOutput[VoigtIndex::ZZ] = stress[2]; // ZZ
599 stressOutput[VoigtIndex::YZ] = stress[5]; // YZ
600 stressOutput[VoigtIndex::XZ] = stress[4]; // XZ
601 stressOutput[VoigtIndex::XY] = stress[3]; // XY
602 }
603 return stressOutput;
604 }
605
613 SymTensor strain(const unsigned globalIdx, const bool /*with_fracture*/) const
614 {
615 // Deviatoric stress
616 auto stressDev = this->linstress(globalIdx);
617 Scalar traceStress = 1.0 / 3.0 * stressDev.trace();
618 stressDev[VoigtIndex::XX] -= traceStress;
619 stressDev[VoigtIndex::YY] -= traceStress;
620 stressDev[VoigtIndex::ZZ] -= traceStress;
621
622 // Deviatoric strain
623 auto& problem = simulator_.problem();
624 const auto sMod = problem.shearModulus(globalIdx);
625 SymTensor strainDev = 1.0 / (2.0 * sMod) * stressDev;
626
627 // Volumetric strain
628 const auto lameParam = problem.lame(globalIdx);
629 Scalar strainVolTerm = traceStress / (3.0 * lameParam + 2 * sMod);
630
631 SymTensor strainVol;
632 strainVol[VoigtIndex::XX] = strainVolTerm;
633 strainVol[VoigtIndex::YY] = strainVolTerm;
634 strainVol[VoigtIndex::ZZ] = strainVolTerm;
635
636 // Total
637 SymTensor strainOutput = strainDev + strainVol;
638 return strainOutput;
639 }
640
647 Scalar mechPotentialForce(unsigned globalIdx) const
648 {
649 return mechPotentialPressForce(globalIdx);
650 }
651
658 Scalar mechPotentialPressForce(unsigned globalIdx) const
659 {
660 return mechPotPresForce_[globalIdx];
661 }
662
663 void setMechPotentialPressForce(unsigned globalIdx, Scalar val)
664 {
665 mechPotPresForce_[globalIdx] = val;
666 }
667
676 Scalar mechPotentialTempForce(unsigned /*globalIdx*/) const
677 {
678 return Scalar(0.0);
679 }
680
681protected:
682 // ///
683 // Protected functions
684 // ///
691 {
692 const std::size_t numDof = simulator_.model().numGridDof();
693 materialState_.resize(numDof);
694 }
695
696private:
697 // ///
698 // Private functions
699 // ///
709 void setMaterialState_(const unsigned globalIdx, const unsigned /*timeIdx*/, PrimaryVariables& values)
710 {
711 auto& dofMaterialState = materialState_[globalIdx];
712 for (unsigned dirIdx = 0; dirIdx < 3; ++dirIdx) {
713 dofMaterialState.setDisplacement(dirIdx, values.makeEvaluation(disp0Idx + dirIdx, 0));
714 dofMaterialState.setRotation(dirIdx, values.makeEvaluation(rot0Idx + dirIdx, 0));
715 }
716 dofMaterialState.setSolidPressure(values.makeEvaluation(solidPres0Idx, 0));
717 }
718
719 std::unique_ptr<Linearizer> linearizer_;
720 NewtonMethod newtonMethod_;
721 Simulator& simulator_;
722 ElementChunks<GridView, Dune::Partitions::All> element_chunks_;
723
724 std::array<std::unique_ptr<TpsaBlockVectorWrapper>, historySize> solution_;
725 std::vector<Scalar> eqWeights_;
726 std::vector<MaterialState> materialState_;
727 PotForceVector mechPotPresForce_;
728}; // class TpsaModel
729
730} // namespace Opm
731
732
733#endif
Data handle for parallel communication which can be used to set the values values of ghost and overla...
Definition: gridcommhandles.hh:109
Linear least squares calculations and properties.
Definition: linearleastsquares.hpp:45
const Vector & x() const
Read-only vector of calculated coefficient vector.
Definition: linearleastsquares.hpp:76
void solve()
Solve linear least squares system.
Definition: linearleastsquares.hpp:66
static void registerParameters()
Register all run-time parameters for the Newton method.
Definition: newtonmethod.hh:135
Simplifies multi-threaded capabilities.
Definition: threadmanager.hpp:36
Small block vector wrapper class for model solutions.
Definition: tpsamodel.hpp:90
bool operator==(const TpsaBlockVectorWrapper &wrapper) const
Check if incoming block vector is the same as current.
Definition: tpsamodel.hpp:144
static TpsaBlockVectorWrapper serializationTestObject()
Test function for serialization.
Definition: tpsamodel.hpp:112
TpsaBlockVectorWrapper()=default
Default constructor.
TpsaBlockVectorWrapper(const std::string &, const std::size_t size)
Constructor.
Definition: tpsamodel.hpp:100
void serializeOp(Serializer &serializer)
Serializing operation.
Definition: tpsamodel.hpp:156
const SolutionVector & blockVector() const
Get const reference of block vector.
Definition: tpsamodel.hpp:135
SolutionVector & blockVector()
Get reference of block vector.
Definition: tpsamodel.hpp:127
SolutionVector blockVector_
Definition: tpsamodel.hpp:92
TPSA geomechanics model.
Definition: tpsamodel.hpp:58
SymTensor delstress(const unsigned globalIdx) const
Output stress tensor without fracture contribution.
Definition: tpsamodel.hpp:463
NewtonMethod & newtonMethod()
Return the Newton method.
Definition: tpsamodel.hpp:302
std::size_t numAuxiliaryDof() const
Return number of auxillary degrees of freedom.
Definition: tpsamodel.hpp:394
SymTensor stress(const unsigned globalIdx, const bool) const
Output stress tensor.
Definition: tpsamodel.hpp:512
SymTensor strain(const unsigned globalIdx, const bool) const
Output strain tensor.
Definition: tpsamodel.hpp:613
DimVector disp(const unsigned globalIdx, const bool) const
Output displacement vector.
Definition: tpsamodel.hpp:422
void finishInit()
Initialize TPSA model.
Definition: tpsamodel.hpp:193
void setEqWeight(unsigned eqIdx, Scalar value)
Set weights for equation.
Definition: tpsamodel.hpp:376
std::size_t numGridDof() const
Return number of degrees of freedom in the grid from the Flow model.
Definition: tpsamodel.hpp:333
const Linearizer & linearizer() const
Return the linearizer.
Definition: tpsamodel.hpp:272
const NewtonMethod & newtonMethod() const
Return the Newton method.
Definition: tpsamodel.hpp:292
std::size_t numAuxiliaryModules() const
Return number of auxillary modules.
Definition: tpsamodel.hpp:385
void updateMaterialState(const unsigned)
Update material state for all cells.
Definition: tpsamodel.hpp:248
Scalar mechPotentialPressForce(unsigned globalIdx) const
Output potential pressure forces.
Definition: tpsamodel.hpp:658
Scalar mechPotentialTempForce(unsigned) const
Output potential temparature forces.
Definition: tpsamodel.hpp:676
SymTensor linstress(const unsigned globalIdx) const
Output linear stress tensor.
Definition: tpsamodel.hpp:490
const SolutionVector & solution(unsigned timeIdx) const
Get reference to history solution vector.
Definition: tpsamodel.hpp:313
const MaterialState & materialState(const unsigned globalIdx, unsigned) const
Return current material state.
Definition: tpsamodel.hpp:408
Scalar dofTotalVolume(unsigned globalIdx) const
Return the total grid volume from the Flow model.
Definition: tpsamodel.hpp:353
void resizeMaterialState_()
Resize material state vector.
Definition: tpsamodel.hpp:690
static void registerParameters()
Register runtime parameters.
Definition: tpsamodel.hpp:205
void setMechPotentialPressForce(unsigned globalIdx, Scalar val)
Definition: tpsamodel.hpp:663
Scalar eqWeight(unsigned, unsigned eqIdx) const
Return equation weights.
Definition: tpsamodel.hpp:365
TpsaModel(Simulator &simulator)
Constructor.
Definition: tpsamodel.hpp:170
SolutionVector & solution(unsigned timeIdx)
Get reference to history solution vector.
Definition: tpsamodel.hpp:324
void prepareTPSA()
Prepare TPSA model for coupled Flow-TPSA scheme.
Definition: tpsamodel.hpp:214
Scalar solidPressure(const unsigned globalIdx) const
Output solid pressure.
Definition: tpsamodel.hpp:452
SymTensor fractureStress(const unsigned) const
Output fracture stress tensor.
Definition: tpsamodel.hpp:476
std::size_t numTotalDof() const
Return the total number of degrees of freedom.
Definition: tpsamodel.hpp:342
Linearizer & linearizer()
Return the linearizer.
Definition: tpsamodel.hpp:282
void syncOverlap()
Sync primary variables in overlapping cells.
Definition: tpsamodel.hpp:225
DimVector rotation(const unsigned globalIdx) const
Output rotation vector.
Definition: tpsamodel.hpp:437
Scalar mechPotentialForce(unsigned globalIdx) const
Output potential forces.
Definition: tpsamodel.hpp:647
Definition: fvbaseprimaryvariables.hh:161
Definition: blackoilbioeffectsmodules.hh:45
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
The Opm property system, traits with inheritance.