4343
4444namespace Opm {
4545
46+ namespace detail {
47+
48+ /* ! \brief Computes modulo 3 of possibly negative integers to get indices in cross product.
49+ *
50+ * If i = x-dir(=0), we want y-dir(=1) and z-dir(=2), hence -1 mod 3 must equal 2 and not -1
51+ */
52+ inline int modNeg (int i)
53+ {
54+ return ((i % 3 ) + 3 ) % 3 ;
55+ }
56+
57+ } // namespace detail
58+
4659/* !
4760* \brief Calculation of (linear) elasticity model terms for the residual
4861*
@@ -167,15 +180,11 @@ class ElasticityLocalResidual
167180 // ///
168181 // Displacement, rotation and solid pressure (directional-dependent) equations
169182 // ///
170- // Lambda function for computing modulo 3 of possibly negative integers to get indices in cross product.
171- // E.g. if i = x-dir(=0), we want y-dir(=1) and z-dir(=2), hence -1 mod 3 must equal 2 and not -1
172- auto modNeg = [](int i) { return ((i % 3 ) + 3 ) % 3 ; };
173-
174183 // Loop over x-, y- and z-dir (corresponding to dirIdx = 0, 1, 2)
175184 for (int dirIdx = 0 ; dirIdx < 3 ; ++dirIdx) {
176185 // Direction indices in cross-product
177- unsigned dirIdxNeg = modNeg (dirIdx - 1 );
178- unsigned dirIdxPos = modNeg (dirIdx + 1 );
186+ unsigned dirIdxNeg = detail:: modNeg (dirIdx - 1 );
187+ unsigned dirIdxPos = detail:: modNeg (dirIdx + 1 );
179188
180189 // Displacement equation
181190 const Scalar faceNormalDir = faceNormal[dirIdx];
@@ -247,6 +256,13 @@ class ElasticityLocalResidual
247256 problem,
248257 globalIndex);
249258 break ;
259+ case BCMECHType::SPRING :
260+ computeBoundaryTermSpring (bndryTerm,
261+ materialState,
262+ bdyInfo,
263+ problem,
264+ globalIndex);
265+ break ;
250266 default :
251267 throw std::logic_error (" Unknown boundary condition type " +
252268 std::to_string (static_cast <int >(bdyInfo.type )) +
@@ -295,15 +311,11 @@ class ElasticityLocalResidual
295311 // ///
296312 // Displacement equation
297313 // ///
298- // Lambda function for computing modulo 3 of possibly negative integers to get indices in cross product.
299- // E.g. if i = x-dir(=0), we want y-dir(=1) and z-dir(=2), hence -1 mod 3 must equal 2 and not -1
300- auto modNeg = [](int i) { return ((i % 3 ) + 3 ) % 3 ; };
301-
302314 // Loop over x-, y- and z-dir (corresponding to dirIdx = 0, 1, 2)
303315 for (int dirIdx = 0 ; dirIdx < 3 ; ++dirIdx) {
304316 // Direction indices in cross-product
305- unsigned dirIdxNeg = modNeg (dirIdx - 1 );
306- unsigned dirIdxPos = modNeg (dirIdx + 1 );
317+ unsigned dirIdxNeg = detail:: modNeg (dirIdx - 1 );
318+ unsigned dirIdxPos = detail:: modNeg (dirIdx + 1 );
307319
308320 // Displacement equation
309321 const Scalar faceNormalDir = faceNormal[dirIdx];
@@ -362,10 +374,6 @@ class ElasticityLocalResidual
362374 // ///
363375 // Rotation and solid pressure (directional-dependent) equations
364376 // ///
365- // Lambda function for computing modulo 3 of possibly negative integers to get indices in cross product.
366- // E.g. if i = x-dir(=0), we want y-dir(=1) and z-dir(=2), hence -1 mod 3 must equal 2 and not -1
367- auto modNeg = [](int i) { return ((i % 3 ) + 3 ) % 3 ; };
368-
369377 // Pre-compute dot product for rotation equation
370378 Evaluation dotProd = 0 ;
371379 for (int dirIdx = 0 ; dirIdx < 3 ; ++dirIdx) {
@@ -375,8 +383,8 @@ class ElasticityLocalResidual
375383 // Loop over x-, y- and z-dir (corresponding to dirIdx = 0, 1, 2)
376384 for (int dirIdx = 0 ; dirIdx < 3 ; ++dirIdx) {
377385 // Direction indices in cross-product
378- unsigned dirIdxNeg = modNeg (dirIdx - 1 );
379- unsigned dirIdxPos = modNeg (dirIdx + 1 );
386+ unsigned dirIdxNeg = detail:: modNeg (dirIdx - 1 );
387+ unsigned dirIdxPos = detail:: modNeg (dirIdx + 1 );
380388
381389 // Rotation equation
382390 const Scalar faceNormalDir = faceNormal[dirIdx];
@@ -400,6 +408,98 @@ class ElasticityLocalResidual
400408 }
401409 }
402410
411+ /* !
412+ * \brief Calculate spring boundary condition in TPSA formulation
413+ *
414+ * \param bndryTerm Boundary term vector
415+ * \param materialState Material state container
416+ * \param bdyInfo Boundary condition info container
417+ * \param problem Flow problem
418+ * \param globalIndex Cell index
419+ *
420+ * \note Spring boundary condition sets a shear modulus at fictitious point outside a certain
421+ * distance outside the boundary
422+ */
423+ template <class BoundaryConditionData >
424+ static void computeBoundaryTermSpring (Dune::FieldVector<Evaluation, numEq>& bndryTerm,
425+ const MaterialState& materialState,
426+ const BoundaryConditionData& bdyInfo,
427+ Problem& problem,
428+ unsigned globalIndex)
429+ {
430+ // Reset bondary term
431+ bndryTerm = 0.0 ;
432+
433+ // Extract cell and boundary information
434+ const unsigned bfIdx = bdyInfo.boundaryFaceIndex ;
435+ const auto & faceNormal = problem.cellFaceNormalBoundary (globalIndex, bfIdx);
436+ const Scalar distIn = problem.normalDistanceBoundary (globalIndex, bfIdx);
437+ const Scalar distEx = bdyInfo.distance ;
438+ const Scalar sModulusIn = problem.shearModulus (globalIndex);
439+ const Scalar sModulusEx = bdyInfo.shearModulus ;
440+
441+ // Calculate face properties
442+ const Scalar weightIn = distIn / sModulusIn ;
443+ const Scalar weightEx = distEx / sModulusEx ;
444+ const Scalar weightAvgIn = weightIn / (weightIn + weightEx);
445+ const Scalar weightAvgEx = 1.0 - weightAvgIn;
446+ const Scalar weightProd = weightIn * weightEx;
447+ const Scalar normDist = distIn + distEx;
448+
449+ // Effective shear modulus
450+ const Scalar eff_sModulus = weightAvgIn * sModulusIn + weightAvgEx * sModulusEx ;
451+
452+ // Distance ratio
453+ const Scalar distRatio = 0.5 * weightProd / normDist;
454+
455+ // Solid pressure equation (direction-independent equation)
456+ const Evaluation& solidP = materialState.solidPressure ();
457+ bndryTerm[contiSolidPresEqIdx] +=
458+ distRatio * eff_sModulus * solidP;
459+
460+ // Pre-compute dot product for rotation equation
461+ Evaluation dotProd = 0 ;
462+ for (int dirIdx = 0 ; dirIdx < 3 ; ++dirIdx) {
463+ dotProd += faceNormal[dirIdx] * materialState.rotation (dirIdx);
464+ }
465+
466+ // Loop over x-, y- and z-dir (corresponding to dirIdx = 0, 1, 2)
467+ for (int dirIdx = 0 ; dirIdx < 3 ; ++dirIdx) {
468+ // Direction indices in cross-product
469+ unsigned dirIdxNeg = detail::modNeg (dirIdx - 1 );
470+ unsigned dirIdxPos = detail::modNeg (dirIdx + 1 );
471+
472+ // Displacement equation
473+ const Scalar faceNormalDir = faceNormal[dirIdx];
474+ const Scalar faceNormalNeg = faceNormal[dirIdxNeg];
475+ const Scalar faceNormalPos = faceNormal[dirIdxPos];
476+
477+ const Evaluation& disp = materialState.displacement (dirIdx);
478+
479+ const Evaluation& rotNeg = materialState.rotation (dirIdxNeg);
480+ const Evaluation& rotPos = materialState.rotation (dirIdxPos);
481+
482+ bndryTerm[conti0EqIdx + dirIdx] +=
483+ 2.0 * (eff_sModulus / normDist) * disp
484+ - weightAvgIn * (faceNormalNeg * rotPos - faceNormalPos * rotNeg)
485+ - faceNormalDir * weightAvgIn * solidP;
486+
487+ // Rotation equation
488+ const Evaluation& dispNeg = materialState.displacement (dirIdxNeg);
489+ const Evaluation& dispPos = materialState.displacement (dirIdxPos);
490+
491+ const Evaluation& rot = materialState.rotation (dirIdx);
492+
493+ bndryTerm[contiRotEqIdx + dirIdx] +=
494+ - weightAvgEx * (faceNormalNeg * dispPos - faceNormalPos * dispNeg)
495+ + distRatio * eff_sModulus * (dotProd * faceNormalDir - rot);
496+
497+ // Solid pressure (directional-dependent) equation
498+ bndryTerm[contiSolidPresEqIdx] +=
499+ - faceNormalDir * weightAvgEx * disp;
500+ }
501+ }
502+
403503 /* !
404504 * \brief Calculate source term in TPSA formulation
405505 *
0 commit comments