GEOS
PoromechanicsConformingFractures.hpp
Go to the documentation of this file.
1 /*
2  * ------------------------------------------------------------------------------------------------------------
3  * SPDX-License-Identifier: LGPL-2.1-only
4  *
5  * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC
6  * Copyright (c) 2018-2024 TotalEnergies
7  * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University
8  * Copyright (c) 2023-2024 Chevron
9  * Copyright (c) 2019- GEOS/GEOSX Contributors
10  * All rights reserved
11  *
12  * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details.
13  * ------------------------------------------------------------------------------------------------------------
14  */
15 
21 #ifndef GEOS_PHYSICSSOLVERS_MULTIPHYSICS_POROMECHANICSCONFORMINGFRACTURES_HPP_
22 #define GEOS_PHYSICSSOLVERS_MULTIPHYSICS_POROMECHANICSCONFORMINGFRACTURES_HPP_
23 
31 #include "constitutive/solid/CoupledSolidBase.hpp"
32 #include "constitutive/contact/HydraulicApertureBase.hpp"
33 #include "constitutive/contact/HydraulicApertureRelationSelector.hpp"
35 #include "common/DataTypes.hpp"
36 #include "mesh/DomainPartition.hpp"
38 
39 namespace geos
40 {
41 
42 template< template< typename, typename > class POROMECHANICS_BASE, typename FLOW_SOLVER >
43 class PoromechanicsConformingFractures : public POROMECHANICS_BASE< FLOW_SOLVER, SolidMechanicsLagrangeContact >
44 {
45 public:
46  using Base = POROMECHANICS_BASE< FLOW_SOLVER, SolidMechanicsLagrangeContact >;
47 
48  PoromechanicsConformingFractures( const string & name,
49  dataRepository::Group * const parent )
50  : Base( name, parent )
51  {}
52 
53  virtual void setupCoupling( DomainPartition const & domain,
54  DofManager & dofManager ) const override
55  {
57  // 1. Poromechanical coupling in the bulk
58  Base::setupCoupling( domain, dofManager );
59 
60  // 2. Traction - pressure coupling in the fracture
61  dofManager.addCoupling( this->getFlowDofKey(),
62  fields::contact::traction::key(),
64  }
65 
66  virtual void setSparsityPattern( DomainPartition & domain,
67  DofManager & dofManager,
69  SparsityPattern< globalIndex > & pattern ) override
70  {
71  // start with the flow solver sparsity pattern (it could be reservoir + wells)
72  SparsityPattern< globalIndex > patternOriginal;
73  this->flowSolver()->setSparsityPattern( domain, dofManager, localMatrix, patternOriginal );
74 
75  // Get the original row lengths (diagonal blocks only)
76  array1d< localIndex > rowLengths( patternOriginal.numRows());
77  for( localIndex localRow = 0; localRow < patternOriginal.numRows(); ++localRow )
78  {
79  rowLengths[localRow] = patternOriginal.numNonZeros( localRow );
80  }
81 
82  // Add the number of nonzeros induced by coupling
83  addTransmissibilityCouplingNNZ( domain, dofManager, rowLengths.toView());
84 
85  // Create a new pattern with enough capacity for coupled matrix
86  pattern.resizeFromRowCapacities< parallelHostPolicy >( patternOriginal.numRows(),
87  patternOriginal.numColumns(),
88  rowLengths.data());
89 
90  // Copy the original nonzeros
91  appendSparsityPattern( pattern, patternOriginal );
92 
93  // Add the nonzeros from coupling
94  addTransmissibilityCouplingPattern( domain, dofManager, pattern.toView());
95 
97  }
98 
99  virtual void assembleSystem( real64 const time_n,
100  real64 const dt,
101  DomainPartition & domain,
102  DofManager const & dofManager,
103  CRSMatrixView< real64, globalIndex const > const & localMatrix,
104  arrayView1d< real64 > const & localRhs ) override
105  {
106 
108 
109  this->solidMechanicsSolver()->synchronizeFractureState( domain );
110 
111  // The flux assembly accumulates into this matrix. Clear it before every
112  // Newton assembly and make the host copy explicit before the host-side
113  // coupling kernels consume it.
114  if( !m_derivativeFluxResidual_dAperture )
115  {
116  setUpDflux_dApertureMatrix( domain );
117  }
118  m_derivativeFluxResidual_dAperture->move( parallelDeviceMemorySpace, false );
119  m_derivativeFluxResidual_dAperture->zero();
120 
122  dt,
123  domain,
124  dofManager,
125  localMatrix,
126  localRhs );
127 
128  // Assemble fluxes 3D/2D and get dFluidResidualDAperture
129  this->flowSolver()->assembleHydrofracFluxTerms( time_n,
130  dt,
131  domain,
132  dofManager,
133  localMatrix,
134  localRhs,
135  getDerivativeFluxResidual_dNormalJump(),
136  nullptr );
137 
138  m_derivativeFluxResidual_dAperture->move( hostMemorySpace, false );
139 
140  // This step must occur after the fluxes are assembled because that's when DerivativeFluxResidual_dAperture is filled.
141  assembleCouplingTerms( time_n,
142  dt,
143  domain,
144  dofManager,
145  localMatrix,
146  localRhs );
147  }
148 
149  virtual void updateState( DomainPartition & domain ) override
150  {
152 
153  // call base poromechanics update
154  Base::updateState( domain );
155  // need to call solid mechanics update separately to compute face displacement jump
156  this->solidMechanicsSolver()->updateState( domain );
157 
158  // remove the contribution of the hydraulic aperture from the stencil weights
159  this->flowSolver()->prepareStencilWeights( domain );
160 
161  updateHydraulicApertureAndFracturePermeability( domain );
162 
163  // update the stencil weights using the updated hydraulic aperture
164  this->flowSolver()->updateStencilWeights( domain );
165  }
166 
167 protected:
168 
176  DofManager const & dofManager,
177  arrayView1d< localIndex > const & rowLengths ) const
178  {
180 
181  integer const numComp = numFluidComponents();
182 
183  this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, // meshBodyName,
184  MeshLevel const & mesh,
185  string_array const & ) // regionNames
186  {
187  ElementRegionManager const & elemManager = mesh.getElemManager();
188 
189  string const flowDofKey = dofManager.getKey( this->getFlowDofKey() );
190 
191  globalIndex const rankOffset = dofManager.rankOffset();
192 
193  NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager();
194  FiniteVolumeManager const & fvManager = numericalMethodManager.getFiniteVolumeManager();
195  FluxApproximationBase const & stabilizationMethod = fvManager.getFluxApproximation( this->solidMechanicsSolver()->getStabilizationName() );
196 
197  stabilizationMethod.forStencils< SurfaceElementStencil >( mesh, [&]( SurfaceElementStencil const & stencil )
198  {
199  for( localIndex iconn=0; iconn<stencil.size(); ++iconn )
200  {
201  localIndex const numFluxElems = stencil.stencilSize( iconn );
202  typename SurfaceElementStencil::IndexContainerViewConstType const & seri = stencil.getElementRegionIndices();
203  typename SurfaceElementStencil::IndexContainerViewConstType const & sesri = stencil.getElementSubRegionIndices();
204  typename SurfaceElementStencil::IndexContainerViewConstType const & sei = stencil.getElementIndices();
205 
206  FaceElementSubRegion const & elementSubRegion =
207  elemManager.getRegion( seri[iconn][0] ).getSubRegion< FaceElementSubRegion >( sesri[iconn][0] );
208 
209  ArrayOfArraysView< localIndex const > const elemsToNodes = elementSubRegion.nodeList().toViewConst();
210 
211  arrayView1d< globalIndex const > const faceElementDofNumber =
212  elementSubRegion.getReference< array1d< globalIndex > >( flowDofKey );
213 
214  for( localIndex k0=0; k0<numFluxElems; ++k0 )
215  {
216  globalIndex const activeFlowDOF = faceElementDofNumber[sei[iconn][k0]];
217  globalIndex const rowNumber = activeFlowDOF - rankOffset;
218 
219  if( rowNumber >= 0 && rowNumber < rowLengths.size() )
220  {
221  for( localIndex k1=0; k1<numFluxElems; ++k1 )
222  {
223  // The coupling with the nodal displacements of the cell itself has already been added by the dofManager
224  // so we only add the coupling with the nodal displacements of the neighbors.
225  if( k1 != k0 )
226  {
227  localIndex const numNodesPerElement = elemsToNodes[sei[iconn][k1]].size();
228  for( integer ic = 0; ic < numComp; ic++ )
229  {
230  rowLengths[rowNumber + ic] += 3*numNodesPerElement;
231  }
232  }
233  }
234  }
235  }
236  }
237  } );
238  } );
239  }
240 
249  DofManager const & dofManager,
250  SparsityPatternView< globalIndex > const & pattern ) const
251  {
253 
254  integer const numComp = numFluidComponents();
255 
256  this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
257  MeshLevel const & mesh,
258  string_array const & )
259  {
260  FaceManager const & faceManager = mesh.getFaceManager();
261  NodeManager const & nodeManager = mesh.getNodeManager();
262  ElementRegionManager const & elemManager = mesh.getElemManager();
263 
264  string const dispDofKey = dofManager.getKey( fields::solidMechanics::totalDisplacement::key() );
265  string const flowDofKey = dofManager.getKey( this->getFlowDofKey() );
266 
267  arrayView1d< globalIndex const > const &
268  dispDofNumber = nodeManager.getReference< globalIndex_array >( dispDofKey );
269  ArrayOfArraysView< localIndex const > const & faceToNodeMap = faceManager.nodeList().toViewConst();
270 
271  // Get the finite volume method used to compute the stabilization
272  NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager();
273  FiniteVolumeManager const & fvManager = numericalMethodManager.getFiniteVolumeManager();
274  FluxApproximationBase const & fvDiscretization = fvManager.getFluxApproximation( this->flowSolver()->getDiscretizationName() );
275 
276  SurfaceElementRegion const & fractureRegion =
277  elemManager.getRegion< SurfaceElementRegion >( this->solidMechanicsSolver()->getUniqueFractureRegionName() );
278  FaceElementSubRegion const & fractureSubRegion =
279  fractureRegion.getUniqueSubRegion< FaceElementSubRegion >();
280 
281  GEOS_ERROR_IF( !fractureSubRegion.hasWrapper( fields::flow::pressure::key() ),
282  "The fracture subregion must contain pressure field.", this->getDataContext() );
283 
284  arrayView2d< localIndex const > const elem2dToFaces = fractureSubRegion.faceList().toViewConst();
285 
286  arrayView1d< globalIndex const > const &
287  flowDofNumber = fractureSubRegion.getReference< globalIndex_array >( flowDofKey );
288 
289  globalIndex const rankOffset = dofManager.rankOffset();
290 
291  fvDiscretization.forStencils< SurfaceElementStencil >( mesh, [&]( SurfaceElementStencil const & stencil )
292  {
293  forAll< serialPolicy >( stencil.size(), [=] ( localIndex const iconn )
294  {
295  localIndex const numFluxElems = stencil.stencilSize( iconn );
296 
297  // A fracture connector has to be an edge shared by two faces
298  if( numFluxElems == 2 )
299  {
300  typename SurfaceElementStencil::IndexContainerViewConstType const & sei = stencil.getElementIndices();
301 
302  // First index: face element. Second index: node
303  for( localIndex kf = 0; kf < 2; ++kf )
304  {
305  // Set row DOF index
306  // Note that the 1-kf index is intentional, as this is coupling the pressure of one face cell
307  // to the nodes of the adjacent cell
308  localIndex const rowIndex = flowDofNumber[sei[iconn][1-kf]] - rankOffset;
309 
310  if( rowIndex >= 0 && rowIndex < pattern.numRows() )
311  {
312 
313  // Get fracture, face and region/subregion/element indices (for elements on both sides)
314  localIndex const fractureIndex = sei[iconn][kf];
315 
316  // Get the number of nodes
317  localIndex const numNodesPerFace = faceToNodeMap.sizeOfArray( elem2dToFaces[fractureIndex][0] );
318 
319  // Loop over the two sides of each fracture element
320  for( localIndex kf1 = 0; kf1 < 2; ++kf1 )
321  {
322  localIndex const faceIndex = elem2dToFaces[fractureIndex][kf1];
323 
324  // Save the list of DOF associated with nodes
325  for( localIndex a=0; a<numNodesPerFace; ++a )
326  {
327  for( localIndex i = 0; i < 3; ++i )
328  {
329  globalIndex const colIndex = dispDofNumber[faceToNodeMap( faceIndex, a )] + LvArray::integerConversion< globalIndex >( i );
330  for( integer ic = 0; ic < numComp; ic++ )
331  {
332  pattern.insertNonZero( rowIndex + ic, colIndex );
333  }
334  }
335  }
336  }
337  }
338  }
339  }
340  } );
341  } );
342  } );
343  }
344 
351  {
352  integer const numComp = numFluidComponents();
353  NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager();
354  FiniteVolumeManager const & fvManager = numericalMethodManager.getFiniteVolumeManager();
355  FluxApproximationBase const & fluxApprox = fvManager.getFluxApproximation( this->flowSolver()->getDiscretizationName() );
356 
357  localIndex numMeshTargets = 0;
358  this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const & meshName,
359  MeshLevel const & mesh,
360  string_array const & regionNames )
361  {
362  std::unique_ptr< CRSMatrix< real64, localIndex > > & derivativeFluxResidual_dAperture = getRefDerivativeFluxResidual_dAperture();
363 
364  // The matrix is re-created per target and the flux kernel indexes it by
365  // the raw per-target surface element index, so only the last target would
366  // survive and the others would write into its rows.
367  ++numMeshTargets;
368  GEOS_ERROR_IF_GT_MSG( numMeshTargets, 1,
369  GEOS_FMT( "{}: this solver supports a single mesh target; '{}' is the second.",
370  this->getName(), meshName ) );
371 
372  localIndex numRows = 0;
373  localIndex numCol = 0;
374  {
375  // calculate number of fracture elements
376  mesh.getElemManager().forElementSubRegions< FaceElementSubRegion >( regionNames,
377  [&]( localIndex const, FaceElementSubRegion const & subRegion )
378  {
379  numRows += subRegion.size();
380  } );
381  // number of columns (derivatives) = number of fracture elements
382  numCol = numRows;
383  // number of rows (equations) = number of fracture elements * number of components
384  numRows *= numComp;
385 
386  derivativeFluxResidual_dAperture = std::make_unique< CRSMatrix< real64, localIndex > >( numRows, numCol );
387  derivativeFluxResidual_dAperture->setName( this->getName() + "/derivativeFluxResidual_dAperture" );
388  }
389 
390  // array1d's sized constructor value-initializes, so no explicit zero().
391  array1d< localIndex > rowCapacities( numRows );
392  fluxApprox.forStencils< SurfaceElementStencil >( mesh, [&]( SurfaceElementStencil const & stencil )
393  {
394  for( localIndex iconn = 0; iconn < stencil.size(); ++iconn )
395  {
396  localIndex const numFluxElems = stencil.stencilSize( iconn );
397  typename SurfaceElementStencil::IndexContainerViewConstType const & sei = stencil.getElementIndices();
398 
399  for( localIndex k0 = 0; k0 < numFluxElems; ++k0 )
400  {
401  // The stencil sweep covers every SurfaceElementStencil on the mesh,
402  // while numRows only counts the subregions found in regionNames.
403  GEOS_ERROR_IF_GE_MSG( sei[iconn][k0] * numComp + numComp - 1, numRows,
404  "Surface stencil index exceeds the fracture derivative matrix size." );
405  for( integer ic = 0; ic < numComp; ic++ )
406  {
407  rowCapacities[sei[iconn][k0] * numComp + ic] += numFluxElems;
408  }
409  }
410  }
411  } );
412 
413  if( numRows > 0 )
414  {
415  derivativeFluxResidual_dAperture->resizeFromRowCapacities< parallelHostPolicy >( numRows,
416  numCol,
417  rowCapacities.data() );
418  }
419 
420  fluxApprox.forStencils< SurfaceElementStencil >( mesh, [&]( SurfaceElementStencil const & stencil )
421  {
422  for( localIndex iconn = 0; iconn < stencil.size(); ++iconn )
423  {
424  localIndex const numFluxElems = stencil.stencilSize( iconn );
425  typename SurfaceElementStencil::IndexContainerViewConstType const & sei = stencil.getElementIndices();
426 
427  for( localIndex k0 = 0; k0 < numFluxElems; ++k0 )
428  {
429  GEOS_ERROR_IF_GE_MSG( sei[iconn][k0] * numComp + numComp - 1, numRows,
430  "Surface stencil index exceeds the fracture derivative matrix size." );
431  for( localIndex k1 = 0; k1 < numFluxElems; ++k1 )
432  {
433  for( integer ic = 0; ic < numComp; ++ic )
434  {
435  derivativeFluxResidual_dAperture->insertNonZero( sei[iconn][k0] * numComp + ic,
436  sei[iconn][k1],
437  0.0 );
438  }
439  }
440  }
441  }
442  } );
443  } );
444  }
445 
447  real64 const dt,
448  DomainPartition & domain,
449  DofManager const & dofManager,
450  CRSMatrixView< real64, globalIndex const > const & localMatrix,
451  arrayView1d< real64 > const & localRhs )
452  {
453  GEOS_UNUSED_VAR( time_n, dt );
454 
456 
457  Base::assembleElementBasedTerms( time_n, dt, domain, dofManager, localMatrix, localRhs );
458 
459  // Flow accumulation for fractures
460  this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
461  MeshLevel & mesh,
462  string_array const & regionNames )
463  {
464  mesh.getElemManager().forElementSubRegions< FaceElementSubRegion >( regionNames, [&]( localIndex const,
465  FaceElementSubRegion const & subRegion )
466  {
467  this->flowSolver()->accumulationAssemblyLaunch( dofManager, subRegion, localMatrix, localRhs );
468  } );
469  } );
470 
471  this->solidMechanicsSolver()->assembleContact( domain, dofManager, localMatrix, localRhs );
472  }
473 
474  virtual void assembleCouplingTerms( real64 const time_n,
475  real64 const dt,
476  DomainPartition const & domain,
477  DofManager const & dofManager,
478  CRSMatrixView< real64, globalIndex const > const & localMatrix,
479  arrayView1d< real64 > const & localRhs ) override
480  {
481  GEOS_UNUSED_VAR( time_n, dt );
482  // These 2 steps need to occur after the fluxes are assembled because that's when DerivativeFluxResidual_dAperture is filled.
483  this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
484  MeshLevel const & mesh,
485  string_array const & regionNames )
486  {
488  assembleForceResidualDerivativeWrtPressure( mesh, regionNames, dofManager, localMatrix, localRhs );
489  assembleFluidMassResidualDerivativeWrtDisplacement( mesh, regionNames, dofManager, localMatrix, localRhs );
490  } );
491  }
492 
493  void assembleForceResidualDerivativeWrtPressure( MeshLevel const & mesh,
494  string_array const & regionNames,
495  DofManager const & dofManager,
496  CRSMatrixView< real64, globalIndex const > const & localMatrix,
497  arrayView1d< real64 > const & localRhs )
498  {
500 
501  FaceManager const & faceManager = mesh.getFaceManager();
502  NodeManager const & nodeManager = mesh.getNodeManager();
503  EdgeManager const & edgeManager = mesh.getEdgeManager();
504  ElementRegionManager const & elemManager = mesh.getElemManager();
505 
506  ArrayOfArraysView< localIndex const > const & faceToNodeMap = faceManager.nodeList().toViewConst();
507  ArrayOfArraysView< localIndex const > const faceToEdgeMap = faceManager.edgeList().toViewConst();
508  arrayView2d< localIndex const > const & edgeToNodeMap = edgeManager.nodeList().toViewConst();
509  arrayView2d< real64 const > faceCenters = faceManager.faceCenter();
510  arrayView2d< real64 const > const & faceNormal = faceManager.faceNormal();
511  arrayView1d< real64 const > faceAreas = faceManager.faceArea();
512 
513  string const & dispDofKey = dofManager.getKey( fields::solidMechanics::totalDisplacement::key() );
514  string const & flowDofKey = dofManager.getKey( this->getFlowDofKey() );
515 
517  dispDofNumber = nodeManager.getReference< globalIndex_array >( dispDofKey );
518  globalIndex const rankOffset = dofManager.rankOffset();
519 
520  // Get the coordinates for all nodes
522 
523  elemManager.forElementSubRegions< FaceElementSubRegion >( regionNames,
524  [&]( localIndex const,
525  FaceElementSubRegion const & subRegion )
526  {
528  flowDofNumber = subRegion.getReference< globalIndex_array >( flowDofKey );
529  arrayView1d< real64 const > const & pressure = subRegion.getReference< array1d< real64 > >( fields::flow::pressure::key() );
530  arrayView2d< localIndex const > const & elemsToFaces = subRegion.faceList().toViewConst();
531 
532  forAll< serialPolicy >( subRegion.size(), [=, this]( localIndex const kfe )
533  {
534  localIndex const kf0 = elemsToFaces[kfe][0];
535  localIndex const numNodesPerFace = faceToNodeMap.sizeOfArray( kf0 );
536 
537  real64 Nbar[3];
538  Nbar[ 0 ] = faceNormal[elemsToFaces[kfe][0]][0] - faceNormal[elemsToFaces[kfe][1]][0];
539  Nbar[ 1 ] = faceNormal[elemsToFaces[kfe][0]][1] - faceNormal[elemsToFaces[kfe][1]][1];
540  Nbar[ 2 ] = faceNormal[elemsToFaces[kfe][0]][2] - faceNormal[elemsToFaces[kfe][1]][2];
541  LvArray::tensorOps::normalize< 3 >( Nbar );
542  globalIndex rowDOF[3 * m_maxFaceNodes]; // this needs to be changed when dealing with arbitrary element types
543  real64 nodeRHS[3 * m_maxFaceNodes];
544  stackArray1d< real64, 3 * m_maxFaceNodes > dRdP( 3*m_maxFaceNodes );
545  globalIndex colDOF[1];
546  colDOF[0] = flowDofNumber[kfe]; // pressure is always first
547 
548  for( localIndex kf=0; kf<2; ++kf )
549  {
550  localIndex const faceIndex = elemsToFaces[kfe][kf];
551 
552  // Compute local area contribution for each node
553  stackArray1d< real64, FaceManager::maxFaceNodes() > nodalArea;
554  this->solidMechanicsSolver()->computeFaceNodalArea( elemsToFaces[kfe][kf],
555  nodePosition,
556  faceToNodeMap,
557  faceToEdgeMap,
558  edgeToNodeMap,
559  faceCenters,
560  faceNormal,
561  faceAreas,
562  nodalArea );
563  for( localIndex a=0; a<numNodesPerFace; ++a )
564  {
565  real64 const nodalForceMag = -( pressure[kfe] ) * nodalArea[a];
566  real64 globalNodalForce[ 3 ];
567  LvArray::tensorOps::scaledCopy< 3 >( globalNodalForce, Nbar, nodalForceMag );
568 
569  for( localIndex i=0; i<3; ++i )
570  {
571  rowDOF[3*a+i] = dispDofNumber[faceToNodeMap( faceIndex, a )] + LvArray::integerConversion< globalIndex >( i );
572  // Opposite sign w.r.t. theory because of minus sign in stiffness matrix definition (K < 0)
573  nodeRHS[3*a+i] = +globalNodalForce[i] * pow( -1, kf );
574 
575  // Opposite sign w.r.t. theory because of minus sign in stiffness matrix definition (K < 0)
576  dRdP( 3*a+i ) = -nodalArea[a] * Nbar[i] * pow( -1, kf );
577  }
578  }
579 
580  for( localIndex idof = 0; idof < numNodesPerFace * 3; ++idof )
581  {
582  localIndex const localRow = LvArray::integerConversion< localIndex >( rowDOF[idof] - rankOffset );
583 
584  if( localRow >= 0 && localRow < localMatrix.numRows() )
585  {
586  localMatrix.addToRow< parallelHostAtomic >( localRow,
587  colDOF,
588  &dRdP[idof],
589  1 );
590  RAJA::atomicAdd( parallelHostAtomic{}, &localRhs[localRow], nodeRHS[idof] );
591  }
592  }
593  }
594  } );
595  } );
596  }
597 
598  virtual void assembleFluidMassResidualDerivativeWrtDisplacement( MeshLevel const & mesh,
599  string_array const & regionNames,
600  DofManager const & dofManager,
601  CRSMatrixView< real64, globalIndex const > const & localMatrix,
602  arrayView1d< real64 > const & localRhs ) = 0;
603 
604  virtual void mapSolutionBetweenSolvers( DomainPartition & domain, integer const solverType ) override
605  {
607 
609  if( solverType == static_cast< integer >( Base::SolverType::SolidMechanics )
610  && !this->m_performStressInitialization ) // do not update during poromechanics initialization
611  {
612  // remove the contribution of the hydraulic aperture from the stencil weights
613  this->flowSolver()->prepareStencilWeights( domain );
614 
615  updateHydraulicApertureAndFracturePermeability( domain );
616 
617  // update the stencil weights using the updated hydraulic aperture
618  this->flowSolver()->updateStencilWeights( domain );
619  }
620 
621  Base::mapSolutionBetweenSolvers( domain, solverType );
622  }
623 
624  void updateHydraulicApertureAndFracturePermeability( DomainPartition & domain )
625  {
626  using namespace constitutive;
627 
628  this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,
629  MeshLevel & mesh,
630  string_array const & regionNames )
631  {
632  ElementRegionManager & elemManager = mesh.getElemManager();
633 
634  elemManager.forElementSubRegions< FaceElementSubRegion >( regionNames,
635  [&]( localIndex const,
636  FaceElementSubRegion & subRegion )
637  {
638  arrayView2d< real64 const > const dispJump = subRegion.getField< fields::contact::dispJump >();
639  arrayView1d< real64 const > const area = subRegion.getElementArea();
640  arrayView1d< real64 const > const volume = subRegion.getElementVolume();
641  arrayView2d< real64 const > const fractureTraction = subRegion.getField< fields::contact::traction >();
642  arrayView1d< real64 const > const pressure = subRegion.getField< fields::flow::pressure >();
643  arrayView1d< real64 const > const oldHydraulicAperture = subRegion.getField< fields::flow::aperture0 >();
644 
645  arrayView1d< real64 > const aperture = subRegion.getElementAperture();
646  arrayView1d< real64 > const hydraulicAperture = subRegion.getField< fields::flow::hydraulicAperture >();
647  arrayView1d< real64 > const deltaVolume = subRegion.getField< fields::flow::deltaVolume >();
648  arrayView1d< integer > const & fractureState = subRegion.getField< fields::contact::fractureState >();
649 
650  string const porousSolidName = subRegion.getReference< string >( FlowSolverBase::viewKeyStruct::solidNamesString() );
651  CoupledSolidBase & porousSolid = subRegion.getConstitutiveModel< CoupledSolidBase >( porousSolidName );
652 
653  string const & hydraulicApertureRelationName = subRegion.template getReference< string >( viewKeyStruct::hydraulicApertureRelationNameString() );
654  HydraulicApertureBase const & hydraulicApertureModel = this->template getConstitutiveModel< HydraulicApertureBase >( subRegion, hydraulicApertureRelationName );
655 
656  constitutiveUpdatePassThru( hydraulicApertureModel, [&] ( auto & castedHydraulicAperture )
657  {
658  using HydraulicApertureType = TYPEOFREF( castedHydraulicAperture );
659  typename HydraulicApertureType::KernelWrapper hydraulicApertureWrapper = castedHydraulicAperture.createKernelWrapper();
660 
661  ConstitutivePassThru< CompressibleSolidBase >::execute( porousSolid, [=, &subRegion] ( auto & castedPorousSolid )
662  {
663  typename TYPEOFREF( castedPorousSolid ) ::KernelWrapper porousMaterialWrapper = castedPorousSolid.createKernelUpdates();
664 
665  poromechanicsFracturesKernels::StateUpdateKernel::
666  launch< parallelDevicePolicy<> >( subRegion.size(),
667  porousMaterialWrapper,
668  hydraulicApertureWrapper,
669  dispJump,
670  pressure,
671  area,
672  volume,
673  deltaVolume,
674  aperture,
675  oldHydraulicAperture,
676  hydraulicAperture,
677  fractureTraction,
678  fractureState );
679 
680  } );
681  } );
682  } );
683  } );
684  }
685 
686  std::unique_ptr< CRSMatrix< real64, localIndex > > & getRefDerivativeFluxResidual_dAperture()
687  {
688  return m_derivativeFluxResidual_dAperture;
689  }
690 
691  CRSMatrixView< real64, localIndex const > getDerivativeFluxResidual_dNormalJump()
692  {
693  return m_derivativeFluxResidual_dAperture->toViewConstSizes();
694  }
695 
696  CRSMatrixView< real64 const, localIndex const > getDerivativeFluxResidual_dNormalJump() const
697  {
698  return m_derivativeFluxResidual_dAperture->toViewConst();
699  }
700 
701  virtual integer numFluidComponents() const = 0;
702 
703  struct viewKeyStruct : public Base::viewKeyStruct
704  {};
705 
706  static const localIndex m_maxFaceNodes = 11; // Maximum number of nodes on a contact face
707 
708  std::unique_ptr< CRSMatrix< real64, localIndex > > m_derivativeFluxResidual_dAperture;
709 
710 };
711 
712 }
713 
714 #endif //GEOS_PHYSICSSOLVERS_MULTIPHYSICS_POROMECHANICSCONFORMINGFRACTURES_HPP_
#define GEOS_UNUSED_VAR(...)
Mark an unused variable and silence compiler warnings.
#define GEOS_ERROR_IF_GE_MSG(lhs, rhs,...)
Raise a hard error if one value compares greater than or equal to the other.
Definition: Logger.hpp:503
#define GEOS_MARK_FUNCTION
Mark function with both Caliper and NVTX if enabled.
The DoFManager is responsible for allocating global dofs, constructing sparsity patterns,...
Definition: DofManager.hpp:45
globalIndex rankOffset(string const &fieldName) const
void addCoupling(string const &rowFieldName, string const &colFieldName, Connector connectivity, stdVector< FieldSupport > const &regions={}, bool symmetric=true)
Add coupling between two fields.
@ Elem
connectivity is element (like in finite elements)
string const & getKey(string const &fieldName) const
Return the key used to record the field in the DofManager.
Partition of the decomposed physical domain. It also manages the connexion information to its neighbo...
Group const & getMeshBodies() const
Get the mesh bodies, const version.
NumericalMethodsManager const & getNumericalMethodManager() const
This class provides an interface to ObjectManagerBase in order to manage edge data.
Definition: EdgeManager.hpp:43
NodeMapType & nodeList()
Get a node list.
The ElementRegionManager class provides an interface to ObjectManagerBase in order to manage ElementR...
void forElementSubRegions(LAMBDA &&lambda)
This function is used to launch kernel function over the element subregions of all the subregion type...
The FaceManager class provides an interface to ObjectManagerBase in order to manage face data.
Definition: FaceManager.hpp:44
array2d< real64 > & faceNormal()
Get a mutable accessor to an array containing all the face normals.
array1d< real64 > & faceArea()
Get a mutable accessor to an array containing all the face area.
array2d< real64 > & faceCenter()
Get a mutable accessor to an array containing all the face center.
NodeMapType & nodeList()
Get a mutable accessor to a map containing the list of each nodes for each faces.
EdgeMapType & edgeList()
Get a mutable accessor to a map containing the list of each edges for each faces.
FluxApproximationBase const & getFluxApproximation(string const &name) const
Return the FluxApproximation associated with the provided name.
void forStencils(MeshLevel const &mesh, LAMBDA &&lambda) const
Call a user-provided function for the each stencil according to the provided TYPE.
Class facilitating the representation of a multi-level discretization of a MeshBody.
Definition: MeshLevel.hpp:42
NodeManager const & getNodeManager() const
Get the node manager.
Definition: MeshLevel.hpp:155
FaceManager const & getFaceManager() const
Get the face manager.
Definition: MeshLevel.hpp:194
ElementRegionManager const & getElemManager() const
Get the element region manager.
Definition: MeshLevel.hpp:207
EdgeManager const & getEdgeManager() const
Get the edge manager.
Definition: MeshLevel.hpp:181
The NodeManager class provides an interface to ObjectManagerBase in order to manage node data.
Definition: NodeManager.hpp:46
FiniteVolumeManager & getFiniteVolumeManager()
void addTransmissibilityCouplingPattern(DomainPartition const &domain, DofManager const &dofManager, SparsityPatternView< globalIndex > const &pattern) const
Set up the Dflux_dApertureMatrix object.
void assembleElementBasedContributions(real64 const time_n, real64 const dt, DomainPartition &domain, DofManager const &dofManager, CRSMatrixView< real64, globalIndex const > const &localMatrix, arrayView1d< real64 > const &localRhs)
void addTransmissibilityCouplingNNZ(DomainPartition const &domain, DofManager const &dofManager, arrayView1d< localIndex > const &rowLengths) const
virtual void assembleCouplingTerms(real64 const time_n, real64 const dt, DomainPartition const &domain, DofManager const &dofManager, CRSMatrixView< real64, globalIndex const > const &localMatrix, arrayView1d< real64 > const &localRhs) override
virtual void mapSolutionBetweenSolvers(DomainPartition &domain, integer const solverType) override
virtual void setupCoupling(DomainPartition const &domain, DofManager &dofManager) const override
void setUpDflux_dApertureMatrix(DomainPartition &domain)
Set up the Dflux_dApertureMatrix object.
Provides management of the interior stencil points for a face elements when using Two-Point flux appr...
GEOS_DECLTYPE_AUTO_RETURN getReference(LOOKUP_TYPE const &lookup) const
Look up a wrapper and get reference to wrapped object.
Definition: Group.hpp:1273
array2d< real64, nodes::REFERENCE_POSITION_PERM > & referencePosition()
Get the mutable reference position array. This table will contain all the node coordinates.
ArrayView< T, 1 > arrayView1d
Alias for 1D array view.
Definition: DataTypes.hpp:179
stdVector< string > string_array
A 1-dimensional array of geos::string types.
Definition: DataTypes.hpp:361
GEOS_GLOBALINDEX_TYPE globalIndex
Global index type (for indexing objects across MPI partitions).
Definition: DataTypes.hpp:87
array1d< globalIndex > globalIndex_array
A 1-dimensional array of geos::globalIndex types.
Definition: DataTypes.hpp:370
LvArray::CRSMatrix< T, COL_INDEX, INDEX_TYPE, LvArray::ChaiBuffer > CRSMatrix
Alias for CRS Matrix class.
Definition: DataTypes.hpp:305
LvArray::SparsityPatternView< COL_INDEX, INDEX_TYPE const, LvArray::ChaiBuffer > SparsityPatternView
Alias for Sparsity pattern View.
Definition: DataTypes.hpp:301
LvArray::SparsityPattern< COL_INDEX, INDEX_TYPE, LvArray::ChaiBuffer > SparsityPattern
Alias for Sparsity pattern class.
Definition: DataTypes.hpp:297
LvArray::ArrayOfArraysView< T, INDEX_TYPE const, CONST_SIZES, LvArray::ChaiBuffer > ArrayOfArraysView
View of array of variable-sized arrays. See LvArray::ArrayOfArraysView for details.
Definition: DataTypes.hpp:285
double real64
64-bit floating point type.
Definition: DataTypes.hpp:98
GEOS_LOCALINDEX_TYPE localIndex
Local index type (for indexing objects within an MPI partition).
Definition: DataTypes.hpp:84
LvArray::CRSMatrixView< T, COL_INDEX, INDEX_TYPE const, LvArray::ChaiBuffer > CRSMatrixView
Alias for CRS Matrix View.
Definition: DataTypes.hpp:309
void appendSparsityPattern(SparsityPattern< globalIndex > &target, SparsityPattern< globalIndex > const &source)
Append all entries from one sparsity pattern to another.
ArrayView< T, 2, USD > arrayView2d
Alias for 2D array view.
Definition: DataTypes.hpp:195
int integer
Signed integer type.
Definition: DataTypes.hpp:81
Array< T, 1 > array1d
Alias for 1D array.
Definition: DataTypes.hpp:175
LvArray::typeManipulation::NestedViewTypeConst< IndexContainerType > IndexContainerViewConstType
The array view to const type for the stencil indices.
Definition: StencilBase.hpp:47