From 52439ee968a48bc1ed7d04c8601f19eee95254b5 Mon Sep 17 00:00:00 2001 From: "Yoojin S. Cha" Date: Thu, 12 Mar 2026 13:20:52 -0700 Subject: [PATCH 01/64] implement mfd for mixed method and update unit tests --- .../BdVLMInnerProduct.hpp | 142 +++++++- .../QuasiTPFAInnerProduct.hpp | 137 +++++++- .../SimpleInnerProduct.hpp | 131 +++++++- .../mimeticInnerProducts/TPFAInnerProduct.hpp | 75 ++++- .../unitTests/testMimeticInnerProducts.cpp | 309 ++++++++++++++++++ 5 files changed, 790 insertions(+), 4 deletions(-) diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp index 5bde9c3a3c3..62ed1c5d1cf 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp @@ -40,7 +40,7 @@ class BdVLMInnerProduct : public MimeticInnerProductBase /** * @brief In a given element, recompute the transmissibility matrix in a cell using the inner product of Beirao da Veiga, Lipnikov, - * Manzini + * Manzini (page 113) * @param[in] nodePosition the position of the nodes * @param[in] transMultiplier the transmissibility multipliers at the mesh faces * @param[in] faceToNodes the map from the face to their nodes @@ -65,6 +65,18 @@ class BdVLMInnerProduct : public MimeticInnerProductBase real64 const (&elemPerm)[ 3 ], real64 const & lengthTolerance, arraySlice2d< real64 > const & transMatrix ); + + template< localIndex NF > + GEOS_HOST_DEVICE + static void + computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, + ArrayOfArraysView< localIndex const > const & faceToNodes, + arraySlice1d< localIndex const > const & elemToFaces, + arraySlice1d< real64 const > const & elemCenter, + real64 const & elemVolume, + real64 const (&elemPerm)[ 3 ], + real64 const & lengthTolerance, + arraySlice2d< real64 > const & M ); }; @@ -207,6 +219,134 @@ BdVLMInnerProduct::compute( arrayView2d< real64 const, nodes::REFERENCE_POSITION } +template< localIndex NF > +GEOS_HOST_DEVICE +void +BdVLMInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, + ArrayOfArraysView< localIndex const > const & faceToNodes, + arraySlice1d< localIndex const > const & elemToFaces, + arraySlice1d< real64 const > const & elemCenter, + real64 const & elemVolume, + real64 const (&elemPerm)[ 3 ], + real64 const & lengthTolerance, + arraySlice2d< real64 > const & M ) +{ + GEOS_UNUSED_VAR( elemVolume ); + real64 const areaTolerance = lengthTolerance * lengthTolerance; + + real64 cellToFaceMat[ NF ][ 3 ] = {{ 0 }}; + real64 normalsMat[ NF ][ 3 ] = {{ 0 }}; + real64 permMat[ 3 ][ 3 ] = {{ 0 }}; + real64 faceAreaMat[ NF ][ NF ] = {{ 0 }}; + real64 faceArea[ NF ] = { 0.0 }; // store diag(A) + + real64 work_dimByDim[ 3 ][ 3 ] = {{ 0 }}; + real64 work_numFacesByDim[ NF ][ 3 ] = {{ 0 }}; + real64 work_dimByNumFaces[ 3 ][ NF ] = {{ 0 }}; + real64 work_numFacesByNumFaces[ NF ][ NF ] = {{ 0 }}; + real64 tmp_numFacesByNumFaces[ NF ][ NF ] = {{ 0 }}; + + // 0) assemble full coefficient tensor from principal axis/components + MimeticInnerProductHelpers::makeFullTensor( elemPerm, permMat ); + + // 1) fill R (= cellToFaceMat) and normalsMat + for ( localIndex ifaceLoc = 0; ifaceLoc < NF; ++ifaceLoc ) + { + real64 faceCenter[ 3 ], faceNormal[ 3 ], cellToFaceVec[ 3 ]; + + faceAreaMat[ ifaceLoc ][ ifaceLoc ] = + computationalGeometry::centroid_3DPolygon( faceToNodes[ elemToFaces[ ifaceLoc ] ], + nodePosition, + faceCenter, + faceNormal, + areaTolerance ); + + faceArea[ ifaceLoc ] = faceAreaMat[ ifaceLoc ][ ifaceLoc ]; + + LvArray::tensorOps::copy< 3 >( cellToFaceVec, faceCenter ); + LvArray::tensorOps::subtract< 3 >( cellToFaceVec, elemCenter ); + + // R row: A_f * (x_f - x_c) + cellToFaceMat[ ifaceLoc ][ 0 ] = faceArea[ ifaceLoc ] * cellToFaceVec[ 0 ]; + cellToFaceMat[ ifaceLoc ][ 1 ] = faceArea[ ifaceLoc ] * cellToFaceVec[ 1 ]; + cellToFaceMat[ ifaceLoc ][ 2 ] = faceArea[ ifaceLoc ] * cellToFaceVec[ 2 ]; + + // orient normal outward + if( LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceNormal ) < 0.0 ) + { + LvArray::tensorOps::scale< 3 >( faceNormal, -1.0 ); + } + + // normalsMat row + normalsMat[ ifaceLoc ][ 0 ] = faceNormal[ 0 ]; + normalsMat[ ifaceLoc ][ 1 ] = faceNormal[ 1 ]; + normalsMat[ ifaceLoc ][ 2 ] = faceNormal[ 2 ]; + } + + // 2) compute N + LvArray::tensorOps::Rij_eq_AikBkj< NF, 3, 3 >( work_numFacesByDim, + normalsMat, + permMat ); + + // BdVLM inner product matrix M = M0 + M1 by Beirao da Veiga, Lipnikov, Manzini (pg.89) + // M0 = R (R^T N)^(-1) R^T + + // 3) compute (R^T N)^-1 -> (3 X 3), work_dimByDim = R^T N + LvArray::tensorOps::Rij_eq_AkiBkj< 3, 3, NF >( work_dimByDim, + cellToFaceMat, + work_numFacesByDim ); + LvArray::tensorOps::invert< 3 >( work_dimByDim ); + + // 4) compute R (R^T N)^(-1) R^T into M + // work_dimByNumFaces = (R^T N)^-1 R^T -> (3 X NF) + LvArray::tensorOps::Rij_eq_AikBjk< 3, NF, 3 >( work_dimByNumFaces, + work_dimByDim, + cellToFaceMat ); + + // M = R * work_dimByNumFaces (NF x NF) + LvArray::tensorOps::Rij_eq_AikBkj< NF, NF, 3 >( M, + cellToFaceMat, + work_dimByNumFaces ); + + // P_N = I - N (N^T N)^(-1) N^T + // 5) compute (N^T N)^-1 + LvArray::tensorOps::Rij_eq_AkiAkj< 3, NF >( work_dimByDim, + work_numFacesByDim ); // N + LvArray::tensorOps::invert< 3 >( work_dimByDim ); + + // 6) tmp = (N^T N)^-1 N^T + LvArray::tensorOps::Rij_eq_AikBjk< 3, NF, 3 >( work_dimByNumFaces, + work_dimByDim, + work_numFacesByDim ); + + // 7) work_numFacesByNumFaces = -I + N * tmp + LvArray::tensorOps::addIdentity< NF >( work_numFacesByNumFaces, -1.0 ); + LvArray::tensorOps::Rij_add_AikBkj< NF, NF, 3 >( work_numFacesByNumFaces, + work_numFacesByDim, // N + work_dimByNumFaces // tmp + ); + + // 8) M = M0 + gamma * P_N + real64 const gamma = 1.0 / static_cast< real64 >( NF ); + LvArray::tensorOps::scaledAdd< NF, NF >( M, work_numFacesByNumFaces, -gamma ); + + // convert to flux-based inner product: M_flux = A^{-1} M_vel A^{-1} + // here, A is diag(faceArea) + real64 invA[ NF ]; + for( localIndex i = 0; i < NF; ++i ) + { + invA[ i ] = 1.0 / faceArea[ i ]; + } + + for( localIndex i = 0; i < NF; ++i ) + { + for( localIndex j = 0; j < NF; ++j ) + { + M[ i ][ j ] *= invA[ i ] * invA[ j ]; + } + } +} + } // end namespace mimeticInnerProduct } // end namespace geos diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp index 4f481e7181b..4082941a677 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp @@ -62,7 +62,18 @@ class QuasiTPFAInnerProduct : public MimeticInnerProductBase real64 const (&elemPerm)[ 3 ], real64 const & lengthTolerance, arraySlice2d< real64 > const & transMatrix ); - + + template< localIndex NF > + GEOS_HOST_DEVICE + static void + computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, + ArrayOfArraysView< localIndex const > const & faceToNodes, + arraySlice1d< localIndex const > const & elemToFaces, + arraySlice1d< real64 const > const & elemCenter, + real64 const & elemVolume, + real64 const (&elemPerm)[ 3 ], + real64 const & lengthTolerance, + arraySlice2d< real64 > const & M ); }; template< localIndex NF > @@ -90,6 +101,130 @@ QuasiTPFAInnerProduct::compute( arrayView2d< real64 const, nodes::REFERENCE_POSI transMatrix ); } +template< localIndex NF > +GEOS_HOST_DEVICE +void +QuasiTPFAInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, + ArrayOfArraysView< localIndex const > const & faceToNodes, + arraySlice1d< localIndex const > const & elemToFaces, + arraySlice1d< real64 const > const & elemCenter, + real64 const & elemVolume, + real64 const (&elemPerm)[ 3 ], + real64 const & lengthTolerance, + arraySlice2d< real64 > const & M ) +{ + real64 const areaTolerance = lengthTolerance * lengthTolerance; + + // 0) stabilization parameter for quasi-TPFA + constexpr real64 tParam = 2.0; + + // 1) assemble permeability tensor and its inverse + real64 K[3][3] = {{0}}; + MimeticInnerProductHelpers::makeFullTensor( elemPerm, K ); + + real64 Kinv[3][3] = {{0}}; + for( int i = 0; i < 3; ++i ) + { + Kinv[i][i] = 1.0 / elemPerm[i]; + } + + // 2) compute C and N + real64 C[ NF ][ 3 ] = {{ 0 }}; + real64 N[ NF ][ 3 ] = {{ 0 }}; + + for( localIndex ifaceLoc = 0; ifaceLoc < NF; ++ifaceLoc ) + { + real64 faceCenter[3], faceNormal[3]; + + real64 const faceArea = + computationalGeometry::centroid_3DPolygon( + faceToNodes[elemToFaces[ifaceLoc]], + nodePosition, + faceCenter, + faceNormal, + areaTolerance ); + + real64 cellToFaceVec[3]; + LvArray::tensorOps::copy<3>( cellToFaceVec, faceCenter ); + LvArray::tensorOps::subtract<3>( cellToFaceVec, elemCenter ); + + if( LvArray::tensorOps::AiBi<3>( cellToFaceVec, faceNormal ) < 0.0 ) + { + LvArray::tensorOps::scale<3>( faceNormal, -1.0 ); + } + + for( int d = 0; d < 3; ++d ) + { + C[ifaceLoc][d] = cellToFaceVec[d]; + N[ifaceLoc][d] = faceArea * faceNormal[d]; + } + } + + // 3) compute consistency part C K^{-1} C^T / volume + real64 CKCt[ NF ][ NF ] = {{ 0 }}; + real64 work[ 3 ][ NF ] = {{ 0 }}; + + LvArray::tensorOps::Rij_eq_AikBjk< 3, NF, 3 >( work, Kinv, C ); + LvArray::tensorOps::Rij_eq_AikBkj< NF, NF, 3 >( CKCt, C, work ); + LvArray::tensorOps::scale< NF, NF >( CKCt, 1.0 / elemVolume ); + + // 4) compute W = N K N' + real64 W[ NF ][ NF ] = {{ 0 }}; + real64 workNK[ 3 ][ NF ] = {{ 0 }}; + + LvArray::tensorOps::Rij_eq_AikBjk< 3, NF, 3 >( workNK, K, N ); + LvArray::tensorOps::Rij_eq_AikBkj< NF, NF, 3 >( W, N, workNK ); + + // 5) build Q from C (orthonormal basis of consistency space) + real64 q0[ NF ], q1[ NF ], q2[ NF ]; + real64 Qmat[ NF ][ 3 ]; + + for( localIndex i = 0; i < NF; ++i ) + { + q0[i] = C[i][0]; + q1[i] = C[i][1]; + q2[i] = C[i][2]; + } + + MimeticInnerProductHelpers::orthonormalize< NF >( q0, q1, q2, Qmat ); + + // 6) compute P = I - Q Q^T + real64 P[ NF ][ NF ] = {{ 0 }}; + LvArray::tensorOps::addIdentity< NF >( P, -1.0 ); + LvArray::tensorOps::Rij_add_AikAjk< NF, 3 >( P, Qmat ); + LvArray::tensorOps::scale< NF, NF >( P, -1.0 ); + + // 7) compute stabilization term (v/t) P diag(W)^{-1} P + real64 stab[ NF ][ NF ] = {{ 0 }}; + + for( localIndex i = 0; i < NF; ++i ) + { + for( localIndex j = 0; j < NF; ++j ) + { + real64 val = 0.0; + for( localIndex k = 0; k < NF; ++k ) + { + if( LvArray::math::abs( W[k][k] ) > 0 ) + { + val += P[i][k] * ( 1.0 / W[k][k] ) * P[k][j]; + } + } + stab[i][j] = val; + } + } + + real64 const scale = elemVolume / tParam; + + // 8) assemble final M + for( localIndex i = 0; i < NF; ++i ) + { + for( localIndex j = 0; j < NF; ++j ) + { + M[i][j] = CKCt[i][j] + scale * stab[i][j]; + } + } +} + } // end namespace mimeticInnerProduct } // end namespace geos diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp index da7f4b2b330..5f243b8a589 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp @@ -23,6 +23,9 @@ #include "finiteVolume/mimeticInnerProducts/MimeticInnerProductBase.hpp" #include "finiteVolume/mimeticInnerProducts/MimeticInnerProductHelpers.hpp" #include "mesh/utilities/ComputationalGeometry.hpp" +#include +#include + namespace geos { @@ -64,7 +67,18 @@ class SimpleInnerProduct : public MimeticInnerProductBase real64 const (&elemPerm)[ 3 ], real64 const & lengthTolerance, arraySlice2d< real64 > const & transMatrix ); - + + template< localIndex NF > + GEOS_HOST_DEVICE + static void + computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, + ArrayOfArraysView< localIndex const > const & faceToNodes, + arraySlice1d< localIndex const > const & elemToFaces, + arraySlice1d< real64 const > const & elemCenter, + real64 const & elemVolume, + real64 const (&elemPerm)[ 3 ], + real64 const & lengthTolerance, + arraySlice2d< real64 > const & M ); }; template< localIndex NF > @@ -195,6 +209,121 @@ SimpleInnerProduct::compute( arrayView2d< real64 const, nodes::REFERENCE_POSITIO } +template< localIndex NF > +GEOS_HOST_DEVICE +void +SimpleInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, + ArrayOfArraysView< localIndex const > const & faceToNodes, + arraySlice1d< localIndex const > const & elemToFaces, + arraySlice1d< real64 const > const & elemCenter, + real64 const & elemVolume, + real64 const (&elemPerm)[ 3 ], + real64 const & lengthTolerance, + arraySlice2d< real64 > const & M ) +{ + real64 const areaTolerance = lengthTolerance * lengthTolerance; + // 0) Helper: diagonal inverse of [k1, k2, k3] + real64 Kinv[3][3] = {{ 0 }}; + for( int i =0; i < 3; ++i) + { + Kinv[i][i] = 1.0 / elemPerm[i]; + } + + // 1) Compute C, N, A + real64 C[ NF ][ 3 ] = {{ 0 }}; + real64 N[ NF ][ 3 ] = {{ 0 }}; + real64 A[ NF ] = { 0.0 }; + + for( localIndex i = 0; i < NF; ++i) + { + real64 fCenter[3], fNormal[3]; + real64 area = computationalGeometry::centroid_3DPolygon( + faceToNodes[elemToFaces[i]], nodePosition, fCenter, fNormal, areaTolerance ); + + real64 vec_cf[3]; + LvArray::tensorOps::copy<3>(vec_cf, fCenter); + LvArray::tensorOps::subtract<3>(vec_cf, elemCenter); + if ( LvArray::tensorOps::AiBi<3>(vec_cf, fNormal) < 0.0 ) + { + LvArray::tensorOps::scale<3>(fNormal, -1.0); + } + + for ( int d = 0; d < 3; ++d) + { + C[i][d] = vec_cf[d]; + N[i][d] = fNormal[d] * area; + } + + A[i] = area; + } + + // 2) Compute C K^{-1} C^T / volume + real64 CKCt[ NF ][ NF ] = {{0}}; + real64 work3xNF[ 3 ][ NF ] = {{0}}; + + LvArray::tensorOps::Rij_eq_AikBjk< 3, NF, 3 >( work3xNF, Kinv, C ); + LvArray::tensorOps::Rij_eq_AikBkj< NF, NF, 3 >( CKCt, C, work3xNF ); + LvArray::tensorOps::scale< NF, NF >( CKCt, 1.0 / elemVolume ); + + // 3) Q = orth(N / A) + real64 q0[ NF ], q1[ NF ], q2[ NF ]; + real64 Qmat[ NF ][ 3 ]; + for( localIndex i = 0; i < NF; ++i) + { + q0[i] = N[i][0] / A[i]; + q1[i] = N[i][1] / A[i]; + q2[i] = N[i][2] / A[i]; + } + + MimeticInnerProductHelpers::orthonormalize< NF >( q0, q1, q2, Qmat ); + + // 4) U = I - Q Q^T + real64 U[ NF ][ NF ] = {{0}}; + LvArray::tensorOps::addIdentity< NF >( U, -1.0 ); + LvArray::tensorOps::Rij_add_AikAjk< NF, 3 >( U, Qmat ); + LvArray::tensorOps::scale< NF, NF >( U, -1.0 ); + + // 5) A^{-1} * U * A^{-1} + real64 invA[ NF ]; + for( localIndex i = 0; i < NF; ++i) + { + invA[i] = 1.0 / A[i]; + } + + // temp = A^{-1} * U + real64 temp[ NF ][ NF ]; + for ( localIndex i = 0; i < NF; ++i ) + { + for ( localIndex j = 0; j < NF; ++j ) + { + temp[i][j] = invA[i] * U[i][j]; + } + } + + // Usc = A^{-1} * U * A^{-1} + real64 Usc[ NF ][ NF ]; + for ( localIndex i = 0; i < NF; ++i ) + { + for ( localIndex j = 0; j < NF; ++j ) + { + Usc[i][j] = temp[i][j] * invA[j]; + } + } + + // 6) M = CKCt + (v / t) * Usc + real64 tParam = 2.0 * ( elemPerm[0] + elemPerm[1] + elemPerm[2] ); + real64 scale = elemVolume / tParam; + + for( localIndex i = 0; i < NF; ++i ) + { + for ( localIndex j = 0; j < NF; ++j) + { + M[i][j] = CKCt[i][j] + scale * Usc[i][j]; + } + } +} + + } // end namespace mimeticInnerProduct } // end namespace geos diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp index dde1042e41b..e9bb2a5a159 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp @@ -60,7 +60,18 @@ class TPFAInnerProduct : public MimeticInnerProductBase real64 const (&elemPerm)[ 3 ], real64 const & lengthTolerance, arraySlice2d< real64 > const & transMatrix ); - + + template< localIndex NF> + GEOS_HOST_DEVICE + static void + computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, + ArrayOfArraysView< localIndex const > const & faceToNodes, + arraySlice1d< localIndex const > const & elemToFaces, + arraySlice1d< real64 const > const & elemCenter, + real64 const & elemVolume, + real64 const (&elemPerm)[ 3 ], + real64 const & lengthTolerance, + arraySlice2d< real64 > const & M ); }; template< localIndex NF > @@ -129,6 +140,68 @@ TPFAInnerProduct::compute( arrayView2d< real64 const, nodes::REFERENCE_POSITION_ } } +template< localIndex NF > +GEOS_HOST_DEVICE +void +TPFAInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, + ArrayOfArraysView< localIndex const > const & faceToNodes, + arraySlice1d< localIndex const > const & elemToFaces, + arraySlice1d< real64 const > const & elemCenter, + real64 const & elemVolume, + real64 const (&elemPerm)[ 3 ], + real64 const & lengthTolerance, + arraySlice2d< real64 > const & M ) +{ + GEOS_UNUSED_VAR( elemVolume ); + + real64 const areaTolerance = lengthTolerance * lengthTolerance; + real64 const weightTolerance = 1e-30 * lengthTolerance; + + // initialize M to zero + for( localIndex i = 0; i < NF; ++i ) + { + for ( localIndex j = 0; j < NF; ++j) + { + M[i][j] = 0.0; + } + } + + for( localIndex ifaceLoc = 0; ifaceLoc < NF; ++ifaceLoc ) + { + real64 faceCenter[3], faceNormal[3], faceConormal[3], cellToFaceVec[3]; + + // 1) face geometry + real64 const faceArea = + computationalGeometry::centroid_3DPolygon( faceToNodes[elemToFaces[ifaceLoc]], + nodePosition, + faceCenter, + faceNormal, + areaTolerance ); + + LvArray::tensorOps::copy<3>(cellToFaceVec, faceCenter); + LvArray::tensorOps::subtract<3>(cellToFaceVec, elemCenter); + + if( LvArray::tensorOps::AiBi<3>(cellToFaceVec, faceNormal) < 0.0 ) + { + LvArray::tensorOps::scale< 3 >( faceNormal, -1 ); + } + + real64 const c2fDistance = LvArray::tensorOps::normalize<3>( cellToFaceVec ); + + // 2) K * n (TPFA assumes diagonal K) + LvArray::tensorOps::hadamardProduct<3>(faceConormal, elemPerm, faceNormal); + + // 3) compute T_ii + real64 Tii = LvArray::tensorOps::AiBi<3>( cellToFaceVec, faceConormal ) * faceArea / c2fDistance; + + Tii = LvArray::math::max( Tii, weightTolerance ); + + // 4) M = |T|^{-1} + M[ifaceLoc][ifaceLoc] = 1.0 / LvArray::math::abs( Tii ); + } +} + + } // end namespace mimeticInnerProduct } // end namespace geos diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp index 49c1df7a4c5..da4ae05498f 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp @@ -24,6 +24,8 @@ #include "mainInterface/initialization.hpp" #include "mesh/FaceManager.hpp" #include "mesh/utilities/ComputationalGeometry.hpp" +#include +#include // TPL includes #include @@ -60,6 +62,20 @@ void compareTransmissibilityMatrices( arraySlice2d< real64 const > const & trans } } +void compareInnerProductMatrices( arraySlice2d< real64 const > const & M, + arraySlice2d< real64 const > const & Mref ) +{ + for( localIndex ifaceLoc = 0; ifaceLoc < M.size( 0 ); ++ifaceLoc ) + { + for( localIndex jfaceLoc = 0; jfaceLoc < M.size( 1 ); ++jfaceLoc ) + { + checkRelativeError( M( ifaceLoc, jfaceLoc ), + Mref( ifaceLoc, jfaceLoc ), + 1e-15 ); + } + } +} + void computeVolumeAndCenter( array2d< real64, nodes::REFERENCE_POSITION_PERM > const & nodePosition, array1d< localIndex > const & toNodes, real64 ( & elemCenter )[3], @@ -527,6 +543,110 @@ void makeTetra( array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodePosition } } +void makeCube( array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodePosition, + FaceManager::NodeMapType & faceToNodes, + array1d< localIndex > & elemToFaces, + real64 ( & elemCenter )[3], + real64 & elemVolume, + real64 ( & elemPerm )[3], + real64 & lengthTolerance, + integer const ipType, + arraySlice2d< real64 > const & transMatrixRef ) +{ + localIndex constexpr numNodes = 8; + localIndex constexpr numFaces = 6; + localIndex constexpr numNodesPerFace = 4; + + lengthTolerance = 1e-12; + + // permeability + elemPerm[0] = 1.0; + elemPerm[1] = 1.0; + elemPerm[2] = 1.0; + + // elem-to-faces + elemToFaces.resize( numFaces ); + for( localIndex i = 0; i < numFaces; ++i ) + elemToFaces(i) = i; + + // face-to-nodes + faceToNodes.resize( numFaces ); + for( localIndex f = 0; f < numFaces; ++f ) + faceToNodes.resizeArray( f, numNodesPerFace ); + + // vertices + nodePosition.resize( numNodes, 3 ); + + nodePosition(0,0) = 0; nodePosition(0,1) = 0; nodePosition(0,2) = 0; + nodePosition(1,0) = 1; nodePosition(1,1) = 0; nodePosition(1,2) = 0; + nodePosition(2,0) = 1; nodePosition(2,1) = 1; nodePosition(2,2) = 0; + nodePosition(3,0) = 0; nodePosition(3,1) = 1; nodePosition(3,2) = 0; + nodePosition(4,0) = 0; nodePosition(4,1) = 0; nodePosition(4,2) = 1; + nodePosition(5,0) = 1; nodePosition(5,1) = 0; nodePosition(5,2) = 1; + nodePosition(6,0) = 1; nodePosition(6,1) = 1; nodePosition(6,2) = 1; + nodePosition(7,0) = 0; nodePosition(7,1) = 1; nodePosition(7,2) = 1; + + // x = 0 + faceToNodes(0,0) = 0; faceToNodes(0,1) = 3; faceToNodes(0,2) = 7; faceToNodes(0,3) = 4; + + // x = 1 + faceToNodes(1,0) = 1; faceToNodes(1,1) = 2; faceToNodes(1,2) = 6; faceToNodes(1,3) = 5; + + // y = 0 + faceToNodes(2,0) = 0; faceToNodes(2,1) = 1; faceToNodes(2,2) = 5; faceToNodes(2,3) = 4; + + // y = 1 + faceToNodes(3,0) = 3; faceToNodes(3,1) = 2; faceToNodes(3,2) = 6; faceToNodes(3,3) = 7; + + // z = 0 + faceToNodes(4,0) = 0; faceToNodes(4,1) = 1; faceToNodes(4,2) = 2; faceToNodes(4,3) = 3; + + // z = 1 + faceToNodes(5,0) = 4; faceToNodes(5,1) = 5; faceToNodes(5,2) = 6; faceToNodes(5,3) = 7; + + // center & volume + array1d< localIndex > toNodes; + toNodes.resize( numNodes ); + for( localIndex i = 0; i < numNodes; ++i ) + toNodes(i) = i; + + computeVolumeAndCenter( nodePosition, + toNodes, + elemCenter, + elemVolume ); + + // reference matrix (MRST / MATLAB) + if( ipType == InnerProductType::TPFA || + ipType == InnerProductType::QUASI_TPFA ) + { + // diagonal + transMatrixRef(0,0) = 0.5; + transMatrixRef(1,1) = 0.5; + transMatrixRef(2,2) = 0.5; + transMatrixRef(3,3) = 0.5; + transMatrixRef(4,4) = 0.5; + transMatrixRef(5,5) = 0.5; + } + else if( ipType == InnerProductType::SIMPLE || + ipType == InnerProductType::BDVLM ) + { + // SIMPLE = TPFA + stabilization + transMatrixRef(0,0) = 0.333333333333; + transMatrixRef(1,1) = 0.333333333333; + transMatrixRef(2,2) = 0.333333333333; + transMatrixRef(3,3) = 0.333333333333; + transMatrixRef(4,4) = 0.333333333333; + transMatrixRef(5,5) = 0.333333333333; + + transMatrixRef(0,1) = -0.166666666666; + transMatrixRef(1,0) = -0.166666666666; + transMatrixRef(2,3) = -0.166666666666; + transMatrixRef(3,2) = -0.166666666666; + transMatrixRef(4,5) = -0.166666666666; + transMatrixRef(5,4) = -0.166666666666; + } +} + template< localIndex NF, typename ARRAY_VIEW_T > static void runConsistencyTest( array2d< real64, nodes::REFERENCE_POSITION_PERM > const & nodePosition, FaceManager::NodeMapType const & faceToNodes, @@ -1003,6 +1123,195 @@ TEST( testMimeticInnerProducts, BdVLMtetra ) transMatrix.toViewConst() ); } +TEST( testMimeticInnerProducts, TPFAM_cube ) +{ + localIndex constexpr NF = 6; + + array2d< real64, nodes::REFERENCE_POSITION_PERM > nodePosition; + FaceManager::NodeMapType faceToNodes; + array1d< localIndex > elemToFaces; + real64 elemCenter[3] = { 0.0 }; + real64 elemPerm[3] = { 0.0 }; + real64 elemVolume = 0; + real64 lengthTolerance = 0; + + stackArray2d< real64, NF * NF > Mref( NF, NF ); + Mref.setValues< parallelHostPolicy >( 0.0 ); + + makeCube( nodePosition, + faceToNodes, + elemToFaces, + elemCenter, + elemVolume, + elemPerm, + lengthTolerance, + InnerProductType::TPFA, + Mref.toSlice() ); + + stackArray2d< real64, NF * NF > M( NF, NF ); + + stackArray1d< real64, 3 > center( 3 ); + center[0] = elemCenter[0]; + center[1] = elemCenter[1]; + center[2] = elemCenter[2]; + + real64 const perm[ 3 ] = { elemPerm[0], elemPerm[1], elemPerm[2] }; + + TPFAInnerProduct::computeM< NF >( nodePosition.toViewConst(), + faceToNodes.toViewConst(), + elemToFaces.toSliceConst(), + center, + 1, + perm, + lengthTolerance, + M.toSlice() ); + + compareInnerProductMatrices( M.toSliceConst(), + Mref.toSliceConst() ); +} + +TEST( testMimeticInnerProducts, QuasiTPFAM_cube ) +{ + localIndex constexpr NF = 6; + + array2d< real64, nodes::REFERENCE_POSITION_PERM > nodePosition; + FaceManager::NodeMapType faceToNodes; + array1d< localIndex > elemToFaces; + real64 elemCenter[3] = { 0.0 }; + real64 elemPerm[3] = { 0.0 }; + real64 elemVolume = 0; + real64 lengthTolerance = 0; + + stackArray2d< real64, NF * NF > Mref( NF, NF ); + Mref.setValues< parallelHostPolicy >( 0.0 ); + + makeCube( nodePosition, + faceToNodes, + elemToFaces, + elemCenter, + elemVolume, + elemPerm, + lengthTolerance, + InnerProductType::QUASI_TPFA, + Mref.toSlice() ); + + stackArray2d< real64, NF * NF > M( NF, NF ); + + stackArray1d< real64, 3 > center( 3 ); + center[0] = elemCenter[0]; + center[1] = elemCenter[1]; + center[2] = elemCenter[2]; + + real64 const perm[ 3 ] = { elemPerm[0], elemPerm[1], elemPerm[2] }; + + QuasiTPFAInnerProduct::computeM< NF >( nodePosition.toViewConst(), + faceToNodes.toViewConst(), + elemToFaces.toSliceConst(), + center, + 1, + perm, + lengthTolerance, + M.toSlice() ); + + compareInnerProductMatrices( M.toSliceConst(), + Mref.toSliceConst() ); +} + +TEST( testMimeticInnerProducts, SimpleM_cube ) +{ + localIndex constexpr NF = 6; + + array2d< real64, nodes::REFERENCE_POSITION_PERM > nodePosition; + FaceManager::NodeMapType faceToNodes; + array1d< localIndex > elemToFaces; + real64 elemCenter[3] = { 0.0 }; + real64 elemPerm[3] = { 0.0 }; + real64 elemVolume = 0; + real64 lengthTolerance = 0; + + stackArray2d< real64, NF * NF > Mref( NF, NF ); + Mref.setValues< parallelHostPolicy >( 0.0 ); + + makeCube( nodePosition, + faceToNodes, + elemToFaces, + elemCenter, + elemVolume, + elemPerm, + lengthTolerance, + InnerProductType::SIMPLE, + Mref.toSlice() ); + + stackArray2d< real64, NF * NF > M( NF, NF ); + + stackArray1d< real64, 3 > center( 3 ); + center[0] = elemCenter[0]; + center[1] = elemCenter[1]; + center[2] = elemCenter[2]; + + real64 const perm[ 3 ] = { elemPerm[0], elemPerm[1], elemPerm[2] }; + + SimpleInnerProduct::computeM< NF >( nodePosition.toViewConst(), + faceToNodes.toViewConst(), + elemToFaces.toSliceConst(), + center, + 1, + perm, + lengthTolerance, + M.toSlice() ); + + compareInnerProductMatrices( M.toSliceConst(), + Mref.toSliceConst() ); +} + +TEST( testMimeticInnerProducts, BdVLMM_cube ) +{ + localIndex constexpr NF = 6; + + array2d< real64, nodes::REFERENCE_POSITION_PERM > nodePosition; + FaceManager::NodeMapType faceToNodes; + array1d< localIndex > elemToFaces; + real64 elemCenter[3] = { 0.0 }; + real64 elemPerm[3] = { 0.0 }; + real64 elemVolume = 0; + real64 lengthTolerance = 0; + + stackArray2d< real64, NF * NF > M( NF, NF ); + M.setValues< parallelHostPolicy >( 0.0 ); + + stackArray2d< real64, NF * NF > Mref( NF, NF ); + Mref.setValues< parallelHostPolicy >( 0.0 ); + + makeCube( nodePosition, + faceToNodes, + elemToFaces, + elemCenter, + elemVolume, + elemPerm, + lengthTolerance, + InnerProductType::BDVLM, + Mref.toSlice() ); + + stackArray1d< real64, 3 > center( 3 ); + center[0] = elemCenter[0]; + center[1] = elemCenter[1]; + center[2] = elemCenter[2]; + + real64 const perm[ 3 ] = { elemPerm[0], elemPerm[1], elemPerm[2] }; + + BdVLMInnerProduct::computeM< NF >( nodePosition.toViewConst(), + faceToNodes.toViewConst(), + elemToFaces.toSliceConst(), + center, + elemVolume, + perm, + lengthTolerance, + M.toSlice() ); + + compareInnerProductMatrices( M.toSliceConst(), + Mref.toSliceConst() ); +} + //======================== Linear Pressure Recovery Test ============================= // Three cases: without distortion (unit cube cell), with distortion: (1) planar (2) nonplanar From f7d1850881c8032a3855b8645f9d19383694d0b1 Mon Sep 17 00:00:00 2001 From: "Yoojin S. Cha" Date: Thu, 12 Mar 2026 14:26:45 -0700 Subject: [PATCH 02/64] apply uncrustify --- .../BdVLMInnerProduct.hpp | 218 +++++++++--------- .../QuasiTPFAInnerProduct.hpp | 24 +- .../SimpleInnerProduct.hpp | 182 +++++++-------- .../mimeticInnerProducts/TPFAInnerProduct.hpp | 108 ++++----- .../unitTests/testMimeticInnerProducts.cpp | 102 ++++---- 5 files changed, 317 insertions(+), 317 deletions(-) diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp index 62ed1c5d1cf..aa96cae10de 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp @@ -65,7 +65,7 @@ class BdVLMInnerProduct : public MimeticInnerProductBase real64 const (&elemPerm)[ 3 ], real64 const & lengthTolerance, arraySlice2d< real64 > const & transMatrix ); - + template< localIndex NF > GEOS_HOST_DEVICE static void @@ -231,120 +231,120 @@ BdVLMInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITIO real64 const & lengthTolerance, arraySlice2d< real64 > const & M ) { - GEOS_UNUSED_VAR( elemVolume ); - real64 const areaTolerance = lengthTolerance * lengthTolerance; - - real64 cellToFaceMat[ NF ][ 3 ] = {{ 0 }}; - real64 normalsMat[ NF ][ 3 ] = {{ 0 }}; - real64 permMat[ 3 ][ 3 ] = {{ 0 }}; - real64 faceAreaMat[ NF ][ NF ] = {{ 0 }}; - real64 faceArea[ NF ] = { 0.0 }; // store diag(A) - - real64 work_dimByDim[ 3 ][ 3 ] = {{ 0 }}; - real64 work_numFacesByDim[ NF ][ 3 ] = {{ 0 }}; - real64 work_dimByNumFaces[ 3 ][ NF ] = {{ 0 }}; - real64 work_numFacesByNumFaces[ NF ][ NF ] = {{ 0 }}; - real64 tmp_numFacesByNumFaces[ NF ][ NF ] = {{ 0 }}; - - // 0) assemble full coefficient tensor from principal axis/components - MimeticInnerProductHelpers::makeFullTensor( elemPerm, permMat ); - - // 1) fill R (= cellToFaceMat) and normalsMat - for ( localIndex ifaceLoc = 0; ifaceLoc < NF; ++ifaceLoc ) + GEOS_UNUSED_VAR( elemVolume ); + real64 const areaTolerance = lengthTolerance * lengthTolerance; + + real64 cellToFaceMat[ NF ][ 3 ] = {{ 0 }}; + real64 normalsMat[ NF ][ 3 ] = {{ 0 }}; + real64 permMat[ 3 ][ 3 ] = {{ 0 }}; + real64 faceAreaMat[ NF ][ NF ] = {{ 0 }}; + real64 faceArea[ NF ] = { 0.0 }; // store diag(A) + + real64 work_dimByDim[ 3 ][ 3 ] = {{ 0 }}; + real64 work_numFacesByDim[ NF ][ 3 ] = {{ 0 }}; + real64 work_dimByNumFaces[ 3 ][ NF ] = {{ 0 }}; + real64 work_numFacesByNumFaces[ NF ][ NF ] = {{ 0 }}; + real64 tmp_numFacesByNumFaces[ NF ][ NF ] = {{ 0 }}; + + // 0) assemble full coefficient tensor from principal axis/components + MimeticInnerProductHelpers::makeFullTensor( elemPerm, permMat ); + + // 1) fill R (= cellToFaceMat) and normalsMat + for( localIndex ifaceLoc = 0; ifaceLoc < NF; ++ifaceLoc ) + { + real64 faceCenter[ 3 ], faceNormal[ 3 ], cellToFaceVec[ 3 ]; + + faceAreaMat[ ifaceLoc ][ ifaceLoc ] = + computationalGeometry::centroid_3DPolygon( faceToNodes[ elemToFaces[ ifaceLoc ] ], + nodePosition, + faceCenter, + faceNormal, + areaTolerance ); + + faceArea[ ifaceLoc ] = faceAreaMat[ ifaceLoc ][ ifaceLoc ]; + + LvArray::tensorOps::copy< 3 >( cellToFaceVec, faceCenter ); + LvArray::tensorOps::subtract< 3 >( cellToFaceVec, elemCenter ); + + // R row: A_f * (x_f - x_c) + cellToFaceMat[ ifaceLoc ][ 0 ] = faceArea[ ifaceLoc ] * cellToFaceVec[ 0 ]; + cellToFaceMat[ ifaceLoc ][ 1 ] = faceArea[ ifaceLoc ] * cellToFaceVec[ 1 ]; + cellToFaceMat[ ifaceLoc ][ 2 ] = faceArea[ ifaceLoc ] * cellToFaceVec[ 2 ]; + + // orient normal outward + if( LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceNormal ) < 0.0 ) { - real64 faceCenter[ 3 ], faceNormal[ 3 ], cellToFaceVec[ 3 ]; - - faceAreaMat[ ifaceLoc ][ ifaceLoc ] = - computationalGeometry::centroid_3DPolygon( faceToNodes[ elemToFaces[ ifaceLoc ] ], - nodePosition, - faceCenter, - faceNormal, - areaTolerance ); - - faceArea[ ifaceLoc ] = faceAreaMat[ ifaceLoc ][ ifaceLoc ]; - - LvArray::tensorOps::copy< 3 >( cellToFaceVec, faceCenter ); - LvArray::tensorOps::subtract< 3 >( cellToFaceVec, elemCenter ); - - // R row: A_f * (x_f - x_c) - cellToFaceMat[ ifaceLoc ][ 0 ] = faceArea[ ifaceLoc ] * cellToFaceVec[ 0 ]; - cellToFaceMat[ ifaceLoc ][ 1 ] = faceArea[ ifaceLoc ] * cellToFaceVec[ 1 ]; - cellToFaceMat[ ifaceLoc ][ 2 ] = faceArea[ ifaceLoc ] * cellToFaceVec[ 2 ]; - - // orient normal outward - if( LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceNormal ) < 0.0 ) - { - LvArray::tensorOps::scale< 3 >( faceNormal, -1.0 ); - } - - // normalsMat row - normalsMat[ ifaceLoc ][ 0 ] = faceNormal[ 0 ]; - normalsMat[ ifaceLoc ][ 1 ] = faceNormal[ 1 ]; - normalsMat[ ifaceLoc ][ 2 ] = faceNormal[ 2 ]; + LvArray::tensorOps::scale< 3 >( faceNormal, -1.0 ); } - - // 2) compute N - LvArray::tensorOps::Rij_eq_AikBkj< NF, 3, 3 >( work_numFacesByDim, - normalsMat, - permMat ); - - // BdVLM inner product matrix M = M0 + M1 by Beirao da Veiga, Lipnikov, Manzini (pg.89) - // M0 = R (R^T N)^(-1) R^T - - // 3) compute (R^T N)^-1 -> (3 X 3), work_dimByDim = R^T N - LvArray::tensorOps::Rij_eq_AkiBkj< 3, 3, NF >( work_dimByDim, - cellToFaceMat, - work_numFacesByDim ); - LvArray::tensorOps::invert< 3 >( work_dimByDim ); - - // 4) compute R (R^T N)^(-1) R^T into M - // work_dimByNumFaces = (R^T N)^-1 R^T -> (3 X NF) - LvArray::tensorOps::Rij_eq_AikBjk< 3, NF, 3 >( work_dimByNumFaces, - work_dimByDim, - cellToFaceMat ); - - // M = R * work_dimByNumFaces (NF x NF) - LvArray::tensorOps::Rij_eq_AikBkj< NF, NF, 3 >( M, - cellToFaceMat, - work_dimByNumFaces ); - - // P_N = I - N (N^T N)^(-1) N^T - // 5) compute (N^T N)^-1 - LvArray::tensorOps::Rij_eq_AkiAkj< 3, NF >( work_dimByDim, - work_numFacesByDim ); // N - LvArray::tensorOps::invert< 3 >( work_dimByDim ); - - // 6) tmp = (N^T N)^-1 N^T - LvArray::tensorOps::Rij_eq_AikBjk< 3, NF, 3 >( work_dimByNumFaces, - work_dimByDim, - work_numFacesByDim ); - - // 7) work_numFacesByNumFaces = -I + N * tmp - LvArray::tensorOps::addIdentity< NF >( work_numFacesByNumFaces, -1.0 ); - LvArray::tensorOps::Rij_add_AikBkj< NF, NF, 3 >( work_numFacesByNumFaces, - work_numFacesByDim, // N - work_dimByNumFaces // tmp + + // normalsMat row + normalsMat[ ifaceLoc ][ 0 ] = faceNormal[ 0 ]; + normalsMat[ ifaceLoc ][ 1 ] = faceNormal[ 1 ]; + normalsMat[ ifaceLoc ][ 2 ] = faceNormal[ 2 ]; + } + + // 2) compute N + LvArray::tensorOps::Rij_eq_AikBkj< NF, 3, 3 >( work_numFacesByDim, + normalsMat, + permMat ); + + // BdVLM inner product matrix M = M0 + M1 by Beirao da Veiga, Lipnikov, Manzini (pg.89) + // M0 = R (R^T N)^(-1) R^T + + // 3) compute (R^T N)^-1 -> (3 X 3), work_dimByDim = R^T N + LvArray::tensorOps::Rij_eq_AkiBkj< 3, 3, NF >( work_dimByDim, + cellToFaceMat, + work_numFacesByDim ); + LvArray::tensorOps::invert< 3 >( work_dimByDim ); + + // 4) compute R (R^T N)^(-1) R^T into M + // work_dimByNumFaces = (R^T N)^-1 R^T -> (3 X NF) + LvArray::tensorOps::Rij_eq_AikBjk< 3, NF, 3 >( work_dimByNumFaces, + work_dimByDim, + cellToFaceMat ); + + // M = R * work_dimByNumFaces (NF x NF) + LvArray::tensorOps::Rij_eq_AikBkj< NF, NF, 3 >( M, + cellToFaceMat, + work_dimByNumFaces ); + + // P_N = I - N (N^T N)^(-1) N^T + // 5) compute (N^T N)^-1 + LvArray::tensorOps::Rij_eq_AkiAkj< 3, NF >( work_dimByDim, + work_numFacesByDim ); // N + LvArray::tensorOps::invert< 3 >( work_dimByDim ); + + // 6) tmp = (N^T N)^-1 N^T + LvArray::tensorOps::Rij_eq_AikBjk< 3, NF, 3 >( work_dimByNumFaces, + work_dimByDim, + work_numFacesByDim ); + + // 7) work_numFacesByNumFaces = -I + N * tmp + LvArray::tensorOps::addIdentity< NF >( work_numFacesByNumFaces, -1.0 ); + LvArray::tensorOps::Rij_add_AikBkj< NF, NF, 3 >( work_numFacesByNumFaces, + work_numFacesByDim, // N + work_dimByNumFaces // tmp ); - - // 8) M = M0 + gamma * P_N - real64 const gamma = 1.0 / static_cast< real64 >( NF ); - LvArray::tensorOps::scaledAdd< NF, NF >( M, work_numFacesByNumFaces, -gamma ); - - // convert to flux-based inner product: M_flux = A^{-1} M_vel A^{-1} - // here, A is diag(faceArea) - real64 invA[ NF ]; - for( localIndex i = 0; i < NF; ++i ) - { - invA[ i ] = 1.0 / faceArea[ i ]; - } - for( localIndex i = 0; i < NF; ++i ) + // 8) M = M0 + gamma * P_N + real64 const gamma = 1.0 / static_cast< real64 >( NF ); + LvArray::tensorOps::scaledAdd< NF, NF >( M, work_numFacesByNumFaces, -gamma ); + + // convert to flux-based inner product: M_flux = A^{-1} M_vel A^{-1} + // here, A is diag(faceArea) + real64 invA[ NF ]; + for( localIndex i = 0; i < NF; ++i ) + { + invA[ i ] = 1.0 / faceArea[ i ]; + } + + for( localIndex i = 0; i < NF; ++i ) + { + for( localIndex j = 0; j < NF; ++j ) { - for( localIndex j = 0; j < NF; ++j ) - { - M[ i ][ j ] *= invA[ i ] * invA[ j ]; - } + M[ i ][ j ] *= invA[ i ] * invA[ j ]; } + } } } // end namespace mimeticInnerProduct diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp index 4082941a677..0f6b1b3fe52 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp @@ -62,7 +62,7 @@ class QuasiTPFAInnerProduct : public MimeticInnerProductBase real64 const (&elemPerm)[ 3 ], real64 const & lengthTolerance, arraySlice2d< real64 > const & transMatrix ); - + template< localIndex NF > GEOS_HOST_DEVICE static void @@ -105,13 +105,13 @@ template< localIndex NF > GEOS_HOST_DEVICE void QuasiTPFAInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, - ArrayOfArraysView< localIndex const > const & faceToNodes, - arraySlice1d< localIndex const > const & elemToFaces, - arraySlice1d< real64 const > const & elemCenter, - real64 const & elemVolume, - real64 const (&elemPerm)[ 3 ], - real64 const & lengthTolerance, - arraySlice2d< real64 > const & M ) + ArrayOfArraysView< localIndex const > const & faceToNodes, + arraySlice1d< localIndex const > const & elemToFaces, + arraySlice1d< real64 const > const & elemCenter, + real64 const & elemVolume, + real64 const (&elemPerm)[ 3 ], + real64 const & lengthTolerance, + arraySlice2d< real64 > const & M ) { real64 const areaTolerance = lengthTolerance * lengthTolerance; @@ -145,12 +145,12 @@ QuasiTPFAInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POS areaTolerance ); real64 cellToFaceVec[3]; - LvArray::tensorOps::copy<3>( cellToFaceVec, faceCenter ); - LvArray::tensorOps::subtract<3>( cellToFaceVec, elemCenter ); + LvArray::tensorOps::copy< 3 >( cellToFaceVec, faceCenter ); + LvArray::tensorOps::subtract< 3 >( cellToFaceVec, elemCenter ); - if( LvArray::tensorOps::AiBi<3>( cellToFaceVec, faceNormal ) < 0.0 ) + if( LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceNormal ) < 0.0 ) { - LvArray::tensorOps::scale<3>( faceNormal, -1.0 ); + LvArray::tensorOps::scale< 3 >( faceNormal, -1.0 ); } for( int d = 0; d < 3; ++d ) diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp index 5f243b8a589..e44e03aafee 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp @@ -67,7 +67,7 @@ class SimpleInnerProduct : public MimeticInnerProductBase real64 const (&elemPerm)[ 3 ], real64 const & lengthTolerance, arraySlice2d< real64 > const & transMatrix ); - + template< localIndex NF > GEOS_HOST_DEVICE static void @@ -221,106 +221,106 @@ SimpleInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITI real64 const & lengthTolerance, arraySlice2d< real64 > const & M ) { - real64 const areaTolerance = lengthTolerance * lengthTolerance; - // 0) Helper: diagonal inverse of [k1, k2, k3] - real64 Kinv[3][3] = {{ 0 }}; - for( int i =0; i < 3; ++i) - { - Kinv[i][i] = 1.0 / elemPerm[i]; - } - - // 1) Compute C, N, A - real64 C[ NF ][ 3 ] = {{ 0 }}; - real64 N[ NF ][ 3 ] = {{ 0 }}; - real64 A[ NF ] = { 0.0 }; - - for( localIndex i = 0; i < NF; ++i) - { - real64 fCenter[3], fNormal[3]; - real64 area = computationalGeometry::centroid_3DPolygon( - faceToNodes[elemToFaces[i]], nodePosition, fCenter, fNormal, areaTolerance ); - - real64 vec_cf[3]; - LvArray::tensorOps::copy<3>(vec_cf, fCenter); - LvArray::tensorOps::subtract<3>(vec_cf, elemCenter); - if ( LvArray::tensorOps::AiBi<3>(vec_cf, fNormal) < 0.0 ) - { - LvArray::tensorOps::scale<3>(fNormal, -1.0); - } - - for ( int d = 0; d < 3; ++d) - { - C[i][d] = vec_cf[d]; - N[i][d] = fNormal[d] * area; - } - - A[i] = area; - } - - // 2) Compute C K^{-1} C^T / volume - real64 CKCt[ NF ][ NF ] = {{0}}; - real64 work3xNF[ 3 ][ NF ] = {{0}}; - - LvArray::tensorOps::Rij_eq_AikBjk< 3, NF, 3 >( work3xNF, Kinv, C ); - LvArray::tensorOps::Rij_eq_AikBkj< NF, NF, 3 >( CKCt, C, work3xNF ); - LvArray::tensorOps::scale< NF, NF >( CKCt, 1.0 / elemVolume ); - - // 3) Q = orth(N / A) - real64 q0[ NF ], q1[ NF ], q2[ NF ]; - real64 Qmat[ NF ][ 3 ]; - for( localIndex i = 0; i < NF; ++i) + real64 const areaTolerance = lengthTolerance * lengthTolerance; + // 0) Helper: diagonal inverse of [k1, k2, k3] + real64 Kinv[3][3] = {{ 0 }}; + for( int i =0; i < 3; ++i ) + { + Kinv[i][i] = 1.0 / elemPerm[i]; + } + + // 1) Compute C, N, A + real64 C[ NF ][ 3 ] = {{ 0 }}; + real64 N[ NF ][ 3 ] = {{ 0 }}; + real64 A[ NF ] = { 0.0 }; + + for( localIndex i = 0; i < NF; ++i ) + { + real64 fCenter[3], fNormal[3]; + real64 area = computationalGeometry::centroid_3DPolygon( + faceToNodes[elemToFaces[i]], nodePosition, fCenter, fNormal, areaTolerance ); + + real64 vec_cf[3]; + LvArray::tensorOps::copy< 3 >( vec_cf, fCenter ); + LvArray::tensorOps::subtract< 3 >( vec_cf, elemCenter ); + if( LvArray::tensorOps::AiBi< 3 >( vec_cf, fNormal ) < 0.0 ) { - q0[i] = N[i][0] / A[i]; - q1[i] = N[i][1] / A[i]; - q2[i] = N[i][2] / A[i]; + LvArray::tensorOps::scale< 3 >( fNormal, -1.0 ); } - - MimeticInnerProductHelpers::orthonormalize< NF >( q0, q1, q2, Qmat ); - - // 4) U = I - Q Q^T - real64 U[ NF ][ NF ] = {{0}}; - LvArray::tensorOps::addIdentity< NF >( U, -1.0 ); - LvArray::tensorOps::Rij_add_AikAjk< NF, 3 >( U, Qmat ); - LvArray::tensorOps::scale< NF, NF >( U, -1.0 ); - - // 5) A^{-1} * U * A^{-1} - real64 invA[ NF ]; - for( localIndex i = 0; i < NF; ++i) + + for( int d = 0; d < 3; ++d ) { - invA[i] = 1.0 / A[i]; + C[i][d] = vec_cf[d]; + N[i][d] = fNormal[d] * area; } - - // temp = A^{-1} * U - real64 temp[ NF ][ NF ]; - for ( localIndex i = 0; i < NF; ++i ) + + A[i] = area; + } + + // 2) Compute C K^{-1} C^T / volume + real64 CKCt[ NF ][ NF ] = {{0}}; + real64 work3xNF[ 3 ][ NF ] = {{0}}; + + LvArray::tensorOps::Rij_eq_AikBjk< 3, NF, 3 >( work3xNF, Kinv, C ); + LvArray::tensorOps::Rij_eq_AikBkj< NF, NF, 3 >( CKCt, C, work3xNF ); + LvArray::tensorOps::scale< NF, NF >( CKCt, 1.0 / elemVolume ); + + // 3) Q = orth(N / A) + real64 q0[ NF ], q1[ NF ], q2[ NF ]; + real64 Qmat[ NF ][ 3 ]; + for( localIndex i = 0; i < NF; ++i ) + { + q0[i] = N[i][0] / A[i]; + q1[i] = N[i][1] / A[i]; + q2[i] = N[i][2] / A[i]; + } + + MimeticInnerProductHelpers::orthonormalize< NF >( q0, q1, q2, Qmat ); + + // 4) U = I - Q Q^T + real64 U[ NF ][ NF ] = {{0}}; + LvArray::tensorOps::addIdentity< NF >( U, -1.0 ); + LvArray::tensorOps::Rij_add_AikAjk< NF, 3 >( U, Qmat ); + LvArray::tensorOps::scale< NF, NF >( U, -1.0 ); + + // 5) A^{-1} * U * A^{-1} + real64 invA[ NF ]; + for( localIndex i = 0; i < NF; ++i ) + { + invA[i] = 1.0 / A[i]; + } + + // temp = A^{-1} * U + real64 temp[ NF ][ NF ]; + for( localIndex i = 0; i < NF; ++i ) + { + for( localIndex j = 0; j < NF; ++j ) { - for ( localIndex j = 0; j < NF; ++j ) - { - temp[i][j] = invA[i] * U[i][j]; - } + temp[i][j] = invA[i] * U[i][j]; } - - // Usc = A^{-1} * U * A^{-1} - real64 Usc[ NF ][ NF ]; - for ( localIndex i = 0; i < NF; ++i ) + } + + // Usc = A^{-1} * U * A^{-1} + real64 Usc[ NF ][ NF ]; + for( localIndex i = 0; i < NF; ++i ) + { + for( localIndex j = 0; j < NF; ++j ) { - for ( localIndex j = 0; j < NF; ++j ) - { - Usc[i][j] = temp[i][j] * invA[j]; - } + Usc[i][j] = temp[i][j] * invA[j]; } - - // 6) M = CKCt + (v / t) * Usc - real64 tParam = 2.0 * ( elemPerm[0] + elemPerm[1] + elemPerm[2] ); - real64 scale = elemVolume / tParam; - - for( localIndex i = 0; i < NF; ++i ) + } + + // 6) M = CKCt + (v / t) * Usc + real64 tParam = 2.0 * ( elemPerm[0] + elemPerm[1] + elemPerm[2] ); + real64 scale = elemVolume / tParam; + + for( localIndex i = 0; i < NF; ++i ) + { + for( localIndex j = 0; j < NF; ++j ) { - for ( localIndex j = 0; j < NF; ++j) - { - M[i][j] = CKCt[i][j] + scale * Usc[i][j]; - } + M[i][j] = CKCt[i][j] + scale * Usc[i][j]; } + } } diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp index e9bb2a5a159..9e23dc1649b 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp @@ -60,18 +60,18 @@ class TPFAInnerProduct : public MimeticInnerProductBase real64 const (&elemPerm)[ 3 ], real64 const & lengthTolerance, arraySlice2d< real64 > const & transMatrix ); - - template< localIndex NF> + + template< localIndex NF > GEOS_HOST_DEVICE static void computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, - ArrayOfArraysView< localIndex const > const & faceToNodes, - arraySlice1d< localIndex const > const & elemToFaces, - arraySlice1d< real64 const > const & elemCenter, - real64 const & elemVolume, - real64 const (&elemPerm)[ 3 ], - real64 const & lengthTolerance, - arraySlice2d< real64 > const & M ); + ArrayOfArraysView< localIndex const > const & faceToNodes, + arraySlice1d< localIndex const > const & elemToFaces, + arraySlice1d< real64 const > const & elemCenter, + real64 const & elemVolume, + real64 const (&elemPerm)[ 3 ], + real64 const & lengthTolerance, + arraySlice2d< real64 > const & M ); }; template< localIndex NF > @@ -144,61 +144,61 @@ template< localIndex NF > GEOS_HOST_DEVICE void TPFAInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, - ArrayOfArraysView< localIndex const > const & faceToNodes, - arraySlice1d< localIndex const > const & elemToFaces, - arraySlice1d< real64 const > const & elemCenter, - real64 const & elemVolume, - real64 const (&elemPerm)[ 3 ], - real64 const & lengthTolerance, - arraySlice2d< real64 > const & M ) + ArrayOfArraysView< localIndex const > const & faceToNodes, + arraySlice1d< localIndex const > const & elemToFaces, + arraySlice1d< real64 const > const & elemCenter, + real64 const & elemVolume, + real64 const (&elemPerm)[ 3 ], + real64 const & lengthTolerance, + arraySlice2d< real64 > const & M ) { GEOS_UNUSED_VAR( elemVolume ); real64 const areaTolerance = lengthTolerance * lengthTolerance; real64 const weightTolerance = 1e-30 * lengthTolerance; - + // initialize M to zero - for( localIndex i = 0; i < NF; ++i ) + for( localIndex i = 0; i < NF; ++i ) + { + for( localIndex j = 0; j < NF; ++j ) { - for ( localIndex j = 0; j < NF; ++j) - { - M[i][j] = 0.0; - } + M[i][j] = 0.0; } - - for( localIndex ifaceLoc = 0; ifaceLoc < NF; ++ifaceLoc ) + } + + for( localIndex ifaceLoc = 0; ifaceLoc < NF; ++ifaceLoc ) + { + real64 faceCenter[3], faceNormal[3], faceConormal[3], cellToFaceVec[3]; + + // 1) face geometry + real64 const faceArea = + computationalGeometry::centroid_3DPolygon( faceToNodes[elemToFaces[ifaceLoc]], + nodePosition, + faceCenter, + faceNormal, + areaTolerance ); + + LvArray::tensorOps::copy< 3 >( cellToFaceVec, faceCenter ); + LvArray::tensorOps::subtract< 3 >( cellToFaceVec, elemCenter ); + + if( LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceNormal ) < 0.0 ) { - real64 faceCenter[3], faceNormal[3], faceConormal[3], cellToFaceVec[3]; - - // 1) face geometry - real64 const faceArea = - computationalGeometry::centroid_3DPolygon( faceToNodes[elemToFaces[ifaceLoc]], - nodePosition, - faceCenter, - faceNormal, - areaTolerance ); - - LvArray::tensorOps::copy<3>(cellToFaceVec, faceCenter); - LvArray::tensorOps::subtract<3>(cellToFaceVec, elemCenter); - - if( LvArray::tensorOps::AiBi<3>(cellToFaceVec, faceNormal) < 0.0 ) - { - LvArray::tensorOps::scale< 3 >( faceNormal, -1 ); - } - - real64 const c2fDistance = LvArray::tensorOps::normalize<3>( cellToFaceVec ); - - // 2) K * n (TPFA assumes diagonal K) - LvArray::tensorOps::hadamardProduct<3>(faceConormal, elemPerm, faceNormal); - - // 3) compute T_ii - real64 Tii = LvArray::tensorOps::AiBi<3>( cellToFaceVec, faceConormal ) * faceArea / c2fDistance; - - Tii = LvArray::math::max( Tii, weightTolerance ); - - // 4) M = |T|^{-1} - M[ifaceLoc][ifaceLoc] = 1.0 / LvArray::math::abs( Tii ); + LvArray::tensorOps::scale< 3 >( faceNormal, -1 ); } + + real64 const c2fDistance = LvArray::tensorOps::normalize< 3 >( cellToFaceVec ); + + // 2) K * n (TPFA assumes diagonal K) + LvArray::tensorOps::hadamardProduct< 3 >( faceConormal, elemPerm, faceNormal ); + + // 3) compute T_ii + real64 Tii = LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceConormal ) * faceArea / c2fDistance; + + Tii = LvArray::math::max( Tii, weightTolerance ); + + // 4) M = |T|^{-1} + M[ifaceLoc][ifaceLoc] = 1.0 / LvArray::math::abs( Tii ); + } } diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp index da4ae05498f..ea4e7b0ebb7 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp @@ -63,7 +63,7 @@ void compareTransmissibilityMatrices( arraySlice2d< real64 const > const & trans } void compareInnerProductMatrices( arraySlice2d< real64 const > const & M, - arraySlice2d< real64 const > const & Mref ) + arraySlice2d< real64 const > const & Mref ) { for( localIndex ifaceLoc = 0; ifaceLoc < M.size( 0 ); ++ifaceLoc ) { @@ -567,7 +567,7 @@ void makeCube( array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodePosition, // elem-to-faces elemToFaces.resize( numFaces ); for( localIndex i = 0; i < numFaces; ++i ) - elemToFaces(i) = i; + elemToFaces( i ) = i; // face-to-nodes faceToNodes.resize( numFaces ); @@ -577,38 +577,38 @@ void makeCube( array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodePosition, // vertices nodePosition.resize( numNodes, 3 ); - nodePosition(0,0) = 0; nodePosition(0,1) = 0; nodePosition(0,2) = 0; - nodePosition(1,0) = 1; nodePosition(1,1) = 0; nodePosition(1,2) = 0; - nodePosition(2,0) = 1; nodePosition(2,1) = 1; nodePosition(2,2) = 0; - nodePosition(3,0) = 0; nodePosition(3,1) = 1; nodePosition(3,2) = 0; - nodePosition(4,0) = 0; nodePosition(4,1) = 0; nodePosition(4,2) = 1; - nodePosition(5,0) = 1; nodePosition(5,1) = 0; nodePosition(5,2) = 1; - nodePosition(6,0) = 1; nodePosition(6,1) = 1; nodePosition(6,2) = 1; - nodePosition(7,0) = 0; nodePosition(7,1) = 1; nodePosition(7,2) = 1; + nodePosition( 0, 0 ) = 0; nodePosition( 0, 1 ) = 0; nodePosition( 0, 2 ) = 0; + nodePosition( 1, 0 ) = 1; nodePosition( 1, 1 ) = 0; nodePosition( 1, 2 ) = 0; + nodePosition( 2, 0 ) = 1; nodePosition( 2, 1 ) = 1; nodePosition( 2, 2 ) = 0; + nodePosition( 3, 0 ) = 0; nodePosition( 3, 1 ) = 1; nodePosition( 3, 2 ) = 0; + nodePosition( 4, 0 ) = 0; nodePosition( 4, 1 ) = 0; nodePosition( 4, 2 ) = 1; + nodePosition( 5, 0 ) = 1; nodePosition( 5, 1 ) = 0; nodePosition( 5, 2 ) = 1; + nodePosition( 6, 0 ) = 1; nodePosition( 6, 1 ) = 1; nodePosition( 6, 2 ) = 1; + nodePosition( 7, 0 ) = 0; nodePosition( 7, 1 ) = 1; nodePosition( 7, 2 ) = 1; // x = 0 - faceToNodes(0,0) = 0; faceToNodes(0,1) = 3; faceToNodes(0,2) = 7; faceToNodes(0,3) = 4; + faceToNodes( 0, 0 ) = 0; faceToNodes( 0, 1 ) = 3; faceToNodes( 0, 2 ) = 7; faceToNodes( 0, 3 ) = 4; // x = 1 - faceToNodes(1,0) = 1; faceToNodes(1,1) = 2; faceToNodes(1,2) = 6; faceToNodes(1,3) = 5; + faceToNodes( 1, 0 ) = 1; faceToNodes( 1, 1 ) = 2; faceToNodes( 1, 2 ) = 6; faceToNodes( 1, 3 ) = 5; // y = 0 - faceToNodes(2,0) = 0; faceToNodes(2,1) = 1; faceToNodes(2,2) = 5; faceToNodes(2,3) = 4; + faceToNodes( 2, 0 ) = 0; faceToNodes( 2, 1 ) = 1; faceToNodes( 2, 2 ) = 5; faceToNodes( 2, 3 ) = 4; // y = 1 - faceToNodes(3,0) = 3; faceToNodes(3,1) = 2; faceToNodes(3,2) = 6; faceToNodes(3,3) = 7; + faceToNodes( 3, 0 ) = 3; faceToNodes( 3, 1 ) = 2; faceToNodes( 3, 2 ) = 6; faceToNodes( 3, 3 ) = 7; // z = 0 - faceToNodes(4,0) = 0; faceToNodes(4,1) = 1; faceToNodes(4,2) = 2; faceToNodes(4,3) = 3; + faceToNodes( 4, 0 ) = 0; faceToNodes( 4, 1 ) = 1; faceToNodes( 4, 2 ) = 2; faceToNodes( 4, 3 ) = 3; // z = 1 - faceToNodes(5,0) = 4; faceToNodes(5,1) = 5; faceToNodes(5,2) = 6; faceToNodes(5,3) = 7; + faceToNodes( 5, 0 ) = 4; faceToNodes( 5, 1 ) = 5; faceToNodes( 5, 2 ) = 6; faceToNodes( 5, 3 ) = 7; // center & volume array1d< localIndex > toNodes; toNodes.resize( numNodes ); for( localIndex i = 0; i < numNodes; ++i ) - toNodes(i) = i; + toNodes( i ) = i; computeVolumeAndCenter( nodePosition, toNodes, @@ -620,30 +620,30 @@ void makeCube( array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodePosition, ipType == InnerProductType::QUASI_TPFA ) { // diagonal - transMatrixRef(0,0) = 0.5; - transMatrixRef(1,1) = 0.5; - transMatrixRef(2,2) = 0.5; - transMatrixRef(3,3) = 0.5; - transMatrixRef(4,4) = 0.5; - transMatrixRef(5,5) = 0.5; + transMatrixRef( 0, 0 ) = 0.5; + transMatrixRef( 1, 1 ) = 0.5; + transMatrixRef( 2, 2 ) = 0.5; + transMatrixRef( 3, 3 ) = 0.5; + transMatrixRef( 4, 4 ) = 0.5; + transMatrixRef( 5, 5 ) = 0.5; } else if( ipType == InnerProductType::SIMPLE || ipType == InnerProductType::BDVLM ) { // SIMPLE = TPFA + stabilization - transMatrixRef(0,0) = 0.333333333333; - transMatrixRef(1,1) = 0.333333333333; - transMatrixRef(2,2) = 0.333333333333; - transMatrixRef(3,3) = 0.333333333333; - transMatrixRef(4,4) = 0.333333333333; - transMatrixRef(5,5) = 0.333333333333; - - transMatrixRef(0,1) = -0.166666666666; - transMatrixRef(1,0) = -0.166666666666; - transMatrixRef(2,3) = -0.166666666666; - transMatrixRef(3,2) = -0.166666666666; - transMatrixRef(4,5) = -0.166666666666; - transMatrixRef(5,4) = -0.166666666666; + transMatrixRef( 0, 0 ) = 0.333333333333; + transMatrixRef( 1, 1 ) = 0.333333333333; + transMatrixRef( 2, 2 ) = 0.333333333333; + transMatrixRef( 3, 3 ) = 0.333333333333; + transMatrixRef( 4, 4 ) = 0.333333333333; + transMatrixRef( 5, 5 ) = 0.333333333333; + + transMatrixRef( 0, 1 ) = -0.166666666666; + transMatrixRef( 1, 0 ) = -0.166666666666; + transMatrixRef( 2, 3 ) = -0.166666666666; + transMatrixRef( 3, 2 ) = -0.166666666666; + transMatrixRef( 4, 5 ) = -0.166666666666; + transMatrixRef( 5, 4 ) = -0.166666666666; } } @@ -1158,13 +1158,13 @@ TEST( testMimeticInnerProducts, TPFAM_cube ) real64 const perm[ 3 ] = { elemPerm[0], elemPerm[1], elemPerm[2] }; TPFAInnerProduct::computeM< NF >( nodePosition.toViewConst(), - faceToNodes.toViewConst(), - elemToFaces.toSliceConst(), - center, - 1, - perm, - lengthTolerance, - M.toSlice() ); + faceToNodes.toViewConst(), + elemToFaces.toSliceConst(), + center, + 1, + perm, + lengthTolerance, + M.toSlice() ); compareInnerProductMatrices( M.toSliceConst(), Mref.toSliceConst() ); @@ -1205,13 +1205,13 @@ TEST( testMimeticInnerProducts, QuasiTPFAM_cube ) real64 const perm[ 3 ] = { elemPerm[0], elemPerm[1], elemPerm[2] }; QuasiTPFAInnerProduct::computeM< NF >( nodePosition.toViewConst(), - faceToNodes.toViewConst(), - elemToFaces.toSliceConst(), - center, - 1, - perm, - lengthTolerance, - M.toSlice() ); + faceToNodes.toViewConst(), + elemToFaces.toSliceConst(), + center, + 1, + perm, + lengthTolerance, + M.toSlice() ); compareInnerProductMatrices( M.toSliceConst(), Mref.toSliceConst() ); @@ -1307,7 +1307,7 @@ TEST( testMimeticInnerProducts, BdVLMM_cube ) perm, lengthTolerance, M.toSlice() ); - + compareInnerProductMatrices( M.toSliceConst(), Mref.toSliceConst() ); } From 817ed6a5fb5b280ae1c04fc7a12597df40fca14e Mon Sep 17 00:00:00 2001 From: Nicola Castelletto Date: Sat, 14 Mar 2026 12:35:15 -0700 Subject: [PATCH 03/64] Added helper functions --- .../BdVLMInnerProduct.hpp | 21 ++--- .../MimeticInnerProductHelpers.hpp | 32 ++++++++ .../QuasiTPFAInnerProduct.hpp | 10 +-- .../SimpleInnerProduct.hpp | 79 +++++++------------ .../mimeticInnerProducts/TPFAInnerProduct.hpp | 26 ++---- 5 files changed, 74 insertions(+), 94 deletions(-) diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp index aa96cae10de..12fa949b753 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp @@ -124,18 +124,13 @@ BdVLMInnerProduct::compute( arrayView2d< real64 const, nodes::REFERENCE_POSITION faceNormal, areaTolerance ); - LvArray::tensorOps::copy< 3 >( cellToFaceVec, faceCenter ); - LvArray::tensorOps::subtract< 3 >( cellToFaceVec, elemCenter ); + MimeticInnerProductHelpers::computeCellToFacetVector( cellToFaceVec, faceCenter, elemCenter ); + MimeticInnerProductHelpers::orientNormalOutward( cellToFaceVec, faceNormal ); cellToFaceMat[ ifaceLoc ][0] = faceAreaMat[ ifaceLoc ][ ifaceLoc ] * cellToFaceVec[ 0 ]; cellToFaceMat[ ifaceLoc ][1] = faceAreaMat[ ifaceLoc ][ ifaceLoc ] * cellToFaceVec[ 1 ]; cellToFaceMat[ ifaceLoc ][2] = faceAreaMat[ ifaceLoc ][ ifaceLoc ] * cellToFaceVec[ 2 ]; - if( LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceNormal ) < 0.0 ) - { - LvArray::tensorOps::scale< 3 >( faceNormal, -1 ); - } - // the two-point transmissibility is computed to computed here because it is needed // in the implementation of the transmissibility multiplier (see below) // TODO: see what it would take to bring the (harmonically averaged) two-point trans here @@ -244,7 +239,7 @@ BdVLMInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITIO real64 work_numFacesByDim[ NF ][ 3 ] = {{ 0 }}; real64 work_dimByNumFaces[ 3 ][ NF ] = {{ 0 }}; real64 work_numFacesByNumFaces[ NF ][ NF ] = {{ 0 }}; - real64 tmp_numFacesByNumFaces[ NF ][ NF ] = {{ 0 }}; + // real64 tmp_numFacesByNumFaces[ NF ][ NF ] = {{ 0 }}; // 0) assemble full coefficient tensor from principal axis/components MimeticInnerProductHelpers::makeFullTensor( elemPerm, permMat ); @@ -263,20 +258,14 @@ BdVLMInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITIO faceArea[ ifaceLoc ] = faceAreaMat[ ifaceLoc ][ ifaceLoc ]; - LvArray::tensorOps::copy< 3 >( cellToFaceVec, faceCenter ); - LvArray::tensorOps::subtract< 3 >( cellToFaceVec, elemCenter ); + MimeticInnerProductHelpers::computeCellToFacetVector( cellToFaceVec, faceCenter, elemCenter ); + MimeticInnerProductHelpers::orientNormalOutward( cellToFaceVec, faceNormal ); // R row: A_f * (x_f - x_c) cellToFaceMat[ ifaceLoc ][ 0 ] = faceArea[ ifaceLoc ] * cellToFaceVec[ 0 ]; cellToFaceMat[ ifaceLoc ][ 1 ] = faceArea[ ifaceLoc ] * cellToFaceVec[ 1 ]; cellToFaceMat[ ifaceLoc ][ 2 ] = faceArea[ ifaceLoc ] * cellToFaceVec[ 2 ]; - // orient normal outward - if( LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceNormal ) < 0.0 ) - { - LvArray::tensorOps::scale< 3 >( faceNormal, -1.0 ); - } - // normalsMat row normalsMat[ ifaceLoc ][ 0 ] = faceNormal[ 0 ]; normalsMat[ ifaceLoc ][ 1 ] = faceNormal[ 1 ]; diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/MimeticInnerProductHelpers.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/MimeticInnerProductHelpers.hpp index 7eb8f5558c6..aa240a1ec65 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/MimeticInnerProductHelpers.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/MimeticInnerProductHelpers.hpp @@ -48,6 +48,38 @@ struct MimeticInnerProductHelpers result[ 2 ][ 2 ] = values[ 2 ]; } + /** + * @brief Compute the vector from cell center to facet center. + * @param[out] cellToFacetVec output vector + * @param[in] facetCenter facet center coordinate + * @param[in] cellCenter cell center coordinate + */ + GEOS_HOST_DEVICE + static + void computeCellToFacetVector( real64 (& cellToFacetVec)[ 3 ], + real64 const (& facetCenter)[ 3 ], + arraySlice1d< real64 const > const & cellCenter ) + { + LvArray::tensorOps::copy< 3 >( cellToFacetVec, facetCenter ); + LvArray::tensorOps::subtract< 3 >( cellToFacetVec, cellCenter ); + } + + /** + * @brief Ensure the facet normal points outward from the cell. + * @param[in] cellToFacetVec vector from cell center to face center + * @param[in,out] faceNormal face normal (may be flipped) + */ + GEOS_HOST_DEVICE + static + void orientNormalOutward( real64 const (& cellToFacetVec)[ 3 ], + real64 (& faceNormal)[ 3 ] ) + { + if( LvArray::tensorOps::AiBi< 3 >( cellToFacetVec, faceNormal ) < 0.0 ) + { + LvArray::tensorOps::scale< 3 >( faceNormal, -1.0 ); + } + } + /** * @brief Orthonormalize a set of three vectors * @tparam NF number of faces in the element diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp index 0f6b1b3fe52..853b4af58d1 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp @@ -21,6 +21,7 @@ #define GEOS_FINITEVOLUME_MIMETICINNERPRODUCTS_QUASITPFAINNERPRODUCT_HPP_ #include "finiteVolume/mimeticInnerProducts/MimeticInnerProductBase.hpp" +#include "finiteVolume/mimeticInnerProducts/MimeticInnerProductHelpers.hpp" namespace geos { @@ -145,13 +146,8 @@ QuasiTPFAInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POS areaTolerance ); real64 cellToFaceVec[3]; - LvArray::tensorOps::copy< 3 >( cellToFaceVec, faceCenter ); - LvArray::tensorOps::subtract< 3 >( cellToFaceVec, elemCenter ); - - if( LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceNormal ) < 0.0 ) - { - LvArray::tensorOps::scale< 3 >( faceNormal, -1.0 ); - } + MimeticInnerProductHelpers::computeCellToFacetVector( cellToFaceVec, faceCenter, elemCenter ); + MimeticInnerProductHelpers::orientNormalOutward( cellToFaceVec, faceNormal ); for( int d = 0; d < 3; ++d ) { diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp index e44e03aafee..57178fb773b 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp @@ -127,18 +127,13 @@ SimpleInnerProduct::compute( arrayView2d< real64 const, nodes::REFERENCE_POSITIO faceNormal, areaTolerance ); - LvArray::tensorOps::copy< 3 >( cellToFaceVec, faceCenter ); - LvArray::tensorOps::subtract< 3 >( cellToFaceVec, elemCenter ); + MimeticInnerProductHelpers::computeCellToFacetVector( cellToFaceVec, faceCenter, elemCenter ); + MimeticInnerProductHelpers::orientNormalOutward( cellToFaceVec, faceNormal ); q0[ ifaceLoc ] = faceArea[ ifaceLoc ] * cellToFaceVec[ 0 ]; q1[ ifaceLoc ] = faceArea[ ifaceLoc ] * cellToFaceVec[ 1 ]; q2[ ifaceLoc ] = faceArea[ ifaceLoc ] * cellToFaceVec[ 2 ]; - if( LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceNormal ) < 0.0 ) - { - LvArray::tensorOps::scale< 3 >( faceNormal, -1 ); - } - // the two-point transmissibility is computed to computed here because it is needed // in the implementation of the transmissibility multiplier (see below) // TODO: see what it would take to bring the (harmonically averaged) two-point trans here @@ -241,12 +236,8 @@ SimpleInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITI faceToNodes[elemToFaces[i]], nodePosition, fCenter, fNormal, areaTolerance ); real64 vec_cf[3]; - LvArray::tensorOps::copy< 3 >( vec_cf, fCenter ); - LvArray::tensorOps::subtract< 3 >( vec_cf, elemCenter ); - if( LvArray::tensorOps::AiBi< 3 >( vec_cf, fNormal ) < 0.0 ) - { - LvArray::tensorOps::scale< 3 >( fNormal, -1.0 ); - } + MimeticInnerProductHelpers::computeCellToFacetVector( vec_cf, fCenter, elemCenter ); + MimeticInnerProductHelpers::orientNormalOutward( vec_cf, fNormal ); for( int d = 0; d < 3; ++d ) { @@ -277,48 +268,36 @@ SimpleInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITI MimeticInnerProductHelpers::orthonormalize< NF >( q0, q1, q2, Qmat ); - // 4) U = I - Q Q^T - real64 U[ NF ][ NF ] = {{0}}; - LvArray::tensorOps::addIdentity< NF >( U, -1.0 ); - LvArray::tensorOps::Rij_add_AikAjk< NF, 3 >( U, Qmat ); - LvArray::tensorOps::scale< NF, NF >( U, -1.0 ); - - // 5) A^{-1} * U * A^{-1} - real64 invA[ NF ]; - for( localIndex i = 0; i < NF; ++i ) + // 4) M = CKCt + (v / t) * A^{-1} * ( I - Q Q^T ) * A^{-1} + real64 invA[NF]; + for (localIndex i = 0; i < NF; ++i) { - invA[i] = 1.0 / A[i]; + invA[i] = real64(1) / A[i]; } - - // temp = A^{-1} * U - real64 temp[ NF ][ NF ]; - for( localIndex i = 0; i < NF; ++i ) + + // scale = elemVolume / tParam, where tParam = 2 * trace(K) + real64 const tParam = real64(2) * (elemPerm[0] + elemPerm[1] + elemPerm[2]); + real64 const scale = elemVolume / tParam; + + for (localIndex i = 0; i < NF; ++i) { - for( localIndex j = 0; j < NF; ++j ) - { - temp[i][j] = invA[i] * U[i][j]; - } - } - - // Usc = A^{-1} * U * A^{-1} - real64 Usc[ NF ][ NF ]; - for( localIndex i = 0; i < NF; ++i ) - { - for( localIndex j = 0; j < NF; ++j ) - { - Usc[i][j] = temp[i][j] * invA[j]; - } - } - - // 6) M = CKCt + (v / t) * Usc - real64 tParam = 2.0 * ( elemPerm[0] + elemPerm[1] + elemPerm[2] ); - real64 scale = elemVolume / tParam; + real64 const invAiScaled = scale * invA[i]; - for( localIndex i = 0; i < NF; ++i ) - { - for( localIndex j = 0; j < NF; ++j ) + real64 const qi0 = Qmat[i][0]; + real64 const qi1 = Qmat[i][1]; + real64 const qi2 = Qmat[i][2]; + + for (localIndex j = i; j < NF; ++j) { - M[i][j] = CKCt[i][j] + scale * Usc[i][j]; + // qdot = (Q Q^T)_{ij} = sum_k Qik * Qjk, with k in [0,2] + real64 const qdot = qi0*Qmat[j][0] + qi1*Qmat[j][1] + qi2*Qmat[j][2]; + + real64 const u = (i == j ? real64(1) : real64(0)) - qdot; + + real64 const mij = CKCt[i][j] + (invAiScaled * invA[j]) * u; + + M[i][j] = mij; + M[j][i] = mij; // symmetry fill } } } diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp index 9e23dc1649b..2567544c271 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp @@ -115,13 +115,8 @@ TPFAInnerProduct::compute( arrayView2d< real64 const, nodes::REFERENCE_POSITION_ faceNormal, areaTolerance ); - LvArray::tensorOps::copy< 3 >( cellToFaceVec, faceCenter ); - LvArray::tensorOps::subtract< 3 >( cellToFaceVec, elemCenter ); - - if( LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceNormal ) < 0.0 ) - { - LvArray::tensorOps::scale< 3 >( faceNormal, -1 ); - } + MimeticInnerProductHelpers::computeCellToFacetVector( cellToFaceVec, faceCenter, elemCenter ); + MimeticInnerProductHelpers::orientNormalOutward( cellToFaceVec, faceNormal ); real64 const c2fDistance = LvArray::tensorOps::normalize< 3 >( cellToFaceVec ); @@ -158,13 +153,7 @@ TPFAInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION real64 const weightTolerance = 1e-30 * lengthTolerance; // initialize M to zero - for( localIndex i = 0; i < NF; ++i ) - { - for( localIndex j = 0; j < NF; ++j ) - { - M[i][j] = 0.0; - } - } + LvArray::tensorOps::fill< NF, NF >( M, 0.0 ); for( localIndex ifaceLoc = 0; ifaceLoc < NF; ++ifaceLoc ) { @@ -178,13 +167,8 @@ TPFAInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION faceNormal, areaTolerance ); - LvArray::tensorOps::copy< 3 >( cellToFaceVec, faceCenter ); - LvArray::tensorOps::subtract< 3 >( cellToFaceVec, elemCenter ); - - if( LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceNormal ) < 0.0 ) - { - LvArray::tensorOps::scale< 3 >( faceNormal, -1 ); - } + MimeticInnerProductHelpers::computeCellToFacetVector( cellToFaceVec, faceCenter, elemCenter ); + MimeticInnerProductHelpers::orientNormalOutward( cellToFaceVec, faceNormal ); real64 const c2fDistance = LvArray::tensorOps::normalize< 3 >( cellToFaceVec ); From 5560168c0d6bba2264833ae9d89cda958ce973e7 Mon Sep 17 00:00:00 2001 From: "Yoojin S. Cha" Date: Mon, 16 Mar 2026 16:20:03 -0700 Subject: [PATCH 04/64] unify geometry variable naming --- .../mimeticInnerProducts/SimpleInnerProduct.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp index 57178fb773b..ce1037a9259 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp @@ -231,18 +231,18 @@ SimpleInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITI for( localIndex i = 0; i < NF; ++i ) { - real64 fCenter[3], fNormal[3]; + real64 faceCenter[3], faceNormal[3]; real64 area = computationalGeometry::centroid_3DPolygon( - faceToNodes[elemToFaces[i]], nodePosition, fCenter, fNormal, areaTolerance ); + faceToNodes[elemToFaces[i]], nodePosition, faceCenter, faceNormal, areaTolerance ); - real64 vec_cf[3]; - MimeticInnerProductHelpers::computeCellToFacetVector( vec_cf, fCenter, elemCenter ); - MimeticInnerProductHelpers::orientNormalOutward( vec_cf, fNormal ); + real64 cellToFaceVec[3]; + MimeticInnerProductHelpers::computeCellToFacetVector( cellToFaceVec, faceCenter, elemCenter ); + MimeticInnerProductHelpers::orientNormalOutward( cellToFaceVec, faceNormal ); for( int d = 0; d < 3; ++d ) { - C[i][d] = vec_cf[d]; - N[i][d] = fNormal[d] * area; + C[i][d] = cellToFaceVec[d]; + N[i][d] = faceNormal[d] * area; } A[i] = area; From 3caf737c55ba0b8c41a87c151a1ef6633619592a Mon Sep 17 00:00:00 2001 From: "Yoojin S. Cha" Date: Mon, 16 Mar 2026 16:43:14 -0700 Subject: [PATCH 05/64] apply uncrustify formatting --- .../MimeticInnerProductHelpers.hpp | 4 ++-- .../SimpleInnerProduct.hpp | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/MimeticInnerProductHelpers.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/MimeticInnerProductHelpers.hpp index aa240a1ec65..54afd79bdcf 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/MimeticInnerProductHelpers.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/MimeticInnerProductHelpers.hpp @@ -57,7 +57,7 @@ struct MimeticInnerProductHelpers GEOS_HOST_DEVICE static void computeCellToFacetVector( real64 (& cellToFacetVec)[ 3 ], - real64 const (& facetCenter)[ 3 ], + real64 const (&facetCenter)[ 3 ], arraySlice1d< real64 const > const & cellCenter ) { LvArray::tensorOps::copy< 3 >( cellToFacetVec, facetCenter ); @@ -71,7 +71,7 @@ struct MimeticInnerProductHelpers */ GEOS_HOST_DEVICE static - void orientNormalOutward( real64 const (& cellToFacetVec)[ 3 ], + void orientNormalOutward( real64 const (&cellToFacetVec)[ 3 ], real64 (& faceNormal)[ 3 ] ) { if( LvArray::tensorOps::AiBi< 3 >( cellToFacetVec, faceNormal ) < 0.0 ) diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp index ce1037a9259..e8123bbf8c9 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp @@ -270,32 +270,32 @@ SimpleInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITI // 4) M = CKCt + (v / t) * A^{-1} * ( I - Q Q^T ) * A^{-1} real64 invA[NF]; - for (localIndex i = 0; i < NF; ++i) + for( localIndex i = 0; i < NF; ++i ) { - invA[i] = real64(1) / A[i]; + invA[i] = real64( 1 ) / A[i]; } - + // scale = elemVolume / tParam, where tParam = 2 * trace(K) - real64 const tParam = real64(2) * (elemPerm[0] + elemPerm[1] + elemPerm[2]); + real64 const tParam = real64( 2 ) * (elemPerm[0] + elemPerm[1] + elemPerm[2]); real64 const scale = elemVolume / tParam; - - for (localIndex i = 0; i < NF; ++i) + + for( localIndex i = 0; i < NF; ++i ) { real64 const invAiScaled = scale * invA[i]; real64 const qi0 = Qmat[i][0]; real64 const qi1 = Qmat[i][1]; real64 const qi2 = Qmat[i][2]; - - for (localIndex j = i; j < NF; ++j) + + for( localIndex j = i; j < NF; ++j ) { // qdot = (Q Q^T)_{ij} = sum_k Qik * Qjk, with k in [0,2] real64 const qdot = qi0*Qmat[j][0] + qi1*Qmat[j][1] + qi2*Qmat[j][2]; - - real64 const u = (i == j ? real64(1) : real64(0)) - qdot; - + + real64 const u = (i == j ? real64( 1 ) : real64( 0 )) - qdot; + real64 const mij = CKCt[i][j] + (invAiScaled * invA[j]) * u; - + M[i][j] = mij; M[j][i] = mij; // symmetry fill } From 56659293e11e75af209ca2d6a39be770846b9da4 Mon Sep 17 00:00:00 2001 From: "Yoojin S. Cha" Date: Mon, 16 Mar 2026 17:23:55 -0700 Subject: [PATCH 06/64] add doxygen documentation for computeM functions --- .../mimeticInnerProducts/BdVLMInnerProduct.hpp | 16 +++++++++++++++- .../QuasiTPFAInnerProduct.hpp | 13 +++++++++++++ .../mimeticInnerProducts/SimpleInnerProduct.hpp | 13 +++++++++++++ .../mimeticInnerProducts/TPFAInnerProduct.hpp | 11 +++++++++++ 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp index 12fa949b753..22524666ee5 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp @@ -40,7 +40,7 @@ class BdVLMInnerProduct : public MimeticInnerProductBase /** * @brief In a given element, recompute the transmissibility matrix in a cell using the inner product of Beirao da Veiga, Lipnikov, - * Manzini (page 113) + *Manzini (page 113) * @param[in] nodePosition the position of the nodes * @param[in] transMultiplier the transmissibility multipliers at the mesh faces * @param[in] faceToNodes the map from the face to their nodes @@ -66,6 +66,20 @@ class BdVLMInnerProduct : public MimeticInnerProductBase real64 const & lengthTolerance, arraySlice2d< real64 > const & transMatrix ); + /** + * @brief Compute the mimetic inner product matrix M in a given element using the inner product of Beirao da Veiga, Lipnikov, Manzini + *(page 89) + * @param[in] nodePosition the position of the nodes + * @param[in] faceToNodes the map from the face to their nodes + * @param[in] elemToFaces the maps from the one-sided face to the corresponding face + * @param[in] elemCenter the center of the element + * @param[in] elemVolume the volume of the element + * @param[in] elemPerm the permeability in the element + * @param[in] lengthTolerance the tolerance used in the trans calculations + * @param[inout] M the output inner product matrix + * + * @details Reference: Beirao da Veiga, Lipnikov, Manzini, "The mimetic finite-difference method for elliptic problems" + */ template< localIndex NF > GEOS_HOST_DEVICE static void diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp index 853b4af58d1..d966871f120 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp @@ -64,6 +64,19 @@ class QuasiTPFAInnerProduct : public MimeticInnerProductBase real64 const & lengthTolerance, arraySlice2d< real64 > const & transMatrix ); + /** + * @brief Compute the mimetic inner product matrix M in a given element using the quasi TPFA inner product. + * @param[in] nodePosition the position of the nodes + * @param[in] faceToNodes the map from the face to their nodes + * @param[in] elemToFaces the maps from the one-sided face to the corresponding face + * @param[in] elemCenter the center of the element + * @param[in] elemVolume the volume of the element + * @param[in] elemPerm the permeability in the element + * @param[in] lengthTolerance the tolerance used in the trans calculations + * @param[inout] M the output inner product matrix + * + * @details Reference: K-A Lie, An Introduction to Reservoir Simulation Using MATLAB/GNU Octave (2019) + */ template< localIndex NF > GEOS_HOST_DEVICE static void diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp index e8123bbf8c9..e3bd12e52a1 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp @@ -68,6 +68,19 @@ class SimpleInnerProduct : public MimeticInnerProductBase real64 const & lengthTolerance, arraySlice2d< real64 > const & transMatrix ); + /** + * @brief Compute the mimetic inner product matrix M in a given element using the Simple inner product. + * @param[in] nodePosition the position of the nodes + * @param[in] faceToNodes the map from the face to their nodes + * @param[in] elemToFaces the maps from the one-sided face to the corresponding face + * @param[in] elemCenter the center of the element + * @param[in] elemVolume the volume of the element + * @param[in] elemPerm the permeability in the element + * @param[in] lengthTolerance the tolerance used in the trans calculations + * @param[inout] M the output inner product matrix + * + * @details Reference: K-A Lie, An Introduction to Reservoir Simulation Using MATLAB/GNU Octave (2019) + */ template< localIndex NF > GEOS_HOST_DEVICE static void diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp index 2567544c271..e2c97d905d4 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp @@ -61,6 +61,17 @@ class TPFAInnerProduct : public MimeticInnerProductBase real64 const & lengthTolerance, arraySlice2d< real64 > const & transMatrix ); + /** + * @brief Compute the mimetic inner product matrix M in a given element using TPFA. + * @param[in] nodePosition the position of the nodes + * @param[in] faceToNodes the map from the face to their nodes + * @param[in] elemToFaces the maps from the one-sided face to the corresponding face + * @param[in] elemCenter the center of the element + * @param[in] elemVolume the volume of the element + * @param[in] elemPerm the permeability in the element + * @param[in] lengthTolerance the tolerance used in the trans calculations + * @param[inout] M the output inner product matrix + */ template< localIndex NF > GEOS_HOST_DEVICE static void From 41f75ee344fd39ae80968a37b133d8c4a5ed5aac Mon Sep 17 00:00:00 2001 From: "Yoojin S. Cha" Date: Mon, 16 Mar 2026 17:28:50 -0700 Subject: [PATCH 07/64] apply uncrustify formatting --- .../finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp index 22524666ee5..f33449bd780 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp @@ -40,7 +40,7 @@ class BdVLMInnerProduct : public MimeticInnerProductBase /** * @brief In a given element, recompute the transmissibility matrix in a cell using the inner product of Beirao da Veiga, Lipnikov, - *Manzini (page 113) + * Manzini (page 113) * @param[in] nodePosition the position of the nodes * @param[in] transMultiplier the transmissibility multipliers at the mesh faces * @param[in] faceToNodes the map from the face to their nodes From 06cbf15a48e64f18c2b9ef020394937b94fc19e5 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 31 Jul 2026 13:56:57 -0700 Subject: [PATCH 08/64] wip: drafting the GA implementation. --- .../incompressible_consistency_mixed.xml | 67 ++ .../structured_smoke_mixed.xml | 132 ++++ src/coreComponents/CMakeLists.txt | 1 + .../discretizationMethods/CMakeLists.txt | 2 +- .../NumericalMethodsManager.cpp | 4 +- .../NumericalMethodsManager.hpp | 17 + .../mainInterface/ProblemManager.cpp | 15 +- .../mixedMimetic/CMakeLists.txt | 42 ++ .../MixedMimeticDiscretization.cpp | 133 ++++ .../MixedMimeticDiscretization.hpp | 141 ++++ .../MixedMimeticDiscretizationManager.cpp | 67 ++ .../MixedMimeticDiscretizationManager.hpp | 72 ++ .../mixedMimetic/MixedMimeticDispatch.hpp | 69 ++ .../mixedMimetic/MixedMimeticFields.hpp | 82 +++ .../adaptivity/GlobalAdaptationKernels.hpp | 311 ++++++++ .../physicsSolvers/fluidFlow/CMakeLists.txt | 3 + .../fluidFlow/SinglePhaseMixedMFD.cpp | 690 ++++++++++++++++++ .../fluidFlow/SinglePhaseMixedMFD.hpp | 187 +++++ .../SinglePhaseMixedMFDKernels.hpp | 596 +++++++++++++++ 19 files changed, 2628 insertions(+), 3 deletions(-) create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_consistency_mixed.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/structured_smoke_mixed.xml create mode 100644 src/coreComponents/mixedMimetic/CMakeLists.txt create mode 100644 src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp create mode 100644 src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp create mode 100644 src/coreComponents/mixedMimetic/MixedMimeticDiscretizationManager.cpp create mode 100644 src/coreComponents/mixedMimetic/MixedMimeticDiscretizationManager.hpp create mode 100644 src/coreComponents/mixedMimetic/MixedMimeticDispatch.hpp create mode 100644 src/coreComponents/mixedMimetic/MixedMimeticFields.hpp create mode 100644 src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp create mode 100644 src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp create mode 100644 src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp create mode 100644 src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_consistency_mixed.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_consistency_mixed.xml new file mode 100644 index 00000000000..9ce1abab69d --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_consistency_mixed.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/structured_smoke_mixed.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/structured_smoke_mixed.xml new file mode 100644 index 00000000000..d752ee5e185 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/structured_smoke_mixed.xml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreComponents/CMakeLists.txt b/src/coreComponents/CMakeLists.txt index b74e728c754..82112093ebd 100644 --- a/src/coreComponents/CMakeLists.txt +++ b/src/coreComponents/CMakeLists.txt @@ -11,6 +11,7 @@ set( subdirs mesh fieldSpecification finiteVolume + mixedMimetic linearAlgebra discretizationMethods events diff --git a/src/coreComponents/discretizationMethods/CMakeLists.txt b/src/coreComponents/discretizationMethods/CMakeLists.txt index 176924f5c41..0af0af36178 100644 --- a/src/coreComponents/discretizationMethods/CMakeLists.txt +++ b/src/coreComponents/discretizationMethods/CMakeLists.txt @@ -23,7 +23,7 @@ Gives access to the discretization methods and their components for applications set( mainInterface_headers NumericalMethodsManager.hpp ) -set( dependencyList ${parallelDeps} finiteVolume ) +set( dependencyList ${parallelDeps} finiteVolume mixedMimetic ) # # Specify all sources diff --git a/src/coreComponents/discretizationMethods/NumericalMethodsManager.cpp b/src/coreComponents/discretizationMethods/NumericalMethodsManager.cpp index f9dbbb06bc3..431060e4af9 100644 --- a/src/coreComponents/discretizationMethods/NumericalMethodsManager.cpp +++ b/src/coreComponents/discretizationMethods/NumericalMethodsManager.cpp @@ -27,12 +27,14 @@ using namespace dataRepository; NumericalMethodsManager::NumericalMethodsManager( string const & name, Group * const parent ): Group( name, parent ), m_finiteElementDiscretizationManager( groupKeysStruct::finiteElementDiscretizationsString(), this ), - m_finiteVolumeManager( groupKeysStruct::finiteVolumeManagerString(), this ) + m_finiteVolumeManager( groupKeysStruct::finiteVolumeManagerString(), this ), + m_mixedMimeticDiscretizationManager( groupKeysStruct::mixedMimeticManagerString(), this ) { setInputFlags( InputFlags::OPTIONAL ); this->registerGroup( groupKeysStruct::finiteElementDiscretizationsString(), &m_finiteElementDiscretizationManager ); this->registerGroup( groupKeysStruct::finiteVolumeManagerString(), &m_finiteVolumeManager ); + this->registerGroup( groupKeysStruct::mixedMimeticManagerString(), &m_mixedMimeticDiscretizationManager ); } NumericalMethodsManager::~NumericalMethodsManager() diff --git a/src/coreComponents/discretizationMethods/NumericalMethodsManager.hpp b/src/coreComponents/discretizationMethods/NumericalMethodsManager.hpp index 503647f206a..027a46699d5 100644 --- a/src/coreComponents/discretizationMethods/NumericalMethodsManager.hpp +++ b/src/coreComponents/discretizationMethods/NumericalMethodsManager.hpp @@ -23,6 +23,7 @@ #include "dataRepository/Group.hpp" #include "finiteElement/FiniteElementDiscretizationManager.hpp" #include "finiteVolume/FiniteVolumeManager.hpp" +#include "mixedMimetic/MixedMimeticDiscretizationManager.hpp" namespace geos { @@ -69,6 +70,9 @@ class NumericalMethodsManager : public dataRepository::Group /// @return Name for the finite volume manager. static constexpr char const * finiteVolumeManagerString() { return "FiniteVolume"; } + + /// @return Name for the mixed mimetic discretization manager. + static constexpr char const * mixedMimeticManagerString() { return "MixedMimetic"; } }; /** @@ -91,6 +95,16 @@ class NumericalMethodsManager : public dataRepository::Group */ FiniteVolumeManager const & getFiniteVolumeManager() const { return m_finiteVolumeManager; } + /** + * @brief @return Returns reference to MixedMimeticDiscretizationManager m_mixedMimeticDiscretizationManager. + */ + MixedMimeticDiscretizationManager & getMixedMimeticDiscretizationManager() { return m_mixedMimeticDiscretizationManager; } + + /** + * @brief @return Returns reference to const MixedMimeticDiscretizationManager m_mixedMimeticDiscretizationManager. + */ + MixedMimeticDiscretizationManager const & getMixedMimeticDiscretizationManager() const { return m_mixedMimeticDiscretizationManager; } + private: /// Contains the finite element discretizations @@ -99,6 +113,9 @@ class NumericalMethodsManager : public dataRepository::Group /// Contains the finite volume discretizations. FiniteVolumeManager m_finiteVolumeManager; + /// Contains the mixed mimetic discretizations. + MixedMimeticDiscretizationManager m_mixedMimeticDiscretizationManager; + }; } /* namespace geos */ diff --git a/src/coreComponents/mainInterface/ProblemManager.cpp b/src/coreComponents/mainInterface/ProblemManager.cpp index bdf792ea36d..601c59d5fbe 100644 --- a/src/coreComponents/mainInterface/ProblemManager.cpp +++ b/src/coreComponents/mainInterface/ProblemManager.cpp @@ -36,6 +36,8 @@ #include "finiteElement/FiniteElementDiscretizationManager.hpp" #include "finiteVolume/FluxApproximationBase.hpp" #include "finiteVolume/HybridMimeticDiscretization.hpp" +#include "mixedMimetic/MixedMimeticDiscretization.hpp" +#include "mixedMimetic/MixedMimeticDiscretizationManager.hpp" #include "fieldSpecification/FieldSpecificationManager.hpp" #include "fileIO/Outputs/OutputBase.hpp" #include "fileIO/Outputs/OutputManager.hpp" @@ -910,6 +912,9 @@ ProblemManager::getDiscretizations() const FiniteVolumeManager const & fvDiscretizationManager = numericalMethodManager.getFiniteVolumeManager(); + MixedMimeticDiscretizationManager const & + mixedMimeticDiscretizationManager = numericalMethodManager.getMixedMimeticDiscretizationManager(); + DomainPartition const & domain = getDomainPartition(); Group const & meshBodies = domain.getMeshBodies(); @@ -929,6 +934,11 @@ ProblemManager::getDiscretizations() const discretization = fvDiscretizationManager.getGroupPointer( discretizationName ); } + if( discretization==nullptr ) + { + discretization = mixedMimeticDiscretizationManager.getGroupPointer( discretizationName ); + } + if( discretization!=nullptr ) { solver.forDiscretizationOnMeshTargets( meshBodies, @@ -961,7 +971,10 @@ void ProblemManager::generateMeshLevel( MeshLevel & meshLevel, auto const * const fvhDisc = dynamic_cast< HybridMimeticDiscretization const * >(discretization); - if( feDisc==nullptr && fvsDisc==nullptr && fvhDisc==nullptr ) + auto const * const + mmDisc = dynamic_cast< MixedMimeticDiscretization const * >(discretization); + + if( feDisc==nullptr && fvsDisc==nullptr && fvhDisc==nullptr && mmDisc==nullptr ) { GEOS_ERROR( "Group expected to cast to a discretization object." ); } diff --git a/src/coreComponents/mixedMimetic/CMakeLists.txt b/src/coreComponents/mixedMimetic/CMakeLists.txt new file mode 100644 index 00000000000..f2377eed661 --- /dev/null +++ b/src/coreComponents/mixedMimetic/CMakeLists.txt @@ -0,0 +1,42 @@ +#-------------------------------------------------------------------------------------------------- +#[[ +Package: mixedMimetic + +Contains the mixed mimetic finite difference discretization infrastructure: +the discretization object registered under the NumericalMethods block, the +residual-based Global Adaptation kernels, and the dispatch over the mixed-form +inner products. +#]] +#-------------------------------------------------------------------------------------------------- + +# +# Specify all headers +# +set( mixedMimetic_headers + MixedMimeticDiscretization.hpp + MixedMimeticDiscretizationManager.hpp + MixedMimeticDispatch.hpp + MixedMimeticFields.hpp + adaptivity/GlobalAdaptationKernels.hpp ) + +# +# Specify all sources +# +set( mixedMimetic_sources + MixedMimeticDiscretization.cpp + MixedMimeticDiscretizationManager.cpp ) + +set( dependencyList ${parallelDeps} finiteVolume ) + +geos_decorate_link_dependencies( LIST decoratedDependencies DEPENDENCIES ${dependencyList} ) + +blt_add_library( NAME mixedMimetic + SOURCES ${mixedMimetic_sources} + HEADERS ${mixedMimetic_headers} + DEPENDS_ON ${decoratedDependencies} + OBJECT ${GEOS_BUILD_OBJ_LIBS} + SHARED ${GEOS_BUILD_SHARED_LIBS} ) + +target_include_directories( mixedMimetic PUBLIC ${CMAKE_SOURCE_DIR}/coreComponents ) + +install( TARGETS mixedMimetic LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib ) diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp new file mode 100644 index 00000000000..1f733c80c23 --- /dev/null +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp @@ -0,0 +1,133 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file MixedMimeticDiscretization.cpp + */ + +#include "MixedMimeticDiscretization.hpp" + +#include "finiteVolume/MimeticInnerProductDispatch.hpp" +#include "finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp" +#include "finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp" +#include "finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp" +#include "finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp" + +namespace geos +{ + +using namespace dataRepository; +using namespace mimeticInnerProduct; + +MixedMimeticDiscretization::MixedMimeticDiscretization( string const & name, + Group * const parent ) + : Group( name, parent ), + m_adaptationType( AdaptationType::None ), + m_residualTolerance( 1e-3 ), + m_nominalGradient( { 1.0, 1.0, 1.0 } ) +{ + setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + + registerWrapper( viewKeyStruct::innerProductTypeString(), &m_innerProductType ). + setInputFlag( InputFlags::REQUIRED ). + setDescription( "Type of inner product used in the mixed mimetic solver" ); + + registerWrapper( viewKeyStruct::adaptationTypeString(), &m_adaptationType ). + setInputFlag( InputFlags::OPTIONAL ). + setApplyDefaultValue( AdaptationType::None ). + setDescription( "Adaptation paradigm used to select the cell-wise inner product. " + "Valid options:\n* none - the inner product selected by innerProductType is used in every cell\n" + "* global - residual-based Global Adaptation selects TPFA or MFD cell-wise" ); + + registerWrapper( viewKeyStruct::residualToleranceString(), &m_residualTolerance ). + setInputFlag( InputFlags::OPTIONAL ). + setApplyDefaultValue( 1e-3 ). + setDescription( "Tolerance on the Global Adaptation residual indicator used in the cell marking criterion" ); + + registerWrapper( viewKeyStruct::nominalGradientString(), &m_nominalGradient ). + setInputFlag( InputFlags::OPTIONAL ). + setApplyDefaultValue( m_nominalGradient ). + setDescription( "Nominal pressure gradient inducing the projected admissible flow field used by the residual indicators" ); +} + +void MixedMimeticDiscretization::postInputInitialization() +{ + Group::postInputInitialization(); + + GEOS_THROW_IF_LE_MSG( m_residualTolerance, 0.0, + GEOS_FMT( "{}: the residual tolerance must be strictly positive", + getWrapperDataContext( viewKeyStruct::residualToleranceString() ) ), + InputError ); + + GEOS_THROW_IF_LT_MSG( LvArray::tensorOps::l2Norm< 3 >( m_nominalGradient.data ), LvArray::NumericLimits< real64 >::epsilon, + GEOS_FMT( "{}: the nominal gradient cannot be the zero vector", + getWrapperDataContext( viewKeyStruct::nominalGradientString() ) ), + InputError ); +} + +void MixedMimeticDiscretization::initializePostInitialConditionsPreSubGroups() +{ + Group::initializePostInitialConditionsPreSubGroups(); + + std::unique_ptr< MimeticInnerProductBase > newMimeticIP = factory( m_innerProductType ); + + registerWrapper< MimeticInnerProductBase >( viewKeyStruct::innerProductString(), std::move( newMimeticIP ) ). + setRestartFlags( dataRepository::RestartFlags::NO_WRITE ); +} + +MixedMimeticDiscretization::CatalogInterface::CatalogType & +MixedMimeticDiscretization::getCatalog() +{ + static MixedMimeticDiscretization::CatalogInterface::CatalogType catalog; + return catalog; +} + +std::unique_ptr< MimeticInnerProductBase > +MixedMimeticDiscretization::factory( string const & mimeticInnerProductType ) const +{ + std::unique_ptr< MimeticInnerProductBase > rval; + if( mimeticInnerProductType == MimeticInnerProductTypeStrings::TPFA ) + { + rval = std::make_unique< TPFAInnerProduct >(); + } + else if( mimeticInnerProductType == MimeticInnerProductTypeStrings::QuasiTPFA ) + { + rval = std::make_unique< QuasiTPFAInnerProduct >(); + } + else if( mimeticInnerProductType == MimeticInnerProductTypeStrings::Simple ) + { + rval = std::make_unique< SimpleInnerProduct >(); + } + else if( mimeticInnerProductType == MimeticInnerProductTypeStrings::BdVLM ) + { + rval = std::make_unique< BdVLMInnerProduct >(); + } + else + { + GEOS_ERROR( GEOS_FMT( "Key value of {} does not have an associated mimetic inner product implementing the mixed-form " + "mass matrix (valid options: {}, {}, {}, {}).", + mimeticInnerProductType, + MimeticInnerProductTypeStrings::TPFA, + MimeticInnerProductTypeStrings::QuasiTPFA, + MimeticInnerProductTypeStrings::Simple, + MimeticInnerProductTypeStrings::BdVLM ), + getDataContext() ); + } + return rval; +} + +REGISTER_CATALOG_ENTRY( MixedMimeticDiscretization, MixedMimeticDiscretization, string const &, Group * const ) + +} diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp new file mode 100644 index 00000000000..33d886e36d3 --- /dev/null +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp @@ -0,0 +1,141 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file MixedMimeticDiscretization.hpp + */ + +#ifndef GEOS_MIXEDMIMETIC_MIXEDMIMETICDISCRETIZATION_HPP_ +#define GEOS_MIXEDMIMETIC_MIXEDMIMETICDISCRETIZATION_HPP_ + +#include "dataRepository/Group.hpp" +#include "common/format/EnumStrings.hpp" +#include "finiteVolume/mimeticInnerProducts/MimeticInnerProductBase.hpp" + +namespace geos +{ + +/** + * @class MixedMimeticDiscretization + * + * Provides management of the cell-wise inner products and of the residual-based + * Global Adaptation (GA) parameters used by the mixed mimetic finite difference solvers. + */ +class MixedMimeticDiscretization : public dataRepository::Group +{ +public: + + /// Adaptation paradigm: no adaptation (single inner product everywhere) or Global Adaptation + enum class AdaptationType : integer + { + None, ///< the inner product selected by innerProductType is used in every cell + Global ///< face-assembled residual indicators select TPFA or MFD cell-wise + }; + + /// Alias for CatalogInterface, necessary declarations for factory instantiation of derived classes + using CatalogInterface = dataRepository::CatalogInterface< MixedMimeticDiscretization, string const &, Group * const >; + + /** + * @brief Return the data type in the data repository. + * @return the data type in the data repository + */ + static typename CatalogInterface::CatalogType & getCatalog(); + + /** + * @brief Static Factory Catalog Functions. + * @return the catalog name + */ + static string catalogName() { return "MixedMimeticDiscretization"; } + + MixedMimeticDiscretization() = delete; + + /** + * @brief Constructor. + * @param name the name of the MixedMimeticDiscretization in the data repository + * @param parent the parent group of this group. + */ + MixedMimeticDiscretization( string const & name, dataRepository::Group * const parent ); + + /** + * @brief View keys. + */ + struct viewKeyStruct + { + /// @return The key for the type of inner product + static constexpr char const * innerProductTypeString() { return "innerProductType"; } + + /// @return The key for the inner product + static constexpr char const * innerProductString() { return "innerProduct"; } + + /// @return The key for the adaptation type + static constexpr char const * adaptationTypeString() { return "adaptationType"; } + + /// @return The key for the residual tolerance + static constexpr char const * residualToleranceString() { return "residualTolerance"; } + + /// @return The key for the nominal gradient of the projection probe + static constexpr char const * nominalGradientString() { return "nominalGradient"; } + }; + + /** + * @brief @return The adaptation type + */ + AdaptationType getAdaptationType() const { return m_adaptationType; } + + /** + * @brief @return The residual tolerance used in the marking criterion + */ + real64 getResidualTolerance() const { return m_residualTolerance; } + + /** + * @brief @return The nominal gradient used to build the projected admissible flow field + */ + R1Tensor getNominalGradient() const { return m_nominalGradient; } + +protected: + + virtual void postInputInitialization() override; + + virtual void initializePostInitialConditionsPreSubGroups() override; + +private: + + /// type of inner product used in the mixed mimetic solver + string m_innerProductType; + + /// adaptation paradigm (none or global) + AdaptationType m_adaptationType; + + /// user-prescribed tolerance for the marking criterion + real64 m_residualTolerance; + + /// nominal gradient inducing the projected admissible flow field + R1Tensor m_nominalGradient; + + /** + * @brief Factory method to instantiate a type of mimetic inner product. + * @return A unique_ptr< MimeticInnerProductBase > which contains the new instantiation. + */ + std::unique_ptr< mimeticInnerProduct::MimeticInnerProductBase > factory( string const & mimeticInnerProductType ) const; + +}; + +ENUM_STRINGS( MixedMimeticDiscretization::AdaptationType, + "none", + "global" ); + +} + +#endif //GEOS_MIXEDMIMETIC_MIXEDMIMETICDISCRETIZATION_HPP_ diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretizationManager.cpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretizationManager.cpp new file mode 100644 index 00000000000..3e484577fb2 --- /dev/null +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretizationManager.cpp @@ -0,0 +1,67 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file MixedMimeticDiscretizationManager.cpp + */ + +#include "MixedMimeticDiscretizationManager.hpp" + +#include "mixedMimetic/MixedMimeticDiscretization.hpp" + +namespace geos +{ + +using namespace dataRepository; + +MixedMimeticDiscretizationManager::MixedMimeticDiscretizationManager( string const & name, Group * const parent ) + : Group( name, parent ) +{ + setInputFlags( InputFlags::OPTIONAL ); +} + +MixedMimeticDiscretizationManager::~MixedMimeticDiscretizationManager() +{} + +Group * MixedMimeticDiscretizationManager::createChild( string const & childKey, string const & childName ) +{ + GEOS_LOG_RANK_0( GEOS_FMT( "{}: adding {} {}", getName(), childKey, childName ) ); + std::unique_ptr< MixedMimeticDiscretization > disc = + MixedMimeticDiscretization::CatalogInterface::factory( childKey, getDataContext(), childName, this ); + return &this->registerGroup< MixedMimeticDiscretization >( childName, std::move( disc ) ); +} + +void MixedMimeticDiscretizationManager::expandObjectCatalogs() +{ + // During schema generation, register one of each type derived from MixedMimeticDiscretization here + for( auto & catalogIter: MixedMimeticDiscretization::getCatalog() ) + { + createChild( catalogIter.first, catalogIter.first ); + } +} + +MixedMimeticDiscretization const & +MixedMimeticDiscretizationManager::getMixedMimeticDiscretization( string const & name ) const +{ + return getGroup< MixedMimeticDiscretization >( name ); +} + +MixedMimeticDiscretization & +MixedMimeticDiscretizationManager::getMixedMimeticDiscretization( string const & name ) +{ + return getGroup< MixedMimeticDiscretization >( name ); +} + +} // namespace geos diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretizationManager.hpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretizationManager.hpp new file mode 100644 index 00000000000..7555cd9ad06 --- /dev/null +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretizationManager.hpp @@ -0,0 +1,72 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file MixedMimeticDiscretizationManager.hpp + */ + +#ifndef GEOS_MIXEDMIMETIC_MIXEDMIMETICDISCRETIZATIONMANAGER_HPP_ +#define GEOS_MIXEDMIMETIC_MIXEDMIMETICDISCRETIZATIONMANAGER_HPP_ + +#include "dataRepository/Group.hpp" + +namespace geos +{ + +class MixedMimeticDiscretization; + +/** + * @class MixedMimeticDiscretizationManager + * + * Contains the mixed mimetic discretizations, registered under the NumericalMethods block. + */ +class MixedMimeticDiscretizationManager : public dataRepository::Group +{ +public: + + MixedMimeticDiscretizationManager() = delete; + + /** + * @brief Constructor. + * @param name the name of the MixedMimeticDiscretizationManager in the data repository + * @param parent the parent group of this group. + */ + MixedMimeticDiscretizationManager( string const & name, Group * const parent ); + + virtual ~MixedMimeticDiscretizationManager() override; + + virtual Group * createChild( string const & childKey, string const & childName ) override; + + virtual void expandObjectCatalogs() override; + + /** + * @brief Get a mixed mimetic discretization by name, const version. + * @param name the name of the discretization + * @return the discretization object + */ + MixedMimeticDiscretization const & getMixedMimeticDiscretization( string const & name ) const; + + /** + * @brief Get a mixed mimetic discretization by name. + * @param name the name of the discretization + * @return the discretization object + */ + MixedMimeticDiscretization & getMixedMimeticDiscretization( string const & name ); + +}; + +} // namespace geos + +#endif //GEOS_MIXEDMIMETIC_MIXEDMIMETICDISCRETIZATIONMANAGER_HPP_ diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDispatch.hpp b/src/coreComponents/mixedMimetic/MixedMimeticDispatch.hpp new file mode 100644 index 00000000000..64e8e98a21a --- /dev/null +++ b/src/coreComponents/mixedMimetic/MixedMimeticDispatch.hpp @@ -0,0 +1,69 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file MixedMimeticDispatch.hpp + */ + +#ifndef GEOS_MIXEDMIMETIC_MIXEDMIMETICDISPATCH_HPP_ +#define GEOS_MIXEDMIMETIC_MIXEDMIMETICDISPATCH_HPP_ + +#include "finiteVolume/mimeticInnerProducts/MimeticInnerProductBase.hpp" +#include "finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp" +#include "finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp" +#include "finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp" +#include "finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp" +#include "common/logger/Logger.hpp" + +namespace geos +{ + +/** + * @brief Dispatch the mixed-form inner products, i.e. the subset of mimetic inner products + * implementing the mass matrix construction (computeM) used by the mixed mimetic solvers. + * @tparam LAMBDA type of the user-provided generic lambda + * @param[in] input the mimetic inner product base reference to dispatch + * @param[in] lambda the user-provided generic lambda + */ +template< typename LAMBDA > +void +mixedMimeticInnerProductDispatch( mimeticInnerProduct::MimeticInnerProductBase const & input, + LAMBDA && lambda ) +{ + if( auto const * const ptr1 = dynamic_cast< mimeticInnerProduct::TPFAInnerProduct const * >(&input) ) + { + lambda( *ptr1 ); + } + else if( auto const * const ptr2 = dynamic_cast< mimeticInnerProduct::QuasiTPFAInnerProduct const * >(&input) ) + { + lambda( *ptr2 ); + } + else if( auto const * const ptr3 = dynamic_cast< mimeticInnerProduct::SimpleInnerProduct const * >(&input) ) + { + lambda( *ptr3 ); + } + else if( auto const * const ptr4 = dynamic_cast< mimeticInnerProduct::BdVLMInnerProduct const * >(&input) ) + { + lambda( *ptr4 ); + } + else + { + GEOS_ERROR( "mixedMimeticInnerProductDispatch() is not implemented for input of "<, + 0, + LEVEL_0, + WRITE_AND_READ, + "Face mass flux unknown of the mixed mimetic formulation (positive in the direction of the global face normal)" ); + +DECLARE_FIELD( faceMassFlux_n, + "faceMassFlux_n", + array1d< real64 >, + 0, + NOPLOT, + WRITE_AND_READ, + "Face mass flux at the previous converged time step" ); + +DECLARE_FIELD( stencilFlag, + "stencilFlag", + array1d< integer >, + 0, + LEVEL_0, + WRITE_AND_READ, + "Adaptive stencil activation flag (0 = TPFA-compatible, 1 = MFD-compatible)" ); + +DECLARE_FIELD( consistencyIndicator, + "consistencyIndicator", + array1d< real64 >, + 0, + LEVEL_0, + WRITE_AND_READ, + "Residual-based consistency indicator per cell (Global Adaptation)" ); + +DECLARE_FIELD( faceResidual, + "faceResidual", + array1d< real64 >, + 0, + LEVEL_1, + NO_WRITE, + "Global Adaptation face-assembled normalized residual" ); + +} + +} + +} + +#endif // GEOS_MIXEDMIMETIC_MIXEDMIMETICFIELDS_HPP_ diff --git a/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp b/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp new file mode 100644 index 00000000000..8b566a8e5ea --- /dev/null +++ b/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp @@ -0,0 +1,311 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file GlobalAdaptationKernels.hpp + * + * Residual-based Global Adaptation (GA) indicators for the mixed mimetic discretization. + * The construction follows four steps: + * 1) projection of an admissible flow field induced by a nominal uniform gradient, + * using the harmonic face-normal diffusive projection across interfaces; + * 2) evaluation of the localized (cell-wise) TPFA constitutive residual, normalized + * by the local pressure drop profile; + * 3) assembly of the normalized residuals across shared interfaces with respect to + * a fixed global orientation of the face normal (Global Adaptation); + * 4) thresholding to produce the binary stencil activation flag per cell. + */ + +#ifndef GEOS_MIXEDMIMETIC_ADAPTIVITY_GLOBALADAPTATIONKERNELS_HPP_ +#define GEOS_MIXEDMIMETIC_ADAPTIVITY_GLOBALADAPTATIONKERNELS_HPP_ + +#include "common/DataTypes.hpp" +#include "common/GEOS_RAJA_Interface.hpp" +#include "finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp" +#include "mesh/ElementRegionManager.hpp" + +namespace geos +{ + +namespace mixedMimeticKernels +{ + +namespace internal +{ + +template< typename T, typename LAMBDA > +void kernelLaunchSelectorFaceSwitch( T value, LAMBDA && lambda ) +{ + static_assert( std::is_integral< T >::value, "kernelLaunchSelectorFaceSwitch: type should be integral" ); + + switch( value ) + { + case 4: + { lambda( std::integral_constant< T, 4 >() ); return;} + case 5: + { lambda( std::integral_constant< T, 5 >() ); return;} + case 6: + { lambda( std::integral_constant< T, 6 >() ); return;} + case 7: + { lambda( std::integral_constant< T, 7 >() ); return;} + case 8: + { lambda( std::integral_constant< T, 8 >() ); return;} + case 9: + { lambda( std::integral_constant< T, 9 >() ); return;} + case 10: + { lambda( std::integral_constant< T, 10 >() ); return;} + case 11: + { lambda( std::integral_constant< T, 11 >() ); return;} + case 12: + { lambda( std::integral_constant< T, 12 >() ); return;} + case 13: + { lambda( std::integral_constant< T, 13 >() ); return;} + default: GEOS_ERROR( GEOS_FMT( "Unknown numFacesInElem value: {}", value ) ); + } +} + +} // namespace internal + +/******************************** FaceFluxProjectionKernel ********************************/ + +/** + * @class FaceFluxProjectionKernel + * @brief Step 1: on each face, evaluate the discrete normal interface flux induced by a + * globally uniform nominal gradient, using the harmonic face-normal diffusive projection. + */ +struct FaceFluxProjectionKernel +{ + template< typename VIEWTYPE > + using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; + + /** + * @brief Launch the face loop computing the projected face fluxes. + */ + template< typename POLICY > + static void + launch( localIndex const numFaces, + arrayView2d< localIndex const > const & elemRegionList, + arrayView2d< localIndex const > const & elemSubRegionList, + arrayView2d< localIndex const > const & elemList, + SortedArrayView< localIndex const > const & regionFilter, + arrayView2d< real64 const > const & faceCenter, + arrayView2d< real64 const > const & faceNormal, + arrayView1d< real64 const > const & faceArea, + ElementViewConst< arrayView2d< real64 const > > const & elemCenter, + ElementViewConst< arrayView3d< real64 const > > const & elemPerm, + real64 const (&gradient)[3], + arrayView1d< real64 > const & projFaceFlux ) + { + forAll< POLICY >( numFaces, [=] GEOS_HOST_DEVICE ( localIndex const kf ) + { + real64 const areaWeightTol = LvArray::NumericLimits< real64 >::epsilon; + + real64 sumInvHalfTrans = 0.0; // sum of d / kappa + real64 sumDist = 0.0; // sum of d + integer elemCounter = 0; + + for( integer k = 0; k < elemRegionList.size( 1 ); ++k ) + { + localIndex const er = elemRegionList[kf][k]; + localIndex const esr = elemSubRegionList[kf][k]; + localIndex const ei = elemList[kf][k]; + + bool const onBoundary = (er == -1 || esr == -1 || ei == -1); + if( onBoundary || !regionFilter.contains( er ) ) + { + continue; + } + + // normal half-cell distance from the cell centroid to the face centroid + real64 cellToFace[3]{}; + for( integer d = 0; d < 3; ++d ) + { + cellToFace[d] = faceCenter[kf][d] - elemCenter[er][esr][ei][d]; + } + real64 dist = 0.0; + for( integer d = 0; d < 3; ++d ) + { + dist += cellToFace[d] * faceNormal[kf][d]; + } + dist = LvArray::math::abs( dist ); + dist = LvArray::math::max( dist, areaWeightTol ); + + // face-normal directional diffusive component kappa = n . K . n (diagonal K) + real64 kappa = 0.0; + for( integer d = 0; d < 3; ++d ) + { + kappa += elemPerm[er][esr][ei][0][d] * faceNormal[kf][d] * faceNormal[kf][d]; + } + kappa = LvArray::math::max( kappa, areaWeightTol ); + + sumInvHalfTrans += dist / kappa; + sumDist += dist; + elemCounter++; + } + + if( elemCounter == 0 ) + { + projFaceFlux[kf] = 0.0; + return; + } + + // harmonic face-normal diffusive projection (one-sided value on boundary faces) + real64 const kappaFace = sumDist / sumInvHalfTrans; + + real64 gDotN = 0.0; + for( integer d = 0; d < 3; ++d ) + { + gDotN += gradient[d] * faceNormal[kf][d]; + } + + projFaceFlux[kf] = -faceArea[kf] * kappaFace * gDotN; + } ); + } +}; + +/******************************** LocalResidualKernel ********************************/ + +/** + * @class LocalResidualKernel + * @tparam NF number of faces per element + * @brief Steps 2-3: evaluate the localized TPFA constitutive residual on the projected pair, + * normalize by the local pressure drop profile, and assemble across shared interfaces + * with respect to the fixed global orientation of the face normal. + */ +template< integer NF > +struct LocalResidualKernel +{ + template< typename POLICY > + static void + launch( localIndex const numElems, + arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, + ArrayOfArraysView< localIndex const > const & faceToNodes, + arrayView2d< localIndex const > const & elemToFaces, + arrayView2d< real64 const > const & elemCenter, + arrayView1d< real64 const > const & elemVolume, + arrayView3d< real64 const > const & elemPerm, + arrayView2d< real64 const > const & faceCenter, + arrayView2d< real64 const > const & faceNormal, + arrayView1d< real64 const > const & projFaceFlux, + real64 const (&gradient)[3], + real64 const lengthTolerance, + arrayView1d< real64 > const & faceResidual ) + { + forAll< POLICY >( numElems, [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + real64 const perm[ 3 ] = { elemPerm[ei][0][0], elemPerm[ei][0][1], elemPerm[ei][0][2] }; + + stackArray2d< real64, NF *NF > massMatrix( NF, NF ); + + mimeticInnerProduct::TPFAInnerProduct::computeM< NF >( nodePosition, + faceToNodes, + elemToFaces[ei], + elemCenter[ei], + elemVolume[ei], + perm, + lengthTolerance, + massMatrix ); + + real64 localFlux[NF]{}; + real64 pressureDrop[NF]{}; + real64 orientation[NF]{}; + + real64 pressureDropNorm = 0.0; + for( integer i = 0; i < NF; ++i ) + { + localIndex const kf = elemToFaces[ei][i]; + + // sign relating the cell-outward normal to the fixed global face normal + real64 dotProd = 0.0; + for( integer d = 0; d < 3; ++d ) + { + dotProd += ( faceCenter[kf][d] - elemCenter[ei][d] ) * faceNormal[kf][d]; + } + orientation[i] = ( dotProd >= 0.0 ) ? 1.0 : -1.0; + + // localized (outward-oriented) projected flux + localFlux[i] = orientation[i] * projFaceFlux[kf]; + + // pressure drop profile of the linear probe field: p(x_C) - p(x_f) + real64 drop = 0.0; + for( integer d = 0; d < 3; ++d ) + { + drop += gradient[d] * ( elemCenter[ei][d] - faceCenter[kf][d] ); + } + pressureDrop[i] = drop; + pressureDropNorm += drop * drop; + } + pressureDropNorm = LvArray::math::sqrt( pressureDropNorm ); + pressureDropNorm = LvArray::math::max( pressureDropNorm, LvArray::NumericLimits< real64 >::epsilon ); + + // normalized constitutive residual, assembled on the global face orientation + for( integer i = 0; i < NF; ++i ) + { + real64 residual = -pressureDrop[i]; + for( integer j = 0; j < NF; ++j ) + { + residual += massMatrix( i, j ) * localFlux[j]; + } + + localIndex const kf = elemToFaces[ei][i]; + RAJA::atomicAdd( parallelDeviceAtomic{}, &faceResidual[kf], orientation[i] * residual / pressureDropNorm ); + } + } ); + } +}; + +/******************************** MarkingKernel ********************************/ + +/** + * @class MarkingKernel + * @tparam NF number of faces per element + * @brief Step 4: build the cell-wise indicator as the max of the assembled face residuals + * over the faces of the cell, and apply the thresholding criterion. + */ +template< integer NF > +struct MarkingKernel +{ + template< typename POLICY > + static localIndex + launch( localIndex const numElems, + arrayView2d< localIndex const > const & elemToFaces, + arrayView1d< real64 const > const & faceResidual, + real64 const tolerance, + arrayView1d< real64 > const & consistencyIndicator, + arrayView1d< integer > const & stencilFlag ) + { + RAJA::ReduceSum< ReducePolicy< POLICY >, localIndex > numMfdCells( 0 ); + + forAll< POLICY >( numElems, [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + real64 indicator = 0.0; + for( integer i = 0; i < NF; ++i ) + { + indicator = LvArray::math::max( indicator, LvArray::math::abs( faceResidual[elemToFaces[ei][i]] ) ); + } + consistencyIndicator[ei] = indicator; + integer const flag = ( indicator > tolerance ) ? 1 : 0; + stencilFlag[ei] = flag; + numMfdCells += flag; + } ); + + return numMfdCells.get(); + } +}; + +} // namespace mixedMimeticKernels + +} // namespace geos + +#endif //GEOS_MIXEDMIMETIC_ADAPTIVITY_GLOBALADAPTATIONKERNELS_HPP_ diff --git a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt index 6a134ee798e..9d34bf19582 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt +++ b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt @@ -39,6 +39,7 @@ set( fluidFlowSolvers_headers SinglePhaseStatisticsTask.hpp SinglePhaseFVM.hpp SinglePhaseHybridFVM.hpp + SinglePhaseMixedMFD.hpp SinglePhaseProppantBase.hpp StencilAccessors.hpp SolutionCheckHelpers.hpp @@ -59,6 +60,7 @@ set( fluidFlowSolvers_headers kernels/singlePhase/MobilityKernel.hpp kernels/singlePhase/ResidualNormKernel.hpp kernels/singlePhase/SinglePhaseHybridFVMKernels.hpp + kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp kernels/singlePhase/SolidInternalEnergyUpdateKernel.hpp kernels/singlePhase/SolutionCheckKernel.hpp kernels/singlePhase/SolutionScalingKernel.hpp @@ -169,6 +171,7 @@ set( fluidFlowSolvers_sources SinglePhaseStatisticsTask.cpp SinglePhaseFVM.cpp SinglePhaseHybridFVM.cpp + SinglePhaseMixedMFD.cpp SinglePhaseProppantBase.cpp SinglePhaseReactiveTransport.cpp SourceFluxStatistics.cpp diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp new file mode 100644 index 00000000000..aae3f6c4df0 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -0,0 +1,690 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file SinglePhaseMixedMFD.cpp + */ + +#include "SinglePhaseMixedMFD.hpp" + +#include "common/logger/Logger.hpp" +#include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" +#include "constitutive/permeability/PermeabilityFields.hpp" +#include "fieldSpecification/AquiferBoundaryCondition.hpp" +#include "fieldSpecification/FieldSpecificationImpl.hpp" +#include "fieldSpecification/FieldSpecificationManager.hpp" +#include "discretizationMethods/NumericalMethodsManager.hpp" +#include "mesh/DomainPartition.hpp" +#include "mesh/mpiCommunications/CommunicationTools.hpp" +#include "mixedMimetic/MixedMimeticDiscretization.hpp" +#include "mixedMimetic/MixedMimeticDiscretizationManager.hpp" +#include "mixedMimetic/MixedMimeticFields.hpp" +#include "mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp" +#include "physicsSolvers/LogLevelsInfo.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/ResidualNormKernel.hpp" + +namespace geos +{ + +using namespace dataRepository; +using namespace constitutive; +using namespace fields; +using namespace mimeticInnerProduct; + +SinglePhaseMixedMFD::SinglePhaseMixedMFD( const string & name, + Group * const parent ): + SinglePhaseBase( name, parent ), + m_areaRelTol( 1e-8 ) +{ + // one cell-centered dof per cell + m_numDofPerCell = 1; +} + +void SinglePhaseMixedMFD::registerDataOnMesh( Group & meshBodies ) +{ + SinglePhaseBase::registerDataOnMesh( meshBodies ); + + forDiscretizationOnMeshTargets( meshBodies, [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + // 1) Register the cell-centered adaptation data + ElementRegionManager & elemManager = mesh.getElemManager(); + elemManager.forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + subRegion.registerField< mixedMimetic::stencilFlag >( getName() ); + subRegion.registerField< mixedMimetic::consistencyIndicator >( getName() ); + } ); + + // 2) Register the face data + FaceManager & faceManager = mesh.getFaceManager(); + { + // primary variables: face mass fluxes + faceManager.registerField< mixedMimetic::faceMassFlux >( getName() ); + faceManager.registerField< mixedMimetic::faceMassFlux_n >( getName() ); + + // boundary condition data + faceManager.registerField< flow::bcPressure >( getName() ); + faceManager.registerField< flow::isBoundaryFace >( getName() ); + + // Global Adaptation face residual + faceManager.registerField< mixedMimetic::faceResidual >( getName() ); + } + } ); +} + +void SinglePhaseMixedMFD::initializePreSubGroups() +{ + SinglePhaseBase::initializePreSubGroups(); + + GEOS_THROW_IF( m_isThermal, + "The thermal option is not supported by SinglePhaseMixedMFD", + InputError, getDataContext() ); + + DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); + NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); + MixedMimeticDiscretizationManager const & mmManager = numericalMethodManager.getMixedMimeticDiscretizationManager(); + + GEOS_THROW_IF( !mmManager.hasGroup< MixedMimeticDiscretization >( m_discretizationName ), + "A MixedMimeticDiscretization must be selected with SinglePhaseMixedMFD", + InputError, getDataContext() ); +} + +void SinglePhaseMixedMFD::initializePostInitialConditionsPreSubGroups() +{ + GEOS_MARK_FUNCTION; + + SinglePhaseBase::initializePostInitialConditionsPreSubGroups(); + + DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); + + FieldSpecificationManager & fsManager = FieldSpecificationManager::getInstance(); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + GEOS_UNUSED_VAR( regionNames ); + + ElementRegionManager const & elemManager = mesh.getElemManager(); + + // in the kernels, we need to make sure that we act only on the target regions + for( string const & regionName : regionNames ) + { + m_regionFilter.insert( elemManager.getRegions().getIndex( regionName ) ); + } + + // flag the faces on which a pressure boundary condition is imposed: + // all the remaining domain-boundary faces are treated as no-flow faces + FaceManager & faceManager = mesh.getFaceManager(); + arrayView1d< integer > const isPresBcFace = faceManager.getField< flow::isBoundaryFace >(); + + fsManager.apply< FaceManager >( 0.0, + mesh, + flow::bcPressure::key(), + [&] ( FieldSpecificationBase const &, + string const &, + SortedArrayView< localIndex const > const & targetSet, + FaceManager &, + string const & ) + { + forAll< parallelDevicePolicy<> >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const a ) + { + isPresBcFace[targetSet[a]] = 1; + } ); + } ); + + fsManager.forSubGroups< AquiferBoundaryCondition >( [&] ( AquiferBoundaryCondition const & bc ) + { + GEOS_UNUSED_VAR( bc ); + GEOS_WARNING( "The aquifer boundary condition was requested in the XML file. \n" + "This type of boundary condition is not yet supported by SinglePhaseMixedMFD and will be ignored", + getDataContext(), bc.getDataContext() ); + } ); + } ); + + // run the residual-based Global Adaptation pipeline (or activate the selected inner product everywhere) + computeGlobalAdaptationIndicators( domain ); +} + +void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + + NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); + MixedMimeticDiscretizationManager const & mmManager = numericalMethodManager.getMixedMimeticDiscretizationManager(); + MixedMimeticDiscretization const & discretization = mmManager.getMixedMimeticDiscretization( m_discretizationName ); + + if( discretization.getAdaptationType() == MixedMimeticDiscretization::AdaptationType::None ) + { + // no adaptation: activate the selected inner product in every cell + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + subRegion.getField< mixedMimetic::stencilFlag >().template setValues< parallelDevicePolicy<> >( 1 ); + } ); + } ); + return; + } + + real64 const lengthTolerance = domain.getMeshBody( 0 ).getGlobalLengthScale() * m_areaRelTol; + real64 const tolerance = discretization.getResidualTolerance(); + R1Tensor const gradientInput = discretization.getNominalGradient(); + real64 const gradient[3] = { gradientInput[0], gradientInput[1], gradientInput[2] }; + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + NodeManager const & nodeManager = mesh.getNodeManager(); + FaceManager & faceManager = mesh.getFaceManager(); + ElementRegionManager & elemManager = mesh.getElemManager(); + + arrayView1d< real64 > const faceResidual = faceManager.getField< mixedMimetic::faceResidual >(); + faceResidual.zero(); + + // step 1: projection of the admissible flow field induced by the nominal gradient + array1d< real64 > projFaceFluxArray( faceManager.size() ); + arrayView1d< real64 > const projFaceFlux = projFaceFluxArray.toView(); + + ElementRegionManager::ElementViewAccessor< arrayView2d< real64 const > > const elemCenterAccessor = + elemManager.constructViewAccessor< array2d< real64 >, arrayView2d< real64 const > >( ElementSubRegionBase::viewKeyStruct::elementCenterString() ); + + using PermeabilityAccessors = StencilMaterialAccessors< PermeabilityBase, fields::permeability::permeability >; + PermeabilityAccessors const permAccessors( elemManager, getName() ); + + mixedMimeticKernels::FaceFluxProjectionKernel:: + launch< parallelDevicePolicy<> >( faceManager.size(), + faceManager.elementRegionList(), + faceManager.elementSubRegionList(), + faceManager.elementList(), + m_regionFilter.toViewConst(), + faceManager.faceCenter(), + faceManager.faceNormal(), + faceManager.faceArea(), + elemCenterAccessor.toNestedViewConst(), + permAccessors.get( fields::permeability::permeability {} ), + gradient, + projFaceFlux ); + + // steps 2-3: localized normalized residuals, assembled on the global face orientation + elemManager.forElementSubRegions< CellElementSubRegion >( regionNames, + [&]( localIndex const, + CellElementSubRegion const & subRegion ) + { + string const & permName = subRegion.getReference< string >( viewKeyStruct::permeabilityNamesString() ); + PermeabilityBase const & permeability = getConstitutiveModel< PermeabilityBase >( subRegion, permName ); + + mixedMimeticKernels::internal::kernelLaunchSelectorFaceSwitch( subRegion.numFacesPerElement(), [&] ( auto NUM_FACES ) + { + mixedMimeticKernels::LocalResidualKernel< NUM_FACES >:: + template launch< parallelDevicePolicy<> >( subRegion.size(), + nodeManager.referencePosition(), + faceManager.nodeList().toViewConst(), + subRegion.faceList().toViewConst(), + subRegion.getElementCenter(), + subRegion.getElementVolume(), + permeability.permeability(), + faceManager.faceCenter(), + faceManager.faceNormal(), + projFaceFlux.toViewConst(), + gradient, + lengthTolerance, + faceResidual ); + } ); + } ); + + // step 4: thresholding + localIndex numMfdCells = 0; + localIndex numCells = 0; + elemManager.forElementSubRegions< CellElementSubRegion >( regionNames, + [&]( localIndex const, + CellElementSubRegion & subRegion ) + { + arrayView1d< real64 > const consistencyIndicator = subRegion.getField< mixedMimetic::consistencyIndicator >(); + arrayView1d< integer > const stencilFlag = subRegion.getField< mixedMimetic::stencilFlag >(); + + mixedMimeticKernels::internal::kernelLaunchSelectorFaceSwitch( subRegion.numFacesPerElement(), [&] ( auto NUM_FACES ) + { + numMfdCells += mixedMimeticKernels::MarkingKernel< NUM_FACES >:: + template launch< parallelDevicePolicy<> >( subRegion.size(), + subRegion.faceList().toViewConst(), + faceResidual.toViewConst(), + tolerance, + consistencyIndicator, + stencilFlag ); + } ); + numCells += subRegion.size(); + } ); + + // make the marking consistent on ghost cells + FieldIdentifiers fieldsToBeSync; + fieldsToBeSync.addElementFields( { mixedMimetic::stencilFlag::key(), mixedMimetic::consistencyIndicator::key() }, regionNames ); + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), false ); + + GEOS_LOG_RANK_0( GEOS_FMT( "{}: Global Adaptation marked {} / {} cells as MFD-compatible (tolerance = {})", + getName(), numMfdCells, numCells, tolerance ) ); + } ); +} + +void SinglePhaseMixedMFD::implicitStepSetup( real64 const & time_n, + real64 const & dt, + DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + + // setup the cell-centered fields + SinglePhaseBase::implicitStepSetup( time_n, dt, domain ); + + // setup the face fields + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & ) + { + FaceManager & faceManager = mesh.getFaceManager(); + + arrayView1d< real64 const > const & faceFlux = + faceManager.getField< mixedMimetic::faceMassFlux >(); + arrayView1d< real64 > const & faceFlux_n = + faceManager.getField< mixedMimetic::faceMassFlux_n >(); + faceFlux_n.setValues< parallelDevicePolicy<> >( faceFlux ); + } ); + + // evaluate the boundary face pressure values used in the constitutive rows + applyFacePressureBCValues( time_n + dt, domain ); +} + +void SinglePhaseMixedMFD::implicitStepComplete( real64 const & time, + real64 const & dt, + DomainPartition & domain ) +{ + SinglePhaseBase::implicitStepComplete( time, dt, domain ); +} + +namespace +{ +char const faceBcLogMessage[] = + "SinglePhaseMixedMFD {}: at time {}s, " + "the <{}> boundary condition '{}' is applied to the face set '{}' in '{}'. " + "\nThe total number of target faces (including ghost faces) is {}. " + "\nNote that if this number is equal to zero, the boundary condition will not be applied on this face set."; +} + +void SinglePhaseMixedMFD::applyFacePressureBCValues( real64 const time, + DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + + FieldSpecificationManager & fsManager = FieldSpecificationManager::getInstance(); + + this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & ) + { + fsManager.apply< FaceManager >( time, + mesh, + flow::bcPressure::key(), + [&] ( FieldSpecificationBase const & fs, + string const & setName, + SortedArrayView< localIndex const > const & targetSet, + FaceManager & targetGroup, + string const & ) + { + // provide some logging at the first nonlinear iteration + if( m_nonlinearSolverParameters.m_numNewtonIterations == 0 ) + { + globalIndex const numTargetFaces = MpiWrapper::sum< globalIndex >( targetSet.size() ); + GEOS_LOG_LEVEL_RANK_0_ON_GROUP( logInfo::BoundaryConditions, + GEOS_FMT( faceBcLogMessage, + this->getName(), time, fs.getCatalogName(), fs.getName(), + setName, targetGroup.getName(), numTargetFaces ), + fs ); + } + + // populate the boundary face pressure values: they enter the residual directly during assembly + FieldSpecificationImpl::applyFieldValue< FieldSpecificationEqual, + parallelDevicePolicy<> >( fs, + targetSet, + time, + targetGroup, + flow::bcPressure::key() ); + } ); + } ); +} + +void SinglePhaseMixedMFD::setupDofs( DomainPartition const & GEOS_UNUSED_PARAM( domain ), + DofManager & dofManager ) const +{ + // face mass-flux unknowns: the dense NF x NF local mass matrices couple + // the fluxes of the faces of each cell + dofManager.addField( mixedMimetic::faceMassFlux::key(), + FieldLocation::Face, + 1, + getMeshTargets() ); + + dofManager.addCoupling( mixedMimetic::faceMassFlux::key(), + mixedMimetic::faceMassFlux::key(), + DofManager::Connector::Elem ); + + // cell pressure unknowns: the mass conservation equation couples the cell + // to its own faces only (no cell-to-cell coupling in the mixed form) + dofManager.addField( viewKeyStruct::elemDofFieldString(), + FieldLocation::Elem, + 1, + getMeshTargets() ); + + dofManager.addCoupling( viewKeyStruct::elemDofFieldString(), + viewKeyStruct::elemDofFieldString(), + DofManager::Connector::None ); + + // coupling between the face fluxes and the cell pressures + dofManager.addCoupling( mixedMimetic::faceMassFlux::key(), + viewKeyStruct::elemDofFieldString(), + DofManager::Connector::Elem ); +} + +void SinglePhaseMixedMFD::assembleFluxTerms( real64 const dt, + DomainPartition const & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + + NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); + MixedMimeticDiscretizationManager const & mmManager = numericalMethodManager.getMixedMimeticDiscretizationManager(); + MixedMimeticDiscretization const & discretization = mmManager.getMixedMimeticDiscretization( m_discretizationName ); + MimeticInnerProductBase const & mimeticInnerProductBase = + discretization.getReference< MimeticInnerProductBase >( MixedMimeticDiscretization::viewKeyStruct::innerProductString() ); + + string const faceDofKey = dofManager.getKey( mixedMimetic::faceMassFlux::key() ); + string const elemDofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); + + // tolerance for the mass matrix computations + real64 const lengthTolerance = domain.getMeshBody( 0 ).getGlobalLengthScale() * m_areaRelTol; + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel const & mesh, + string_array const & regionNames ) + { + NodeManager const & nodeManager = mesh.getNodeManager(); + FaceManager const & faceManager = mesh.getFaceManager(); + + mesh.getElemManager().forElementSubRegions< CellElementSubRegion >( regionNames, + [&]( localIndex const, + CellElementSubRegion const & subRegion ) + { + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase const & fluid = getConstitutiveModel< SingleFluidBase >( subRegion, fluidName ); + + string const & permName = subRegion.getReference< string >( viewKeyStruct::permeabilityNamesString() ); + PermeabilityBase const & permeability = getConstitutiveModel< PermeabilityBase >( subRegion, permName ); + + singlePhaseMixedMFDKernels:: + ElementBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(), + lengthTolerance, + elemDofKey, + faceDofKey, + nodeManager, + faceManager, + subRegion, + mimeticInnerProductBase, + fluid, + permeability, + dt, + localMatrix, + localRhs ); + } ); + } ); +} + +void SinglePhaseMixedMFD::assembleStabilizedFluxTerms( real64 const dt, + DomainPartition const & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + // pressure stabilization not implemented + GEOS_UNUSED_VAR( dt, domain, dofManager, localMatrix, localRhs ); + GEOS_ERROR( "Stabilized flux not available for this flow solver" ); +} + +void SinglePhaseMixedMFD::assembleEDFMFluxTerms( real64 const GEOS_UNUSED_PARAM( time_n ), + real64 const dt, + DomainPartition const & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs, + string const & jumpDofKey ) +{ + GEOS_UNUSED_VAR( jumpDofKey ); + + assembleFluxTerms( dt, + domain, + dofManager, + localMatrix, + localRhs ); +} + +void SinglePhaseMixedMFD::applyBoundaryConditions( real64 const time_n, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + + // the face pressure boundary values are applied inside the constitutive rows during assembly; + // only the cell-centered boundary conditions (Dirichlet cells, source fluxes) remain to be applied here + SinglePhaseBase::applyBoundaryConditions( time_n, dt, domain, dofManager, localMatrix, localRhs ); +} + +void SinglePhaseMixedMFD::applyAquiferBC( real64 const time, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const +{ + GEOS_MARK_FUNCTION; + GEOS_UNUSED_VAR( time, dt, dofManager, domain, localMatrix, localRhs ); +} + +void SinglePhaseMixedMFD::saveAquiferConvergedState( real64 const & time, + real64 const & dt, + DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + GEOS_UNUSED_VAR( time, dt, domain ); +} + +real64 SinglePhaseMixedMFD::calculateResidualNorm( real64 const & GEOS_UNUSED_PARAM( time_n ), + real64 const & GEOS_UNUSED_PARAM( dt ), + DomainPartition const & domain, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + + real64 localResidualNorm = 0.0; + real64 localResidualNormalizer = 0.0; + + physicsSolverBaseKernels::NormType const normType = getNonlinearSolverParameters().normType(); + + globalIndex const rankOffset = dofManager.rankOffset(); + string const elemDofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); + string const faceDofKey = dofManager.getKey( mixedMimetic::faceMassFlux::key() ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel const & mesh, + string_array const & regionNames ) + { + ElementRegionManager const & elemManager = mesh.getElemManager(); + FaceManager const & faceManager = mesh.getFaceManager(); + + // step 1: compute the residual for the element-based mass conservation equations + + elemManager.forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase const & subRegion ) + { + real64 subRegionResidualNorm[1]{}; + real64 subRegionResidualNormalizer[1]{}; + + singlePhaseBaseKernels:: + ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( normType, + rankOffset, + elemDofKey, + localRhs, + subRegion, + m_nonlinearSolverParameters.m_minNormalizer, + subRegionResidualNorm, + subRegionResidualNormalizer ); + + if( normType == physicsSolverBaseKernels::NormType::Linf ) + { + if( subRegionResidualNorm[0] > localResidualNorm ) + { + localResidualNorm = subRegionResidualNorm[0]; + } + } + else + { + localResidualNorm += subRegionResidualNorm[0]; + localResidualNormalizer += subRegionResidualNormalizer[0]; + } + } ); + + // step 2: compute the residual for the face-based constitutive rows + + real64 faceResidualNorm[1]{}; + real64 faceResidualNormalizer[1]{}; + + singlePhaseMixedMFDKernels:: + ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( normType, + rankOffset, + faceDofKey, + localRhs, + m_regionFilter.toViewConst(), + getName(), + elemManager, + faceManager, + m_nonlinearSolverParameters.m_minNormalizer, + faceResidualNorm, + faceResidualNormalizer ); + + if( normType == physicsSolverBaseKernels::NormType::Linf ) + { + if( faceResidualNorm[0] > localResidualNorm ) + { + localResidualNorm = faceResidualNorm[0]; + } + } + else + { + localResidualNorm += faceResidualNorm[0]; + localResidualNormalizer += faceResidualNormalizer[0]; + } + } ); + + // step 3: second reduction across MPI ranks + + real64 residualNorm = 0.0; + if( normType == physicsSolverBaseKernels::NormType::Linf ) + { + physicsSolverBaseKernels::LinfResidualNormHelper::computeGlobalNorm( localResidualNorm, residualNorm ); + } + else + { + physicsSolverBaseKernels::L2ResidualNormHelper::computeGlobalNorm( localResidualNorm, localResidualNormalizer, residualNorm ); + } + + GEOS_LOG_LEVEL_RANK_0_NLR( logInfo::ResidualNorm, + GEOS_FMT( " ( R{} ) = ( {:4.2e} )", coupledSolverAttributePrefix(), residualNorm )); + getConvergenceStats().setResidualValue( GEOS_FMT( "R{}", coupledSolverAttributePrefix()), residualNorm ); + + return residualNorm; +} + +void SinglePhaseMixedMFD::applySystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain ) +{ + GEOS_UNUSED_VAR( dt ); + + // 1. apply the cell-centered update + + dofManager.addVectorToField( localSolution, + viewKeyStruct::elemDofFieldString(), + flow::pressure::key(), + scalingFactor ); + + // 2. apply the face-based update + + dofManager.addVectorToField( localSolution, + mixedMimetic::faceMassFlux::key(), + mixedMimetic::faceMassFlux::key(), + scalingFactor ); + + // 3. synchronize + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + FieldIdentifiers fieldsToBeSync; + + fieldsToBeSync.addElementFields( { flow::pressure::key() }, regionNames ); + fieldsToBeSync.addFields( FieldLocation::Face, { mixedMimetic::faceMassFlux::key() } ); + + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), true ); + } ); +} + +void SinglePhaseMixedMFD::resetStateToBeginningOfStep( DomainPartition & domain ) +{ + // Reset the cell-centered fields + SinglePhaseBase::resetStateToBeginningOfStep( domain ); + + // Reset the face-based fields + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & ) + { + FaceManager & faceManager = mesh.getFaceManager(); + + arrayView1d< real64 > const & faceFlux = + faceManager.getField< mixedMimetic::faceMassFlux >(); + arrayView1d< real64 const > const & faceFlux_n = + faceManager.getField< mixedMimetic::faceMassFlux_n >(); + faceFlux.setValues< parallelDevicePolicy<> >( faceFlux_n ); + } ); +} + +REGISTER_CATALOG_ENTRY( PhysicsSolverBase, SinglePhaseMixedMFD, string const &, Group * const ) +} /* namespace geos */ diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp new file mode 100644 index 00000000000..93a84212311 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp @@ -0,0 +1,187 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file SinglePhaseMixedMFD.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEMIXEDMFD_HPP_ +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEMIXEDMFD_HPP_ + +#include "physicsSolvers/fluidFlow/SinglePhaseBase.hpp" + +namespace geos +{ + +/** + * @class SinglePhaseMixedMFD + * + * Single-phase flow solver based on the mixed mimetic finite difference formulation: + * the primary unknowns are the cell pressures and the face mass fluxes, coupled in a + * saddle-point system. The cell-wise inner product is selected adaptively (TPFA/MFD) + * through residual-based Global Adaptation indicators. + */ +class SinglePhaseMixedMFD : public SinglePhaseBase +{ +public: + + /** + * @brief main constructor for Group Objects + * @param name the name of this instantiation of Group in the repository + * @param parent the parent group of this instantiation of Group + */ + SinglePhaseMixedMFD( const string & name, + Group * const parent ); + + /// deleted default constructor + SinglePhaseMixedMFD() = delete; + + /// deleted copy constructor + SinglePhaseMixedMFD( SinglePhaseMixedMFD const & ) = delete; + + /// default move constructor + SinglePhaseMixedMFD( SinglePhaseMixedMFD && ) = default; + + /// deleted assignment operator + SinglePhaseMixedMFD & operator=( SinglePhaseMixedMFD const & ) = delete; + + /// deleted move operator + SinglePhaseMixedMFD & operator=( SinglePhaseMixedMFD && ) = delete; + + /** + * @brief default destructor + */ + virtual ~SinglePhaseMixedMFD() override = default; + + /** + * @brief name of the solver in the object catalog + * @return string that contains the catalog name to generate a new object through the object catalog. + */ + static string catalogName() + { return "SinglePhaseMixedMFD"; } + + /** + * @copydoc PhysicsSolverBase::getCatalogName() + */ + string getCatalogName() const override { return catalogName(); } + + virtual void registerDataOnMesh( Group & meshBodies ) override; + + virtual void + setupDofs( DomainPartition const & domain, + DofManager & dofManager ) const override; + + virtual void + applyBoundaryConditions( real64 const time_n, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + + virtual real64 + calculateResidualNorm( real64 const & time_n, + real64 const & dt, + DomainPartition const & domain, + DofManager const & dofManager, + arrayView1d< real64 const > const & localRhs ) override; + + virtual void + applySystemSolution( DofManager const & dofManager, + arrayView1d< real64 const > const & localSolution, + real64 const scalingFactor, + real64 const dt, + DomainPartition & domain ) override; + + virtual void + resetStateToBeginningOfStep( DomainPartition & domain ) override; + + virtual void + implicitStepSetup( real64 const & time_n, + real64 const & dt, + DomainPartition & domain ) override; + + virtual void + implicitStepComplete( real64 const & time, + real64 const & dt, + DomainPartition & domain ) override; + + virtual void + assembleFluxTerms( real64 const dt, + DomainPartition const & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + + virtual void + assembleStabilizedFluxTerms( real64 const dt, + DomainPartition const & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + + virtual void + assembleEDFMFluxTerms( real64 const time_n, + real64 const dt, + DomainPartition const & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs, + string const & jumpDofKey ) override final; + + virtual void + applyAquiferBC( real64 const time, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const override final; + + virtual void + saveAquiferConvergedState( real64 const & time, + real64 const & dt, + DomainPartition & domain ) override; + + virtual void initializePreSubGroups() override; + + virtual void initializePostInitialConditionsPreSubGroups() override; + +private: + + /** + * @brief Populate the boundary face pressure values from the field specifications. + * @param[in] time the time at which the boundary values are evaluated + * @param[in] domain the domain + */ + void applyFacePressureBCValues( real64 const time, + DomainPartition & domain ); + + /** + * @brief Run the residual-based Global Adaptation pipeline and mark the cells. + * @param[in] domain the domain + */ + void computeGlobalAdaptationIndicators( DomainPartition & domain ); + + /// relative tolerance used in the mass matrix computations + real64 m_areaRelTol; + + /// region filter used in flux assembly + SortedArray< localIndex > m_regionFilter; + +}; + +} /* namespace geos */ + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASEMIXEDMFD_HPP_ diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp new file mode 100644 index 00000000000..5d94b0b7098 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp @@ -0,0 +1,596 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file SinglePhaseMixedMFDKernels.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_MIXEDMFDKERNELS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_MIXEDMFDKERNELS_HPP + +#include "common/DataTypes.hpp" +#include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" +#include "constitutive/fluid/singlefluid/SingleFluidFields.hpp" +#include "constitutive/permeability/PermeabilityBase.hpp" +#include "linearAlgebra/interfaces/InterfaceTypes.hpp" +#include "mesh/MeshLevel.hpp" +#include "mixedMimetic/MixedMimeticDispatch.hpp" +#include "mixedMimetic/MixedMimeticFields.hpp" +#include "mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp" +#include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseBaseFields.hpp" +#include "physicsSolvers/fluidFlow/StencilAccessors.hpp" +#include "physicsSolvers/PhysicsSolverBaseKernels.hpp" + +namespace geos +{ +namespace singlePhaseMixedMFDKernels +{ + +/******************************** ElementBasedAssemblyKernel ********************************/ + +/** + * @class ElementBasedAssemblyKernel + * @tparam NUM_FACE number of faces per element + * @tparam IP the type of MFD inner product used in the MFD-compatible cells + * @brief Assemble the cell-wise contributions of the mixed mimetic saddle-point system: + * the face-based constitutive (Darcy closure) rows and the cell-based divergence terms. + * + * On each cell, the local mass matrix is selected according to the adaptive stencil flag: + * TPFA-compatible cells use the diagonal TPFA mass matrix, MFD-compatible cells use IP. + * The face rows are assembled with respect to the fixed global orientation of the face normal, + * so that the (unknown) interface pressure cancels across interior interfaces. + */ +template< integer NUM_FACE, typename IP > +class ElementBasedAssemblyKernel +{ +public: + + using DerivOffset = constitutive::singlefluid::DerivativeOffsetC< 0 >; + + ElementBasedAssemblyKernel( globalIndex const rankOffset, + real64 const & lengthTolerance, + string const elemDofKey, + string const faceDofKey, + NodeManager const & nodeManager, + FaceManager const & faceManager, + CellElementSubRegion const & subRegion, + constitutive::SingleFluidBase const & fluid, + constitutive::PermeabilityBase const & permeability, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + : + m_rankOffset( rankOffset ), + m_lengthTolerance( lengthTolerance ), + m_dt( dt ), + m_elemGhostRank( subRegion.ghostRank() ), + m_elemDofNumber( subRegion.getReference< array1d< globalIndex > >( elemDofKey ) ), + m_faceGhostRank( faceManager.ghostRank() ), + m_faceDofNumber( faceManager.getReference< array1d< globalIndex > >( faceDofKey ) ), + m_elemToFaces( subRegion.faceList().toViewConst() ), + m_elemCenter( subRegion.getElementCenter() ), + m_elemVolume( subRegion.getElementVolume() ), + m_elemGravCoef( subRegion.getField< fields::flow::gravityCoefficient >() ), + m_faceToNodes( faceManager.nodeList().toViewConst() ), + m_faceGravCoef( faceManager.getField< fields::flow::gravityCoefficient >() ), + m_faceCenter( faceManager.faceCenter() ), + m_faceNormal( faceManager.faceNormal() ), + m_elemRegionList( faceManager.elementRegionList() ), + m_nodePosition( nodeManager.referencePosition() ), + m_elemPerm( permeability.permeability() ), + m_elemPres( subRegion.getField< fields::flow::pressure >() ), + m_faceFlux( faceManager.getField< fields::mixedMimetic::faceMassFlux >() ), + m_bcPres( faceManager.getField< fields::flow::bcPressure >() ), + m_isPresBcFace( faceManager.getField< fields::flow::isBoundaryFace >() ), + m_stencilFlag( subRegion.getField< fields::mixedMimetic::stencilFlag >() ), + m_elemDens( fluid.density() ), + m_dElemDens( fluid.dDensity() ), + m_mob( subRegion.getField< fields::flow::mobility >() ), + m_dMob( subRegion.getField< fields::flow::dMobility >() ), + m_localMatrix( localMatrix ), + m_localRhs( localRhs ) + {} + + /** + * @struct StackVariables + * @brief Kernel variables (dof numbers, jacobian and residual) located on the stack + */ + struct StackVariables + { + GEOS_HOST_DEVICE + StackVariables() + : massMatrix( NUM_FACE, NUM_FACE ), + mfdMassMatrix( NUM_FACE, NUM_FACE ), + tpfaMassMatrix( NUM_FACE, NUM_FACE ) + {} + + /// blended local mass matrix: chi * M_mfd + (1 - chi) * M_tpfa + stackArray2d< real64, NUM_FACE *NUM_FACE > massMatrix; + + /// scratch for the two candidate mass matrices + stackArray2d< real64, NUM_FACE *NUM_FACE > mfdMassMatrix; + stackArray2d< real64, NUM_FACE *NUM_FACE > tpfaMassMatrix; + + /// sign relating the cell-outward normal to the fixed global face normal + real64 orientation[NUM_FACE]{}; + + /// 1 if the face is a domain-boundary face without a pressure boundary condition (no-flow) + integer isNoFlowFace[NUM_FACE]{}; + + /// face constitutive residuals (local, outward-oriented) and their derivatives + real64 faceResidual[NUM_FACE]{}; + real64 dFaceResidual_dFlux[NUM_FACE][NUM_FACE]{}; + real64 dFaceResidual_dPres[NUM_FACE]{}; + + /// divergence of the face fluxes and its derivatives + real64 divFlux = 0.0; + real64 dDivFlux_dFlux[NUM_FACE]{}; + + localIndex cellCenteredEqnRowIndex = 0; + localIndex faceCenteredEqnRowIndex[NUM_FACE]{}; + globalIndex elemDofColIndex = 0; + globalIndex faceDofColIndices[NUM_FACE]{}; + }; + + /** + * @brief Performs the setup phase for the kernel. + */ + GEOS_HOST_DEVICE + void setup( localIndex const ei, + StackVariables & stack ) const + { + stack.cellCenteredEqnRowIndex = m_elemDofNumber[ei] - m_rankOffset; + stack.elemDofColIndex = m_elemDofNumber[ei]; + for( integer iFaceLoc = 0; iFaceLoc < NUM_FACE; ++iFaceLoc ) + { + localIndex const kf = m_elemToFaces[ei][iFaceLoc]; + stack.faceCenteredEqnRowIndex[iFaceLoc] = m_faceDofNumber[kf] - m_rankOffset; + stack.faceDofColIndices[iFaceLoc] = m_faceDofNumber[kf]; + } + } + + /** + * @brief Compute the local constitutive rows and the flux divergence in a given element. + */ + GEOS_HOST_DEVICE + void compute( localIndex const ei, + StackVariables & stack ) const + { + real64 const perm[ 3 ] = { m_elemPerm[ei][0][0], m_elemPerm[ei][0][1], m_elemPerm[ei][0][2] }; + + // step 1: branch-free adaptive operator blend, M = chi * M_mfd + (1 - chi) * M_tpfa + IP::template computeM< NUM_FACE >( m_nodePosition, + m_faceToNodes, + m_elemToFaces[ei], + m_elemCenter[ei], + m_elemVolume[ei], + perm, + m_lengthTolerance, + stack.mfdMassMatrix ); + + mimeticInnerProduct::TPFAInnerProduct::computeM< NUM_FACE >( m_nodePosition, + m_faceToNodes, + m_elemToFaces[ei], + m_elemCenter[ei], + m_elemVolume[ei], + perm, + m_lengthTolerance, + stack.tpfaMassMatrix ); + + real64 const chi = static_cast< real64 >( m_stencilFlag[ei] ); + for( integer i = 0; i < NUM_FACE; ++i ) + { + for( integer j = 0; j < NUM_FACE; ++j ) + { + stack.massMatrix( i, j ) = chi * stack.mfdMassMatrix( i, j ) + ( 1.0 - chi ) * stack.tpfaMassMatrix( i, j ); + } + } + + // step 2: cell mobility scaling of the inverse Darcy coefficient + real64 const mob = m_mob[ei]; + real64 const dMob_dPres = m_dMob[ei][DerivOffset::dP]; + real64 const invMob = 1.0 / mob; + real64 const dInvMob_dPres = -dMob_dPres * invMob * invMob; + + real64 const ccDens = m_elemDens[ei][0]; + real64 const dCcDens_dPres = m_dElemDens[ei][0][DerivOffset::dP]; + + // step 3: face orientations, localized fluxes and no-flow flags + real64 localFlux[NUM_FACE]{}; + for( integer i = 0; i < NUM_FACE; ++i ) + { + localIndex const kf = m_elemToFaces[ei][i]; + + real64 dotProd = 0.0; + for( integer d = 0; d < 3; ++d ) + { + dotProd += ( m_faceCenter[kf][d] - m_elemCenter[ei][d] ) * m_faceNormal[kf][d]; + } + stack.orientation[i] = ( dotProd >= 0.0 ) ? 1.0 : -1.0; + localFlux[i] = stack.orientation[i] * m_faceFlux[kf]; + + bool const onBoundary = ( m_elemRegionList[kf][0] == -1 || m_elemRegionList[kf][1] == -1 ); + stack.isNoFlowFace[i] = ( onBoundary && m_isPresBcFace[kf] == 0 ) ? 1 : 0; + } + + // step 4: constitutive (Darcy closure) rows: + // invMob * sum_j M_ij (sigma_j m_j) - p_K + pi_f + rho * ( g_K - g_f ) = 0 + // the interface pressure pi_f is dropped on interior faces (it cancels upon global + // orientation-signed assembly) and replaced by the prescribed value on boundary faces + for( integer i = 0; i < NUM_FACE; ++i ) + { + if( stack.isNoFlowFace[i] == 1 ) + { + continue; + } + + localIndex const kf = m_elemToFaces[ei][i]; + + real64 mDotFlux = 0.0; + for( integer j = 0; j < NUM_FACE; ++j ) + { + mDotFlux += stack.massMatrix( i, j ) * localFlux[j]; + stack.dFaceResidual_dFlux[i][j] = invMob * stack.massMatrix( i, j ) * stack.orientation[j]; + } + + real64 const gravCoefDif = m_elemGravCoef[ei] - m_faceGravCoef[kf]; + + stack.faceResidual[i] = invMob * mDotFlux - m_elemPres[ei] + ccDens * gravCoefDif; + stack.dFaceResidual_dPres[i] = dInvMob_dPres * mDotFlux - 1.0 + dCcDens_dPres * gravCoefDif; + + bool const onBoundary = ( m_elemRegionList[kf][0] == -1 || m_elemRegionList[kf][1] == -1 ); + if( onBoundary && m_isPresBcFace[kf] == 1 ) + { + stack.faceResidual[i] += m_bcPres[kf]; + } + } + + // step 5: divergence of the face fluxes for the cell-centered mass conservation equation + for( integer i = 0; i < NUM_FACE; ++i ) + { + stack.divFlux += m_dt * localFlux[i]; + stack.dDivFlux_dFlux[i] = m_dt * stack.orientation[i]; + } + } + + /** + * @brief Assemble the local contributions into the global system. + */ + GEOS_HOST_DEVICE + void complete( localIndex const ei, + StackVariables & stack ) const + { + // step 1: cell-centered mass conservation contribution (single thread per row) + if( m_elemGhostRank[ei] < 0 ) + { + m_localRhs[stack.cellCenteredEqnRowIndex] += stack.divFlux; + + m_localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( stack.cellCenteredEqnRowIndex, + &stack.faceDofColIndices[0], + &stack.dDivFlux_dFlux[0], + NUM_FACE ); + } + + // step 2: face-centered constitutive rows, signed by the global face orientation + for( integer i = 0; i < NUM_FACE; ++i ) + { + localIndex const kf = m_elemToFaces[ei][i]; + if( m_faceGhostRank[kf] >= 0 ) + { + continue; + } + + if( stack.isNoFlowFace[i] == 1 ) + { + // essential no-flow condition: m_f = 0 (row owned by the unique adjacent cell) + real64 const one = 1.0; + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[stack.faceCenteredEqnRowIndex[i]], m_faceFlux[kf] ); + m_localMatrix.addToRow< parallelDeviceAtomic >( stack.faceCenteredEqnRowIndex[i], + &stack.faceDofColIndices[i], + &one, + 1 ); + continue; + } + + real64 const sigma = stack.orientation[i]; + + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[stack.faceCenteredEqnRowIndex[i]], sigma * stack.faceResidual[i] ); + + real64 const dRes_dPres = sigma * stack.dFaceResidual_dPres[i]; + m_localMatrix.addToRow< parallelDeviceAtomic >( stack.faceCenteredEqnRowIndex[i], + &stack.elemDofColIndex, + &dRes_dPres, + 1 ); + + real64 dRes_dFlux[NUM_FACE]{}; + for( integer j = 0; j < NUM_FACE; ++j ) + { + dRes_dFlux[j] = sigma * stack.dFaceResidual_dFlux[i][j]; + } + m_localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic >( stack.faceCenteredEqnRowIndex[i], + &stack.faceDofColIndices[0], + &dRes_dFlux[0], + NUM_FACE ); + } + } + + /** + * @brief Performs the kernel launch + */ + template< typename POLICY, typename KERNEL_TYPE > + static void + launch( localIndex const numElems, + KERNEL_TYPE const & kernelComponent ) + { + GEOS_MARK_FUNCTION; + + forAll< POLICY >( numElems, [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + typename KERNEL_TYPE::StackVariables stack; + + kernelComponent.setup( ei, stack ); + kernelComponent.compute( ei, stack ); + kernelComponent.complete( ei, stack ); + } ); + } + +protected: + + /// offset for my MPI rank + globalIndex const m_rankOffset; + + /// length tolerance + real64 const m_lengthTolerance; + + /// time step size + real64 const m_dt; + + /// ghost ranks and dof numbers + arrayView1d< integer const > const m_elemGhostRank; + arrayView1d< globalIndex const > const m_elemDofNumber; + arrayView1d< integer const > const m_faceGhostRank; + arrayView1d< globalIndex const > const m_faceDofNumber; + + /// topological and geometrical data + arrayView2d< localIndex const > const m_elemToFaces; + arrayView2d< real64 const > const m_elemCenter; + arrayView1d< real64 const > const m_elemVolume; + arrayView1d< real64 const > const m_elemGravCoef; + ArrayOfArraysView< localIndex const > const m_faceToNodes; + arrayView1d< real64 const > const m_faceGravCoef; + arrayView2d< real64 const > const m_faceCenter; + arrayView2d< real64 const > const m_faceNormal; + arrayView2d< localIndex const > const m_elemRegionList; + arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const m_nodePosition; + + /// permeability + arrayView3d< real64 const > const m_elemPerm; + + /// primary variables and boundary data + arrayView1d< real64 const > const m_elemPres; + arrayView1d< real64 const > const m_faceFlux; + arrayView1d< real64 const > const m_bcPres; + arrayView1d< integer const > const m_isPresBcFace; + + /// adaptive stencil flag + arrayView1d< integer const > const m_stencilFlag; + + /// fluid data + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const m_elemDens; + arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const m_dElemDens; + arrayView1d< real64 const > const m_mob; + arrayView2d< real64 const > const m_dMob; + + /// View on the local CRS matrix and RHS + CRSMatrixView< real64, globalIndex const > const m_localMatrix; + arrayView1d< real64 > const m_localRhs; + +}; + +/** + * @class ElementBasedAssemblyKernelFactory + */ +class ElementBasedAssemblyKernelFactory +{ +public: + + template< typename POLICY > + static void + createAndLaunch( globalIndex const rankOffset, + real64 const lengthTolerance, + string const elemDofKey, + string const faceDofKey, + NodeManager const & nodeManager, + FaceManager const & faceManager, + CellElementSubRegion const & subRegion, + mimeticInnerProduct::MimeticInnerProductBase const & mimeticInnerProductBase, + constitutive::SingleFluidBase const & fluid, + constitutive::PermeabilityBase const & permeability, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + mixedMimeticInnerProductDispatch( mimeticInnerProductBase, + [&] ( auto const mimeticInnerProduct ) + { + using IP = TYPEOFREF( mimeticInnerProduct ); + + mixedMimeticKernels::internal::kernelLaunchSelectorFaceSwitch( subRegion.numFacesPerElement(), [&] ( auto NUM_FACES ) + { + ElementBasedAssemblyKernel< NUM_FACES, IP > + kernel( rankOffset, lengthTolerance, elemDofKey, faceDofKey, nodeManager, faceManager, + subRegion, fluid, permeability, dt, localMatrix, localRhs ); + ElementBasedAssemblyKernel< NUM_FACES, IP >::template launch< POLICY >( subRegion.size(), kernel ); + } ); + } ); + } + +}; + +/******************************** ResidualNormKernel ********************************/ + +/** + * @class ResidualNormKernel + * @brief Computes the norm of the face-based constitutive residuals, normalized by a + * pressure scale built from the adjacent cell pressures at the previous converged step. + */ +class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBase< 1 > +{ +public: + + using Base = physicsSolverBaseKernels::ResidualNormKernelBase< 1 >; + using Base::m_minNormalizer; + using Base::m_rankOffset; + using Base::m_localResidual; + using Base::m_dofNumber; + + template< typename VIEWTYPE > + using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; + + using SinglePhaseFlowAccessors = + StencilAccessors< fields::flow::pressure_n >; + + ResidualNormKernel( globalIndex const rankOffset, + arrayView1d< real64 const > const & localResidual, + arrayView1d< globalIndex const > const & dofNumber, + arrayView1d< localIndex const > const & ghostRank, + SortedArrayView< localIndex const > const & regionFilter, + FaceManager const & faceManager, + SinglePhaseFlowAccessors const & singlePhaseFlowAccessors, + real64 const minNormalizer ) + : Base( rankOffset, + localResidual, + dofNumber, + ghostRank, + minNormalizer ), + m_regionFilter( regionFilter ), + m_elemRegionList( faceManager.elementRegionList() ), + m_elemSubRegionList( faceManager.elementSubRegionList() ), + m_elemList( faceManager.elementList() ), + m_pres_n( singlePhaseFlowAccessors.get( fields::flow::pressure_n {} ) ) + {} + + GEOS_HOST_DEVICE + void computePressureNormalizer( localIndex const kf, + real64 & pressureNormalizer ) const + { + integer elemCounter = 0; + for( integer k = 0; k < m_elemRegionList.size( 1 ); ++k ) + { + localIndex const er = m_elemRegionList[kf][k]; + localIndex const esr = m_elemSubRegionList[kf][k]; + localIndex const ei = m_elemList[kf][k]; + bool const onBoundary = (er == -1 || esr == -1 || ei == -1); + + if( !onBoundary && m_regionFilter.contains( er ) ) + { + pressureNormalizer = pressureNormalizer + LvArray::math::abs( m_pres_n[er][esr][ei] ); + elemCounter++; + } + } + pressureNormalizer /= elemCounter; + } + + GEOS_HOST_DEVICE + virtual void computeLinf( localIndex const kf, + LinfStackVariables & stack ) const override + { + if( m_dofNumber[kf] >= 0 ) + { + real64 pressureNormalizer = 0.0; + computePressureNormalizer( kf, pressureNormalizer ); + + stack.localValue[0] = stack.localValue[0] + + LvArray::math::abs( m_localResidual[stack.localRow] ) / LvArray::math::max( m_minNormalizer, pressureNormalizer ); + } + } + + GEOS_HOST_DEVICE + virtual void computeL2( localIndex const kf, + L2StackVariables & stack ) const override + { + if( m_dofNumber[kf] >= 0 ) + { + real64 pressureNormalizer = 0.0; + computePressureNormalizer( kf, pressureNormalizer ); + + real64 const val = m_localResidual[stack.localRow]; + stack.localValue[0] += val * val; + stack.localNormalizer[0] += LvArray::math::max( m_minNormalizer, pressureNormalizer ); + } + } + +protected: + + /// Filter to identify the target regions of the solver + SortedArrayView< localIndex const > const m_regionFilter; + + /// Views on the maps face to elements + arrayView2d< localIndex const > const m_elemRegionList; + arrayView2d< localIndex const > const m_elemSubRegionList; + arrayView2d< localIndex const > const m_elemList; + + /// View on pressure at the previous converged time step + ElementViewConst< arrayView1d< real64 const > > const m_pres_n; + +}; + +/** + * @class ResidualNormKernelFactory + */ +class ResidualNormKernelFactory +{ +public: + + template< typename POLICY > + static void + createAndLaunch( physicsSolverBaseKernels::NormType const normType, + globalIndex const rankOffset, + string const & dofKey, + arrayView1d< real64 const > const & localResidual, + SortedArrayView< localIndex const > const & regionFilter, + string const & solverName, + ElementRegionManager const & elemManager, + FaceManager const & faceManager, + real64 const minNormalizer, + real64 (& residualNorm)[1], + real64 (& residualNormalizer)[1] ) + { + arrayView1d< globalIndex const > const dofNumber = faceManager.getReference< array1d< globalIndex > >( dofKey ); + arrayView1d< integer const > const ghostRank = faceManager.ghostRank(); + + using kernelType = ResidualNormKernel; + typename kernelType::SinglePhaseFlowAccessors flowAccessors( elemManager, solverName ); + + ResidualNormKernel kernel( rankOffset, localResidual, dofNumber, ghostRank, + regionFilter, faceManager, flowAccessors, minNormalizer ); + if( normType == physicsSolverBaseKernels::NormType::Linf ) + { + ResidualNormKernel::launchLinf< POLICY >( faceManager.size(), kernel, residualNorm ); + } + else // L2 norm + { + ResidualNormKernel::launchL2< POLICY >( faceManager.size(), kernel, residualNorm, residualNormalizer ); + } + } + +}; + +} // namespace singlePhaseMixedMFDKernels + +} // namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_MIXEDMFDKERNELS_HPP From 75c12a209efada3df4d5bb6c6839b9e576ac4216 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 31 Jul 2026 15:24:14 -0700 Subject: [PATCH 09/64] wip: including adaptive inner product --- .../incompressible_consistency_base.xml | 2 +- .../incompressible_consistency_mixed.xml | 2 +- .../structured_smoke_mixed.xml | 14 ++- .../finiteVolume/CMakeLists.txt | 1 + .../AdaptiveInnerProduct.hpp | 110 ++++++++++++++++++ .../QuasiTPFAInnerProduct.hpp | 9 +- .../MixedMimeticDiscretization.cpp | 30 +++-- .../MixedMimeticDiscretization.hpp | 27 ++--- .../fluidFlow/SinglePhaseMixedMFD.cpp | 6 +- .../SinglePhaseMixedMFDKernels.hpp | 47 +++----- 10 files changed, 175 insertions(+), 73 deletions(-) create mode 100644 src/coreComponents/finiteVolume/mimeticInnerProducts/AdaptiveInnerProduct.hpp diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_consistency_base.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_consistency_base.xml index a53894b8129..7b574083af3 100644 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_consistency_base.xml +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_consistency_base.xml @@ -42,7 +42,7 @@ referencePressure="0.0" compressibility="0.0" viscosibility="0.0" - densityModelType="linear"/> + densityModelType="exponential"/> diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/structured_smoke_mixed.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/structured_smoke_mixed.xml index d752ee5e185..3ff0ad0ba45 100644 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/structured_smoke_mixed.xml +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/structured_smoke_mixed.xml @@ -57,7 +57,7 @@ @@ -73,7 +73,7 @@ referencePressure="0.0" compressibility="0.0" viscosibility="0.0" - densityModelType="linear"/> + densityModelType="exponential"/> + - + + + diff --git a/src/coreComponents/finiteVolume/CMakeLists.txt b/src/coreComponents/finiteVolume/CMakeLists.txt index 7a46a4b304e..e3e97df7e48 100644 --- a/src/coreComponents/finiteVolume/CMakeLists.txt +++ b/src/coreComponents/finiteVolume/CMakeLists.txt @@ -35,6 +35,7 @@ set( finiteVolume_headers MimeticInnerProductDispatch.hpp StencilBase.hpp TwoPointFluxApproximation.hpp + mimeticInnerProducts/AdaptiveInnerProduct.hpp mimeticInnerProducts/BdVLMInnerProduct.hpp mimeticInnerProducts/MimeticInnerProductBase.hpp mimeticInnerProducts/MimeticInnerProductHelpers.hpp diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/AdaptiveInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/AdaptiveInnerProduct.hpp new file mode 100644 index 00000000000..7dc09de49ca --- /dev/null +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/AdaptiveInnerProduct.hpp @@ -0,0 +1,110 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file AdaptiveInnerProduct.hpp + */ + +#ifndef GEOS_FINITEVOLUME_MIMETICINNERPRODUCTS_ADAPTIVEINNERPRODUCT_HPP_ +#define GEOS_FINITEVOLUME_MIMETICINNERPRODUCTS_ADAPTIVEINNERPRODUCT_HPP_ + +#include "finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp" + +namespace geos +{ +namespace mimeticInnerProduct +{ + +/** + * @class AdaptiveInnerProduct + * @tparam MFD_IP the consistent (MFD) inner product used in the MFD-compatible cells + * + * @brief Adaptive mixed-form inner product blending the MFD mass matrix of @p MFD_IP with the + * diagonal TPFA mass matrix according to a cell-wise weight: + * + * M_adapt = eta * M_mfd + (1 - eta) * M_tpfa, eta in [0, 1]. + * + * With the binary Global Adaptation stencil flag (eta = chi in {0, 1}) this reproduces the + * adaptive TPFA/MFD discretization; a fractional weight yields a convex combination whose + * spectral bounds follow from those of the two operands (uniform coercivity of the blend). + * + * The blend is branch-free by design and lives at the inner-product level so that any + * mixed mimetic assembly kernel (single-phase today, compositional in the future) can adopt + * the adaptation by wrapping its inner product type: AdaptiveInnerProduct< IP >::computeM. + */ +template< typename MFD_IP > +struct AdaptiveInnerProduct +{ + /** + * @brief Compute the blended mixed-form mass matrix M_adapt in a given element. + * @param[in] nodePosition the position of the nodes + * @param[in] faceToNodes the map from the face to their nodes + * @param[in] elemToFaces the maps from the one-sided face to the corresponding face + * @param[in] elemCenter the center of the element + * @param[in] elemVolume the volume of the element + * @param[in] elemPerm the permeability in the element + * @param[in] lengthTolerance the tolerance used in the trans calculations + * @param[in] weight the cell-wise blending weight eta in [0, 1] (1 = MFD, 0 = TPFA) + * @param[inout] M the output blended mass matrix + */ + template< localIndex NF > + GEOS_HOST_DEVICE + static void + computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, + ArrayOfArraysView< localIndex const > const & faceToNodes, + arraySlice1d< localIndex const > const & elemToFaces, + arraySlice1d< real64 const > const & elemCenter, + real64 const & elemVolume, + real64 const (&elemPerm)[ 3 ], + real64 const & lengthTolerance, + real64 const & weight, + arraySlice2d< real64 > const & M ) + { + stackArray2d< real64, NF *NF > mfdMassMatrix( NF, NF ); + stackArray2d< real64, NF *NF > tpfaMassMatrix( NF, NF ); + + MFD_IP::template computeM< NF >( nodePosition, + faceToNodes, + elemToFaces, + elemCenter, + elemVolume, + elemPerm, + lengthTolerance, + mfdMassMatrix ); + + TPFAInnerProduct::computeM< NF >( nodePosition, + faceToNodes, + elemToFaces, + elemCenter, + elemVolume, + elemPerm, + lengthTolerance, + tpfaMassMatrix ); + + for( localIndex i = 0; i < NF; ++i ) + { + for( localIndex j = 0; j < NF; ++j ) + { + M[i][j] = weight * mfdMassMatrix( i, j ) + ( 1.0 - weight ) * tpfaMassMatrix( i, j ); + } + } + } +}; + +} // end namespace mimeticInnerProduct + +} // end namespace geos + +#endif //GEOS_FINITEVOLUME_MIMETICINNERPRODUCTS_ADAPTIVEINNERPRODUCT_HPP_ diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp index d966871f120..23dbd33ed82 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp @@ -184,15 +184,16 @@ QuasiTPFAInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POS LvArray::tensorOps::Rij_eq_AikBjk< 3, NF, 3 >( workNK, K, N ); LvArray::tensorOps::Rij_eq_AikBkj< NF, NF, 3 >( W, N, workNK ); - // 5) build Q from C (orthonormal basis of consistency space) + // 5) build Q from N (orthonormal basis of the exact-flux consistency space): + // the stabilization must annihilate range(N) so that M N K = C holds exactly real64 q0[ NF ], q1[ NF ], q2[ NF ]; real64 Qmat[ NF ][ 3 ]; for( localIndex i = 0; i < NF; ++i ) { - q0[i] = C[i][0]; - q1[i] = C[i][1]; - q2[i] = C[i][2]; + q0[i] = N[i][0]; + q1[i] = N[i][1]; + q2[i] = N[i][2]; } MimeticInnerProductHelpers::orthonormalize< NF >( q0, q1, q2, Qmat ); diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp index 1f733c80c23..55c325161ae 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp @@ -34,7 +34,7 @@ using namespace mimeticInnerProduct; MixedMimeticDiscretization::MixedMimeticDiscretization( string const & name, Group * const parent ) : Group( name, parent ), - m_adaptationType( AdaptationType::None ), + m_isAdaptive( 1 ), m_residualTolerance( 1e-3 ), m_nominalGradient( { 1.0, 1.0, 1.0 } ) { @@ -42,14 +42,14 @@ MixedMimeticDiscretization::MixedMimeticDiscretization( string const & name, registerWrapper( viewKeyStruct::innerProductTypeString(), &m_innerProductType ). setInputFlag( InputFlags::REQUIRED ). - setDescription( "Type of inner product used in the mixed mimetic solver" ); + setDescription( "Type of inner product used in the MFD-compatible cells of the mixed mimetic solver" ); - registerWrapper( viewKeyStruct::adaptationTypeString(), &m_adaptationType ). + registerWrapper( viewKeyStruct::adaptiveString(), &m_isAdaptive ). setInputFlag( InputFlags::OPTIONAL ). - setApplyDefaultValue( AdaptationType::None ). - setDescription( "Adaptation paradigm used to select the cell-wise inner product. " - "Valid options:\n* none - the inner product selected by innerProductType is used in every cell\n" - "* global - residual-based Global Adaptation selects TPFA or MFD cell-wise" ); + setApplyDefaultValue( 1 ). + setDescription( "Flag enabling the residual-based Global Adaptation: when enabled (1, default), the " + "cell-wise inner product is selected between TPFA and innerProductType according to the " + "consistency indicator; when disabled (0), innerProductType is used in every cell" ); registerWrapper( viewKeyStruct::residualToleranceString(), &m_residualTolerance ). setInputFlag( InputFlags::OPTIONAL ). @@ -66,8 +66,8 @@ void MixedMimeticDiscretization::postInputInitialization() { Group::postInputInitialization(); - GEOS_THROW_IF_LE_MSG( m_residualTolerance, 0.0, - GEOS_FMT( "{}: the residual tolerance must be strictly positive", + GEOS_THROW_IF_LT_MSG( m_residualTolerance, 0.0, + GEOS_FMT( "{}: the residual tolerance cannot be negative", getWrapperDataContext( viewKeyStruct::residualToleranceString() ) ), InputError ); @@ -75,6 +75,18 @@ void MixedMimeticDiscretization::postInputInitialization() GEOS_FMT( "{}: the nominal gradient cannot be the zero vector", getWrapperDataContext( viewKeyStruct::nominalGradientString() ) ), InputError ); + + // degenerate combination: with a TPFA inner product, the adaptive blend mixes TPFA with TPFA + // and the operator is TPFA everywhere regardless of the marking; the consistency indicator + // fields are still computed and output, which makes this combination a useful diagnostic mode + if( isAdaptive() && m_innerProductType == mimeticInnerProduct::MimeticInnerProductTypeStrings::TPFA ) + { + GEOS_WARNING( GEOS_FMT( "{}: 'adaptive' is enabled but 'innerProductType' is TPFA: the adaptation has no " + "effect on the discretization (both operators of the adaptive blend coincide), and " + "the scheme reduces to full TPFA. The consistency indicators are still computed and " + "output. Select an MFD inner product (e.g. quasiTPFA) to activate the adaptation.", + getDataContext() ) ); + } } void MixedMimeticDiscretization::initializePostInitialConditionsPreSubGroups() diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp index 33d886e36d3..0968975534c 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp @@ -21,7 +21,6 @@ #define GEOS_MIXEDMIMETIC_MIXEDMIMETICDISCRETIZATION_HPP_ #include "dataRepository/Group.hpp" -#include "common/format/EnumStrings.hpp" #include "finiteVolume/mimeticInnerProducts/MimeticInnerProductBase.hpp" namespace geos @@ -32,18 +31,14 @@ namespace geos * * Provides management of the cell-wise inner products and of the residual-based * Global Adaptation (GA) parameters used by the mixed mimetic finite difference solvers. + * Global Adaptation is the only supported cell classification paradigm: when adaptation + * is enabled, the residual tolerance controls the TPFA/MFD partition; when disabled, the + * selected inner product is used in every cell. */ class MixedMimeticDiscretization : public dataRepository::Group { public: - /// Adaptation paradigm: no adaptation (single inner product everywhere) or Global Adaptation - enum class AdaptationType : integer - { - None, ///< the inner product selected by innerProductType is used in every cell - Global ///< face-assembled residual indicators select TPFA or MFD cell-wise - }; - /// Alias for CatalogInterface, necessary declarations for factory instantiation of derived classes using CatalogInterface = dataRepository::CatalogInterface< MixedMimeticDiscretization, string const &, Group * const >; @@ -79,8 +74,8 @@ class MixedMimeticDiscretization : public dataRepository::Group /// @return The key for the inner product static constexpr char const * innerProductString() { return "innerProduct"; } - /// @return The key for the adaptation type - static constexpr char const * adaptationTypeString() { return "adaptationType"; } + /// @return The key for the adaptive flag + static constexpr char const * adaptiveString() { return "adaptive"; } /// @return The key for the residual tolerance static constexpr char const * residualToleranceString() { return "residualTolerance"; } @@ -90,9 +85,9 @@ class MixedMimeticDiscretization : public dataRepository::Group }; /** - * @brief @return The adaptation type + * @brief @return Whether the residual-based Global Adaptation is enabled */ - AdaptationType getAdaptationType() const { return m_adaptationType; } + bool isAdaptive() const { return m_isAdaptive == 1; } /** * @brief @return The residual tolerance used in the marking criterion @@ -115,8 +110,8 @@ class MixedMimeticDiscretization : public dataRepository::Group /// type of inner product used in the mixed mimetic solver string m_innerProductType; - /// adaptation paradigm (none or global) - AdaptationType m_adaptationType; + /// flag enabling the residual-based Global Adaptation (1 = adaptive, 0 = selected inner product everywhere) + integer m_isAdaptive; /// user-prescribed tolerance for the marking criterion real64 m_residualTolerance; @@ -132,10 +127,6 @@ class MixedMimeticDiscretization : public dataRepository::Group }; -ENUM_STRINGS( MixedMimeticDiscretization::AdaptationType, - "none", - "global" ); - } #endif //GEOS_MIXEDMIMETIC_MIXEDMIMETICDISCRETIZATION_HPP_ diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index aae3f6c4df0..b3ceb55ac83 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -137,7 +137,7 @@ void SinglePhaseMixedMFD::initializePostInitialConditionsPreSubGroups() fsManager.apply< FaceManager >( 0.0, mesh, flow::bcPressure::key(), - [&] ( FieldSpecificationBase const &, + [&] ( FieldSpecification const &, string const &, SortedArrayView< localIndex const > const & targetSet, FaceManager &, @@ -170,7 +170,7 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d MixedMimeticDiscretizationManager const & mmManager = numericalMethodManager.getMixedMimeticDiscretizationManager(); MixedMimeticDiscretization const & discretization = mmManager.getMixedMimeticDiscretization( m_discretizationName ); - if( discretization.getAdaptationType() == MixedMimeticDiscretization::AdaptationType::None ) + if( !discretization.isAdaptive() ) { // no adaptation: activate the selected inner product in every cell forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, @@ -344,7 +344,7 @@ void SinglePhaseMixedMFD::applyFacePressureBCValues( real64 const time, fsManager.apply< FaceManager >( time, mesh, flow::bcPressure::key(), - [&] ( FieldSpecificationBase const & fs, + [&] ( FieldSpecification const & fs, string const & setName, SortedArrayView< localIndex const > const & targetSet, FaceManager & targetGroup, diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp index 5d94b0b7098..5a5d853ae2b 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp @@ -24,6 +24,7 @@ #include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" #include "constitutive/fluid/singlefluid/SingleFluidFields.hpp" #include "constitutive/permeability/PermeabilityBase.hpp" +#include "finiteVolume/mimeticInnerProducts/AdaptiveInnerProduct.hpp" #include "linearAlgebra/interfaces/InterfaceTypes.hpp" #include "mesh/MeshLevel.hpp" #include "mixedMimetic/MixedMimeticDispatch.hpp" @@ -112,18 +113,12 @@ class ElementBasedAssemblyKernel { GEOS_HOST_DEVICE StackVariables() - : massMatrix( NUM_FACE, NUM_FACE ), - mfdMassMatrix( NUM_FACE, NUM_FACE ), - tpfaMassMatrix( NUM_FACE, NUM_FACE ) + : massMatrix( NUM_FACE, NUM_FACE ) {} - /// blended local mass matrix: chi * M_mfd + (1 - chi) * M_tpfa + /// adaptive local mass matrix: chi * M_mfd + (1 - chi) * M_tpfa stackArray2d< real64, NUM_FACE *NUM_FACE > massMatrix; - /// scratch for the two candidate mass matrices - stackArray2d< real64, NUM_FACE *NUM_FACE > mfdMassMatrix; - stackArray2d< real64, NUM_FACE *NUM_FACE > tpfaMassMatrix; - /// sign relating the cell-outward normal to the fixed global face normal real64 orientation[NUM_FACE]{}; @@ -171,33 +166,17 @@ class ElementBasedAssemblyKernel { real64 const perm[ 3 ] = { m_elemPerm[ei][0][0], m_elemPerm[ei][0][1], m_elemPerm[ei][0][2] }; - // step 1: branch-free adaptive operator blend, M = chi * M_mfd + (1 - chi) * M_tpfa - IP::template computeM< NUM_FACE >( m_nodePosition, - m_faceToNodes, - m_elemToFaces[ei], - m_elemCenter[ei], - m_elemVolume[ei], - perm, - m_lengthTolerance, - stack.mfdMassMatrix ); - - mimeticInnerProduct::TPFAInnerProduct::computeM< NUM_FACE >( m_nodePosition, - m_faceToNodes, - m_elemToFaces[ei], - m_elemCenter[ei], - m_elemVolume[ei], - perm, - m_lengthTolerance, - stack.tpfaMassMatrix ); - + // step 1: adaptive inner product, M = chi * M_mfd + (1 - chi) * M_tpfa real64 const chi = static_cast< real64 >( m_stencilFlag[ei] ); - for( integer i = 0; i < NUM_FACE; ++i ) - { - for( integer j = 0; j < NUM_FACE; ++j ) - { - stack.massMatrix( i, j ) = chi * stack.mfdMassMatrix( i, j ) + ( 1.0 - chi ) * stack.tpfaMassMatrix( i, j ); - } - } + mimeticInnerProduct::AdaptiveInnerProduct< IP >::template computeM< NUM_FACE >( m_nodePosition, + m_faceToNodes, + m_elemToFaces[ei], + m_elemCenter[ei], + m_elemVolume[ei], + perm, + m_lengthTolerance, + chi, + stack.massMatrix ); // step 2: cell mobility scaling of the inverse Darcy coefficient real64 const mob = m_mob[ei]; From 33a55f3e5468745a519fd95a4a7757d0d33e5940 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 31 Jul 2026 15:44:38 -0700 Subject: [PATCH 10/64] wip:testing classification with 4 partitions --- .../spe11b_mixed_mfd.xml | 150 ++++++++++++++++++ .../adaptivity/GlobalAdaptationKernels.hpp | 8 +- .../fluidFlow/SinglePhaseMixedMFD.cpp | 7 +- 3 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/spe11b_mixed_mfd.xml diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/spe11b_mixed_mfd.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/spe11b_mixed_mfd.xml new file mode 100644 index 00000000000..6d4844500bc --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/spe11b_mixed_mfd.xml @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp b/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp index 8b566a8e5ea..78bae80d6f6 100644 --- a/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp +++ b/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp @@ -276,10 +276,16 @@ struct LocalResidualKernel template< integer NF > struct MarkingKernel { + /** + * @brief Launch the marking loop. + * @return the number of locally-owned cells marked as MFD-compatible (ghost cells are + * marked too, but excluded from the count so that an MPI sum yields the global count) + */ template< typename POLICY > static localIndex launch( localIndex const numElems, arrayView2d< localIndex const > const & elemToFaces, + arrayView1d< integer const > const & elemGhostRank, arrayView1d< real64 const > const & faceResidual, real64 const tolerance, arrayView1d< real64 > const & consistencyIndicator, @@ -297,7 +303,7 @@ struct MarkingKernel consistencyIndicator[ei] = indicator; integer const flag = ( indicator > tolerance ) ? 1 : 0; stencilFlag[ei] = flag; - numMfdCells += flag; + numMfdCells += ( elemGhostRank[ei] < 0 ) ? flag : 0; } ); return numMfdCells.get(); diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index b3ceb55ac83..9b3ebb72392 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -269,12 +269,13 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d numMfdCells += mixedMimeticKernels::MarkingKernel< NUM_FACES >:: template launch< parallelDevicePolicy<> >( subRegion.size(), subRegion.faceList().toViewConst(), + subRegion.ghostRank(), faceResidual.toViewConst(), tolerance, consistencyIndicator, stencilFlag ); } ); - numCells += subRegion.size(); + numCells += subRegion.size() - subRegion.getNumberOfGhosts(); } ); // make the marking consistent on ghost cells @@ -282,8 +283,10 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d fieldsToBeSync.addElementFields( { mixedMimetic::stencilFlag::key(), mixedMimetic::consistencyIndicator::key() }, regionNames ); CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), false ); + globalIndex const globalNumMfdCells = MpiWrapper::sum< globalIndex >( numMfdCells ); + globalIndex const globalNumCells = MpiWrapper::sum< globalIndex >( numCells ); GEOS_LOG_RANK_0( GEOS_FMT( "{}: Global Adaptation marked {} / {} cells as MFD-compatible (tolerance = {})", - getName(), numMfdCells, numCells, tolerance ) ); + getName(), globalNumMfdCells, globalNumCells, tolerance ) ); } ); } From e0c3c6c8776fc04ef59c77e7b386de7051b21a06 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 31 Jul 2026 15:50:33 -0700 Subject: [PATCH 11/64] wip --- src/coreComponents/mixedMimetic/MixedMimeticFields.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp index 6da2c645528..20c346ecd46 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp @@ -61,7 +61,7 @@ DECLARE_FIELD( consistencyIndicator, "consistencyIndicator", array1d< real64 >, 0, - LEVEL_0, + NOPLOT, WRITE_AND_READ, "Residual-based consistency indicator per cell (Global Adaptation)" ); From 31a0398f47f458d291f0d3be004962abacb13a5d Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 31 Jul 2026 16:11:35 -0700 Subject: [PATCH 12/64] wip: adding MRG strategy for saddle point problems --- .../linearAlgebra/CMakeLists.txt | 1 + .../interfaces/hypre/HypreMGR.cpp | 6 + .../mgrStrategies/SinglePhaseMixedMFD.hpp | 104 ++++++++++++++++++ .../utilities/LinearSolverParameters.hpp | 2 + .../fluidFlow/SinglePhaseMixedMFD.cpp | 2 + .../SinglePhaseMixedMFDKernels.hpp | 48 ++++++-- 6 files changed, 152 insertions(+), 11 deletions(-) create mode 100644 src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp diff --git a/src/coreComponents/linearAlgebra/CMakeLists.txt b/src/coreComponents/linearAlgebra/CMakeLists.txt index af5310e79ea..376a23bae91 100644 --- a/src/coreComponents/linearAlgebra/CMakeLists.txt +++ b/src/coreComponents/linearAlgebra/CMakeLists.txt @@ -160,6 +160,7 @@ if( ENABLE_HYPRE ) interfaces/hypre/mgrStrategies/CompositionalMultiphaseReservoirFVM.hpp interfaces/hypre/mgrStrategies/CompositionalMultiphaseReservoirHybridFVM.hpp interfaces/hypre/mgrStrategies/HybridSinglePhasePoromechanics.hpp + interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp interfaces/hypre/mgrStrategies/Hydrofracture.hpp interfaces/hypre/mgrStrategies/ImmiscibleMultiphaseFVM.hpp interfaces/hypre/mgrStrategies/LagrangianContactMechanics.hpp diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp index 84912abe16c..1b33d0a6956 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp @@ -34,6 +34,7 @@ #include "linearAlgebra/interfaces/hypre/mgrStrategies/MultiphasePoromechanicsReservoirFVM.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/ReactiveCompositionalMultiphaseOBL.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseHybridFVM.hpp" +#include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhasePoromechanics.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhasePoromechanicsEmbeddedFractures.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhasePoromechanicsConformingFractures.hpp" @@ -173,6 +174,11 @@ void hypre::mgr::createMGR( LinearSolverParameters const & params, setStrategy< SinglePhaseHybridFVM >( params.mgr, numComponentsPerField, precond, mgrData ); break; } + case LinearSolverParameters::MGR::StrategyType::singlePhaseMixedMFD: + { + setStrategy< SinglePhaseMixedMFD >( params.mgr, numComponentsPerField, precond, mgrData ); + break; + } case LinearSolverParameters::MGR::StrategyType::singlePhasePoromechanics: { setStrategy< SinglePhasePoromechanics >( params.mgr, numComponentsPerField, precond, mgrData ); diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp new file mode 100644 index 00000000000..1e0b59855cc --- /dev/null +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp @@ -0,0 +1,104 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file SinglePhaseMixedMFD.hpp + */ + +#ifndef GEOS_LINEARALGEBRA_INTERFACES_HYPREMGRSINGLEPHASEMIXEDMFD_HPP_ +#define GEOS_LINEARALGEBRA_INTERFACES_HYPREMGRSINGLEPHASEMIXEDMFD_HPP_ + +#include "linearAlgebra/interfaces/hypre/HypreMGR.hpp" + +namespace geos +{ + +namespace hypre +{ + +namespace mgr +{ + +/** + * @brief SinglePhaseMixedMFD strategy. + * + * Two-level MGR reduction for the mixed mimetic finite difference saddle-point system + * [ M -B^T ] + * [ B 0 ] + * with face mass-flux and cell pressure unknowns. + * + * Labels description stored in point_marker_array + * dofLabel: 0 = face-centered mass flux + * dofLabel: 1 = cell-centered pressure + * + * Ingredients: + * 1. F-points face-centered mass flux (the SPD adaptive inner-product block M), + * C-points cell-centered pressure + * 2. F-points smoother: single BoomerAMG V-cycle on the flux block + * 3. Restriction: injection; prolongation: Jacobi; coarse grid: Galerkin (RAP), + * yielding an approximate pressure Schur complement + * 4. C-points coarse-grid/Schur complement solver: BoomerAMG + * 5. Global smoother: none + * + * The corresponding Krylov solver is (F)GMRES. This mirrors the hypredrive recipe used + * in the adaptive-MFD reference implementation (two-level MGR with f_dofs = flux labels, + * AMG F-relaxation and an AMG-solved RAP coarse operator). + */ +class SinglePhaseMixedMFD : public MGRStrategyBase< 1 > +{ +public: + /** + * @brief Constructor. + */ + explicit SinglePhaseMixedMFD( arrayView1d< int const > const & ) + : MGRStrategyBase( LvArray::integerConversion< HYPRE_Int >( 2 ) ) + { + // Level 0: eliminate the face-centered mass fluxes, keep the cell-centered pressure + m_labels[0].push_back( 1 ); + + setupLabels(); + + // Level 0 + m_levelFRelaxType[0] = MGRFRelaxationType::amgVCycle; + m_levelFRelaxIters[0] = 1; + m_levelInterpType[0] = MGRInterpolationType::jacobi; + m_levelRestrictType[0] = MGRRestrictionType::injection; + m_levelCoarseGridMethod[0] = MGRCoarseGridMethod::galerkin; + m_levelGlobalSmootherType[0] = MGRGlobalSmootherType::none; + } + + /** + * @brief Setup the MGR strategy. + * @param precond preconditioner wrapper + * @param mgrData auxiliary MGR data + */ + void setup( LinearSolverParameters::MGR const &, + HyprePrecWrapper & precond, + HypreMGRData & mgrData ) + { + setReduction( precond, mgrData ); + + // Configure the BoomerAMG solver used as mgr coarse solver for the pressure Schur complement + setPressureAMG( mgrData.coarseSolver ); + } +}; + +} // namespace mgr + +} // namespace hypre + +} // namespace geos + +#endif /*GEOS_LINEARALGEBRA_INTERFACES_HYPREMGRSINGLEPHASEMIXEDMFD_HPP_*/ diff --git a/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp b/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp index d9c6a954a54..e746bc10f21 100644 --- a/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp +++ b/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp @@ -299,6 +299,7 @@ struct LinearSolverParameters singlePhaseReservoirFVM, ///< finite volume single-phase flow with wells thermalSinglePhaseReservoirFVM, ///< finite volume thermal single-phase flow with wells singlePhaseHybridFVM, ///< hybrid finite volume single-phase flow + singlePhaseMixedMFD, ///< mixed mimetic finite difference single-phase flow singlePhaseReservoirHybridFVM, ///< hybrid finite volume single-phase flow with wells singlePhasePoromechanics, ///< single phase poromechanics with finite volume single phase flow thermalSinglePhasePoromechanics, ///< thermal single phase poromechanics with finite volume single phase flow @@ -576,6 +577,7 @@ ENUM_STRINGS( LinearSolverParameters::MGR::StrategyType, "singlePhaseReservoirFVM", "thermalSinglePhaseReservoirFVM", "singlePhaseHybridFVM", + "singlePhaseMixedMFD", "singlePhaseReservoirHybridFVM", "singlePhasePoromechanics", "thermalSinglePhasePoromechanics", diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index 9b3ebb72392..0bfde0b2ea8 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -51,6 +51,7 @@ SinglePhaseMixedMFD::SinglePhaseMixedMFD( const string & name, { // one cell-centered dof per cell m_numDofPerCell = 1; + m_linearSolverParameters.get().mgr.strategy = LinearSolverParameters::MGR::StrategyType::singlePhaseMixedMFD; } void SinglePhaseMixedMFD::registerDataOnMesh( Group & meshBodies ) @@ -451,6 +452,7 @@ void SinglePhaseMixedMFD::assembleFluxTerms( real64 const dt, faceDofKey, nodeManager, faceManager, + mesh.getElemManager(), subRegion, mimeticInnerProductBase, fluid, diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp index 5a5d853ae2b..b872715be1a 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp @@ -61,6 +61,11 @@ class ElementBasedAssemblyKernel using DerivOffset = constitutive::singlefluid::DerivativeOffsetC< 0 >; + template< typename VIEWTYPE > + using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; + + using LocalToGlobalAccessor = ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > >; + ElementBasedAssemblyKernel( globalIndex const rankOffset, real64 const & lengthTolerance, string const elemDofKey, @@ -68,6 +73,7 @@ class ElementBasedAssemblyKernel NodeManager const & nodeManager, FaceManager const & faceManager, CellElementSubRegion const & subRegion, + LocalToGlobalAccessor const & elemLocalToGlobal, constitutive::SingleFluidBase const & fluid, constitutive::PermeabilityBase const & permeability, real64 const & dt, @@ -87,9 +93,11 @@ class ElementBasedAssemblyKernel m_elemGravCoef( subRegion.getField< fields::flow::gravityCoefficient >() ), m_faceToNodes( faceManager.nodeList().toViewConst() ), m_faceGravCoef( faceManager.getField< fields::flow::gravityCoefficient >() ), - m_faceCenter( faceManager.faceCenter() ), - m_faceNormal( faceManager.faceNormal() ), m_elemRegionList( faceManager.elementRegionList() ), + m_elemSubRegionList( faceManager.elementSubRegionList() ), + m_elemList( faceManager.elementList() ), + m_elemLocalToGlobal( elemLocalToGlobal.toNestedViewConst() ), + m_myElemLocalToGlobal( subRegion.localToGlobalMap() ), m_nodePosition( nodeManager.referencePosition() ), m_elemPerm( permeability.permeability() ), m_elemPres( subRegion.getField< fields::flow::pressure >() ), @@ -187,21 +195,31 @@ class ElementBasedAssemblyKernel real64 const ccDens = m_elemDens[ei][0]; real64 const dCcDens_dPres = m_dElemDens[ei][0][DerivOffset::dP]; - // step 3: face orientations, localized fluxes and no-flow flags + // step 3: face orientations, localized fluxes and no-flow flags. + // The orientation must be identical on every MPI rank sharing the face (the face + // mass-flux unknown is shared across ranks), so it cannot rely on the face normal, + // whose direction is not guaranteed to be rank-invariant. Instead, the global face + // orientation points out of the adjacent cell with the smallest global element index. real64 localFlux[NUM_FACE]{}; + globalIndex const myGlobalElem = m_myElemLocalToGlobal[ei]; for( integer i = 0; i < NUM_FACE; ++i ) { localIndex const kf = m_elemToFaces[ei][i]; - real64 dotProd = 0.0; - for( integer d = 0; d < 3; ++d ) + real64 sigma = 1.0; + bool const valid0 = ( m_elemRegionList[kf][0] >= 0 && m_elemSubRegionList[kf][0] >= 0 && m_elemList[kf][0] >= 0 ); + bool const valid1 = ( m_elemRegionList[kf][1] >= 0 && m_elemSubRegionList[kf][1] >= 0 && m_elemList[kf][1] >= 0 ); + if( valid0 && valid1 ) { - dotProd += ( m_faceCenter[kf][d] - m_elemCenter[ei][d] ) * m_faceNormal[kf][d]; + globalIndex const g0 = m_elemLocalToGlobal[m_elemRegionList[kf][0]][m_elemSubRegionList[kf][0]][m_elemList[kf][0]]; + globalIndex const g1 = m_elemLocalToGlobal[m_elemRegionList[kf][1]][m_elemSubRegionList[kf][1]][m_elemList[kf][1]]; + globalIndex const gMin = ( g0 < g1 ) ? g0 : g1; + sigma = ( myGlobalElem == gMin ) ? 1.0 : -1.0; } - stack.orientation[i] = ( dotProd >= 0.0 ) ? 1.0 : -1.0; + stack.orientation[i] = sigma; localFlux[i] = stack.orientation[i] * m_faceFlux[kf]; - bool const onBoundary = ( m_elemRegionList[kf][0] == -1 || m_elemRegionList[kf][1] == -1 ); + bool const onBoundary = !( valid0 && valid1 ); stack.isNoFlowFace[i] = ( onBoundary && m_isPresBcFace[kf] == 0 ) ? 1 : 0; } @@ -350,9 +368,11 @@ class ElementBasedAssemblyKernel arrayView1d< real64 const > const m_elemGravCoef; ArrayOfArraysView< localIndex const > const m_faceToNodes; arrayView1d< real64 const > const m_faceGravCoef; - arrayView2d< real64 const > const m_faceCenter; - arrayView2d< real64 const > const m_faceNormal; arrayView2d< localIndex const > const m_elemRegionList; + arrayView2d< localIndex const > const m_elemSubRegionList; + arrayView2d< localIndex const > const m_elemList; + ElementViewConst< arrayView1d< globalIndex const > > const m_elemLocalToGlobal; + arrayView1d< globalIndex const > const m_myElemLocalToGlobal; arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const m_nodePosition; /// permeability @@ -394,6 +414,7 @@ class ElementBasedAssemblyKernelFactory string const faceDofKey, NodeManager const & nodeManager, FaceManager const & faceManager, + ElementRegionManager const & elemManager, CellElementSubRegion const & subRegion, mimeticInnerProduct::MimeticInnerProductBase const & mimeticInnerProductBase, constitutive::SingleFluidBase const & fluid, @@ -402,6 +423,11 @@ class ElementBasedAssemblyKernelFactory CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { + // rank-invariant global face orientation requires the global element indices of both + // cells adjacent to each face, possibly living in different subregions + ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > const elemLocalToGlobal = + elemManager.constructArrayViewAccessor< globalIndex, 1 >( ObjectManagerBase::viewKeyStruct::localToGlobalMapString() ); + mixedMimeticInnerProductDispatch( mimeticInnerProductBase, [&] ( auto const mimeticInnerProduct ) { @@ -411,7 +437,7 @@ class ElementBasedAssemblyKernelFactory { ElementBasedAssemblyKernel< NUM_FACES, IP > kernel( rankOffset, lengthTolerance, elemDofKey, faceDofKey, nodeManager, faceManager, - subRegion, fluid, permeability, dt, localMatrix, localRhs ); + subRegion, elemLocalToGlobal, fluid, permeability, dt, localMatrix, localRhs ); ElementBasedAssemblyKernel< NUM_FACES, IP >::template launch< POLICY >( subRegion.size(), kernel ); } ); } ); From 111b1d477ca9b00cf1160ec6c9ae9febc1044971 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 31 Jul 2026 16:22:15 -0700 Subject: [PATCH 13/64] wip: testing MRG on saddle point problems --- .../polyhedralDiscretizations/spe11b_mixed_mfd.xml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/spe11b_mixed_mfd.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/spe11b_mixed_mfd.xml index 6d4844500bc..55d972cb7aa 100644 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/spe11b_mixed_mfd.xml +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/spe11b_mixed_mfd.xml @@ -58,8 +58,12 @@ - + + From 85ccb1f4b185b55028e53841d0ebf5069fc33ee0 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 31 Jul 2026 18:32:57 -0700 Subject: [PATCH 14/64] wip: stencilFlag-guided three-level MGR strategy --- .../interfaces/hypre/HypreMGR.cpp | 15 +++ .../mgrStrategies/SinglePhaseMixedMFD.hpp | 64 ++++++----- .../utilities/LinearSolverParameters.hpp | 3 + .../fluidFlow/SinglePhaseMixedMFD.cpp | 102 ++++++++++++++++++ .../fluidFlow/SinglePhaseMixedMFD.hpp | 19 ++++ 5 files changed, 178 insertions(+), 25 deletions(-) diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp index 1b33d0a6956..3f476927bef 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp @@ -78,6 +78,18 @@ void hypre::mgr::createMGR( LinearSolverParameters const & params, array1d< int > const numComponentsPerField = dofManager->numComponentsPerField(); dofManager->getLocalDofComponentLabels( mgrData.pointMarkers ); + // solver-provided per-dof labels override the field-component labels (e.g. to distinguish + // sub-blocks of a field, as in the adaptive mixed MFD TPFA/MFD face-flux splitting) + if( !params.mgr.customPointMarkers.empty() ) + { + GEOS_ERROR_IF_NE_MSG( params.mgr.customPointMarkers.size(), mgrData.pointMarkers.size(), + "MGR preconditioner: customPointMarkers size does not match the number of local dofs" ); + for( localIndex i = 0; i < mgrData.pointMarkers.size(); ++i ) + { + mgrData.pointMarkers[i] = LvArray::integerConversion< HYPRE_Int >( params.mgr.customPointMarkers[i] ); + } + } + if( params.logLevel >= 1 ) { GEOS_LOG_RANK_0( GEOS_FMT( " MGR preconditioner: numComponentsPerField = {}", numComponentsPerField ) ); @@ -176,6 +188,9 @@ void hypre::mgr::createMGR( LinearSolverParameters const & params, } case LinearSolverParameters::MGR::StrategyType::singlePhaseMixedMFD: { + GEOS_ERROR_IF( params.mgr.customPointMarkers.empty(), + "The singlePhaseMixedMFD MGR strategy requires solver-provided point markers " + "splitting the face-flux dofs by the TPFA/MFD classification" ); setStrategy< SinglePhaseMixedMFD >( params.mgr, numComponentsPerField, precond, mgrData ); break; } diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp index 1e0b59855cc..5e3a012b148 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp @@ -32,51 +32,65 @@ namespace mgr { /** - * @brief SinglePhaseMixedMFD strategy. + * @brief SinglePhaseMixedMFD strategy: stencilFlag-guided three-level MGR reduction of the + * mixed mimetic finite difference saddle-point system + * [ M -B^T ] + * [ B 0 ] + * with face mass-flux and cell pressure unknowns. * - * Two-level MGR reduction for the mixed mimetic finite difference saddle-point system - * [ M -B^T ] - * [ B 0 ] - * with face mass-flux and cell pressure unknowns. - * - * Labels description stored in point_marker_array - * dofLabel: 0 = face-centered mass flux - * dofLabel: 1 = cell-centered pressure + * The solver provides custom point markers (LinearSolverParameters::MGR::customPointMarkers) + * splitting the face-flux dofs by the Global Adaptation classification: + * dofLabel: 0 = face flux whose adjacent cells are all TPFA-compatible (the assembled + * flux row is exactly diagonal: TPFA/TPFA interfaces, boundary faces of + * TPFA cells and no-flow identity rows) + * dofLabel: 1 = face flux adjacent to at least one MFD-compatible cell + * dofLabel: 2 = cell-centered pressure * * Ingredients: - * 1. F-points face-centered mass flux (the SPD adaptive inner-product block M), - * C-points cell-centered pressure - * 2. F-points smoother: single BoomerAMG V-cycle on the flux block - * 3. Restriction: injection; prolongation: Jacobi; coarse grid: Galerkin (RAP), - * yielding an approximate pressure Schur complement - * 4. C-points coarse-grid/Schur complement solver: BoomerAMG - * 5. Global smoother: none - * - * The corresponding Krylov solver is (F)GMRES. This mirrors the hypredrive recipe used - * in the adaptive-MFD reference implementation (two-level MGR with f_dofs = flux labels, - * AMG F-relaxation and an AMG-solved RAP coarse operator). + * 1. Level 0: F-points = TPFA-diagonal face fluxes (label 0). The F-block is exactly + * diagonal, so a single Jacobi sweep and Jacobi (diagonal) prolongation perform the + * elimination exactly, and the Galerkin (RAP) coarse grid is the exact Schur complement. + * The cost of this level is proportional to the TPFA fraction selected by the + * residual tolerance, mirroring the sparsity-reduction metric of the adaptive scheme. + * 2. Level 1: F-points = MFD face fluxes (label 1), relaxed with a single BoomerAMG + * V-cycle on the (SPD) MFD flux block; Jacobi prolongation, injection restriction, + * Galerkin (RAP) coarse grid approximating the pressure Schur complement. + * 3. Coarsest level: cell-pressure system solved with BoomerAMG. + * 4. Global smoother: none. The Krylov solver is (F)GMRES. */ -class SinglePhaseMixedMFD : public MGRStrategyBase< 1 > +class SinglePhaseMixedMFD : public MGRStrategyBase< 2 > { public: /** * @brief Constructor. */ explicit SinglePhaseMixedMFD( arrayView1d< int const > const & ) - : MGRStrategyBase( LvArray::integerConversion< HYPRE_Int >( 2 ) ) + : MGRStrategyBase( LvArray::integerConversion< HYPRE_Int >( 3 ) ) { - // Level 0: eliminate the face-centered mass fluxes, keep the cell-centered pressure + // Level 0: eliminate the TPFA-diagonal face fluxes, keep the MFD fluxes and the pressure m_labels[0].push_back( 1 ); + m_labels[0].push_back( 2 ); + + // Level 1: eliminate the MFD face fluxes, keep the pressure + m_labels[1].push_back( 2 ); setupLabels(); - // Level 0 - m_levelFRelaxType[0] = MGRFRelaxationType::amgVCycle; + // Level 0: the F-block is exactly diagonal - one Jacobi sweep is an exact solve + m_levelFRelaxType[0] = MGRFRelaxationType::jacobi; m_levelFRelaxIters[0] = 1; m_levelInterpType[0] = MGRInterpolationType::jacobi; m_levelRestrictType[0] = MGRRestrictionType::injection; m_levelCoarseGridMethod[0] = MGRCoarseGridMethod::galerkin; m_levelGlobalSmootherType[0] = MGRGlobalSmootherType::none; + + // Level 1: genuine multilevel relaxation on the MFD flux block + m_levelFRelaxType[1] = MGRFRelaxationType::amgVCycle; + m_levelFRelaxIters[1] = 1; + m_levelInterpType[1] = MGRInterpolationType::jacobi; + m_levelRestrictType[1] = MGRRestrictionType::injection; + m_levelCoarseGridMethod[1] = MGRCoarseGridMethod::galerkin; + m_levelGlobalSmootherType[1] = MGRGlobalSmootherType::none; } /** diff --git a/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp b/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp index e746bc10f21..64d36f77742 100644 --- a/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp +++ b/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp @@ -330,6 +330,9 @@ struct LinearSolverParameters integer separateComponents = false; ///< Apply a separate displacement component (SDC) filter before AMG construction integer areWellsShut = false; ///< Flag to let MGR know that wells are shut, and that jacobi can be applied to the well ///< block + array1d< integer > customPointMarkers; ///< Optional solver-provided per-local-dof labels overriding the field-component + ///< labels (empty = use the DofManager field labels); used by strategies whose + ///< reduction is finer-grained than the dof fields (e.g. adaptive mixed MFD) } mgr; ///< Multigrid reduction (MGR) parameters diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index 0bfde0b2ea8..48d6836becf 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -376,6 +376,108 @@ void SinglePhaseMixedMFD::applyFacePressureBCValues( real64 const time, } ); } +void SinglePhaseMixedMFD::setupSystem( DomainPartition & domain, + DofManager & dofManager, + CRSMatrix< real64, globalIndex > & localMatrix, + ParallelVector & rhs, + ParallelVector & solution, + bool const setSparsity ) +{ + SinglePhaseBase::setupSystem( domain, dofManager, localMatrix, rhs, solution, setSparsity ); + + // with the dof numbering finalized, build the per-dof labels driving the + // stencilFlag-guided three-level MGR reduction + computeMgrPointMarkers( domain, dofManager ); +} + +void SinglePhaseMixedMFD::computeMgrPointMarkers( DomainPartition const & domain, + DofManager const & dofManager ) +{ + GEOS_MARK_FUNCTION; + + string const faceDofKey = dofManager.getKey( mixedMimetic::faceMassFlux::key() ); + string const elemDofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); + globalIndex const rankOffset = dofManager.rankOffset(); + + // when the selected inner product is itself TPFA, the effective operator is diagonal in + // every cell regardless of the stencil activation flag, and all face-flux rows belong to + // the exactly-eliminable class + NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); + MixedMimeticDiscretizationManager const & mmManager = numericalMethodManager.getMixedMimeticDiscretizationManager(); + MixedMimeticDiscretization const & discretization = mmManager.getMixedMimeticDiscretization( m_discretizationName ); + MimeticInnerProductBase const & mimeticInnerProductBase = + discretization.getReference< MimeticInnerProductBase >( MixedMimeticDiscretization::viewKeyStruct::innerProductString() ); + bool const effectiveTpfa = dynamic_cast< TPFAInnerProduct const * >( &mimeticInnerProductBase ) != nullptr; + + array1d< integer > & pointMarkers = m_linearSolverParameters.get().mgr.customPointMarkers; + pointMarkers.resize( dofManager.numLocalDofs() ); + arrayView1d< integer > const markers = pointMarkers.toView(); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel const & mesh, + string_array const & regionNames ) + { + FaceManager const & faceManager = mesh.getFaceManager(); + ElementRegionManager const & elemManager = mesh.getElemManager(); + + ElementRegionManager::ElementViewAccessor< arrayView1d< integer const > > const stencilFlagAccessor = + elemManager.constructArrayViewAccessor< integer, 1 >( mixedMimetic::stencilFlag::key() ); + ElementRegionManager::ElementViewConst< arrayView1d< integer const > > const stencilFlag = + stencilFlagAccessor.toNestedViewConst(); + + arrayView1d< globalIndex const > const faceDofNumber = + faceManager.getReference< array1d< globalIndex > >( faceDofKey ); + arrayView1d< integer const > const faceGhostRank = faceManager.ghostRank(); + arrayView2d< localIndex const > const elemRegionList = faceManager.elementRegionList(); + arrayView2d< localIndex const > const elemSubRegionList = faceManager.elementSubRegionList(); + arrayView2d< localIndex const > const elemList = faceManager.elementList(); + SortedArrayView< localIndex const > const regionFilter = m_regionFilter.toViewConst(); + + // face-flux dofs: label 0 if every adjacent target cell is TPFA-compatible (the + // assembled flux row is exactly diagonal), label 1 otherwise + forAll< parallelHostPolicy >( faceManager.size(), [=]( localIndex const kf ) + { + if( faceGhostRank[kf] >= 0 || faceDofNumber[kf] < 0 ) + { + return; + } + integer label = 0; + if( !effectiveTpfa ) + { + for( integer k = 0; k < elemRegionList.size( 1 ); ++k ) + { + localIndex const er = elemRegionList[kf][k]; + localIndex const esr = elemSubRegionList[kf][k]; + localIndex const ei = elemList[kf][k]; + if( er >= 0 && esr >= 0 && ei >= 0 && regionFilter.contains( er ) ) + { + label = LvArray::math::max( label, stencilFlag[er][esr][ei] ); + } + } + } + markers[faceDofNumber[kf] - rankOffset] = label; + } ); + + // cell-pressure dofs: label 2 + elemManager.forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase const & subRegion ) + { + arrayView1d< globalIndex const > const elemDofNumber = + subRegion.getReference< array1d< globalIndex > >( elemDofKey ); + arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); + + forAll< parallelHostPolicy >( subRegion.size(), [=]( localIndex const ei ) + { + if( elemGhostRank[ei] < 0 ) + { + markers[elemDofNumber[ei] - rankOffset] = 2; + } + } ); + } ); + } ); +} + void SinglePhaseMixedMFD::setupDofs( DomainPartition const & GEOS_UNUSED_PARAM( domain ), DofManager & dofManager ) const { diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp index 93a84212311..f483076e140 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp @@ -83,6 +83,14 @@ class SinglePhaseMixedMFD : public SinglePhaseBase setupDofs( DomainPartition const & domain, DofManager & dofManager ) const override; + virtual void + setupSystem( DomainPartition & domain, + DofManager & dofManager, + CRSMatrix< real64, globalIndex > & localMatrix, + ParallelVector & rhs, + ParallelVector & solution, + bool const setSparsity = true ) override; + virtual void applyBoundaryConditions( real64 const time_n, real64 const dt, @@ -174,6 +182,17 @@ class SinglePhaseMixedMFD : public SinglePhaseBase */ void computeGlobalAdaptationIndicators( DomainPartition & domain ); + /** + * @brief Build the per-dof labels used by the stencilFlag-guided three-level MGR strategy: + * 0 = face flux with exactly-diagonal row (all adjacent cells TPFA-compatible), + * 1 = face flux adjacent to at least one MFD-compatible cell, + * 2 = cell pressure. + * @param[in] domain the domain + * @param[in] dofManager the dof manager (dof numbers must be finalized) + */ + void computeMgrPointMarkers( DomainPartition const & domain, + DofManager const & dofManager ); + /// relative tolerance used in the mass matrix computations real64 m_areaRelTol; From 625ac547bf65d42db63ebc5d19cc86c3e4501d52 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 31 Jul 2026 18:51:24 -0700 Subject: [PATCH 15/64] wip: locally condensable TPFA facet-dof --- .../MixedMimeticDiscretization.cpp | 5 + .../MixedMimeticDiscretization.hpp | 5 + .../mixedMimetic/MixedMimeticFields.hpp | 9 + .../adaptivity/GlobalAdaptationKernels.hpp | 48 ++ .../fluidFlow/SinglePhaseMixedMFD.cpp | 103 +++-- .../fluidFlow/SinglePhaseMixedMFD.hpp | 6 + .../SinglePhaseMixedMFDKernels.hpp | 415 +++++++++++++++++- 7 files changed, 536 insertions(+), 55 deletions(-) diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp index 55c325161ae..25b77c66d79 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp @@ -99,6 +99,11 @@ void MixedMimeticDiscretization::initializePostInitialConditionsPreSubGroups() setRestartFlags( dataRepository::RestartFlags::NO_WRITE ); } +bool MixedMimeticDiscretization::isTpfaInnerProduct() const +{ + return m_innerProductType == mimeticInnerProduct::MimeticInnerProductTypeStrings::TPFA; +} + MixedMimeticDiscretization::CatalogInterface::CatalogType & MixedMimeticDiscretization::getCatalog() { diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp index 0968975534c..debf3b14f74 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp @@ -89,6 +89,11 @@ class MixedMimeticDiscretization : public dataRepository::Group */ bool isAdaptive() const { return m_isAdaptive == 1; } + /** + * @brief @return Whether the selected inner product is the (diagonal) TPFA inner product + */ + bool isTpfaInnerProduct() const; + /** * @brief @return The residual tolerance used in the marking criterion */ diff --git a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp index 20c346ecd46..5485b8a908b 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp @@ -65,6 +65,15 @@ DECLARE_FIELD( consistencyIndicator, WRITE_AND_READ, "Residual-based consistency indicator per cell (Global Adaptation)" ); +DECLARE_FIELD( faceStencilLabel, + "faceStencilLabel", + array1d< integer >, + 0, + NOPLOT, + NO_WRITE, + "Face classification for the adaptive solver (0 = all adjacent cells TPFA-compatible: the flux is condensed " + "into a two-point expression; 1 = adjacent to at least one MFD-compatible cell: the flux stays a live unknown)" ); + DECLARE_FIELD( faceResidual, "faceResidual", array1d< real64 >, diff --git a/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp b/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp index 78bae80d6f6..9989bc69531 100644 --- a/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp +++ b/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp @@ -174,6 +174,54 @@ struct FaceFluxProjectionKernel } }; +/******************************** FaceLabelKernel ********************************/ + +/** + * @class FaceLabelKernel + * @brief Classify the faces from the cell-wise stencil activation flags: a face whose + * adjacent target cells are all TPFA-compatible gets label 0 (its constitutive row + * is exactly diagonal and the flux can be condensed into a two-point expression); + * a face adjacent to at least one MFD-compatible cell gets label 1 (live unknown). + * When the selected inner product is itself TPFA, the effective operator is diagonal + * everywhere and all faces get label 0. + */ +struct FaceLabelKernel +{ + template< typename VIEWTYPE > + using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; + + template< typename POLICY > + static void + launch( localIndex const numFaces, + arrayView2d< localIndex const > const & elemRegionList, + arrayView2d< localIndex const > const & elemSubRegionList, + arrayView2d< localIndex const > const & elemList, + SortedArrayView< localIndex const > const & regionFilter, + ElementViewConst< arrayView1d< integer const > > const & stencilFlag, + bool const effectiveTpfa, + arrayView1d< integer > const & faceStencilLabel ) + { + forAll< POLICY >( numFaces, [=] GEOS_HOST_DEVICE ( localIndex const kf ) + { + integer label = 0; + if( !effectiveTpfa ) + { + for( integer k = 0; k < elemRegionList.size( 1 ); ++k ) + { + localIndex const er = elemRegionList[kf][k]; + localIndex const esr = elemSubRegionList[kf][k]; + localIndex const ei = elemList[kf][k]; + if( er >= 0 && esr >= 0 && ei >= 0 && regionFilter.contains( er ) ) + { + label = LvArray::math::max( label, stencilFlag[er][esr][ei] ); + } + } + } + faceStencilLabel[kf] = label; + } ); + } +}; + /******************************** LocalResidualKernel ********************************/ /** diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index 48d6836becf..30a24538e9e 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -85,6 +85,9 @@ void SinglePhaseMixedMFD::registerDataOnMesh( Group & meshBodies ) // Global Adaptation face residual faceManager.registerField< mixedMimetic::faceResidual >( getName() ); + + // face classification driving the TPFA-face condensation and the MGR labels + faceManager.registerField< mixedMimetic::faceStencilLabel >( getName() ); } } ); } @@ -185,6 +188,7 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d subRegion.getField< mixedMimetic::stencilFlag >().template setValues< parallelDevicePolicy<> >( 1 ); } ); } ); + computeFaceStencilLabels( domain ); return; } @@ -289,6 +293,42 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d GEOS_LOG_RANK_0( GEOS_FMT( "{}: Global Adaptation marked {} / {} cells as MFD-compatible (tolerance = {})", getName(), globalNumMfdCells, globalNumCells, tolerance ) ); } ); + + computeFaceStencilLabels( domain ); +} + +void SinglePhaseMixedMFD::computeFaceStencilLabels( DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + + NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); + MixedMimeticDiscretizationManager const & mmManager = numericalMethodManager.getMixedMimeticDiscretizationManager(); + MixedMimeticDiscretization const & discretization = mmManager.getMixedMimeticDiscretization( m_discretizationName ); + + // with a TPFA inner product the effective operator is diagonal in every cell, + // regardless of the stencil activation flags + bool const effectiveTpfa = discretization.isTpfaInnerProduct(); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & ) + { + FaceManager & faceManager = mesh.getFaceManager(); + ElementRegionManager const & elemManager = mesh.getElemManager(); + + ElementRegionManager::ElementViewAccessor< arrayView1d< integer const > > const stencilFlagAccessor = + elemManager.constructArrayViewAccessor< integer, 1 >( mixedMimetic::stencilFlag::key() ); + + mixedMimeticKernels::FaceLabelKernel:: + launch< parallelDevicePolicy<> >( faceManager.size(), + faceManager.elementRegionList(), + faceManager.elementSubRegionList(), + faceManager.elementList(), + m_regionFilter.toViewConst(), + stencilFlagAccessor.toNestedViewConst(), + effectiveTpfa, + faceManager.getField< mixedMimetic::faceStencilLabel >() ); + } ); } void SinglePhaseMixedMFD::implicitStepSetup( real64 const & time_n, @@ -399,16 +439,6 @@ void SinglePhaseMixedMFD::computeMgrPointMarkers( DomainPartition const & domain string const elemDofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); globalIndex const rankOffset = dofManager.rankOffset(); - // when the selected inner product is itself TPFA, the effective operator is diagonal in - // every cell regardless of the stencil activation flag, and all face-flux rows belong to - // the exactly-eliminable class - NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); - MixedMimeticDiscretizationManager const & mmManager = numericalMethodManager.getMixedMimeticDiscretizationManager(); - MixedMimeticDiscretization const & discretization = mmManager.getMixedMimeticDiscretization( m_discretizationName ); - MimeticInnerProductBase const & mimeticInnerProductBase = - discretization.getReference< MimeticInnerProductBase >( MixedMimeticDiscretization::viewKeyStruct::innerProductString() ); - bool const effectiveTpfa = dynamic_cast< TPFAInnerProduct const * >( &mimeticInnerProductBase ) != nullptr; - array1d< integer > & pointMarkers = m_linearSolverParameters.get().mgr.customPointMarkers; pointMarkers.resize( dofManager.numLocalDofs() ); arrayView1d< integer > const markers = pointMarkers.toView(); @@ -420,42 +450,19 @@ void SinglePhaseMixedMFD::computeMgrPointMarkers( DomainPartition const & domain FaceManager const & faceManager = mesh.getFaceManager(); ElementRegionManager const & elemManager = mesh.getElemManager(); - ElementRegionManager::ElementViewAccessor< arrayView1d< integer const > > const stencilFlagAccessor = - elemManager.constructArrayViewAccessor< integer, 1 >( mixedMimetic::stencilFlag::key() ); - ElementRegionManager::ElementViewConst< arrayView1d< integer const > > const stencilFlag = - stencilFlagAccessor.toNestedViewConst(); - arrayView1d< globalIndex const > const faceDofNumber = faceManager.getReference< array1d< globalIndex > >( faceDofKey ); arrayView1d< integer const > const faceGhostRank = faceManager.ghostRank(); - arrayView2d< localIndex const > const elemRegionList = faceManager.elementRegionList(); - arrayView2d< localIndex const > const elemSubRegionList = faceManager.elementSubRegionList(); - arrayView2d< localIndex const > const elemList = faceManager.elementList(); - SortedArrayView< localIndex const > const regionFilter = m_regionFilter.toViewConst(); + arrayView1d< integer const > const faceStencilLabel = faceManager.getField< mixedMimetic::faceStencilLabel >(); - // face-flux dofs: label 0 if every adjacent target cell is TPFA-compatible (the - // assembled flux row is exactly diagonal), label 1 otherwise + // face-flux dofs: the face classification is the MGR label (0 = exactly-diagonal row) forAll< parallelHostPolicy >( faceManager.size(), [=]( localIndex const kf ) { if( faceGhostRank[kf] >= 0 || faceDofNumber[kf] < 0 ) { return; } - integer label = 0; - if( !effectiveTpfa ) - { - for( integer k = 0; k < elemRegionList.size( 1 ); ++k ) - { - localIndex const er = elemRegionList[kf][k]; - localIndex const esr = elemSubRegionList[kf][k]; - localIndex const ei = elemList[kf][k]; - if( er >= 0 && esr >= 0 && ei >= 0 && regionFilter.contains( er ) ) - { - label = LvArray::math::max( label, stencilFlag[er][esr][ei] ); - } - } - } - markers[faceDofNumber[kf] - rankOffset] = label; + markers[faceDofNumber[kf] - rankOffset] = faceStencilLabel[kf]; } ); // cell-pressure dofs: label 2 @@ -492,8 +499,9 @@ void SinglePhaseMixedMFD::setupDofs( DomainPartition const & GEOS_UNUSED_PARAM( mixedMimetic::faceMassFlux::key(), DofManager::Connector::Elem ); - // cell pressure unknowns: the mass conservation equation couples the cell - // to its own faces only (no cell-to-cell coupling in the mixed form) + // cell pressure unknowns: the TPFA-face condensation writes two-point stencil + // entries directly into the mass conservation rows, so cell-to-cell coupling + // through the faces is required dofManager.addField( viewKeyStruct::elemDofFieldString(), FieldLocation::Elem, 1, @@ -501,7 +509,7 @@ void SinglePhaseMixedMFD::setupDofs( DomainPartition const & GEOS_UNUSED_PARAM( dofManager.addCoupling( viewKeyStruct::elemDofFieldString(), viewKeyStruct::elemDofFieldString(), - DofManager::Connector::None ); + DofManager::Connector::Face ); // coupling between the face fluxes and the cell pressures dofManager.addCoupling( mixedMimetic::faceMassFlux::key(), @@ -563,6 +571,23 @@ void SinglePhaseMixedMFD::assembleFluxTerms( real64 const dt, localMatrix, localRhs ); } ); + + // condensed (label-0) faces: two-point flux contributions to the mass conservation + // rows and one-way closure rows, assembled in a single face-based sweep + singlePhaseMixedMFDKernels:: + TpfaCondensedFluxKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(), + lengthTolerance, + elemDofKey, + faceDofKey, + getName(), + nodeManager, + faceManager, + mesh.getElemManager(), + m_regionFilter.toViewConst(), + dt, + localMatrix, + localRhs ); } ); } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp index f483076e140..5d27ffbb4d9 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp @@ -182,6 +182,12 @@ class SinglePhaseMixedMFD : public SinglePhaseBase */ void computeGlobalAdaptationIndicators( DomainPartition & domain ); + /** + * @brief Classify the faces from the cell marking (0 = condensable TPFA face, 1 = live MFD face). + * @param[in] domain the domain + */ + void computeFaceStencilLabels( DomainPartition & domain ); + /** * @brief Build the per-dof labels used by the stencilFlag-guided three-level MGR strategy: * 0 = face flux with exactly-diagonal row (all adjacent cells TPFA-compatible), diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp index b872715be1a..35e299e6c7a 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp @@ -104,6 +104,7 @@ class ElementBasedAssemblyKernel m_faceFlux( faceManager.getField< fields::mixedMimetic::faceMassFlux >() ), m_bcPres( faceManager.getField< fields::flow::bcPressure >() ), m_isPresBcFace( faceManager.getField< fields::flow::isBoundaryFace >() ), + m_faceStencilLabel( faceManager.getField< fields::mixedMimetic::faceStencilLabel >() ), m_stencilFlag( subRegion.getField< fields::mixedMimetic::stencilFlag >() ), m_elemDens( fluid.density() ), m_dElemDens( fluid.dDensity() ), @@ -133,6 +134,10 @@ class ElementBasedAssemblyKernel /// 1 if the face is a domain-boundary face without a pressure boundary condition (no-flow) integer isNoFlowFace[NUM_FACE]{}; + /// 1 if the face flux is condensed into a two-point expression (all-TPFA neighborhood): + /// its closure row and its divergence contribution are assembled by the face-based kernel + integer isCondensedFace[NUM_FACE]{}; + /// face constitutive residuals (local, outward-oriented) and their derivatives real64 faceResidual[NUM_FACE]{}; real64 dFaceResidual_dFlux[NUM_FACE][NUM_FACE]{}; @@ -174,18 +179,6 @@ class ElementBasedAssemblyKernel { real64 const perm[ 3 ] = { m_elemPerm[ei][0][0], m_elemPerm[ei][0][1], m_elemPerm[ei][0][2] }; - // step 1: adaptive inner product, M = chi * M_mfd + (1 - chi) * M_tpfa - real64 const chi = static_cast< real64 >( m_stencilFlag[ei] ); - mimeticInnerProduct::AdaptiveInnerProduct< IP >::template computeM< NUM_FACE >( m_nodePosition, - m_faceToNodes, - m_elemToFaces[ei], - m_elemCenter[ei], - m_elemVolume[ei], - perm, - m_lengthTolerance, - chi, - stack.massMatrix ); - // step 2: cell mobility scaling of the inverse Darcy coefficient real64 const mob = m_mob[ei]; real64 const dMob_dPres = m_dMob[ei][DerivOffset::dP]; @@ -221,15 +214,39 @@ class ElementBasedAssemblyKernel bool const onBoundary = !( valid0 && valid1 ); stack.isNoFlowFace[i] = ( onBoundary && m_isPresBcFace[kf] == 0 ) ? 1 : 0; + stack.isCondensedFace[i] = ( m_faceStencilLabel[kf] == 0 && stack.isNoFlowFace[i] == 0 ) ? 1 : 0; + } + + // step 1: adaptive inner product, M = chi * M_mfd + (1 - chi) * M_tpfa. + // Only needed if the cell has at least one live (non-condensed, non-no-flow) face: + // cells fully surrounded by TPFA-condensed faces skip the local operator entirely + bool anyLiveFace = false; + for( integer i = 0; i < NUM_FACE; ++i ) + { + anyLiveFace = anyLiveFace || ( stack.isNoFlowFace[i] == 0 && stack.isCondensedFace[i] == 0 ); + } + if( anyLiveFace ) + { + real64 const chi = static_cast< real64 >( m_stencilFlag[ei] ); + mimeticInnerProduct::AdaptiveInnerProduct< IP >::template computeM< NUM_FACE >( m_nodePosition, + m_faceToNodes, + m_elemToFaces[ei], + m_elemCenter[ei], + m_elemVolume[ei], + perm, + m_lengthTolerance, + chi, + stack.massMatrix ); } // step 4: constitutive (Darcy closure) rows: // invMob * sum_j M_ij (sigma_j m_j) - p_K + pi_f + rho * ( g_K - g_f ) = 0 // the interface pressure pi_f is dropped on interior faces (it cancels upon global - // orientation-signed assembly) and replaced by the prescribed value on boundary faces + // orientation-signed assembly) and replaced by the prescribed value on boundary faces; + // condensed faces are assembled by the face-based two-point kernel instead for( integer i = 0; i < NUM_FACE; ++i ) { - if( stack.isNoFlowFace[i] == 1 ) + if( stack.isNoFlowFace[i] == 1 || stack.isCondensedFace[i] == 1 ) { continue; } @@ -255,9 +272,16 @@ class ElementBasedAssemblyKernel } } - // step 5: divergence of the face fluxes for the cell-centered mass conservation equation + // step 5: divergence of the face fluxes for the cell-centered mass conservation equation. + // No-flow faces carry m = 0 exactly and condensed faces are handled by the face-based + // kernel (two-point expression in the pressures), so only live faces contribute here for( integer i = 0; i < NUM_FACE; ++i ) { + if( stack.isNoFlowFace[i] == 1 || stack.isCondensedFace[i] == 1 ) + { + stack.dDivFlux_dFlux[i] = 0.0; + continue; + } stack.divFlux += m_dt * localFlux[i]; stack.dDivFlux_dFlux[i] = m_dt * stack.orientation[i]; } @@ -282,10 +306,11 @@ class ElementBasedAssemblyKernel } // step 2: face-centered constitutive rows, signed by the global face orientation + // (condensed faces are assembled by the face-based two-point kernel) for( integer i = 0; i < NUM_FACE; ++i ) { localIndex const kf = m_elemToFaces[ei][i]; - if( m_faceGhostRank[kf] >= 0 ) + if( m_faceGhostRank[kf] >= 0 || stack.isCondensedFace[i] == 1 ) { continue; } @@ -383,6 +408,7 @@ class ElementBasedAssemblyKernel arrayView1d< real64 const > const m_faceFlux; arrayView1d< real64 const > const m_bcPres; arrayView1d< integer const > const m_isPresBcFace; + arrayView1d< integer const > const m_faceStencilLabel; /// adaptive stencil flag arrayView1d< integer const > const m_stencilFlag; @@ -445,6 +471,363 @@ class ElementBasedAssemblyKernelFactory }; +/******************************** TpfaCondensedFluxKernel ********************************/ + +/** + * @class TpfaCondensedFluxKernel + * @brief Face-based assembly of the condensed (label-0) fluxes: on faces whose adjacent + * cells are all TPFA-compatible, the closure row is exactly diagonal and the flux + * can be substituted into the mass conservation rows as the standard two-point + * expression m_f = pot / A with A = sum_K invMob_K / t_K. + * + * The kernel writes: + * - the TPFA two-point contribution dt * m_f (+full Jacobian wrt the two cell pressures) + * into both adjacent cell rows (the pressure block gains standard TPFA stencil entries); + * - the closure row A * m_f - pot = 0 as a one-way *definition* of the face flux + * (no other equation references m_f, so the flux dof decouples from the Krylov + * iteration and reduces the live system to cells + MFD faces). + * + * The scheme is algebraically identical to the fully-coupled assembly: the closure row is + * unchanged, only its exact substitution into the divergence is performed ahead of time. + */ +class TpfaCondensedFluxKernel +{ +public: + + using DerivOffset = constitutive::singlefluid::DerivativeOffsetC< 0 >; + + template< typename VIEWTYPE > + using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; + + using FlowAccessors = + StencilAccessors< fields::flow::pressure, + fields::flow::gravityCoefficient, + fields::flow::mobility, + fields::flow::dMobility >; + + using FluidAccessors = + StencilMaterialAccessors< constitutive::SingleFluidBase, + fields::singlefluid::density, + fields::singlefluid::dDensity >; + + using PermeabilityAccessors = + StencilMaterialAccessors< constitutive::PermeabilityBase, + fields::permeability::permeability >; + + using DofNumberAccessor = ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > >; + + using GhostRankAccessor = ElementRegionManager::ElementViewAccessor< arrayView1d< integer const > >; + + TpfaCondensedFluxKernel( globalIndex const rankOffset, + real64 const & lengthTolerance, + string const faceDofKey, + NodeManager const & nodeManager, + FaceManager const & faceManager, + DofNumberAccessor const & elemDofNumber, + DofNumberAccessor const & elemLocalToGlobal, + GhostRankAccessor const & elemGhostRank, + ElementRegionManager::ElementViewAccessor< arrayView2d< real64 const > > const & elemCenter, + FlowAccessors const & flowAccessors, + FluidAccessors const & fluidAccessors, + PermeabilityAccessors const & permAccessors, + SortedArrayView< localIndex const > const & regionFilter, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + : + m_rankOffset( rankOffset ), + m_lengthTolerance( lengthTolerance ), + m_dt( dt ), + m_faceGhostRank( faceManager.ghostRank() ), + m_faceDofNumber( faceManager.getReference< array1d< globalIndex > >( faceDofKey ) ), + m_faceToNodes( faceManager.nodeList().toViewConst() ), + m_faceGravCoef( faceManager.getField< fields::flow::gravityCoefficient >() ), + m_faceFlux( faceManager.getField< fields::mixedMimetic::faceMassFlux >() ), + m_bcPres( faceManager.getField< fields::flow::bcPressure >() ), + m_isPresBcFace( faceManager.getField< fields::flow::isBoundaryFace >() ), + m_faceStencilLabel( faceManager.getField< fields::mixedMimetic::faceStencilLabel >() ), + m_elemRegionList( faceManager.elementRegionList() ), + m_elemSubRegionList( faceManager.elementSubRegionList() ), + m_elemList( faceManager.elementList() ), + m_nodePosition( nodeManager.referencePosition() ), + m_regionFilter( regionFilter ), + m_elemDofNumber( elemDofNumber.toNestedViewConst() ), + m_elemLocalToGlobal( elemLocalToGlobal.toNestedViewConst() ), + m_elemGhostRank( elemGhostRank.toNestedViewConst() ), + m_elemCenter( elemCenter.toNestedViewConst() ), + m_pres( flowAccessors.get( fields::flow::pressure {} ) ), + m_elemGravCoef( flowAccessors.get( fields::flow::gravityCoefficient {} ) ), + m_mob( flowAccessors.get( fields::flow::mobility {} ) ), + m_dMob( flowAccessors.get( fields::flow::dMobility {} ) ), + m_dens( fluidAccessors.get( fields::singlefluid::density {} ) ), + m_dDens( fluidAccessors.get( fields::singlefluid::dDensity {} ) ), + m_elemPerm( permAccessors.get( fields::permeability::permeability {} ) ), + m_localMatrix( localMatrix ), + m_localRhs( localRhs ) + {} + + GEOS_HOST_DEVICE + void compute( localIndex const kf ) const + { + if( m_faceStencilLabel[kf] != 0 ) + { + return; + } + + // gather the (at most two) adjacent target cells + localIndex er[2]{}, esr[2]{}, ei[2]{}; + integer numElems = 0; + for( integer k = 0; k < m_elemRegionList.size( 1 ); ++k ) + { + localIndex const erk = m_elemRegionList[kf][k]; + localIndex const esrk = m_elemSubRegionList[kf][k]; + localIndex const eik = m_elemList[kf][k]; + if( erk >= 0 && esrk >= 0 && eik >= 0 && m_regionFilter.contains( erk ) ) + { + er[numElems] = erk; esr[numElems] = esrk; ei[numElems] = eik; + numElems++; + } + } + if( numElems == 0 ) + { + return; + } + + bool const onBoundary = ( numElems == 1 ); + if( onBoundary && m_isPresBcFace[kf] == 0 ) + { + // no-flow face: the identity row m_f = 0 is assembled by the cell-based kernel + return; + } + + // global orientation: the face points out of the adjacent cell with the smallest + // global element index (must match the cell-based kernel and be rank-invariant) + if( numElems == 2 && + m_elemLocalToGlobal[er[1]][esr[1]][ei[1]] < m_elemLocalToGlobal[er[0]][esr[0]][ei[0]] ) + { + localIndex tmp; + tmp = er[0]; er[0] = er[1]; er[1] = tmp; + tmp = esr[0]; esr[0] = esr[1]; esr[1] = tmp; + tmp = ei[0]; ei[0] = ei[1]; ei[1] = tmp; + } + + // face geometry (same construction as TPFAInnerProduct::computeM) + real64 const areaTolerance = m_lengthTolerance * m_lengthTolerance; + real64 const weightTolerance = 1e-30 * m_lengthTolerance; + + real64 faceCenter[3], faceNormalRef[3]; + real64 const faceArea = + computationalGeometry::centroid_3DPolygon( m_faceToNodes[kf], + m_nodePosition, + faceCenter, + faceNormalRef, + areaTolerance ); + + // per-side one-sided coefficients: a_K = invMob_K / t_K and gravity terms + real64 a[2]{}, dA_dp[2]{}, grav[2]{}, dGrav_dp[2]{}; + for( integer k = 0; k < numElems; ++k ) + { + real64 faceNormal[3] = { faceNormalRef[0], faceNormalRef[1], faceNormalRef[2] }; + real64 cellToFaceVec[3]; + mimeticInnerProduct::MimeticInnerProductHelpers::computeCellToFacetVector( cellToFaceVec, + faceCenter, + m_elemCenter[er[k]][esr[k]][ei[k]] ); + mimeticInnerProduct::MimeticInnerProductHelpers::orientNormalOutward( cellToFaceVec, faceNormal ); + real64 const c2fDistance = LvArray::tensorOps::normalize< 3 >( cellToFaceVec ); + + real64 const perm[3] = { m_elemPerm[er[k]][esr[k]][ei[k]][0][0], + m_elemPerm[er[k]][esr[k]][ei[k]][0][1], + m_elemPerm[er[k]][esr[k]][ei[k]][0][2] }; + real64 faceConormal[3]; + LvArray::tensorOps::hadamardProduct< 3 >( faceConormal, perm, faceNormal ); + + real64 Tii = LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceConormal ) * faceArea / c2fDistance; + Tii = LvArray::math::max( Tii, weightTolerance ); + real64 const t = LvArray::math::abs( Tii ); + + real64 const mob = m_mob[er[k]][esr[k]][ei[k]]; + real64 const invMob = 1.0 / mob; + real64 const dInvMob = -m_dMob[er[k]][esr[k]][ei[k]][DerivOffset::dP] * invMob * invMob; + + a[k] = invMob / t; + dA_dp[k] = dInvMob / t; + + real64 const gravCoefDif = m_elemGravCoef[er[k]][esr[k]][ei[k]] - m_faceGravCoef[kf]; + grav[k] = m_dens[er[k]][esr[k]][ei[k]][0] * gravCoefDif; + dGrav_dp[k] = m_dDens[er[k]][esr[k]][ei[k]][0][DerivOffset::dP] * gravCoefDif; + } + + // assembled closure: A * m_f = pot, + // interior: pot = (p_L - p_R) - (grav_L - grav_R) + // boundary: pot = p_L - pi_bc - grav_L + real64 const A = ( numElems == 2 ) ? a[0] + a[1] : a[0]; + real64 const pL = m_pres[er[0]][esr[0]][ei[0]]; + real64 const pR = ( numElems == 2 ) ? m_pres[er[1]][esr[1]][ei[1]] : m_bcPres[kf]; + real64 const pot = ( pL - pR ) - grav[0] + ( ( numElems == 2 ) ? grav[1] : 0.0 ); + + real64 const mFlux = pot / A; + real64 const dPot_dp[2] = { 1.0 - dGrav_dp[0], -1.0 + dGrav_dp[1] }; + real64 dFlux_dp[2]; + for( integer k = 0; k < numElems; ++k ) + { + dFlux_dp[k] = ( dPot_dp[k] - mFlux * dA_dp[k] ) / A; + } + + globalIndex elemDofCols[2]; + for( integer k = 0; k < numElems; ++k ) + { + elemDofCols[k] = m_elemDofNumber[er[k]][esr[k]][ei[k]]; + } + + // 1) two-point contributions to the mass conservation rows: +dt*m to L, -dt*m to R + for( integer k = 0; k < numElems; ++k ) + { + if( m_elemGhostRank[er[k]][esr[k]][ei[k]] >= 0 ) + { + continue; + } + real64 const sign = ( k == 0 ) ? 1.0 : -1.0; + localIndex const row = m_elemDofNumber[er[k]][esr[k]][ei[k]] - m_rankOffset; + + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[row], sign * m_dt * mFlux ); + + real64 vals[2]; + for( integer j = 0; j < numElems; ++j ) + { + vals[j] = sign * m_dt * dFlux_dp[j]; + } + m_localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic >( row, + elemDofCols, + vals, + numElems ); + } + + // 2) one-way closure row defining the (decoupled) face flux: A * m_f - pot = 0 + if( m_faceGhostRank[kf] < 0 ) + { + localIndex const row = m_faceDofNumber[kf] - m_rankOffset; + globalIndex const faceDofCol = m_faceDofNumber[kf]; + real64 const mCurrent = m_faceFlux[kf]; + + m_localRhs[row] += A * mCurrent - pot; + + m_localMatrix.addToRow< serialAtomic >( row, &faceDofCol, &A, 1 ); + + real64 vals[2]; + for( integer k = 0; k < numElems; ++k ) + { + vals[k] = dA_dp[k] * mCurrent - dPot_dp[k]; + } + m_localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( row, + elemDofCols, + vals, + numElems ); + } + } + + template< typename POLICY, typename KERNEL_TYPE > + static void + launch( localIndex const numFaces, + KERNEL_TYPE const & kernelComponent ) + { + GEOS_MARK_FUNCTION; + + forAll< POLICY >( numFaces, [=] GEOS_HOST_DEVICE ( localIndex const kf ) + { + kernelComponent.compute( kf ); + } ); + } + +protected: + + globalIndex const m_rankOffset; + real64 const m_lengthTolerance; + real64 const m_dt; + + /// face data + arrayView1d< integer const > const m_faceGhostRank; + arrayView1d< globalIndex const > const m_faceDofNumber; + ArrayOfArraysView< localIndex const > const m_faceToNodes; + arrayView1d< real64 const > const m_faceGravCoef; + arrayView1d< real64 const > const m_faceFlux; + arrayView1d< real64 const > const m_bcPres; + arrayView1d< integer const > const m_isPresBcFace; + arrayView1d< integer const > const m_faceStencilLabel; + arrayView2d< localIndex const > const m_elemRegionList; + arrayView2d< localIndex const > const m_elemSubRegionList; + arrayView2d< localIndex const > const m_elemList; + arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const m_nodePosition; + SortedArrayView< localIndex const > const m_regionFilter; + + /// element data (cross-region accessors) + ElementViewConst< arrayView1d< globalIndex const > > const m_elemDofNumber; + ElementViewConst< arrayView1d< globalIndex const > > const m_elemLocalToGlobal; + ElementViewConst< arrayView1d< integer const > > const m_elemGhostRank; + ElementViewConst< arrayView2d< real64 const > > const m_elemCenter; + ElementViewConst< arrayView1d< real64 const > > const m_pres; + ElementViewConst< arrayView1d< real64 const > > const m_elemGravCoef; + ElementViewConst< arrayView1d< real64 const > > const m_mob; + ElementViewConst< arrayView2d< real64 const > > const m_dMob; + ElementViewConst< arrayView2d< real64 const > > const m_dens; + ElementViewConst< arrayView3d< real64 const > > const m_dDens; + ElementViewConst< arrayView3d< real64 const > > const m_elemPerm; + + /// system views + CRSMatrixView< real64, globalIndex const > const m_localMatrix; + arrayView1d< real64 > const m_localRhs; + +}; + +/** + * @class TpfaCondensedFluxKernelFactory + */ +class TpfaCondensedFluxKernelFactory +{ +public: + + template< typename POLICY > + static void + createAndLaunch( globalIndex const rankOffset, + real64 const lengthTolerance, + string const elemDofKey, + string const faceDofKey, + string const solverName, + NodeManager const & nodeManager, + FaceManager const & faceManager, + ElementRegionManager const & elemManager, + SortedArrayView< localIndex const > const & regionFilter, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + TpfaCondensedFluxKernel::DofNumberAccessor elemDofNumber = + elemManager.constructArrayViewAccessor< globalIndex, 1 >( elemDofKey ); + elemDofNumber.setName( solverName + "/accessors/" + elemDofKey ); + + TpfaCondensedFluxKernel::DofNumberAccessor const elemLocalToGlobal = + elemManager.constructArrayViewAccessor< globalIndex, 1 >( ObjectManagerBase::viewKeyStruct::localToGlobalMapString() ); + + TpfaCondensedFluxKernel::GhostRankAccessor const elemGhostRank = + elemManager.constructArrayViewAccessor< integer, 1 >( ObjectManagerBase::viewKeyStruct::ghostRankString() ); + + ElementRegionManager::ElementViewAccessor< arrayView2d< real64 const > > const elemCenter = + elemManager.constructViewAccessor< array2d< real64 >, arrayView2d< real64 const > >( ElementSubRegionBase::viewKeyStruct::elementCenterString() ); + + TpfaCondensedFluxKernel::FlowAccessors flowAccessors( elemManager, solverName ); + TpfaCondensedFluxKernel::FluidAccessors fluidAccessors( elemManager, solverName ); + TpfaCondensedFluxKernel::PermeabilityAccessors permAccessors( elemManager, solverName ); + + TpfaCondensedFluxKernel kernel( rankOffset, lengthTolerance, faceDofKey, + nodeManager, faceManager, + elemDofNumber, elemLocalToGlobal, elemGhostRank, elemCenter, + flowAccessors, fluidAccessors, permAccessors, + regionFilter, dt, localMatrix, localRhs ); + + TpfaCondensedFluxKernel::launch< POLICY >( faceManager.size(), kernel ); + } + +}; + /******************************** ResidualNormKernel ********************************/ /** From 584958b1d683ab43b3f3fda113c46c435c2801e7 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Sun, 2 Aug 2026 17:39:08 -0700 Subject: [PATCH 16/64] wip: leaner MGR --- .../mgrStrategies/SinglePhaseMixedMFD.hpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp index 5e3a012b148..c845f8712e9 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp @@ -52,9 +52,10 @@ namespace mgr * elimination exactly, and the Galerkin (RAP) coarse grid is the exact Schur complement. * The cost of this level is proportional to the TPFA fraction selected by the * residual tolerance, mirroring the sparsity-reduction metric of the adaptive scheme. - * 2. Level 1: F-points = MFD face fluxes (label 1), relaxed with a single BoomerAMG - * V-cycle on the (SPD) MFD flux block; Jacobi prolongation, injection restriction, - * Galerkin (RAP) coarse grid approximating the pressure Schur complement. + * 2. Level 1: F-points = MFD face fluxes (label 1), relaxed with symmetric Gauss-Seidel + * sweeps on the (SPD, well-conditioned) MFD flux block; Jacobi prolongation, + * injection restriction, Galerkin (RAP) coarse grid approximating the pressure + * Schur complement. * 3. Coarsest level: cell-pressure system solved with BoomerAMG. * 4. Global smoother: none. The Krylov solver is (F)GMRES. */ @@ -84,9 +85,10 @@ class SinglePhaseMixedMFD : public MGRStrategyBase< 2 > m_levelCoarseGridMethod[0] = MGRCoarseGridMethod::galerkin; m_levelGlobalSmootherType[0] = MGRGlobalSmootherType::none; - // Level 1: genuine multilevel relaxation on the MFD flux block - m_levelFRelaxType[1] = MGRFRelaxationType::amgVCycle; - m_levelFRelaxIters[1] = 1; + // Level 1: SGS sweeps on the well-conditioned SPD flux block outperform an AMG + // V-cycle; Jacobi prolongation avoids the setup cost of approximateInverse + m_levelFRelaxType[1] = MGRFRelaxationType::hybridSymmetricGaussSeidel; + m_levelFRelaxIters[1] = 3; m_levelInterpType[1] = MGRInterpolationType::jacobi; m_levelRestrictType[1] = MGRRestrictionType::injection; m_levelCoarseGridMethod[1] = MGRCoarseGridMethod::galerkin; @@ -104,8 +106,11 @@ class SinglePhaseMixedMFD : public MGRStrategyBase< 2 > { setReduction( precond, mgrData ); - // Configure the BoomerAMG solver used as mgr coarse solver for the pressure Schur complement + // Configure the BoomerAMG solver used as mgr coarse solver for the pressure Schur + // complement. Two V-cycles per MGR application: the coarse solve accuracy governs the + // outer FGMRES iteration count (a single cycle leaves the reduction quality unused) setPressureAMG( mgrData.coarseSolver ); + GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetMaxIter( mgrData.coarseSolver.ptr, 2 ) ); } }; From d3c5c1c1653501ea91a90ceb2c9f8c436689ce1a Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Mon, 3 Aug 2026 18:10:15 -0700 Subject: [PATCH 17/64] wip: fix qTPFA and BdVLM --- .../BdVLMInnerProduct.hpp | 5 +- .../QuasiTPFAInnerProduct.hpp | 8 +- .../SimpleInnerProduct.hpp | 3 - .../unitTests/testMimeticInnerProducts.cpp | 525 +++++++++++++++++- 4 files changed, 511 insertions(+), 30 deletions(-) diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp index f33449bd780..25114e7b705 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp @@ -253,7 +253,6 @@ BdVLMInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITIO real64 work_numFacesByDim[ NF ][ 3 ] = {{ 0 }}; real64 work_dimByNumFaces[ 3 ][ NF ] = {{ 0 }}; real64 work_numFacesByNumFaces[ NF ][ NF ] = {{ 0 }}; - // real64 tmp_numFacesByNumFaces[ NF ][ NF ] = {{ 0 }}; // 0) assemble full coefficient tensor from principal axis/components MimeticInnerProductHelpers::makeFullTensor( elemPerm, permMat ); @@ -329,8 +328,8 @@ BdVLMInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITIO work_dimByNumFaces // tmp ); - // 8) M = M0 + gamma * P_N - real64 const gamma = 1.0 / static_cast< real64 >( NF ); + // 8) M = M0 + gamma * P_N, gamma = 2/NF * trace(M0) (mirrors stabCoef in compute()) + real64 const gamma = 2.0 / NF * LvArray::tensorOps::trace< NF >( M ); LvArray::tensorOps::scaledAdd< NF, NF >( M, work_numFacesByNumFaces, -gamma ); // convert to flux-based inner product: M_flux = A^{-1} M_vel A^{-1} diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp index d966871f120..a3b873e1e50 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp @@ -184,15 +184,15 @@ QuasiTPFAInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POS LvArray::tensorOps::Rij_eq_AikBjk< 3, NF, 3 >( workNK, K, N ); LvArray::tensorOps::Rij_eq_AikBkj< NF, NF, 3 >( W, N, workNK ); - // 5) build Q from C (orthonormal basis of consistency space) + // 5) build Q from N so the stabilization annihilates range(N) (=> M N K = C) real64 q0[ NF ], q1[ NF ], q2[ NF ]; real64 Qmat[ NF ][ 3 ]; for( localIndex i = 0; i < NF; ++i ) { - q0[i] = C[i][0]; - q1[i] = C[i][1]; - q2[i] = C[i][2]; + q0[i] = N[i][0]; + q1[i] = N[i][1]; + q2[i] = N[i][2]; } MimeticInnerProductHelpers::orthonormalize< NF >( q0, q1, q2, Qmat ); diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp index e3bd12e52a1..2283dafc213 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp @@ -23,9 +23,6 @@ #include "finiteVolume/mimeticInnerProducts/MimeticInnerProductBase.hpp" #include "finiteVolume/mimeticInnerProducts/MimeticInnerProductHelpers.hpp" #include "mesh/utilities/ComputationalGeometry.hpp" -#include -#include - namespace geos { diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp index cf64a4a869f..c403693464d 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp @@ -24,8 +24,6 @@ #include "mainInterface/initialization.hpp" #include "mesh/FaceManager.hpp" #include "mesh/utilities/ComputationalGeometry.hpp" -#include -#include // TPL includes #include @@ -617,9 +615,10 @@ void makeCube( array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodePosition, // reference matrix (MRST / MATLAB) if( ipType == InnerProductType::TPFA || - ipType == InnerProductType::QUASI_TPFA ) + ipType == InnerProductType::QUASI_TPFA || + ipType == InnerProductType::BDVLM ) { - // diagonal + // these inner products reduce to TPFA on the unit cube: M = inv(T) = 0.5 I transMatrixRef( 0, 0 ) = 0.5; transMatrixRef( 1, 1 ) = 0.5; transMatrixRef( 2, 2 ) = 0.5; @@ -627,23 +626,22 @@ void makeCube( array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodePosition, transMatrixRef( 4, 4 ) = 0.5; transMatrixRef( 5, 5 ) = 0.5; } - else if( ipType == InnerProductType::SIMPLE || - ipType == InnerProductType::BDVLM ) + else if( ipType == InnerProductType::SIMPLE ) { // SIMPLE = TPFA + stabilization - transMatrixRef( 0, 0 ) = 0.333333333333; - transMatrixRef( 1, 1 ) = 0.333333333333; - transMatrixRef( 2, 2 ) = 0.333333333333; - transMatrixRef( 3, 3 ) = 0.333333333333; - transMatrixRef( 4, 4 ) = 0.333333333333; - transMatrixRef( 5, 5 ) = 0.333333333333; - - transMatrixRef( 0, 1 ) = -0.166666666666; - transMatrixRef( 1, 0 ) = -0.166666666666; - transMatrixRef( 2, 3 ) = -0.166666666666; - transMatrixRef( 3, 2 ) = -0.166666666666; - transMatrixRef( 4, 5 ) = -0.166666666666; - transMatrixRef( 5, 4 ) = -0.166666666666; + transMatrixRef( 0, 0 ) = 1.0 / 3.0; + transMatrixRef( 1, 1 ) = 1.0 / 3.0; + transMatrixRef( 2, 2 ) = 1.0 / 3.0; + transMatrixRef( 3, 3 ) = 1.0 / 3.0; + transMatrixRef( 4, 4 ) = 1.0 / 3.0; + transMatrixRef( 5, 5 ) = 1.0 / 3.0; + + transMatrixRef( 0, 1 ) = -1.0 / 6.0; + transMatrixRef( 1, 0 ) = -1.0 / 6.0; + transMatrixRef( 2, 3 ) = -1.0 / 6.0; + transMatrixRef( 3, 2 ) = -1.0 / 6.0; + transMatrixRef( 4, 5 ) = -1.0 / 6.0; + transMatrixRef( 5, 4 ) = -1.0 / 6.0; } } @@ -1459,7 +1457,8 @@ static inline void computeDistortedVolumeAndCenter( array2d< real64, nodes::REFE center[1] += X[a][1]; center[2] += X[a][2]; } - vol = hexahedronVolume( X ); + // node ordering is axis-swapped w.r.t. hexahedronVolume's convention -> negative sign + vol = std::fabs( hexahedronVolume( X ) ); center[0] /= 8.0; center[1] /= 8.0; center[2] /= 8.0; } @@ -1754,6 +1753,492 @@ TEST( MimeticIP_Linear, Distortion_NonPlanar_LinearPressure ) } } +//======================== Linear Pressure Recovery Test (mixed form, computeM) ============================= +// Same two-cell setup as the hybrid test above, but solving the mixed saddle-point system +// (M q = p_C 1 - pi per cell, mass conservation, interface flux continuity) built from computeM. + +static constexpr real64 mixed_consistency_tol = 1e-13; + +template< int NF > +static void computeM_dispatch( int ipKind, + array2d< real64, nodes::REFERENCE_POSITION_PERM > const & node, + FaceManager::NodeMapType const & faceTonode, + array1d< localIndex > const & elemToface, + arraySlice1d< real64 const > const & center, + real64 const vol, + real64 const (&Kvec)[3], + real64 const ltol, + arraySlice2d< real64 > const & M ) +{ + if( ipKind == InnerProductType::TPFA ) + { + TPFAInnerProduct::computeM< NF >( node.toViewConst(), faceTonode.toViewConst(), elemToface.toSliceConst(), + center, vol, Kvec, ltol, M ); + } + else if( ipKind == InnerProductType::QUASI_TPFA ) + { + QuasiTPFAInnerProduct::computeM< NF >( node.toViewConst(), faceTonode.toViewConst(), elemToface.toSliceConst(), + center, vol, Kvec, ltol, M ); + } + else if( ipKind == InnerProductType::SIMPLE ) + { + SimpleInnerProduct::computeM< NF >( node.toViewConst(), faceTonode.toViewConst(), elemToface.toSliceConst(), + center, vol, Kvec, ltol, M ); + } + else if( ipKind == InnerProductType::BDVLM ) + { + BdVLMInnerProduct::computeM< NF >( node.toViewConst(), faceTonode.toViewConst(), elemToface.toSliceConst(), + center, vol, Kvec, ltol, M ); + } +} + +// solve a small dense linear system A x = b by Gaussian elimination with partial pivoting +template< int N > +static void solveDense( real64 (& A)[N][N], real64 (& b)[N], real64 (& x)[N] ) +{ + for( int k = 0; k < N; ++k ) + { + int piv = k; + for( int i = k + 1; i < N; ++i ) + { + if( std::abs( A[i][k] ) > std::abs( A[piv][k] ) ) + piv = i; + } + if( piv != k ) + { + for( int j = 0; j < N; ++j ) + std::swap( A[k][j], A[piv][j] ); + std::swap( b[k], b[piv] ); + } + for( int i = k + 1; i < N; ++i ) + { + real64 const f = A[i][k] / A[k][k]; + for( int j = k; j < N; ++j ) + A[i][j] -= f * A[k][j]; + b[i] -= f * b[k]; + } + } + for( int i = N - 1; i >= 0; --i ) + { + real64 s = b[i]; + for( int j = i + 1; j < N; ++j ) + s -= A[i][j] * x[j]; + x[i] = s / A[i][i]; + } +} + +// recover the two cell pressures from the mixed (saddle-point) system based on M and +// compare against the analytical values +template< int NF > +static double computeLinearPressureMixed_error( int ipKind, + DistortionMode mode = DistortionMode::None, + real64 eps = 0.0 ) +{ + array2d< real64, nodes::REFERENCE_POSITION_PERM > node_L; + array2d< real64, nodes::REFERENCE_POSITION_PERM > node_R; + FaceManager::NodeMapType faceTonode_L; + FaceManager::NodeMapType faceTonode_R; + array1d< localIndex > elemToface_L; + array1d< localIndex > elemToface_R; + real64 center_L[3], center_R[3], vol_L = 0, vol_R = 0; + + makeUnitCube( 0.0, node_L, faceTonode_L, elemToface_L, center_L, vol_L ); + makeUnitCube( 1.0, node_R, faceTonode_R, elemToface_R, center_R, vol_R ); + + if( mode == DistortionMode::Planar ) + { + makeDistortedPlanar( node_L, node_R, faceTonode_L, faceTonode_R, eps ); + } + else if( mode == DistortionMode::NonPlanar ) + { + makeDistortedNonplanar( node_L, node_R, faceTonode_L, faceTonode_R, eps ); + } + + computeDistortedVolumeAndCenter( node_L, center_L, vol_L ); + computeDistortedVolumeAndCenter( node_R, center_R, vol_R ); + + // indicate the shared face + localIndex const fL_int = 3; + localIndex const fR_int = 2; + + real64 const alpha_lin = 0.3; // pressure = x + alpha_lin * y + constexpr real64 ltol = 1e-12; + real64 Kvec[3] = {4.0, 1.0, 0.5}; + + stackArray1d< real64, 3 > cLc( 3 ), cRc( 3 ); + for( int d = 0; d < 3; ++d ) + { + cLc[d] = center_L[d]; + cRc[d] = center_R[d]; + } + + stackArray2d< real64, NF * NF > ML( NF, NF ), MR( NF, NF ); + ML.template setValues< parallelHostPolicy >( 0.0 ); + MR.template setValues< parallelHostPolicy >( 0.0 ); + + computeM_dispatch< NF >( ipKind, node_L, faceTonode_L, elemToface_L, cLc.toSliceConst(), vol_L, Kvec, ltol, ML.toSlice() ); + computeM_dispatch< NF >( ipKind, node_R, faceTonode_R, elemToface_R, cRc.toSliceConst(), vol_R, Kvec, ltol, MR.toSlice() ); + + // unknown ordering: [ qL(0..NF-1), qR(0..NF-1), pL, pR, piInt ] + constexpr int N = 2 * NF + 3; + int const ipL = 2 * NF, ipR = 2 * NF + 1, iPi = 2 * NF + 2; + real64 A[N][N] = {{ 0 }}; + real64 b[N] = { 0 }; + real64 x[N] = { 0 }; + + // flux equations + for( int i = 0; i < NF; ++i ) + { + for( int j = 0; j < NF; ++j ) + { + A[i][j] = ML( i, j ); + A[NF + i][NF + j] = MR( i, j ); + } + A[i][ipL] = -1.0; + A[NF + i][ipR] = -1.0; + + if( i == fL_int ) + A[i][iPi] = 1.0; + else + b[i] = -computeBoundaryPressure( faceTonode_L, node_L, i, alpha_lin ); + + if( i == fR_int ) + A[NF + i][iPi] = 1.0; + else + b[NF + i] = -computeBoundaryPressure( faceTonode_R, node_R, i, alpha_lin ); + } + + // mass conservation + for( int j = 0; j < NF; ++j ) + { + A[ipL][j] = 1.0; + A[ipR][NF + j] = 1.0; + } + + // flux continuity at the interface + A[iPi][fL_int] = 1.0; + A[iPi][NF + fR_int] = 1.0; + + solveDense< N >( A, b, x ); + + real64 const pL = x[ipL]; + real64 const pR = x[ipR]; + + // compute analytical pressure values + double pL_exact = center_L[0] + alpha_lin * center_L[1]; + double pR_exact = center_R[0] + alpha_lin * center_R[1]; + double err = std::max( std::abs( pL - pL_exact ) / std::abs( pL_exact ), std::abs( pR - pR_exact ) / std::abs( pR_exact ) ); + + return err; +} + +// ================= mixed form, case 0: without distortion =========================== +TEST( MimeticIP_MixedLinear, UnitCube_LinearPressure_TPFA ) +{ + double err = computeLinearPressureMixed_error< 6 >( InnerProductType::TPFA ); + EXPECT_LT( err, mixed_consistency_tol ); +} + +TEST( MimeticIP_MixedLinear, UnitCube_LinearPressure_QuasiTPFA ) +{ + double err = computeLinearPressureMixed_error< 6 >( InnerProductType::QUASI_TPFA ); + EXPECT_LT( err, mixed_consistency_tol ); +} + +TEST( MimeticIP_MixedLinear, UnitCube_LinearPressure_Simple ) +{ + double err = computeLinearPressureMixed_error< 6 >( InnerProductType::SIMPLE ); + EXPECT_LT( err, mixed_consistency_tol ); +} + +TEST( MimeticIP_MixedLinear, UnitCube_LinearPressure_BdVLM ) +{ + double err = computeLinearPressureMixed_error< 6 >( InnerProductType::BDVLM ); + EXPECT_LT( err, mixed_consistency_tol ); +} + +// =================== mixed form, case 1: with distortion (planar) =========================== +TEST( MimeticIP_MixedLinear, Distortion_Planar_LinearPressure ) +{ + int neps = 3; + stdVector< double > eps_values( 3 ); + eps_values[0] = 0.0; // no distortion + eps_values[1] = 0.2; // moderate distortion + eps_values[2] = 0.9; // severe distortion + + // all mimetic inner products must remain consistent on distorted (planar-face) cells + for( int i = 0; i < neps; ++i ) + { + double eps = eps_values[i]; + + double errQTPFA = computeLinearPressureMixed_error< 6 >( InnerProductType::QUASI_TPFA, DistortionMode::Planar, eps ); + EXPECT_LT( errQTPFA, mixed_consistency_tol ); + + double errSIMPLE = computeLinearPressureMixed_error< 6 >( InnerProductType::SIMPLE, DistortionMode::Planar, eps ); + EXPECT_LT( errSIMPLE, mixed_consistency_tol ); + + double errBDVLM = computeLinearPressureMixed_error< 6 >( InnerProductType::BDVLM, DistortionMode::Planar, eps ); + EXPECT_LT( errBDVLM, mixed_consistency_tol ); + } + + for( int i = 0; i < neps; ++i ) + { + double eps = eps_values[i]; + + double errTPFA = computeLinearPressureMixed_error< 6 >( InnerProductType::TPFA, DistortionMode::Planar, eps ); + + if( i == 0 ) + { + EXPECT_LT( errTPFA, mixed_consistency_tol ); // TPFA is consistent on K-orthogonal grids + } + else + { + EXPECT_GT( errTPFA, mixed_consistency_tol ); // TPFA is inconsistent on non K-orthogonal grids + } + } +} + +// =================== mixed form, case 2: with distortion (nonplanar) =========================== +TEST( MimeticIP_MixedLinear, Distortion_NonPlanar_LinearPressure ) +{ + int neps = 2; + stdVector< double > eps_values( 2 ); + eps_values[0] = 0.2; // moderate distortion + eps_values[1] = 0.9; // severe distortion + + // all schemes are inconsistent with nonplanar faces + for( int i = 0; i < neps; ++i ) + { + double eps = eps_values[i]; + + double errTPFA = computeLinearPressureMixed_error< 6 >( InnerProductType::TPFA, DistortionMode::NonPlanar, eps ); + EXPECT_GT( errTPFA, mixed_consistency_tol ); + + double errQTPFA = computeLinearPressureMixed_error< 6 >( InnerProductType::QUASI_TPFA, DistortionMode::NonPlanar, eps ); + EXPECT_GT( errQTPFA, mixed_consistency_tol ); + + double errSIMPLE = computeLinearPressureMixed_error< 6 >( InnerProductType::SIMPLE, DistortionMode::NonPlanar, eps ); + EXPECT_GT( errSIMPLE, mixed_consistency_tol ); + + double errBDVLM = computeLinearPressureMixed_error< 6 >( InnerProductType::BDVLM, DistortionMode::NonPlanar, eps ); + EXPECT_GT( errBDVLM, mixed_consistency_tol ); + } +} + +//======================== Mixed Inner Product Algebraic Consistency Test ============================= +// Direct check of the mimetic consistency condition M (N K) = C on the element matrix +// (stronger than the pressure recovery above, where a defect can hide in the fluxes). +// Valid only on cells where C^T N = vol * I holds with vertex-averaged face centers. + +template< int NF > +static double computeMixedIPConsistency_error( int ipKind, + DistortionMode mode = DistortionMode::None, + real64 eps = 0.0 ) +{ + array2d< real64, nodes::REFERENCE_POSITION_PERM > node_L; + array2d< real64, nodes::REFERENCE_POSITION_PERM > node_R; + FaceManager::NodeMapType faceTonode_L; + FaceManager::NodeMapType faceTonode_R; + array1d< localIndex > elemToface_L; + array1d< localIndex > elemToface_R; + real64 center_L[3], center_R[3], vol_L = 0, vol_R = 0; + + makeUnitCube( 0.0, node_L, faceTonode_L, elemToface_L, center_L, vol_L ); + makeUnitCube( 1.0, node_R, faceTonode_R, elemToface_R, center_R, vol_R ); + + if( mode == DistortionMode::Planar ) + { + makeDistortedPlanar( node_L, node_R, faceTonode_L, faceTonode_R, eps ); + } + else if( mode == DistortionMode::NonPlanar ) + { + makeDistortedNonplanar( node_L, node_R, faceTonode_L, faceTonode_R, eps ); + } + + computeDistortedVolumeAndCenter( node_L, center_L, vol_L ); + + constexpr real64 ltol = 1e-12; + real64 Kvec[3] = {4.0, 1.0, 0.5}; + + stackArray1d< real64, 3 > cLc( 3 ); + for( int d = 0; d < 3; ++d ) + { + cLc[d] = center_L[d]; + } + + stackArray2d< real64, NF * NF > M( NF, NF ); + M.template setValues< parallelHostPolicy >( 0.0 ); + computeM_dispatch< NF >( ipKind, node_L, faceTonode_L, elemToface_L, cLc.toSliceConst(), vol_L, Kvec, ltol, M.toSlice() ); + + // assemble N (area-weighted outward normals, times K) and C (cell-to-face vectors), + // using the same geometry convention as computeM (centroid_3DPolygon) + real64 NK[NF][3]; + real64 C[NF][3]; + for( int f = 0; f < NF; ++f ) + { + real64 fc[3], fn[3]; + real64 const area = centroid_3DPolygon( faceTonode_L[f], node_L.toViewConst(), fc, fn ); + real64 c2f[3] = { fc[0] - center_L[0], fc[1] - center_L[1], fc[2] - center_L[2] }; + if( LvArray::tensorOps::AiBi< 3 >( c2f, fn ) < 0.0 ) + { + LvArray::tensorOps::scale< 3 >( fn, -1.0 ); + } + for( int d = 0; d < 3; ++d ) + { + NK[f][d] = area * fn[d] * Kvec[d]; // (|f| n_f) K for diagonal K + C[f][d] = c2f[d]; + } + } + + // err = max | ( M NK - C )_fd | + double err = 0.0; + for( int f = 0; f < NF; ++f ) + { + for( int d = 0; d < 3; ++d ) + { + real64 s = 0.0; + for( int k = 0; k < NF; ++k ) + { + s += M( f, k ) * NK[k][d]; + } + err = std::max( err, std::abs( s - C[f][d] ) ); + } + } + return err; +} + +TEST( MimeticIP_MixedConsistency, UnitCube ) +{ + for( int ip : { InnerProductType::TPFA, InnerProductType::QUASI_TPFA, + InnerProductType::SIMPLE, InnerProductType::BDVLM } ) + { + EXPECT_LT( computeMixedIPConsistency_error< 6 >( ip ), mixed_consistency_tol ) << "ipKind = " << ip; + } +} + +TEST( MimeticIP_MixedConsistency, Distortion_Planar ) +{ + for( double eps : { 0.2, 0.9 } ) + { + // all mimetic inner products must satisfy M ( N K ) = C on planar-face cells + EXPECT_LT( computeMixedIPConsistency_error< 6 >( InnerProductType::QUASI_TPFA, DistortionMode::Planar, eps ), + mixed_consistency_tol ) << "eps = " << eps; + EXPECT_LT( computeMixedIPConsistency_error< 6 >( InnerProductType::SIMPLE, DistortionMode::Planar, eps ), + mixed_consistency_tol ) << "eps = " << eps; + EXPECT_LT( computeMixedIPConsistency_error< 6 >( InnerProductType::BDVLM, DistortionMode::Planar, eps ), + mixed_consistency_tol ) << "eps = " << eps; + + // TPFA is inconsistent on non K-orthogonal cells + EXPECT_GT( computeMixedIPConsistency_error< 6 >( InnerProductType::TPFA, DistortionMode::Planar, eps ), + mixed_consistency_tol ) << "eps = " << eps; + } +} + +//======================== Hybrid/Mixed Duality Test ============================= +// M = T^{-1} must hold; exact on parallelepipeds with isotropic K at any cell size h. + +template< int NF > +static double computeHybridMixedDuality_error( int ipKind, real64 h ) +{ + array2d< real64, nodes::REFERENCE_POSITION_PERM > node; + FaceManager::NodeMapType faceTonode; + array1d< localIndex > elemToface; + real64 center[3], vol = 0; + + makeUnitCube( 0.0, node, faceTonode, elemToface, center, vol ); + + // scale the cell by h (stays a parallelepiped) + for( int a = 0; a < 8; ++a ) + { + for( int d = 0; d < 3; ++d ) + { + node( a, d ) *= h; + } + } + computeDistortedVolumeAndCenter( node, center, vol ); + + constexpr real64 ltol = 1e-12; + real64 Kvec[3] = {1.0, 1.0, 1.0}; + + stackArray1d< real64, 3 > cc( 3 ); + for( int d = 0; d < 3; ++d ) + { + cc[d] = center[d]; + } + + array1d< real64 > mult( NF ); + mult.setValues< parallelHostPolicy >( 1.0 ); + + stackArray2d< real64, NF * NF > T( NF, NF ), M( NF, NF ); + T.template setValues< parallelHostPolicy >( 0.0 ); + M.template setValues< parallelHostPolicy >( 0.0 ); + + if( ipKind == InnerProductType::TPFA ) + { + TPFAInnerProduct::compute< NF >( node.toViewConst(), mult.toViewConst(), faceTonode.toViewConst(), + elemToface.toSliceConst(), cc, vol, Kvec, ltol, T.toSlice() ); + } + else if( ipKind == InnerProductType::QUASI_TPFA ) + { + QuasiTPFAInnerProduct::compute< NF >( node.toViewConst(), mult.toViewConst(), faceTonode.toViewConst(), + elemToface.toSliceConst(), cc, vol, Kvec, ltol, T.toSlice() ); + } + else if( ipKind == InnerProductType::SIMPLE ) + { + SimpleInnerProduct::compute< NF >( node.toViewConst(), mult.toViewConst(), faceTonode.toViewConst(), + elemToface.toSliceConst(), cc, vol, Kvec, ltol, T.toSlice() ); + } + else if( ipKind == InnerProductType::BDVLM ) + { + BdVLMInnerProduct::compute< NF >( node.toViewConst(), mult.toViewConst(), faceTonode.toViewConst(), + elemToface.toSliceConst(), cc, vol, Kvec, ltol, T.toSlice() ); + } + + computeM_dispatch< NF >( ipKind, node, faceTonode, elemToface, cc.toSliceConst(), vol, Kvec, ltol, M.toSlice() ); + + // err = max | (M T)_ij - delta_ij | + double err = 0.0; + for( int i = 0; i < NF; ++i ) + { + for( int j = 0; j < NF; ++j ) + { + real64 s = 0.0; + for( int k = 0; k < NF; ++k ) + { + s += M( i, k ) * T( k, j ); + } + err = std::max( err, std::abs( s - ( i == j ? 1.0 : 0.0 ) ) ); + } + } + return err; +} + +static constexpr real64 duality_tol = 1e-10; + +TEST( MimeticIP_MixedDuality, TPFA ) +{ + EXPECT_LT( computeHybridMixedDuality_error< 6 >( InnerProductType::TPFA, 1.0 ), duality_tol ); + EXPECT_LT( computeHybridMixedDuality_error< 6 >( InnerProductType::TPFA, 0.2 ), duality_tol ); +} + +TEST( MimeticIP_MixedDuality, QuasiTPFA ) +{ + EXPECT_LT( computeHybridMixedDuality_error< 6 >( InnerProductType::QUASI_TPFA, 1.0 ), duality_tol ); + EXPECT_LT( computeHybridMixedDuality_error< 6 >( InnerProductType::QUASI_TPFA, 0.2 ), duality_tol ); +} + +TEST( MimeticIP_MixedDuality, Simple ) +{ + EXPECT_LT( computeHybridMixedDuality_error< 6 >( InnerProductType::SIMPLE, 1.0 ), duality_tol ); + EXPECT_LT( computeHybridMixedDuality_error< 6 >( InnerProductType::SIMPLE, 0.2 ), duality_tol ); +} + +TEST( MimeticIP_MixedDuality, BdVLM ) +{ + EXPECT_LT( computeHybridMixedDuality_error< 6 >( InnerProductType::BDVLM, 1.0 ), duality_tol ); + EXPECT_LT( computeHybridMixedDuality_error< 6 >( InnerProductType::BDVLM, 0.2 ), duality_tol ); +} + //======================== Hydrostatic Equilibrium Consistency Test ============================= static inline void makeDistortedPlanar_gravity( array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodeL, array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodeU, From 5b450de13f219ac89003bd213675a9f2e7d66cfb Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Mon, 3 Aug 2026 18:36:27 -0700 Subject: [PATCH 18/64] fix: centroid computation exact for planar polygons in R3 --- .../unitTests/testMimeticInnerProducts.cpp | 193 ++++++++++++++++-- .../mesh/utilities/ComputationalGeometry.hpp | 18 ++ 2 files changed, 195 insertions(+), 16 deletions(-) diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp index c403693464d..abcf66f7d03 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp @@ -1313,7 +1313,7 @@ TEST( testMimeticInnerProducts, BdVLMM_cube ) //======================== Linear Pressure Recovery Test ============================= // Three cases: without distortion (unit cube cell), with distortion: (1) planar (2) nonplanar -enum class DistortionMode { None, Planar, NonPlanar }; +enum class DistortionMode { None, Planar, PlanarFunnel, NonPlanar }; static inline void makeUnitCube( double x_start, array2d< real64, nodes::REFERENCE_POSITION_PERM > & node, @@ -1401,6 +1401,25 @@ static inline void makeDistortedPlanar( array2d< real64, nodes::REFERENCE_POSITI nodeL( Lvert2, 0 ) += eps; nodeR( Rvert2, 0 ) += eps; } +// shrink the shared face toward its center: two funnel-shaped cells, all faces planar +static inline void makeDistortedPlanarFunnel( array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodeL, + array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodeR, + FaceManager::NodeMapType const & faceL, + FaceManager::NodeMapType const & faceR, + real64 eps ) +{ + localIndex const fL = 3, fR = 2; + for( int a = 0; a < 4; ++a ) + { + localIndex const vL = faceL( fL, a ); + localIndex const vR = faceR( fR, a ); + nodeL( vL, 1 ) += eps * ( 0.5 - nodeL( vL, 1 ) ); + nodeL( vL, 2 ) += eps * ( 0.5 - nodeL( vL, 2 ) ); + nodeR( vR, 1 ) += eps * ( 0.5 - nodeR( vR, 1 ) ); + nodeR( vR, 2 ) += eps * ( 0.5 - nodeR( vR, 2 ) ); + } +} + // create nonplanar case for distortion test static inline void makeDistortedNonplanar( array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodeL, array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodeR, @@ -1462,19 +1481,6 @@ static inline void computeDistortedVolumeAndCenter( array2d< real64, nodes::REFE center[0] /= 8.0; center[1] /= 8.0; center[2] /= 8.0; } -static inline bool isBoundaryFace( FaceManager::NodeMapType const & faceTonode, - array2d< real64, nodes::REFERENCE_POSITION_PERM > const & node, - localIndex f ) -{ - real64 fc[3], fn[3]; - centroid_3DPolygon( faceTonode[f], node.toViewConst(), fc, fn ); - constexpr real64 tol = 1e-12; - - return (std::abs( fc[0] - 0.0 ) < tol) || (std::abs( fc[0] - 2.0 ) < tol) || - (std::abs( fc[1] - 0.0 ) < tol) || (std::abs( fc[1] - 1.0 ) < tol) || - (std::abs( fc[2] - 0.0 ) < tol) || (std::abs( fc[2] - 1.0 ) < tol); -} - // calculate pressures on cell boundary (for Dirichlet boundary condition) static inline real64 computeBoundaryPressure( FaceManager::NodeMapType const & faceTonode, array2d< real64, nodes::REFERENCE_POSITION_PERM > const & node, @@ -1507,6 +1513,10 @@ static double computeLinearPressure_error( int ipKind, { makeDistortedPlanar( node_L, node_R, faceTonode_L, faceTonode_R, eps ); } + else if( mode == DistortionMode::PlanarFunnel ) + { + makeDistortedPlanarFunnel( node_L, node_R, faceTonode_L, faceTonode_R, eps ); + } else if( mode == DistortionMode::NonPlanar ) { makeDistortedNonplanar( node_L, node_R, faceTonode_L, faceTonode_R, eps ); @@ -1520,8 +1530,9 @@ static double computeLinearPressure_error( int ipKind, localIndex const fR_int = 2; real64 alpha_lin = 0.3; // pressure = x * alpha_lin * y - auto isDirL = [&]( localIndex f ){ return isBoundaryFace( faceTonode_L, node_L, f ); }; - auto isDirR = [&]( localIndex f ){ return isBoundaryFace( faceTonode_R, node_R, f ); }; + // in this two-cell mesh, every face except the shared one is a Dirichlet boundary face + auto isDirL = [&]( localIndex f ){ return f != fL_int; }; + auto isDirR = [&]( localIndex f ){ return f != fR_int; }; // compute the transmissibility matrix T constexpr real64 ltol = 1e-12; @@ -1726,6 +1737,23 @@ TEST( MimeticIP_Linear, Distortion_Planar_LinearPressure ) } } +// =================== case 1b: with distortion (funnel) =========================== +TEST( MimeticIP_Linear, Distortion_PlanarFunnel_LinearPressure ) +{ + for( double eps : { 0.2, 0.4 } ) + { + EXPECT_LT( computeLinearPressure_error< 6 >( InnerProductType::QUASI_TPFA, DistortionMode::PlanarFunnel, eps ), + consistency_tol ) << "eps = " << eps; + EXPECT_LT( computeLinearPressure_error< 6 >( InnerProductType::SIMPLE, DistortionMode::PlanarFunnel, eps ), + consistency_tol ) << "eps = " << eps; + EXPECT_LT( computeLinearPressure_error< 6 >( InnerProductType::BDVLM, DistortionMode::PlanarFunnel, eps ), + consistency_tol ) << "eps = " << eps; + + EXPECT_GT( computeLinearPressure_error< 6 >( InnerProductType::TPFA, DistortionMode::PlanarFunnel, eps ), + consistency_tol ) << "eps = " << eps; + } +} + // =================== case 2: with distortion (nonplanar) =========================== TEST( MimeticIP_Linear, Distortion_NonPlanar_LinearPressure ) { @@ -1849,6 +1877,10 @@ static double computeLinearPressureMixed_error( int ipKind, { makeDistortedPlanar( node_L, node_R, faceTonode_L, faceTonode_R, eps ); } + else if( mode == DistortionMode::PlanarFunnel ) + { + makeDistortedPlanarFunnel( node_L, node_R, faceTonode_L, faceTonode_R, eps ); + } else if( mode == DistortionMode::NonPlanar ) { makeDistortedNonplanar( node_L, node_R, faceTonode_L, faceTonode_R, eps ); @@ -1998,6 +2030,23 @@ TEST( MimeticIP_MixedLinear, Distortion_Planar_LinearPressure ) } } +// =================== mixed form: with distortion (funnel) =========================== +TEST( MimeticIP_MixedLinear, Distortion_PlanarFunnel_LinearPressure ) +{ + for( double eps : { 0.2, 0.4 } ) + { + EXPECT_LT( computeLinearPressureMixed_error< 6 >( InnerProductType::QUASI_TPFA, DistortionMode::PlanarFunnel, eps ), + mixed_consistency_tol ) << "eps = " << eps; + EXPECT_LT( computeLinearPressureMixed_error< 6 >( InnerProductType::SIMPLE, DistortionMode::PlanarFunnel, eps ), + mixed_consistency_tol ) << "eps = " << eps; + EXPECT_LT( computeLinearPressureMixed_error< 6 >( InnerProductType::BDVLM, DistortionMode::PlanarFunnel, eps ), + mixed_consistency_tol ) << "eps = " << eps; + + EXPECT_GT( computeLinearPressureMixed_error< 6 >( InnerProductType::TPFA, DistortionMode::PlanarFunnel, eps ), + mixed_consistency_tol ) << "eps = " << eps; + } +} + // =================== mixed form, case 2: with distortion (nonplanar) =========================== TEST( MimeticIP_MixedLinear, Distortion_NonPlanar_LinearPressure ) { @@ -2050,6 +2099,10 @@ static double computeMixedIPConsistency_error( int ipKind, { makeDistortedPlanar( node_L, node_R, faceTonode_L, faceTonode_R, eps ); } + else if( mode == DistortionMode::PlanarFunnel ) + { + makeDistortedPlanarFunnel( node_L, node_R, faceTonode_L, faceTonode_R, eps ); + } else if( mode == DistortionMode::NonPlanar ) { makeDistortedNonplanar( node_L, node_R, faceTonode_L, faceTonode_R, eps ); @@ -2134,6 +2187,114 @@ TEST( MimeticIP_MixedConsistency, Distortion_Planar ) } } +TEST( MimeticIP_MixedConsistency, Distortion_PlanarFunnel ) +{ + for( double eps : { 0.2, 0.4 } ) + { + EXPECT_LT( computeMixedIPConsistency_error< 6 >( InnerProductType::QUASI_TPFA, DistortionMode::PlanarFunnel, eps ), + mixed_consistency_tol ) << "eps = " << eps; + EXPECT_LT( computeMixedIPConsistency_error< 6 >( InnerProductType::SIMPLE, DistortionMode::PlanarFunnel, eps ), + mixed_consistency_tol ) << "eps = " << eps; + EXPECT_LT( computeMixedIPConsistency_error< 6 >( InnerProductType::BDVLM, DistortionMode::PlanarFunnel, eps ), + mixed_consistency_tol ) << "eps = " << eps; + + EXPECT_GT( computeMixedIPConsistency_error< 6 >( InnerProductType::TPFA, DistortionMode::PlanarFunnel, eps ), + mixed_consistency_tol ) << "eps = " << eps; + } +} + +//======================== Geometric Identity Test ============================= +// C^T N = vol * I, with N_f = |f| n_f and C_f = x_f - x_c (any reference point x_c); +// holds iff faces are planar and x_f is the area centroid. This identity is what the +// consistency term of every mimetic inner product relies on. + +template< int NF > +static double computeGeometricIdentity_error( DistortionMode mode = DistortionMode::None, + real64 eps = 0.0 ) +{ + array2d< real64, nodes::REFERENCE_POSITION_PERM > node_L; + array2d< real64, nodes::REFERENCE_POSITION_PERM > node_R; + FaceManager::NodeMapType faceTonode_L; + FaceManager::NodeMapType faceTonode_R; + array1d< localIndex > elemToface_L; + array1d< localIndex > elemToface_R; + real64 center_L[3], center_R[3], vol_L = 0, vol_R = 0; + + makeUnitCube( 0.0, node_L, faceTonode_L, elemToface_L, center_L, vol_L ); + makeUnitCube( 1.0, node_R, faceTonode_R, elemToface_R, center_R, vol_R ); + + if( mode == DistortionMode::Planar ) + { + makeDistortedPlanar( node_L, node_R, faceTonode_L, faceTonode_R, eps ); + } + else if( mode == DistortionMode::PlanarFunnel ) + { + makeDistortedPlanarFunnel( node_L, node_R, faceTonode_L, faceTonode_R, eps ); + } + else if( mode == DistortionMode::NonPlanar ) + { + makeDistortedNonplanar( node_L, node_R, faceTonode_L, faceTonode_R, eps ); + } + + computeDistortedVolumeAndCenter( node_L, center_L, vol_L ); + + real64 E[3][3] = {{ 0 }}; + for( int d = 0; d < 3; ++d ) + { + E[d][d] = -vol_L; + } + + for( int f = 0; f < NF; ++f ) + { + real64 fc[3], fn[3]; + real64 const area = centroid_3DPolygon( faceTonode_L[f], node_L.toViewConst(), fc, fn ); + real64 c2f[3] = { fc[0] - center_L[0], fc[1] - center_L[1], fc[2] - center_L[2] }; + if( LvArray::tensorOps::AiBi< 3 >( c2f, fn ) < 0.0 ) + { + LvArray::tensorOps::scale< 3 >( fn, -1.0 ); + } + for( int i = 0; i < 3; ++i ) + { + for( int j = 0; j < 3; ++j ) + { + E[i][j] += c2f[i] * area * fn[j]; + } + } + } + + double err = 0.0; + for( int i = 0; i < 3; ++i ) + { + for( int j = 0; j < 3; ++j ) + { + err = std::max( err, std::abs( E[i][j] ) / vol_L ); + } + } + return err; +} + +TEST( MimeticIP_GeometricIdentity, PlanarFaceCells ) +{ + EXPECT_LT( computeGeometricIdentity_error< 6 >(), mixed_consistency_tol ); + for( double eps : { 0.2, 0.9 } ) + { + EXPECT_LT( computeGeometricIdentity_error< 6 >( DistortionMode::Planar, eps ), + mixed_consistency_tol ) << "eps = " << eps; + } + for( double eps : { 0.2, 0.4 } ) + { + EXPECT_LT( computeGeometricIdentity_error< 6 >( DistortionMode::PlanarFunnel, eps ), + mixed_consistency_tol ) << "eps = " << eps; + } +} + +TEST( MimeticIP_GeometricIdentity, NonPlanarCells ) +{ + // non-planar faces break the identity + EXPECT_GT( computeGeometricIdentity_error< 6 >( DistortionMode::NonPlanar, 0.2 ), + mixed_consistency_tol ); +} + //======================== Hybrid/Mixed Duality Test ============================= // M = T^{-1} must hold; exact on parallelepipeds with isotropic K at any cell size h. diff --git a/src/coreComponents/mesh/utilities/ComputationalGeometry.hpp b/src/coreComponents/mesh/utilities/ComputationalGeometry.hpp index b42090eb5e6..4cd4cffc448 100644 --- a/src/coreComponents/mesh/utilities/ComputationalGeometry.hpp +++ b/src/coreComponents/mesh/utilities/ComputationalGeometry.hpp @@ -271,6 +271,24 @@ real64 centroid_3DPolygon( arraySlice1d< localIndex const > const pointsIndices, { LvArray::tensorOps::normalize< 3 >( normal ); area *= 0.5; + + // replace the vertex average by the area centroid (exact for planar polygons) + real64 areaCentroid[ 3 ] = { 0.0 }; + for( localIndex a=0; a( current, points[ pointsIndices[ a++ ]] ); + LvArray::tensorOps::scaledAdd< 3 >( current, origin, -1. ); + LvArray::tensorOps::copy< 3 >( next, points[ pointsIndices[ a % numberOfPoints ] ] ); + LvArray::tensorOps::scaledAdd< 3 >( next, origin, -1. ); + + LvArray::tensorOps::crossProduct( crossProduct, current, next ); + real64 const signedTriArea = 0.5 * LvArray::tensorOps::AiBi< 3 >( crossProduct, normal ); + + LvArray::tensorOps::scaledAdd< 3 >( areaCentroid, current, signedTriArea / 3.0 ); + LvArray::tensorOps::scaledAdd< 3 >( areaCentroid, next, signedTriArea / 3.0 ); + } + LvArray::tensorOps::copy< 3 >( center, origin ); + LvArray::tensorOps::scaledAdd< 3 >( center, areaCentroid, 1.0 / area ); } else if( area < -areaTolerance ) { From b5a7c8fffbc5e7a89135489b0e69aab3138f2e6c Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Mon, 3 Aug 2026 18:44:18 -0700 Subject: [PATCH 19/64] wip --- .../unitTests/testMimeticInnerProducts.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp index abcf66f7d03..c9df233b9f9 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp @@ -1403,10 +1403,10 @@ static inline void makeDistortedPlanar( array2d< real64, nodes::REFERENCE_POSITI // shrink the shared face toward its center: two funnel-shaped cells, all faces planar static inline void makeDistortedPlanarFunnel( array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodeL, - array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodeR, - FaceManager::NodeMapType const & faceL, - FaceManager::NodeMapType const & faceR, - real64 eps ) + array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodeR, + FaceManager::NodeMapType const & faceL, + FaceManager::NodeMapType const & faceR, + real64 eps ) { localIndex const fL = 3, fR = 2; for( int a = 0; a < 4; ++a ) From e3c85922f308ef292652f622074e7cbbcca4722f Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Tue, 4 Aug 2026 21:41:44 -0700 Subject: [PATCH 20/64] Bug fix on LC dane --- src/coreComponents/common/format/Format.hpp | 25 +++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/coreComponents/common/format/Format.hpp b/src/coreComponents/common/format/Format.hpp index e624dea1d65..ca03131f89f 100644 --- a/src/coreComponents/common/format/Format.hpp +++ b/src/coreComponents/common/format/Format.hpp @@ -18,6 +18,7 @@ #include #include +#include #if __cplusplus < 202002L #define GEOS_USE_FMT @@ -140,16 +141,22 @@ constexpr auto GEOS_FMT_NS::detail::has_const_formatter_impl< GEOS_FMT_NS::forma /** * Evaluates at compile time if a fmt::formatter exists for a given type */ -#if __cplusplus < 202002L template< class T > -static constexpr bool has_formatter_v = fmt::has_formatter< fmt::remove_cvref_t< T >, fmt::format_context >(); -#else -template< typename T > -concept has_formatter_v = requires ( T& v, std::format_context ctx ) -{ - std::formatter< std::remove_cvref_t< T > >().format( v, ctx ); -}; -#endif +using remove_cvref_t = std::remove_cv_t< std::remove_reference_t< T > >; + +template< class T, class = void > +struct HasFormatter : std::false_type +{}; + +template< class T > +struct HasFormatter< T, + std::void_t< decltype( GEOS_FMT_NS::format( "{}", + std::declval< remove_cvref_t< T > const & >() ) ) > > + : std::true_type +{}; + +template< class T > +inline constexpr bool has_formatter_v = HasFormatter< T >::value; namespace geos::format { From df516a68b2a91c010ee964eca9ccdb26945beaf0 Mon Sep 17 00:00:00 2001 From: "Victor A. P. Magri" Date: Tue, 4 Aug 2026 21:42:09 -0700 Subject: [PATCH 21/64] Add hypredrive bridge for singlePhaseMixedMFD --- .../linearAlgebra/interfaces/hypre/hypredrive.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp b/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp index 1db7a6e581c..747997226b9 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp @@ -21,6 +21,7 @@ #include "linearAlgebra/interfaces/hypre/mgrStrategies/MultiphasePoromechanicsReservoirFVM.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/ReactiveCompositionalMultiphaseOBL.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseHybridFVM.hpp" +#include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhasePoromechanics.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhasePoromechanicsConformingFractures.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhasePoromechanicsEmbeddedFractures.hpp" @@ -395,6 +396,7 @@ bool strategyUsesCompositionalSemanticLabels( LinearSolverParameters::MGR::Strat case StrategyType::singlePhaseReservoirFVM: case StrategyType::thermalSinglePhaseReservoirFVM: case StrategyType::singlePhaseHybridFVM: + case StrategyType::singlePhaseMixedMFD: case StrategyType::singlePhaseReservoirHybridFVM: case StrategyType::singlePhasePoromechanics: case StrategyType::thermalSinglePhasePoromechanics: @@ -961,6 +963,7 @@ MGRSpecialization getSpecialization( LinearSolverParameters::MGR::StrategyType c case StrategyType::invalid: case StrategyType::singlePhaseReservoirFVM: case StrategyType::singlePhaseHybridFVM: + case StrategyType::singlePhaseMixedMFD: case StrategyType::singlePhaseReservoirHybridFVM: case StrategyType::compositionalMultiphaseFVM: case StrategyType::compositionalMultiphaseHybridFVM: @@ -1257,6 +1260,8 @@ bool buildMGRPreconditionerYaml( LinearSolverParameters const & params, return buildStrategyYaml< hypre::mgr::ThermalSinglePhaseReservoirFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); case StrategyType::singlePhaseHybridFVM: return buildStrategyYaml< hypre::mgr::SinglePhaseHybridFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); + case StrategyType::singlePhaseMixedMFD: + return buildStrategyYaml< hypre::mgr::SinglePhaseMixedMFD >( params, labelNames, numComponentsPerField, preconditionerYaml ); case StrategyType::singlePhaseReservoirHybridFVM: return buildStrategyYaml< hypre::mgr::SinglePhaseReservoirHybridFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); case StrategyType::singlePhasePoromechanics: From e1a8e6c5c4ef5a83de25a3eddd460fc0d6ad5ac9 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Thu, 6 Aug 2026 14:51:56 -0700 Subject: [PATCH 22/64] wip: some work on robustness --- .../mimeticInnerProducts/TPFAInnerProduct.hpp | 13 ++++++++----- .../adaptivity/GlobalAdaptationKernels.hpp | 17 ++++++++++++----- .../fluidFlow/SinglePhaseMixedMFD.cpp | 1 + 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp index e2c97d905d4..bf201c00135 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp @@ -134,9 +134,11 @@ TPFAInnerProduct::compute( arrayView2d< real64 const, nodes::REFERENCE_POSITION_ LvArray::tensorOps::hadamardProduct< 3 >( faceConormal, elemPerm, faceNormal ); // 3) compute the one-sided face transmissibility - transMatrix[ifaceLoc][jfaceLoc] = LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceConormal ); - transMatrix[ifaceLoc][jfaceLoc] *= mult * faceArea / c2fDistance; - transMatrix[ifaceLoc][jfaceLoc] = LvArray::math::max( transMatrix[ifaceLoc][jfaceLoc], weightTolerance ); + real64 const halfTrans = + LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceConormal ) * mult * faceArea / c2fDistance; + // T := sign(T) * max(|T|, tol), so that |T| >= tol and sign(T) is preserved + transMatrix[ifaceLoc][jfaceLoc] = ( halfTrans < 0.0 ? -1.0 : 1.0 ) * + LvArray::math::max( LvArray::math::abs( halfTrans ), weightTolerance ); } else { @@ -189,10 +191,11 @@ TPFAInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION // 3) compute T_ii real64 Tii = LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceConormal ) * faceArea / c2fDistance; - Tii = LvArray::math::max( Tii, weightTolerance ); + // T := max(|T|, tol), so that M_ii = 1/T is well-defined with M_ii <= 1/tol + Tii = LvArray::math::max( LvArray::math::abs( Tii ), weightTolerance ); // 4) M = |T|^{-1} - M[ifaceLoc][ifaceLoc] = 1.0 / LvArray::math::abs( Tii ); + M[ifaceLoc][ifaceLoc] = 1.0 / Tii; } } diff --git a/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp b/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp index 9989bc69531..256baeecb34 100644 --- a/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp +++ b/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp @@ -105,12 +105,11 @@ struct FaceFluxProjectionKernel ElementViewConst< arrayView2d< real64 const > > const & elemCenter, ElementViewConst< arrayView3d< real64 const > > const & elemPerm, real64 const (&gradient)[3], + real64 const lengthTolerance, arrayView1d< real64 > const & projFaceFlux ) { forAll< POLICY >( numFaces, [=] GEOS_HOST_DEVICE ( localIndex const kf ) { - real64 const areaWeightTol = LvArray::NumericLimits< real64 >::epsilon; - real64 sumInvHalfTrans = 0.0; // sum of d / kappa real64 sumDist = 0.0; // sum of d integer elemCounter = 0; @@ -139,15 +138,19 @@ struct FaceFluxProjectionKernel dist += cellToFace[d] * faceNormal[kf][d]; } dist = LvArray::math::abs( dist ); - dist = LvArray::math::max( dist, areaWeightTol ); + dist = LvArray::math::max( dist, lengthTolerance ); // face-normal directional diffusive component kappa = n . K . n (diagonal K) real64 kappa = 0.0; + real64 kappaScale = 0.0; for( integer d = 0; d < 3; ++d ) { kappa += elemPerm[er][esr][ei][0][d] * faceNormal[kf][d] * faceNormal[kf][d]; + kappaScale = LvArray::math::max( kappaScale, elemPerm[er][esr][ei][0][d] ); } - kappa = LvArray::math::max( kappa, areaWeightTol ); + // roundoff floor: n.K.n is computed from terms bounded by max_d K_d, so values below + // eps * max_d K_d are numerically indistinguishable from a collapsed eigenvalue + kappa = LvArray::math::max( kappa, LvArray::NumericLimits< real64 >::epsilon * kappaScale ); sumInvHalfTrans += dist / kappa; sumDist += dist; @@ -250,6 +253,10 @@ struct LocalResidualKernel real64 const lengthTolerance, arrayView1d< real64 > const & faceResidual ) { + // ||DeltaP_C|| >= |g| * (star-shape radius) on admissible cells: a smaller value signals + // a degenerate cell, and the normalization saturates at the mesh length tolerance scale + real64 const dropTolerance = LvArray::tensorOps::l2Norm< 3 >( gradient ) * lengthTolerance; + forAll< POLICY >( numElems, [=] GEOS_HOST_DEVICE ( localIndex const ei ) { real64 const perm[ 3 ] = { elemPerm[ei][0][0], elemPerm[ei][0][1], elemPerm[ei][0][2] }; @@ -295,7 +302,7 @@ struct LocalResidualKernel pressureDropNorm += drop * drop; } pressureDropNorm = LvArray::math::sqrt( pressureDropNorm ); - pressureDropNorm = LvArray::math::max( pressureDropNorm, LvArray::NumericLimits< real64 >::epsilon ); + pressureDropNorm = LvArray::math::max( pressureDropNorm, dropTolerance ); // normalized constitutive residual, assembled on the global face orientation for( integer i = 0; i < NF; ++i ) diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index 30a24538e9e..880df0b6268 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -230,6 +230,7 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d elemCenterAccessor.toNestedViewConst(), permAccessors.get( fields::permeability::permeability {} ), gradient, + lengthTolerance, projFaceFlux ); // steps 2-3: localized normalized residuals, assembled on the global face orientation From c35061a9906db70a355c4c2134e39d628e9a7c02 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Thu, 6 Aug 2026 20:14:20 -0700 Subject: [PATCH 23/64] wip: fix compilation with cuda --- .../kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp index 35e299e6c7a..6c833a76ebb 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp @@ -417,7 +417,7 @@ class ElementBasedAssemblyKernel arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const m_elemDens; arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const m_dElemDens; arrayView1d< real64 const > const m_mob; - arrayView2d< real64 const > const m_dMob; + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const m_dMob; /// View on the local CRS matrix and RHS CRSMatrixView< real64, globalIndex const > const m_localMatrix; @@ -767,9 +767,9 @@ class TpfaCondensedFluxKernel ElementViewConst< arrayView1d< real64 const > > const m_pres; ElementViewConst< arrayView1d< real64 const > > const m_elemGravCoef; ElementViewConst< arrayView1d< real64 const > > const m_mob; - ElementViewConst< arrayView2d< real64 const > > const m_dMob; - ElementViewConst< arrayView2d< real64 const > > const m_dens; - ElementViewConst< arrayView3d< real64 const > > const m_dDens; + ElementViewConst< arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > > const m_dMob; + ElementViewConst< arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > > const m_dens; + ElementViewConst< arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > > const m_dDens; ElementViewConst< arrayView3d< real64 const > > const m_elemPerm; /// system views From bba283380a93e953d2771db6d29ce804fce143a7 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Sun, 9 Aug 2026 20:00:26 -0700 Subject: [PATCH 24/64] fix: condensation of truly TPFA dofs --- .../interfaces/hypre/HypreMGR.hpp | 5 +-- .../mgrStrategies/SinglePhaseMixedMFD.hpp | 35 +++++++++++++++++-- .../adaptivity/GlobalAdaptationKernels.hpp | 31 +++++++++++++--- .../fluidFlow/SinglePhaseMixedMFD.cpp | 35 ++++++++++++++++++- 4 files changed, 97 insertions(+), 9 deletions(-) diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.hpp b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.hpp index 8457dd8df09..fad738c6c57 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.hpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.hpp @@ -133,7 +133,8 @@ class MGRStrategyBase * @param mgrData auxiliary MGR data */ void setReduction( HyprePrecWrapper & precond, - HypreMGRData & mgrData ) + HypreMGRData & mgrData, + HYPRE_Int const numActiveLevels = numLevels ) { // Ensure that if no F-relaxation or global smoothing is chosen the corresponding number @@ -151,7 +152,7 @@ class MGRStrategyBase } GEOS_LAI_CHECK_ERROR( HYPRE_MGRSetCpointsByPointMarkerArray( precond.ptr, - m_numBlocks, numLevels, + m_numBlocks, numActiveLevels, m_numLabels, m_ptrLabels, mgrData.pointMarkers.data() ) ); GEOS_LAI_CHECK_ERROR( HYPRE_MGRSetLevelFRelaxType( precond.ptr, toUnderlyingPtr( m_levelFRelaxType ) ) ); diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp index c845f8712e9..eb6a7ec78ad 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp @@ -20,6 +20,7 @@ #ifndef GEOS_LINEARALGEBRA_INTERFACES_HYPREMGRSINGLEPHASEMIXEDMFD_HPP_ #define GEOS_LINEARALGEBRA_INTERFACES_HYPREMGRSINGLEPHASEMIXEDMFD_HPP_ +#include "common/MpiWrapper.hpp" #include "linearAlgebra/interfaces/hypre/HypreMGR.hpp" namespace geos @@ -100,17 +101,47 @@ class SinglePhaseMixedMFD : public MGRStrategyBase< 2 > * @param precond preconditioner wrapper * @param mgrData auxiliary MGR data */ - void setup( LinearSolverParameters::MGR const &, + void setup( LinearSolverParameters::MGR const & mgrParams, HyprePrecWrapper & precond, HypreMGRData & mgrData ) { - setReduction( precond, mgrData ); + // if the label-1 set is empty the level-1 reduction is the identity: use a single + // level eliminating all (diagonal) fluxes at once + integer localAnyLive = 0; + for( localIndex i = 0; i < mgrParams.customPointMarkers.size(); ++i ) + { + if( mgrParams.customPointMarkers[i] == 1 ) + { + localAnyLive = 1; + break; + } + } + bool const anyLiveFaces = MpiWrapper::max( localAnyLive ) == 1; + + m_labels[0].clear(); + m_labels[1].clear(); + if( anyLiveFaces ) + { + m_labels[0].push_back( 1 ); + m_labels[0].push_back( 2 ); + m_labels[1].push_back( 2 ); + } + else + { + m_labels[0].push_back( 2 ); + } + setupLabels(); + + setReduction( precond, mgrData, anyLiveFaces ? numLevels : 1 ); // Configure the BoomerAMG solver used as mgr coarse solver for the pressure Schur // complement. Two V-cycles per MGR application: the coarse solve accuracy governs the // outer FGMRES iteration count (a single cycle leaves the reduction quality unused) setPressureAMG( mgrData.coarseSolver ); GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetMaxIter( mgrData.coarseSolver.ptr, 2 ) ); + // strength threshold suited to 3D anisotropic permeability fields (hypre default 0.25 + // over-couples the weak direction and degrades coarsening quality) + GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetStrongThreshold( mgrData.coarseSolver.ptr, 0.6 ) ); } }; diff --git a/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp b/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp index 256baeecb34..5c794950736 100644 --- a/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp +++ b/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp @@ -181,10 +181,10 @@ struct FaceFluxProjectionKernel /** * @class FaceLabelKernel - * @brief Classify the faces from the cell-wise stencil activation flags: a face whose - * adjacent target cells are all TPFA-compatible gets label 0 (its constitutive row - * is exactly diagonal and the flux can be condensed into a two-point expression); - * a face adjacent to at least one MFD-compatible cell gets label 1 (live unknown). + * @brief Classify the face dofs. A face dof f is a TPFA dof iff, for every adjacent + * cell C, (i) chi_C = 0 and (ii) (x_f - x_C) . K_C n_out > 0: its constitutive + * row is then exactly diagonal with a positive two-point coefficient. Only TPFA + * dofs are condensed (label 0); every other face dof is a live unknown (label 1). * When the selected inner product is itself TPFA, the effective operator is diagonal * everywhere and all faces get label 0. */ @@ -201,6 +201,10 @@ struct FaceLabelKernel arrayView2d< localIndex const > const & elemList, SortedArrayView< localIndex const > const & regionFilter, ElementViewConst< arrayView1d< integer const > > const & stencilFlag, + arrayView2d< real64 const > const & faceCenter, + arrayView2d< real64 const > const & faceNormal, + ElementViewConst< arrayView2d< real64 const > > const & elemCenter, + ElementViewConst< arrayView3d< real64 const > > const & elemPerm, bool const effectiveTpfa, arrayView1d< integer > const & faceStencilLabel ) { @@ -217,6 +221,25 @@ struct FaceLabelKernel if( er >= 0 && esr >= 0 && ei >= 0 && regionFilter.contains( er ) ) { label = LvArray::math::max( label, stencilFlag[er][esr][ei] ); + + // u = x_f - x_C, n the fixed global normal: n_out = sign(u . n) n, so + // sign(u . K n_out) = sign((u . K n)(u . n)) + real64 uDotKn = 0.0; + for( integer d = 0; d < 3; ++d ) + { + uDotKn += ( faceCenter[kf][d] - elemCenter[er][esr][ei][d] ) * + elemPerm[er][esr][ei][0][d] * faceNormal[kf][d]; + } + real64 uDotN = 0.0; + for( integer d = 0; d < 3; ++d ) + { + uDotN += ( faceCenter[kf][d] - elemCenter[er][esr][ei][d] ) * faceNormal[kf][d]; + } + // u . K n_out <= 0: half-transmissibility not positive, keep the flux live + if( uDotKn * uDotN <= 0.0 ) + { + label = 1; + } } } } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index 880df0b6268..880fff0a160 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -315,11 +315,17 @@ void SinglePhaseMixedMFD::computeFaceStencilLabels( DomainPartition & domain ) string_array const & ) { FaceManager & faceManager = mesh.getFaceManager(); - ElementRegionManager const & elemManager = mesh.getElemManager(); + ElementRegionManager & elemManager = mesh.getElemManager(); ElementRegionManager::ElementViewAccessor< arrayView1d< integer const > > const stencilFlagAccessor = elemManager.constructArrayViewAccessor< integer, 1 >( mixedMimetic::stencilFlag::key() ); + ElementRegionManager::ElementViewAccessor< arrayView2d< real64 const > > const elemCenterAccessor = + elemManager.constructViewAccessor< array2d< real64 >, arrayView2d< real64 const > >( ElementSubRegionBase::viewKeyStruct::elementCenterString() ); + + using PermeabilityAccessors = StencilMaterialAccessors< PermeabilityBase, fields::permeability::permeability >; + PermeabilityAccessors const permAccessors( elemManager, getName() ); + mixedMimeticKernels::FaceLabelKernel:: launch< parallelDevicePolicy<> >( faceManager.size(), faceManager.elementRegionList(), @@ -327,6 +333,10 @@ void SinglePhaseMixedMFD::computeFaceStencilLabels( DomainPartition & domain ) faceManager.elementList(), m_regionFilter.toViewConst(), stencilFlagAccessor.toNestedViewConst(), + faceManager.faceCenter(), + faceManager.faceNormal(), + elemCenterAccessor.toNestedViewConst(), + permAccessors.get( fields::permeability::permeability {} ), effectiveTpfa, faceManager.getField< mixedMimetic::faceStencilLabel >() ); } ); @@ -444,6 +454,29 @@ void SinglePhaseMixedMFD::computeMgrPointMarkers( DomainPartition const & domain pointMarkers.resize( dofManager.numLocalDofs() ); arrayView1d< integer > const markers = pointMarkers.toView(); + // an empty intermediate level is not supported by hypre MGR: when the marking produces + // no live MFD faces, relabel to two blocks and use the two-level condensed strategy + localIndex numLiveFaces = 0; + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel const & mesh, + string_array const & ) + { + FaceManager const & faceManager = mesh.getFaceManager(); + arrayView1d< globalIndex const > const faceDofNumber = + faceManager.getReference< array1d< globalIndex > >( faceDofKey ); + arrayView1d< integer const > const faceGhostRank = faceManager.ghostRank(); + arrayView1d< integer const > const faceStencilLabel = faceManager.getField< mixedMimetic::faceStencilLabel >(); + + RAJA::ReduceSum< parallelHostReduce, localIndex > numLive( 0 ); + forAll< parallelHostPolicy >( faceManager.size(), [=]( localIndex const kf ) + { + numLive += ( faceGhostRank[kf] < 0 && faceDofNumber[kf] >= 0 && faceStencilLabel[kf] == 1 ) ? 1 : 0; + } ); + numLiveFaces += numLive.get(); + } ); + globalIndex const globalNumLiveFaces = MpiWrapper::sum< globalIndex >( numLiveFaces ); + GEOS_LOG_RANK_0( GEOS_FMT( "{}: {} live MFD face dofs", getName(), globalNumLiveFaces ) ); + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, MeshLevel const & mesh, string_array const & regionNames ) From 05d834d0bd6b091ffe9f58fbcc6c9c1c4aa8e2b3 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 4 Sep 2026 15:24:02 -0700 Subject: [PATCH 25/64] =?UTF-8?q?wip:=20include=20RT=20product=20which=20i?= =?UTF-8?q?s=20the=20exact=20Raviart=E2=80=93Thomas=20on=20simplexes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mimeticInnerProducts/RTInnerProduct.hpp | 212 ++++++++++++++++++ .../unitTests/testMimeticInnerProducts.cpp | 199 +++++++++++++++- 2 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 src/coreComponents/finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp new file mode 100644 index 00000000000..4e6ba00f00c --- /dev/null +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp @@ -0,0 +1,212 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file RTInnerProduct.hpp + */ + +#ifndef GEOS_FINITEVOLUME_MIMETICINNERPRODUCTS_RTINNERPRODUCT_HPP_ +#define GEOS_FINITEVOLUME_MIMETICINNERPRODUCTS_RTINNERPRODUCT_HPP_ + +#include "finiteVolume/mimeticInnerProducts/MimeticInnerProductBase.hpp" +#include "finiteVolume/mimeticInnerProducts/MimeticInnerProductHelpers.hpp" + +namespace geos +{ +namespace mimeticInnerProduct +{ + +/** + * @class RTInnerProduct + * + * Provides the mimetic inner product that reproduces the lowest-order Raviart-Thomas mass matrix + * exactly on simplices, and extends it consistently to general polyhedra + */ +class RTInnerProduct +{ +public: + + /** + * @brief In a given element, recompute the transmissibility matrix as the exact inverse of the + * RT inner product matrix M, so that the hybrid and mixed forms are exactly dual. + * @param[in] nodePosition the position of the nodes + * @param[in] transMultiplier the transmissibility multipliers at the mesh faces + * @param[in] faceToNodes the map from the face to their nodes + * @param[in] elemToFaces the maps from the one-sided face to the corresponding face + * @param[in] elemCenter the center of the element + * @param[in] elemVolume the volume of the element + * @param[in] elemPerm the permeability in the element + * @param[in] lengthTolerance the tolerance used in the trans calculations + * @param[inout] transMatrix the output W = M^{-1}, with the face multipliers applied symmetrically + */ + template< localIndex NF > + GEOS_HOST_DEVICE + static void + compute( arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, + arrayView1d< real64 const > const & transMultiplier, + ArrayOfArraysView< localIndex const > const & faceToNodes, + arraySlice1d< localIndex const > const & elemToFaces, + arraySlice1d< real64 const > const & elemCenter, + real64 const & elemVolume, + real64 const (&elemPerm)[ 3 ], + real64 const & lengthTolerance, + arraySlice2d< real64 > const & transMatrix ); + + /** + * @brief Compute the mimetic inner product matrix M in a given element. + * @param[in] nodePosition the position of the nodes + * @param[in] faceToNodes the map from the face to their nodes + * @param[in] elemToFaces the maps from the one-sided face to the corresponding face + * @param[in] elemCenter the center of the element + * @param[in] elemVolume the volume of the element + * @param[in] elemPerm the permeability in the element + * @param[in] lengthTolerance the tolerance used in the trans calculations + * @param[inout] M the output inner product matrix + * + * @details M = C K^{-1} C^T / V + s ( I - Q Q^T ) with Q an orthonormal basis of the range of the + * K-weighted area normals; s = tr( C K^{-1} C^T / V ) / (d+2) is the unique scale matching the + * conforming RT0 element on simplices. + */ + template< localIndex NF > + GEOS_HOST_DEVICE + static void + computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, + ArrayOfArraysView< localIndex const > const & faceToNodes, + arraySlice1d< localIndex const > const & elemToFaces, + arraySlice1d< real64 const > const & elemCenter, + real64 const & elemVolume, + real64 const (&elemPerm)[ 3 ], + real64 const & lengthTolerance, + arraySlice2d< real64 > const & M ); +}; + +template< localIndex NF > +GEOS_HOST_DEVICE +void +RTInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, + ArrayOfArraysView< localIndex const > const & faceToNodes, + arraySlice1d< localIndex const > const & elemToFaces, + arraySlice1d< real64 const > const & elemCenter, + real64 const & elemVolume, + real64 const (&elemPerm)[ 3 ], + real64 const & lengthTolerance, + arraySlice2d< real64 > const & M ) +{ + real64 const areaTolerance = lengthTolerance * lengthTolerance; + + // 1) compute C, N, face areas and the consistency term M1 = C K^{-1} C^T / V + real64 C[ NF ][ 3 ] = {{ 0 }}; + real64 N[ NF ][ 3 ] = {{ 0 }}; + real64 faceArea[ NF ] = { 0.0 }; + real64 M1[ NF ][ NF ] = {{ 0 }}; + + MimeticInnerProductHelpers::computeCellToFaceGeometry< NF >( nodePosition, faceToNodes, elemToFaces, + elemCenter, areaTolerance, C, N, faceArea ); + MimeticInnerProductHelpers::computeConsistencyTerm< NF >( C, elemVolume, elemPerm, M1 ); + + // 2) build Q from the K-weighted area normals N K, so I - Q Q^T projects onto ker( (N K)^T ) + real64 q0[ NF ], q1[ NF ], q2[ NF ]; + real64 Qmat[ NF ][ 3 ]; + for( localIndex i = 0; i < NF; ++i ) + { + q0[i] = N[i][0] * elemPerm[0]; + q1[i] = N[i][1] * elemPerm[1]; + q2[i] = N[i][2] * elemPerm[2]; + } + MimeticInnerProductHelpers::orthonormalize< NF >( q0, q1, q2, Qmat ); + + // 3) compute P = I - Q Q^T + real64 P[ NF ][ NF ] = {{ 0 }}; + LvArray::tensorOps::addIdentity< NF >( P, -1.0 ); + LvArray::tensorOps::Rij_add_AikAjk< NF, 3 >( P, Qmat ); + LvArray::tensorOps::scale< NF, NF >( P, -1.0 ); + + // 4) s = tr(M1)/(d+2), the unique RT0-matching stabilization scale on simplices (d = 3) + real64 const s = LvArray::tensorOps::trace< NF >( M1 ) / 5.0; + + // 5) assemble M = M1 + s P + for( localIndex i = 0; i < NF; ++i ) + { + for( localIndex j = 0; j < NF; ++j ) + { + M[i][j] = M1[i][j] + s * P[i][j]; + } + } +} + +template< localIndex NF > +GEOS_HOST_DEVICE +void +RTInnerProduct::compute( arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const & nodePosition, + arrayView1d< real64 const > const & transMultiplier, + ArrayOfArraysView< localIndex const > const & faceToNodes, + arraySlice1d< localIndex const > const & elemToFaces, + arraySlice1d< real64 const > const & elemCenter, + real64 const & elemVolume, + real64 const (&elemPerm)[ 3 ], + real64 const & lengthTolerance, + arraySlice2d< real64 > const & transMatrix ) +{ + // 1) assemble M + computeM< NF >( nodePosition, faceToNodes, elemToFaces, elemCenter, + elemVolume, elemPerm, lengthTolerance, transMatrix ); + + // 2) W = M^{-1} by Gauss-Jordan on the SPD local matrix + real64 A[ NF ][ NF ]; + real64 W[ NF ][ NF ] = {{ 0 }}; + for( localIndex i = 0; i < NF; ++i ) + { + W[i][i] = 1.0; + for( localIndex j = 0; j < NF; ++j ) + { + A[i][j] = transMatrix[i][j]; + } + } + for( localIndex k = 0; k < NF; ++k ) + { + real64 const invPivot = 1.0 / A[k][k]; + for( localIndex j = 0; j < NF; ++j ) + { + A[k][j] *= invPivot; + W[k][j] *= invPivot; + } + for( localIndex i = 0; i < NF; ++i ) + { + if( i == k ) + continue; + real64 const factor = A[i][k]; + for( localIndex j = 0; j < NF; ++j ) + { + A[i][j] -= factor * A[k][j]; + W[i][j] -= factor * W[k][j]; + } + } + } + + // 3) apply the face transmissibility multipliers symmetrically + for( localIndex i = 0; i < NF; ++i ) + { + for( localIndex j = 0; j < NF; ++j ) + { + transMatrix[i][j] = W[i][j] * LvArray::math::sqrt( transMultiplier[elemToFaces[i]] * transMultiplier[elemToFaces[j]] ); + } + } +} + +} // end namespace mimeticInnerProduct + +} // end namespace geos + +#endif //GEOS_FINITEVOLUME_MIMETICINNERPRODUCTS_RTINNERPRODUCT_HPP_ diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp index 070e8dc09b5..1c0a4f98563 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp @@ -21,6 +21,7 @@ #include "finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp" #include "finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp" #include "finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp" +#include "finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp" #include "mainInterface/initialization.hpp" #include "mesh/FaceManager.hpp" #include "mesh/utilities/ComputationalGeometry.hpp" @@ -42,6 +43,7 @@ struct InnerProductType static constexpr integer SIMPLE_WITH_MULTIPLIERS = 4; static constexpr integer BDVLM = 5; static constexpr integer BDVLM_WITH_MULTIPLIERS = 6; + static constexpr integer RT = 7; }; static constexpr real64 consistency_tol = 1e-14; @@ -1818,6 +1820,11 @@ static void computeM_dispatch( int ipKind, BdVLMInnerProduct::computeM< NF >( node.toViewConst(), faceTonode.toViewConst(), elemToface.toSliceConst(), center, vol, Kvec, ltol, M ); } + else if( ipKind == InnerProductType::RT ) + { + RTInnerProduct::computeM< NF >( node.toViewConst(), faceTonode.toViewConst(), elemToface.toSliceConst(), + center, vol, Kvec, ltol, M ); + } } // solve a small dense linear system A x = b by Gaussian elimination with partial pivoting @@ -1989,6 +1996,12 @@ TEST( MimeticIP_MixedLinear, UnitCube_LinearPressure_BdVLM ) EXPECT_LT( err, mixed_consistency_tol ); } +TEST( MimeticIP_MixedLinear, UnitCube_LinearPressure_RT ) +{ + double err = computeLinearPressureMixed_error< 6 >( InnerProductType::RT ); + EXPECT_LT( err, mixed_consistency_tol ); +} + // =================== mixed form, case 1: with distortion (planar) =========================== TEST( MimeticIP_MixedLinear, Distortion_Planar_LinearPressure ) { @@ -2011,6 +2024,9 @@ TEST( MimeticIP_MixedLinear, Distortion_Planar_LinearPressure ) double errBDVLM = computeLinearPressureMixed_error< 6 >( InnerProductType::BDVLM, DistortionMode::Planar, eps ); EXPECT_LT( errBDVLM, mixed_consistency_tol ); + + double errRT = computeLinearPressureMixed_error< 6 >( InnerProductType::RT, DistortionMode::Planar, eps ); + EXPECT_LT( errRT, mixed_consistency_tol ); } for( int i = 0; i < neps; ++i ) @@ -2041,6 +2057,8 @@ TEST( MimeticIP_MixedLinear, Distortion_PlanarFunnel_LinearPressure ) mixed_consistency_tol ) << "eps = " << eps; EXPECT_LT( computeLinearPressureMixed_error< 6 >( InnerProductType::BDVLM, DistortionMode::PlanarFunnel, eps ), mixed_consistency_tol ) << "eps = " << eps; + EXPECT_LT( computeLinearPressureMixed_error< 6 >( InnerProductType::RT, DistortionMode::PlanarFunnel, eps ), + mixed_consistency_tol ) << "eps = " << eps; EXPECT_GT( computeLinearPressureMixed_error< 6 >( InnerProductType::TPFA, DistortionMode::PlanarFunnel, eps ), mixed_consistency_tol ) << "eps = " << eps; @@ -2071,6 +2089,9 @@ TEST( MimeticIP_MixedLinear, Distortion_NonPlanar_LinearPressure ) double errBDVLM = computeLinearPressureMixed_error< 6 >( InnerProductType::BDVLM, DistortionMode::NonPlanar, eps ); EXPECT_GT( errBDVLM, mixed_consistency_tol ); + + double errRT = computeLinearPressureMixed_error< 6 >( InnerProductType::RT, DistortionMode::NonPlanar, eps ); + EXPECT_GT( errRT, mixed_consistency_tol ); } } @@ -2163,7 +2184,8 @@ static double computeMixedIPConsistency_error( int ipKind, TEST( MimeticIP_MixedConsistency, UnitCube ) { for( int ip : { InnerProductType::TPFA, InnerProductType::QUASI_TPFA, - InnerProductType::SIMPLE, InnerProductType::BDVLM } ) + InnerProductType::SIMPLE, InnerProductType::BDVLM, + InnerProductType::RT } ) { EXPECT_LT( computeMixedIPConsistency_error< 6 >( ip ), mixed_consistency_tol ) << "ipKind = " << ip; } @@ -2180,6 +2202,8 @@ TEST( MimeticIP_MixedConsistency, Distortion_Planar ) mixed_consistency_tol ) << "eps = " << eps; EXPECT_LT( computeMixedIPConsistency_error< 6 >( InnerProductType::BDVLM, DistortionMode::Planar, eps ), mixed_consistency_tol ) << "eps = " << eps; + EXPECT_LT( computeMixedIPConsistency_error< 6 >( InnerProductType::RT, DistortionMode::Planar, eps ), + mixed_consistency_tol ) << "eps = " << eps; // TPFA is inconsistent on non K-orthogonal cells EXPECT_GT( computeMixedIPConsistency_error< 6 >( InnerProductType::TPFA, DistortionMode::Planar, eps ), @@ -2197,6 +2221,8 @@ TEST( MimeticIP_MixedConsistency, Distortion_PlanarFunnel ) mixed_consistency_tol ) << "eps = " << eps; EXPECT_LT( computeMixedIPConsistency_error< 6 >( InnerProductType::BDVLM, DistortionMode::PlanarFunnel, eps ), mixed_consistency_tol ) << "eps = " << eps; + EXPECT_LT( computeMixedIPConsistency_error< 6 >( InnerProductType::RT, DistortionMode::PlanarFunnel, eps ), + mixed_consistency_tol ) << "eps = " << eps; EXPECT_GT( computeMixedIPConsistency_error< 6 >( InnerProductType::TPFA, DistortionMode::PlanarFunnel, eps ), mixed_consistency_tol ) << "eps = " << eps; @@ -2354,6 +2380,11 @@ static double computeHybridMixedDuality_error( int ipKind, real64 h ) BdVLMInnerProduct::compute< NF >( node.toViewConst(), mult.toViewConst(), faceTonode.toViewConst(), elemToface.toSliceConst(), cc, vol, Kvec, ltol, T.toSlice() ); } + else if( ipKind == InnerProductType::RT ) + { + RTInnerProduct::compute< NF >( node.toViewConst(), mult.toViewConst(), faceTonode.toViewConst(), + elemToface.toSliceConst(), cc, vol, Kvec, ltol, T.toSlice() ); + } computeM_dispatch< NF >( ipKind, node, faceTonode, elemToface, cc.toSliceConst(), vol, Kvec, ltol, M.toSlice() ); @@ -2400,6 +2431,166 @@ TEST( MimeticIP_MixedDuality, BdVLM ) EXPECT_LT( computeHybridMixedDuality_error< 6 >( InnerProductType::BDVLM, 0.2 ), duality_tol ); } +TEST( MimeticIP_MixedDuality, RT ) +{ + // compute() returns the exact inverse of computeM(), so duality holds to roundoff on any cell + EXPECT_LT( computeHybridMixedDuality_error< 6 >( InnerProductType::RT, 1.0 ), duality_tol ); + EXPECT_LT( computeHybridMixedDuality_error< 6 >( InnerProductType::RT, 0.2 ), duality_tol ); +} + +//======================== RT0 Exactness Test ============================= +// The RT inner product must reproduce the conforming lowest-order Raviart-Thomas mass matrix +// exactly on simplices. Reference element and dof conventions: DefElement, degree-1 Raviart-Thomas, +// https://defelement.org/elements/raviart-thomas.html (facet normal integral moments, H(div)); +// dofs expressed here as outward-normal fluxes, matrices computed exactly with sympy. + +static constexpr real64 rt0_exactness_tol = 1e-14; + +TEST( MimeticIP_RT0Exactness, Tetrahedron ) +{ + // reference tetrahedron (0,0,0)-(1,0,0)-(0,1,0)-(0,0,1); face f opposite vertex f + localIndex constexpr numNodes = 4; + localIndex constexpr numFaces = 4; + + array2d< real64, nodes::REFERENCE_POSITION_PERM > nodePosition; + nodePosition.resize( numNodes, 3 ); + nodePosition( 0, 0 ) = 0; nodePosition( 0, 1 ) = 0; nodePosition( 0, 2 ) = 0; + nodePosition( 1, 0 ) = 1; nodePosition( 1, 1 ) = 0; nodePosition( 1, 2 ) = 0; + nodePosition( 2, 0 ) = 0; nodePosition( 2, 1 ) = 1; nodePosition( 2, 2 ) = 0; + nodePosition( 3, 0 ) = 0; nodePosition( 3, 1 ) = 0; nodePosition( 3, 2 ) = 1; + + FaceManager::NodeMapType faceToNodes; + faceToNodes.resize( numFaces ); + for( localIndex f = 0; f < numFaces; ++f ) + faceToNodes.resizeArray( f, 3 ); + faceToNodes( 0, 0 ) = 1; faceToNodes( 0, 1 ) = 2; faceToNodes( 0, 2 ) = 3; + faceToNodes( 1, 0 ) = 0; faceToNodes( 1, 1 ) = 2; faceToNodes( 1, 2 ) = 3; + faceToNodes( 2, 0 ) = 0; faceToNodes( 2, 1 ) = 1; faceToNodes( 2, 2 ) = 3; + faceToNodes( 3, 0 ) = 0; faceToNodes( 3, 1 ) = 1; faceToNodes( 3, 2 ) = 2; + + array1d< localIndex > elemToFaces; + elemToFaces.resize( numFaces ); + for( localIndex f = 0; f < numFaces; ++f ) + elemToFaces( f ) = f; + + stackArray1d< real64, 3 > center( 3 ); + center[0] = 0.25; center[1] = 0.25; center[2] = 0.25; + real64 const vol = 1.0 / 6.0; + real64 constexpr ltol = 1e-12; + + // exact RT0 mass matrices in outward-flux dofs (sympy, exact rational integration) + real64 const refIso[4][4] = + { { 1.0/5.0, 1.0/30.0, 1.0/30.0, 1.0/30.0 }, + { 1.0/30.0, 8.0/15.0, -2.0/15.0, -2.0/15.0 }, + { 1.0/30.0, -2.0/15.0, 8.0/15.0, -2.0/15.0 }, + { 1.0/30.0, -2.0/15.0, -2.0/15.0, 8.0/15.0 } }; + real64 const refAniso[4][4] = + { { 7.0/60.0, -1.0/20.0, 1.0/30.0, 3.0/40.0 }, + { -1.0/20.0, 9.0/20.0, -2.0/15.0, -11.0/120.0 }, + { 1.0/30.0, -2.0/15.0, 17.0/60.0, -1.0/120.0 }, + { 3.0/40.0, -11.0/120.0, -1.0/120.0, 1.0/5.0 } }; + + real64 const permIso[3] = { 1.0, 1.0, 1.0 }; + real64 const permAniso[3] = { 1.0, 2.0, 4.0 }; + + stackArray2d< real64, 16 > M( 4, 4 ); + for( int c = 0; c < 2; ++c ) + { + real64 const (&perm)[3] = ( c == 0 ) ? permIso : permAniso; + real64 const (&ref)[4][4] = ( c == 0 ) ? refIso : refAniso; + + M.template setValues< parallelHostPolicy >( 0.0 ); + RTInnerProduct::computeM< 4 >( nodePosition.toViewConst(), + faceToNodes.toViewConst(), + elemToFaces.toSliceConst(), + center.toSliceConst(), + vol, perm, ltol, M.toSlice() ); + + for( int i = 0; i < 4; ++i ) + { + for( int j = 0; j < 4; ++j ) + { + EXPECT_NEAR( M( i, j ), ref[i][j], rt0_exactness_tol ) << "K case " << c << " entry " << i << "," << j; + } + } + } +} + +TEST( MimeticIP_RT0Exactness, Triangle2D ) +{ + // The stabilization rule s = tr(M1)/(d+2) is dimension generic; computeM() hard-codes d = 3, + // so the d = 2 instance is built directly here on the reference triangle (0,0)-(1,0)-(0,1), + // edge f opposite vertex f, and compared with the exact 2D RT0 mass matrix. + real64 const verts[3][2] = { { 0.0, 0.0 }, { 1.0, 0.0 }, { 0.0, 1.0 } }; + real64 const area = 0.5; + real64 const xc[2] = { 1.0/3.0, 1.0/3.0 }; + + real64 const refIso[3][3] = + { { 1.0/6.0, 0.0, 0.0 }, + { 0.0, 1.0/3.0, -1.0/6.0 }, + { 0.0, -1.0/6.0, 1.0/3.0 } }; + real64 const refAniso[3][3] = + { { 1.0/9.0, -1.0/18.0, 1.0/18.0 }, + { -1.0/18.0, 5.0/18.0, -1.0/9.0 }, + { 1.0/18.0, -1.0/9.0, 1.0/6.0 } }; + + real64 const permIso[2] = { 1.0, 1.0 }; + real64 const permAniso[2] = { 1.0, 3.0 }; + + for( int c = 0; c < 2; ++c ) + { + real64 const (&perm)[2] = ( c == 0 ) ? permIso : permAniso; + real64 const (&ref)[3][3] = ( c == 0 ) ? refIso : refAniso; + + // C rows: edge midpoint - cell centroid; N rows: |e| K n_e with outward unit normal + real64 C[3][2], N[3][2]; + for( int f = 0; f < 3; ++f ) + { + int const a = ( f + 1 ) % 3, b = ( f + 2 ) % 3; + real64 const ex = verts[b][0] - verts[a][0], ey = verts[b][1] - verts[a][1]; + real64 const len = std::sqrt( ex * ex + ey * ey ); + real64 const mid[2] = { 0.5 * ( verts[a][0] + verts[b][0] ), 0.5 * ( verts[a][1] + verts[b][1] ) }; + real64 n[2] = { ey / len, -ex / len }; + if( n[0] * ( mid[0] - xc[0] ) + n[1] * ( mid[1] - xc[1] ) < 0.0 ) + { + n[0] = -n[0]; n[1] = -n[1]; + } + for( int d = 0; d < 2; ++d ) + { + C[f][d] = mid[d] - xc[d]; + N[f][d] = len * perm[d] * n[d]; + } + } + + // M1 = C (A K)^{-1} C^T + real64 M1[3][3]; + for( int i = 0; i < 3; ++i ) + for( int j = 0; j < 3; ++j ) + M1[i][j] = ( C[i][0] * C[j][0] / perm[0] + C[i][1] * C[j][1] / perm[1] ) / area; + + // Q = Gram-Schmidt of the two columns of N; P = I - Q Q^T + real64 q0[3] = { N[0][0], N[1][0], N[2][0] }; + real64 q1[3] = { N[0][1], N[1][1], N[2][1] }; + real64 nrm = std::sqrt( q0[0]*q0[0] + q0[1]*q0[1] + q0[2]*q0[2] ); + for( int i = 0; i < 3; ++i ) q0[i] /= nrm; + real64 const dot = q0[0]*q1[0] + q0[1]*q1[1] + q0[2]*q1[2]; + for( int i = 0; i < 3; ++i ) q1[i] -= dot * q0[i]; + nrm = std::sqrt( q1[0]*q1[0] + q1[1]*q1[1] + q1[2]*q1[2] ); + for( int i = 0; i < 3; ++i ) q1[i] /= nrm; + + real64 const s = ( M1[0][0] + M1[1][1] + M1[2][2] ) / 4.0; // tr(M1)/(d+2), d = 2 + + for( int i = 0; i < 3; ++i ) + { + for( int j = 0; j < 3; ++j ) + { + real64 const Pij = ( i == j ? 1.0 : 0.0 ) - q0[i]*q0[j] - q1[i]*q1[j]; + EXPECT_NEAR( M1[i][j] + s * Pij, ref[i][j], rt0_exactness_tol ) << "K case " << c << " entry " << i << "," << j; + } + } + } +} + //======================== TPFA Reduction Test ============================= // On K-orthogonal grids: T = diag( 2 k_d ), M = T^{-1} on the unit cube. // BdVLM reduces iff gamma = 2/NF * trace (isotropic K only); Eq. (3.67) gamma = @@ -2790,6 +2981,12 @@ TEST( Hydrostatic, GravityConsistency_NoDistortion_BdVLM ) EXPECT_LE( err, consistency_tol ); } +TEST( Hydrostatic, GravityConsistency_NoDistortion_RT ) +{ + double err = computeGravityConsistency_error< 6 >( InnerProductType::RT, DistortionMode::None, 0.0, 1.0, 1.0, 1.0 ); + EXPECT_LE( err, consistency_tol ); +} + // =================== case 1: with distortion (planar) =========================== TEST( Hydrostatic, GravityConsistency_Distortion_Planar ) { From 6f722885fb13e9d3f173e8302291f56034cf0370 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Sat, 5 Sep 2026 11:34:05 -0700 Subject: [PATCH 26/64] wip: improve testing for RT product --- .../hybrid_riesz_mfd_base.xml | 129 ++++++++++++++++++ .../MimeticInnerProductDispatch.hpp | 11 ++ .../mimeticInnerProducts/RTInnerProduct.hpp | 2 +- .../unitTests/testMimeticInnerProducts.cpp | 9 +- 4 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_base.xml diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_base.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_base.xml new file mode 100644 index 00000000000..37c92ae4c9d --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_base.xml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreComponents/finiteVolume/MimeticInnerProductDispatch.hpp b/src/coreComponents/finiteVolume/MimeticInnerProductDispatch.hpp index 560c043ea8f..7461b06ca90 100644 --- a/src/coreComponents/finiteVolume/MimeticInnerProductDispatch.hpp +++ b/src/coreComponents/finiteVolume/MimeticInnerProductDispatch.hpp @@ -26,6 +26,7 @@ #include "finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp" #include "finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp" #include "finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp" +#include "finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp" #include "LvArray/src/system.hpp" namespace geos @@ -49,6 +50,8 @@ struct MimeticInnerProductTypeStrings static constexpr auto Simple = "simple"; /// string for the inner product of Beirao da Veiga, Lipnikov, Manzini static constexpr auto BdVLM = "beiraoDaVeigaLipnikovManzini"; + /// string for the Raviart-Thomas inner product (exact RT0 on simplices) + static constexpr auto RT = "RT"; }; /** @@ -82,6 +85,10 @@ mimeticInnerProductDispatch( MimeticInnerProductBase const & input, { lambda( *ptr5 ); } + else if( auto const * const ptr6 = dynamic_cast< RTInnerProduct const * >(&input) ) + { + lambda( *ptr6 ); + } else { GEOS_ERROR( GEOS_FMT( "mimeticInnerProductDispatch() is not implemented for input of {}", @@ -120,6 +127,10 @@ mimeticInnerProductDispatch( MimeticInnerProductBase & input, { lambda( *ptr5 ); } + else if( auto * const ptr6 = dynamic_cast< RTInnerProduct * >(&input) ) + { + lambda( *ptr6 ); + } else { GEOS_ERROR( GEOS_FMT( "mimeticInnerProductDispatch() is not implemented for input of {}", diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp index 4e6ba00f00c..5972a59edf9 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp @@ -34,7 +34,7 @@ namespace mimeticInnerProduct * Provides the mimetic inner product that reproduces the lowest-order Raviart-Thomas mass matrix * exactly on simplices, and extends it consistently to general polyhedra */ -class RTInnerProduct +class RTInnerProduct : public MimeticInnerProductBase { public: diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp index 1c0a4f98563..24ac7f970d4 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp @@ -2572,11 +2572,14 @@ TEST( MimeticIP_RT0Exactness, Triangle2D ) real64 q0[3] = { N[0][0], N[1][0], N[2][0] }; real64 q1[3] = { N[0][1], N[1][1], N[2][1] }; real64 nrm = std::sqrt( q0[0]*q0[0] + q0[1]*q0[1] + q0[2]*q0[2] ); - for( int i = 0; i < 3; ++i ) q0[i] /= nrm; + for( int i = 0; i < 3; ++i ) + q0[i] /= nrm; real64 const dot = q0[0]*q1[0] + q0[1]*q1[1] + q0[2]*q1[2]; - for( int i = 0; i < 3; ++i ) q1[i] -= dot * q0[i]; + for( int i = 0; i < 3; ++i ) + q1[i] -= dot * q0[i]; nrm = std::sqrt( q1[0]*q1[0] + q1[1]*q1[1] + q1[2]*q1[2] ); - for( int i = 0; i < 3; ++i ) q1[i] /= nrm; + for( int i = 0; i < 3; ++i ) + q1[i] /= nrm; real64 const s = ( M1[0][0] + M1[1][1] + M1[2][2] ) / 4.0; // tr(M1)/(d+2), d = 2 From df29f564c69e1033181c2c5fb91bd8df0bfb8336 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Sat, 5 Sep 2026 11:35:10 -0700 Subject: [PATCH 27/64] wip: include a riesz preconditioner --- .../hybrid_riesz_mfd_l0.xml | 15 + .../hybrid_riesz_mfd_l1.xml | 15 + .../hybrid_riesz_mfd_l2.xml | 15 + .../hybrid_riesz_mfd_l3.xml | 15 + .../hybrid_riesz_mfd_l4.xml | 15 + .../mfd_ads_test.xml | 143 ++++ .../interfaces/hypre/HyprePreconditioner.cpp | 6 + .../interfaces/hypre/HypreRieszMFD.cpp | 771 ++++++++++++++++++ .../interfaces/hypre/HypreRieszMFD.hpp | 59 ++ .../interfaces/hypre/hypredrive.cpp | 12 + .../mgrStrategies/SinglePhaseMixedMFD.hpp | 38 +- .../testLinearSolverParametersEnums.cpp | 1 + .../utilities/LinearSolverParameters.hpp | 32 +- .../MixedMimeticDiscretization.cpp | 10 +- .../mixedMimetic/MixedMimeticDispatch.hpp | 5 + .../adaptivity/GlobalAdaptationKernels.hpp | 36 +- .../fluidFlow/SinglePhaseMixedMFD.cpp | 548 ++++++++++--- .../fluidFlow/SinglePhaseMixedMFD.hpp | 18 +- 18 files changed, 1586 insertions(+), 168 deletions(-) create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l0.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l1.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l2.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l3.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l4.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_ads_test.xml create mode 100644 src/coreComponents/linearAlgebra/interfaces/hypre/HypreRieszMFD.cpp create mode 100644 src/coreComponents/linearAlgebra/interfaces/hypre/HypreRieszMFD.hpp diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l0.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l0.xml new file mode 100644 index 00000000000..be26bc67a64 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l0.xml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l1.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l1.xml new file mode 100644 index 00000000000..56893420c60 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l1.xml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l2.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l2.xml new file mode 100644 index 00000000000..ec0732e1873 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l2.xml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l3.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l3.xml new file mode 100644 index 00000000000..b3c596b02e9 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l3.xml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l4.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l4.xml new file mode 100644 index 00000000000..53d949fb3e5 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l4.xml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_ads_test.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_ads_test.xml new file mode 100644 index 00000000000..c23e647954a --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_ads_test.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/HyprePreconditioner.cpp b/src/coreComponents/linearAlgebra/interfaces/hypre/HyprePreconditioner.cpp index 1a6e2691b34..ec159d306ac 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/HyprePreconditioner.cpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/HyprePreconditioner.cpp @@ -19,6 +19,7 @@ #include "HyprePreconditioner.hpp" #include "HypreMGR.hpp" +#include "HypreRieszMFD.hpp" #include "linearAlgebra/DofManager.hpp" #include "linearAlgebra/interfaces/hypre/HypreUtils.hpp" @@ -362,6 +363,11 @@ void HyprePreconditioner::create( DofManager const * const dofManager ) m_precond->destroy = hypre::SuperLUDistDestroy; break; } + case LinearSolverParameters::PreconditionerType::riesz: + { + hypre::createRieszMFD( m_params, *m_precond ); + break; + } default: { GEOS_ERROR( GEOS_FMT( "Preconditioner type not supported in hypre interface: {}", m_params.preconditionerType ) ); diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreRieszMFD.cpp b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreRieszMFD.cpp new file mode 100644 index 00000000000..609d2b8aa21 --- /dev/null +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreRieszMFD.cpp @@ -0,0 +1,771 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file HypreRieszMFD.cpp + */ + +#include "HypreRieszMFD.hpp" + +#include "linearAlgebra/common/common.hpp" + +#include "common/MpiWrapper.hpp" + +#include +#include +#include +#include <_hypre_utilities.h> +#include <_hypre_parcsr_mv.h> + +#include +#include +#include +#include +#include +#include +#include + +namespace geos +{ + +namespace hypre +{ + +namespace +{ + +/// dof markers set by the physics solver +constexpr HYPRE_Int condensedFaceMarker = 0; +constexpr HYPRE_Int liveFaceMarker = 1; +constexpr HYPRE_Int pressureMarker = 2; + +using RowMap = std::map< HYPRE_Int, HYPRE_Real >; + +/// Owning container for the Riesz-map preconditioner. The hypre_Solver base is first so the +/// handle is also usable where hypre dispatches through the base struct (e.g. as an MGR solver). +struct RieszMFDData +{ + hypre_Solver base{}; + + stdVector< HYPRE_Int > blockRow; ///< row of each local dof within its block + stdVector< HYPRE_Int > kindOf; ///< marker of each local dof + stdVector< HYPRE_Int > dofOfFlux0; ///< block row -> local dof, condensed faces + stdVector< HYPRE_Int > dofOfFlux1; ///< block row -> local dof, live faces + stdVector< HYPRE_Int > dofOfPres; ///< block row -> local dof, pressures + + HYPRE_IJMatrix curlIJ{}; ///< discrete curl of the live-face sub-complex + HYPRE_IJMatrix gradIJ{}; ///< discrete gradient of the live-face sub-complex + HYPRE_IJVector coordIJ[3]{}; ///< active vertex coordinates + + stdVector< HYPRE_Int > isMfd; ///< 1 for the pressure of an MFD cell, 0 for a TPFA cell + stdVector< HYPRE_Real > normScale; ///< (l_e/D)^2 of each pressure row: geometric factor of the L2 weight + + HYPRE_IJMatrix nIJ{}; ///< M + B_M^T W_M^{-1} B_M on the live faces + HYPRE_IJMatrix pIJ{}; ///< pressure block: two-point couplings, L2 mass of the MFD cells, interface links + HYPRE_IJMatrix b0tIJ{}; ///< F0, condensed-face rows x pressures + stdVector< HYPRE_Real > invD0; ///< inverse diagonal of the condensed-face closure rows + stdVector< HYPRE_Int > pinned; ///< 1 for a live flux dof out of the space + stdVector< HYPRE_Real > invDiagPinned; ///< exact diagonal solve of the pinned dofs + stdVector< HYPRE_Real > rowScale; ///< gamma / s_e: takes a conservation residual to the symmetrized system + stdVector< HYPRE_Real > invWm; ///< 1 / (W_M + C_M) on the MFD pressures, 0 elsewhere + bool adsActive = false; ///< false when no live dof is left for the flux block + HYPRE_Solver ads{}; + HYPRE_Solver pcg{}; ///< CG on the flux block preconditioned by ADS + HYPRE_Solver amg{}; + + HYPRE_IJVector r1{}, x1{}, rt{}, xt{}, xp{}, t0{}; ///< block-sized work vectors +}; + +/// PCG re-setup hook: ADS is set up once, before PCG +HYPRE_Int noSetup( HYPRE_Solver, HYPRE_ParCSRMatrix, HYPRE_ParVector, HYPRE_ParVector ) +{ + return 0; +} + +HYPRE_IJMatrix createIJFromRowMaps( stdVector< RowMap > const & rows, + HYPRE_BigInt const numCols ) +{ + HYPRE_BigInt const numRows = LvArray::integerConversion< HYPRE_BigInt >( rows.size() ); + HYPRE_IJMatrix ij{}; + GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixCreate( MPI_COMM_WORLD, 0, numRows - 1, 0, numCols - 1, &ij ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixSetObjectType( ij, HYPRE_PARCSR ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixInitialize( ij ) ); + stdVector< HYPRE_BigInt > cols; + stdVector< HYPRE_Real > vals; + for( HYPRE_BigInt i = 0; i < numRows; ++i ) + { + RowMap const & row = rows[i]; + HYPRE_Int nnz = LvArray::integerConversion< HYPRE_Int >( row.size() ); + if( nnz == 0 ) + { + continue; + } + cols.clear(); + vals.clear(); + for( auto const & [c, v] : row ) + { + cols.push_back( c ); + vals.push_back( v ); + } + GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixSetValues( ij, 1, &nnz, &i, cols.data(), vals.data() ) ); + } + GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixAssemble( ij ) ); + return ij; +} + +HYPRE_IJMatrix createIJFromCSR( arrayView1d< globalIndex const > const & rowPtr, + arrayView1d< globalIndex const > const & cols, + arrayView1d< real64 const > const & vals, + globalIndex const numCols ) +{ + HYPRE_BigInt const numRows = LvArray::integerConversion< HYPRE_BigInt >( rowPtr.size() - 1 ); + HYPRE_IJMatrix ij{}; + GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixCreate( MPI_COMM_WORLD, 0, numRows - 1, 0, numCols - 1, &ij ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixSetObjectType( ij, HYPRE_PARCSR ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixInitialize( ij ) ); + stdVector< HYPRE_BigInt > colIdx; + for( HYPRE_BigInt i = 0; i < numRows; ++i ) + { + HYPRE_Int nnz = LvArray::integerConversion< HYPRE_Int >( rowPtr[i + 1] - rowPtr[i] ); + if( nnz == 0 ) + { + continue; + } + colIdx.assign( nnz, 0 ); + for( HYPRE_Int k = 0; k < nnz; ++k ) + { + colIdx[k] = LvArray::integerConversion< HYPRE_BigInt >( cols[rowPtr[i] + k] ); + } + GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixSetValues( ij, 1, &nnz, &i, colIdx.data(), &vals[rowPtr[i]] ) ); + } + GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixAssemble( ij ) ); + return ij; +} + +HYPRE_IJVector createIJVector( HYPRE_BigInt const n, HYPRE_Real const * const vals ) +{ + HYPRE_IJVector ij{}; + GEOS_LAI_CHECK_ERROR( HYPRE_IJVectorCreate( MPI_COMM_WORLD, 0, n - 1, &ij ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_IJVectorSetObjectType( ij, HYPRE_PARCSR ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_IJVectorInitialize( ij ) ); + if( n > 0 ) + { + stdVector< HYPRE_BigInt > idx( n ); + std::iota( idx.begin(), idx.end(), HYPRE_BigInt( 0 ) ); + if( vals != nullptr ) + { + GEOS_LAI_CHECK_ERROR( HYPRE_IJVectorSetValues( ij, n, idx.data(), vals ) ); + } + } + GEOS_LAI_CHECK_ERROR( HYPRE_IJVectorAssemble( ij ) ); + return ij; +} + +/// ADS builds its vector interpolation from the linear part of the coordinate functions; a +/// projected coordinate system carries an offset that dwarfs the domain and turns that linear +/// part into a rounding error, so the coordinates are shifted to the origin +HYPRE_IJVector createShiftedCoordinates( arrayView1d< real64 const > const & vals ) +{ + HYPRE_BigInt const n = LvArray::integerConversion< HYPRE_BigInt >( vals.size() ); + real64 const lo = n > 0 ? *std::min_element( vals.begin(), vals.end() ) : 0.0; + stdVector< HYPRE_Real > shifted( n ); + for( HYPRE_BigInt i = 0; i < n; ++i ) + { + shifted[i] = vals[i] - lo; + } + return createIJVector( n, shifted.data() ); +} + +hypre_ParCSRMatrix * parCSROf( HYPRE_IJMatrix const ij ) +{ + hypre_ParCSRMatrix * mat{}; + GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixGetObject( ij, reinterpret_cast< void * * >( &mat ) ) ); + return mat; +} + +hypre_ParVector * parVectorOf( HYPRE_IJVector const ij ) +{ + hypre_ParVector * vec{}; + GEOS_LAI_CHECK_ERROR( HYPRE_IJVectorGetObject( ij, reinterpret_cast< void * * >( &vec ) ) ); + return vec; +} + +HYPRE_Real * dataOf( HYPRE_IJVector const ij ) +{ + return hypre_VectorData( hypre_ParVectorLocalVector( parVectorOf( ij ) ) ); +} + +void destroyIJ( HYPRE_IJMatrix & ij ) +{ + if( ij ) + { + GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixDestroy( ij ) ); + ij = nullptr; + } +} + +void destroyIJ( HYPRE_IJVector & ij ) +{ + if( ij ) + { + GEOS_LAI_CHECK_ERROR( HYPRE_IJVectorDestroy( ij ) ); + ij = nullptr; + } +} + +void destroySetupObjects( RieszMFDData & data ) +{ + if( data.pcg ) + { + GEOS_LAI_CHECK_ERROR( HYPRE_ParCSRPCGDestroy( data.pcg ) ); + data.pcg = nullptr; + } + if( data.ads ) + { + GEOS_LAI_CHECK_ERROR( HYPRE_ADSDestroy( data.ads ) ); + data.ads = nullptr; + } + if( data.amg ) + { + GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGDestroy( data.amg ) ); + data.amg = nullptr; + } + destroyIJ( data.nIJ ); + destroyIJ( data.pIJ ); + destroyIJ( data.b0tIJ ); + destroyIJ( data.r1 ); + destroyIJ( data.x1 ); + destroyIJ( data.rt ); + destroyIJ( data.xt ); + destroyIJ( data.xp ); + destroyIJ( data.t0 ); +} + +/** + * The assembled system, in the unknowns (q1 live fluxes, q0 condensed fluxes, p pressures), is + * + * [ M 0 F ] [ q1 ] F = -gamma B^T, B the +-1 face-to-cell incidence of the live faces, + * [ 0 D0 F0 ] [ q0 ] s_e B_e the conservation row of cell e (its own scale s_e), + * [ s B 0 C ] [ p ] C = s (C_acc + S_TT), S_TT the two-point Laplacian of the TPFA cells. + * + * Multiplying the conservation rows by gamma / s_e gives the symmetrized saddle point with + * gamma B and its transpose, on which the Riesz map of the product norm + * + * H(div; MFD region) x L2(MFD cells) x H1(TPFA cells) + * + * is P = diag( M + gamma^2 B_M^T W_M^{-1} B_M , W_M + C_M , C_T + S_TT ): + * the flux block by CG under ADS, the MFD pressures by their diagonal, the TPFA pressures by one + * AMG cycle. The interface faces are not weighted: they are bounded by the normal-trace inequality. + * The condensed fluxes are recovered exactly from their closure rows. + */ +HYPRE_Int rieszSetup( HYPRE_Solver solver, + HYPRE_ParCSRMatrix A, + HYPRE_ParVector, + HYPRE_ParVector ) +{ + RieszMFDData & data = *reinterpret_cast< RieszMFDData * >( solver ); + destroySetupObjects( data ); + + hypre_ParCSRMatrix * const mat = reinterpret_cast< hypre_ParCSRMatrix * >( A ); + hypre_CSRMatrix * const diag = hypre_ParCSRMatrixDiag( mat ); + HYPRE_Int const n = hypre_CSRMatrixNumRows( diag ); + HYPRE_Int const * const ia = hypre_CSRMatrixI( diag ); + HYPRE_Int const * const ja = hypre_CSRMatrixJ( diag ); + HYPRE_Real const * const va = hypre_CSRMatrixData( diag ); + GEOS_ERROR_IF_NE_MSG( LvArray::integerConversion< std::size_t >( n ), data.kindOf.size(), + "Riesz MFD preconditioner: the dof markers do not match the system size" ); + + HYPRE_Int const nf0 = LvArray::integerConversion< HYPRE_Int >( data.dofOfFlux0.size() ); + HYPRE_Int const nf1 = LvArray::integerConversion< HYPRE_Int >( data.dofOfFlux1.size() ); + HYPRE_Int const np = LvArray::integerConversion< HYPRE_Int >( data.dofOfPres.size() ); + + // 1) read the blocks off the assembled system + stdVector< RowMap > m1( nf1 ), c( np ), b0t( nf0 ); + stdVector< RowMap > b1( np ); // conservation rows x live faces + stdVector< stdVector< std::pair< HYPRE_Int, HYPRE_Real > > > b1Cols( nf1 ); // live face -> (cell, value) + HYPRE_Real gamma = 0.0; // magnitude of the (1,2) entries: |F| = gamma + data.invD0.assign( nf0, 0.0 ); + for( HYPRE_Int i = 0; i < n; ++i ) + { + HYPRE_Int const ki = data.kindOf[i]; + HYPRE_Int const ri = data.blockRow[i]; + for( HYPRE_Int k = ia[i]; k < ia[i + 1]; ++k ) + { + HYPRE_Int const j = ja[k]; + HYPRE_Int const kj = data.kindOf[j]; + HYPRE_Int const rj = data.blockRow[j]; + HYPRE_Real const v = va[k]; + if( ki == liveFaceMarker && kj == liveFaceMarker ) + { + m1[ri][rj] += v; + } + else if( ki == pressureMarker && kj == liveFaceMarker ) + { + b1[ri][rj] += v; + b1Cols[rj].emplace_back( ri, v ); + } + else if( ki == liveFaceMarker && kj == pressureMarker ) + { + gamma = std::max( gamma, std::abs( v ) ); + } + else if( ki == pressureMarker && kj == pressureMarker ) + { + c[ri][rj] += v; + } + else if( ki == condensedFaceMarker && kj == pressureMarker ) + { + b0t[ri][rj] += v; + } + else if( ki == condensedFaceMarker && kj == condensedFaceMarker && i == j ) + { + data.invD0[ri] = v != 0.0 ? 1.0 / v : 0.0; + } + } + } + + // 2) a live flux dof the divergence never touches (no-flow face) or whose row couples + // negligibly against its own diagonal is out of the space: solved by its diagonal, outside the block + HYPRE_Real bMaxAll = 0.0; + for( HYPRE_Int e = 0; e < np; ++e ) + { + for( auto const & [f, bf] : b1[e] ) + { + bMaxAll = std::max( bMaxAll, std::abs( bf ) ); + } + } + data.pinned.assign( nf1, 0 ); + data.invDiagPinned.assign( nf1, 0.0 ); + HYPRE_Int numFree = 0; + for( HYPRE_Int f = 0; f < nf1; ++f ) + { + HYPRE_Real colMax = 0.0; + for( auto const & [e, be] : b1Cols[f] ) + { + colMax = std::max( colMax, std::abs( be ) ); + } + HYPRE_Real offMax = colMax; + for( auto const & [g, v] : m1[f] ) + { + offMax = g != f ? std::max( offMax, std::abs( v ) ) : offMax; + } + auto const dIt = m1[f].find( f ); + HYPRE_Real const diagAbs = dIt != m1[f].end() ? std::abs( dIt->second ) : 0.0; + if( colMax <= 1e-12 * bMaxAll || offMax <= 1e-10 * diagAbs ) + { + data.pinned[f] = 1; + data.invDiagPinned[f] = diagAbs > 0.0 ? 1.0 / dIt->second : 1.0; + for( auto const & [g, v] : m1[f] ) + { + if( g != f ) + { + m1[g].erase( f ); + } + } + HYPRE_Real const diagVal = diagAbs > 0.0 ? dIt->second : 1.0; + m1[f].clear(); + m1[f][f] = diagVal; + b1Cols[f].clear(); + } + else + { + ++numFree; + } + } + for( HYPRE_Int e = 0; e < np; ++e ) + { + for( auto it = b1[e].begin(); it != b1[e].end(); ) + { + it = data.pinned[it->first] ? b1[e].erase( it ) : std::next( it ); + } + } + + // 3) the scale s_e of each conservation row is the magnitude of its incidence entries; rows + // without a live face take the median of the others (the scaling is per field) + stdVector< HYPRE_Real > s( np, 0.0 ), known; + for( HYPRE_Int e = 0; e < np; ++e ) + { + for( auto const & [f, bf] : b1[e] ) + { + s[e] = std::max( s[e], std::abs( bf ) ); + } + if( s[e] > 0.0 ) + { + known.push_back( s[e] ); + } + } + HYPRE_Real sMedian = gamma > 0.0 ? gamma : 1.0; + if( !known.empty() ) + { + std::nth_element( known.begin(), known.begin() + known.size() / 2, known.end() ); + sMedian = known[known.size() / 2]; + } + if( gamma == 0.0 ) + { + gamma = sMedian; + } + data.rowScale.assign( np, 0.0 ); + for( HYPRE_Int e = 0; e < np; ++e ) + { + data.rowScale[e] = gamma / ( s[e] > 0.0 ? s[e] : sMedian ); + } + + // 4) MFD pressures: W_M = (l_e/D)^2 gamma^2 sum_f 1/M_ff, the L2 mass of the cell in the units of + // the symmetrized operator, plus its accumulation + stdVector< HYPRE_Real > invDiagM1( nf1, 0.0 ); + for( HYPRE_Int f = 0; f < nf1; ++f ) + { + auto const it = m1[f].find( f ); + invDiagM1[f] = ( it != m1[f].end() && it->second != 0.0 ) ? 1.0 / it->second : 0.0; + } + // A cell belongs to the H1 block iff its two-point couplings connect it to a Dirichlet + // contribution: a TPFA-flagged cell whose faces are all live has no coupling, and an island of + // TPFA cells enclosed by MFD cells has a singular (pure Neumann) sub-block whose level is set by + // the interface fluxes; both live in the L2 block with the saddle point + stdVector< HYPRE_Int > root( np ); + std::iota( root.begin(), root.end(), 0 ); + auto const findRoot = [&]( HYPRE_Int e ) + { + while( root[e] != e ) + { + root[e] = root[root[e]]; + e = root[e]; + } + return e; + }; + for( HYPRE_Int e = 0; e < np; ++e ) + { + for( auto const & [g, v] : c[e] ) + { + if( g != e && v != 0.0 ) + { + root[findRoot( e )] = findRoot( g ); + } + } + } + std::map< HYPRE_Int, bool > componentGrounded; + for( HYPRE_Int e = 0; e < np; ++e ) + { + HYPRE_Real diagVal = 0.0, offSum = 0.0; + bool coupled = false; + for( auto const & [g, v] : c[e] ) + { + diagVal += g == e ? v : 0.0; + offSum += g != e ? std::abs( v ) : 0.0; + coupled = coupled || ( g != e && v != 0.0 ); + } + if( coupled ) + { + bool & grounded = componentGrounded[findRoot( e )]; + grounded = grounded || diagVal > offSum * ( 1.0 + 1e-10 ); + } + } + data.invWm.assign( np, 0.0 ); + stdVector< HYPRE_Real > massOf( np, 0.0 ); // W_M of the L2 cells + for( HYPRE_Int e = 0; e < np; ++e ) + { + auto const cIt = componentGrounded.find( findRoot( e ) ); + bool const h1 = cIt != componentGrounded.end() && cIt->second; + if( data.isMfd[e] || !h1 ) + { + data.isMfd[e] = 1; + HYPRE_Real w = 0.0; + for( auto const & [f, bf] : b1[e] ) + { + w += invDiagM1[f]; + } + w *= data.normScale[e] * gamma * gamma; + massOf[e] = w; + auto const it = c[e].find( e ); + HYPRE_Real const acc = it != c[e].end() ? data.rowScale[e] * it->second : 0.0; + data.invWm[e] = ( w + acc ) > 0.0 ? 1.0 / ( w + acc ) : 0.0; + } + else + { + data.isMfd[e] = 0; + } + } + + // 5) flux block N = M + gamma^2 B_M^T W_M^{-1} B_M, the divergence taken into MFD cells only + for( HYPRE_Int e = 0; e < np; ++e ) + { + if( !data.isMfd[e] || s[e] <= 0.0 ) + { + continue; + } + HYPRE_Real const wInv = gamma * gamma * data.invWm[e] / ( s[e] * s[e] ); + for( auto const & [f, bf] : b1[e] ) + { + for( auto const & [g, bg] : b1[e] ) + { + m1[f][g] += bf * bg * wInv; + } + } + } + + // 6) pressure block, one sparse SPD matrix over all cells: the two-point couplings wherever a + // face was condensed (R^{1/2} C R^{1/2}, accumulation included), the L2 mass W_M of the L2 cells, + // and the two-point energy gamma^2 / M_ff of each interface face, which weighs the mean pressure + // of a patch like its neighbours instead of by its (h^2 smaller) L2 mass + stdVector< RowMap > pp( np ); + for( HYPRE_Int e = 0; e < np; ++e ) + { + for( auto const & [g, v] : c[e] ) + { + pp[e][g] += std::sqrt( data.rowScale[e] * data.rowScale[g] ) * v; + } + pp[e][e] += massOf[e]; + } + for( HYPRE_Int f = 0; f < nf1; ++f ) + { + if( b1Cols[f].size() != 2 ) + { + continue; + } + HYPRE_Int const e = b1Cols[f][0].first; + HYPRE_Int const g = b1Cols[f][1].first; + if( data.isMfd[e] == data.isMfd[g] ) + { + continue; + } + HYPRE_Real const tau = gamma * gamma * invDiagM1[f]; + pp[e][e] += tau; + pp[g][g] += tau; + pp[e][g] -= tau; + pp[g][e] -= tau; + } + + data.nIJ = createIJFromRowMaps( m1, nf1 ); + data.pIJ = createIJFromRowMaps( pp, np ); + data.b0tIJ = createIJFromRowMaps( b0t, np ); + data.r1 = createIJVector( nf1, nullptr ); + data.x1 = createIJVector( nf1, nullptr ); + data.rt = createIJVector( np, nullptr ); + data.xt = createIJVector( np, nullptr ); + data.xp = createIJVector( np, nullptr ); + data.t0 = createIJVector( nf0, nullptr ); + + // 7) CG under one ADS cycle on the flux block (the reference's ads-cg) + data.adsActive = numFree > 0; + if( data.adsActive ) + { + GEOS_LAI_CHECK_ERROR( HYPRE_ADSCreate( &data.ads ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetDiscreteCurl( data.ads, reinterpret_cast< HYPRE_ParCSRMatrix >( parCSROf( data.curlIJ ) ) ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetDiscreteGradient( data.ads, reinterpret_cast< HYPRE_ParCSRMatrix >( parCSROf( data.gradIJ ) ) ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetCoordinateVectors( data.ads, + reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.coordIJ[0] ) ), + reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.coordIJ[1] ) ), + reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.coordIJ[2] ) ) ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetCycleType( data.ads, 13 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetMaxIter( data.ads, 1 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetTol( data.ads, 0.0 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetPrintLevel( data.ads, 0 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetAMGOptions( data.ads, 10, 1, 3, 0.25, 0, 0 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetAMSOptions( data.ads, 11, 10, 1, 3, 0.25, 0, 0 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetup( data.ads, + reinterpret_cast< HYPRE_ParCSRMatrix >( parCSROf( data.nIJ ) ), + reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.r1 ) ), + reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.x1 ) ) ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_ParCSRPCGCreate( MPI_COMM_WORLD, &data.pcg ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_PCGSetTol( data.pcg, 1e-2 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_PCGSetMaxIter( data.pcg, 50 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_PCGSetTwoNorm( data.pcg, 1 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_PCGSetPrintLevel( data.pcg, 0 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_PCGSetLogging( data.pcg, 0 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_ParCSRPCGSetPrecond( data.pcg, + reinterpret_cast< HYPRE_PtrToParSolverFcn >( HYPRE_ADSSolve ), + reinterpret_cast< HYPRE_PtrToParSolverFcn >( noSetup ), + data.ads ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_ParCSRPCGSetup( data.pcg, + reinterpret_cast< HYPRE_ParCSRMatrix >( parCSROf( data.nIJ ) ), + reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.r1 ) ), + reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.x1 ) ) ) ); + } + + // 8) one BoomerAMG cycle on the pressure block + if( np > 0 ) + { + GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGCreate( &data.amg ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetMaxIter( data.amg, 1 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetTol( data.amg, 0.0 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetPrintLevel( data.amg, 0 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetCoarsenType( data.amg, 8 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetInterpType( data.amg, 6 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetAggNumLevels( data.amg, 1 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetRelaxType( data.amg, 8 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetNumSweeps( data.amg, 1 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetStrongThreshold( data.amg, 0.25 ) ); + GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetup( data.amg, + reinterpret_cast< HYPRE_ParCSRMatrix >( parCSROf( data.pIJ ) ), + reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.rt ) ), + reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.xt ) ) ) ); + } + + hypre_SolverSetIsSetup( &data.base ); + return 0; +} + +HYPRE_Int rieszSolve( HYPRE_Solver solver, + HYPRE_ParCSRMatrix, + HYPRE_ParVector f, + HYPRE_ParVector u ) +{ + RieszMFDData & data = *reinterpret_cast< RieszMFDData * >( solver ); + HYPRE_Real const * const r = hypre_VectorData( hypre_ParVectorLocalVector( reinterpret_cast< hypre_ParVector * >( f ) ) ); + HYPRE_Real * const x = hypre_VectorData( hypre_ParVectorLocalVector( reinterpret_cast< hypre_ParVector * >( u ) ) ); + + HYPRE_Int const nf0 = LvArray::integerConversion< HYPRE_Int >( data.dofOfFlux0.size() ); + HYPRE_Int const nf1 = LvArray::integerConversion< HYPRE_Int >( data.dofOfFlux1.size() ); + HYPRE_Int const np = LvArray::integerConversion< HYPRE_Int >( data.dofOfPres.size() ); + + // pressures: the conservation residual taken to the symmetrized system, one AMG cycle on the block + HYPRE_Real * const xp = dataOf( data.xp ); + HYPRE_Real * const rt = dataOf( data.rt ); + HYPRE_Real * const xt = dataOf( data.xt ); + for( HYPRE_Int e = 0; e < np; ++e ) + { + rt[e] = data.rowScale[e] * r[data.dofOfPres[e]]; + xt[e] = 0.0; + } + if( np > 0 ) + { + GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSolve( data.amg, + reinterpret_cast< HYPRE_ParCSRMatrix >( parCSROf( data.pIJ ) ), + reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.rt ) ), + reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.xt ) ) ) ); + } + for( HYPRE_Int e = 0; e < np; ++e ) + { + xp[e] = xt[e]; + x[data.dofOfPres[e]] = xp[e]; + } + + // live fluxes: the flux block alone (block-diagonal map), pinned dofs by their diagonal + if( nf1 > 0 ) + { + HYPRE_Real * const r1 = dataOf( data.r1 ); + HYPRE_Real * const x1 = dataOf( data.x1 ); + for( HYPRE_Int i = 0; i < nf1; ++i ) + { + r1[i] = r[data.dofOfFlux1[i]]; + x1[i] = 0.0; + } + if( data.adsActive ) + { + // a CG that stops on its iteration cap reports a convergence error: not fatal here + HYPRE_ParCSRPCGSolve( data.pcg, + reinterpret_cast< HYPRE_ParCSRMatrix >( parCSROf( data.nIJ ) ), + reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.r1 ) ), + reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.x1 ) ) ); + HYPRE_ClearAllErrors(); + } + for( HYPRE_Int i = 0; i < nf1; ++i ) + { + x[data.dofOfFlux1[i]] = data.pinned[i] ? data.invDiagPinned[i] * r1[i] : x1[i]; + } + } + + // condensed fluxes: exact closure q0 = D0^{-1} ( r0 - F0 p ) + if( nf0 > 0 ) + { + GEOS_LAI_CHECK_ERROR( hypre_ParCSRMatrixMatvec( 1.0, parCSROf( data.b0tIJ ), parVectorOf( data.xp ), + 0.0, parVectorOf( data.t0 ) ) ); + HYPRE_Real const * const t0 = dataOf( data.t0 ); + for( HYPRE_Int k = 0; k < nf0; ++k ) + { + HYPRE_Int const dof = data.dofOfFlux0[k]; + x[dof] = data.invD0[k] * ( r[dof] - t0[k] ); + } + } + return 0; +} + +HYPRE_Int rieszDestroy( HYPRE_Solver solver ) +{ + RieszMFDData * const data = reinterpret_cast< RieszMFDData * >( solver ); + destroySetupObjects( *data ); + destroyIJ( data->curlIJ ); + destroyIJ( data->gradIJ ); + for( int d = 0; d < 3; ++d ) + { + destroyIJ( data->coordIJ[d] ); + } + delete data; + return 0; +} + +} // namespace + +void createRieszMFD( LinearSolverParameters const & params, + HyprePrecWrapper & precond ) +{ + GEOS_ERROR_IF( MpiWrapper::commSize( MPI_COMM_WORLD ) > 1, + "Riesz MFD preconditioner: only serial runs are supported at the moment" ); + GEOS_ERROR_IF( params.mgr.customPointMarkers.empty(), + "Riesz MFD preconditioner: the solver must provide the dof markers (0 = condensed face, 1 = live face, 2 = pressure)" ); + + auto * const data = new RieszMFDData{}; + localIndex const n = params.mgr.customPointMarkers.size(); + data->kindOf.resize( n ); + data->blockRow.resize( n ); + for( localIndex i = 0; i < n; ++i ) + { + HYPRE_Int const kind = LvArray::integerConversion< HYPRE_Int >( params.mgr.customPointMarkers[i] ); + data->kindOf[i] = kind; + stdVector< HYPRE_Int > & block = kind == condensedFaceMarker ? data->dofOfFlux0 : + kind == liveFaceMarker ? data->dofOfFlux1 : data->dofOfPres; + GEOS_ERROR_IF( kind != condensedFaceMarker && kind != liveFaceMarker && kind != pressureMarker, + GEOS_FMT( "Riesz MFD preconditioner: unknown dof marker {}", kind ) ); + data->blockRow[i] = LvArray::integerConversion< HYPRE_Int >( block.size() ); + block.push_back( LvArray::integerConversion< HYPRE_Int >( i ) ); + } + + LinearSolverParameters::ADSAuxData const & aux = params.adsAuxData; + GEOS_ERROR_IF( aux.mfdCell.size() != n || aux.pressureNormScale.size() != n, + "Riesz MFD preconditioner: the solver must provide the MFD flag and the L2 scale of every pressure dof" ); + data->isMfd.resize( data->dofOfPres.size() ); + data->normScale.resize( data->dofOfPres.size() ); + for( std::size_t e = 0; e < data->dofOfPres.size(); ++e ) + { + data->isMfd[e] = aux.mfdCell[data->dofOfPres[e]]; + data->normScale[e] = aux.pressureNormScale[data->dofOfPres[e]]; + } + globalIndex const numFluxRows = aux.cRowPtr.size() - 1; + GEOS_ERROR_IF_NE_MSG( numFluxRows, LvArray::integerConversion< globalIndex >( data->dofOfFlux1.size() ), + "Riesz MFD preconditioner: the discrete curl's rows are not the live flux dofs" ); + if( numFluxRows > 0 ) + { + globalIndex const numEdges = aux.gRowPtr.size() - 1; + globalIndex const numVertices = aux.xCoords.size(); + data->curlIJ = createIJFromCSR( aux.cRowPtr, aux.cCols, aux.cVals, numEdges ); + data->gradIJ = createIJFromCSR( aux.gRowPtr, aux.gCols, aux.gVals, numVertices ); + data->coordIJ[0] = createShiftedCoordinates( aux.xCoords ); + data->coordIJ[1] = createShiftedCoordinates( aux.yCoords ); + data->coordIJ[2] = createShiftedCoordinates( aux.zCoords ); + } + + data->base.setup = reinterpret_cast< HYPRE_PtrToSolverFcn >( rieszSetup ); + data->base.solve = reinterpret_cast< HYPRE_PtrToSolverFcn >( rieszSolve ); + data->base.destroy = reinterpret_cast< HYPRE_PtrToDestroyFcn >( rieszDestroy ); + data->base.is_setup = 0; + + precond.ptr = reinterpret_cast< HYPRE_Solver >( data ); + precond.setup = rieszSetup; + precond.solve = rieszSolve; + precond.destroy = rieszDestroy; +} + +} // namespace hypre + +} // namespace geos diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreRieszMFD.hpp b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreRieszMFD.hpp new file mode 100644 index 00000000000..a6f41fe1553 --- /dev/null +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreRieszMFD.hpp @@ -0,0 +1,59 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file HypreRieszMFD.hpp + */ + +#ifndef GEOS_LINEARALGEBRA_INTERFACES_HYPRERIESZMFD_HPP_ +#define GEOS_LINEARALGEBRA_INTERFACES_HYPRERIESZMFD_HPP_ + +#include "linearAlgebra/interfaces/hypre/HypreUtils.hpp" +#include "linearAlgebra/utilities/LinearSolverParameters.hpp" + +namespace geos +{ + +namespace hypre +{ + +/** + * @brief Create the Riesz-map block preconditioner for the adaptive mixed MFD saddle point. + * + * The unknowns are the live fluxes q (faces of MFD cells), the pressures p_M of the MFD cells and + * the pressures p_T of the TPFA cells; the condensed fluxes are recovered from their closure rows. + * The preconditioner is the Riesz map of the product norm H(div; MFD region) x L2(MFD cells) x + * H1(TPFA cells), + * + * P = diag( M + B_M^T W_M^{-1} B_M , W_M , C_T + S_TT ), + * + * with B_M the face-to-cell incidence of the live faces into the MFD cells, W_M the L2 mass of the + * MFD cells (plus accumulation) and S_TT the two-point Laplacian of the TPFA cells. The flux block + * is solved by CG under one ADS cycle, the TPFA block by one BoomerAMG cycle. The interface faces + * carry no weight: they are bounded by the normal-trace inequality. All blocks are read off the + * assembled system, whose row scalings are identified and removed; the solver provides the de Rham + * sub-complex of the live faces, the dof markers, the MFD flag and the L2 scale of each cell. + * + * @param params the linear solver parameters (dof markers and auxiliary discretization data) + * @param precond the output preconditioner wrapper + */ +void createRieszMFD( LinearSolverParameters const & params, + HyprePrecWrapper & precond ); + +} // namespace hypre + +} // namespace geos + +#endif /*GEOS_LINEARALGEBRA_INTERFACES_HYPRERIESZMFD_HPP_*/ diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp b/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp index 54dc3e90c0d..caabd47faf0 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp @@ -283,6 +283,7 @@ bool supportsGeneratedPreconditioner( LinearSolverParameters::PreconditionerType case PreconditionerType::direct: case PreconditionerType::bgs: case PreconditionerType::multiscale: + case PreconditionerType::riesz: return false; } @@ -1387,6 +1388,17 @@ bool buildStrategyYaml( LinearSolverParameters const & params, strategy.setup( params.mgr, precond, mgrData ); + // strategies driven by solver-provided custom point markers use more blocks than there are + // dof fields; those labels have no field-name representation, so skip the YAML preview + if( strategy.m_numBlocks > LvArray::integerConversion< HYPRE_Int >( labelNames.size() ) ) + { + destroyWrapper( mgrData.coarseSolver ); + destroyWrapper( mgrData.mechSolver ); + destroyWrapper( mgrData.nestedSolver ); + GEOS_LAI_CHECK_ERROR( HYPRE_MGRDestroy( precond.ptr ) ); + return false; + } + std::ostringstream stream; if( params.mgr.strategy == LinearSolverParameters::MGR::StrategyType::singlePhasePoromechanicsConformingFracturesALM ) { diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp index eb6a7ec78ad..0deb380e41f 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp @@ -105,43 +105,53 @@ class SinglePhaseMixedMFD : public MGRStrategyBase< 2 > HyprePrecWrapper & precond, HypreMGRData & mgrData ) { - // if the label-1 set is empty the level-1 reduction is the identity: use a single - // level eliminating all (diagonal) fluxes at once - integer localAnyLive = 0; + // a reduction level with an empty F-set is the identity and is dropped: the level + // structure is determined by which label sets D_0 (TPFA dofs) and D_1 (MFD dofs) + // are nonempty + integer localD0Nonempty = 0; + integer localD1Nonempty = 0; for( localIndex i = 0; i < mgrParams.customPointMarkers.size(); ++i ) { - if( mgrParams.customPointMarkers[i] == 1 ) - { - localAnyLive = 1; - break; - } + localD0Nonempty |= ( mgrParams.customPointMarkers[i] == 0 ); + localD1Nonempty |= ( mgrParams.customPointMarkers[i] == 1 ); } - bool const anyLiveFaces = MpiWrapper::max( localAnyLive ) == 1; + bool const d0Nonempty = MpiWrapper::max( localD0Nonempty ) == 1; + bool const d1Nonempty = MpiWrapper::max( localD1Nonempty ) == 1; m_labels[0].clear(); m_labels[1].clear(); - if( anyLiveFaces ) + HYPRE_Int numActiveLevels; + if( d0Nonempty && d1Nonempty ) { + // two reduction levels: level 0 with F = D_0 (exact Jacobi elimination of the + // diagonal block), level 1 with F = D_1 (SSOR sweeps on the MFD block) m_labels[0].push_back( 1 ); m_labels[0].push_back( 2 ); m_labels[1].push_back( 2 ); + m_levelFRelaxType[0] = MGRFRelaxationType::jacobi; + m_levelFRelaxIters[0] = 1; + m_levelInterpType[0] = MGRInterpolationType::jacobi; + numActiveLevels = numLevels; } else { + // one reduction level with F = D_0 (exact Jacobi elimination) or F = D_1 (SSOR sweeps) m_labels[0].push_back( 2 ); + m_levelFRelaxType[0] = d1Nonempty ? MGRFRelaxationType::hybridSymmetricGaussSeidel + : MGRFRelaxationType::jacobi; + m_levelFRelaxIters[0] = d1Nonempty ? m_levelFRelaxIters[1] : 1; + m_levelInterpType[0] = MGRInterpolationType::jacobi; + numActiveLevels = 1; } setupLabels(); - setReduction( precond, mgrData, anyLiveFaces ? numLevels : 1 ); + setReduction( precond, mgrData, numActiveLevels ); // Configure the BoomerAMG solver used as mgr coarse solver for the pressure Schur // complement. Two V-cycles per MGR application: the coarse solve accuracy governs the // outer FGMRES iteration count (a single cycle leaves the reduction quality unused) setPressureAMG( mgrData.coarseSolver ); GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetMaxIter( mgrData.coarseSolver.ptr, 2 ) ); - // strength threshold suited to 3D anisotropic permeability fields (hypre default 0.25 - // over-couples the weak direction and degrades coarsening quality) - GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetStrongThreshold( mgrData.coarseSolver.ptr, 0.6 ) ); } }; diff --git a/src/coreComponents/linearAlgebra/unitTests/testLinearSolverParametersEnums.cpp b/src/coreComponents/linearAlgebra/unitTests/testLinearSolverParametersEnums.cpp index d9404b5e7b4..34b6aa3e75b 100644 --- a/src/coreComponents/linearAlgebra/unitTests/testLinearSolverParametersEnums.cpp +++ b/src/coreComponents/linearAlgebra/unitTests/testLinearSolverParametersEnums.cpp @@ -56,6 +56,7 @@ TEST( LinearSolverParametersEnums, PreconditionerType ) ASSERT_EQ( "ict", toString( EnumType::ict ) ); ASSERT_EQ( "amg", toString( EnumType::amg ) ); ASSERT_EQ( "mgr", toString( EnumType::mgr ) ); + ASSERT_EQ( "riesz", toString( EnumType::riesz ) ); ASSERT_EQ( "block", toString( EnumType::block ) ); ASSERT_EQ( "direct", toString( EnumType::direct ) ); ASSERT_EQ( "bgs", toString( EnumType::bgs ) ); diff --git a/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp b/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp index 3bad3f9d633..0cccedca26a 100644 --- a/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp +++ b/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp @@ -69,7 +69,8 @@ struct LinearSolverParameters block, ///< Block preconditioner direct, ///< Direct solver as preconditioner bgs, ///< Gauss-Seidel smoothing (backward sweep) - multiscale ///< Multiscale preconditioner + multiscale, ///< Multiscale preconditioner + riesz ///< Riesz-map block preconditioner for the mixed MFD saddle point (Hypre only) }; integer logLevel = 0; ///< Output level [0=none, 1=basic, 2=everything] @@ -299,6 +300,7 @@ struct LinearSolverParameters singlePhaseReservoirFVM, ///< finite volume single-phase flow with wells thermalSinglePhaseReservoirFVM, ///< finite volume thermal single-phase flow with wells singlePhaseHybridFVM, ///< hybrid finite volume single-phase flow + singlePhaseMixedMFD, ///< mixed mimetic finite difference single-phase flow singlePhaseReservoirHybridFVM, ///< hybrid finite volume single-phase flow with wells singlePhasePoromechanics, ///< single phase poromechanics with finite volume single phase flow thermalSinglePhasePoromechanics, ///< thermal single phase poromechanics with finite volume single phase flow @@ -334,9 +336,33 @@ struct LinearSolverParameters integer separateComponents = false; ///< Apply a separate displacement component (SDC) filter before AMG construction integer areWellsShut = false; ///< Flag to let MGR know that wells are shut, and that jacobi can be applied to the ///< well block + array1d< integer > customPointMarkers; ///< Optional solver-provided per-local-dof labels overriding the field-component + ///< labels (empty = use the DofManager field labels); used by strategies whose + ///< reduction is finer-grained than the dof fields (e.g. adaptive mixed MFD) } mgr; ///< Multigrid reduction (MGR) parameters + /** + * @brief Solver-provided de Rham sub-complex of the live mixed-MFD flux dofs, for the Riesz-map + * preconditioner (empty = unused). CSR over compact auxiliary numberings: flux rows follow the + * dof order of the live faces, edge and vertex columns the active entities of those faces. + */ + struct ADSAuxData + { + array1d< globalIndex > cRowPtr; ///< CSR row offsets of the discrete curl (live faces x active edges) + array1d< globalIndex > cCols; ///< CSR column indices of the discrete curl + array1d< real64 > cVals; ///< CSR values (+-1) of the discrete curl + array1d< globalIndex > gRowPtr; ///< CSR row offsets of the discrete gradient (active edges x active vertices) + array1d< globalIndex > gCols; ///< CSR column indices of the discrete gradient + array1d< real64 > gVals; ///< CSR values (+-1) of the discrete gradient + array1d< real64 > xCoords; ///< active vertex x coordinates + array1d< real64 > yCoords; ///< active vertex y coordinates + array1d< real64 > zCoords; ///< active vertex z coordinates + array1d< integer > mfdCell; ///< 1 at the pressure dof of an MFD cell, 0 elsewhere + array1d< real64 > pressureNormScale; ///< (l_e/D)^2 at each pressure dof, l_e^2 = |E|^2 / sum_f A_f^2 + } + adsAuxData; ///< live-face sub-complex for the Riesz-map preconditioner + /// Incomplete factorization parameters struct IFact { @@ -559,7 +585,8 @@ ENUM_STRINGS( LinearSolverParameters::PreconditionerType, "block", "direct", "bgs", - "multiscale" ); + "multiscale", + "riesz" ); /// Declare strings associated with enumeration values. ENUM_STRINGS( LinearSolverParameters::Direct::ColPerm, @@ -581,6 +608,7 @@ ENUM_STRINGS( LinearSolverParameters::MGR::StrategyType, "singlePhaseReservoirFVM", "thermalSinglePhaseReservoirFVM", "singlePhaseHybridFVM", + "singlePhaseMixedMFD", "singlePhaseReservoirHybridFVM", "singlePhasePoromechanics", "thermalSinglePhasePoromechanics", diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp index 25b77c66d79..2b789c2233f 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp @@ -24,6 +24,7 @@ #include "finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp" #include "finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp" #include "finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp" +#include "finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp" namespace geos { @@ -131,15 +132,20 @@ MixedMimeticDiscretization::factory( string const & mimeticInnerProductType ) co { rval = std::make_unique< BdVLMInnerProduct >(); } + else if( mimeticInnerProductType == MimeticInnerProductTypeStrings::RT ) + { + rval = std::make_unique< RTInnerProduct >(); + } else { GEOS_ERROR( GEOS_FMT( "Key value of {} does not have an associated mimetic inner product implementing the mixed-form " - "mass matrix (valid options: {}, {}, {}, {}).", + "mass matrix (valid options: {}, {}, {}, {}, {}).", mimeticInnerProductType, MimeticInnerProductTypeStrings::TPFA, MimeticInnerProductTypeStrings::QuasiTPFA, MimeticInnerProductTypeStrings::Simple, - MimeticInnerProductTypeStrings::BdVLM ), + MimeticInnerProductTypeStrings::BdVLM, + MimeticInnerProductTypeStrings::RT ), getDataContext() ); } return rval; diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDispatch.hpp b/src/coreComponents/mixedMimetic/MixedMimeticDispatch.hpp index 64e8e98a21a..bfeda6495c0 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDispatch.hpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDispatch.hpp @@ -25,6 +25,7 @@ #include "finiteVolume/mimeticInnerProducts/QuasiTPFAInnerProduct.hpp" #include "finiteVolume/mimeticInnerProducts/SimpleInnerProduct.hpp" #include "finiteVolume/mimeticInnerProducts/BdVLMInnerProduct.hpp" +#include "finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp" #include "common/logger/Logger.hpp" namespace geos @@ -58,6 +59,10 @@ mixedMimeticInnerProductDispatch( mimeticInnerProduct::MimeticInnerProductBase c { lambda( *ptr4 ); } + else if( auto const * const ptr5 = dynamic_cast< mimeticInnerProduct::RTInnerProduct const * >(&input) ) + { + lambda( *ptr5 ); + } else { GEOS_ERROR( "mixedMimeticInnerProductDispatch() is not implemented for input of "< 0: its constitutive - * row is then exactly diagonal with a positive two-point coefficient. Only TPFA - * dofs are condensed (label 0); every other face dof is a live unknown (label 1). + * @brief Classify the face dofs. A face dof f is a TPFA dof iff chi_C = 0 for every + * adjacent cell C: its constitutive row is then exactly diagonal and the dof is + * condensed (label 0), whatever the sign of its two-point coefficient. Every other + * face dof is a live unknown (label 1). * When the selected inner product is itself TPFA, the effective operator is diagonal * everywhere and all faces get label 0. */ @@ -201,11 +201,8 @@ struct FaceLabelKernel arrayView2d< localIndex const > const & elemList, SortedArrayView< localIndex const > const & regionFilter, ElementViewConst< arrayView1d< integer const > > const & stencilFlag, - arrayView2d< real64 const > const & faceCenter, - arrayView2d< real64 const > const & faceNormal, - ElementViewConst< arrayView2d< real64 const > > const & elemCenter, - ElementViewConst< arrayView3d< real64 const > > const & elemPerm, bool const effectiveTpfa, + bool const keepAllFacesLive, arrayView1d< integer > const & faceStencilLabel ) { forAll< POLICY >( numFaces, [=] GEOS_HOST_DEVICE ( localIndex const kf ) @@ -220,26 +217,9 @@ struct FaceLabelKernel localIndex const ei = elemList[kf][k]; if( er >= 0 && esr >= 0 && ei >= 0 && regionFilter.contains( er ) ) { - label = LvArray::math::max( label, stencilFlag[er][esr][ei] ); - - // u = x_f - x_C, n the fixed global normal: n_out = sign(u . n) n, so - // sign(u . K n_out) = sign((u . K n)(u . n)) - real64 uDotKn = 0.0; - for( integer d = 0; d < 3; ++d ) - { - uDotKn += ( faceCenter[kf][d] - elemCenter[er][esr][ei][d] ) * - elemPerm[er][esr][ei][0][d] * faceNormal[kf][d]; - } - real64 uDotN = 0.0; - for( integer d = 0; d < 3; ++d ) - { - uDotN += ( faceCenter[kf][d] - elemCenter[er][esr][ei][d] ) * faceNormal[kf][d]; - } - // u . K n_out <= 0: half-transmissibility not positive, keep the flux live - if( uDotKn * uDotN <= 0.0 ) - { - label = 1; - } + // a face is live next to an MFD cell, or everywhere when the saddle point is kept whole + // (the Riesz-map preconditioner needs it): a TPFA cell then only selects its diagonal star + label = keepAllFacesLive ? 1 : LvArray::math::max( label, stencilFlag[er][esr][ei] ); } } } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index 880fff0a160..41b4554b749 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -188,7 +188,7 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d subRegion.getField< mixedMimetic::stencilFlag >().template setValues< parallelDevicePolicy<> >( 1 ); } ); } ); - computeFaceStencilLabels( domain ); + computeFaceStencilLabels( domain, false ); return; } @@ -196,6 +196,7 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d real64 const tolerance = discretization.getResidualTolerance(); R1Tensor const gradientInput = discretization.getNominalGradient(); real64 const gradient[3] = { gradientInput[0], gradientInput[1], gradientInput[2] }; + localIndex numMfdCellsTotal = 0; forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, MeshLevel & mesh, @@ -244,19 +245,19 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d mixedMimeticKernels::internal::kernelLaunchSelectorFaceSwitch( subRegion.numFacesPerElement(), [&] ( auto NUM_FACES ) { mixedMimeticKernels::LocalResidualKernel< NUM_FACES >:: - template launch< parallelDevicePolicy<> >( subRegion.size(), - nodeManager.referencePosition(), - faceManager.nodeList().toViewConst(), - subRegion.faceList().toViewConst(), - subRegion.getElementCenter(), - subRegion.getElementVolume(), - permeability.permeability(), - faceManager.faceCenter(), - faceManager.faceNormal(), - projFaceFlux.toViewConst(), - gradient, - lengthTolerance, - faceResidual ); + template launch< parallelDevicePolicy<> >( subRegion.size(), + nodeManager.referencePosition(), + faceManager.nodeList().toViewConst(), + subRegion.faceList().toViewConst(), + subRegion.getElementCenter(), + subRegion.getElementVolume(), + permeability.permeability(), + faceManager.faceCenter(), + faceManager.faceNormal(), + projFaceFlux.toViewConst(), + gradient, + lengthTolerance, + faceResidual ); } ); } ); @@ -273,13 +274,13 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d mixedMimeticKernels::internal::kernelLaunchSelectorFaceSwitch( subRegion.numFacesPerElement(), [&] ( auto NUM_FACES ) { numMfdCells += mixedMimeticKernels::MarkingKernel< NUM_FACES >:: - template launch< parallelDevicePolicy<> >( subRegion.size(), - subRegion.faceList().toViewConst(), - subRegion.ghostRank(), - faceResidual.toViewConst(), - tolerance, - consistencyIndicator, - stencilFlag ); + template launch< parallelDevicePolicy<> >( subRegion.size(), + subRegion.faceList().toViewConst(), + subRegion.ghostRank(), + faceResidual.toViewConst(), + tolerance, + consistencyIndicator, + stencilFlag ); } ); numCells += subRegion.size() - subRegion.getNumberOfGhosts(); } ); @@ -293,12 +294,18 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d globalIndex const globalNumCells = MpiWrapper::sum< globalIndex >( numCells ); GEOS_LOG_RANK_0( GEOS_FMT( "{}: Global Adaptation marked {} / {} cells as MFD-compatible (tolerance = {})", getName(), globalNumMfdCells, globalNumCells, tolerance ) ); + numMfdCellsTotal += numMfdCells; } ); - computeFaceStencilLabels( domain ); + // the Riesz-map preconditioner is a map of the whole saddle point: as soon as one cell is MFD no + // face is condensed (eliminating faces removes the elliptic content of the div term from the map); + // with no MFD cell the condensed SPD system is solved as such + bool const riesz = m_linearSolverParameters.get().preconditionerType == LinearSolverParameters::PreconditionerType::riesz; + bool const keepAllFacesLive = riesz && MpiWrapper::sum< globalIndex >( numMfdCellsTotal ) > 0; + computeFaceStencilLabels( domain, keepAllFacesLive ); } -void SinglePhaseMixedMFD::computeFaceStencilLabels( DomainPartition & domain ) +void SinglePhaseMixedMFD::computeFaceStencilLabels( DomainPartition & domain, bool const keepAllFacesLive ) { GEOS_MARK_FUNCTION; @@ -320,12 +327,6 @@ void SinglePhaseMixedMFD::computeFaceStencilLabels( DomainPartition & domain ) ElementRegionManager::ElementViewAccessor< arrayView1d< integer const > > const stencilFlagAccessor = elemManager.constructArrayViewAccessor< integer, 1 >( mixedMimetic::stencilFlag::key() ); - ElementRegionManager::ElementViewAccessor< arrayView2d< real64 const > > const elemCenterAccessor = - elemManager.constructViewAccessor< array2d< real64 >, arrayView2d< real64 const > >( ElementSubRegionBase::viewKeyStruct::elementCenterString() ); - - using PermeabilityAccessors = StencilMaterialAccessors< PermeabilityBase, fields::permeability::permeability >; - PermeabilityAccessors const permAccessors( elemManager, getName() ); - mixedMimeticKernels::FaceLabelKernel:: launch< parallelDevicePolicy<> >( faceManager.size(), faceManager.elementRegionList(), @@ -333,11 +334,8 @@ void SinglePhaseMixedMFD::computeFaceStencilLabels( DomainPartition & domain ) faceManager.elementList(), m_regionFilter.toViewConst(), stencilFlagAccessor.toNestedViewConst(), - faceManager.faceCenter(), - faceManager.faceNormal(), - elemCenterAccessor.toNestedViewConst(), - permAccessors.get( fields::permeability::permeability {} ), effectiveTpfa, + keepAllFacesLive, faceManager.getField< mixedMimetic::faceStencilLabel >() ); } ); } @@ -410,9 +408,9 @@ void SinglePhaseMixedMFD::applyFacePressureBCValues( real64 const time, { globalIndex const numTargetFaces = MpiWrapper::sum< globalIndex >( targetSet.size() ); GEOS_LOG_LEVEL_RANK_0_ON_GROUP( logInfo::BoundaryConditions, - GEOS_FMT( faceBcLogMessage, - this->getName(), time, fs.getCatalogName(), fs.getName(), - setName, targetGroup.getName(), numTargetFaces ), + GEOS_FMT_RUNTIME( faceBcLogMessage, + this->getName(), time, fs.getCatalogName(), fs.getName(), + setName, targetGroup.getName(), numTargetFaces ), fs ); } @@ -439,6 +437,11 @@ void SinglePhaseMixedMFD::setupSystem( DomainPartition & domain, // with the dof numbering finalized, build the per-dof labels driving the // stencilFlag-guided three-level MGR reduction computeMgrPointMarkers( domain, dofManager ); + + if( m_linearSolverParameters.get().preconditionerType == LinearSolverParameters::PreconditionerType::riesz ) + { + computeADSAuxData( domain, dofManager ); + } } void SinglePhaseMixedMFD::computeMgrPointMarkers( DomainPartition const & domain, @@ -519,6 +522,266 @@ void SinglePhaseMixedMFD::computeMgrPointMarkers( DomainPartition const & domain } ); } +void SinglePhaseMixedMFD::computeADSAuxData( DomainPartition const & domain, + DofManager const & dofManager ) +{ + GEOS_MARK_FUNCTION; + + GEOS_ERROR_IF( MpiWrapper::commSize() > 1, + GEOS_FMT( "{}: the Riesz-map preconditioner currently supports serial runs only", getName() ) ); + + LinearSolverParameters::ADSAuxData & aux = m_linearSolverParameters.get().adsAuxData; + string const faceDofKey = dofManager.getKey( mixedMimetic::faceMassFlux::key() ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel const & mesh, + string_array const & ) + { + NodeManager const & nodeManager = mesh.getNodeManager(); + EdgeManager const & edgeManager = mesh.getEdgeManager(); + FaceManager const & faceManager = mesh.getFaceManager(); + + localIndex const numNodes = nodeManager.size(); + localIndex const numEdges = edgeManager.size(); + localIndex const numFaces = faceManager.size(); + + arrayView1d< globalIndex const > const faceDofNumber = + faceManager.getReference< array1d< globalIndex > >( faceDofKey ); + arrayView1d< integer const > const faceStencilLabel = faceManager.getField< mixedMimetic::faceStencilLabel >(); + + // flux rows: live MFD faces in ascending dof order, matching the F-point order of the ADS level + stdVector< std::pair< globalIndex, localIndex > > liveFaces; + for( localIndex kf = 0; kf < numFaces; ++kf ) + { + if( faceDofNumber[kf] >= 0 && faceStencilLabel[kf] == 1 ) + { + liveFaces.emplace_back( faceDofNumber[kf], kf ); + } + } + std::sort( liveFaces.begin(), liveFaces.end() ); + localIndex const numFluxRows = LvArray::integerConversion< localIndex >( liveFaces.size() ); + + array1d< localIndex > faceToRow( numFaces ); + faceToRow.setValues< serialPolicy >( -1 ); + for( localIndex r = 0; r < numFluxRows; ++r ) + { + faceToRow[liveFaces[r].second] = r; + } + + // ---- the de Rham sub-complex of the MFD region: only the edges of the live faces and the + // vertices of those edges enter the auxiliary spaces, compactly renumbered, so ADS sees the + // complex of exactly the block it is handed (the condensed TPFA faces are its boundary) + ArrayOfArraysView< localIndex const > const faceToNodes = faceManager.nodeList().toViewConst(); + ArrayOfArraysView< localIndex const > const faceToEdges = faceManager.edgeList().toViewConst(); + arrayView2d< localIndex const > const edgeToNodes = edgeManager.nodeList(); + + array1d< localIndex > edgeToAux( numEdges ); + edgeToAux.setValues< serialPolicy >( -1 ); + for( localIndex r = 0; r < numFluxRows; ++r ) + { + localIndex const kf = liveFaces[r].second; + for( localIndex j = 0; j < faceToEdges.sizeOfArray( kf ); ++j ) + { + edgeToAux[faceToEdges( kf, j )] = 0; + } + } + localIndex numActiveEdges = 0; + for( localIndex e = 0; e < numEdges; ++e ) + { + if( edgeToAux[e] == 0 ) + { + edgeToAux[e] = numActiveEdges++; + } + } + + array1d< localIndex > nodeToAux( numNodes ); + nodeToAux.setValues< serialPolicy >( -1 ); + for( localIndex e = 0; e < numEdges; ++e ) + { + if( edgeToAux[e] >= 0 ) + { + nodeToAux[edgeToNodes( e, 0 )] = 0; + nodeToAux[edgeToNodes( e, 1 )] = 0; + } + } + localIndex numActiveNodes = 0; + for( localIndex n = 0; n < numNodes; ++n ) + { + if( nodeToAux[n] == 0 ) + { + nodeToAux[n] = numActiveNodes++; + } + } + + // ---- discrete curl: signed edges of the boundary loop of each live face. The flux dof + // follows the normal pointing out of the adjacent element with the smaller global index + // (the assembly kernel's convention), so the loop of faceToNodes is flipped when its + // right-hand normal points into that element + arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const nodePosition = nodeManager.referencePosition(); + arrayView2d< localIndex const > const fToElemRegion = faceManager.elementRegionList(); + arrayView2d< localIndex const > const fToElemSubRegion = faceManager.elementSubRegionList(); + arrayView2d< localIndex const > const fToElem = faceManager.elementList(); + ElementRegionManager const & elemManager = mesh.getElemManager(); + ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > const elemLocalToGlobal = + elemManager.constructArrayViewAccessor< globalIndex, 1 >( ObjectManagerBase::viewKeyStruct::localToGlobalMapString() ); + ElementRegionManager::ElementViewAccessor< arrayView2d< real64 const > > const elemCenter = + elemManager.constructArrayViewAccessor< real64, 2 >( ElementSubRegionBase::viewKeyStruct::elementCenterString() ); + real64 const areaTolerance = LvArray::math::square( domain.getMeshBody( 0 ).getGlobalLengthScale() * m_areaRelTol ); + + aux.cRowPtr.resize( numFluxRows + 1 ); + stdVector< globalIndex > cCols; + stdVector< real64 > cVals; + aux.cRowPtr[0] = 0; + for( localIndex r = 0; r < numFluxRows; ++r ) + { + localIndex const kf = liveFaces[r].second; + + // the element the dof normal points out of: smallest global index among the neighbours + globalIndex gMin = -1; + real64 refCenter[3]{}; + for( localIndex k = 0; k < 2; ++k ) + { + localIndex const er = fToElemRegion( kf, k ); + localIndex const esr = fToElemSubRegion( kf, k ); + localIndex const ei = fToElem( kf, k ); + if( er < 0 || esr < 0 || ei < 0 ) + { + continue; + } + globalIndex const g = elemLocalToGlobal[er][esr][ei]; + if( gMin < 0 || g < gMin ) + { + gMin = g; + LvArray::tensorOps::copy< 3 >( refCenter, elemCenter[er][esr][ei] ); + } + } + + real64 faceCenter[3], loopNormal[3]; + computationalGeometry::centroid_3DPolygon( faceToNodes[kf], nodePosition, faceCenter, loopNormal, areaTolerance ); + LvArray::tensorOps::subtract< 3 >( faceCenter, refCenter ); + real64 const loopSign = LvArray::tensorOps::AiBi< 3 >( faceCenter, loopNormal ) < 0.0 ? -1.0 : 1.0; + + localIndex const numFaceNodes = faceToNodes.sizeOfArray( kf ); + for( localIndex i = 0; i < numFaceNodes; ++i ) + { + localIndex const a = faceToNodes( kf, i ); + localIndex const b = faceToNodes( kf, ( i + 1 ) % numFaceNodes ); + for( localIndex j = 0; j < faceToEdges.sizeOfArray( kf ); ++j ) + { + localIndex const e = faceToEdges( kf, j ); + localIndex const n0 = edgeToNodes( e, 0 ); + localIndex const n1 = edgeToNodes( e, 1 ); + if( ( n0 == a && n1 == b ) || ( n0 == b && n1 == a ) ) + { + cCols.push_back( edgeToAux[e] ); + cVals.push_back( loopSign * ( n0 == a ? 1.0 : -1.0 ) ); + break; + } + } + } + aux.cRowPtr[r + 1] = LvArray::integerConversion< globalIndex >( cCols.size() ); + } + aux.cCols.resize( cCols.size() ); + aux.cVals.resize( cVals.size() ); + std::copy( cCols.begin(), cCols.end(), aux.cCols.begin() ); + std::copy( cVals.begin(), cVals.end(), aux.cVals.begin() ); + + // ---- discrete gradient: signed vertex-edge incidence over the active edges + aux.gRowPtr.resize( numActiveEdges + 1 ); + aux.gCols.resize( 2 * numActiveEdges ); + aux.gVals.resize( 2 * numActiveEdges ); + aux.gRowPtr[0] = 0; + for( localIndex e = 0; e < numEdges; ++e ) + { + localIndex const ea = edgeToAux[e]; + if( ea < 0 ) + { + continue; + } + aux.gCols[2 * ea] = nodeToAux[edgeToNodes( e, 0 )]; + aux.gVals[2 * ea] = -1.0; + aux.gCols[2 * ea + 1] = nodeToAux[edgeToNodes( e, 1 )]; + aux.gVals[2 * ea + 1] = 1.0; + aux.gRowPtr[ea + 1] = 2 * ( ea + 1 ); + } + + // ---- coordinates of the active vertices + aux.xCoords.resize( numActiveNodes ); + aux.yCoords.resize( numActiveNodes ); + aux.zCoords.resize( numActiveNodes ); + for( localIndex n = 0; n < numNodes; ++n ) + { + localIndex const na = nodeToAux[n]; + if( na >= 0 ) + { + aux.xCoords[na] = nodePosition( n, 0 ); + aux.yCoords[na] = nodePosition( n, 1 ); + aux.zCoords[na] = nodePosition( n, 2 ); + } + } + + // ---- per pressure dof: the cell's stencil flag and the geometric factor (l_e/D)^2 of its L2 + // weight, l_e^2 = |E|^2 / sum_f A_f^2 and D the bounding-box diagonal of the mesh + { + string const elemDofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); + globalIndex const rankOffset = dofManager.rankOffset(); + aux.mfdCell.resize( dofManager.numLocalDofs() ); + aux.mfdCell.zero(); + aux.pressureNormScale.resize( dofManager.numLocalDofs() ); + aux.pressureNormScale.zero(); + + real64 lo[3] = { LvArray::NumericLimits< real64 >::max, LvArray::NumericLimits< real64 >::max, LvArray::NumericLimits< real64 >::max }; + real64 hi[3] = { -LvArray::NumericLimits< real64 >::max, -LvArray::NumericLimits< real64 >::max, -LvArray::NumericLimits< real64 >::max }; + for( localIndex n = 0; n < numNodes; ++n ) + { + for( int d = 0; d < 3; ++d ) + { + lo[d] = LvArray::math::min( lo[d], nodePosition( n, d ) ); + hi[d] = LvArray::math::max( hi[d], nodePosition( n, d ) ); + } + } + real64 diag2 = 0.0; + for( int d = 0; d < 3; ++d ) + { + diag2 += LvArray::math::square( MpiWrapper::max( hi[d] ) - MpiWrapper::min( lo[d] ) ); + } + + arrayView1d< real64 const > const faceArea = faceManager.faceArea(); + elemManager.forElementSubRegions< CellElementSubRegion >( [&]( CellElementSubRegion const & subRegion ) + { + if( !subRegion.hasWrapper( elemDofKey ) ) + { + return; + } + arrayView1d< globalIndex const > const elemDofNumber = subRegion.getReference< array1d< globalIndex > >( elemDofKey ); + arrayView1d< real64 const > const elemVolume = subRegion.getElementVolume(); + arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); + arrayView1d< integer const > const stencilFlag = subRegion.getField< mixedMimetic::stencilFlag >(); + arrayView2d< localIndex const > const elemsToFaces = subRegion.faceList().toViewConst(); + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + if( elemGhostRank[ei] >= 0 || elemDofNumber[ei] < 0 ) + { + continue; + } + real64 sumArea2 = 0.0; + for( localIndex j = 0; j < elemsToFaces.size( 1 ); ++j ) + { + localIndex const kf = elemsToFaces( ei, j ); + sumArea2 += kf >= 0 ? LvArray::math::square( faceArea[kf] ) : 0.0; + } + localIndex const row = elemDofNumber[ei] - rankOffset; + aux.mfdCell[row] = stencilFlag[ei] != 0 ? 1 : 0; + aux.pressureNormScale[row] = sumArea2 > 0.0 ? LvArray::math::square( elemVolume[ei] ) / sumArea2 / diag2 : 0.0; + } + } ); + } + + GEOS_LOG_RANK_0( GEOS_FMT( "{}: Riesz-map sub-complex built: {} live flux rows, {} active edges (of {}), {} active vertices (of {})", + getName(), numFluxRows, numActiveEdges, numEdges, numActiveNodes, numNodes ) ); + } ); +} + void SinglePhaseMixedMFD::setupDofs( DomainPartition const & GEOS_UNUSED_PARAM( domain ), DofManager & dofManager ) const { @@ -665,128 +928,171 @@ void SinglePhaseMixedMFD::applyBoundaryConditions( real64 const time_n, // the face pressure boundary values are applied inside the constitutive rows during assembly; // only the cell-centered boundary conditions (Dirichlet cells, source fluxes) remain to be applied here SinglePhaseBase::applyBoundaryConditions( time_n, dt, domain, dofManager, localMatrix, localRhs ); -} - -void SinglePhaseMixedMFD::applyAquiferBC( real64 const time, - real64 const dt, - DomainPartition & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) const -{ - GEOS_MARK_FUNCTION; - GEOS_UNUSED_VAR( time, dt, dofManager, domain, localMatrix, localRhs ); -} - -void SinglePhaseMixedMFD::saveAquiferConvergedState( real64 const & time, - real64 const & dt, - DomainPartition & domain ) -{ - GEOS_MARK_FUNCTION; - GEOS_UNUSED_VAR( time, dt, domain ); -} - -real64 SinglePhaseMixedMFD::calculateResidualNorm( real64 const & GEOS_UNUSED_PARAM( time_n ), - real64 const & GEOS_UNUSED_PARAM( dt ), - DomainPartition const & domain, - DofManager const & dofManager, - arrayView1d< real64 const > const & localRhs ) -{ - GEOS_MARK_FUNCTION; - real64 localResidualNorm = 0.0; - real64 localResidualNormalizer = 0.0; - - physicsSolverBaseKernels::NormType const normType = getNonlinearSolverParameters().normType(); + // residual-norm weights w_i = ||A_i S||_inf = max_j |A_ij| s_j, S = diag(s_j) the + // characteristic scales of the unknowns: each term |A_ij| s_j has the units of r_i, + // and w_i is invariant under row rescaling and under the TPFA-face condensation + localIndex const numRows = localMatrix.numRows(); + m_residualWeight.resize( numRows ); + m_dofScale.resize( numRows ); + arrayView1d< real64 > const residualWeight = m_residualWeight.toView(); + arrayView1d< real64 > const dofScale = m_dofScale.toView(); + residualWeight.zero(); + dofScale.zero(); globalIndex const rankOffset = dofManager.rankOffset(); string const elemDofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); string const faceDofKey = dofManager.getKey( mixedMimetic::faceMassFlux::key() ); - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel const & mesh, - string_array const & regionNames ) + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel const & mesh, + string_array const & regionNames ) { ElementRegionManager const & elemManager = mesh.getElemManager(); FaceManager const & faceManager = mesh.getFaceManager(); - // step 1: compute the residual for the element-based mass conservation equations - + // s_p = |p_n| for the cell-pressure dofs elemManager.forElementSubRegions< ElementSubRegionBase >( regionNames, [&]( localIndex const, ElementSubRegionBase const & subRegion ) { - real64 subRegionResidualNorm[1]{}; - real64 subRegionResidualNormalizer[1]{}; + arrayView1d< globalIndex const > const elemDofNumber = + subRegion.getReference< array1d< globalIndex > >( elemDofKey ); + arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); + arrayView1d< real64 const > const presN = subRegion.getField< fields::flow::pressure_n >(); - singlePhaseBaseKernels:: - ResidualNormKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( normType, - rankOffset, - elemDofKey, - localRhs, - subRegion, - m_nonlinearSolverParameters.m_minNormalizer, - subRegionResidualNorm, - subRegionResidualNormalizer ); + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + if( elemGhostRank[ei] < 0 ) + { + dofScale[elemDofNumber[ei] - rankOffset] = LvArray::math::abs( presN[ei] ); + } + } ); + } ); + + // s_m = p_scale / |M_ff| for the face-flux dofs, p_scale the mean adjacent |p_n| + ElementRegionManager::ElementViewAccessor< arrayView1d< real64 const > > const presNAccessor = + elemManager.constructArrayViewAccessor< real64, 1 >( fields::flow::pressure_n::key() ); + ElementRegionManager::ElementViewConst< arrayView1d< real64 const > > const presN = presNAccessor.toNestedViewConst(); + + arrayView1d< globalIndex const > const faceDofNumber = + faceManager.getReference< array1d< globalIndex > >( faceDofKey ); + arrayView1d< integer const > const faceGhostRank = faceManager.ghostRank(); + arrayView2d< localIndex const > const elemRegionList = faceManager.elementRegionList(); + arrayView2d< localIndex const > const elemSubRegionList = faceManager.elementSubRegionList(); + arrayView2d< localIndex const > const elemList = faceManager.elementList(); + SortedArrayView< localIndex const > const regionFilter = m_regionFilter.toViewConst(); - if( normType == physicsSolverBaseKernels::NormType::Linf ) + forAll< parallelDevicePolicy<> >( faceManager.size(), [=] GEOS_HOST_DEVICE ( localIndex const kf ) + { + if( faceGhostRank[kf] >= 0 || faceDofNumber[kf] < 0 ) { - if( subRegionResidualNorm[0] > localResidualNorm ) + return; + } + real64 presSum = 0.0; + integer elemCounter = 0; + for( integer k = 0; k < elemRegionList.size( 1 ); ++k ) + { + localIndex const er = elemRegionList[kf][k]; + localIndex const esr = elemSubRegionList[kf][k]; + localIndex const ei = elemList[kf][k]; + if( er >= 0 && esr >= 0 && ei >= 0 && regionFilter.contains( er ) ) { - localResidualNorm = subRegionResidualNorm[0]; + presSum += LvArray::math::abs( presN[er][esr][ei] ); + elemCounter++; } } - else + real64 const pScale = elemCounter > 0 ? presSum / elemCounter : 0.0; + localIndex const localRow = faceDofNumber[kf] - rankOffset; + real64 diag = 0.0; + arraySlice1d< globalIndex const > const columns = localMatrix.getColumns( localRow ); + arraySlice1d< real64 const > const entries = localMatrix.getEntries( localRow ); + for( localIndex k = 0; k < localMatrix.numNonZeros( localRow ); ++k ) { - localResidualNorm += subRegionResidualNorm[0]; - localResidualNormalizer += subRegionResidualNormalizer[0]; + if( columns[k] == faceDofNumber[kf] ) + { + diag = LvArray::math::abs( entries[k] ); + } } + dofScale[localRow] = diag > 0.0 ? pScale / diag : pScale; } ); + } ); - // step 2: compute the residual for the face-based constitutive rows - - real64 faceResidualNorm[1]{}; - real64 faceResidualNormalizer[1]{}; - - singlePhaseMixedMFDKernels:: - ResidualNormKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( normType, - rankOffset, - faceDofKey, - localRhs, - m_regionFilter.toViewConst(), - getName(), - elemManager, - faceManager, - m_nonlinearSolverParameters.m_minNormalizer, - faceResidualNorm, - faceResidualNormalizer ); - - if( normType == physicsSolverBaseKernels::NormType::Linf ) + // w_i = max_j |A_ij| s_j over the locally-owned columns of row i + forAll< parallelDevicePolicy<> >( numRows, [=] GEOS_HOST_DEVICE ( localIndex const i ) + { + real64 w = 0.0; + arraySlice1d< globalIndex const > const columns = localMatrix.getColumns( i ); + arraySlice1d< real64 const > const entries = localMatrix.getEntries( i ); + for( localIndex k = 0; k < localMatrix.numNonZeros( i ); ++k ) { - if( faceResidualNorm[0] > localResidualNorm ) + globalIndex const localCol = columns[k] - rankOffset; + if( localCol >= 0 && localCol < numRows ) { - localResidualNorm = faceResidualNorm[0]; + w = LvArray::math::max( w, LvArray::math::abs( entries[k] ) * dofScale[localCol] ); } } - else - { - localResidualNorm += faceResidualNorm[0]; - localResidualNormalizer += faceResidualNormalizer[0]; - } + residualWeight[i] = w; } ); +} - // step 3: second reduction across MPI ranks +void SinglePhaseMixedMFD::applyAquiferBC( real64 const time, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const +{ + GEOS_MARK_FUNCTION; + GEOS_UNUSED_VAR( time, dt, dofManager, domain, localMatrix, localRhs ); +} + +void SinglePhaseMixedMFD::saveAquiferConvergedState( real64 const & time, + real64 const & dt, + DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + GEOS_UNUSED_VAR( time, dt, domain ); +} + +real64 SinglePhaseMixedMFD::calculateResidualNorm( real64 const & GEOS_UNUSED_PARAM( time_n ), + real64 const & GEOS_UNUSED_PARAM( dt ), + DomainPartition const & GEOS_UNUSED_PARAM( domain ), + DofManager const & GEOS_UNUSED_PARAM( dofManager ), + arrayView1d< real64 const > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + + // row-equilibrated residual norm ||D^{-1} r||, D = diag(max(eps, w_i)), w_i = ||A_i S||_inf + physicsSolverBaseKernels::NormType const normType = getNonlinearSolverParameters().normType(); + real64 const minNormalizer = m_nonlinearSolverParameters.m_minNormalizer; + + arrayView1d< real64 const > const residualWeight = m_residualWeight.toViewConst(); + localIndex const numRows = localRhs.size(); real64 residualNorm = 0.0; if( normType == physicsSolverBaseKernels::NormType::Linf ) { - physicsSolverBaseKernels::LinfResidualNormHelper::computeGlobalNorm( localResidualNorm, residualNorm ); + RAJA::ReduceMax< parallelDeviceReduce, real64 > maxVal( 0.0 ); + forAll< parallelDevicePolicy<> >( numRows, [=] GEOS_HOST_DEVICE ( localIndex const i ) + { + maxVal.max( LvArray::math::abs( localRhs[i] ) / + LvArray::math::max( minNormalizer, residualWeight[i] ) ); + } ); + real64 const localNorm = maxVal.get(); + physicsSolverBaseKernels::LinfResidualNormHelper::computeGlobalNorm( localNorm, residualNorm ); } else { - physicsSolverBaseKernels::L2ResidualNormHelper::computeGlobalNorm( localResidualNorm, localResidualNormalizer, residualNorm ); + RAJA::ReduceSum< parallelDeviceReduce, real64 > sumVal( 0.0 ); + forAll< parallelDevicePolicy<> >( numRows, [=] GEOS_HOST_DEVICE ( localIndex const i ) + { + real64 const r = localRhs[i] / LvArray::math::max( minNormalizer, residualWeight[i] ); + sumVal += r * r; + } ); + real64 const localSum = sumVal.get(); + real64 const localCount = static_cast< real64 >( numRows ); + physicsSolverBaseKernels::L2ResidualNormHelper::computeGlobalNorm( localSum, localCount, residualNorm ); } GEOS_LOG_LEVEL_RANK_0_NLR( logInfo::ResidualNorm, diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp index 5d27ffbb4d9..413db379e4a 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp @@ -186,7 +186,7 @@ class SinglePhaseMixedMFD : public SinglePhaseBase * @brief Classify the faces from the cell marking (0 = condensable TPFA face, 1 = live MFD face). * @param[in] domain the domain */ - void computeFaceStencilLabels( DomainPartition & domain ); + void computeFaceStencilLabels( DomainPartition & domain, bool const keepAllFacesLive ); /** * @brief Build the per-dof labels used by the stencilFlag-guided three-level MGR strategy: @@ -199,12 +199,28 @@ class SinglePhaseMixedMFD : public SinglePhaseBase void computeMgrPointMarkers( DomainPartition const & domain, DofManager const & dofManager ); + /** + * @brief Build the de Rham sub-complex of the live MFD faces (discrete curl and gradient, + * active vertex coordinates) for the Riesz-map preconditioner. + * @param[in] domain the domain + * @param[in] dofManager the dof manager (dof numbers must be finalized) + */ + void computeADSAuxData( DomainPartition const & domain, + DofManager const & dofManager ); + /// relative tolerance used in the mass matrix computations real64 m_areaRelTol; + /// region filter used in flux assembly SortedArray< localIndex > m_regionFilter; + /// residual-norm weights w_i = ||A_i S||_inf, S = diag(s_j) + array1d< real64 > m_residualWeight; + + /// characteristic scale s_j of unknown x_j: s_p = |p_n|, s_m = p_scale / |M_ff| + array1d< real64 > m_dofScale; + }; } /* namespace geos */ From dcf55bfbcf07652b0999046a52633db8246c54db Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Sat, 5 Sep 2026 22:46:07 -0700 Subject: [PATCH 28/64] wip: include volume degeneracy Tolerance --- .../mgrStrategies/SinglePhaseMixedMFD.hpp | 114 ++++-------------- .../MixedMimeticDiscretization.cpp | 15 ++- .../MixedMimeticDiscretization.hpp | 11 ++ .../mixedMimetic/MixedMimeticFields.hpp | 8 ++ .../fluidFlow/SinglePhaseMixedMFD.cpp | 87 +++++++++++++ .../fluidFlow/SinglePhaseMixedMFD.hpp | 8 ++ 6 files changed, 154 insertions(+), 89 deletions(-) diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp index 0deb380e41f..45230e6d5a2 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp @@ -20,7 +20,6 @@ #ifndef GEOS_LINEARALGEBRA_INTERFACES_HYPREMGRSINGLEPHASEMIXEDMFD_HPP_ #define GEOS_LINEARALGEBRA_INTERFACES_HYPREMGRSINGLEPHASEMIXEDMFD_HPP_ -#include "common/MpiWrapper.hpp" #include "linearAlgebra/interfaces/hypre/HypreMGR.hpp" namespace geos @@ -33,34 +32,27 @@ namespace mgr { /** - * @brief SinglePhaseMixedMFD strategy: stencilFlag-guided three-level MGR reduction of the - * mixed mimetic finite difference saddle-point system - * [ M -B^T ] - * [ B 0 ] - * with face mass-flux and cell pressure unknowns. + * @brief SinglePhaseMixedMFD strategy: one-level multigrid reduction of the mixed mimetic + * saddle point + * [ M D^T ] + * [ D C ] + * with face mass-flux (F) and cell pressure (C) unknowns. * - * The solver provides custom point markers (LinearSolverParameters::MGR::customPointMarkers) - * splitting the face-flux dofs by the Global Adaptation classification: - * dofLabel: 0 = face flux whose adjacent cells are all TPFA-compatible (the assembled - * flux row is exactly diagonal: TPFA/TPFA interfaces, boundary faces of - * TPFA cells and no-flow identity rows) - * dofLabel: 1 = face flux adjacent to at least one MFD-compatible cell - * dofLabel: 2 = cell-centered pressure + * The interpolation is built from the diagonal of the flux block, W_p = -diag(M)^{-1} D^T, + * the restriction is injection and the Galerkin coarse operator is the cell-centred + * Laplacian S = C + D diag(M)^{-1} D^T: exact where both cells of a face use the diagonal + * (TPFA) product, and spectrally equivalent to the pressure Schur complement elsewhere with + * the constants of diag(M) ~ M. Being an M-matrix, S is what classical AMG is built for; + * the F-relaxation is one symmetric Gauss-Seidel sweep on M and the coarse solve one BoomerAMG + * V-cycle. * - * Ingredients: - * 1. Level 0: F-points = TPFA-diagonal face fluxes (label 0). The F-block is exactly - * diagonal, so a single Jacobi sweep and Jacobi (diagonal) prolongation perform the - * elimination exactly, and the Galerkin (RAP) coarse grid is the exact Schur complement. - * The cost of this level is proportional to the TPFA fraction selected by the - * residual tolerance, mirroring the sparsity-reduction metric of the adaptive scheme. - * 2. Level 1: F-points = MFD face fluxes (label 1), relaxed with symmetric Gauss-Seidel - * sweeps on the (SPD, well-conditioned) MFD flux block; Jacobi prolongation, - * injection restriction, Galerkin (RAP) coarse grid approximating the pressure - * Schur complement. - * 3. Coarsest level: cell-pressure system solved with BoomerAMG. - * 4. Global smoother: none. The Krylov solver is (F)GMRES. + * The solver provides custom point markers (LinearSolverParameters::MGR::customPointMarkers): + * 0 = face flux whose row is exactly diagonal (condensed two-point face, no-flow face) + * 1 = face flux adjacent to at least one stabilized (MFD) cell + * 2 = cell pressure + * Both flux labels are F-points of the single reduction level. */ -class SinglePhaseMixedMFD : public MGRStrategyBase< 2 > +class SinglePhaseMixedMFD : public MGRStrategyBase< 1 > { public: /** @@ -69,35 +61,23 @@ class SinglePhaseMixedMFD : public MGRStrategyBase< 2 > explicit SinglePhaseMixedMFD( arrayView1d< int const > const & ) : MGRStrategyBase( LvArray::integerConversion< HYPRE_Int >( 3 ) ) { - // Level 0: eliminate the TPFA-diagonal face fluxes, keep the MFD fluxes and the pressure - m_labels[0].push_back( 1 ); + // Level 0: eliminate every face flux, keep the pressure m_labels[0].push_back( 2 ); - - // Level 1: eliminate the MFD face fluxes, keep the pressure - m_labels[1].push_back( 2 ); - setupLabels(); - // Level 0: the F-block is exactly diagonal - one Jacobi sweep is an exact solve - m_levelFRelaxType[0] = MGRFRelaxationType::jacobi; + // l1-scaled relaxations are rejected by hypre here (the pressure rows have an empty C-C block + // without accumulation) and plain Jacobi has no weight in MGR: symmetric Gauss-Seidel instead + m_levelFRelaxType[0] = MGRFRelaxationType::hybridSymmetricGaussSeidel; m_levelFRelaxIters[0] = 1; m_levelInterpType[0] = MGRInterpolationType::jacobi; m_levelRestrictType[0] = MGRRestrictionType::injection; m_levelCoarseGridMethod[0] = MGRCoarseGridMethod::galerkin; m_levelGlobalSmootherType[0] = MGRGlobalSmootherType::none; - - // Level 1: SGS sweeps on the well-conditioned SPD flux block outperform an AMG - // V-cycle; Jacobi prolongation avoids the setup cost of approximateInverse - m_levelFRelaxType[1] = MGRFRelaxationType::hybridSymmetricGaussSeidel; - m_levelFRelaxIters[1] = 3; - m_levelInterpType[1] = MGRInterpolationType::jacobi; - m_levelRestrictType[1] = MGRRestrictionType::injection; - m_levelCoarseGridMethod[1] = MGRCoarseGridMethod::galerkin; - m_levelGlobalSmootherType[1] = MGRGlobalSmootherType::none; } /** * @brief Setup the MGR strategy. + * @param mgrParams MGR parameters * @param precond preconditioner wrapper * @param mgrData auxiliary MGR data */ @@ -105,53 +85,11 @@ class SinglePhaseMixedMFD : public MGRStrategyBase< 2 > HyprePrecWrapper & precond, HypreMGRData & mgrData ) { - // a reduction level with an empty F-set is the identity and is dropped: the level - // structure is determined by which label sets D_0 (TPFA dofs) and D_1 (MFD dofs) - // are nonempty - integer localD0Nonempty = 0; - integer localD1Nonempty = 0; - for( localIndex i = 0; i < mgrParams.customPointMarkers.size(); ++i ) - { - localD0Nonempty |= ( mgrParams.customPointMarkers[i] == 0 ); - localD1Nonempty |= ( mgrParams.customPointMarkers[i] == 1 ); - } - bool const d0Nonempty = MpiWrapper::max( localD0Nonempty ) == 1; - bool const d1Nonempty = MpiWrapper::max( localD1Nonempty ) == 1; - - m_labels[0].clear(); - m_labels[1].clear(); - HYPRE_Int numActiveLevels; - if( d0Nonempty && d1Nonempty ) - { - // two reduction levels: level 0 with F = D_0 (exact Jacobi elimination of the - // diagonal block), level 1 with F = D_1 (SSOR sweeps on the MFD block) - m_labels[0].push_back( 1 ); - m_labels[0].push_back( 2 ); - m_labels[1].push_back( 2 ); - m_levelFRelaxType[0] = MGRFRelaxationType::jacobi; - m_levelFRelaxIters[0] = 1; - m_levelInterpType[0] = MGRInterpolationType::jacobi; - numActiveLevels = numLevels; - } - else - { - // one reduction level with F = D_0 (exact Jacobi elimination) or F = D_1 (SSOR sweeps) - m_labels[0].push_back( 2 ); - m_levelFRelaxType[0] = d1Nonempty ? MGRFRelaxationType::hybridSymmetricGaussSeidel - : MGRFRelaxationType::jacobi; - m_levelFRelaxIters[0] = d1Nonempty ? m_levelFRelaxIters[1] : 1; - m_levelInterpType[0] = MGRInterpolationType::jacobi; - numActiveLevels = 1; - } - setupLabels(); - - setReduction( precond, mgrData, numActiveLevels ); + GEOS_UNUSED_VAR( mgrParams ); + setReduction( precond, mgrData ); - // Configure the BoomerAMG solver used as mgr coarse solver for the pressure Schur - // complement. Two V-cycles per MGR application: the coarse solve accuracy governs the - // outer FGMRES iteration count (a single cycle leaves the reduction quality unused) + // one V-cycle on the cell-centred Laplacian setPressureAMG( mgrData.coarseSolver ); - GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetMaxIter( mgrData.coarseSolver.ptr, 2 ) ); } }; diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp index 2b789c2233f..7da73048a68 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp @@ -37,7 +37,8 @@ MixedMimeticDiscretization::MixedMimeticDiscretization( string const & name, : Group( name, parent ), m_isAdaptive( 1 ), m_residualTolerance( 1e-3 ), - m_nominalGradient( { 1.0, 1.0, 1.0 } ) + m_nominalGradient( { 1.0, 1.0, 1.0 } ), + m_degeneracyTolerance( 1.0 ) { setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); @@ -61,6 +62,13 @@ MixedMimeticDiscretization::MixedMimeticDiscretization( string const & name, setInputFlag( InputFlags::OPTIONAL ). setApplyDefaultValue( m_nominalGradient ). setDescription( "Nominal pressure gradient inducing the projected admissible flow field used by the residual indicators" ); + + registerWrapper( viewKeyStruct::degeneracyToleranceString(), &m_degeneracyTolerance ). + setInputFlag( InputFlags::OPTIONAL ). + setApplyDefaultValue( 1.0 ). + setDescription( "Degeneracy tolerance in percent: a cell whose volume is below this percentage of the mean volume " + "of its node star is not admissible for the stabilized reconstruction and uses the diagonal (TPFA) " + "product, whatever the consistency indicator says; 0 disables the layer" ); } void MixedMimeticDiscretization::postInputInitialization() @@ -72,6 +80,11 @@ void MixedMimeticDiscretization::postInputInitialization() getWrapperDataContext( viewKeyStruct::residualToleranceString() ) ), InputError ); + GEOS_THROW_IF_LT_MSG( m_degeneracyTolerance, 0.0, + GEOS_FMT( "{}: the degeneracy tolerance cannot be negative", + getWrapperDataContext( viewKeyStruct::degeneracyToleranceString() ) ), + InputError ); + GEOS_THROW_IF_LT_MSG( LvArray::tensorOps::l2Norm< 3 >( m_nominalGradient.data ), LvArray::NumericLimits< real64 >::epsilon, GEOS_FMT( "{}: the nominal gradient cannot be the zero vector", getWrapperDataContext( viewKeyStruct::nominalGradientString() ) ), diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp index debf3b14f74..65973ad5495 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp @@ -82,6 +82,9 @@ class MixedMimeticDiscretization : public dataRepository::Group /// @return The key for the nominal gradient of the projection probe static constexpr char const * nominalGradientString() { return "nominalGradient"; } + + /// @return The key for the degeneracy tolerance + static constexpr char const * degeneracyToleranceString() { return "degeneracyTolerance"; } }; /** @@ -104,6 +107,11 @@ class MixedMimeticDiscretization : public dataRepository::Group */ R1Tensor getNominalGradient() const { return m_nominalGradient; } + /** + * @brief @return The degeneracy tolerance in percent of the mean node-star volume + */ + real64 getDegeneracyTolerance() const { return m_degeneracyTolerance; } + protected: virtual void postInputInitialization() override; @@ -124,6 +132,9 @@ class MixedMimeticDiscretization : public dataRepository::Group /// nominal gradient inducing the projected admissible flow field R1Tensor m_nominalGradient; + /// cells whose volume is below this percentage of the mean volume of their node star use the diagonal product + real64 m_degeneracyTolerance; + /** * @brief Factory method to instantiate a type of mimetic inner product. * @return A unique_ptr< MimeticInnerProductBase > which contains the new instantiation. diff --git a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp index 5485b8a908b..62399273a05 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp @@ -65,6 +65,14 @@ DECLARE_FIELD( consistencyIndicator, WRITE_AND_READ, "Residual-based consistency indicator per cell (Global Adaptation)" ); +DECLARE_FIELD( degeneracyIndicator, + "degeneracyIndicator", + array1d< real64 >, + 0, + LEVEL_0, + WRITE_AND_READ, + "Cell volume over the mean volume of its node star (admissibility of the stabilized reconstruction)" ); + DECLARE_FIELD( faceStencilLabel, "faceStencilLabel", array1d< integer >, diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index 41b4554b749..26739900afe 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -19,6 +19,9 @@ #include "SinglePhaseMixedMFD.hpp" +#include +#include + #include "common/logger/Logger.hpp" #include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" #include "constitutive/permeability/PermeabilityFields.hpp" @@ -70,6 +73,7 @@ void SinglePhaseMixedMFD::registerDataOnMesh( Group & meshBodies ) { subRegion.registerField< mixedMimetic::stencilFlag >( getName() ); subRegion.registerField< mixedMimetic::consistencyIndicator >( getName() ); + subRegion.registerField< mixedMimetic::degeneracyIndicator >( getName() ); } ); // 2) Register the face data @@ -188,6 +192,9 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d subRegion.getField< mixedMimetic::stencilFlag >().template setValues< parallelDevicePolicy<> >( 1 ); } ); } ); + globalIndex const numDegenerate = MpiWrapper::sum< globalIndex >( applyDegeneracyLayer( domain ) ); + GEOS_LOG_RANK_0( GEOS_FMT( "{}: degeneracy layer (tolerance = {}%) switched {} cells to the diagonal product", + getName(), discretization.getDegeneracyTolerance(), numDegenerate ) ); computeFaceStencilLabels( domain, false ); return; } @@ -297,6 +304,12 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d numMfdCellsTotal += numMfdCells; } ); + // second layer: the degenerate cells fall back to the diagonal product whatever the consistency says + localIndex const numDegenerate = applyDegeneracyLayer( domain ); + numMfdCellsTotal -= numDegenerate; + GEOS_LOG_RANK_0( GEOS_FMT( "{}: degeneracy layer (tolerance = {}%) switched {} more cells to the diagonal product", + getName(), discretization.getDegeneracyTolerance(), MpiWrapper::sum< globalIndex >( numDegenerate ) ) ); + // the Riesz-map preconditioner is a map of the whole saddle point: as soon as one cell is MFD no // face is condensed (eliminating faces removes the elliptic content of the div term from the map); // with no MFD cell the condensed SPD system is solved as such @@ -305,6 +318,80 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d computeFaceStencilLabels( domain, keepAllFacesLive ); } +localIndex SinglePhaseMixedMFD::applyDegeneracyLayer( DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + + NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); + MixedMimeticDiscretizationManager const & mmManager = numericalMethodManager.getMixedMimeticDiscretizationManager(); + MixedMimeticDiscretization const & discretization = mmManager.getMixedMimeticDiscretization( m_discretizationName ); + real64 const tolerance = discretization.getDegeneracyTolerance(); + + localIndex numDegenerate = 0; + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + NodeManager const & nodeManager = mesh.getNodeManager(); + ElementRegionManager & elemManager = mesh.getElemManager(); + ArrayOfArraysView< localIndex const > const nodeToRegion = nodeManager.elementRegionList(); + ArrayOfArraysView< localIndex const > const nodeToSubRegion = nodeManager.elementSubRegionList(); + ArrayOfArraysView< localIndex const > const nodeToElem = nodeManager.elementList(); + ElementRegionManager::ElementViewAccessor< arrayView1d< real64 const > > const elemVolume = + elemManager.constructArrayViewAccessor< real64, 1 >( ElementSubRegionBase::viewKeyStruct::elementVolumeString() ); + + elemManager.forElementSubRegions< CellElementSubRegion >( regionNames, + [&]( localIndex const, + CellElementSubRegion & subRegion ) + { + arrayView2d< localIndex const, cells::NODE_MAP_USD > const elemToNodes = subRegion.nodeList().toViewConst(); + arrayView1d< real64 const > const volume = subRegion.getElementVolume(); + arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + arrayView1d< real64 > const indicator = subRegion.getField< mixedMimetic::degeneracyIndicator >(); + arrayView1d< integer > const stencilFlag = subRegion.getField< mixedMimetic::stencilFlag >(); + localIndex const numNodes = subRegion.numNodesPerElement(); + + // the node star of a cell: every cell sharing a vertex with it, counted once + stdVector< std::array< localIndex, 3 > > star; + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + star.clear(); + for( localIndex a = 0; a < numNodes; ++a ) + { + localIndex const n = elemToNodes( ei, a ); + for( localIndex k = 0; k < nodeToElem.sizeOfArray( n ); ++k ) + { + if( nodeToRegion( n, k ) >= 0 && nodeToSubRegion( n, k ) >= 0 && nodeToElem( n, k ) >= 0 ) + { + star.push_back( { nodeToRegion( n, k ), nodeToSubRegion( n, k ), nodeToElem( n, k ) } ); + } + } + } + std::sort( star.begin(), star.end() ); + star.erase( std::unique( star.begin(), star.end() ), star.end() ); + real64 sum = 0.0; + for( auto const & c : star ) + { + sum += elemVolume[c[0]][c[1]][c[2]]; + } + real64 const mean = star.empty() ? 0.0 : sum / star.size(); + real64 const ratio = mean > 0.0 ? volume[ei] / mean : 0.0; + indicator[ei] = ratio; + if( 100.0 * ratio < tolerance && stencilFlag[ei] == 1 ) + { + stencilFlag[ei] = 0; + numDegenerate += ( ghostRank[ei] < 0 ) ? 1 : 0; + } + } + } ); + + FieldIdentifiers fieldsToBeSync; + fieldsToBeSync.addElementFields( { mixedMimetic::stencilFlag::key(), mixedMimetic::degeneracyIndicator::key() }, regionNames ); + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), false ); + } ); + return numDegenerate; +} + void SinglePhaseMixedMFD::computeFaceStencilLabels( DomainPartition & domain, bool const keepAllFacesLive ) { GEOS_MARK_FUNCTION; diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp index 413db379e4a..bbb3116f383 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp @@ -182,6 +182,14 @@ class SinglePhaseMixedMFD : public SinglePhaseBase */ void computeGlobalAdaptationIndicators( DomainPartition & domain ); + /** + * @brief Second classification layer: cells whose volume is below the degeneracy tolerance + * (percent of the mean volume of their node star) fall back to the diagonal product. + * @param domain the domain + * @return the number of locally-owned cells switched to the diagonal product by this layer + */ + localIndex applyDegeneracyLayer( DomainPartition & domain ); + /** * @brief Classify the faces from the cell marking (0 = condensable TPFA face, 1 = live MFD face). * @param[in] domain the domain From 1b2d76fe0493760a5ea609880fb219a695ba98a4 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Sat, 5 Sep 2026 23:44:16 -0700 Subject: [PATCH 29/64] wip: apply same fix from the FV-TPFA --- .../mimeticInnerProducts/TPFAInnerProduct.hpp | 17 +++++++++++++++-- .../mixedMimetic/MixedMimeticDiscretization.cpp | 6 +++--- .../mixedMimetic/MixedMimeticDiscretization.hpp | 4 ++-- .../mixedMimetic/MixedMimeticFields.hpp | 2 +- .../fluidFlow/SinglePhaseMixedMFD.cpp | 8 ++++---- .../fluidFlow/SinglePhaseMixedMFD.hpp | 2 +- 6 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp index 64e30b10b31..ff474a5018c 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp @@ -123,7 +123,21 @@ class TPFAInnerProduct : public MimeticInnerProductBase // TPFA assumes diagonal K LvArray::tensorOps::hadamardProduct< 3 >( faceConormal, elemPerm, faceNormal ); - return LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceConormal ) * faceArea / c2fDistance; + // Two-point conductance t = (A/d) c.(K n) with c the unit cell-to-face direction: it is the + // exact flux of a linear pressure iff K n is parallel to c (K-orthogonality) and it is a valid + // (positive) conductance only while c.(K n) > 0. On a K-obtuse face, c.(K n) <= 0, the same + // fallback as the finite-volume TPFA (CellElementStencilTPFAWrapper::computeWeights, "correct + // negative weight issue arising from non-K-orthogonal grids") replaces K n by K c: the + // conductance the face would have if it were K-orthogonal, c.(K c) > 0 for SPD K, so the + // product stays positive definite at the price of consistency, which the indicator measures. + real64 halfTrans = LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceConormal ); + if( halfTrans < 0.0 ) + { + LvArray::tensorOps::hadamardProduct< 3 >( faceConormal, elemPerm, cellToFaceVec ); + halfTrans = LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceConormal ); + } + + return halfTrans * faceArea / c2fDistance; } }; @@ -200,7 +214,6 @@ TPFAInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION } } - } // end namespace mimeticInnerProduct } // end namespace geos diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp index 7da73048a68..275881a2912 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp @@ -38,7 +38,7 @@ MixedMimeticDiscretization::MixedMimeticDiscretization( string const & name, m_isAdaptive( 1 ), m_residualTolerance( 1e-3 ), m_nominalGradient( { 1.0, 1.0, 1.0 } ), - m_degeneracyTolerance( 1.0 ) + m_degeneracyTolerance( 0.1 ) { setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); @@ -65,8 +65,8 @@ MixedMimeticDiscretization::MixedMimeticDiscretization( string const & name, registerWrapper( viewKeyStruct::degeneracyToleranceString(), &m_degeneracyTolerance ). setInputFlag( InputFlags::OPTIONAL ). - setApplyDefaultValue( 1.0 ). - setDescription( "Degeneracy tolerance in percent: a cell whose volume is below this percentage of the mean volume " + setApplyDefaultValue( 0.1 ). + setDescription( "Degeneracy tolerance in percent: a cell whose volume is below this percentage of the total volume " "of its node star is not admissible for the stabilized reconstruction and uses the diagonal (TPFA) " "product, whatever the consistency indicator says; 0 disables the layer" ); } diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp index 65973ad5495..50ee8a01c42 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp @@ -108,7 +108,7 @@ class MixedMimeticDiscretization : public dataRepository::Group R1Tensor getNominalGradient() const { return m_nominalGradient; } /** - * @brief @return The degeneracy tolerance in percent of the mean node-star volume + * @brief @return The degeneracy tolerance in percent of the node-star volume */ real64 getDegeneracyTolerance() const { return m_degeneracyTolerance; } @@ -132,7 +132,7 @@ class MixedMimeticDiscretization : public dataRepository::Group /// nominal gradient inducing the projected admissible flow field R1Tensor m_nominalGradient; - /// cells whose volume is below this percentage of the mean volume of their node star use the diagonal product + /// cells whose volume is below this percentage of the volume of their node star use the diagonal product real64 m_degeneracyTolerance; /** diff --git a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp index 62399273a05..6eff358de8c 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp @@ -71,7 +71,7 @@ DECLARE_FIELD( degeneracyIndicator, 0, LEVEL_0, WRITE_AND_READ, - "Cell volume over the mean volume of its node star (admissibility of the stabilized reconstruction)" ); + "Cell volume as a percentage of the total volume of its node star (admissibility of the stabilized reconstruction)" ); DECLARE_FIELD( faceStencilLabel, "faceStencilLabel", diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index 26739900afe..2bd1f3bdf12 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -374,10 +374,10 @@ localIndex SinglePhaseMixedMFD::applyDegeneracyLayer( DomainPartition & domain ) { sum += elemVolume[c[0]][c[1]][c[2]]; } - real64 const mean = star.empty() ? 0.0 : sum / star.size(); - real64 const ratio = mean > 0.0 ? volume[ei] / mean : 0.0; - indicator[ei] = ratio; - if( 100.0 * ratio < tolerance && stencilFlag[ei] == 1 ) + // share of the cell in the volume of its node star, in percent + real64 const percent = sum > 0.0 ? 100.0 * volume[ei] / sum : 0.0; + indicator[ei] = percent; + if( percent < tolerance && stencilFlag[ei] == 1 ) { stencilFlag[ei] = 0; numDegenerate += ( ghostRank[ei] < 0 ) ? 1 : 0; diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp index bbb3116f383..91f37774b43 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp @@ -184,7 +184,7 @@ class SinglePhaseMixedMFD : public SinglePhaseBase /** * @brief Second classification layer: cells whose volume is below the degeneracy tolerance - * (percent of the mean volume of their node star) fall back to the diagonal product. + * (percent of the volume of their node star) fall back to the diagonal product. * @param domain the domain * @return the number of locally-owned cells switched to the diagonal product by this layer */ From c77197296564aadd22907b031e4d6250158919c0 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Sun, 6 Sep 2026 01:07:27 -0700 Subject: [PATCH 30/64] wip: correct parallel runs and remove riesz map concept --- .../hybrid_riesz_mfd_base.xml | 129 --- .../hybrid_riesz_mfd_l0.xml | 15 - .../hybrid_riesz_mfd_l1.xml | 15 - .../hybrid_riesz_mfd_l2.xml | 15 - .../hybrid_riesz_mfd_l3.xml | 15 - .../hybrid_riesz_mfd_l4.xml | 15 - .../mfd_ads_test.xml | 143 ---- .../interfaces/hypre/HyprePreconditioner.cpp | 6 - .../interfaces/hypre/HypreRieszMFD.cpp | 771 ------------------ .../interfaces/hypre/HypreRieszMFD.hpp | 59 -- .../interfaces/hypre/hypredrive.cpp | 1 - .../testLinearSolverParametersEnums.cpp | 1 - .../utilities/LinearSolverParameters.hpp | 27 +- .../MixedMimeticDiscretization.cpp | 22 +- .../MixedMimeticDiscretization.hpp | 24 +- .../mixedMimetic/MixedMimeticFields.hpp | 16 + .../adaptivity/GlobalAdaptationKernels.hpp | 5 +- .../fluidFlow/SinglePhaseMixedMFD.cpp | 422 +++------- .../fluidFlow/SinglePhaseMixedMFD.hpp | 23 +- .../SinglePhaseMixedMFDKernels.hpp | 35 +- 20 files changed, 184 insertions(+), 1575 deletions(-) delete mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_base.xml delete mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l0.xml delete mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l1.xml delete mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l2.xml delete mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l3.xml delete mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l4.xml delete mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_ads_test.xml delete mode 100644 src/coreComponents/linearAlgebra/interfaces/hypre/HypreRieszMFD.cpp delete mode 100644 src/coreComponents/linearAlgebra/interfaces/hypre/HypreRieszMFD.hpp diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_base.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_base.xml deleted file mode 100644 index 37c92ae4c9d..00000000000 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_base.xml +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l0.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l0.xml deleted file mode 100644 index be26bc67a64..00000000000 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l0.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l1.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l1.xml deleted file mode 100644 index 56893420c60..00000000000 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l1.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l2.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l2.xml deleted file mode 100644 index ec0732e1873..00000000000 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l2.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l3.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l3.xml deleted file mode 100644 index b3c596b02e9..00000000000 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l3.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l4.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l4.xml deleted file mode 100644 index 53d949fb3e5..00000000000 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_riesz_mfd_l4.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_ads_test.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_ads_test.xml deleted file mode 100644 index c23e647954a..00000000000 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_ads_test.xml +++ /dev/null @@ -1,143 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/HyprePreconditioner.cpp b/src/coreComponents/linearAlgebra/interfaces/hypre/HyprePreconditioner.cpp index ec159d306ac..1a6e2691b34 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/HyprePreconditioner.cpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/HyprePreconditioner.cpp @@ -19,7 +19,6 @@ #include "HyprePreconditioner.hpp" #include "HypreMGR.hpp" -#include "HypreRieszMFD.hpp" #include "linearAlgebra/DofManager.hpp" #include "linearAlgebra/interfaces/hypre/HypreUtils.hpp" @@ -363,11 +362,6 @@ void HyprePreconditioner::create( DofManager const * const dofManager ) m_precond->destroy = hypre::SuperLUDistDestroy; break; } - case LinearSolverParameters::PreconditionerType::riesz: - { - hypre::createRieszMFD( m_params, *m_precond ); - break; - } default: { GEOS_ERROR( GEOS_FMT( "Preconditioner type not supported in hypre interface: {}", m_params.preconditionerType ) ); diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreRieszMFD.cpp b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreRieszMFD.cpp deleted file mode 100644 index 609d2b8aa21..00000000000 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreRieszMFD.cpp +++ /dev/null @@ -1,771 +0,0 @@ -/* - * ------------------------------------------------------------------------------------------------------------ - * SPDX-License-Identifier: LGPL-2.1-only - * - * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC - * Copyright (c) 2018-2024 TotalEnergies - * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University - * Copyright (c) 2023-2024 Chevron - * Copyright (c) 2019- GEOS/GEOSX Contributors - * All rights reserved - * - * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. - * ------------------------------------------------------------------------------------------------------------ - */ - -/** - * @file HypreRieszMFD.cpp - */ - -#include "HypreRieszMFD.hpp" - -#include "linearAlgebra/common/common.hpp" - -#include "common/MpiWrapper.hpp" - -#include -#include -#include -#include <_hypre_utilities.h> -#include <_hypre_parcsr_mv.h> - -#include -#include -#include -#include -#include -#include -#include - -namespace geos -{ - -namespace hypre -{ - -namespace -{ - -/// dof markers set by the physics solver -constexpr HYPRE_Int condensedFaceMarker = 0; -constexpr HYPRE_Int liveFaceMarker = 1; -constexpr HYPRE_Int pressureMarker = 2; - -using RowMap = std::map< HYPRE_Int, HYPRE_Real >; - -/// Owning container for the Riesz-map preconditioner. The hypre_Solver base is first so the -/// handle is also usable where hypre dispatches through the base struct (e.g. as an MGR solver). -struct RieszMFDData -{ - hypre_Solver base{}; - - stdVector< HYPRE_Int > blockRow; ///< row of each local dof within its block - stdVector< HYPRE_Int > kindOf; ///< marker of each local dof - stdVector< HYPRE_Int > dofOfFlux0; ///< block row -> local dof, condensed faces - stdVector< HYPRE_Int > dofOfFlux1; ///< block row -> local dof, live faces - stdVector< HYPRE_Int > dofOfPres; ///< block row -> local dof, pressures - - HYPRE_IJMatrix curlIJ{}; ///< discrete curl of the live-face sub-complex - HYPRE_IJMatrix gradIJ{}; ///< discrete gradient of the live-face sub-complex - HYPRE_IJVector coordIJ[3]{}; ///< active vertex coordinates - - stdVector< HYPRE_Int > isMfd; ///< 1 for the pressure of an MFD cell, 0 for a TPFA cell - stdVector< HYPRE_Real > normScale; ///< (l_e/D)^2 of each pressure row: geometric factor of the L2 weight - - HYPRE_IJMatrix nIJ{}; ///< M + B_M^T W_M^{-1} B_M on the live faces - HYPRE_IJMatrix pIJ{}; ///< pressure block: two-point couplings, L2 mass of the MFD cells, interface links - HYPRE_IJMatrix b0tIJ{}; ///< F0, condensed-face rows x pressures - stdVector< HYPRE_Real > invD0; ///< inverse diagonal of the condensed-face closure rows - stdVector< HYPRE_Int > pinned; ///< 1 for a live flux dof out of the space - stdVector< HYPRE_Real > invDiagPinned; ///< exact diagonal solve of the pinned dofs - stdVector< HYPRE_Real > rowScale; ///< gamma / s_e: takes a conservation residual to the symmetrized system - stdVector< HYPRE_Real > invWm; ///< 1 / (W_M + C_M) on the MFD pressures, 0 elsewhere - bool adsActive = false; ///< false when no live dof is left for the flux block - HYPRE_Solver ads{}; - HYPRE_Solver pcg{}; ///< CG on the flux block preconditioned by ADS - HYPRE_Solver amg{}; - - HYPRE_IJVector r1{}, x1{}, rt{}, xt{}, xp{}, t0{}; ///< block-sized work vectors -}; - -/// PCG re-setup hook: ADS is set up once, before PCG -HYPRE_Int noSetup( HYPRE_Solver, HYPRE_ParCSRMatrix, HYPRE_ParVector, HYPRE_ParVector ) -{ - return 0; -} - -HYPRE_IJMatrix createIJFromRowMaps( stdVector< RowMap > const & rows, - HYPRE_BigInt const numCols ) -{ - HYPRE_BigInt const numRows = LvArray::integerConversion< HYPRE_BigInt >( rows.size() ); - HYPRE_IJMatrix ij{}; - GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixCreate( MPI_COMM_WORLD, 0, numRows - 1, 0, numCols - 1, &ij ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixSetObjectType( ij, HYPRE_PARCSR ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixInitialize( ij ) ); - stdVector< HYPRE_BigInt > cols; - stdVector< HYPRE_Real > vals; - for( HYPRE_BigInt i = 0; i < numRows; ++i ) - { - RowMap const & row = rows[i]; - HYPRE_Int nnz = LvArray::integerConversion< HYPRE_Int >( row.size() ); - if( nnz == 0 ) - { - continue; - } - cols.clear(); - vals.clear(); - for( auto const & [c, v] : row ) - { - cols.push_back( c ); - vals.push_back( v ); - } - GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixSetValues( ij, 1, &nnz, &i, cols.data(), vals.data() ) ); - } - GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixAssemble( ij ) ); - return ij; -} - -HYPRE_IJMatrix createIJFromCSR( arrayView1d< globalIndex const > const & rowPtr, - arrayView1d< globalIndex const > const & cols, - arrayView1d< real64 const > const & vals, - globalIndex const numCols ) -{ - HYPRE_BigInt const numRows = LvArray::integerConversion< HYPRE_BigInt >( rowPtr.size() - 1 ); - HYPRE_IJMatrix ij{}; - GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixCreate( MPI_COMM_WORLD, 0, numRows - 1, 0, numCols - 1, &ij ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixSetObjectType( ij, HYPRE_PARCSR ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixInitialize( ij ) ); - stdVector< HYPRE_BigInt > colIdx; - for( HYPRE_BigInt i = 0; i < numRows; ++i ) - { - HYPRE_Int nnz = LvArray::integerConversion< HYPRE_Int >( rowPtr[i + 1] - rowPtr[i] ); - if( nnz == 0 ) - { - continue; - } - colIdx.assign( nnz, 0 ); - for( HYPRE_Int k = 0; k < nnz; ++k ) - { - colIdx[k] = LvArray::integerConversion< HYPRE_BigInt >( cols[rowPtr[i] + k] ); - } - GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixSetValues( ij, 1, &nnz, &i, colIdx.data(), &vals[rowPtr[i]] ) ); - } - GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixAssemble( ij ) ); - return ij; -} - -HYPRE_IJVector createIJVector( HYPRE_BigInt const n, HYPRE_Real const * const vals ) -{ - HYPRE_IJVector ij{}; - GEOS_LAI_CHECK_ERROR( HYPRE_IJVectorCreate( MPI_COMM_WORLD, 0, n - 1, &ij ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_IJVectorSetObjectType( ij, HYPRE_PARCSR ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_IJVectorInitialize( ij ) ); - if( n > 0 ) - { - stdVector< HYPRE_BigInt > idx( n ); - std::iota( idx.begin(), idx.end(), HYPRE_BigInt( 0 ) ); - if( vals != nullptr ) - { - GEOS_LAI_CHECK_ERROR( HYPRE_IJVectorSetValues( ij, n, idx.data(), vals ) ); - } - } - GEOS_LAI_CHECK_ERROR( HYPRE_IJVectorAssemble( ij ) ); - return ij; -} - -/// ADS builds its vector interpolation from the linear part of the coordinate functions; a -/// projected coordinate system carries an offset that dwarfs the domain and turns that linear -/// part into a rounding error, so the coordinates are shifted to the origin -HYPRE_IJVector createShiftedCoordinates( arrayView1d< real64 const > const & vals ) -{ - HYPRE_BigInt const n = LvArray::integerConversion< HYPRE_BigInt >( vals.size() ); - real64 const lo = n > 0 ? *std::min_element( vals.begin(), vals.end() ) : 0.0; - stdVector< HYPRE_Real > shifted( n ); - for( HYPRE_BigInt i = 0; i < n; ++i ) - { - shifted[i] = vals[i] - lo; - } - return createIJVector( n, shifted.data() ); -} - -hypre_ParCSRMatrix * parCSROf( HYPRE_IJMatrix const ij ) -{ - hypre_ParCSRMatrix * mat{}; - GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixGetObject( ij, reinterpret_cast< void * * >( &mat ) ) ); - return mat; -} - -hypre_ParVector * parVectorOf( HYPRE_IJVector const ij ) -{ - hypre_ParVector * vec{}; - GEOS_LAI_CHECK_ERROR( HYPRE_IJVectorGetObject( ij, reinterpret_cast< void * * >( &vec ) ) ); - return vec; -} - -HYPRE_Real * dataOf( HYPRE_IJVector const ij ) -{ - return hypre_VectorData( hypre_ParVectorLocalVector( parVectorOf( ij ) ) ); -} - -void destroyIJ( HYPRE_IJMatrix & ij ) -{ - if( ij ) - { - GEOS_LAI_CHECK_ERROR( HYPRE_IJMatrixDestroy( ij ) ); - ij = nullptr; - } -} - -void destroyIJ( HYPRE_IJVector & ij ) -{ - if( ij ) - { - GEOS_LAI_CHECK_ERROR( HYPRE_IJVectorDestroy( ij ) ); - ij = nullptr; - } -} - -void destroySetupObjects( RieszMFDData & data ) -{ - if( data.pcg ) - { - GEOS_LAI_CHECK_ERROR( HYPRE_ParCSRPCGDestroy( data.pcg ) ); - data.pcg = nullptr; - } - if( data.ads ) - { - GEOS_LAI_CHECK_ERROR( HYPRE_ADSDestroy( data.ads ) ); - data.ads = nullptr; - } - if( data.amg ) - { - GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGDestroy( data.amg ) ); - data.amg = nullptr; - } - destroyIJ( data.nIJ ); - destroyIJ( data.pIJ ); - destroyIJ( data.b0tIJ ); - destroyIJ( data.r1 ); - destroyIJ( data.x1 ); - destroyIJ( data.rt ); - destroyIJ( data.xt ); - destroyIJ( data.xp ); - destroyIJ( data.t0 ); -} - -/** - * The assembled system, in the unknowns (q1 live fluxes, q0 condensed fluxes, p pressures), is - * - * [ M 0 F ] [ q1 ] F = -gamma B^T, B the +-1 face-to-cell incidence of the live faces, - * [ 0 D0 F0 ] [ q0 ] s_e B_e the conservation row of cell e (its own scale s_e), - * [ s B 0 C ] [ p ] C = s (C_acc + S_TT), S_TT the two-point Laplacian of the TPFA cells. - * - * Multiplying the conservation rows by gamma / s_e gives the symmetrized saddle point with - * gamma B and its transpose, on which the Riesz map of the product norm - * - * H(div; MFD region) x L2(MFD cells) x H1(TPFA cells) - * - * is P = diag( M + gamma^2 B_M^T W_M^{-1} B_M , W_M + C_M , C_T + S_TT ): - * the flux block by CG under ADS, the MFD pressures by their diagonal, the TPFA pressures by one - * AMG cycle. The interface faces are not weighted: they are bounded by the normal-trace inequality. - * The condensed fluxes are recovered exactly from their closure rows. - */ -HYPRE_Int rieszSetup( HYPRE_Solver solver, - HYPRE_ParCSRMatrix A, - HYPRE_ParVector, - HYPRE_ParVector ) -{ - RieszMFDData & data = *reinterpret_cast< RieszMFDData * >( solver ); - destroySetupObjects( data ); - - hypre_ParCSRMatrix * const mat = reinterpret_cast< hypre_ParCSRMatrix * >( A ); - hypre_CSRMatrix * const diag = hypre_ParCSRMatrixDiag( mat ); - HYPRE_Int const n = hypre_CSRMatrixNumRows( diag ); - HYPRE_Int const * const ia = hypre_CSRMatrixI( diag ); - HYPRE_Int const * const ja = hypre_CSRMatrixJ( diag ); - HYPRE_Real const * const va = hypre_CSRMatrixData( diag ); - GEOS_ERROR_IF_NE_MSG( LvArray::integerConversion< std::size_t >( n ), data.kindOf.size(), - "Riesz MFD preconditioner: the dof markers do not match the system size" ); - - HYPRE_Int const nf0 = LvArray::integerConversion< HYPRE_Int >( data.dofOfFlux0.size() ); - HYPRE_Int const nf1 = LvArray::integerConversion< HYPRE_Int >( data.dofOfFlux1.size() ); - HYPRE_Int const np = LvArray::integerConversion< HYPRE_Int >( data.dofOfPres.size() ); - - // 1) read the blocks off the assembled system - stdVector< RowMap > m1( nf1 ), c( np ), b0t( nf0 ); - stdVector< RowMap > b1( np ); // conservation rows x live faces - stdVector< stdVector< std::pair< HYPRE_Int, HYPRE_Real > > > b1Cols( nf1 ); // live face -> (cell, value) - HYPRE_Real gamma = 0.0; // magnitude of the (1,2) entries: |F| = gamma - data.invD0.assign( nf0, 0.0 ); - for( HYPRE_Int i = 0; i < n; ++i ) - { - HYPRE_Int const ki = data.kindOf[i]; - HYPRE_Int const ri = data.blockRow[i]; - for( HYPRE_Int k = ia[i]; k < ia[i + 1]; ++k ) - { - HYPRE_Int const j = ja[k]; - HYPRE_Int const kj = data.kindOf[j]; - HYPRE_Int const rj = data.blockRow[j]; - HYPRE_Real const v = va[k]; - if( ki == liveFaceMarker && kj == liveFaceMarker ) - { - m1[ri][rj] += v; - } - else if( ki == pressureMarker && kj == liveFaceMarker ) - { - b1[ri][rj] += v; - b1Cols[rj].emplace_back( ri, v ); - } - else if( ki == liveFaceMarker && kj == pressureMarker ) - { - gamma = std::max( gamma, std::abs( v ) ); - } - else if( ki == pressureMarker && kj == pressureMarker ) - { - c[ri][rj] += v; - } - else if( ki == condensedFaceMarker && kj == pressureMarker ) - { - b0t[ri][rj] += v; - } - else if( ki == condensedFaceMarker && kj == condensedFaceMarker && i == j ) - { - data.invD0[ri] = v != 0.0 ? 1.0 / v : 0.0; - } - } - } - - // 2) a live flux dof the divergence never touches (no-flow face) or whose row couples - // negligibly against its own diagonal is out of the space: solved by its diagonal, outside the block - HYPRE_Real bMaxAll = 0.0; - for( HYPRE_Int e = 0; e < np; ++e ) - { - for( auto const & [f, bf] : b1[e] ) - { - bMaxAll = std::max( bMaxAll, std::abs( bf ) ); - } - } - data.pinned.assign( nf1, 0 ); - data.invDiagPinned.assign( nf1, 0.0 ); - HYPRE_Int numFree = 0; - for( HYPRE_Int f = 0; f < nf1; ++f ) - { - HYPRE_Real colMax = 0.0; - for( auto const & [e, be] : b1Cols[f] ) - { - colMax = std::max( colMax, std::abs( be ) ); - } - HYPRE_Real offMax = colMax; - for( auto const & [g, v] : m1[f] ) - { - offMax = g != f ? std::max( offMax, std::abs( v ) ) : offMax; - } - auto const dIt = m1[f].find( f ); - HYPRE_Real const diagAbs = dIt != m1[f].end() ? std::abs( dIt->second ) : 0.0; - if( colMax <= 1e-12 * bMaxAll || offMax <= 1e-10 * diagAbs ) - { - data.pinned[f] = 1; - data.invDiagPinned[f] = diagAbs > 0.0 ? 1.0 / dIt->second : 1.0; - for( auto const & [g, v] : m1[f] ) - { - if( g != f ) - { - m1[g].erase( f ); - } - } - HYPRE_Real const diagVal = diagAbs > 0.0 ? dIt->second : 1.0; - m1[f].clear(); - m1[f][f] = diagVal; - b1Cols[f].clear(); - } - else - { - ++numFree; - } - } - for( HYPRE_Int e = 0; e < np; ++e ) - { - for( auto it = b1[e].begin(); it != b1[e].end(); ) - { - it = data.pinned[it->first] ? b1[e].erase( it ) : std::next( it ); - } - } - - // 3) the scale s_e of each conservation row is the magnitude of its incidence entries; rows - // without a live face take the median of the others (the scaling is per field) - stdVector< HYPRE_Real > s( np, 0.0 ), known; - for( HYPRE_Int e = 0; e < np; ++e ) - { - for( auto const & [f, bf] : b1[e] ) - { - s[e] = std::max( s[e], std::abs( bf ) ); - } - if( s[e] > 0.0 ) - { - known.push_back( s[e] ); - } - } - HYPRE_Real sMedian = gamma > 0.0 ? gamma : 1.0; - if( !known.empty() ) - { - std::nth_element( known.begin(), known.begin() + known.size() / 2, known.end() ); - sMedian = known[known.size() / 2]; - } - if( gamma == 0.0 ) - { - gamma = sMedian; - } - data.rowScale.assign( np, 0.0 ); - for( HYPRE_Int e = 0; e < np; ++e ) - { - data.rowScale[e] = gamma / ( s[e] > 0.0 ? s[e] : sMedian ); - } - - // 4) MFD pressures: W_M = (l_e/D)^2 gamma^2 sum_f 1/M_ff, the L2 mass of the cell in the units of - // the symmetrized operator, plus its accumulation - stdVector< HYPRE_Real > invDiagM1( nf1, 0.0 ); - for( HYPRE_Int f = 0; f < nf1; ++f ) - { - auto const it = m1[f].find( f ); - invDiagM1[f] = ( it != m1[f].end() && it->second != 0.0 ) ? 1.0 / it->second : 0.0; - } - // A cell belongs to the H1 block iff its two-point couplings connect it to a Dirichlet - // contribution: a TPFA-flagged cell whose faces are all live has no coupling, and an island of - // TPFA cells enclosed by MFD cells has a singular (pure Neumann) sub-block whose level is set by - // the interface fluxes; both live in the L2 block with the saddle point - stdVector< HYPRE_Int > root( np ); - std::iota( root.begin(), root.end(), 0 ); - auto const findRoot = [&]( HYPRE_Int e ) - { - while( root[e] != e ) - { - root[e] = root[root[e]]; - e = root[e]; - } - return e; - }; - for( HYPRE_Int e = 0; e < np; ++e ) - { - for( auto const & [g, v] : c[e] ) - { - if( g != e && v != 0.0 ) - { - root[findRoot( e )] = findRoot( g ); - } - } - } - std::map< HYPRE_Int, bool > componentGrounded; - for( HYPRE_Int e = 0; e < np; ++e ) - { - HYPRE_Real diagVal = 0.0, offSum = 0.0; - bool coupled = false; - for( auto const & [g, v] : c[e] ) - { - diagVal += g == e ? v : 0.0; - offSum += g != e ? std::abs( v ) : 0.0; - coupled = coupled || ( g != e && v != 0.0 ); - } - if( coupled ) - { - bool & grounded = componentGrounded[findRoot( e )]; - grounded = grounded || diagVal > offSum * ( 1.0 + 1e-10 ); - } - } - data.invWm.assign( np, 0.0 ); - stdVector< HYPRE_Real > massOf( np, 0.0 ); // W_M of the L2 cells - for( HYPRE_Int e = 0; e < np; ++e ) - { - auto const cIt = componentGrounded.find( findRoot( e ) ); - bool const h1 = cIt != componentGrounded.end() && cIt->second; - if( data.isMfd[e] || !h1 ) - { - data.isMfd[e] = 1; - HYPRE_Real w = 0.0; - for( auto const & [f, bf] : b1[e] ) - { - w += invDiagM1[f]; - } - w *= data.normScale[e] * gamma * gamma; - massOf[e] = w; - auto const it = c[e].find( e ); - HYPRE_Real const acc = it != c[e].end() ? data.rowScale[e] * it->second : 0.0; - data.invWm[e] = ( w + acc ) > 0.0 ? 1.0 / ( w + acc ) : 0.0; - } - else - { - data.isMfd[e] = 0; - } - } - - // 5) flux block N = M + gamma^2 B_M^T W_M^{-1} B_M, the divergence taken into MFD cells only - for( HYPRE_Int e = 0; e < np; ++e ) - { - if( !data.isMfd[e] || s[e] <= 0.0 ) - { - continue; - } - HYPRE_Real const wInv = gamma * gamma * data.invWm[e] / ( s[e] * s[e] ); - for( auto const & [f, bf] : b1[e] ) - { - for( auto const & [g, bg] : b1[e] ) - { - m1[f][g] += bf * bg * wInv; - } - } - } - - // 6) pressure block, one sparse SPD matrix over all cells: the two-point couplings wherever a - // face was condensed (R^{1/2} C R^{1/2}, accumulation included), the L2 mass W_M of the L2 cells, - // and the two-point energy gamma^2 / M_ff of each interface face, which weighs the mean pressure - // of a patch like its neighbours instead of by its (h^2 smaller) L2 mass - stdVector< RowMap > pp( np ); - for( HYPRE_Int e = 0; e < np; ++e ) - { - for( auto const & [g, v] : c[e] ) - { - pp[e][g] += std::sqrt( data.rowScale[e] * data.rowScale[g] ) * v; - } - pp[e][e] += massOf[e]; - } - for( HYPRE_Int f = 0; f < nf1; ++f ) - { - if( b1Cols[f].size() != 2 ) - { - continue; - } - HYPRE_Int const e = b1Cols[f][0].first; - HYPRE_Int const g = b1Cols[f][1].first; - if( data.isMfd[e] == data.isMfd[g] ) - { - continue; - } - HYPRE_Real const tau = gamma * gamma * invDiagM1[f]; - pp[e][e] += tau; - pp[g][g] += tau; - pp[e][g] -= tau; - pp[g][e] -= tau; - } - - data.nIJ = createIJFromRowMaps( m1, nf1 ); - data.pIJ = createIJFromRowMaps( pp, np ); - data.b0tIJ = createIJFromRowMaps( b0t, np ); - data.r1 = createIJVector( nf1, nullptr ); - data.x1 = createIJVector( nf1, nullptr ); - data.rt = createIJVector( np, nullptr ); - data.xt = createIJVector( np, nullptr ); - data.xp = createIJVector( np, nullptr ); - data.t0 = createIJVector( nf0, nullptr ); - - // 7) CG under one ADS cycle on the flux block (the reference's ads-cg) - data.adsActive = numFree > 0; - if( data.adsActive ) - { - GEOS_LAI_CHECK_ERROR( HYPRE_ADSCreate( &data.ads ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetDiscreteCurl( data.ads, reinterpret_cast< HYPRE_ParCSRMatrix >( parCSROf( data.curlIJ ) ) ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetDiscreteGradient( data.ads, reinterpret_cast< HYPRE_ParCSRMatrix >( parCSROf( data.gradIJ ) ) ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetCoordinateVectors( data.ads, - reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.coordIJ[0] ) ), - reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.coordIJ[1] ) ), - reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.coordIJ[2] ) ) ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetCycleType( data.ads, 13 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetMaxIter( data.ads, 1 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetTol( data.ads, 0.0 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetPrintLevel( data.ads, 0 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetAMGOptions( data.ads, 10, 1, 3, 0.25, 0, 0 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetAMSOptions( data.ads, 11, 10, 1, 3, 0.25, 0, 0 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_ADSSetup( data.ads, - reinterpret_cast< HYPRE_ParCSRMatrix >( parCSROf( data.nIJ ) ), - reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.r1 ) ), - reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.x1 ) ) ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_ParCSRPCGCreate( MPI_COMM_WORLD, &data.pcg ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_PCGSetTol( data.pcg, 1e-2 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_PCGSetMaxIter( data.pcg, 50 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_PCGSetTwoNorm( data.pcg, 1 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_PCGSetPrintLevel( data.pcg, 0 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_PCGSetLogging( data.pcg, 0 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_ParCSRPCGSetPrecond( data.pcg, - reinterpret_cast< HYPRE_PtrToParSolverFcn >( HYPRE_ADSSolve ), - reinterpret_cast< HYPRE_PtrToParSolverFcn >( noSetup ), - data.ads ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_ParCSRPCGSetup( data.pcg, - reinterpret_cast< HYPRE_ParCSRMatrix >( parCSROf( data.nIJ ) ), - reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.r1 ) ), - reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.x1 ) ) ) ); - } - - // 8) one BoomerAMG cycle on the pressure block - if( np > 0 ) - { - GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGCreate( &data.amg ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetMaxIter( data.amg, 1 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetTol( data.amg, 0.0 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetPrintLevel( data.amg, 0 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetCoarsenType( data.amg, 8 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetInterpType( data.amg, 6 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetAggNumLevels( data.amg, 1 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetRelaxType( data.amg, 8 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetNumSweeps( data.amg, 1 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetStrongThreshold( data.amg, 0.25 ) ); - GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSetup( data.amg, - reinterpret_cast< HYPRE_ParCSRMatrix >( parCSROf( data.pIJ ) ), - reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.rt ) ), - reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.xt ) ) ) ); - } - - hypre_SolverSetIsSetup( &data.base ); - return 0; -} - -HYPRE_Int rieszSolve( HYPRE_Solver solver, - HYPRE_ParCSRMatrix, - HYPRE_ParVector f, - HYPRE_ParVector u ) -{ - RieszMFDData & data = *reinterpret_cast< RieszMFDData * >( solver ); - HYPRE_Real const * const r = hypre_VectorData( hypre_ParVectorLocalVector( reinterpret_cast< hypre_ParVector * >( f ) ) ); - HYPRE_Real * const x = hypre_VectorData( hypre_ParVectorLocalVector( reinterpret_cast< hypre_ParVector * >( u ) ) ); - - HYPRE_Int const nf0 = LvArray::integerConversion< HYPRE_Int >( data.dofOfFlux0.size() ); - HYPRE_Int const nf1 = LvArray::integerConversion< HYPRE_Int >( data.dofOfFlux1.size() ); - HYPRE_Int const np = LvArray::integerConversion< HYPRE_Int >( data.dofOfPres.size() ); - - // pressures: the conservation residual taken to the symmetrized system, one AMG cycle on the block - HYPRE_Real * const xp = dataOf( data.xp ); - HYPRE_Real * const rt = dataOf( data.rt ); - HYPRE_Real * const xt = dataOf( data.xt ); - for( HYPRE_Int e = 0; e < np; ++e ) - { - rt[e] = data.rowScale[e] * r[data.dofOfPres[e]]; - xt[e] = 0.0; - } - if( np > 0 ) - { - GEOS_LAI_CHECK_ERROR( HYPRE_BoomerAMGSolve( data.amg, - reinterpret_cast< HYPRE_ParCSRMatrix >( parCSROf( data.pIJ ) ), - reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.rt ) ), - reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.xt ) ) ) ); - } - for( HYPRE_Int e = 0; e < np; ++e ) - { - xp[e] = xt[e]; - x[data.dofOfPres[e]] = xp[e]; - } - - // live fluxes: the flux block alone (block-diagonal map), pinned dofs by their diagonal - if( nf1 > 0 ) - { - HYPRE_Real * const r1 = dataOf( data.r1 ); - HYPRE_Real * const x1 = dataOf( data.x1 ); - for( HYPRE_Int i = 0; i < nf1; ++i ) - { - r1[i] = r[data.dofOfFlux1[i]]; - x1[i] = 0.0; - } - if( data.adsActive ) - { - // a CG that stops on its iteration cap reports a convergence error: not fatal here - HYPRE_ParCSRPCGSolve( data.pcg, - reinterpret_cast< HYPRE_ParCSRMatrix >( parCSROf( data.nIJ ) ), - reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.r1 ) ), - reinterpret_cast< HYPRE_ParVector >( parVectorOf( data.x1 ) ) ); - HYPRE_ClearAllErrors(); - } - for( HYPRE_Int i = 0; i < nf1; ++i ) - { - x[data.dofOfFlux1[i]] = data.pinned[i] ? data.invDiagPinned[i] * r1[i] : x1[i]; - } - } - - // condensed fluxes: exact closure q0 = D0^{-1} ( r0 - F0 p ) - if( nf0 > 0 ) - { - GEOS_LAI_CHECK_ERROR( hypre_ParCSRMatrixMatvec( 1.0, parCSROf( data.b0tIJ ), parVectorOf( data.xp ), - 0.0, parVectorOf( data.t0 ) ) ); - HYPRE_Real const * const t0 = dataOf( data.t0 ); - for( HYPRE_Int k = 0; k < nf0; ++k ) - { - HYPRE_Int const dof = data.dofOfFlux0[k]; - x[dof] = data.invD0[k] * ( r[dof] - t0[k] ); - } - } - return 0; -} - -HYPRE_Int rieszDestroy( HYPRE_Solver solver ) -{ - RieszMFDData * const data = reinterpret_cast< RieszMFDData * >( solver ); - destroySetupObjects( *data ); - destroyIJ( data->curlIJ ); - destroyIJ( data->gradIJ ); - for( int d = 0; d < 3; ++d ) - { - destroyIJ( data->coordIJ[d] ); - } - delete data; - return 0; -} - -} // namespace - -void createRieszMFD( LinearSolverParameters const & params, - HyprePrecWrapper & precond ) -{ - GEOS_ERROR_IF( MpiWrapper::commSize( MPI_COMM_WORLD ) > 1, - "Riesz MFD preconditioner: only serial runs are supported at the moment" ); - GEOS_ERROR_IF( params.mgr.customPointMarkers.empty(), - "Riesz MFD preconditioner: the solver must provide the dof markers (0 = condensed face, 1 = live face, 2 = pressure)" ); - - auto * const data = new RieszMFDData{}; - localIndex const n = params.mgr.customPointMarkers.size(); - data->kindOf.resize( n ); - data->blockRow.resize( n ); - for( localIndex i = 0; i < n; ++i ) - { - HYPRE_Int const kind = LvArray::integerConversion< HYPRE_Int >( params.mgr.customPointMarkers[i] ); - data->kindOf[i] = kind; - stdVector< HYPRE_Int > & block = kind == condensedFaceMarker ? data->dofOfFlux0 : - kind == liveFaceMarker ? data->dofOfFlux1 : data->dofOfPres; - GEOS_ERROR_IF( kind != condensedFaceMarker && kind != liveFaceMarker && kind != pressureMarker, - GEOS_FMT( "Riesz MFD preconditioner: unknown dof marker {}", kind ) ); - data->blockRow[i] = LvArray::integerConversion< HYPRE_Int >( block.size() ); - block.push_back( LvArray::integerConversion< HYPRE_Int >( i ) ); - } - - LinearSolverParameters::ADSAuxData const & aux = params.adsAuxData; - GEOS_ERROR_IF( aux.mfdCell.size() != n || aux.pressureNormScale.size() != n, - "Riesz MFD preconditioner: the solver must provide the MFD flag and the L2 scale of every pressure dof" ); - data->isMfd.resize( data->dofOfPres.size() ); - data->normScale.resize( data->dofOfPres.size() ); - for( std::size_t e = 0; e < data->dofOfPres.size(); ++e ) - { - data->isMfd[e] = aux.mfdCell[data->dofOfPres[e]]; - data->normScale[e] = aux.pressureNormScale[data->dofOfPres[e]]; - } - globalIndex const numFluxRows = aux.cRowPtr.size() - 1; - GEOS_ERROR_IF_NE_MSG( numFluxRows, LvArray::integerConversion< globalIndex >( data->dofOfFlux1.size() ), - "Riesz MFD preconditioner: the discrete curl's rows are not the live flux dofs" ); - if( numFluxRows > 0 ) - { - globalIndex const numEdges = aux.gRowPtr.size() - 1; - globalIndex const numVertices = aux.xCoords.size(); - data->curlIJ = createIJFromCSR( aux.cRowPtr, aux.cCols, aux.cVals, numEdges ); - data->gradIJ = createIJFromCSR( aux.gRowPtr, aux.gCols, aux.gVals, numVertices ); - data->coordIJ[0] = createShiftedCoordinates( aux.xCoords ); - data->coordIJ[1] = createShiftedCoordinates( aux.yCoords ); - data->coordIJ[2] = createShiftedCoordinates( aux.zCoords ); - } - - data->base.setup = reinterpret_cast< HYPRE_PtrToSolverFcn >( rieszSetup ); - data->base.solve = reinterpret_cast< HYPRE_PtrToSolverFcn >( rieszSolve ); - data->base.destroy = reinterpret_cast< HYPRE_PtrToDestroyFcn >( rieszDestroy ); - data->base.is_setup = 0; - - precond.ptr = reinterpret_cast< HYPRE_Solver >( data ); - precond.setup = rieszSetup; - precond.solve = rieszSolve; - precond.destroy = rieszDestroy; -} - -} // namespace hypre - -} // namespace geos diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreRieszMFD.hpp b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreRieszMFD.hpp deleted file mode 100644 index a6f41fe1553..00000000000 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreRieszMFD.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * ------------------------------------------------------------------------------------------------------------ - * SPDX-License-Identifier: LGPL-2.1-only - * - * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC - * Copyright (c) 2018-2024 TotalEnergies - * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University - * Copyright (c) 2023-2024 Chevron - * Copyright (c) 2019- GEOS/GEOSX Contributors - * All rights reserved - * - * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. - * ------------------------------------------------------------------------------------------------------------ - */ - -/** - * @file HypreRieszMFD.hpp - */ - -#ifndef GEOS_LINEARALGEBRA_INTERFACES_HYPRERIESZMFD_HPP_ -#define GEOS_LINEARALGEBRA_INTERFACES_HYPRERIESZMFD_HPP_ - -#include "linearAlgebra/interfaces/hypre/HypreUtils.hpp" -#include "linearAlgebra/utilities/LinearSolverParameters.hpp" - -namespace geos -{ - -namespace hypre -{ - -/** - * @brief Create the Riesz-map block preconditioner for the adaptive mixed MFD saddle point. - * - * The unknowns are the live fluxes q (faces of MFD cells), the pressures p_M of the MFD cells and - * the pressures p_T of the TPFA cells; the condensed fluxes are recovered from their closure rows. - * The preconditioner is the Riesz map of the product norm H(div; MFD region) x L2(MFD cells) x - * H1(TPFA cells), - * - * P = diag( M + B_M^T W_M^{-1} B_M , W_M , C_T + S_TT ), - * - * with B_M the face-to-cell incidence of the live faces into the MFD cells, W_M the L2 mass of the - * MFD cells (plus accumulation) and S_TT the two-point Laplacian of the TPFA cells. The flux block - * is solved by CG under one ADS cycle, the TPFA block by one BoomerAMG cycle. The interface faces - * carry no weight: they are bounded by the normal-trace inequality. All blocks are read off the - * assembled system, whose row scalings are identified and removed; the solver provides the de Rham - * sub-complex of the live faces, the dof markers, the MFD flag and the L2 scale of each cell. - * - * @param params the linear solver parameters (dof markers and auxiliary discretization data) - * @param precond the output preconditioner wrapper - */ -void createRieszMFD( LinearSolverParameters const & params, - HyprePrecWrapper & precond ); - -} // namespace hypre - -} // namespace geos - -#endif /*GEOS_LINEARALGEBRA_INTERFACES_HYPRERIESZMFD_HPP_*/ diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp b/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp index caabd47faf0..ee616cf26da 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp @@ -283,7 +283,6 @@ bool supportsGeneratedPreconditioner( LinearSolverParameters::PreconditionerType case PreconditionerType::direct: case PreconditionerType::bgs: case PreconditionerType::multiscale: - case PreconditionerType::riesz: return false; } diff --git a/src/coreComponents/linearAlgebra/unitTests/testLinearSolverParametersEnums.cpp b/src/coreComponents/linearAlgebra/unitTests/testLinearSolverParametersEnums.cpp index 34b6aa3e75b..d9404b5e7b4 100644 --- a/src/coreComponents/linearAlgebra/unitTests/testLinearSolverParametersEnums.cpp +++ b/src/coreComponents/linearAlgebra/unitTests/testLinearSolverParametersEnums.cpp @@ -56,7 +56,6 @@ TEST( LinearSolverParametersEnums, PreconditionerType ) ASSERT_EQ( "ict", toString( EnumType::ict ) ); ASSERT_EQ( "amg", toString( EnumType::amg ) ); ASSERT_EQ( "mgr", toString( EnumType::mgr ) ); - ASSERT_EQ( "riesz", toString( EnumType::riesz ) ); ASSERT_EQ( "block", toString( EnumType::block ) ); ASSERT_EQ( "direct", toString( EnumType::direct ) ); ASSERT_EQ( "bgs", toString( EnumType::bgs ) ); diff --git a/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp b/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp index 0cccedca26a..4ab68c33926 100644 --- a/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp +++ b/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp @@ -69,8 +69,7 @@ struct LinearSolverParameters block, ///< Block preconditioner direct, ///< Direct solver as preconditioner bgs, ///< Gauss-Seidel smoothing (backward sweep) - multiscale, ///< Multiscale preconditioner - riesz ///< Riesz-map block preconditioner for the mixed MFD saddle point (Hypre only) + multiscale ///< Multiscale preconditioner }; integer logLevel = 0; ///< Output level [0=none, 1=basic, 2=everything] @@ -342,27 +341,6 @@ struct LinearSolverParameters } mgr; ///< Multigrid reduction (MGR) parameters - /** - * @brief Solver-provided de Rham sub-complex of the live mixed-MFD flux dofs, for the Riesz-map - * preconditioner (empty = unused). CSR over compact auxiliary numberings: flux rows follow the - * dof order of the live faces, edge and vertex columns the active entities of those faces. - */ - struct ADSAuxData - { - array1d< globalIndex > cRowPtr; ///< CSR row offsets of the discrete curl (live faces x active edges) - array1d< globalIndex > cCols; ///< CSR column indices of the discrete curl - array1d< real64 > cVals; ///< CSR values (+-1) of the discrete curl - array1d< globalIndex > gRowPtr; ///< CSR row offsets of the discrete gradient (active edges x active vertices) - array1d< globalIndex > gCols; ///< CSR column indices of the discrete gradient - array1d< real64 > gVals; ///< CSR values (+-1) of the discrete gradient - array1d< real64 > xCoords; ///< active vertex x coordinates - array1d< real64 > yCoords; ///< active vertex y coordinates - array1d< real64 > zCoords; ///< active vertex z coordinates - array1d< integer > mfdCell; ///< 1 at the pressure dof of an MFD cell, 0 elsewhere - array1d< real64 > pressureNormScale; ///< (l_e/D)^2 at each pressure dof, l_e^2 = |E|^2 / sum_f A_f^2 - } - adsAuxData; ///< live-face sub-complex for the Riesz-map preconditioner - /// Incomplete factorization parameters struct IFact { @@ -585,8 +563,7 @@ ENUM_STRINGS( LinearSolverParameters::PreconditionerType, "block", "direct", "bgs", - "multiscale", - "riesz" ); + "multiscale" ); /// Declare strings associated with enumeration values. ENUM_STRINGS( LinearSolverParameters::Direct::ColPerm, diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp index 275881a2912..cc3461af81d 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp @@ -35,8 +35,8 @@ using namespace mimeticInnerProduct; MixedMimeticDiscretization::MixedMimeticDiscretization( string const & name, Group * const parent ) : Group( name, parent ), - m_isAdaptive( 1 ), - m_residualTolerance( 1e-3 ), + m_adaptiveConsistency( 1 ), + m_consistencyTolerance( 1e-3 ), m_nominalGradient( { 1.0, 1.0, 1.0 } ), m_degeneracyTolerance( 0.1 ) { @@ -46,17 +46,17 @@ MixedMimeticDiscretization::MixedMimeticDiscretization( string const & name, setInputFlag( InputFlags::REQUIRED ). setDescription( "Type of inner product used in the MFD-compatible cells of the mixed mimetic solver" ); - registerWrapper( viewKeyStruct::adaptiveString(), &m_isAdaptive ). + registerWrapper( viewKeyStruct::adaptiveConsistencyString(), &m_adaptiveConsistency ). setInputFlag( InputFlags::OPTIONAL ). setApplyDefaultValue( 1 ). - setDescription( "Flag enabling the residual-based Global Adaptation: when enabled (1, default), the " + setDescription( "Flag enabling the consistency layer of the Global Adaptation: when enabled (1, default), the " "cell-wise inner product is selected between TPFA and innerProductType according to the " "consistency indicator; when disabled (0), innerProductType is used in every cell" ); - registerWrapper( viewKeyStruct::residualToleranceString(), &m_residualTolerance ). + registerWrapper( viewKeyStruct::consistencyToleranceString(), &m_consistencyTolerance ). setInputFlag( InputFlags::OPTIONAL ). setApplyDefaultValue( 1e-3 ). - setDescription( "Tolerance on the Global Adaptation residual indicator used in the cell marking criterion" ); + setDescription( "Tolerance on the consistency indicator of the Global Adaptation used in the cell marking criterion" ); registerWrapper( viewKeyStruct::nominalGradientString(), &m_nominalGradient ). setInputFlag( InputFlags::OPTIONAL ). @@ -75,9 +75,9 @@ void MixedMimeticDiscretization::postInputInitialization() { Group::postInputInitialization(); - GEOS_THROW_IF_LT_MSG( m_residualTolerance, 0.0, - GEOS_FMT( "{}: the residual tolerance cannot be negative", - getWrapperDataContext( viewKeyStruct::residualToleranceString() ) ), + GEOS_THROW_IF_LT_MSG( m_consistencyTolerance, 0.0, + GEOS_FMT( "{}: the consistency tolerance cannot be negative", + getWrapperDataContext( viewKeyStruct::consistencyToleranceString() ) ), InputError ); GEOS_THROW_IF_LT_MSG( m_degeneracyTolerance, 0.0, @@ -93,9 +93,9 @@ void MixedMimeticDiscretization::postInputInitialization() // degenerate combination: with a TPFA inner product, the adaptive blend mixes TPFA with TPFA // and the operator is TPFA everywhere regardless of the marking; the consistency indicator // fields are still computed and output, which makes this combination a useful diagnostic mode - if( isAdaptive() && m_innerProductType == mimeticInnerProduct::MimeticInnerProductTypeStrings::TPFA ) + if( isAdaptiveConsistency() && m_innerProductType == mimeticInnerProduct::MimeticInnerProductTypeStrings::TPFA ) { - GEOS_WARNING( GEOS_FMT( "{}: 'adaptive' is enabled but 'innerProductType' is TPFA: the adaptation has no " + GEOS_WARNING( GEOS_FMT( "{}: 'adaptiveConsistency' is enabled but 'innerProductType' is TPFA: the adaptation has no " "effect on the discretization (both operators of the adaptive blend coincide), and " "the scheme reduces to full TPFA. The consistency indicators are still computed and " "output. Select an MFD inner product (e.g. quasiTPFA) to activate the adaptation.", diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp index 50ee8a01c42..ecac372b5cb 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp @@ -74,11 +74,11 @@ class MixedMimeticDiscretization : public dataRepository::Group /// @return The key for the inner product static constexpr char const * innerProductString() { return "innerProduct"; } - /// @return The key for the adaptive flag - static constexpr char const * adaptiveString() { return "adaptive"; } + /// @return The key for the adaptive consistency flag + static constexpr char const * adaptiveConsistencyString() { return "adaptiveConsistency"; } - /// @return The key for the residual tolerance - static constexpr char const * residualToleranceString() { return "residualTolerance"; } + /// @return The key for the consistency tolerance + static constexpr char const * consistencyToleranceString() { return "consistencyTolerance"; } /// @return The key for the nominal gradient of the projection probe static constexpr char const * nominalGradientString() { return "nominalGradient"; } @@ -88,9 +88,9 @@ class MixedMimeticDiscretization : public dataRepository::Group }; /** - * @brief @return Whether the residual-based Global Adaptation is enabled + * @brief @return Whether the consistency layer of the Global Adaptation is enabled */ - bool isAdaptive() const { return m_isAdaptive == 1; } + bool isAdaptiveConsistency() const { return m_adaptiveConsistency == 1; } /** * @brief @return Whether the selected inner product is the (diagonal) TPFA inner product @@ -98,9 +98,9 @@ class MixedMimeticDiscretization : public dataRepository::Group bool isTpfaInnerProduct() const; /** - * @brief @return The residual tolerance used in the marking criterion + * @brief @return The consistency tolerance used in the marking criterion */ - real64 getResidualTolerance() const { return m_residualTolerance; } + real64 getConsistencyTolerance() const { return m_consistencyTolerance; } /** * @brief @return The nominal gradient used to build the projected admissible flow field @@ -123,11 +123,11 @@ class MixedMimeticDiscretization : public dataRepository::Group /// type of inner product used in the mixed mimetic solver string m_innerProductType; - /// flag enabling the residual-based Global Adaptation (1 = adaptive, 0 = selected inner product everywhere) - integer m_isAdaptive; + /// flag enabling the consistency layer of the Global Adaptation (1 = adaptive, 0 = selected inner product everywhere) + integer m_adaptiveConsistency; - /// user-prescribed tolerance for the marking criterion - real64 m_residualTolerance; + /// user-prescribed tolerance on the consistency indicator + real64 m_consistencyTolerance; /// nominal gradient inducing the projected admissible flow field R1Tensor m_nominalGradient; diff --git a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp index 6eff358de8c..3d468aee4b0 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp @@ -73,6 +73,22 @@ DECLARE_FIELD( degeneracyIndicator, WRITE_AND_READ, "Cell volume as a percentage of the total volume of its node star (admissibility of the stabilized reconstruction)" ); +DECLARE_FIELD( faceOrientationCell, + "faceOrientationCell", + array1d< globalIndex >, + -1, + NOPLOT, + NO_WRITE, + "Global index of the cell E_min whose outward normal orients the face flux: sigma_{E,f} = +1 if E = E_min, -1 otherwise" ); + +DECLARE_FIELD( faceDofScale, + "faceDofScale", + array1d< real64 >, + 0, + NOPLOT, + NO_WRITE, + "Characteristic scale of the face flux unknown, s_f = p_scale / |M_ff|, used by the residual-norm weights" ); + DECLARE_FIELD( faceStencilLabel, "faceStencilLabel", array1d< integer >, diff --git a/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp b/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp index e6e9ebf092f..fc0b318cf05 100644 --- a/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp +++ b/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp @@ -202,7 +202,6 @@ struct FaceLabelKernel SortedArrayView< localIndex const > const & regionFilter, ElementViewConst< arrayView1d< integer const > > const & stencilFlag, bool const effectiveTpfa, - bool const keepAllFacesLive, arrayView1d< integer > const & faceStencilLabel ) { forAll< POLICY >( numFaces, [=] GEOS_HOST_DEVICE ( localIndex const kf ) @@ -217,9 +216,7 @@ struct FaceLabelKernel localIndex const ei = elemList[kf][k]; if( er >= 0 && esr >= 0 && ei >= 0 && regionFilter.contains( er ) ) { - // a face is live next to an MFD cell, or everywhere when the saddle point is kept whole - // (the Riesz-map preconditioner needs it): a TPFA cell then only selects its diagonal star - label = keepAllFacesLive ? 1 : LvArray::math::max( label, stencilFlag[er][esr][ei] ); + label = LvArray::math::max( label, stencilFlag[er][esr][ei] ); } } } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index 2bd1f3bdf12..14b60b7257a 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -20,6 +20,7 @@ #include "SinglePhaseMixedMFD.hpp" #include +#include #include #include "common/logger/Logger.hpp" @@ -92,6 +93,8 @@ void SinglePhaseMixedMFD::registerDataOnMesh( Group & meshBodies ) // face classification driving the TPFA-face condensation and the MGR labels faceManager.registerField< mixedMimetic::faceStencilLabel >( getName() ); + faceManager.registerField< mixedMimetic::faceOrientationCell >( getName() ); + faceManager.registerField< mixedMimetic::faceDofScale >( getName() ); } } ); } @@ -166,10 +169,54 @@ void SinglePhaseMixedMFD::initializePostInitialConditionsPreSubGroups() } ); } ); + computeFaceOrientation( domain ); + // run the residual-based Global Adaptation pipeline (or activate the selected inner product everywhere) computeGlobalAdaptationIndicators( domain ); } +void SinglePhaseMixedMFD::computeFaceOrientation( DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & ) + { + FaceManager & faceManager = mesh.getFaceManager(); + ElementRegionManager const & elemManager = mesh.getElemManager(); + arrayView2d< localIndex const > const elemRegionList = faceManager.elementRegionList(); + arrayView2d< localIndex const > const elemSubRegionList = faceManager.elementSubRegionList(); + arrayView2d< localIndex const > const elemList = faceManager.elementList(); + ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > const elemLocalToGlobal = + elemManager.constructArrayViewAccessor< globalIndex, 1 >( ObjectManagerBase::viewKeyStruct::localToGlobalMapString() ); + ElementRegionManager::ElementViewConst< arrayView1d< globalIndex const > > const l2g = elemLocalToGlobal.toNestedViewConst(); + arrayView1d< globalIndex > const orientationCell = faceManager.getField< mixedMimetic::faceOrientationCell >(); + + // the owner of a face sees both of its cells; ghost faces take the owner's value through the sync + forAll< parallelHostPolicy >( faceManager.size(), [=]( localIndex const kf ) + { + globalIndex gMin = -1; + for( localIndex k = 0; k < 2; ++k ) + { + localIndex const er = elemRegionList[kf][k]; + localIndex const esr = elemSubRegionList[kf][k]; + localIndex const ei = elemList[kf][k]; + if( er >= 0 && esr >= 0 && ei >= 0 ) + { + globalIndex const g = l2g[er][esr][ei]; + gMin = ( gMin < 0 || g < gMin ) ? g : gMin; + } + } + orientationCell[kf] = gMin; + } ); + + FieldIdentifiers fieldsToBeSync; + fieldsToBeSync.addFields( FieldLocation::Face, { mixedMimetic::faceOrientationCell::key() } ); + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), false ); + } ); +} + void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & domain ) { GEOS_MARK_FUNCTION; @@ -178,7 +225,7 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d MixedMimeticDiscretizationManager const & mmManager = numericalMethodManager.getMixedMimeticDiscretizationManager(); MixedMimeticDiscretization const & discretization = mmManager.getMixedMimeticDiscretization( m_discretizationName ); - if( !discretization.isAdaptive() ) + if( !discretization.isAdaptiveConsistency() ) { // no adaptation: activate the selected inner product in every cell forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, @@ -192,18 +239,15 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d subRegion.getField< mixedMimetic::stencilFlag >().template setValues< parallelDevicePolicy<> >( 1 ); } ); } ); - globalIndex const numDegenerate = MpiWrapper::sum< globalIndex >( applyDegeneracyLayer( domain ) ); - GEOS_LOG_RANK_0( GEOS_FMT( "{}: degeneracy layer (tolerance = {}%) switched {} cells to the diagonal product", - getName(), discretization.getDegeneracyTolerance(), numDegenerate ) ); - computeFaceStencilLabels( domain, false ); + applyDegeneracyLayer( domain ); + computeFaceStencilLabels( domain ); return; } real64 const lengthTolerance = domain.getMeshBody( 0 ).getGlobalLengthScale() * m_areaRelTol; - real64 const tolerance = discretization.getResidualTolerance(); + real64 const tolerance = discretization.getConsistencyTolerance(); R1Tensor const gradientInput = discretization.getNominalGradient(); real64 const gradient[3] = { gradientInput[0], gradientInput[1], gradientInput[2] }; - localIndex numMfdCellsTotal = 0; forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, MeshLevel & mesh, @@ -299,23 +343,13 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d globalIndex const globalNumMfdCells = MpiWrapper::sum< globalIndex >( numMfdCells ); globalIndex const globalNumCells = MpiWrapper::sum< globalIndex >( numCells ); - GEOS_LOG_RANK_0( GEOS_FMT( "{}: Global Adaptation marked {} / {} cells as MFD-compatible (tolerance = {})", - getName(), globalNumMfdCells, globalNumCells, tolerance ) ); - numMfdCellsTotal += numMfdCells; + GEOS_LOG_RANK_0( GEOS_FMT( "mixedMFD Flow: consistency layer (tolerance = {}) set eta = 1 on {} / {} cells", + tolerance, globalNumMfdCells, globalNumCells ) ); } ); // second layer: the degenerate cells fall back to the diagonal product whatever the consistency says - localIndex const numDegenerate = applyDegeneracyLayer( domain ); - numMfdCellsTotal -= numDegenerate; - GEOS_LOG_RANK_0( GEOS_FMT( "{}: degeneracy layer (tolerance = {}%) switched {} more cells to the diagonal product", - getName(), discretization.getDegeneracyTolerance(), MpiWrapper::sum< globalIndex >( numDegenerate ) ) ); - - // the Riesz-map preconditioner is a map of the whole saddle point: as soon as one cell is MFD no - // face is condensed (eliminating faces removes the elliptic content of the div term from the map); - // with no MFD cell the condensed SPD system is solved as such - bool const riesz = m_linearSolverParameters.get().preconditionerType == LinearSolverParameters::PreconditionerType::riesz; - bool const keepAllFacesLive = riesz && MpiWrapper::sum< globalIndex >( numMfdCellsTotal ) > 0; - computeFaceStencilLabels( domain, keepAllFacesLive ); + applyDegeneracyLayer( domain ); + computeFaceStencilLabels( domain ); } localIndex SinglePhaseMixedMFD::applyDegeneracyLayer( DomainPartition & domain ) @@ -392,7 +426,7 @@ localIndex SinglePhaseMixedMFD::applyDegeneracyLayer( DomainPartition & domain ) return numDegenerate; } -void SinglePhaseMixedMFD::computeFaceStencilLabels( DomainPartition & domain, bool const keepAllFacesLive ) +void SinglePhaseMixedMFD::computeFaceStencilLabels( DomainPartition & domain ) { GEOS_MARK_FUNCTION; @@ -422,7 +456,6 @@ void SinglePhaseMixedMFD::computeFaceStencilLabels( DomainPartition & domain, bo m_regionFilter.toViewConst(), stencilFlagAccessor.toNestedViewConst(), effectiveTpfa, - keepAllFacesLive, faceManager.getField< mixedMimetic::faceStencilLabel >() ); } ); } @@ -521,14 +554,8 @@ void SinglePhaseMixedMFD::setupSystem( DomainPartition & domain, { SinglePhaseBase::setupSystem( domain, dofManager, localMatrix, rhs, solution, setSparsity ); - // with the dof numbering finalized, build the per-dof labels driving the - // stencilFlag-guided three-level MGR reduction + // with the dof numbering finalized, build the per-dof labels of the MGR reduction computeMgrPointMarkers( domain, dofManager ); - - if( m_linearSolverParameters.get().preconditionerType == LinearSolverParameters::PreconditionerType::riesz ) - { - computeADSAuxData( domain, dofManager ); - } } void SinglePhaseMixedMFD::computeMgrPointMarkers( DomainPartition const & domain, @@ -544,9 +571,9 @@ void SinglePhaseMixedMFD::computeMgrPointMarkers( DomainPartition const & domain pointMarkers.resize( dofManager.numLocalDofs() ); arrayView1d< integer > const markers = pointMarkers.toView(); - // an empty intermediate level is not supported by hypre MGR: when the marking produces - // no live MFD faces, relabel to two blocks and use the two-level condensed strategy + // flux dofs kept in the saddle point (label 1) and condensed into the pressure system (label 0) localIndex numLiveFaces = 0; + localIndex numCondensedFaces = 0; forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, MeshLevel const & mesh, string_array const & ) @@ -558,14 +585,20 @@ void SinglePhaseMixedMFD::computeMgrPointMarkers( DomainPartition const & domain arrayView1d< integer const > const faceStencilLabel = faceManager.getField< mixedMimetic::faceStencilLabel >(); RAJA::ReduceSum< parallelHostReduce, localIndex > numLive( 0 ); + RAJA::ReduceSum< parallelHostReduce, localIndex > numCondensed( 0 ); forAll< parallelHostPolicy >( faceManager.size(), [=]( localIndex const kf ) { - numLive += ( faceGhostRank[kf] < 0 && faceDofNumber[kf] >= 0 && faceStencilLabel[kf] == 1 ) ? 1 : 0; + bool const owned = faceGhostRank[kf] < 0 && faceDofNumber[kf] >= 0; + numLive += ( owned && faceStencilLabel[kf] == 1 ) ? 1 : 0; + numCondensed += ( owned && faceStencilLabel[kf] == 0 ) ? 1 : 0; } ); numLiveFaces += numLive.get(); + numCondensedFaces += numCondensed.get(); } ); globalIndex const globalNumLiveFaces = MpiWrapper::sum< globalIndex >( numLiveFaces ); - GEOS_LOG_RANK_0( GEOS_FMT( "{}: {} live MFD face dofs", getName(), globalNumLiveFaces ) ); + globalIndex const globalNumCondensedFaces = MpiWrapper::sum< globalIndex >( numCondensedFaces ); + GEOS_LOG_RANK_0( GEOS_FMT( "mixedMFD Flow: flux dofs {} non-condensed (saddle point), {} condensed (two-point closure)", + globalNumLiveFaces, globalNumCondensedFaces ) ); forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, MeshLevel const & mesh, @@ -609,266 +642,6 @@ void SinglePhaseMixedMFD::computeMgrPointMarkers( DomainPartition const & domain } ); } -void SinglePhaseMixedMFD::computeADSAuxData( DomainPartition const & domain, - DofManager const & dofManager ) -{ - GEOS_MARK_FUNCTION; - - GEOS_ERROR_IF( MpiWrapper::commSize() > 1, - GEOS_FMT( "{}: the Riesz-map preconditioner currently supports serial runs only", getName() ) ); - - LinearSolverParameters::ADSAuxData & aux = m_linearSolverParameters.get().adsAuxData; - string const faceDofKey = dofManager.getKey( mixedMimetic::faceMassFlux::key() ); - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel const & mesh, - string_array const & ) - { - NodeManager const & nodeManager = mesh.getNodeManager(); - EdgeManager const & edgeManager = mesh.getEdgeManager(); - FaceManager const & faceManager = mesh.getFaceManager(); - - localIndex const numNodes = nodeManager.size(); - localIndex const numEdges = edgeManager.size(); - localIndex const numFaces = faceManager.size(); - - arrayView1d< globalIndex const > const faceDofNumber = - faceManager.getReference< array1d< globalIndex > >( faceDofKey ); - arrayView1d< integer const > const faceStencilLabel = faceManager.getField< mixedMimetic::faceStencilLabel >(); - - // flux rows: live MFD faces in ascending dof order, matching the F-point order of the ADS level - stdVector< std::pair< globalIndex, localIndex > > liveFaces; - for( localIndex kf = 0; kf < numFaces; ++kf ) - { - if( faceDofNumber[kf] >= 0 && faceStencilLabel[kf] == 1 ) - { - liveFaces.emplace_back( faceDofNumber[kf], kf ); - } - } - std::sort( liveFaces.begin(), liveFaces.end() ); - localIndex const numFluxRows = LvArray::integerConversion< localIndex >( liveFaces.size() ); - - array1d< localIndex > faceToRow( numFaces ); - faceToRow.setValues< serialPolicy >( -1 ); - for( localIndex r = 0; r < numFluxRows; ++r ) - { - faceToRow[liveFaces[r].second] = r; - } - - // ---- the de Rham sub-complex of the MFD region: only the edges of the live faces and the - // vertices of those edges enter the auxiliary spaces, compactly renumbered, so ADS sees the - // complex of exactly the block it is handed (the condensed TPFA faces are its boundary) - ArrayOfArraysView< localIndex const > const faceToNodes = faceManager.nodeList().toViewConst(); - ArrayOfArraysView< localIndex const > const faceToEdges = faceManager.edgeList().toViewConst(); - arrayView2d< localIndex const > const edgeToNodes = edgeManager.nodeList(); - - array1d< localIndex > edgeToAux( numEdges ); - edgeToAux.setValues< serialPolicy >( -1 ); - for( localIndex r = 0; r < numFluxRows; ++r ) - { - localIndex const kf = liveFaces[r].second; - for( localIndex j = 0; j < faceToEdges.sizeOfArray( kf ); ++j ) - { - edgeToAux[faceToEdges( kf, j )] = 0; - } - } - localIndex numActiveEdges = 0; - for( localIndex e = 0; e < numEdges; ++e ) - { - if( edgeToAux[e] == 0 ) - { - edgeToAux[e] = numActiveEdges++; - } - } - - array1d< localIndex > nodeToAux( numNodes ); - nodeToAux.setValues< serialPolicy >( -1 ); - for( localIndex e = 0; e < numEdges; ++e ) - { - if( edgeToAux[e] >= 0 ) - { - nodeToAux[edgeToNodes( e, 0 )] = 0; - nodeToAux[edgeToNodes( e, 1 )] = 0; - } - } - localIndex numActiveNodes = 0; - for( localIndex n = 0; n < numNodes; ++n ) - { - if( nodeToAux[n] == 0 ) - { - nodeToAux[n] = numActiveNodes++; - } - } - - // ---- discrete curl: signed edges of the boundary loop of each live face. The flux dof - // follows the normal pointing out of the adjacent element with the smaller global index - // (the assembly kernel's convention), so the loop of faceToNodes is flipped when its - // right-hand normal points into that element - arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const nodePosition = nodeManager.referencePosition(); - arrayView2d< localIndex const > const fToElemRegion = faceManager.elementRegionList(); - arrayView2d< localIndex const > const fToElemSubRegion = faceManager.elementSubRegionList(); - arrayView2d< localIndex const > const fToElem = faceManager.elementList(); - ElementRegionManager const & elemManager = mesh.getElemManager(); - ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > const elemLocalToGlobal = - elemManager.constructArrayViewAccessor< globalIndex, 1 >( ObjectManagerBase::viewKeyStruct::localToGlobalMapString() ); - ElementRegionManager::ElementViewAccessor< arrayView2d< real64 const > > const elemCenter = - elemManager.constructArrayViewAccessor< real64, 2 >( ElementSubRegionBase::viewKeyStruct::elementCenterString() ); - real64 const areaTolerance = LvArray::math::square( domain.getMeshBody( 0 ).getGlobalLengthScale() * m_areaRelTol ); - - aux.cRowPtr.resize( numFluxRows + 1 ); - stdVector< globalIndex > cCols; - stdVector< real64 > cVals; - aux.cRowPtr[0] = 0; - for( localIndex r = 0; r < numFluxRows; ++r ) - { - localIndex const kf = liveFaces[r].second; - - // the element the dof normal points out of: smallest global index among the neighbours - globalIndex gMin = -1; - real64 refCenter[3]{}; - for( localIndex k = 0; k < 2; ++k ) - { - localIndex const er = fToElemRegion( kf, k ); - localIndex const esr = fToElemSubRegion( kf, k ); - localIndex const ei = fToElem( kf, k ); - if( er < 0 || esr < 0 || ei < 0 ) - { - continue; - } - globalIndex const g = elemLocalToGlobal[er][esr][ei]; - if( gMin < 0 || g < gMin ) - { - gMin = g; - LvArray::tensorOps::copy< 3 >( refCenter, elemCenter[er][esr][ei] ); - } - } - - real64 faceCenter[3], loopNormal[3]; - computationalGeometry::centroid_3DPolygon( faceToNodes[kf], nodePosition, faceCenter, loopNormal, areaTolerance ); - LvArray::tensorOps::subtract< 3 >( faceCenter, refCenter ); - real64 const loopSign = LvArray::tensorOps::AiBi< 3 >( faceCenter, loopNormal ) < 0.0 ? -1.0 : 1.0; - - localIndex const numFaceNodes = faceToNodes.sizeOfArray( kf ); - for( localIndex i = 0; i < numFaceNodes; ++i ) - { - localIndex const a = faceToNodes( kf, i ); - localIndex const b = faceToNodes( kf, ( i + 1 ) % numFaceNodes ); - for( localIndex j = 0; j < faceToEdges.sizeOfArray( kf ); ++j ) - { - localIndex const e = faceToEdges( kf, j ); - localIndex const n0 = edgeToNodes( e, 0 ); - localIndex const n1 = edgeToNodes( e, 1 ); - if( ( n0 == a && n1 == b ) || ( n0 == b && n1 == a ) ) - { - cCols.push_back( edgeToAux[e] ); - cVals.push_back( loopSign * ( n0 == a ? 1.0 : -1.0 ) ); - break; - } - } - } - aux.cRowPtr[r + 1] = LvArray::integerConversion< globalIndex >( cCols.size() ); - } - aux.cCols.resize( cCols.size() ); - aux.cVals.resize( cVals.size() ); - std::copy( cCols.begin(), cCols.end(), aux.cCols.begin() ); - std::copy( cVals.begin(), cVals.end(), aux.cVals.begin() ); - - // ---- discrete gradient: signed vertex-edge incidence over the active edges - aux.gRowPtr.resize( numActiveEdges + 1 ); - aux.gCols.resize( 2 * numActiveEdges ); - aux.gVals.resize( 2 * numActiveEdges ); - aux.gRowPtr[0] = 0; - for( localIndex e = 0; e < numEdges; ++e ) - { - localIndex const ea = edgeToAux[e]; - if( ea < 0 ) - { - continue; - } - aux.gCols[2 * ea] = nodeToAux[edgeToNodes( e, 0 )]; - aux.gVals[2 * ea] = -1.0; - aux.gCols[2 * ea + 1] = nodeToAux[edgeToNodes( e, 1 )]; - aux.gVals[2 * ea + 1] = 1.0; - aux.gRowPtr[ea + 1] = 2 * ( ea + 1 ); - } - - // ---- coordinates of the active vertices - aux.xCoords.resize( numActiveNodes ); - aux.yCoords.resize( numActiveNodes ); - aux.zCoords.resize( numActiveNodes ); - for( localIndex n = 0; n < numNodes; ++n ) - { - localIndex const na = nodeToAux[n]; - if( na >= 0 ) - { - aux.xCoords[na] = nodePosition( n, 0 ); - aux.yCoords[na] = nodePosition( n, 1 ); - aux.zCoords[na] = nodePosition( n, 2 ); - } - } - - // ---- per pressure dof: the cell's stencil flag and the geometric factor (l_e/D)^2 of its L2 - // weight, l_e^2 = |E|^2 / sum_f A_f^2 and D the bounding-box diagonal of the mesh - { - string const elemDofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); - globalIndex const rankOffset = dofManager.rankOffset(); - aux.mfdCell.resize( dofManager.numLocalDofs() ); - aux.mfdCell.zero(); - aux.pressureNormScale.resize( dofManager.numLocalDofs() ); - aux.pressureNormScale.zero(); - - real64 lo[3] = { LvArray::NumericLimits< real64 >::max, LvArray::NumericLimits< real64 >::max, LvArray::NumericLimits< real64 >::max }; - real64 hi[3] = { -LvArray::NumericLimits< real64 >::max, -LvArray::NumericLimits< real64 >::max, -LvArray::NumericLimits< real64 >::max }; - for( localIndex n = 0; n < numNodes; ++n ) - { - for( int d = 0; d < 3; ++d ) - { - lo[d] = LvArray::math::min( lo[d], nodePosition( n, d ) ); - hi[d] = LvArray::math::max( hi[d], nodePosition( n, d ) ); - } - } - real64 diag2 = 0.0; - for( int d = 0; d < 3; ++d ) - { - diag2 += LvArray::math::square( MpiWrapper::max( hi[d] ) - MpiWrapper::min( lo[d] ) ); - } - - arrayView1d< real64 const > const faceArea = faceManager.faceArea(); - elemManager.forElementSubRegions< CellElementSubRegion >( [&]( CellElementSubRegion const & subRegion ) - { - if( !subRegion.hasWrapper( elemDofKey ) ) - { - return; - } - arrayView1d< globalIndex const > const elemDofNumber = subRegion.getReference< array1d< globalIndex > >( elemDofKey ); - arrayView1d< real64 const > const elemVolume = subRegion.getElementVolume(); - arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); - arrayView1d< integer const > const stencilFlag = subRegion.getField< mixedMimetic::stencilFlag >(); - arrayView2d< localIndex const > const elemsToFaces = subRegion.faceList().toViewConst(); - for( localIndex ei = 0; ei < subRegion.size(); ++ei ) - { - if( elemGhostRank[ei] >= 0 || elemDofNumber[ei] < 0 ) - { - continue; - } - real64 sumArea2 = 0.0; - for( localIndex j = 0; j < elemsToFaces.size( 1 ); ++j ) - { - localIndex const kf = elemsToFaces( ei, j ); - sumArea2 += kf >= 0 ? LvArray::math::square( faceArea[kf] ) : 0.0; - } - localIndex const row = elemDofNumber[ei] - rankOffset; - aux.mfdCell[row] = stencilFlag[ei] != 0 ? 1 : 0; - aux.pressureNormScale[row] = sumArea2 > 0.0 ? LvArray::math::square( elemVolume[ei] ) / sumArea2 / diag2 : 0.0; - } - } ); - } - - GEOS_LOG_RANK_0( GEOS_FMT( "{}: Riesz-map sub-complex built: {} live flux rows, {} active edges (of {}), {} active vertices (of {})", - getName(), numFluxRows, numActiveEdges, numEdges, numActiveNodes, numNodes ) ); - } ); -} - void SinglePhaseMixedMFD::setupDofs( DomainPartition const & GEOS_UNUSED_PARAM( domain ), DofManager & dofManager ) const { @@ -946,7 +719,6 @@ void SinglePhaseMixedMFD::assembleFluxTerms( real64 const dt, faceDofKey, nodeManager, faceManager, - mesh.getElemManager(), subRegion, mimeticInnerProductBase, fluid, @@ -1032,11 +804,11 @@ void SinglePhaseMixedMFD::applyBoundaryConditions( real64 const time_n, string const faceDofKey = dofManager.getKey( mixedMimetic::faceMassFlux::key() ); forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, - MeshLevel const & mesh, + MeshLevel & mesh, string_array const & regionNames ) { - ElementRegionManager const & elemManager = mesh.getElemManager(); - FaceManager const & faceManager = mesh.getFaceManager(); + ElementRegionManager & elemManager = mesh.getElemManager(); + FaceManager & faceManager = mesh.getFaceManager(); // s_p = |p_n| for the cell-pressure dofs elemManager.forElementSubRegions< ElementSubRegionBase >( regionNames, @@ -1069,6 +841,7 @@ void SinglePhaseMixedMFD::applyBoundaryConditions( real64 const time_n, arrayView2d< localIndex const > const elemSubRegionList = faceManager.elementSubRegionList(); arrayView2d< localIndex const > const elemList = faceManager.elementList(); SortedArrayView< localIndex const > const regionFilter = m_regionFilter.toViewConst(); + arrayView1d< real64 > const faceScale = faceManager.getField< mixedMimetic::faceDofScale >(); forAll< parallelDevicePolicy<> >( faceManager.size(), [=] GEOS_HOST_DEVICE ( localIndex const kf ) { @@ -1102,11 +875,51 @@ void SinglePhaseMixedMFD::applyBoundaryConditions( real64 const time_n, } } dofScale[localRow] = diag > 0.0 ? pScale / diag : pScale; + faceScale[kf] = dofScale[localRow]; } ); } ); - // w_i = max_j |A_ij| s_j over the locally-owned columns of row i - forAll< parallelDevicePolicy<> >( numRows, [=] GEOS_HOST_DEVICE ( localIndex const i ) + // the scales of the ghost dofs: a cell may own none of its faces, so the face scales of its + // owners are synchronized; the pressure scale of a ghost cell is its synchronized |p_n| + std::unordered_map< globalIndex, real64 > ghostScale; + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + FieldIdentifiers fieldsToBeSync; + fieldsToBeSync.addFields( FieldLocation::Face, { mixedMimetic::faceDofScale::key() } ); + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), false ); + + FaceManager const & faceManager = mesh.getFaceManager(); + arrayView1d< globalIndex const > const faceDofNumber = faceManager.getReference< array1d< globalIndex > >( faceDofKey ); + arrayView1d< integer const > const faceGhostRank = faceManager.ghostRank(); + arrayView1d< real64 const > const faceScale = faceManager.getField< mixedMimetic::faceDofScale >(); + for( localIndex kf = 0; kf < faceManager.size(); ++kf ) + { + if( faceGhostRank[kf] >= 0 && faceDofNumber[kf] >= 0 ) + { + ghostScale[faceDofNumber[kf]] = faceScale[kf]; + } + } + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase const & subRegion ) + { + arrayView1d< globalIndex const > const elemDofNumber = subRegion.getReference< array1d< globalIndex > >( elemDofKey ); + arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); + arrayView1d< real64 const > const presN = subRegion.getField< fields::flow::pressure_n >(); + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + if( elemGhostRank[ei] >= 0 && elemDofNumber[ei] >= 0 ) + { + ghostScale[elemDofNumber[ei]] = LvArray::math::abs( presN[ei] ); + } + } + } ); + } ); + + // w_i = max_j |A_ij| s_j over every column of row i + forAll< serialPolicy >( numRows, [&]( localIndex const i ) { real64 w = 0.0; arraySlice1d< globalIndex const > const columns = localMatrix.getColumns( i ); @@ -1114,10 +927,17 @@ void SinglePhaseMixedMFD::applyBoundaryConditions( real64 const time_n, for( localIndex k = 0; k < localMatrix.numNonZeros( i ); ++k ) { globalIndex const localCol = columns[k] - rankOffset; + real64 scale = 0.0; if( localCol >= 0 && localCol < numRows ) { - w = LvArray::math::max( w, LvArray::math::abs( entries[k] ) * dofScale[localCol] ); + scale = dofScale[localCol]; + } + else + { + auto const it = ghostScale.find( columns[k] ); + scale = it != ghostScale.end() ? it->second : 0.0; } + w = LvArray::math::max( w, LvArray::math::abs( entries[k] ) * scale ); } residualWeight[i] = w; } ); diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp index 91f37774b43..4a8f4d24153 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp @@ -180,6 +180,13 @@ class SinglePhaseMixedMFD : public SinglePhaseBase * @brief Run the residual-based Global Adaptation pipeline and mark the cells. * @param[in] domain the domain */ + /** + * @brief Store, for every face, the global index of the cell its flux unknown points out of, + * and synchronize it so that the orientation is the same on every rank. + * @param domain the domain + */ + void computeFaceOrientation( DomainPartition & domain ); + void computeGlobalAdaptationIndicators( DomainPartition & domain ); /** @@ -194,12 +201,11 @@ class SinglePhaseMixedMFD : public SinglePhaseBase * @brief Classify the faces from the cell marking (0 = condensable TPFA face, 1 = live MFD face). * @param[in] domain the domain */ - void computeFaceStencilLabels( DomainPartition & domain, bool const keepAllFacesLive ); + void computeFaceStencilLabels( DomainPartition & domain ); /** - * @brief Build the per-dof labels used by the stencilFlag-guided three-level MGR strategy: - * 0 = face flux with exactly-diagonal row (all adjacent cells TPFA-compatible), - * 1 = face flux adjacent to at least one MFD-compatible cell, + * @brief Build the per-dof labels of the MGR strategy: + * 0 = condensed face flux (exactly diagonal row), 1 = non-condensed face flux, * 2 = cell pressure. * @param[in] domain the domain * @param[in] dofManager the dof manager (dof numbers must be finalized) @@ -207,15 +213,6 @@ class SinglePhaseMixedMFD : public SinglePhaseBase void computeMgrPointMarkers( DomainPartition const & domain, DofManager const & dofManager ); - /** - * @brief Build the de Rham sub-complex of the live MFD faces (discrete curl and gradient, - * active vertex coordinates) for the Riesz-map preconditioner. - * @param[in] domain the domain - * @param[in] dofManager the dof manager (dof numbers must be finalized) - */ - void computeADSAuxData( DomainPartition const & domain, - DofManager const & dofManager ); - /// relative tolerance used in the mass matrix computations real64 m_areaRelTol; diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp index 6c833a76ebb..7ff5dd24b0e 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp @@ -73,7 +73,6 @@ class ElementBasedAssemblyKernel NodeManager const & nodeManager, FaceManager const & faceManager, CellElementSubRegion const & subRegion, - LocalToGlobalAccessor const & elemLocalToGlobal, constitutive::SingleFluidBase const & fluid, constitutive::PermeabilityBase const & permeability, real64 const & dt, @@ -96,8 +95,8 @@ class ElementBasedAssemblyKernel m_elemRegionList( faceManager.elementRegionList() ), m_elemSubRegionList( faceManager.elementSubRegionList() ), m_elemList( faceManager.elementList() ), - m_elemLocalToGlobal( elemLocalToGlobal.toNestedViewConst() ), m_myElemLocalToGlobal( subRegion.localToGlobalMap() ), + m_faceOrientationCell( faceManager.getField< fields::mixedMimetic::faceOrientationCell >() ), m_nodePosition( nodeManager.referencePosition() ), m_elemPerm( permeability.permeability() ), m_elemPres( subRegion.getField< fields::flow::pressure >() ), @@ -190,28 +189,21 @@ class ElementBasedAssemblyKernel // step 3: face orientations, localized fluxes and no-flow flags. // The orientation must be identical on every MPI rank sharing the face (the face - // mass-flux unknown is shared across ranks), so it cannot rely on the face normal, - // whose direction is not guaranteed to be rank-invariant. Instead, the global face - // orientation points out of the adjacent cell with the smallest global element index. + // mass-flux unknown is shared across ranks): the face points out of the adjacent cell + // with the smallest global element index, stored per face by the face owner and + // synchronized, so that a ghost cell whose neighbour is absent on this rank still + // orients the face like its owner real64 localFlux[NUM_FACE]{}; globalIndex const myGlobalElem = m_myElemLocalToGlobal[ei]; for( integer i = 0; i < NUM_FACE; ++i ) { localIndex const kf = m_elemToFaces[ei][i]; - real64 sigma = 1.0; - bool const valid0 = ( m_elemRegionList[kf][0] >= 0 && m_elemSubRegionList[kf][0] >= 0 && m_elemList[kf][0] >= 0 ); - bool const valid1 = ( m_elemRegionList[kf][1] >= 0 && m_elemSubRegionList[kf][1] >= 0 && m_elemList[kf][1] >= 0 ); - if( valid0 && valid1 ) - { - globalIndex const g0 = m_elemLocalToGlobal[m_elemRegionList[kf][0]][m_elemSubRegionList[kf][0]][m_elemList[kf][0]]; - globalIndex const g1 = m_elemLocalToGlobal[m_elemRegionList[kf][1]][m_elemSubRegionList[kf][1]][m_elemList[kf][1]]; - globalIndex const gMin = ( g0 < g1 ) ? g0 : g1; - sigma = ( myGlobalElem == gMin ) ? 1.0 : -1.0; - } - stack.orientation[i] = sigma; + stack.orientation[i] = ( myGlobalElem == m_faceOrientationCell[kf] ) ? 1.0 : -1.0; localFlux[i] = stack.orientation[i] * m_faceFlux[kf]; + bool const valid0 = ( m_elemRegionList[kf][0] >= 0 && m_elemSubRegionList[kf][0] >= 0 && m_elemList[kf][0] >= 0 ); + bool const valid1 = ( m_elemRegionList[kf][1] >= 0 && m_elemSubRegionList[kf][1] >= 0 && m_elemList[kf][1] >= 0 ); bool const onBoundary = !( valid0 && valid1 ); stack.isNoFlowFace[i] = ( onBoundary && m_isPresBcFace[kf] == 0 ) ? 1 : 0; stack.isCondensedFace[i] = ( m_faceStencilLabel[kf] == 0 && stack.isNoFlowFace[i] == 0 ) ? 1 : 0; @@ -396,8 +388,9 @@ class ElementBasedAssemblyKernel arrayView2d< localIndex const > const m_elemRegionList; arrayView2d< localIndex const > const m_elemSubRegionList; arrayView2d< localIndex const > const m_elemList; - ElementViewConst< arrayView1d< globalIndex const > > const m_elemLocalToGlobal; arrayView1d< globalIndex const > const m_myElemLocalToGlobal; + /// global index of the cell each face points out of (rank-invariant orientation) + arrayView1d< globalIndex const > const m_faceOrientationCell; arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const m_nodePosition; /// permeability @@ -440,7 +433,6 @@ class ElementBasedAssemblyKernelFactory string const faceDofKey, NodeManager const & nodeManager, FaceManager const & faceManager, - ElementRegionManager const & elemManager, CellElementSubRegion const & subRegion, mimeticInnerProduct::MimeticInnerProductBase const & mimeticInnerProductBase, constitutive::SingleFluidBase const & fluid, @@ -449,11 +441,6 @@ class ElementBasedAssemblyKernelFactory CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { - // rank-invariant global face orientation requires the global element indices of both - // cells adjacent to each face, possibly living in different subregions - ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > const elemLocalToGlobal = - elemManager.constructArrayViewAccessor< globalIndex, 1 >( ObjectManagerBase::viewKeyStruct::localToGlobalMapString() ); - mixedMimeticInnerProductDispatch( mimeticInnerProductBase, [&] ( auto const mimeticInnerProduct ) { @@ -463,7 +450,7 @@ class ElementBasedAssemblyKernelFactory { ElementBasedAssemblyKernel< NUM_FACES, IP > kernel( rankOffset, lengthTolerance, elemDofKey, faceDofKey, nodeManager, faceManager, - subRegion, elemLocalToGlobal, fluid, permeability, dt, localMatrix, localRhs ); + subRegion, fluid, permeability, dt, localMatrix, localRhs ); ElementBasedAssemblyKernel< NUM_FACES, IP >::template launch< POLICY >( subRegion.size(), kernel ); } ); } ); From c798a74eb9efb9b7e0d7613a9589871d5a03dcc7 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Sun, 6 Sep 2026 02:28:33 -0700 Subject: [PATCH 31/64] wip: support for user defined mfdFlag --- .../mgrStrategies/SinglePhaseMixedMFD.hpp | 2 +- .../MixedMimeticDiscretization.cpp | 2 +- .../mixedMimetic/MixedMimeticFields.hpp | 17 ++- .../adaptivity/GlobalAdaptationKernels.hpp | 8 +- .../fluidFlow/SinglePhaseMixedMFD.cpp | 114 +++++++++++++----- .../fluidFlow/SinglePhaseMixedMFD.hpp | 21 +++- .../SinglePhaseMixedMFDKernels.hpp | 6 +- 7 files changed, 127 insertions(+), 43 deletions(-) diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp index 45230e6d5a2..b753f780069 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp @@ -48,7 +48,7 @@ namespace mgr * * The solver provides custom point markers (LinearSolverParameters::MGR::customPointMarkers): * 0 = face flux whose row is exactly diagonal (condensed two-point face, no-flow face) - * 1 = face flux adjacent to at least one stabilized (MFD) cell + * 1 = face flux adjacent to at least one cell with the consistent (MFD) product * 2 = cell pressure * Both flux labels are F-points of the single reduction level. */ diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp index cc3461af81d..bbf1aa0c1b1 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp @@ -67,7 +67,7 @@ MixedMimeticDiscretization::MixedMimeticDiscretization( string const & name, setInputFlag( InputFlags::OPTIONAL ). setApplyDefaultValue( 0.1 ). setDescription( "Degeneracy tolerance in percent: a cell whose volume is below this percentage of the total volume " - "of its node star is not admissible for the stabilized reconstruction and uses the diagonal (TPFA) " + "of its node star is not admissible for the consistent (MFD) product and uses the diagonal (TPFA) " "product, whatever the consistency indicator says; 0 disables the layer" ); } diff --git a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp index 3d468aee4b0..105152e76cf 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp @@ -49,13 +49,22 @@ DECLARE_FIELD( faceMassFlux_n, WRITE_AND_READ, "Face mass flux at the previous converged time step" ); -DECLARE_FIELD( stencilFlag, - "stencilFlag", +DECLARE_FIELD( mfdFlag, + "mfdFlag", array1d< integer >, 0, LEVEL_0, WRITE_AND_READ, - "Adaptive stencil activation flag (0 = TPFA-compatible, 1 = MFD-compatible)" ); + "Inner product selector eta of the cell: 1 = consistent (MFD) product, 0 = diagonal (TPFA) product" ); + +DECLARE_FIELD( prescribedMfdFlag, + "prescribedMfdFlag", + array1d< real64 >, + -1.0, + LEVEL_0, + WRITE_AND_READ, + "User prescription of eta read from the mesh (real-valued so that it can be imported): negative = free " + "(the solver decides), 0 = diagonal product, 1 = consistent (MFD) product (rejected on a degenerate cell)" ); DECLARE_FIELD( consistencyIndicator, "consistencyIndicator", @@ -71,7 +80,7 @@ DECLARE_FIELD( degeneracyIndicator, 0, LEVEL_0, WRITE_AND_READ, - "Cell volume as a percentage of the total volume of its node star (admissibility of the stabilized reconstruction)" ); + "Cell volume as a percentage of the total volume of its node star (admissibility of the consistent (MFD) product)" ); DECLARE_FIELD( faceOrientationCell, "faceOrientationCell", diff --git a/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp b/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp index fc0b318cf05..b3aaefb321e 100644 --- a/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp +++ b/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp @@ -200,7 +200,7 @@ struct FaceLabelKernel arrayView2d< localIndex const > const & elemSubRegionList, arrayView2d< localIndex const > const & elemList, SortedArrayView< localIndex const > const & regionFilter, - ElementViewConst< arrayView1d< integer const > > const & stencilFlag, + ElementViewConst< arrayView1d< integer const > > const & mfdFlag, bool const effectiveTpfa, arrayView1d< integer > const & faceStencilLabel ) { @@ -216,7 +216,7 @@ struct FaceLabelKernel localIndex const ei = elemList[kf][k]; if( er >= 0 && esr >= 0 && ei >= 0 && regionFilter.contains( er ) ) { - label = LvArray::math::max( label, stencilFlag[er][esr][ei] ); + label = LvArray::math::max( label, mfdFlag[er][esr][ei] ); } } } @@ -344,7 +344,7 @@ struct MarkingKernel arrayView1d< real64 const > const & faceResidual, real64 const tolerance, arrayView1d< real64 > const & consistencyIndicator, - arrayView1d< integer > const & stencilFlag ) + arrayView1d< integer > const & mfdFlag ) { RAJA::ReduceSum< ReducePolicy< POLICY >, localIndex > numMfdCells( 0 ); @@ -357,7 +357,7 @@ struct MarkingKernel } consistencyIndicator[ei] = indicator; integer const flag = ( indicator > tolerance ) ? 1 : 0; - stencilFlag[ei] = flag; + mfdFlag[ei] = flag; numMfdCells += ( elemGhostRank[ei] < 0 ) ? flag : 0; } ); diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index 14b60b7257a..af0fc540874 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -72,7 +72,8 @@ void SinglePhaseMixedMFD::registerDataOnMesh( Group & meshBodies ) [&]( localIndex const, ElementSubRegionBase & subRegion ) { - subRegion.registerField< mixedMimetic::stencilFlag >( getName() ); + subRegion.registerField< mixedMimetic::mfdFlag >( getName() ); + subRegion.registerField< mixedMimetic::prescribedMfdFlag >( getName() ); subRegion.registerField< mixedMimetic::consistencyIndicator >( getName() ); subRegion.registerField< mixedMimetic::degeneracyIndicator >( getName() ); } ); @@ -225,22 +226,22 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d MixedMimeticDiscretizationManager const & mmManager = numericalMethodManager.getMixedMimeticDiscretizationManager(); MixedMimeticDiscretization const & discretization = mmManager.getMixedMimeticDiscretization( m_discretizationName ); - if( !discretization.isAdaptiveConsistency() ) + // every cell starts with the consistent (MFD) product; the layers below can only refine that choice + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) { - // no adaptation: activate the selected inner product in every cell - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) { - mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, - [&]( localIndex const, - ElementSubRegionBase & subRegion ) - { - subRegion.getField< mixedMimetic::stencilFlag >().template setValues< parallelDevicePolicy<> >( 1 ); - } ); + subRegion.getField< mixedMimetic::mfdFlag >().template setValues< parallelDevicePolicy<> >( 1 ); } ); - applyDegeneracyLayer( domain ); - computeFaceStencilLabels( domain ); + } ); + + if( !discretization.isAdaptiveConsistency() ) + { + applyLayersAndLabel( domain ); return; } @@ -320,7 +321,7 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d CellElementSubRegion & subRegion ) { arrayView1d< real64 > const consistencyIndicator = subRegion.getField< mixedMimetic::consistencyIndicator >(); - arrayView1d< integer > const stencilFlag = subRegion.getField< mixedMimetic::stencilFlag >(); + arrayView1d< integer > const mfdFlag = subRegion.getField< mixedMimetic::mfdFlag >(); mixedMimeticKernels::internal::kernelLaunchSelectorFaceSwitch( subRegion.numFacesPerElement(), [&] ( auto NUM_FACES ) { @@ -331,14 +332,14 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d faceResidual.toViewConst(), tolerance, consistencyIndicator, - stencilFlag ); + mfdFlag ); } ); numCells += subRegion.size() - subRegion.getNumberOfGhosts(); } ); // make the marking consistent on ghost cells FieldIdentifiers fieldsToBeSync; - fieldsToBeSync.addElementFields( { mixedMimetic::stencilFlag::key(), mixedMimetic::consistencyIndicator::key() }, regionNames ); + fieldsToBeSync.addElementFields( { mixedMimetic::mfdFlag::key(), mixedMimetic::consistencyIndicator::key() }, regionNames ); CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), false ); globalIndex const globalNumMfdCells = MpiWrapper::sum< globalIndex >( numMfdCells ); @@ -347,12 +348,66 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d tolerance, globalNumMfdCells, globalNumCells ) ); } ); - // second layer: the degenerate cells fall back to the diagonal product whatever the consistency says - applyDegeneracyLayer( domain ); + applyLayersAndLabel( domain ); +} + +void SinglePhaseMixedMFD::applyLayersAndLabel( DomainPartition & domain ) +{ + // prescription, then degeneracy (admissibility, which alone may override the prescription) + std::pair< localIndex, localIndex > const prescribed = applyPrescribedFlag( domain ); + std::pair< localIndex, localIndex > const degenerate = applyDegeneracyLayer( domain ); + globalIndex const numPrescribed0 = MpiWrapper::sum< globalIndex >( prescribed.first ); + globalIndex const numPrescribed1 = MpiWrapper::sum< globalIndex >( prescribed.second ); + globalIndex const numRejected = MpiWrapper::sum< globalIndex >( degenerate.second ); + if( numPrescribed0 + numPrescribed1 > 0 ) + { + GEOS_LOG_RANK_0( GEOS_FMT( "mixedMFD Flow: prescribed eta = 0 on {} cells and eta = 1 on {} cells ({} rejected by the degeneracy layer)", + numPrescribed0, numPrescribed1, numRejected ) ); + } computeFaceStencilLabels( domain ); } -localIndex SinglePhaseMixedMFD::applyDegeneracyLayer( DomainPartition & domain ) +std::pair< localIndex, localIndex > SinglePhaseMixedMFD::applyPrescribedFlag( DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + + localIndex numPrescribed0 = 0; + localIndex numPrescribed1 = 0; + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + FieldIdentifiers prescribedToBeSync; + prescribedToBeSync.addElementFields( { mixedMimetic::prescribedMfdFlag::key() }, regionNames ); + CommunicationTools::getInstance().synchronizeFields( prescribedToBeSync, mesh, domain.getNeighbors(), false ); + + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + arrayView1d< real64 const > const prescribed = subRegion.getField< mixedMimetic::prescribedMfdFlag >(); + arrayView1d< integer > const mfdFlag = subRegion.getField< mixedMimetic::mfdFlag >(); + arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + if( prescribed[ei] < 0.0 ) + { + continue; + } + integer const eta = prescribed[ei] > 0.5 ? 1 : 0; + mfdFlag[ei] = eta; + if( ghostRank[ei] < 0 ) + { + numPrescribed0 += ( eta == 0 ); + numPrescribed1 += ( eta == 1 ); + } + } + } ); + } ); + return { numPrescribed0, numPrescribed1 }; +} + +std::pair< localIndex, localIndex > SinglePhaseMixedMFD::applyDegeneracyLayer( DomainPartition & domain ) { GEOS_MARK_FUNCTION; @@ -362,6 +417,7 @@ localIndex SinglePhaseMixedMFD::applyDegeneracyLayer( DomainPartition & domain ) real64 const tolerance = discretization.getDegeneracyTolerance(); localIndex numDegenerate = 0; + localIndex numRejected = 0; forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, MeshLevel & mesh, string_array const & regionNames ) @@ -382,7 +438,8 @@ localIndex SinglePhaseMixedMFD::applyDegeneracyLayer( DomainPartition & domain ) arrayView1d< real64 const > const volume = subRegion.getElementVolume(); arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); arrayView1d< real64 > const indicator = subRegion.getField< mixedMimetic::degeneracyIndicator >(); - arrayView1d< integer > const stencilFlag = subRegion.getField< mixedMimetic::stencilFlag >(); + arrayView1d< integer > const mfdFlag = subRegion.getField< mixedMimetic::mfdFlag >(); + arrayView1d< real64 const > const prescribed = subRegion.getField< mixedMimetic::prescribedMfdFlag >(); localIndex const numNodes = subRegion.numNodesPerElement(); // the node star of a cell: every cell sharing a vertex with it, counted once @@ -411,19 +468,20 @@ localIndex SinglePhaseMixedMFD::applyDegeneracyLayer( DomainPartition & domain ) // share of the cell in the volume of its node star, in percent real64 const percent = sum > 0.0 ? 100.0 * volume[ei] / sum : 0.0; indicator[ei] = percent; - if( percent < tolerance && stencilFlag[ei] == 1 ) + if( percent < tolerance && mfdFlag[ei] == 1 ) { - stencilFlag[ei] = 0; + mfdFlag[ei] = 0; numDegenerate += ( ghostRank[ei] < 0 ) ? 1 : 0; + numRejected += ( ghostRank[ei] < 0 && prescribed[ei] > 0.5 ) ? 1 : 0; } } } ); FieldIdentifiers fieldsToBeSync; - fieldsToBeSync.addElementFields( { mixedMimetic::stencilFlag::key(), mixedMimetic::degeneracyIndicator::key() }, regionNames ); + fieldsToBeSync.addElementFields( { mixedMimetic::mfdFlag::key(), mixedMimetic::degeneracyIndicator::key() }, regionNames ); CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), false ); } ); - return numDegenerate; + return { numDegenerate, numRejected }; } void SinglePhaseMixedMFD::computeFaceStencilLabels( DomainPartition & domain ) @@ -445,8 +503,8 @@ void SinglePhaseMixedMFD::computeFaceStencilLabels( DomainPartition & domain ) FaceManager & faceManager = mesh.getFaceManager(); ElementRegionManager & elemManager = mesh.getElemManager(); - ElementRegionManager::ElementViewAccessor< arrayView1d< integer const > > const stencilFlagAccessor = - elemManager.constructArrayViewAccessor< integer, 1 >( mixedMimetic::stencilFlag::key() ); + ElementRegionManager::ElementViewAccessor< arrayView1d< integer const > > const mfdFlagAccessor = + elemManager.constructArrayViewAccessor< integer, 1 >( mixedMimetic::mfdFlag::key() ); mixedMimeticKernels::FaceLabelKernel:: launch< parallelDevicePolicy<> >( faceManager.size(), @@ -454,7 +512,7 @@ void SinglePhaseMixedMFD::computeFaceStencilLabels( DomainPartition & domain ) faceManager.elementSubRegionList(), faceManager.elementList(), m_regionFilter.toViewConst(), - stencilFlagAccessor.toNestedViewConst(), + mfdFlagAccessor.toNestedViewConst(), effectiveTpfa, faceManager.getField< mixedMimetic::faceStencilLabel >() ); } ); diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp index 4a8f4d24153..1cb10f75f65 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp @@ -22,6 +22,8 @@ #include "physicsSolvers/fluidFlow/SinglePhaseBase.hpp" +#include + namespace geos { @@ -193,9 +195,24 @@ class SinglePhaseMixedMFD : public SinglePhaseBase * @brief Second classification layer: cells whose volume is below the degeneracy tolerance * (percent of the volume of their node star) fall back to the diagonal product. * @param domain the domain - * @return the number of locally-owned cells switched to the diagonal product by this layer + * @return the number of locally-owned cells switched to the diagonal product by this layer, and how many + * of them were prescribed the consistent (MFD) product + */ + std::pair< localIndex, localIndex > applyDegeneracyLayer( DomainPartition & domain ); + + /** + * @brief Third classification layer: the user prescription of eta read from the mesh + * (prescribedMfdFlag: negative free, 0 diagonal product, 1 consistent (MFD) product). + * @param domain the domain + * @return the number of locally-owned cells prescribed 0 and prescribed 1 + */ + std::pair< localIndex, localIndex > applyPrescribedFlag( DomainPartition & domain ); + + /** + * @brief Apply the prescription and degeneracy layers on top of the current eta, then label the faces. + * @param domain the domain */ - localIndex applyDegeneracyLayer( DomainPartition & domain ); + void applyLayersAndLabel( DomainPartition & domain ); /** * @brief Classify the faces from the cell marking (0 = condensable TPFA face, 1 = live MFD face). diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp index 7ff5dd24b0e..ce7c9636641 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp @@ -104,7 +104,7 @@ class ElementBasedAssemblyKernel m_bcPres( faceManager.getField< fields::flow::bcPressure >() ), m_isPresBcFace( faceManager.getField< fields::flow::isBoundaryFace >() ), m_faceStencilLabel( faceManager.getField< fields::mixedMimetic::faceStencilLabel >() ), - m_stencilFlag( subRegion.getField< fields::mixedMimetic::stencilFlag >() ), + m_mfdFlag( subRegion.getField< fields::mixedMimetic::mfdFlag >() ), m_elemDens( fluid.density() ), m_dElemDens( fluid.dDensity() ), m_mob( subRegion.getField< fields::flow::mobility >() ), @@ -219,7 +219,7 @@ class ElementBasedAssemblyKernel } if( anyLiveFace ) { - real64 const chi = static_cast< real64 >( m_stencilFlag[ei] ); + real64 const chi = static_cast< real64 >( m_mfdFlag[ei] ); mimeticInnerProduct::AdaptiveInnerProduct< IP >::template computeM< NUM_FACE >( m_nodePosition, m_faceToNodes, m_elemToFaces[ei], @@ -404,7 +404,7 @@ class ElementBasedAssemblyKernel arrayView1d< integer const > const m_faceStencilLabel; /// adaptive stencil flag - arrayView1d< integer const > const m_stencilFlag; + arrayView1d< integer const > const m_mfdFlag; /// fluid data arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const m_elemDens; From f3ba8f7bd04a23026b31a5bf8790a1c153690b44 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Sun, 6 Sep 2026 11:30:11 -0700 Subject: [PATCH 32/64] wip: testing adaptive consistency --- .../mimeticInnerProducts/TPFAInnerProduct.hpp | 2 - .../unitTests/testMimeticInnerProducts.cpp | 235 +++++++++-- .../mixedMimetic/CMakeLists.txt | 12 +- .../MixedMimeticDiscretization.cpp | 12 +- .../MixedMimeticDiscretization.hpp | 16 +- .../mixedMimetic/MixedMimeticFields.hpp | 4 +- .../consistency/ConsistencyAdaptation.cpp | 288 +++++++++++++ .../consistency/ConsistencyAdaptation.hpp | 144 +++++++ .../ConsistencyAdaptationKernels.hpp} | 12 +- .../mixedMimetic/unitTests/CMakeLists.txt | 27 ++ .../unitTests/testConsistencyAdaptation.cpp | 378 ++++++++++++++++++ .../fluidFlow/SinglePhaseMixedMFD.cpp | 310 ++------------ .../fluidFlow/SinglePhaseMixedMFD.hpp | 41 +- .../SinglePhaseMixedMFDKernels.hpp | 2 +- 14 files changed, 1099 insertions(+), 384 deletions(-) create mode 100644 src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp create mode 100644 src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.hpp rename src/coreComponents/mixedMimetic/{adaptivity/GlobalAdaptationKernels.hpp => consistency/ConsistencyAdaptationKernels.hpp} (96%) create mode 100644 src/coreComponents/mixedMimetic/unitTests/CMakeLists.txt create mode 100644 src/coreComponents/mixedMimetic/unitTests/testConsistencyAdaptation.cpp diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp index ff474a5018c..b448c61feba 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp @@ -84,8 +84,6 @@ class TPFAInnerProduct : public MimeticInnerProductBase real64 const & lengthTolerance, arraySlice2d< real64 > const & M ); -private: - /** * @brief Compute the one-sided (half) TPFA transmissibility of a local face, k_n A / d. * @param[in] nodePosition the position of the nodes diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp index 24ac7f970d4..879d21da964 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp @@ -2100,6 +2100,49 @@ TEST( MimeticIP_MixedLinear, Distortion_NonPlanar_LinearPressure ) // (stronger than the pressure recovery above, where a defect can hide in the fluxes). // Valid only on cells where C^T N = vol * I holds with vertex-averaged face centers. +// err = max | ( M ( |f| n_f K ) - c_f )_fd |, with the geometry convention of computeM (centroid_3DPolygon) +template< int NF > +static double mixedIPConsistencyError( array2d< real64, nodes::REFERENCE_POSITION_PERM > const & node, + FaceManager::NodeMapType const & faceTonode, + real64 const (¢er)[3], + real64 const (&Kvec)[3], + arraySlice2d< real64 const > const & M ) +{ + real64 NK[NF][3]; + real64 C[NF][3]; + for( int f = 0; f < NF; ++f ) + { + real64 fc[3], fn[3]; + real64 const area = centroid_3DPolygon( faceTonode[f], node.toViewConst(), fc, fn ); + real64 c2f[3] = { fc[0] - center[0], fc[1] - center[1], fc[2] - center[2] }; + if( LvArray::tensorOps::AiBi< 3 >( c2f, fn ) < 0.0 ) + { + LvArray::tensorOps::scale< 3 >( fn, -1.0 ); + } + for( int d = 0; d < 3; ++d ) + { + NK[f][d] = area * fn[d] * Kvec[d]; // (|f| n_f) K for diagonal K + C[f][d] = c2f[d]; + } + } + + // err = max | ( M NK - C )_fd | + double err = 0.0; + for( int f = 0; f < NF; ++f ) + { + for( int d = 0; d < 3; ++d ) + { + real64 s = 0.0; + for( int k = 0; k < NF; ++k ) + { + s += M( f, k ) * NK[k][d]; + } + err = std::max( err, std::abs( s - C[f][d] ) ); + } + } + return err; +} + template< int NF > static double computeMixedIPConsistency_error( int ipKind, DistortionMode mode = DistortionMode::None, @@ -2144,41 +2187,7 @@ static double computeMixedIPConsistency_error( int ipKind, M.template setValues< parallelHostPolicy >( 0.0 ); computeM_dispatch< NF >( ipKind, node_L, faceTonode_L, elemToface_L, cLc.toSliceConst(), vol_L, Kvec, ltol, M.toSlice() ); - // assemble N (area-weighted outward normals, times K) and C (cell-to-face vectors), - // using the same geometry convention as computeM (centroid_3DPolygon) - real64 NK[NF][3]; - real64 C[NF][3]; - for( int f = 0; f < NF; ++f ) - { - real64 fc[3], fn[3]; - real64 const area = centroid_3DPolygon( faceTonode_L[f], node_L.toViewConst(), fc, fn ); - real64 c2f[3] = { fc[0] - center_L[0], fc[1] - center_L[1], fc[2] - center_L[2] }; - if( LvArray::tensorOps::AiBi< 3 >( c2f, fn ) < 0.0 ) - { - LvArray::tensorOps::scale< 3 >( fn, -1.0 ); - } - for( int d = 0; d < 3; ++d ) - { - NK[f][d] = area * fn[d] * Kvec[d]; // (|f| n_f) K for diagonal K - C[f][d] = c2f[d]; - } - } - - // err = max | ( M NK - C )_fd | - double err = 0.0; - for( int f = 0; f < NF; ++f ) - { - for( int d = 0; d < 3; ++d ) - { - real64 s = 0.0; - for( int k = 0; k < NF; ++k ) - { - s += M( f, k ) * NK[k][d]; - } - err = std::max( err, std::abs( s - C[f][d] ) ); - } - } - return err; + return mixedIPConsistencyError< NF >( node_L, faceTonode_L, center_L, Kvec, M.toSliceConst() ); } TEST( MimeticIP_MixedConsistency, UnitCube ) @@ -3044,6 +3053,162 @@ TEST( Hydrostatic, GravityConsistency_Distortion_NonPlanar ) } } +// tetrahedron A B C D with a K-obtuse face BCD: c . ( K n ) < 0 for K = diag( k, k, 1 ), k < 1/4 +static void makeKObtuseTetra( array2d< real64, nodes::REFERENCE_POSITION_PERM > & nodePosition, + FaceManager::NodeMapType & faceToNodes, + array1d< localIndex > & elemToFaces, + real64 ( & elemCenter )[3], + real64 & elemVolume ) +{ + real64 const xyz[4][3] = { { 0, 0, 0 }, { 1, 0, 0 }, { 0, 1, 0 }, { 1, 1, 1 } }; + localIndex const f2n[4][3] = { { 0, 1, 2 }, { 0, 1, 3 }, { 1, 2, 3 }, { 0, 2, 3 } }; + + nodePosition.resize( 4, 3 ); + faceToNodes.resize( 4 ); + elemToFaces.resize( 4 ); + array1d< localIndex > toNodes( 4 ); + for( localIndex i = 0; i < 4; ++i ) + { + faceToNodes.resizeArray( i, 3 ); + elemToFaces( i ) = i; + toNodes( i ) = i; + for( localIndex d = 0; d < 3; ++d ) + { + nodePosition( i, d ) = xyz[i][d]; + faceToNodes( i, d ) = f2n[i][d]; + } + } + computeVolumeAndCenter( nodePosition, toNodes, elemCenter, elemVolume ); +} + +// reference one-sided transmissibility of a triangle: |f|/d c.(K n), replaced by |f|/d c.(K c) when c.(K n) <= 0 +static real64 referenceOneSidedTrans( real64 const (&x)[3][3], + real64 const (¢er)[3], + real64 const (&K)[3], + real64 & rawWeight ) +{ + real64 e1[3], e2[3], n[3], c[3], Kn[3], Kc[3]; + for( int d = 0; d < 3; ++d ) + { + e1[d] = x[1][d] - x[0][d]; + e2[d] = x[2][d] - x[0][d]; + c[d] = ( x[0][d] + x[1][d] + x[2][d] ) / 3.0 - center[d]; + } + LvArray::tensorOps::crossProduct( n, e1, e2 ); + real64 const area = 0.5 * LvArray::tensorOps::normalize< 3 >( n ); + real64 const dist = LvArray::tensorOps::normalize< 3 >( c ); + if( LvArray::tensorOps::AiBi< 3 >( c, n ) < 0.0 ) + { + LvArray::tensorOps::scale< 3 >( n, -1.0 ); + } + LvArray::tensorOps::hadamardProduct< 3 >( Kn, K, n ); + LvArray::tensorOps::hadamardProduct< 3 >( Kc, K, c ); + rawWeight = LvArray::tensorOps::AiBi< 3 >( c, Kn ); + return area / dist * ( rawWeight > 0.0 ? rawWeight : LvArray::tensorOps::AiBi< 3 >( c, Kc ) ); +} + +TEST( testMimeticInnerProducts, TPFA_KObtuseFallback ) +{ + localIndex constexpr NF = 4; + + array2d< real64, nodes::REFERENCE_POSITION_PERM > nodePosition; + FaceManager::NodeMapType faceToNodes; + array1d< localIndex > elemToFaces; + real64 elemCenter[3] = { 0.0 }; + real64 elemVolume = 0; + makeKObtuseTetra( nodePosition, faceToNodes, elemToFaces, elemCenter, elemVolume ); + + real64 const perm[3] = { 1e-13, 1e-13, 1e-12 }; + real64 const lengthTolerance = 1e-8; + stackArray1d< real64, 3 > center( 3 ); + for( int d = 0; d < 3; ++d ) + { + center[d] = elemCenter[d]; + } + + real64 trans[NF]; + for( localIndex f = 0; f < NF; ++f ) + { + real64 x[3][3]; + for( localIndex k = 0; k < 3; ++k ) + { + for( int d = 0; d < 3; ++d ) + { + x[k][d] = nodePosition( faceToNodes( f, k ), d ); + } + } + real64 rawWeight = 0.0; + real64 const ref = referenceOneSidedTrans( x, elemCenter, perm, rawWeight ); + // only the face BCD is K-obtuse + if( f == 2 ) + { + EXPECT_LT( rawWeight, 0.0 ); + } + else + { + EXPECT_GT( rawWeight, 0.0 ); + } + trans[f] = TPFAInnerProduct::computeOneSidedTrans( nodePosition.toViewConst(), faceToNodes.toViewConst(), f, + center.toSliceConst(), perm, lengthTolerance * lengthTolerance ); + EXPECT_GT( trans[f], 0.0 ); + checkRelativeError( trans[f], ref, 1e-12 ); + } + + // M = diag( 1 / T_f ) stays positive and finite on the K-obtuse face + stackArray2d< real64, NF * NF > M( NF, NF ); + TPFAInnerProduct::computeM< NF >( nodePosition.toViewConst(), faceToNodes.toViewConst(), elemToFaces.toSliceConst(), + center, elemVolume, perm, lengthTolerance, M.toSlice() ); + for( localIndex f = 0; f < NF; ++f ) + { + checkRelativeError( M( f, f ), 1.0 / trans[f], 1e-12 ); + for( localIndex g = 0; g < NF; ++g ) + { + if( g != f ) + { + EXPECT_DOUBLE_EQ( M( f, g ), 0.0 ); + } + } + } +} + +TEST( MimeticIP_MixedConsistency, Tetra ) +{ + localIndex constexpr NF = 4; + + array2d< real64, nodes::REFERENCE_POSITION_PERM > nodePosition; + FaceManager::NodeMapType faceToNodes; + array1d< localIndex > elemToFaces; + real64 elemCenter[3] = { 0.0 }; + real64 elemPerm[3] = { 0.0 }; + real64 elemVolume = 0; + real64 lengthTolerance = 0; + stackArray2d< real64, NF * NF > Mref( NF, NF ); + makeTetra( nodePosition, faceToNodes, elemToFaces, elemCenter, elemVolume, elemPerm, lengthTolerance, + InnerProductType::TPFA, Mref.toSlice() ); + + stackArray1d< real64, 3 > center( 3 ); + for( int d = 0; d < 3; ++d ) + { + center[d] = elemCenter[d]; + } + + // the RT product is the exact Raviart-Thomas product on a simplex: M ( N K ) = C + stackArray2d< real64, NF * NF > M( NF, NF ); + M.setValues< parallelHostPolicy >( 0.0 ); + computeM_dispatch< NF >( InnerProductType::RT, nodePosition, faceToNodes, elemToFaces, center.toSliceConst(), + elemVolume, elemPerm, lengthTolerance, M.toSlice() ); + EXPECT_LT( mixedIPConsistencyError< NF >( nodePosition, faceToNodes, elemCenter, elemPerm, M.toSliceConst() ), + mixed_consistency_tol ); + + // the two-point product is not consistent on a simplex + M.setValues< parallelHostPolicy >( 0.0 ); + computeM_dispatch< NF >( InnerProductType::TPFA, nodePosition, faceToNodes, elemToFaces, center.toSliceConst(), + elemVolume, elemPerm, lengthTolerance, M.toSlice() ); + EXPECT_GT( mixedIPConsistencyError< NF >( nodePosition, faceToNodes, elemCenter, elemPerm, M.toSliceConst() ), + mixed_consistency_tol ); +} + + int main( int argc, char * * argv ) { ::testing::InitGoogleTest( &argc, argv ); diff --git a/src/coreComponents/mixedMimetic/CMakeLists.txt b/src/coreComponents/mixedMimetic/CMakeLists.txt index f2377eed661..477c13006b5 100644 --- a/src/coreComponents/mixedMimetic/CMakeLists.txt +++ b/src/coreComponents/mixedMimetic/CMakeLists.txt @@ -4,7 +4,7 @@ Package: mixedMimetic Contains the mixed mimetic finite difference discretization infrastructure: the discretization object registered under the NumericalMethods block, the -residual-based Global Adaptation kernels, and the dispatch over the mixed-form +selection of the cell inner product (ConsistencyAdaptation) and the dispatch over the mixed-form inner products. #]] #-------------------------------------------------------------------------------------------------- @@ -17,14 +17,16 @@ set( mixedMimetic_headers MixedMimeticDiscretizationManager.hpp MixedMimeticDispatch.hpp MixedMimeticFields.hpp - adaptivity/GlobalAdaptationKernels.hpp ) + consistency/ConsistencyAdaptation.hpp + consistency/ConsistencyAdaptationKernels.hpp ) # # Specify all sources # set( mixedMimetic_sources MixedMimeticDiscretization.cpp - MixedMimeticDiscretizationManager.cpp ) + MixedMimeticDiscretizationManager.cpp + consistency/ConsistencyAdaptation.cpp ) set( dependencyList ${parallelDeps} finiteVolume ) @@ -40,3 +42,7 @@ blt_add_library( NAME mixedMimetic target_include_directories( mixedMimetic PUBLIC ${CMAKE_SOURCE_DIR}/coreComponents ) install( TARGETS mixedMimetic LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib ) + +if( GEOS_ENABLE_TESTS ) + add_subdirectory( unitTests ) +endif() diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp index bbf1aa0c1b1..edd1c03e81e 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp @@ -49,14 +49,14 @@ MixedMimeticDiscretization::MixedMimeticDiscretization( string const & name, registerWrapper( viewKeyStruct::adaptiveConsistencyString(), &m_adaptiveConsistency ). setInputFlag( InputFlags::OPTIONAL ). setApplyDefaultValue( 1 ). - setDescription( "Flag enabling the consistency layer of the Global Adaptation: when enabled (1, default), the " - "cell-wise inner product is selected between TPFA and innerProductType according to the " - "consistency indicator; when disabled (0), innerProductType is used in every cell" ); + setDescription( "Flag enabling the consistency layer: when enabled (1, default), the cell-wise inner product is " + "TPFA where the consistency indicator is below consistencyTolerance and innerProductType elsewhere; " + "when disabled (0), innerProductType is used in every cell" ); registerWrapper( viewKeyStruct::consistencyToleranceString(), &m_consistencyTolerance ). setInputFlag( InputFlags::OPTIONAL ). setApplyDefaultValue( 1e-3 ). - setDescription( "Tolerance on the consistency indicator of the Global Adaptation used in the cell marking criterion" ); + setDescription( "Tolerance of the consistency layer: the consistent (MFD) product is selected where the indicator exceeds it" ); registerWrapper( viewKeyStruct::nominalGradientString(), &m_nominalGradient ). setInputFlag( InputFlags::OPTIONAL ). @@ -90,9 +90,7 @@ void MixedMimeticDiscretization::postInputInitialization() getWrapperDataContext( viewKeyStruct::nominalGradientString() ) ), InputError ); - // degenerate combination: with a TPFA inner product, the adaptive blend mixes TPFA with TPFA - // and the operator is TPFA everywhere regardless of the marking; the consistency indicator - // fields are still computed and output, which makes this combination a useful diagnostic mode + // with a TPFA inner product both branches of the selection coincide: the indicators are only a diagnostic if( isAdaptiveConsistency() && m_innerProductType == mimeticInnerProduct::MimeticInnerProductTypeStrings::TPFA ) { GEOS_WARNING( GEOS_FMT( "{}: 'adaptiveConsistency' is enabled but 'innerProductType' is TPFA: the adaptation has no " diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp index ecac372b5cb..d622cca1ae6 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.hpp @@ -29,11 +29,9 @@ namespace geos /** * @class MixedMimeticDiscretization * - * Provides management of the cell-wise inner products and of the residual-based - * Global Adaptation (GA) parameters used by the mixed mimetic finite difference solvers. - * Global Adaptation is the only supported cell classification paradigm: when adaptation - * is enabled, the residual tolerance controls the TPFA/MFD partition; when disabled, the - * selected inner product is used in every cell. + * Cell-wise inner product of the mixed mimetic finite difference solvers and parameters of + * its selection eta (ConsistencyAdaptation): consistency tolerance of the residual layer and + * degeneracy tolerance; without adaptation the selected inner product is used in every cell. */ class MixedMimeticDiscretization : public dataRepository::Group { @@ -88,7 +86,7 @@ class MixedMimeticDiscretization : public dataRepository::Group }; /** - * @brief @return Whether the consistency layer of the Global Adaptation is enabled + * @brief @return Whether the consistency layer is enabled */ bool isAdaptiveConsistency() const { return m_adaptiveConsistency == 1; } @@ -98,7 +96,7 @@ class MixedMimeticDiscretization : public dataRepository::Group bool isTpfaInnerProduct() const; /** - * @brief @return The consistency tolerance used in the marking criterion + * @brief @return The tolerance of the consistency layer: eta = 1 where the indicator exceeds it */ real64 getConsistencyTolerance() const { return m_consistencyTolerance; } @@ -123,10 +121,10 @@ class MixedMimeticDiscretization : public dataRepository::Group /// type of inner product used in the mixed mimetic solver string m_innerProductType; - /// flag enabling the consistency layer of the Global Adaptation (1 = adaptive, 0 = selected inner product everywhere) + /// flag enabling the consistency layer (1 = adaptive, 0 = selected inner product everywhere) integer m_adaptiveConsistency; - /// user-prescribed tolerance on the consistency indicator + /// tolerance of the consistency layer real64 m_consistencyTolerance; /// nominal gradient inducing the projected admissible flow field diff --git a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp index 105152e76cf..3e8bfcb4830 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp @@ -72,7 +72,7 @@ DECLARE_FIELD( consistencyIndicator, 0, NOPLOT, WRITE_AND_READ, - "Residual-based consistency indicator per cell (Global Adaptation)" ); + "Residual-based consistency indicator per cell" ); DECLARE_FIELD( degeneracyIndicator, "degeneracyIndicator", @@ -113,7 +113,7 @@ DECLARE_FIELD( faceResidual, 0, LEVEL_1, NO_WRITE, - "Global Adaptation face-assembled normalized residual" ); + "Face-assembled normalized residual of the consistency layer" ); } diff --git a/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp b/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp new file mode 100644 index 00000000000..93a8457ab62 --- /dev/null +++ b/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp @@ -0,0 +1,288 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file ConsistencyAdaptation.cpp + */ + +#include "ConsistencyAdaptation.hpp" + +#include "mesh/MeshLevel.hpp" +#include "mesh/mpiCommunications/CommunicationTools.hpp" +#include "mixedMimetic/MixedMimeticFields.hpp" +#include "mixedMimetic/consistency/ConsistencyAdaptationKernels.hpp" + +#include +#include + +namespace geos +{ + +using namespace fields; + +ConsistencyAdaptation::Report ConsistencyAdaptation::classify( MeshLevel & mesh, + string_array const & regionNames, + SortedArrayView< localIndex const > const & regionFilter, + PermeabilityAccessor const & permeability, + Parameters const & params, + stdVector< NeighborCommunicator > & neighbors ) +{ + GEOS_MARK_FUNCTION; + + Report report; + ElementRegionManager & elemManager = mesh.getElemManager(); + + // every cell starts with the consistent (MFD) product; the layers below can only refine that choice + elemManager.forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + subRegion.getField< mixedMimetic::mfdFlag >().template setValues< parallelDevicePolicy<> >( 1 ); + report.numCells += subRegion.size() - subRegion.getNumberOfGhosts(); + } ); + + report.numConsistent = params.adaptiveConsistency + ? applyConsistencyLayer( mesh, regionNames, regionFilter, permeability, params, neighbors ) + : report.numCells; + + std::pair< localIndex, localIndex > const prescribed = applyPrescription( mesh, regionNames, neighbors ); + report.numPrescribed0 = prescribed.first; + report.numPrescribed1 = prescribed.second; + + std::pair< localIndex, localIndex > const degenerate = applyDegeneracyLayer( mesh, regionNames, params.degeneracyTolerance, neighbors ); + report.numDegenerate = degenerate.first; + report.numRejected = degenerate.second; + + labelFaces( mesh, regionFilter, params.effectiveTpfa ); + return report; +} + +localIndex ConsistencyAdaptation::applyConsistencyLayer( MeshLevel & mesh, + string_array const & regionNames, + SortedArrayView< localIndex const > const & regionFilter, + PermeabilityAccessor const & permeability, + Parameters const & params, + stdVector< NeighborCommunicator > & neighbors ) +{ + NodeManager const & nodeManager = mesh.getNodeManager(); + FaceManager & faceManager = mesh.getFaceManager(); + ElementRegionManager & elemManager = mesh.getElemManager(); + real64 const (&gradient)[3] = params.nominalGradient; + + arrayView1d< real64 > const faceResidual = faceManager.getField< mixedMimetic::faceResidual >(); + faceResidual.zero(); + + // step 1: projection of the admissible flow field induced by the nominal gradient + array1d< real64 > projFaceFluxArray( faceManager.size() ); + arrayView1d< real64 > const projFaceFlux = projFaceFluxArray.toView(); + + ElementRegionManager::ElementViewAccessor< arrayView2d< real64 const > > const elemCenterAccessor = + elemManager.constructViewAccessor< array2d< real64 >, arrayView2d< real64 const > >( ElementSubRegionBase::viewKeyStruct::elementCenterString() ); + + mixedMimeticKernels::FaceFluxProjectionKernel:: + launch< parallelDevicePolicy<> >( faceManager.size(), + faceManager.elementRegionList(), + faceManager.elementSubRegionList(), + faceManager.elementList(), + regionFilter, + faceManager.faceCenter(), + faceManager.faceNormal(), + faceManager.faceArea(), + elemCenterAccessor.toNestedViewConst(), + permeability, + gradient, + params.lengthTolerance, + projFaceFlux ); + + // steps 2-3: localized normalized residuals, assembled on the global face orientation + elemManager.forElementSubRegionsComplete< CellElementSubRegion >( regionNames, + [&]( localIndex const, + localIndex const er, + localIndex const esr, + ElementRegionBase &, + CellElementSubRegion const & subRegion ) + { + mixedMimeticKernels::internal::kernelLaunchSelectorFaceSwitch( subRegion.numFacesPerElement(), [&] ( auto NUM_FACES ) + { + mixedMimeticKernels::LocalResidualKernel< NUM_FACES >:: + template launch< parallelDevicePolicy<> >( subRegion.size(), + nodeManager.referencePosition(), + faceManager.nodeList().toViewConst(), + subRegion.faceList().toViewConst(), + subRegion.getElementCenter(), + subRegion.getElementVolume(), + permeability[er][esr], + faceManager.faceCenter(), + faceManager.faceNormal(), + projFaceFlux.toViewConst(), + gradient, + params.lengthTolerance, + faceResidual ); + } ); + } ); + + // step 4: thresholding + localIndex numConsistent = 0; + elemManager.forElementSubRegions< CellElementSubRegion >( regionNames, + [&]( localIndex const, + CellElementSubRegion & subRegion ) + { + arrayView1d< real64 > const consistencyIndicator = subRegion.getField< mixedMimetic::consistencyIndicator >(); + arrayView1d< integer > const mfdFlag = subRegion.getField< mixedMimetic::mfdFlag >(); + + mixedMimeticKernels::internal::kernelLaunchSelectorFaceSwitch( subRegion.numFacesPerElement(), [&] ( auto NUM_FACES ) + { + numConsistent += mixedMimeticKernels::MarkingKernel< NUM_FACES >:: + template launch< parallelDevicePolicy<> >( subRegion.size(), + subRegion.faceList().toViewConst(), + subRegion.ghostRank(), + faceResidual.toViewConst(), + params.consistencyTolerance, + consistencyIndicator, + mfdFlag ); + } ); + } ); + + FieldIdentifiers fieldsToBeSync; + fieldsToBeSync.addElementFields( { mixedMimetic::mfdFlag::key(), mixedMimetic::consistencyIndicator::key() }, regionNames ); + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, neighbors, false ); + return numConsistent; +} + +std::pair< localIndex, localIndex > ConsistencyAdaptation::applyPrescription( MeshLevel & mesh, + string_array const & regionNames, + stdVector< NeighborCommunicator > & neighbors ) +{ + FieldIdentifiers prescribedToBeSync; + prescribedToBeSync.addElementFields( { mixedMimetic::prescribedMfdFlag::key() }, regionNames ); + CommunicationTools::getInstance().synchronizeFields( prescribedToBeSync, mesh, neighbors, false ); + + localIndex numPrescribed0 = 0; + localIndex numPrescribed1 = 0; + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + arrayView1d< real64 const > const prescribed = subRegion.getField< mixedMimetic::prescribedMfdFlag >(); + arrayView1d< integer > const mfdFlag = subRegion.getField< mixedMimetic::mfdFlag >(); + arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + if( prescribed[ei] < 0.0 ) + { + continue; + } + integer const eta = prescribed[ei] > 0.5 ? 1 : 0; + mfdFlag[ei] = eta; + if( ghostRank[ei] < 0 ) + { + numPrescribed0 += ( eta == 0 ); + numPrescribed1 += ( eta == 1 ); + } + } + } ); + return { numPrescribed0, numPrescribed1 }; +} + +std::pair< localIndex, localIndex > ConsistencyAdaptation::applyDegeneracyLayer( MeshLevel & mesh, + string_array const & regionNames, + real64 const tolerance, + stdVector< NeighborCommunicator > & neighbors ) +{ + NodeManager const & nodeManager = mesh.getNodeManager(); + ElementRegionManager & elemManager = mesh.getElemManager(); + ArrayOfArraysView< localIndex const > const nodeToRegion = nodeManager.elementRegionList(); + ArrayOfArraysView< localIndex const > const nodeToSubRegion = nodeManager.elementSubRegionList(); + ArrayOfArraysView< localIndex const > const nodeToElem = nodeManager.elementList(); + ElementRegionManager::ElementViewAccessor< arrayView1d< real64 const > > const elemVolume = + elemManager.constructArrayViewAccessor< real64, 1 >( ElementSubRegionBase::viewKeyStruct::elementVolumeString() ); + + localIndex numDegenerate = 0; + localIndex numRejected = 0; + elemManager.forElementSubRegions< CellElementSubRegion >( regionNames, + [&]( localIndex const, + CellElementSubRegion & subRegion ) + { + arrayView2d< localIndex const, cells::NODE_MAP_USD > const elemToNodes = subRegion.nodeList().toViewConst(); + arrayView1d< real64 const > const volume = subRegion.getElementVolume(); + arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + arrayView1d< real64 > const indicator = subRegion.getField< mixedMimetic::degeneracyIndicator >(); + arrayView1d< integer > const mfdFlag = subRegion.getField< mixedMimetic::mfdFlag >(); + arrayView1d< real64 const > const prescribed = subRegion.getField< mixedMimetic::prescribedMfdFlag >(); + localIndex const numNodes = subRegion.numNodesPerElement(); + + // the node star of a cell: every cell sharing a vertex with it, counted once + stdVector< std::array< localIndex, 3 > > star; + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + star.clear(); + for( localIndex a = 0; a < numNodes; ++a ) + { + localIndex const n = elemToNodes( ei, a ); + for( localIndex k = 0; k < nodeToElem.sizeOfArray( n ); ++k ) + { + if( nodeToRegion( n, k ) >= 0 && nodeToSubRegion( n, k ) >= 0 && nodeToElem( n, k ) >= 0 ) + { + star.push_back( { nodeToRegion( n, k ), nodeToSubRegion( n, k ), nodeToElem( n, k ) } ); + } + } + } + std::sort( star.begin(), star.end() ); + star.erase( std::unique( star.begin(), star.end() ), star.end() ); + real64 sum = 0.0; + for( auto const & c : star ) + { + sum += elemVolume[c[0]][c[1]][c[2]]; + } + // share of the cell in the volume of its node star, in percent + real64 const percent = sum > 0.0 ? 100.0 * volume[ei] / sum : 0.0; + indicator[ei] = percent; + if( percent < tolerance && mfdFlag[ei] == 1 ) + { + mfdFlag[ei] = 0; + numDegenerate += ( ghostRank[ei] < 0 ) ? 1 : 0; + numRejected += ( ghostRank[ei] < 0 && prescribed[ei] > 0.5 ) ? 1 : 0; + } + } + } ); + + FieldIdentifiers fieldsToBeSync; + fieldsToBeSync.addElementFields( { mixedMimetic::mfdFlag::key(), mixedMimetic::degeneracyIndicator::key() }, regionNames ); + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, neighbors, false ); + return { numDegenerate, numRejected }; +} + +void ConsistencyAdaptation::labelFaces( MeshLevel & mesh, + SortedArrayView< localIndex const > const & regionFilter, + bool const effectiveTpfa ) +{ + FaceManager & faceManager = mesh.getFaceManager(); + ElementRegionManager & elemManager = mesh.getElemManager(); + + ElementRegionManager::ElementViewAccessor< arrayView1d< integer const > > const mfdFlagAccessor = + elemManager.constructArrayViewAccessor< integer, 1 >( mixedMimetic::mfdFlag::key() ); + + mixedMimeticKernels::FaceLabelKernel:: + launch< parallelDevicePolicy<> >( faceManager.size(), + faceManager.elementRegionList(), + faceManager.elementSubRegionList(), + faceManager.elementList(), + regionFilter, + mfdFlagAccessor.toNestedViewConst(), + effectiveTpfa, + faceManager.getField< mixedMimetic::faceStencilLabel >() ); +} + +} // namespace geos diff --git a/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.hpp b/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.hpp new file mode 100644 index 00000000000..690f7c7713f --- /dev/null +++ b/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.hpp @@ -0,0 +1,144 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file ConsistencyAdaptation.hpp + */ + +#ifndef GEOS_MIXEDMIMETIC_CONSISTENCY_CONSISTENCYADAPTATION_HPP_ +#define GEOS_MIXEDMIMETIC_CONSISTENCY_CONSISTENCYADAPTATION_HPP_ + +#include "common/DataTypes.hpp" +#include "mesh/ElementRegionManager.hpp" + +#include + +namespace geos +{ + +class MeshLevel; +class NeighborCommunicator; + +/** + * @class ConsistencyAdaptation + * @brief Selects the inner product of every cell of the mixed mimetic discretization, eta = 1 for the + * consistent (MFD) product and eta = 0 for the diagonal (TPFA) product, through three layers + * applied in order: the residual-based consistency layer, the prescription read from the mesh + * and the degeneracy layer (admissibility of the consistent product, the only one allowed to + * override a prescription). The faces are then labelled: 0 when both cells use the diagonal + * product (the flux dof is condensed), 1 otherwise (the flux dof stays in the saddle point). + */ +class ConsistencyAdaptation +{ +public: + + /// Parameters of the three layers + struct Parameters + { + bool adaptiveConsistency = true; ///< run the consistency layer + real64 consistencyTolerance = 1e-3; ///< eta = 1 where the consistency indicator exceeds it + real64 degeneracyTolerance = 0.1; ///< eta = 0 below this percentage of the node-star volume + real64 nominalGradient[3] = { 1.0, 1.0, 1.0 }; ///< gradient of the probe pressure field + real64 lengthTolerance = 0.0; ///< geometric tolerance of the face computations + bool effectiveTpfa = false; ///< the selected product is itself TPFA: every face is condensed + }; + + /// Locally-owned cell counts of one classification (reduce over the ranks to report) + struct Report + { + localIndex numCells = 0; ///< cells of the target regions + localIndex numConsistent = 0; ///< cells with eta = 1 after the consistency layer + localIndex numPrescribed0 = 0; ///< cells prescribed the diagonal product + localIndex numPrescribed1 = 0; ///< cells prescribed the consistent product + localIndex numDegenerate = 0; ///< cells switched to the diagonal product by the degeneracy layer + localIndex numRejected = 0; ///< prescribed consistent products rejected by the degeneracy layer + + /** + * @brief Accumulate the counts of another mesh level. + * @param other the counts to add + */ + void add( Report const & other ) + { + numCells += other.numCells; + numConsistent += other.numConsistent; + numPrescribed0 += other.numPrescribed0; + numPrescribed1 += other.numPrescribed1; + numDegenerate += other.numDegenerate; + numRejected += other.numRejected; + } + }; + + /// Cell permeability by region and subregion + using PermeabilityAccessor = ElementRegionManager::ElementViewConst< arrayView3d< real64 const > >; + + /** + * @brief Run the three layers on the cells of the target regions and label the faces. + * Collective over the ranks: the fields are synchronized on the ghost cells. + * @param mesh the mesh level + * @param regionNames the target regions + * @param regionFilter the target region indices + * @param permeability the cell permeability + * @param params the parameters of the layers + * @param neighbors the neighbor communicators of the domain + * @return the locally-owned cell counts + */ + static Report classify( MeshLevel & mesh, + string_array const & regionNames, + SortedArrayView< localIndex const > const & regionFilter, + PermeabilityAccessor const & permeability, + Parameters const & params, + stdVector< NeighborCommunicator > & neighbors ); + +private: + + /** + * @brief Consistency layer: eta = 0 where the two-point product reproduces the probe field within tolerance. + * @return the locally-owned cells left with eta = 1 + */ + static localIndex applyConsistencyLayer( MeshLevel & mesh, + string_array const & regionNames, + SortedArrayView< localIndex const > const & regionFilter, + PermeabilityAccessor const & permeability, + Parameters const & params, + stdVector< NeighborCommunicator > & neighbors ); + + /** + * @brief Prescription layer: eta follows prescribedMfdFlag where it is not negative. + * @return the locally-owned cells prescribed 0 and prescribed 1 + */ + static std::pair< localIndex, localIndex > applyPrescription( MeshLevel & mesh, + string_array const & regionNames, + stdVector< NeighborCommunicator > & neighbors ); + + /** + * @brief Degeneracy layer: eta = 0 where the cell volume is below the tolerance (percent of its node star). + * @return the locally-owned cells switched, and how many of them were prescribed eta = 1 + */ + static std::pair< localIndex, localIndex > applyDegeneracyLayer( MeshLevel & mesh, + string_array const & regionNames, + real64 const tolerance, + stdVector< NeighborCommunicator > & neighbors ); + + /** + * @brief Label the faces from eta: 0 when both cells use the diagonal product, 1 otherwise. + */ + static void labelFaces( MeshLevel & mesh, + SortedArrayView< localIndex const > const & regionFilter, + bool const effectiveTpfa ); +}; + +} // namespace geos + +#endif /* GEOS_MIXEDMIMETIC_CONSISTENCY_CONSISTENCYADAPTATION_HPP_ */ diff --git a/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp b/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptationKernels.hpp similarity index 96% rename from src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp rename to src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptationKernels.hpp index b3aaefb321e..bc5513c752d 100644 --- a/src/coreComponents/mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp +++ b/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptationKernels.hpp @@ -14,21 +14,21 @@ */ /** - * @file GlobalAdaptationKernels.hpp + * @file ConsistencyAdaptationKernels.hpp * - * Residual-based Global Adaptation (GA) indicators for the mixed mimetic discretization. + * Kernels of the consistency layer: residual-based indicator of the two-point product per cell. * The construction follows four steps: * 1) projection of an admissible flow field induced by a nominal uniform gradient, * using the harmonic face-normal diffusive projection across interfaces; * 2) evaluation of the localized (cell-wise) TPFA constitutive residual, normalized * by the local pressure drop profile; * 3) assembly of the normalized residuals across shared interfaces with respect to - * a fixed global orientation of the face normal (Global Adaptation); + * a fixed global orientation of the face normal (assembled on the faces); * 4) thresholding to produce the binary stencil activation flag per cell. */ -#ifndef GEOS_MIXEDMIMETIC_ADAPTIVITY_GLOBALADAPTATIONKERNELS_HPP_ -#define GEOS_MIXEDMIMETIC_ADAPTIVITY_GLOBALADAPTATIONKERNELS_HPP_ +#ifndef GEOS_MIXEDMIMETIC_CONSISTENCY_CONSISTENCYADAPTATIONKERNELS_HPP_ +#define GEOS_MIXEDMIMETIC_CONSISTENCY_CONSISTENCYADAPTATIONKERNELS_HPP_ #include "common/DataTypes.hpp" #include "common/GEOS_RAJA_Interface.hpp" @@ -369,4 +369,4 @@ struct MarkingKernel } // namespace geos -#endif //GEOS_MIXEDMIMETIC_ADAPTIVITY_GLOBALADAPTATIONKERNELS_HPP_ +#endif //GEOS_MIXEDMIMETIC_CONSISTENCY_CONSISTENCYADAPTATIONKERNELS_HPP_ diff --git a/src/coreComponents/mixedMimetic/unitTests/CMakeLists.txt b/src/coreComponents/mixedMimetic/unitTests/CMakeLists.txt new file mode 100644 index 00000000000..5c681f4068c --- /dev/null +++ b/src/coreComponents/mixedMimetic/unitTests/CMakeLists.txt @@ -0,0 +1,27 @@ +#[[ +Package: mixedMimetic unitTests +Builds the unit tests of the cell classification (ConsistencyAdaptation) +#]] + +set( mixedMimetic_gtest_tests + testConsistencyAdaptation.cpp ) + +set( dependencyList mainInterface gtest ${parallelDeps} ) + +foreach( test_source ${mixedMimetic_gtest_tests} ) + get_filename_component( test_name ${test_source} NAME_WE ) + blt_add_executable( NAME ${test_name} + SOURCES ${test_source} + OUTPUT_DIR ${TEST_OUTPUT_DIRECTORY} + DEPENDS_ON ${dependencyList} + FOLDER UnitTests ) + geos_add_test( NAME ${test_name} + COMMAND ${test_name} ) + + if( ENABLE_MPI AND NOT ENABLE_CUDA AND NOT ENABLE_HIP ) + set( nranks 2 ) + geos_add_test( NAME ${test_name}_mpi + COMMAND ${test_name} -x ${nranks} + NUM_MPI_TASKS ${nranks} ) + endif() +endforeach() diff --git a/src/coreComponents/mixedMimetic/unitTests/testConsistencyAdaptation.cpp b/src/coreComponents/mixedMimetic/unitTests/testConsistencyAdaptation.cpp new file mode 100644 index 00000000000..d42acbf0757 --- /dev/null +++ b/src/coreComponents/mixedMimetic/unitTests/testConsistencyAdaptation.cpp @@ -0,0 +1,378 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +// Source includes +#include "codingUtilities/UnitTestUtilities.hpp" +#include "common/MpiWrapper.hpp" +#include "constitutive/ConstitutiveManager.hpp" +#include "constitutive/permeability/PermeabilityBase.hpp" +#include "constitutive/permeability/PermeabilityFields.hpp" +#include "dataRepository/xmlWrapper.hpp" +#include "mainInterface/GeosxState.hpp" +#include "mainInterface/ProblemManager.hpp" +#include "mainInterface/initialization.hpp" +#include "mesh/DomainPartition.hpp" +#include "mesh/MeshBody.hpp" +#include "mesh/MeshManager.hpp" +#include "mixedMimetic/MixedMimeticFields.hpp" +#include "mixedMimetic/consistency/ConsistencyAdaptation.hpp" + +// TPL includes +#include + +using namespace geos; +using namespace geos::dataRepository; +using namespace geos::testing; + +CommandLineOptions g_commandLineOptions; + +// mixed MFD problem on an InternalMesh: the layers are then run directly with chosen parameters +static string makeInput( string const & elementType, + string const & xCoords, + string const & nx, + string const & cellBlocks, + string const & prescriptions ) +{ + return GEOS_FMT( R"xml( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {} + + + + +)xml", elementType, xCoords, nx, cellBlocks, + prescriptions ); +} + +static string const prescriptions = + R"xml( + )xml"; + +static string const noPrescription; + +// Cartesian hexahedra: K-orthogonal, the two-point product is consistent everywhere +static string const cartesianHexa = makeInput( "C3D8", "0, 1", "10", "cb1", noPrescription ); +// tetrahedra: the two-point product is not consistent in any cell +static string const tetrahedra = makeInput( "C3D4", "0, 1", "4", "cb1", noPrescription ); +// a 1e-3 thick layer of 4 cells (0.2475 % of their node star) in front of 40 unit-height cells +static string const thinLayer = makeInput( "C3D8", "0, 1e-3, 1", "1, 10", "cb1, cb2", noPrescription ); +static string const thinLayerPrescribed = makeInput( "C3D8", "0, 1e-3, 1", "1, 10", "cb1, cb2", prescriptions ); + +static void setupProblemFromXML( ProblemManager & problemManager, string const & xmlInput ) +{ + xmlWrapper::xmlDocument xmlDocument; + xmlWrapper::xmlResult const xmlResult = xmlDocument.loadString( xmlInput ); + ASSERT_TRUE( xmlResult ) << xmlResult.description(); + + Group & commandLine = problemManager.getGroup< Group >( problemManager.groupKeys.commandLine ); + commandLine.registerWrapper< integer >( problemManager.viewKeys.xPartitionsOverride.key() ). + setApplyDefaultValue( MpiWrapper::commSize( MPI_COMM_GEOS ) ); + + xmlWrapper::xmlNode xmlProblemNode = xmlDocument.getChild( keys::ProblemManager ); + problemManager.processInputFileRecursive( xmlDocument, xmlProblemNode ); + + DomainPartition & domain = problemManager.getDomainPartition(); + constitutive::ConstitutiveManager & constitutiveManager = domain.getConstitutiveManager(); + xmlWrapper::xmlNode topLevelNode = xmlProblemNode.child( constitutiveManager.getName().c_str() ); + constitutiveManager.processInputFileRecursive( xmlDocument, topLevelNode ); + + MeshManager & meshManager = problemManager.getGroup< MeshManager >( problemManager.groupKeys.meshManager ); + meshManager.generateMeshLevels( domain ); + + ElementRegionManager & elementManager = domain.getMeshBody( 0 ).getBaseDiscretization().getElemManager(); + topLevelNode = xmlProblemNode.child( elementManager.getName().c_str() ); + elementManager.processInputFileRecursive( xmlDocument, topLevelNode ); + + problemManager.problemSetup(); + problemManager.applyInitialConditions(); +} + +class ConsistencyAdaptationTest +{ +public: + + explicit ConsistencyAdaptationTest( string const & xml ) + : m_state( std::make_unique< CommandLineOptions >( g_commandLineOptions ) ) + { + setupProblemFromXML( m_state.getProblemManager(), xml ); + m_regionNames.emplace_back( "Domain" ); + m_regionFilter.insert( elemManager().getRegions().getIndex( "Domain" ) ); + } + + DomainPartition & domain() { return m_state.getProblemManager().getDomainPartition(); } + MeshLevel & mesh() { return domain().getMeshBody( 0 ).getBaseDiscretization(); } + ElementRegionManager & elemManager() { return mesh().getElemManager(); } + + /// run the layers and reduce the counts over the ranks + ConsistencyAdaptation::Report classify( ConsistencyAdaptation::Parameters const & params ) + { + ElementRegionManager::ElementViewAccessor< arrayView3d< real64 const > > const permeability = + elemManager().constructMaterialArrayViewAccessor< constitutive::PermeabilityBase, real64, 3 >( fields::permeability::permeability::key() ); + ConsistencyAdaptation::Report r = ConsistencyAdaptation::classify( mesh(), m_regionNames, m_regionFilter.toViewConst(), + permeability.toNestedViewConst(), params, domain().getNeighbors() ); + r.numCells = MpiWrapper::sum( r.numCells ); + r.numConsistent = MpiWrapper::sum( r.numConsistent ); + r.numPrescribed0 = MpiWrapper::sum( r.numPrescribed0 ); + r.numPrescribed1 = MpiWrapper::sum( r.numPrescribed1 ); + r.numDegenerate = MpiWrapper::sum( r.numDegenerate ); + r.numRejected = MpiWrapper::sum( r.numRejected ); + return r; + } + + /// number of owned cells with eta = value + localIndex countFlag( integer const value ) + { + localIndex count = 0; + elemManager().forElementSubRegions< ElementSubRegionBase >( m_regionNames, [&]( localIndex const, ElementSubRegionBase & subRegion ) + { + arrayView1d< integer const > const mfdFlag = subRegion.getField< fields::mixedMimetic::mfdFlag >(); + arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + count += ( ghostRank[ei] < 0 && mfdFlag[ei] == value ) ? 1 : 0; + } + } ); + return MpiWrapper::sum( count ); + } + + /// number of owned cells whose degeneracy indicator is below the percentage + localIndex countDegeneracyBelow( real64 const percent ) + { + localIndex count = 0; + elemManager().forElementSubRegions< ElementSubRegionBase >( m_regionNames, [&]( localIndex const, ElementSubRegionBase & subRegion ) + { + arrayView1d< real64 const > const indicator = subRegion.getField< fields::mixedMimetic::degeneracyIndicator >(); + arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + count += ( ghostRank[ei] < 0 && indicator[ei] < percent ) ? 1 : 0; + } + } ); + return MpiWrapper::sum( count ); + } + + /// check label_f = max of eta over the cells of f, and return the number of owned faces with label 1 + localIndex checkFaceLabels() + { + FaceManager const & faceManager = mesh().getFaceManager(); + arrayView2d< localIndex const > const elemRegionList = faceManager.elementRegionList(); + arrayView2d< localIndex const > const elemSubRegionList = faceManager.elementSubRegionList(); + arrayView2d< localIndex const > const elemList = faceManager.elementList(); + arrayView1d< integer const > const label = faceManager.getField< fields::mixedMimetic::faceStencilLabel >(); + arrayView1d< integer const > const ghostRank = faceManager.ghostRank(); + ElementRegionManager::ElementViewAccessor< arrayView1d< integer const > > const mfdFlag = + elemManager().constructArrayViewAccessor< integer, 1 >( fields::mixedMimetic::mfdFlag::key() ); + + localIndex count = 0; + for( localIndex kf = 0; kf < faceManager.size(); ++kf ) + { + integer expected = 0; + for( localIndex k = 0; k < elemRegionList.size( 1 ); ++k ) + { + localIndex const er = elemRegionList( kf, k ); + localIndex const esr = elemSubRegionList( kf, k ); + localIndex const ei = elemList( kf, k ); + if( er >= 0 && esr >= 0 && ei >= 0 && m_regionFilter.contains( er ) ) + { + expected = std::max( expected, mfdFlag[er][esr][ei] ); + } + } + EXPECT_EQ( label[kf], expected ) << "face " << kf; + count += ( ghostRank[kf] < 0 && label[kf] == 1 ) ? 1 : 0; + } + return MpiWrapper::sum( count ); + } + +private: + + GeosxState m_state; + string_array m_regionNames; + SortedArray< localIndex > m_regionFilter; +}; + +TEST( ConsistencyAdaptation, ConsistencyLayer_CartesianHexahedra ) +{ + ConsistencyAdaptationTest test( cartesianHexa ); + ConsistencyAdaptation::Parameters params; + + // the two-point product is consistent on K-orthogonal cells: eta = 0 everywhere (up to round-off) + params.consistencyTolerance = 1e-10; + ConsistencyAdaptation::Report r = test.classify( params ); + EXPECT_EQ( r.numCells, 40 ); + EXPECT_EQ( r.numConsistent, 0 ); + EXPECT_EQ( test.countFlag( 1 ), 0 ); + EXPECT_EQ( test.checkFaceLabels(), 0 ); + + // without the layer the selected product is used everywhere + params.adaptiveConsistency = false; + r = test.classify( params ); + EXPECT_EQ( r.numConsistent, 40 ); + EXPECT_EQ( test.countFlag( 1 ), 40 ); + EXPECT_EQ( test.checkFaceLabels(), 164 ); + + // a TPFA discretization condenses every face whatever eta + params.effectiveTpfa = true; + test.classify( params ); + EXPECT_EQ( test.countFlag( 1 ), 40 ); + FaceManager const & faceManager = test.mesh().getFaceManager(); + arrayView1d< integer const > const label = faceManager.getField< fields::mixedMimetic::faceStencilLabel >(); + for( localIndex kf = 0; kf < faceManager.size(); ++kf ) + { + EXPECT_EQ( label[kf], 0 ); + } +} + +TEST( ConsistencyAdaptation, ConsistencyLayer_Tetrahedra ) +{ + ConsistencyAdaptationTest test( tetrahedra ); + ConsistencyAdaptation::Parameters params; + + // the two-point product is not consistent on any simplex + params.consistencyTolerance = 1e-12; + ConsistencyAdaptation::Report r = test.classify( params ); + EXPECT_EQ( r.numCells, 96 ); + EXPECT_EQ( r.numConsistent, 96 ); + EXPECT_EQ( test.countFlag( 1 ), 96 ); + EXPECT_EQ( test.checkFaceLabels(), MpiWrapper::sum( [&] + { + arrayView1d< integer const > const ghostRank = test.mesh().getFaceManager().ghostRank(); + localIndex owned = 0; + for( localIndex kf = 0; kf < ghostRank.size(); ++kf ) + { + owned += ( ghostRank[kf] < 0 ) ? 1 : 0; + } + return owned; + }() ) ); + + // the number of consistent cells is non-increasing with the tolerance + localIndex previous = 96; + for( real64 const tolerance : { 1e-3, 1e-1, 1.0, 1e+20 } ) + { + params.consistencyTolerance = tolerance; + r = test.classify( params ); + EXPECT_LE( r.numConsistent, previous ) << "tolerance = " << tolerance; + EXPECT_EQ( r.numConsistent, test.countFlag( 1 ) ); + test.checkFaceLabels(); + previous = r.numConsistent; + } + EXPECT_EQ( previous, 0 ); +} + +TEST( ConsistencyAdaptation, DegeneracyLayer_ThinLayer ) +{ + ConsistencyAdaptationTest test( thinLayer ); + ConsistencyAdaptation::Parameters params; + params.adaptiveConsistency = false; + + // the 4 thin cells are 0.2475 % of their node star + params.degeneracyTolerance = 0.0; + ConsistencyAdaptation::Report r = test.classify( params ); + EXPECT_EQ( r.numCells, 44 ); + EXPECT_EQ( r.numDegenerate, 0 ); + EXPECT_EQ( test.countFlag( 1 ), 44 ); + EXPECT_EQ( test.countDegeneracyBelow( 0.2 ), 0 ); + EXPECT_EQ( test.countDegeneracyBelow( 0.3 ), 4 ); + EXPECT_EQ( test.countDegeneracyBelow( 100.0 ), 44 ); + + params.degeneracyTolerance = 0.2; + r = test.classify( params ); + EXPECT_EQ( r.numDegenerate, 0 ); + EXPECT_EQ( test.countFlag( 1 ), 44 ); + + // the 16 faces of the thin block not shared with a thick cell are condensed + params.degeneracyTolerance = 1.0; + r = test.classify( params ); + EXPECT_EQ( r.numDegenerate, 4 ); + EXPECT_EQ( r.numRejected, 0 ); + EXPECT_EQ( test.countFlag( 1 ), 40 ); + EXPECT_EQ( test.checkFaceLabels(), 180 - 16 ); +} + +TEST( ConsistencyAdaptation, Prescription_ThinLayer ) +{ + ConsistencyAdaptationTest test( thinLayerPrescribed ); + ConsistencyAdaptation::Parameters params; + + // the prescription overrides the consistency layer (eta = 0 everywhere on this mesh) + params.consistencyTolerance = 1e-10; + params.degeneracyTolerance = 0.0; + ConsistencyAdaptation::Report r = test.classify( params ); + EXPECT_EQ( r.numConsistent, 0 ); + EXPECT_EQ( r.numPrescribed1, 4 ); + EXPECT_EQ( r.numPrescribed0, 4 ); + EXPECT_EQ( r.numDegenerate, 0 ); + EXPECT_EQ( test.countFlag( 1 ), 4 ); + EXPECT_EQ( test.checkFaceLabels(), 20 ); + + // and the selected product everywhere: the 4 east cells are prescribed the diagonal product, + // the 16 faces of the east block not shared with the previous column are condensed + params.adaptiveConsistency = false; + r = test.classify( params ); + EXPECT_EQ( test.countFlag( 0 ), 4 ); + EXPECT_EQ( test.checkFaceLabels(), 180 - 16 ); + + // only the degeneracy layer rejects a prescribed consistent product + params.adaptiveConsistency = true; + params.degeneracyTolerance = 1.0; + r = test.classify( params ); + EXPECT_EQ( r.numPrescribed1, 4 ); + EXPECT_EQ( r.numDegenerate, 4 ); + EXPECT_EQ( r.numRejected, 4 ); + EXPECT_EQ( test.countFlag( 1 ), 0 ); + EXPECT_EQ( test.checkFaceLabels(), 0 ); +} + +int main( int argc, char * * argv ) +{ + ::testing::InitGoogleTest( &argc, argv ); + g_commandLineOptions = *geos::basicSetup( argc, argv ); + int const result = RUN_ALL_TESTS(); + geos::basicCleanup(); + return result; +} diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index af0fc540874..8b4d9df7788 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -19,9 +19,7 @@ #include "SinglePhaseMixedMFD.hpp" -#include #include -#include #include "common/logger/Logger.hpp" #include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" @@ -35,7 +33,7 @@ #include "mixedMimetic/MixedMimeticDiscretization.hpp" #include "mixedMimetic/MixedMimeticDiscretizationManager.hpp" #include "mixedMimetic/MixedMimeticFields.hpp" -#include "mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp" +#include "mixedMimetic/consistency/ConsistencyAdaptation.hpp" #include "physicsSolvers/LogLevelsInfo.hpp" #include "physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp" #include "physicsSolvers/fluidFlow/kernels/singlePhase/ResidualNormKernel.hpp" @@ -89,7 +87,7 @@ void SinglePhaseMixedMFD::registerDataOnMesh( Group & meshBodies ) faceManager.registerField< flow::bcPressure >( getName() ); faceManager.registerField< flow::isBoundaryFace >( getName() ); - // Global Adaptation face residual + // face residual of the consistency layer faceManager.registerField< mixedMimetic::faceResidual >( getName() ); // face classification driving the TPFA-face condensation and the MGR labels @@ -172,8 +170,7 @@ void SinglePhaseMixedMFD::initializePostInitialConditionsPreSubGroups() computeFaceOrientation( domain ); - // run the residual-based Global Adaptation pipeline (or activate the selected inner product everywhere) - computeGlobalAdaptationIndicators( domain ); + classifyCells( domain ); } void SinglePhaseMixedMFD::computeFaceOrientation( DomainPartition & domain ) @@ -218,7 +215,7 @@ void SinglePhaseMixedMFD::computeFaceOrientation( DomainPartition & domain ) } ); } -void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & domain ) +void SinglePhaseMixedMFD::classifyCells( DomainPartition & domain ) { GEOS_MARK_FUNCTION; @@ -226,296 +223,45 @@ void SinglePhaseMixedMFD::computeGlobalAdaptationIndicators( DomainPartition & d MixedMimeticDiscretizationManager const & mmManager = numericalMethodManager.getMixedMimeticDiscretizationManager(); MixedMimeticDiscretization const & discretization = mmManager.getMixedMimeticDiscretization( m_discretizationName ); - // every cell starts with the consistent (MFD) product; the layers below can only refine that choice - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) + ConsistencyAdaptation::Parameters params; + params.adaptiveConsistency = discretization.isAdaptiveConsistency(); + params.consistencyTolerance = discretization.getConsistencyTolerance(); + params.degeneracyTolerance = discretization.getDegeneracyTolerance(); + params.lengthTolerance = domain.getMeshBody( 0 ).getGlobalLengthScale() * m_areaRelTol; + params.effectiveTpfa = discretization.isTpfaInnerProduct(); + R1Tensor const gradient = discretization.getNominalGradient(); + for( int d = 0; d < 3; ++d ) { - mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, - [&]( localIndex const, - ElementSubRegionBase & subRegion ) - { - subRegion.getField< mixedMimetic::mfdFlag >().template setValues< parallelDevicePolicy<> >( 1 ); - } ); - } ); - - if( !discretization.isAdaptiveConsistency() ) - { - applyLayersAndLabel( domain ); - return; + params.nominalGradient[d] = gradient[d]; } - real64 const lengthTolerance = domain.getMeshBody( 0 ).getGlobalLengthScale() * m_areaRelTol; - real64 const tolerance = discretization.getConsistencyTolerance(); - R1Tensor const gradientInput = discretization.getNominalGradient(); - real64 const gradient[3] = { gradientInput[0], gradientInput[1], gradientInput[2] }; - + ConsistencyAdaptation::Report report; forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, MeshLevel & mesh, string_array const & regionNames ) { - NodeManager const & nodeManager = mesh.getNodeManager(); - FaceManager & faceManager = mesh.getFaceManager(); - ElementRegionManager & elemManager = mesh.getElemManager(); - - arrayView1d< real64 > const faceResidual = faceManager.getField< mixedMimetic::faceResidual >(); - faceResidual.zero(); - - // step 1: projection of the admissible flow field induced by the nominal gradient - array1d< real64 > projFaceFluxArray( faceManager.size() ); - arrayView1d< real64 > const projFaceFlux = projFaceFluxArray.toView(); - - ElementRegionManager::ElementViewAccessor< arrayView2d< real64 const > > const elemCenterAccessor = - elemManager.constructViewAccessor< array2d< real64 >, arrayView2d< real64 const > >( ElementSubRegionBase::viewKeyStruct::elementCenterString() ); - using PermeabilityAccessors = StencilMaterialAccessors< PermeabilityBase, fields::permeability::permeability >; - PermeabilityAccessors const permAccessors( elemManager, getName() ); - - mixedMimeticKernels::FaceFluxProjectionKernel:: - launch< parallelDevicePolicy<> >( faceManager.size(), - faceManager.elementRegionList(), - faceManager.elementSubRegionList(), - faceManager.elementList(), - m_regionFilter.toViewConst(), - faceManager.faceCenter(), - faceManager.faceNormal(), - faceManager.faceArea(), - elemCenterAccessor.toNestedViewConst(), - permAccessors.get( fields::permeability::permeability {} ), - gradient, - lengthTolerance, - projFaceFlux ); - - // steps 2-3: localized normalized residuals, assembled on the global face orientation - elemManager.forElementSubRegions< CellElementSubRegion >( regionNames, - [&]( localIndex const, - CellElementSubRegion const & subRegion ) - { - string const & permName = subRegion.getReference< string >( viewKeyStruct::permeabilityNamesString() ); - PermeabilityBase const & permeability = getConstitutiveModel< PermeabilityBase >( subRegion, permName ); - - mixedMimeticKernels::internal::kernelLaunchSelectorFaceSwitch( subRegion.numFacesPerElement(), [&] ( auto NUM_FACES ) - { - mixedMimeticKernels::LocalResidualKernel< NUM_FACES >:: - template launch< parallelDevicePolicy<> >( subRegion.size(), - nodeManager.referencePosition(), - faceManager.nodeList().toViewConst(), - subRegion.faceList().toViewConst(), - subRegion.getElementCenter(), - subRegion.getElementVolume(), - permeability.permeability(), - faceManager.faceCenter(), - faceManager.faceNormal(), - projFaceFlux.toViewConst(), - gradient, - lengthTolerance, - faceResidual ); - } ); - } ); - - // step 4: thresholding - localIndex numMfdCells = 0; - localIndex numCells = 0; - elemManager.forElementSubRegions< CellElementSubRegion >( regionNames, - [&]( localIndex const, - CellElementSubRegion & subRegion ) - { - arrayView1d< real64 > const consistencyIndicator = subRegion.getField< mixedMimetic::consistencyIndicator >(); - arrayView1d< integer > const mfdFlag = subRegion.getField< mixedMimetic::mfdFlag >(); - - mixedMimeticKernels::internal::kernelLaunchSelectorFaceSwitch( subRegion.numFacesPerElement(), [&] ( auto NUM_FACES ) - { - numMfdCells += mixedMimeticKernels::MarkingKernel< NUM_FACES >:: - template launch< parallelDevicePolicy<> >( subRegion.size(), - subRegion.faceList().toViewConst(), - subRegion.ghostRank(), - faceResidual.toViewConst(), - tolerance, - consistencyIndicator, - mfdFlag ); - } ); - numCells += subRegion.size() - subRegion.getNumberOfGhosts(); - } ); - - // make the marking consistent on ghost cells - FieldIdentifiers fieldsToBeSync; - fieldsToBeSync.addElementFields( { mixedMimetic::mfdFlag::key(), mixedMimetic::consistencyIndicator::key() }, regionNames ); - CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), false ); - - globalIndex const globalNumMfdCells = MpiWrapper::sum< globalIndex >( numMfdCells ); - globalIndex const globalNumCells = MpiWrapper::sum< globalIndex >( numCells ); - GEOS_LOG_RANK_0( GEOS_FMT( "mixedMFD Flow: consistency layer (tolerance = {}) set eta = 1 on {} / {} cells", - tolerance, globalNumMfdCells, globalNumCells ) ); + PermeabilityAccessors const permAccessors( mesh.getElemManager(), getName() ); + report.add( ConsistencyAdaptation::classify( mesh, regionNames, m_regionFilter.toViewConst(), + permAccessors.get( fields::permeability::permeability {} ), + params, domain.getNeighbors() ) ); } ); - applyLayersAndLabel( domain ); -} - -void SinglePhaseMixedMFD::applyLayersAndLabel( DomainPartition & domain ) -{ - // prescription, then degeneracy (admissibility, which alone may override the prescription) - std::pair< localIndex, localIndex > const prescribed = applyPrescribedFlag( domain ); - std::pair< localIndex, localIndex > const degenerate = applyDegeneracyLayer( domain ); - globalIndex const numPrescribed0 = MpiWrapper::sum< globalIndex >( prescribed.first ); - globalIndex const numPrescribed1 = MpiWrapper::sum< globalIndex >( prescribed.second ); - globalIndex const numRejected = MpiWrapper::sum< globalIndex >( degenerate.second ); + globalIndex const numCells = MpiWrapper::sum< globalIndex >( report.numCells ); + globalIndex const numConsistent = MpiWrapper::sum< globalIndex >( report.numConsistent ); + globalIndex const numPrescribed0 = MpiWrapper::sum< globalIndex >( report.numPrescribed0 ); + globalIndex const numPrescribed1 = MpiWrapper::sum< globalIndex >( report.numPrescribed1 ); + globalIndex const numRejected = MpiWrapper::sum< globalIndex >( report.numRejected ); + if( params.adaptiveConsistency ) + { + GEOS_LOG_RANK_0( GEOS_FMT( "mixedMFD Flow: consistency layer (tolerance = {}) set eta = 1 on {} / {} cells", + params.consistencyTolerance, numConsistent, numCells ) ); + } if( numPrescribed0 + numPrescribed1 > 0 ) { GEOS_LOG_RANK_0( GEOS_FMT( "mixedMFD Flow: prescribed eta = 0 on {} cells and eta = 1 on {} cells ({} rejected by the degeneracy layer)", numPrescribed0, numPrescribed1, numRejected ) ); } - computeFaceStencilLabels( domain ); -} - -std::pair< localIndex, localIndex > SinglePhaseMixedMFD::applyPrescribedFlag( DomainPartition & domain ) -{ - GEOS_MARK_FUNCTION; - - localIndex numPrescribed0 = 0; - localIndex numPrescribed1 = 0; - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - FieldIdentifiers prescribedToBeSync; - prescribedToBeSync.addElementFields( { mixedMimetic::prescribedMfdFlag::key() }, regionNames ); - CommunicationTools::getInstance().synchronizeFields( prescribedToBeSync, mesh, domain.getNeighbors(), false ); - - mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, - [&]( localIndex const, - ElementSubRegionBase & subRegion ) - { - arrayView1d< real64 const > const prescribed = subRegion.getField< mixedMimetic::prescribedMfdFlag >(); - arrayView1d< integer > const mfdFlag = subRegion.getField< mixedMimetic::mfdFlag >(); - arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); - for( localIndex ei = 0; ei < subRegion.size(); ++ei ) - { - if( prescribed[ei] < 0.0 ) - { - continue; - } - integer const eta = prescribed[ei] > 0.5 ? 1 : 0; - mfdFlag[ei] = eta; - if( ghostRank[ei] < 0 ) - { - numPrescribed0 += ( eta == 0 ); - numPrescribed1 += ( eta == 1 ); - } - } - } ); - } ); - return { numPrescribed0, numPrescribed1 }; -} - -std::pair< localIndex, localIndex > SinglePhaseMixedMFD::applyDegeneracyLayer( DomainPartition & domain ) -{ - GEOS_MARK_FUNCTION; - - NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); - MixedMimeticDiscretizationManager const & mmManager = numericalMethodManager.getMixedMimeticDiscretizationManager(); - MixedMimeticDiscretization const & discretization = mmManager.getMixedMimeticDiscretization( m_discretizationName ); - real64 const tolerance = discretization.getDegeneracyTolerance(); - - localIndex numDegenerate = 0; - localIndex numRejected = 0; - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & regionNames ) - { - NodeManager const & nodeManager = mesh.getNodeManager(); - ElementRegionManager & elemManager = mesh.getElemManager(); - ArrayOfArraysView< localIndex const > const nodeToRegion = nodeManager.elementRegionList(); - ArrayOfArraysView< localIndex const > const nodeToSubRegion = nodeManager.elementSubRegionList(); - ArrayOfArraysView< localIndex const > const nodeToElem = nodeManager.elementList(); - ElementRegionManager::ElementViewAccessor< arrayView1d< real64 const > > const elemVolume = - elemManager.constructArrayViewAccessor< real64, 1 >( ElementSubRegionBase::viewKeyStruct::elementVolumeString() ); - - elemManager.forElementSubRegions< CellElementSubRegion >( regionNames, - [&]( localIndex const, - CellElementSubRegion & subRegion ) - { - arrayView2d< localIndex const, cells::NODE_MAP_USD > const elemToNodes = subRegion.nodeList().toViewConst(); - arrayView1d< real64 const > const volume = subRegion.getElementVolume(); - arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); - arrayView1d< real64 > const indicator = subRegion.getField< mixedMimetic::degeneracyIndicator >(); - arrayView1d< integer > const mfdFlag = subRegion.getField< mixedMimetic::mfdFlag >(); - arrayView1d< real64 const > const prescribed = subRegion.getField< mixedMimetic::prescribedMfdFlag >(); - localIndex const numNodes = subRegion.numNodesPerElement(); - - // the node star of a cell: every cell sharing a vertex with it, counted once - stdVector< std::array< localIndex, 3 > > star; - for( localIndex ei = 0; ei < subRegion.size(); ++ei ) - { - star.clear(); - for( localIndex a = 0; a < numNodes; ++a ) - { - localIndex const n = elemToNodes( ei, a ); - for( localIndex k = 0; k < nodeToElem.sizeOfArray( n ); ++k ) - { - if( nodeToRegion( n, k ) >= 0 && nodeToSubRegion( n, k ) >= 0 && nodeToElem( n, k ) >= 0 ) - { - star.push_back( { nodeToRegion( n, k ), nodeToSubRegion( n, k ), nodeToElem( n, k ) } ); - } - } - } - std::sort( star.begin(), star.end() ); - star.erase( std::unique( star.begin(), star.end() ), star.end() ); - real64 sum = 0.0; - for( auto const & c : star ) - { - sum += elemVolume[c[0]][c[1]][c[2]]; - } - // share of the cell in the volume of its node star, in percent - real64 const percent = sum > 0.0 ? 100.0 * volume[ei] / sum : 0.0; - indicator[ei] = percent; - if( percent < tolerance && mfdFlag[ei] == 1 ) - { - mfdFlag[ei] = 0; - numDegenerate += ( ghostRank[ei] < 0 ) ? 1 : 0; - numRejected += ( ghostRank[ei] < 0 && prescribed[ei] > 0.5 ) ? 1 : 0; - } - } - } ); - - FieldIdentifiers fieldsToBeSync; - fieldsToBeSync.addElementFields( { mixedMimetic::mfdFlag::key(), mixedMimetic::degeneracyIndicator::key() }, regionNames ); - CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), false ); - } ); - return { numDegenerate, numRejected }; -} - -void SinglePhaseMixedMFD::computeFaceStencilLabels( DomainPartition & domain ) -{ - GEOS_MARK_FUNCTION; - - NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); - MixedMimeticDiscretizationManager const & mmManager = numericalMethodManager.getMixedMimeticDiscretizationManager(); - MixedMimeticDiscretization const & discretization = mmManager.getMixedMimeticDiscretization( m_discretizationName ); - - // with a TPFA inner product the effective operator is diagonal in every cell, - // regardless of the stencil activation flags - bool const effectiveTpfa = discretization.isTpfaInnerProduct(); - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - string_array const & ) - { - FaceManager & faceManager = mesh.getFaceManager(); - ElementRegionManager & elemManager = mesh.getElemManager(); - - ElementRegionManager::ElementViewAccessor< arrayView1d< integer const > > const mfdFlagAccessor = - elemManager.constructArrayViewAccessor< integer, 1 >( mixedMimetic::mfdFlag::key() ); - - mixedMimeticKernels::FaceLabelKernel:: - launch< parallelDevicePolicy<> >( faceManager.size(), - faceManager.elementRegionList(), - faceManager.elementSubRegionList(), - faceManager.elementList(), - m_regionFilter.toViewConst(), - mfdFlagAccessor.toNestedViewConst(), - effectiveTpfa, - faceManager.getField< mixedMimetic::faceStencilLabel >() ); - } ); } void SinglePhaseMixedMFD::implicitStepSetup( real64 const & time_n, diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp index 1cb10f75f65..a81c78220d2 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp @@ -22,8 +22,6 @@ #include "physicsSolvers/fluidFlow/SinglePhaseBase.hpp" -#include - namespace geos { @@ -32,8 +30,7 @@ namespace geos * * Single-phase flow solver based on the mixed mimetic finite difference formulation: * the primary unknowns are the cell pressures and the face mass fluxes, coupled in a - * saddle-point system. The cell-wise inner product is selected adaptively (TPFA/MFD) - * through residual-based Global Adaptation indicators. + * saddle-point system. The cell-wise inner product (TPFA/MFD) is selected by ConsistencyAdaptation. */ class SinglePhaseMixedMFD : public SinglePhaseBase { @@ -179,46 +176,16 @@ class SinglePhaseMixedMFD : public SinglePhaseBase DomainPartition & domain ); /** - * @brief Run the residual-based Global Adaptation pipeline and mark the cells. - * @param[in] domain the domain - */ - /** - * @brief Store, for every face, the global index of the cell its flux unknown points out of, - * and synchronize it so that the orientation is the same on every rank. + * @brief Set the orienting cell E_min of every face: sigma_{E,f} = +1 if E = E_min, -1 otherwise. * @param domain the domain */ void computeFaceOrientation( DomainPartition & domain ); - void computeGlobalAdaptationIndicators( DomainPartition & domain ); - - /** - * @brief Second classification layer: cells whose volume is below the degeneracy tolerance - * (percent of the volume of their node star) fall back to the diagonal product. - * @param domain the domain - * @return the number of locally-owned cells switched to the diagonal product by this layer, and how many - * of them were prescribed the consistent (MFD) product - */ - std::pair< localIndex, localIndex > applyDegeneracyLayer( DomainPartition & domain ); - /** - * @brief Third classification layer: the user prescription of eta read from the mesh - * (prescribedMfdFlag: negative free, 0 diagonal product, 1 consistent (MFD) product). + * @brief Select the inner product of every cell (ConsistencyAdaptation) and label the faces. * @param domain the domain - * @return the number of locally-owned cells prescribed 0 and prescribed 1 - */ - std::pair< localIndex, localIndex > applyPrescribedFlag( DomainPartition & domain ); - - /** - * @brief Apply the prescription and degeneracy layers on top of the current eta, then label the faces. - * @param domain the domain - */ - void applyLayersAndLabel( DomainPartition & domain ); - - /** - * @brief Classify the faces from the cell marking (0 = condensable TPFA face, 1 = live MFD face). - * @param[in] domain the domain */ - void computeFaceStencilLabels( DomainPartition & domain ); + void classifyCells( DomainPartition & domain ); /** * @brief Build the per-dof labels of the MGR strategy: diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp index ce7c9636641..5b2bbf6b513 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp @@ -29,7 +29,7 @@ #include "mesh/MeshLevel.hpp" #include "mixedMimetic/MixedMimeticDispatch.hpp" #include "mixedMimetic/MixedMimeticFields.hpp" -#include "mixedMimetic/adaptivity/GlobalAdaptationKernels.hpp" +#include "mixedMimetic/consistency/ConsistencyAdaptationKernels.hpp" #include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" #include "physicsSolvers/fluidFlow/SinglePhaseBaseFields.hpp" #include "physicsSolvers/fluidFlow/StencilAccessors.hpp" From b22f3b2c0c4cea7be40f3f6b2d8c042480a58c82 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Sun, 6 Sep 2026 20:16:44 -0700 Subject: [PATCH 33/64] wip: minor improvements on prescribed mfdFlag --- .../mesh/generators/VTKUtilities.cpp | 27 +++++-- .../MixedMimeticDiscretization.cpp | 7 +- .../mixedMimetic/MixedMimeticFields.hpp | 9 +-- .../consistency/ConsistencyAdaptation.cpp | 71 +++++++++++++++---- .../consistency/ConsistencyAdaptation.hpp | 19 ++--- .../unitTests/testConsistencyAdaptation.cpp | 29 ++++++-- .../fluidFlow/SinglePhaseMixedMFD.cpp | 21 +++--- 7 files changed, 134 insertions(+), 49 deletions(-) diff --git a/src/coreComponents/mesh/generators/VTKUtilities.cpp b/src/coreComponents/mesh/generators/VTKUtilities.cpp index c1f83e559ac..9ee1e80d3f9 100644 --- a/src/coreComponents/mesh/generators/VTKUtilities.cpp +++ b/src/coreComponents/mesh/generators/VTKUtilities.cpp @@ -32,6 +32,9 @@ #endif #include + +#include +#include #include #include #include @@ -3477,7 +3480,8 @@ void importRegularField( stdVector< vtkIdType > const & cellIds, GEOS_FMT( "Destination wrapper for VTK field '{}' has {} entries, but {} cells were requested during import", vtkArray->GetName(), wrapper.size(), cellIds.size() ) ); - using ImportTypes = types::ListofTypeList< types::ArrayTypes< types::RealTypes, types::DimsRange< 1, 2 > > >; + using ImportTypes = types::ListofTypeList< types::Join< types::ArrayTypes< types::RealTypes, types::DimsRange< 1, 2 > >, + types::ArrayTypes< types::TypeList< integer >, types::DimsSingle< 1 > > > >; types::dispatch( ImportTypes{}, [&]( auto tupleOfTypes ) { using ArrayType = camp::first< decltype( tupleOfTypes ) >; @@ -3489,7 +3493,7 @@ void importRegularField( stdVector< vtkIdType > const & cellIds, GEOS_ERROR_IF_NE_MSG( numComponentsDst, numComponentsSrc, GEOS_FMT( "Mismatch in number of components for field {}", vtkArray->GetName() ) ); - vtkArrayDispatch::DispatchByValueType< vtkArrayDispatch::Reals >::Execute( vtkArray, [&]( auto const * srcArray ) + bool const dispatched = vtkArrayDispatch::DispatchByValueType< vtkArrayDispatch::AllTypes >::Execute( vtkArray, [&]( auto const * srcArray ) { vtkDataArrayAccessor< TYPEOFPTR( srcArray ) > data( srcArray ); localIndex cellCount = 0; @@ -3497,11 +3501,23 @@ void importRegularField( stdVector< vtkIdType > const & cellIds, { LvArray::forValuesInSlice( view[cellCount], [&, componentIdx = 0]( auto & val ) mutable { - val = data.Get( cellIdx, componentIdx++ ); + using DstType = std::remove_reference_t< decltype( val ) >; + auto const src = data.Get( cellIdx, componentIdx++ ); + // an integer field only accepts whole numbers from a floating point array + if constexpr ( std::is_integral< DstType >::value && std::is_floating_point< decltype( src ) >::value ) + { + GEOS_THROW_IF( !std::isfinite( src ) || std::fabs( src - std::round( src ) ) > 0.0, + GEOS_FMT( "Field '{}' targets an integer field but holds the non-integer value {} at cell {}", + vtkArray->GetName(), src, cellIdx ), + InputError ); + } + val = static_cast< DstType >( src ); } ); ++cellCount; } } ); + GEOS_ERROR_IF( !dispatched, + GEOS_FMT( "Field '{}' has unsupported type: {}", vtkArray->GetName(), vtkArray->GetDataTypeAsString() ) ); }, wrapper ); } @@ -3593,9 +3609,8 @@ findArrayForImport( vtkDataSet & mesh, GEOS_FMT( "Source field '{}' not found in dataset", sourceName ), InputError ); - int const dataType = curArray->GetDataType(); - GEOS_ERROR_IF( dataType != VTK_FLOAT && dataType != VTK_DOUBLE, - GEOS_FMT( "Source field '{}' has unsupported type: {} (expected floating point type)", + GEOS_ERROR_IF( !curArray->IsNumeric(), + GEOS_FMT( "Source field '{}' has unsupported type: {} (expected a numeric type)", sourceName, curArray->GetDataTypeAsString() ) ); return vtkDataArray::SafeDownCast( curArray ); } diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp index edd1c03e81e..4bab7fcbef4 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp @@ -66,9 +66,10 @@ MixedMimeticDiscretization::MixedMimeticDiscretization( string const & name, registerWrapper( viewKeyStruct::degeneracyToleranceString(), &m_degeneracyTolerance ). setInputFlag( InputFlags::OPTIONAL ). setApplyDefaultValue( 0.1 ). - setDescription( "Degeneracy tolerance in percent: a cell whose volume is below this percentage of the total volume " - "of its node star is not admissible for the consistent (MFD) product and uses the diagonal (TPFA) " - "product, whatever the consistency indicator says; 0 disables the layer" ); + setDescription( "Degeneracy tolerance in percent: a free cell whose volume is below this percentage of the total " + "volume of its node star is not admissible for the consistent (MFD) product and uses the diagonal " + "(TPFA) product, whatever the consistency indicator says; a prescribed cell is not altered; " + "0 disables the layer" ); } void MixedMimeticDiscretization::postInputInitialization() diff --git a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp index 3e8bfcb4830..a27d1b1286a 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp @@ -59,12 +59,13 @@ DECLARE_FIELD( mfdFlag, DECLARE_FIELD( prescribedMfdFlag, "prescribedMfdFlag", - array1d< real64 >, - -1.0, + array1d< integer >, + -1, LEVEL_0, WRITE_AND_READ, - "User prescription of eta read from the mesh (real-valued so that it can be imported): negative = free " - "(the solver decides), 0 = diagonal product, 1 = consistent (MFD) product (rejected on a degenerate cell)" ); + "Constraint on eta read from the mesh, not the flag itself (mfdFlag is the answer): -1 = free, the layers " + "decide; 0 = diagonal (TPFA) product; 1 = consistent (MFD) product; a prescribed value is final; " + "any other value is rejected" ); DECLARE_FIELD( consistencyIndicator, "consistencyIndicator", diff --git a/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp b/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp index 93a8457ab62..ab185ff3dd1 100644 --- a/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp +++ b/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp @@ -19,6 +19,7 @@ #include "ConsistencyAdaptation.hpp" +#include "common/MpiWrapper.hpp" #include "mesh/MeshLevel.hpp" #include "mesh/mpiCommunications/CommunicationTools.hpp" #include "mixedMimetic/MixedMimeticFields.hpp" @@ -63,7 +64,24 @@ ConsistencyAdaptation::Report ConsistencyAdaptation::classify( MeshLevel & mesh, std::pair< localIndex, localIndex > const degenerate = applyDegeneracyLayer( mesh, regionNames, params.degeneracyTolerance, neighbors ); report.numDegenerate = degenerate.first; - report.numRejected = degenerate.second; + report.numPrescribedDegenerate = degenerate.second; + + // final count, and the invariant of the layers: a prescribed cell is never altered + elemManager.forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + arrayView1d< integer const > const mfdFlag = subRegion.getField< mixedMimetic::mfdFlag >(); + arrayView1d< integer const > const prescribed = subRegion.getField< mixedMimetic::prescribedMfdFlag >(); + arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + GEOS_ERROR_IF( prescribed[ei] >= 0 && mfdFlag[ei] != prescribed[ei], + GEOS_FMT( "ConsistencyAdaptation: the prescription of cell {} of {} was altered by the layers", + subRegion.localToGlobalMap()[ei], subRegion.getName() ) ); + report.numConsistentFinal += ( ghostRank[ei] < 0 && mfdFlag[ei] == 1 ) ? 1 : 0; + } + } ); labelFaces( mesh, regionFilter, params.effectiveTpfa ); return report; @@ -169,27 +187,47 @@ std::pair< localIndex, localIndex > ConsistencyAdaptation::applyPrescription( Me prescribedToBeSync.addElementFields( { mixedMimetic::prescribedMfdFlag::key() }, regionNames ); CommunicationTools::getInstance().synchronizeFields( prescribedToBeSync, mesh, neighbors, false ); + // validation on every rank before the collective throw: a rank must not leave the others in a collective + localIndex numInvalid = 0; + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + arrayView1d< integer const > const prescribed = subRegion.getField< mixedMimetic::prescribedMfdFlag >(); + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + if( prescribed[ei] < -1 || prescribed[ei] > 1 ) + { + GEOS_LOG_RANK( GEOS_FMT( "{}: prescribedMfdFlag = {} on cell {} is not one of -1 (free), 0 (diagonal product), 1 (consistent product)", + subRegion.getName(), prescribed[ei], subRegion.localToGlobalMap()[ei] ) ); + ++numInvalid; + } + } + } ); + GEOS_THROW_IF( MpiWrapper::sum( numInvalid ) > 0, + "ConsistencyAdaptation: invalid prescribedMfdFlag values (see the cells listed above)", + InputError ); + localIndex numPrescribed0 = 0; localIndex numPrescribed1 = 0; mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, [&]( localIndex const, ElementSubRegionBase & subRegion ) { - arrayView1d< real64 const > const prescribed = subRegion.getField< mixedMimetic::prescribedMfdFlag >(); + arrayView1d< integer const > const prescribed = subRegion.getField< mixedMimetic::prescribedMfdFlag >(); arrayView1d< integer > const mfdFlag = subRegion.getField< mixedMimetic::mfdFlag >(); arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); for( localIndex ei = 0; ei < subRegion.size(); ++ei ) { - if( prescribed[ei] < 0.0 ) + if( prescribed[ei] < 0 ) { continue; } - integer const eta = prescribed[ei] > 0.5 ? 1 : 0; - mfdFlag[ei] = eta; + mfdFlag[ei] = prescribed[ei]; if( ghostRank[ei] < 0 ) { - numPrescribed0 += ( eta == 0 ); - numPrescribed1 += ( eta == 1 ); + numPrescribed0 += ( prescribed[ei] == 0 ); + numPrescribed1 += ( prescribed[ei] == 1 ); } } } ); @@ -210,7 +248,7 @@ std::pair< localIndex, localIndex > ConsistencyAdaptation::applyDegeneracyLayer( elemManager.constructArrayViewAccessor< real64, 1 >( ElementSubRegionBase::viewKeyStruct::elementVolumeString() ); localIndex numDegenerate = 0; - localIndex numRejected = 0; + localIndex numPrescribedDegenerate = 0; elemManager.forElementSubRegions< CellElementSubRegion >( regionNames, [&]( localIndex const, CellElementSubRegion & subRegion ) @@ -220,7 +258,7 @@ std::pair< localIndex, localIndex > ConsistencyAdaptation::applyDegeneracyLayer( arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); arrayView1d< real64 > const indicator = subRegion.getField< mixedMimetic::degeneracyIndicator >(); arrayView1d< integer > const mfdFlag = subRegion.getField< mixedMimetic::mfdFlag >(); - arrayView1d< real64 const > const prescribed = subRegion.getField< mixedMimetic::prescribedMfdFlag >(); + arrayView1d< integer const > const prescribed = subRegion.getField< mixedMimetic::prescribedMfdFlag >(); localIndex const numNodes = subRegion.numNodesPerElement(); // the node star of a cell: every cell sharing a vertex with it, counted once @@ -251,9 +289,16 @@ std::pair< localIndex, localIndex > ConsistencyAdaptation::applyDegeneracyLayer( indicator[ei] = percent; if( percent < tolerance && mfdFlag[ei] == 1 ) { - mfdFlag[ei] = 0; - numDegenerate += ( ghostRank[ei] < 0 ) ? 1 : 0; - numRejected += ( ghostRank[ei] < 0 && prescribed[ei] > 0.5 ) ? 1 : 0; + // a prescribed cell is final: only a free cell is switched + if( prescribed[ei] < 0 ) + { + mfdFlag[ei] = 0; + numDegenerate += ( ghostRank[ei] < 0 ) ? 1 : 0; + } + else + { + numPrescribedDegenerate += ( ghostRank[ei] < 0 ) ? 1 : 0; + } } } } ); @@ -261,7 +306,7 @@ std::pair< localIndex, localIndex > ConsistencyAdaptation::applyDegeneracyLayer( FieldIdentifiers fieldsToBeSync; fieldsToBeSync.addElementFields( { mixedMimetic::mfdFlag::key(), mixedMimetic::degeneracyIndicator::key() }, regionNames ); CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, neighbors, false ); - return { numDegenerate, numRejected }; + return { numDegenerate, numPrescribedDegenerate }; } void ConsistencyAdaptation::labelFaces( MeshLevel & mesh, diff --git a/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.hpp b/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.hpp index 690f7c7713f..61a060515f5 100644 --- a/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.hpp +++ b/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.hpp @@ -36,9 +36,9 @@ class NeighborCommunicator; * @brief Selects the inner product of every cell of the mixed mimetic discretization, eta = 1 for the * consistent (MFD) product and eta = 0 for the diagonal (TPFA) product, through three layers * applied in order: the residual-based consistency layer, the prescription read from the mesh - * and the degeneracy layer (admissibility of the consistent product, the only one allowed to - * override a prescription). The faces are then labelled: 0 when both cells use the diagonal - * product (the flux dof is condensed), 1 otherwise (the flux dof stays in the saddle point). + * and the degeneracy layer (admissibility of the consistent product). A prescribed cell is + * final: the two other layers act on the free cells only. The faces are then labelled: 0 when + * both cells use the diagonal product (the flux dof is condensed), 1 otherwise (saddle point). */ class ConsistencyAdaptation { @@ -62,8 +62,9 @@ class ConsistencyAdaptation localIndex numConsistent = 0; ///< cells with eta = 1 after the consistency layer localIndex numPrescribed0 = 0; ///< cells prescribed the diagonal product localIndex numPrescribed1 = 0; ///< cells prescribed the consistent product - localIndex numDegenerate = 0; ///< cells switched to the diagonal product by the degeneracy layer - localIndex numRejected = 0; ///< prescribed consistent products rejected by the degeneracy layer + localIndex numDegenerate = 0; ///< free cells switched to the diagonal product by the degeneracy layer + localIndex numPrescribedDegenerate = 0; ///< cells prescribed the consistent product below the degeneracy tolerance (kept) + localIndex numConsistentFinal = 0; ///< cells with eta = 1 after the three layers /** * @brief Accumulate the counts of another mesh level. @@ -76,7 +77,8 @@ class ConsistencyAdaptation numPrescribed0 += other.numPrescribed0; numPrescribed1 += other.numPrescribed1; numDegenerate += other.numDegenerate; - numRejected += other.numRejected; + numPrescribedDegenerate += other.numPrescribedDegenerate; + numConsistentFinal += other.numConsistentFinal; } }; @@ -123,8 +125,9 @@ class ConsistencyAdaptation stdVector< NeighborCommunicator > & neighbors ); /** - * @brief Degeneracy layer: eta = 0 where the cell volume is below the tolerance (percent of its node star). - * @return the locally-owned cells switched, and how many of them were prescribed eta = 1 + * @brief Degeneracy layer on the free cells: eta = 0 where the cell volume is below the tolerance + * (percent of its node star); a prescribed cell is left unchanged. + * @return the locally-owned free cells switched, and the prescribed eta = 1 cells below the tolerance */ static std::pair< localIndex, localIndex > applyDegeneracyLayer( MeshLevel & mesh, string_array const & regionNames, diff --git a/src/coreComponents/mixedMimetic/unitTests/testConsistencyAdaptation.cpp b/src/coreComponents/mixedMimetic/unitTests/testConsistencyAdaptation.cpp index d42acbf0757..b9a5c51ee26 100644 --- a/src/coreComponents/mixedMimetic/unitTests/testConsistencyAdaptation.cpp +++ b/src/coreComponents/mixedMimetic/unitTests/testConsistencyAdaptation.cpp @@ -94,6 +94,10 @@ static string const prescriptions = )xml"; +static string const invalidPrescription = + R"xml()xml"; + static string const noPrescription; // Cartesian hexahedra: K-orthogonal, the two-point product is consistent everywhere @@ -103,6 +107,7 @@ static string const tetrahedra = makeInput( "C3D4", "0, 1", "4", "cb1", noPrescr // a 1e-3 thick layer of 4 cells (0.2475 % of their node star) in front of 40 unit-height cells static string const thinLayer = makeInput( "C3D8", "0, 1e-3, 1", "1, 10", "cb1, cb2", noPrescription ); static string const thinLayerPrescribed = makeInput( "C3D8", "0, 1e-3, 1", "1, 10", "cb1, cb2", prescriptions ); +static string const thinLayerInvalid = makeInput( "C3D8", "0, 1e-3, 1", "1, 10", "cb1, cb2", invalidPrescription ); static void setupProblemFromXML( ProblemManager & problemManager, string const & xmlInput ) { @@ -161,7 +166,8 @@ class ConsistencyAdaptationTest r.numPrescribed0 = MpiWrapper::sum( r.numPrescribed0 ); r.numPrescribed1 = MpiWrapper::sum( r.numPrescribed1 ); r.numDegenerate = MpiWrapper::sum( r.numDegenerate ); - r.numRejected = MpiWrapper::sum( r.numRejected ); + r.numPrescribedDegenerate = MpiWrapper::sum( r.numPrescribedDegenerate ); + r.numConsistentFinal = MpiWrapper::sum( r.numConsistentFinal ); return r; } @@ -329,7 +335,8 @@ TEST( ConsistencyAdaptation, DegeneracyLayer_ThinLayer ) params.degeneracyTolerance = 1.0; r = test.classify( params ); EXPECT_EQ( r.numDegenerate, 4 ); - EXPECT_EQ( r.numRejected, 0 ); + EXPECT_EQ( r.numPrescribedDegenerate, 0 ); + EXPECT_EQ( r.numConsistentFinal, 40 ); EXPECT_EQ( test.countFlag( 1 ), 40 ); EXPECT_EQ( test.checkFaceLabels(), 180 - 16 ); } @@ -347,6 +354,7 @@ TEST( ConsistencyAdaptation, Prescription_ThinLayer ) EXPECT_EQ( r.numPrescribed1, 4 ); EXPECT_EQ( r.numPrescribed0, 4 ); EXPECT_EQ( r.numDegenerate, 0 ); + EXPECT_EQ( r.numConsistentFinal, 4 ); EXPECT_EQ( test.countFlag( 1 ), 4 ); EXPECT_EQ( test.checkFaceLabels(), 20 ); @@ -357,15 +365,22 @@ TEST( ConsistencyAdaptation, Prescription_ThinLayer ) EXPECT_EQ( test.countFlag( 0 ), 4 ); EXPECT_EQ( test.checkFaceLabels(), 180 - 16 ); - // only the degeneracy layer rejects a prescribed consistent product + // a prescribed consistent product is final: the degeneracy layer reports the thin cells but keeps them params.adaptiveConsistency = true; params.degeneracyTolerance = 1.0; r = test.classify( params ); EXPECT_EQ( r.numPrescribed1, 4 ); - EXPECT_EQ( r.numDegenerate, 4 ); - EXPECT_EQ( r.numRejected, 4 ); - EXPECT_EQ( test.countFlag( 1 ), 0 ); - EXPECT_EQ( test.checkFaceLabels(), 0 ); + EXPECT_EQ( r.numDegenerate, 0 ); + EXPECT_EQ( r.numPrescribedDegenerate, 4 ); + EXPECT_EQ( r.numConsistentFinal, 4 ); + EXPECT_EQ( test.countFlag( 1 ), 4 ); + EXPECT_EQ( test.checkFaceLabels(), 20 ); +} + +TEST( ConsistencyAdaptation, Prescription_InvalidValue ) +{ + // a value outside { -1, 0, 1 } is rejected (with the cell index) by the classification run at initialization + EXPECT_THROW( ConsistencyAdaptationTest test( thinLayerInvalid ), InputError ); } int main( int argc, char * * argv ) diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index 8b4d9df7788..6524828ec23 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -251,16 +251,21 @@ void SinglePhaseMixedMFD::classifyCells( DomainPartition & domain ) globalIndex const numConsistent = MpiWrapper::sum< globalIndex >( report.numConsistent ); globalIndex const numPrescribed0 = MpiWrapper::sum< globalIndex >( report.numPrescribed0 ); globalIndex const numPrescribed1 = MpiWrapper::sum< globalIndex >( report.numPrescribed1 ); - globalIndex const numRejected = MpiWrapper::sum< globalIndex >( report.numRejected ); - if( params.adaptiveConsistency ) - { - GEOS_LOG_RANK_0( GEOS_FMT( "mixedMFD Flow: consistency layer (tolerance = {}) set eta = 1 on {} / {} cells", - params.consistencyTolerance, numConsistent, numCells ) ); - } + globalIndex const numDegenerate = MpiWrapper::sum< globalIndex >( report.numDegenerate ); + globalIndex const numPrescribedDegenerate = MpiWrapper::sum< globalIndex >( report.numPrescribedDegenerate ); + globalIndex const numConsistentFinal = MpiWrapper::sum< globalIndex >( report.numConsistentFinal ); + string const consistencyLayer = params.adaptiveConsistency + ? GEOS_FMT( "consistency layer (tolerance = {}) selected {}", params.consistencyTolerance, numConsistent ) + : "consistency layer off"; + GEOS_LOG_RANK_0( GEOS_FMT( "mixedMFD Flow: eta = 1 on {} / {} cells: {}, degeneracy layer (tolerance = {} %) switched {} free cells to eta = 0", + numConsistentFinal, numCells, consistencyLayer, params.degeneracyTolerance, numDegenerate ) ); if( numPrescribed0 + numPrescribed1 > 0 ) { - GEOS_LOG_RANK_0( GEOS_FMT( "mixedMFD Flow: prescribed eta = 0 on {} cells and eta = 1 on {} cells ({} rejected by the degeneracy layer)", - numPrescribed0, numPrescribed1, numRejected ) ); + GEOS_LOG_RANK_0( GEOS_FMT( "mixedMFD Flow: prescribed eta = 0 on {} cells and eta = 1 on {} cells, kept unchanged ({} of the latter below the degeneracy tolerance)", + numPrescribed0, numPrescribed1, numPrescribedDegenerate ) ); + GEOS_WARNING_IF( numPrescribed0 + numPrescribed1 == numCells, + GEOS_FMT( "{}: every cell is prescribed (no prescribedMfdFlag = -1): the consistency and degeneracy layers are inert", + getDataContext() ) ); } } From 151a3d12135e95695cae6f1bfec72e4088c11dd8 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Tue, 15 Sep 2026 00:27:42 -0700 Subject: [PATCH 34/64] wip: resolve conflict --- .../interfaces/hypre/hypredrive.cpp | 71 +------------------ 1 file changed, 2 insertions(+), 69 deletions(-) diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp b/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp index b5c89e029b0..616fcc76307 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp @@ -1400,14 +1400,9 @@ bool buildStrategyYaml( LinearSolverParameters const & params, strategy.configure( params.mgr ); strategy.normalizeReductionParameters(); - // strategies driven by solver-provided custom point markers use more blocks than there are - // dof fields; those labels have no field-name representation, so skip the YAML preview + // custom point markers use more blocks than dof fields: no field-name representation, skip the YAML if( strategy.m_numBlocks > LvArray::integerConversion< HYPRE_Int >( labelNames.size() ) ) { - destroyWrapper( mgrData.coarseSolver ); - destroyWrapper( mgrData.mechSolver ); - destroyWrapper( mgrData.nestedSolver ); - GEOS_LAI_CHECK_ERROR( HYPRE_MGRDestroy( precond.ptr ) ); return false; } @@ -1520,74 +1515,13 @@ bool buildMGRPreconditionerYaml( LinearSolverParameters const & params, switch( params.mgr.strategy ) { -<<<<<<< HEAD - case StrategyType::singlePhaseReservoirFVM: - return buildStrategyYaml< hypre::mgr::SinglePhaseReservoirFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::thermalSinglePhaseReservoirFVM: - return buildStrategyYaml< hypre::mgr::ThermalSinglePhaseReservoirFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::singlePhaseHybridFVM: - return buildStrategyYaml< hypre::mgr::SinglePhaseHybridFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::singlePhaseMixedMFD: - return buildStrategyYaml< hypre::mgr::SinglePhaseMixedMFD >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::singlePhaseReservoirHybridFVM: - return buildStrategyYaml< hypre::mgr::SinglePhaseReservoirHybridFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::singlePhasePoromechanics: - return buildStrategyYaml< hypre::mgr::SinglePhasePoromechanics >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::thermalSinglePhasePoromechanics: - return buildStrategyYaml< hypre::mgr::ThermalSinglePhasePoromechanics >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::hybridSinglePhasePoromechanics: - return buildStrategyYaml< hypre::mgr::HybridSinglePhasePoromechanics >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::singlePhasePoromechanicsEmbeddedFractures: - return buildStrategyYaml< hypre::mgr::SinglePhasePoromechanicsEmbeddedFractures >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::singlePhasePoromechanicsConformingFractures: - return buildStrategyYaml< hypre::mgr::SinglePhasePoromechanicsConformingFractures >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::singlePhasePoromechanicsConformingFracturesALM: - return buildStrategyYaml< hypre::mgr::SinglePhasePoromechanicsConformingFracturesALM >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::singlePhasePoromechanicsConformingFracturesALMReservoirFVM: - return buildStrategyYaml< hypre::mgr::SinglePhasePoromechanicsConformingFracturesALMReservoirFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::singlePhasePoromechanicsReservoirFVM: - return buildStrategyYaml< hypre::mgr::SinglePhasePoromechanicsReservoirFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::thermalSinglePhasePoromechanicsReservoirFVM: - return buildStrategyYaml< hypre::mgr::ThermalSinglePhasePoromechanicsReservoirFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::compositionalMultiphaseFVM: - return buildStrategyYaml< hypre::mgr::CompositionalMultiphaseFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::compositionalMultiphaseHybridFVM: - return buildStrategyYaml< hypre::mgr::CompositionalMultiphaseHybridFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::compositionalMultiphaseReservoirFVM: - return buildStrategyYaml< hypre::mgr::CompositionalMultiphaseReservoirFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::compositionalMultiphaseReservoirHybridFVM: - return buildStrategyYaml< hypre::mgr::CompositionalMultiphaseReservoirHybridFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::immiscibleMultiphaseFVM: - return buildStrategyYaml< hypre::mgr::ImmiscibleMultiphaseFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::reactiveCompositionalMultiphaseOBL: - return buildStrategyYaml< hypre::mgr::ReactiveCompositionalMultiphaseOBL >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::thermalCompositionalMultiphaseFVM: - return buildStrategyYaml< hypre::mgr::ThermalCompositionalMultiphaseFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::thermalCompositionalMultiphaseReservoirFVM: - return buildStrategyYaml< hypre::mgr::ThermalCompositionalMultiphaseReservoirFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::multiphasePoromechanics: - return buildStrategyYaml< hypre::mgr::MultiphasePoromechanics >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::multiphasePoromechanicsReservoirFVM: - return buildStrategyYaml< hypre::mgr::MultiphasePoromechanicsReservoirFVM >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::thermalMultiphasePoromechanics: - return buildStrategyYaml< hypre::mgr::ThermalMultiphasePoromechanics >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::hydrofracture: - return buildStrategyYaml< hypre::mgr::Hydrofracture >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::lagrangianContactMechanics: - return buildStrategyYaml< hypre::mgr::LagrangianContactMechanics >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::augmentedLagrangianContactMechanics: - return buildStrategyYaml< hypre::mgr::AugmentedLagrangianContactMechanics >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::lagrangianContactMechanicsBubbleStab: - return buildStrategyYaml< hypre::mgr::LagrangianContactMechanicsBubbleStabilization >( params, labelNames, numComponentsPerField, preconditionerYaml ); - case StrategyType::solidMechanicsEmbeddedFractures: - return buildStrategyYaml< hypre::mgr::SolidMechanicsEmbeddedFractures >( params, labelNames, numComponentsPerField, preconditionerYaml ); -======= #define GEOS_HYPREDRIVE_MGR_CASE( enumName, typeName ) \ case StrategyType::enumName: \ return buildStrategyYaml< hypre::mgr::typeName >( params, labelNames, numComponentsPerField, preconditionerYaml ) GEOS_HYPREDRIVE_MGR_CASE( singlePhaseReservoirFVM, SinglePhaseReservoirFVM ); GEOS_HYPREDRIVE_MGR_CASE( thermalSinglePhaseReservoirFVM, ThermalSinglePhaseReservoirFVM ); GEOS_HYPREDRIVE_MGR_CASE( singlePhaseHybridFVM, SinglePhaseHybridFVM ); + GEOS_HYPREDRIVE_MGR_CASE( singlePhaseMixedMFD, SinglePhaseMixedMFD ); GEOS_HYPREDRIVE_MGR_CASE( singlePhaseReservoirHybridFVM, SinglePhaseReservoirHybridFVM ); GEOS_HYPREDRIVE_MGR_CASE( singlePhasePoromechanics, SinglePhasePoromechanics ); GEOS_HYPREDRIVE_MGR_CASE( thermalSinglePhasePoromechanics, ThermalSinglePhasePoromechanics ); @@ -1615,7 +1549,6 @@ bool buildMGRPreconditionerYaml( LinearSolverParameters const & params, GEOS_HYPREDRIVE_MGR_CASE( lagrangianContactMechanicsBubbleStab, LagrangianContactMechanicsBubbleStabilization ); GEOS_HYPREDRIVE_MGR_CASE( solidMechanicsEmbeddedFractures, SolidMechanicsEmbeddedFractures ); #undef GEOS_HYPREDRIVE_MGR_CASE ->>>>>>> develop case StrategyType::invalid: return false; } From f726042f137740e2c637d7b6af8cb770ceba9992 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Tue, 15 Sep 2026 09:07:30 -0700 Subject: [PATCH 35/64] wip: minor fixes --- .../common/FieldSpecificationOps.hpp | 12 +- .../interfaces/hypre/HypreMGR.hpp | 5 +- .../unitTests/testHypredrive.cpp | 10 + .../fluidFlow/SinglePhaseMixedMFD.cpp | 7 - .../fluidFlow/SinglePhaseMixedMFD.hpp | 5 - .../SinglePhaseMixedMFDKernels.hpp | 181 +----------------- 6 files changed, 31 insertions(+), 189 deletions(-) diff --git a/src/coreComponents/common/FieldSpecificationOps.hpp b/src/coreComponents/common/FieldSpecificationOps.hpp index f521342c242..e3de9218fba 100644 --- a/src/coreComponents/common/FieldSpecificationOps.hpp +++ b/src/coreComponents/common/FieldSpecificationOps.hpp @@ -568,6 +568,14 @@ struct FieldSpecificationEqual : public FieldSpecificationOp< OpEqual > real64 diagonal = 0; real64 const minDiagonal = 1e-15; + // a degenerate diagonal is reset to max(||row||_inf, minDiagonal), the infinity norm + // of the row before replacement, so the constraint keeps the scale of the equation it replaces + real64 rowNormInf = 0; + for( localIndex j = 0; j < numEntries; ++j ) + { + rowNormInf = LvArray::math::max( rowNormInf, LvArray::math::abs( entries[ j ] ) ); + } + real64 const diagonalScale = LvArray::math::max( rowNormInf, minDiagonal ); for( localIndex j = 0; j < numEntries; ++j ) { if( columns[ j ] == dof ) @@ -577,11 +585,11 @@ struct FieldSpecificationEqual : public FieldSpecificationOp< OpEqual > // check that the entry is large enough to enforce the boundary condition if( entry >= 0 && entry < minDiagonal ) { - entries[ j ] = minDiagonal; + entries[ j ] = diagonalScale; } else if( entry < 0 && entry > -minDiagonal ) { - entries[ j ] = -minDiagonal; + entries[ j ] = -diagonalScale; } diagonal = entries[ j ]; } diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.hpp b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.hpp index a29bb2e083f..1008d600e37 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.hpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.hpp @@ -418,15 +418,14 @@ class MGRStrategyBase * @param mgrData auxiliary MGR data */ void setReduction( HyprePrecWrapper & precond, - HypreMGRData & mgrData, - HYPRE_Int const numActiveLevels = numLevels ) + HypreMGRData & mgrData ) { normalizeReductionParameters(); MGRParameters const mgrParameters = defaultMGRParameters(); GEOS_LAI_CHECK_ERROR( HYPRE_MGRSetCpointsByPointMarkerArray( precond.ptr, - m_numBlocks, numActiveLevels, + m_numBlocks, numLevels, m_numLabels, m_ptrLabels, mgrData.pointMarkers.data() ) ); GEOS_LAI_CHECK_ERROR( HYPRE_MGRSetLevelFRelaxType( precond.ptr, toUnderlyingPtr( m_levelFRelaxType ) ) ); diff --git a/src/coreComponents/linearAlgebra/unitTests/testHypredrive.cpp b/src/coreComponents/linearAlgebra/unitTests/testHypredrive.cpp index 70f845afdb6..0d33a104257 100644 --- a/src/coreComponents/linearAlgebra/unitTests/testHypredrive.cpp +++ b/src/coreComponents/linearAlgebra/unitTests/testHypredrive.cpp @@ -289,6 +289,16 @@ TEST( HypredriveYaml, BuildsGeneratedYamlForEveryMGRStrategy ) } hypre::hypredrive::InputArgsParseTarget target; + if( strategy == StrategyType::singlePhaseMixedMFD ) + { + // solver-provided point markers have no field-label form: this strategy stays on legacy hypre + EXPECT_FALSE( hypre::hypredrive::buildInputArgsParseTarget( makeMgrParameters( strategy ), + strategyFieldNames, + strategyNumComponentsPerField, + target ) ); + continue; + } + try { EXPECT_TRUE( hypre::hypredrive::buildInputArgsParseTarget( makeMgrParameters( strategy ), diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index 6524828ec23..de8bd283413 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -296,13 +296,6 @@ void SinglePhaseMixedMFD::implicitStepSetup( real64 const & time_n, applyFacePressureBCValues( time_n + dt, domain ); } -void SinglePhaseMixedMFD::implicitStepComplete( real64 const & time, - real64 const & dt, - DomainPartition & domain ) -{ - SinglePhaseBase::implicitStepComplete( time, dt, domain ); -} - namespace { char const faceBcLogMessage[] = diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp index a81c78220d2..1665183c2c5 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp @@ -120,11 +120,6 @@ class SinglePhaseMixedMFD : public SinglePhaseBase real64 const & dt, DomainPartition & domain ) override; - virtual void - implicitStepComplete( real64 const & time, - real64 const & dt, - DomainPartition & domain ) override; - virtual void assembleFluxTerms( real64 const dt, DomainPartition const & domain, diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp index 5b2bbf6b513..cab481b9f7c 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp @@ -25,6 +25,7 @@ #include "constitutive/fluid/singlefluid/SingleFluidFields.hpp" #include "constitutive/permeability/PermeabilityBase.hpp" #include "finiteVolume/mimeticInnerProducts/AdaptiveInnerProduct.hpp" +#include "finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp" #include "linearAlgebra/interfaces/InterfaceTypes.hpp" #include "mesh/MeshLevel.hpp" #include "mixedMimetic/MixedMimeticDispatch.hpp" @@ -33,7 +34,6 @@ #include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" #include "physicsSolvers/fluidFlow/SinglePhaseBaseFields.hpp" #include "physicsSolvers/fluidFlow/StencilAccessors.hpp" -#include "physicsSolvers/PhysicsSolverBaseKernels.hpp" namespace geos { @@ -598,39 +598,25 @@ class TpfaCondensedFluxKernel tmp = ei[0]; ei[0] = ei[1]; ei[1] = tmp; } - // face geometry (same construction as TPFAInnerProduct::computeM) + // the same one-sided conductance and floor as the diagonal entries of TPFAInnerProduct::computeM real64 const areaTolerance = m_lengthTolerance * m_lengthTolerance; real64 const weightTolerance = 1e-30 * m_lengthTolerance; - real64 faceCenter[3], faceNormalRef[3]; - real64 const faceArea = - computationalGeometry::centroid_3DPolygon( m_faceToNodes[kf], - m_nodePosition, - faceCenter, - faceNormalRef, - areaTolerance ); - // per-side one-sided coefficients: a_K = invMob_K / t_K and gravity terms real64 a[2]{}, dA_dp[2]{}, grav[2]{}, dGrav_dp[2]{}; for( integer k = 0; k < numElems; ++k ) { - real64 faceNormal[3] = { faceNormalRef[0], faceNormalRef[1], faceNormalRef[2] }; - real64 cellToFaceVec[3]; - mimeticInnerProduct::MimeticInnerProductHelpers::computeCellToFacetVector( cellToFaceVec, - faceCenter, - m_elemCenter[er[k]][esr[k]][ei[k]] ); - mimeticInnerProduct::MimeticInnerProductHelpers::orientNormalOutward( cellToFaceVec, faceNormal ); - real64 const c2fDistance = LvArray::tensorOps::normalize< 3 >( cellToFaceVec ); - real64 const perm[3] = { m_elemPerm[er[k]][esr[k]][ei[k]][0][0], m_elemPerm[er[k]][esr[k]][ei[k]][0][1], m_elemPerm[er[k]][esr[k]][ei[k]][0][2] }; - real64 faceConormal[3]; - LvArray::tensorOps::hadamardProduct< 3 >( faceConormal, perm, faceNormal ); - real64 Tii = LvArray::tensorOps::AiBi< 3 >( cellToFaceVec, faceConormal ) * faceArea / c2fDistance; - Tii = LvArray::math::max( Tii, weightTolerance ); - real64 const t = LvArray::math::abs( Tii ); + real64 const t = LvArray::math::max( mimeticInnerProduct::TPFAInnerProduct::computeOneSidedTrans( m_nodePosition, + m_faceToNodes, + kf, + m_elemCenter[er[k]][esr[k]][ei[k]], + perm, + areaTolerance ), + weightTolerance ); real64 const mob = m_mob[er[k]][esr[k]][ei[k]]; real64 const invMob = 1.0 / mob; @@ -815,155 +801,6 @@ class TpfaCondensedFluxKernelFactory }; -/******************************** ResidualNormKernel ********************************/ - -/** - * @class ResidualNormKernel - * @brief Computes the norm of the face-based constitutive residuals, normalized by a - * pressure scale built from the adjacent cell pressures at the previous converged step. - */ -class ResidualNormKernel : public physicsSolverBaseKernels::ResidualNormKernelBase< 1 > -{ -public: - - using Base = physicsSolverBaseKernels::ResidualNormKernelBase< 1 >; - using Base::m_minNormalizer; - using Base::m_rankOffset; - using Base::m_localResidual; - using Base::m_dofNumber; - - template< typename VIEWTYPE > - using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; - - using SinglePhaseFlowAccessors = - StencilAccessors< fields::flow::pressure_n >; - - ResidualNormKernel( globalIndex const rankOffset, - arrayView1d< real64 const > const & localResidual, - arrayView1d< globalIndex const > const & dofNumber, - arrayView1d< localIndex const > const & ghostRank, - SortedArrayView< localIndex const > const & regionFilter, - FaceManager const & faceManager, - SinglePhaseFlowAccessors const & singlePhaseFlowAccessors, - real64 const minNormalizer ) - : Base( rankOffset, - localResidual, - dofNumber, - ghostRank, - minNormalizer ), - m_regionFilter( regionFilter ), - m_elemRegionList( faceManager.elementRegionList() ), - m_elemSubRegionList( faceManager.elementSubRegionList() ), - m_elemList( faceManager.elementList() ), - m_pres_n( singlePhaseFlowAccessors.get( fields::flow::pressure_n {} ) ) - {} - - GEOS_HOST_DEVICE - void computePressureNormalizer( localIndex const kf, - real64 & pressureNormalizer ) const - { - integer elemCounter = 0; - for( integer k = 0; k < m_elemRegionList.size( 1 ); ++k ) - { - localIndex const er = m_elemRegionList[kf][k]; - localIndex const esr = m_elemSubRegionList[kf][k]; - localIndex const ei = m_elemList[kf][k]; - bool const onBoundary = (er == -1 || esr == -1 || ei == -1); - - if( !onBoundary && m_regionFilter.contains( er ) ) - { - pressureNormalizer = pressureNormalizer + LvArray::math::abs( m_pres_n[er][esr][ei] ); - elemCounter++; - } - } - pressureNormalizer /= elemCounter; - } - - GEOS_HOST_DEVICE - virtual void computeLinf( localIndex const kf, - LinfStackVariables & stack ) const override - { - if( m_dofNumber[kf] >= 0 ) - { - real64 pressureNormalizer = 0.0; - computePressureNormalizer( kf, pressureNormalizer ); - - stack.localValue[0] = stack.localValue[0] - + LvArray::math::abs( m_localResidual[stack.localRow] ) / LvArray::math::max( m_minNormalizer, pressureNormalizer ); - } - } - - GEOS_HOST_DEVICE - virtual void computeL2( localIndex const kf, - L2StackVariables & stack ) const override - { - if( m_dofNumber[kf] >= 0 ) - { - real64 pressureNormalizer = 0.0; - computePressureNormalizer( kf, pressureNormalizer ); - - real64 const val = m_localResidual[stack.localRow]; - stack.localValue[0] += val * val; - stack.localNormalizer[0] += LvArray::math::max( m_minNormalizer, pressureNormalizer ); - } - } - -protected: - - /// Filter to identify the target regions of the solver - SortedArrayView< localIndex const > const m_regionFilter; - - /// Views on the maps face to elements - arrayView2d< localIndex const > const m_elemRegionList; - arrayView2d< localIndex const > const m_elemSubRegionList; - arrayView2d< localIndex const > const m_elemList; - - /// View on pressure at the previous converged time step - ElementViewConst< arrayView1d< real64 const > > const m_pres_n; - -}; - -/** - * @class ResidualNormKernelFactory - */ -class ResidualNormKernelFactory -{ -public: - - template< typename POLICY > - static void - createAndLaunch( physicsSolverBaseKernels::NormType const normType, - globalIndex const rankOffset, - string const & dofKey, - arrayView1d< real64 const > const & localResidual, - SortedArrayView< localIndex const > const & regionFilter, - string const & solverName, - ElementRegionManager const & elemManager, - FaceManager const & faceManager, - real64 const minNormalizer, - real64 (& residualNorm)[1], - real64 (& residualNormalizer)[1] ) - { - arrayView1d< globalIndex const > const dofNumber = faceManager.getReference< array1d< globalIndex > >( dofKey ); - arrayView1d< integer const > const ghostRank = faceManager.ghostRank(); - - using kernelType = ResidualNormKernel; - typename kernelType::SinglePhaseFlowAccessors flowAccessors( elemManager, solverName ); - - ResidualNormKernel kernel( rankOffset, localResidual, dofNumber, ghostRank, - regionFilter, faceManager, flowAccessors, minNormalizer ); - if( normType == physicsSolverBaseKernels::NormType::Linf ) - { - ResidualNormKernel::launchLinf< POLICY >( faceManager.size(), kernel, residualNorm ); - } - else // L2 norm - { - ResidualNormKernel::launchL2< POLICY >( faceManager.size(), kernel, residualNorm, residualNormalizer ); - } - } - -}; - } // namespace singlePhaseMixedMFDKernels } // namespace geos From d0b0b5d00c64e501903b61d915eb34a43e37eee0 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Tue, 15 Sep 2026 10:36:33 -0700 Subject: [PATCH 36/64] wip: add input deck for robustness --- .../mfd_robustness.py | 253 ++++++++++++++++++ .../mfd_robustness_template.xml | 91 +++++++ 2 files changed, 344 insertions(+) create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness_template.xml diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py new file mode 100644 index 00000000000..9dbac8bf494 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python3 +"""h-robustness, contrast-robustness and convergence of SinglePhaseMixedMFD on regular hexahedral and tetrahedral meshes. + +Standard library only. Each case is a deck generated from mfd_robustness_template.xml, run serially +with geosx, and parsed from the log and the ASCII VTK output. + + python3 mfd_robustness.py --geosx /path/to/geosx # everything: h-sweep + contrast sweep, hex and tet + python3 mfd_robustness.py --geosx geosx --mesh tet --levels 4,8,16 + python3 mfd_robustness.py --geosx geosx --contrasts 1,1e2,1e4,1e6 --pattern layersX --level 16 + python3 mfd_robustness.py --geosx geosx --mesh tet --mfd-percents 0,25,50,75,100 --level 8 # prescribed MFD fraction + python3 mfd_robustness.py --geosx geosx --mfd-percent 50 # sweeps at a fixed 50 % MFD + python3 mfd_robustness.py --geosx geosx --table # mesh x mfd% x n x contrast table, n up to 64 + python3 mfd_robustness.py --geosx geosx --table --np 8 --table-levels 32,64 # the large levels on 8 MPI ranks + +Exact solution used for the convergence check (homogeneous permeability k, injection rate density q): + p(x) = p0 + q mu / (2 rho k) x (1 - x) +""" +import argparse, glob, math, os, re, shutil, subprocess, sys, time + +HERE = os.path.dirname(os.path.abspath(__file__)) +STATES = {"tpfa": "1.0e+20", "mixed": "0.1", "mfd": "1.0e-20"} +RHO, MU, K_REF, L = 1000.0, 1.0e-3, 1.0e-14, 1.0 +VOLUME = 1.0 * 1.0 * 2.0 + +def linspace(a, b, m): + return [a + (b - a) * i / m for i in range(m + 1)] + +def mesh_params(mesh, n, pattern): + """four cell blocks: layers of thickness 0.5 along z (parallel to the flow) or two bands along x (in series); + returns the deck tokens and the cell edges along x, y, z""" + elem = "C3D8" if mesh == "hex" else "C3D4" + ye = linspace(0.0, 1.0, n) + if pattern == "layersX": + m = max(n // 2, 1) + xc, nx = "0, 0.5, 1", f"{m}, {m}" + zc, nz = "0, 2", str(2 * n) + xe = [0.0] + [0.5 * b + 0.5 * i / m for b in range(2) for i in range(1, m + 1)] + ze = linspace(0.0, 2.0, 2 * n) + else: + m = max(n // 2, 1) + xc, nx = "0, 1", str(n) + zc, nz = "0, 0.5, 1, 1.5, 2", ", ".join(str(m) for _ in range(4)) + xe = linspace(0.0, 1.0, n) + ze = [0.0] + [0.5 * b + 0.5 * i / m for b in range(4) for i in range(1, m + 1)] + nblocks = 2 if pattern == "layersX" else 4 + names = ", ".join(f"cb{i}" for i in range(nblocks)) + tokens = dict(elementType=elem, xCoords=xc, nx=nx, ny=str(n), zCoords=zc, nz=nz, cellBlockNames=names, + blocksA="cb0, cb2" if nblocks == 4 else "cb0", blocksB="cb1, cb3" if nblocks == 4 else "cb1") + return tokens, (xe, ye, ze) + +def prescription(edges, percent): + """prescribedMfdFlag = 0 on every cell, then 1 on the first m cells in z-major order (m = percent of the + cells, rounded), selected with up to three boxes; the resulting percentage is exact""" + if percent is None: + return "", "" + xe, ye, ze = edges; nx, ny, nz = len(xe) - 1, len(ye) - 1, len(ze) - 1 + m = int(round(percent / 100.0 * nx * ny * nz)) + a, rem = divmod(m, nx * ny); b, c = divmod(rem, nx) + eps = 1.0e-6 + boxes = [] + if a > 0: boxes.append((xe[0], xe[-1], ye[0], ye[-1], ze[0], ze[a])) + if a < nz and b > 0: boxes.append((xe[0], xe[-1], ye[0], ye[b], ze[a], ze[a + 1])) + if a < nz and b < ny and c > 0: boxes.append((xe[0], xe[c], ye[b], ye[b + 1], ze[a], ze[a + 1])) + box_xml = "".join(f' \n' for i, (x0, x1, y0, y1, z0, z1) in enumerate(boxes)) + spec_xml = (' \n' + + "".join(f' \n' + for i in range(len(boxes)))) + return box_xml, spec_xml + +def write_case(template, workdir, mesh, n, pattern, contrast, state, rate, p0, percent=None): + tokens, edges = mesh_params(mesh, n, pattern) + tokens["prescriptionBoxes"], tokens["prescription"] = prescription(edges, percent) + write_case.cells = tuple(len(e) - 1 for e in edges) # cells per axis, for the MPI partitioning + tokens.update(consistencyTolerance=STATES[state], permA=f"{K_REF:.6e}", permB=f"{K_REF / contrast:.6e}", sourceRate=f"{rate:.6e}", p0=f"{p0:.6e}") + text = open(template).read() + for k, v in tokens.items(): + text = text.replace(f"@{k}@", v) + assert "@" not in text, "unreplaced token in the template" + os.makedirs(workdir, exist_ok=True) + with open(os.path.join(workdir, "deck.xml"), "w") as f: + f.write(text) + +SOLVE = re.compile(r"Linear Solver \| (\w+) \| Unknowns: ([\d,]+) \| Nonzeros: [\d,]+ \| Iterations: (\d+)") + +def partition(np, cells): + """split np ranks over the three axes of the internal mesh, each prime factor going to the axis + with the most cells per rank, so that no axis gets more partitions than cells""" + p = [1, 1, 1]; f = 2; m = np + factors = [] + while m > 1: + while m % f == 0: factors.append(f); m //= f + f += 1 + for f in sorted(factors, reverse=True): + axis = max(range(3), key=lambda i: cells[i] / p[i]) + p[axis] *= f + return p + +def run_case(geosx, workdir, timeout, np=1, mpirun="mpirun", cells=(1, 1, 1)): + t0 = time.time() + cmd = [geosx, "-i", "deck.xml"] + if np > 1: + px, py, pz = partition(np, cells) + cmd = [mpirun, "-np", str(np), geosx, "-i", "deck.xml", "-x", str(px), "-y", str(py), "-z", str(pz)] + with open(os.path.join(workdir, "run.log"), "w") as log: + try: + subprocess.run(cmd, cwd=workdir, stdout=log, stderr=subprocess.STDOUT, timeout=timeout) + except subprocess.TimeoutExpired: + pass + text = open(os.path.join(workdir, "run.log"), errors="replace").read() + r = dict(wall=time.time() - t0, error="Error" in text or "Signal encountered" in text) + m = re.search(r"eta = 1 on (\d+) / (\d+) cells", text) + if m: r["mfdCells"], r["cells"] = int(m.group(1)), int(m.group(2)) + m = re.search(r"flux dofs (\d+) non-condensed \(saddle point\), (\d+) condensed", text) + if m: r["live"], r["condensed"] = int(m.group(1)), int(m.group(2)) + s = SOLVE.findall(text) + if s: + r["status"], r["unknowns"], r["iterations"] = s[0][0], int(s[0][1].replace(",", "")), int(s[0][2]) + r["numSolves"] = len(s) + return r + +def read_vtu_arrays(path, names): + text = open(path).read() + out = {} + for name in names: + m = re.search(r']*Name="%s"[^>]*>(.*?)' % name, text, re.S) + if m: + body = re.sub(r"", "", m.group(1), flags=re.S) + out[name] = [float(v) for v in body.split()] + return out + +def exact_series(x, q, contrast): + """-(k p')' = q on [0,1], p(0) = p(1) = 0, k = K_REF on [0, 0.5), K_REF/contrast on [0.5, 1]""" + k = [K_REF, K_REF / contrast] + # u = -k p' = q x + c ; p(x) = -int_0^x (q s + c)/k(s) ds ; p(1) = 0 fixes c + def integrals(x): + i0 = i1 = 0.0; t0 = 0.0 + for j in range(2): + t1 = min(0.5 * (j + 1), x) + if t1 <= t0: break + i0 += (t1 - t0) / k[j]; i1 += 0.5 * (t1 * t1 - t0 * t0) / k[j]; t0 = t1 + return i0, i1 + I0, I1 = integrals(1.0); c = -q * I1 / I0 + i0, i1 = integrals(x) + return -(q * i1 + c * i0) * MU / RHO + +def pressure_error(workdir, rate, contrast, p0, pattern): + """relative L2 error of the cell pressures against the exact solution (homogeneous, or layers in series)""" + files = sorted(glob.glob(os.path.join(workdir, "robustness", "*", "mesh", "Level0", "*", "rank_*.vtu"))) + files = [f for f in files if "/000000/" not in f] # skip the initial-time output + if not files or (contrast != 1.0 and pattern != "layersX"): + return None, None + q = -rate / VOLUME # injected mass rate per unit volume + num = den = 0.0 + for f in files: + a = read_vtu_arrays(f, ["pressure", "elementCenter"]) + p, c = a.get("pressure", []), a.get("elementCenter", []) + for i, pi in enumerate(p): + x = c[3 * i] + pe = exact_series(x, q, contrast) if contrast != 1.0 else q * MU / (2.0 * RHO * K_REF) * x * (L - x) + num += (pi - p0 - pe) ** 2; den += pe ** 2 + return math.sqrt(num / den) if den > 0 else None, q * MU / (8.0 * RHO * K_REF) + +def main(): + ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--geosx", default=os.environ.get("GEOSX", shutil.which("geosx") or ""), help="geosx executable") + ap.add_argument("--template", default=os.path.join(HERE, "mfd_robustness_template.xml")) + ap.add_argument("--workdir", default="mfd_robustness_runs") + ap.add_argument("--mesh", default="hex,tet", help="hex, tet or both (comma separated)") + ap.add_argument("--levels", default="4,8,16,32", help="cells per unit length for the h-sweep (even numbers)") + ap.add_argument("--level", type=int, default=16, help="mesh size of the contrast sweep") + ap.add_argument("--contrasts", default="1,1e2,1e4,1e6") + ap.add_argument("--pattern", default="layersZ", choices=["layersZ", "layersX"], help="contrast layers parallel (Z) or in series (X) with the flow") + ap.add_argument("--states", default="tpfa,mixed,mfd") + ap.add_argument("--rate", type=float, default=-0.16, help="total mass rate [kg/s], negative = injection; -0.16 gives p_max - p0 = 1e6 Pa") + ap.add_argument("--p0", type=float, default=1.0e7, help="reference pressure of the initial state and of the two Dirichlet faces") + ap.add_argument("--mfd-percent", type=float, default=None, help="prescribe the MFD product on this percentage of the cells (a y-slab) instead of classifying; 100 = full MFD") + ap.add_argument("--mfd-percents", default=None, help="extra sweep over prescribed MFD percentages at --level, e.g. 0,25,50,75,100") + ap.add_argument("--table", action="store_true", help="4-D table mesh x mfd%% x n x contrast (layers in series), entries 'rel L2 error / iterations'") + ap.add_argument("--table-percents", default="0,25,75,100"); ap.add_argument("--table-levels", default="2,4,8,16,32,64"); ap.add_argument("--table-contrasts", default="1,1e4,1e7") + ap.add_argument("--np", type=int, default=1, help="number of MPI ranks (1 = run geosx directly)") + ap.add_argument("--mpirun", default="mpirun", help="MPI launcher used when --np > 1") + ap.add_argument("--timeout", type=int, default=3600) + ap.add_argument("--write-only", action="store_true", help="generate the decks without running") + a = ap.parse_args() + if not a.geosx and not a.write_only: + sys.exit("geosx executable not found: pass --geosx or set GEOSX") + + def do(mesh, n, pattern, contrast, state, percent=None): + tag = state if percent is None else f"mfd{percent:g}pct" + d = os.path.join(a.workdir, f"{mesh}_n{n}_{pattern}_c{contrast:g}_{tag}") + write_case(a.template, d, mesh, n, pattern, contrast, state, a.rate, a.p0, percent) + if a.write_only: + return None + r = run_case(a.geosx, d, a.timeout, a.np, a.mpirun, write_case.cells) + r["l2"], r["pmax"] = pressure_error(d, a.rate, contrast, a.p0, pattern) + return r + + if a.table: + contrasts = [float(x) for x in a.table_contrasts.split(",")] + print(f"\nrelative L2 pressure error / GMRES iterations of the first solve; permeability layers in series along the flow") + print(f"{'mesh':>4} {'mfd%':>5} {'n':>3} {'cells':>8} | " + " | ".join(f"{'contrast ' + format(c, 'g'):>20}" for c in contrasts) + " | wall") + for mesh in a.mesh.split(","): + for percent in [float(x) for x in a.table_percents.split(",")]: + for n in [int(x) for x in a.table_levels.split(",")]: + cells, walls = [], [] + for contrast in contrasts: + r = do(mesh, n, "layersX", contrast, "mfd", percent) + if r is None: + continue + got = 100.0 * r.get("mfdCells", 0) / max(r.get("cells", 1), 1) + err = f"{r['l2']:.2e}" if r["l2"] is not None else " n/a " + cells.append(f"{err} / {r.get('iterations', '-'):>3}" + ("!" if r["error"] else " ") + (f"({got:.0f}%)" if abs(got - percent) > 0.5 else " ")) + walls.append(r["wall"]) + if not cells: + continue + print(f"{mesh:>4} {percent:>5g} {n:>3} {r.get('cells', '-'):>8} | " + " | ".join(f"{c:>20}" for c in cells) + f" | {sum(walls):6.0f} s", flush=True) + return + + for mesh in a.mesh.split(","): + print(f"\n=== {mesh}: h-sweep, contrast 1 (iterations of the first solve; L2 error vs the quadratic solution, observed order) ===") + print(f"{'state':>6} {'n':>4} {'cells':>8} {'unknowns':>9} {'mfd%':>5} {'its':>4} {'rel L2 err':>11} {'order':>6} {'s':>6}") + states = a.states.split(",") if a.mfd_percent is None else [f"mfd{a.mfd_percent:g}pct"] + for state in states: + prev = None + for n in [int(x) for x in a.levels.split(",")]: + r = do(mesh, n, "layersZ", 1.0, "mfd", a.mfd_percent) if a.mfd_percent is not None else do(mesh, n, "layersZ", 1.0, state) + if r is None: continue + order = math.log(prev / r["l2"]) / math.log(2.0) if (prev and r["l2"]) else float("nan") + prev = r["l2"] + frac = 100.0 * r.get("mfdCells", 0) / max(r.get("cells", 1), 1) + print(f"{state:>6} {n:>4} {r.get('cells','-'):>8} {r.get('unknowns','-'):>9} {frac:>5.0f} {r.get('iterations','-'):>4} " + f"{(r['l2'] if r['l2'] is not None else float('nan')):>11.3e} {order:>6.2f} {r['wall']:>6.1f}{' ERROR' if r['error'] else ''}", flush=True) + print(f"\n=== {mesh}: contrast sweep at n = {a.level}, pattern {a.pattern} ===") + print(f"{'state':>6} {'contrast':>9} {'mfd%':>5} {'live':>8} {'its':>4} {'s':>6}") + for state in states: + for contrast in [float(x) for x in a.contrasts.split(",")]: + r = do(mesh, a.level, a.pattern, contrast, "mfd", a.mfd_percent) if a.mfd_percent is not None else do(mesh, a.level, a.pattern, contrast, state) + if r is None: continue + frac = 100.0 * r.get("mfdCells", 0) / max(r.get("cells", 1), 1) + print(f"{state:>6} {contrast:>9g} {frac:>5.0f} {r.get('live','-'):>8} {r.get('iterations','-'):>4} {r['wall']:>6.1f}{' ERROR' if r['error'] else ''}", flush=True) + if a.mfd_percents: + print(f"\n=== {mesh}: prescribed MFD percentage sweep at n = {a.level}, contrast 1 (y-slab of MFD cells) ===") + print(f"{'asked%':>7} {'mfd%':>5} {'live':>8} {'condensed':>9} {'its':>4} {'rel L2 err':>11} {'s':>6}") + for percent in [float(x) for x in a.mfd_percents.split(",")]: + r = do(mesh, a.level, "layersZ", 1.0, "mfd", percent) + if r is None: continue + frac = 100.0 * r.get("mfdCells", 0) / max(r.get("cells", 1), 1) + print(f"{percent:>7g} {frac:>5.0f} {r.get('live','-'):>8} {r.get('condensed','-'):>9} {r.get('iterations','-'):>4} " + f"{(r['l2'] if r['l2'] is not None else float('nan')):>11.3e} {r['wall']:>6.1f}{' ERROR' if r['error'] else ''}", flush=True) + +if __name__ == "__main__": + main() diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness_template.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness_template.xml new file mode 100644 index 00000000000..4b66c2f176b --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness_template.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + +@prescriptionBoxes@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +@prescription@ + + + + + + + + + + + From f4040dafe18618f4cc841c5f4cb15128fdf0fa60 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Tue, 15 Sep 2026 11:03:06 -0700 Subject: [PATCH 37/64] wip: add input deck for robustness --- .../mfd_robustness.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py index 9dbac8bf494..d3fac837aa6 100644 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py @@ -117,6 +117,10 @@ def run_case(geosx, workdir, timeout, np=1, mpirun="mpirun", cells=(1, 1, 1)): if s: r["status"], r["unknowns"], r["iterations"] = s[0][0], int(s[0][1].replace(",", "")), int(s[0][2]) r["numSolves"] = len(s) + else: + r["error"] = True + lines = [l for l in text.splitlines() if l.strip() and not l.startswith("-")] + print(f" no linear solve in {os.path.join(workdir, 'run.log')}: {lines[-1][:120] if lines else 'empty log'}", file=sys.stderr, flush=True) return r def read_vtu_arrays(path, names): @@ -183,8 +187,20 @@ def main(): ap.add_argument("--timeout", type=int, default=3600) ap.add_argument("--write-only", action="store_true", help="generate the decks without running") a = ap.parse_args() - if not a.geosx and not a.write_only: - sys.exit("geosx executable not found: pass --geosx or set GEOSX") + # geosx runs with the case directory as working directory: resolve the paths given on the command line now + if not a.write_only: + geosx = os.path.abspath(a.geosx) if os.path.exists(a.geosx) else shutil.which(a.geosx or "geosx") + if not geosx or not os.access(geosx, os.X_OK): + sys.exit(f"geosx executable not found or not executable: '{a.geosx}' (pass --geosx with a valid path or set GEOSX)") + a.geosx = geosx + if a.np > 1: + mpirun = os.path.abspath(a.mpirun) if os.path.exists(a.mpirun) else shutil.which(a.mpirun) + if not mpirun: + sys.exit(f"MPI launcher not found: '{a.mpirun}' (pass --mpirun)") + a.mpirun = mpirun + a.template = os.path.abspath(a.template) + if not os.path.exists(a.template): + sys.exit(f"template not found: {a.template}") def do(mesh, n, pattern, contrast, state, percent=None): tag = state if percent is None else f"mfd{percent:g}pct" From ae2208a705052b6792135cda9446baad0871d361 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Tue, 15 Sep 2026 11:23:28 -0700 Subject: [PATCH 38/64] =?UTF-8?q?fix:=20shadow=20declaration=20of=20?= =?UTF-8?q?=E2=80=98prescribed=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../polyhedralDiscretizations/mfd_robustness.py | 10 +++++++--- .../mfd_robustness_template.xml | 2 +- .../consistency/ConsistencyAdaptation.cpp | 12 ++++++------ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py index d3fac837aa6..d2eacf8d873 100644 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py @@ -10,7 +10,7 @@ python3 mfd_robustness.py --geosx geosx --mesh tet --mfd-percents 0,25,50,75,100 --level 8 # prescribed MFD fraction python3 mfd_robustness.py --geosx geosx --mfd-percent 50 # sweeps at a fixed 50 % MFD python3 mfd_robustness.py --geosx geosx --table # mesh x mfd% x n x contrast table, n up to 64 - python3 mfd_robustness.py --geosx geosx --table --np 8 --table-levels 32,64 # the large levels on 8 MPI ranks + python3 mfd_robustness.py --geosx geosx --table --np 8 --table-levels 32,64 --no-output # the large levels on 8 MPI ranks, iterations only Exact solution used for the convergence check (homogeneous permeability k, injection rate density q): p(x) = p0 + q mu / (2 rho k) x (1 - x) @@ -68,12 +68,15 @@ def prescription(edges, percent): for i in range(len(boxes)))) return box_xml, spec_xml -def write_case(template, workdir, mesh, n, pattern, contrast, state, rate, p0, percent=None): +def write_case(template, workdir, mesh, n, pattern, contrast, state, rate, p0, percent=None, output=True): tokens, edges = mesh_params(mesh, n, pattern) tokens["prescriptionBoxes"], tokens["prescription"] = prescription(edges, percent) write_case.cells = tuple(len(e) - 1 for e in edges) # cells per axis, for the MPI partitioning tokens.update(consistencyTolerance=STATES[state], permA=f"{K_REF:.6e}", permB=f"{K_REF / contrast:.6e}", sourceRate=f"{rate:.6e}", p0=f"{p0:.6e}") text = open(template).read() + if not output: + text = re.sub(r'\s*]*/>', "", text) + text = re.sub(r'\s*.*?', "", text, flags=re.S) for k, v in tokens.items(): text = text.replace(f"@{k}@", v) assert "@" not in text, "unreplaced token in the template" @@ -182,6 +185,7 @@ def main(): ap.add_argument("--mfd-percents", default=None, help="extra sweep over prescribed MFD percentages at --level, e.g. 0,25,50,75,100") ap.add_argument("--table", action="store_true", help="4-D table mesh x mfd%% x n x contrast (layers in series), entries 'rel L2 error / iterations'") ap.add_argument("--table-percents", default="0,25,75,100"); ap.add_argument("--table-levels", default="2,4,8,16,32,64"); ap.add_argument("--table-contrasts", default="1,1e4,1e7") + ap.add_argument("--no-output", action="store_true", help="no VTK output: iterations only, no pressure error (use for the large levels)") ap.add_argument("--np", type=int, default=1, help="number of MPI ranks (1 = run geosx directly)") ap.add_argument("--mpirun", default="mpirun", help="MPI launcher used when --np > 1") ap.add_argument("--timeout", type=int, default=3600) @@ -205,7 +209,7 @@ def main(): def do(mesh, n, pattern, contrast, state, percent=None): tag = state if percent is None else f"mfd{percent:g}pct" d = os.path.join(a.workdir, f"{mesh}_n{n}_{pattern}_c{contrast:g}_{tag}") - write_case(a.template, d, mesh, n, pattern, contrast, state, a.rate, a.p0, percent) + write_case(a.template, d, mesh, n, pattern, contrast, state, a.rate, a.p0, percent, not a.no_output) if a.write_only: return None r = run_case(a.geosx, d, a.timeout, a.np, a.mpirun, write_case.cells) diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness_template.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness_template.xml index 4b66c2f176b..9bc25afbda3 100644 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness_template.xml +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness_template.xml @@ -85,7 +85,7 @@ - + diff --git a/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp b/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp index ab185ff3dd1..dcabf6de820 100644 --- a/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp +++ b/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp @@ -58,13 +58,13 @@ ConsistencyAdaptation::Report ConsistencyAdaptation::classify( MeshLevel & mesh, ? applyConsistencyLayer( mesh, regionNames, regionFilter, permeability, params, neighbors ) : report.numCells; - std::pair< localIndex, localIndex > const prescribed = applyPrescription( mesh, regionNames, neighbors ); - report.numPrescribed0 = prescribed.first; - report.numPrescribed1 = prescribed.second; + std::pair< localIndex, localIndex > const prescribedCounts = applyPrescription( mesh, regionNames, neighbors ); + report.numPrescribed0 = prescribedCounts.first; + report.numPrescribed1 = prescribedCounts.second; - std::pair< localIndex, localIndex > const degenerate = applyDegeneracyLayer( mesh, regionNames, params.degeneracyTolerance, neighbors ); - report.numDegenerate = degenerate.first; - report.numPrescribedDegenerate = degenerate.second; + std::pair< localIndex, localIndex > const degenerateCounts = applyDegeneracyLayer( mesh, regionNames, params.degeneracyTolerance, neighbors ); + report.numDegenerate = degenerateCounts.first; + report.numPrescribedDegenerate = degenerateCounts.second; // final count, and the invariant of the layers: a prescribed cell is never altered elemManager.forElementSubRegions< ElementSubRegionBase >( regionNames, From 431ad02b0d8462a2cc77755f5744324c4de49bb4 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Tue, 15 Sep 2026 15:29:16 -0700 Subject: [PATCH 39/64] wip: improve input deck for robustness. --- .../polyhedralDiscretizations/mfd_robustness.py | 4 ++-- ...robustness_template.xml => mfd_robustness_template.xml.in} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename inputFiles/singlePhaseFlow/polyhedralDiscretizations/{mfd_robustness_template.xml => mfd_robustness_template.xml.in} (100%) diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py index d2eacf8d873..7272ad34827 100644 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """h-robustness, contrast-robustness and convergence of SinglePhaseMixedMFD on regular hexahedral and tetrahedral meshes. -Standard library only. Each case is a deck generated from mfd_robustness_template.xml, run serially +Standard library only. Each case is a deck generated from mfd_robustness_template.xml.in, run serially with geosx, and parsed from the log and the ASCII VTK output. python3 mfd_robustness.py --geosx /path/to/geosx # everything: h-sweep + contrast sweep, hex and tet @@ -171,7 +171,7 @@ def pressure_error(workdir, rate, contrast, p0, pattern): def main(): ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) ap.add_argument("--geosx", default=os.environ.get("GEOSX", shutil.which("geosx") or ""), help="geosx executable") - ap.add_argument("--template", default=os.path.join(HERE, "mfd_robustness_template.xml")) + ap.add_argument("--template", default=os.path.join(HERE, "mfd_robustness_template.xml.in")) ap.add_argument("--workdir", default="mfd_robustness_runs") ap.add_argument("--mesh", default="hex,tet", help="hex, tet or both (comma separated)") ap.add_argument("--levels", default="4,8,16,32", help="cells per unit length for the h-sweep (even numbers)") diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness_template.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness_template.xml.in similarity index 100% rename from inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness_template.xml rename to inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness_template.xml.in From a0ffe6470658922d9dd9bce5ee0d2a5725dbbba0 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Thu, 17 Sep 2026 00:33:39 -0700 Subject: [PATCH 40/64] wip: srun --overlap --- .../mfd_robustness.py | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py index 7272ad34827..3d6e5d18b55 100644 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py @@ -11,11 +11,12 @@ python3 mfd_robustness.py --geosx geosx --mfd-percent 50 # sweeps at a fixed 50 % MFD python3 mfd_robustness.py --geosx geosx --table # mesh x mfd% x n x contrast table, n up to 64 python3 mfd_robustness.py --geosx geosx --table --np 8 --table-levels 32,64 --no-output # the large levels on 8 MPI ranks, iterations only + python3 mfd_robustness.py --geosx geosx --table --mpirun "srun --overlap" # inside a Slurm step: every run through srun, serial included Exact solution used for the convergence check (homogeneous permeability k, injection rate density q): p(x) = p0 + q mu / (2 rho k) x (1 - x) """ -import argparse, glob, math, os, re, shutil, subprocess, sys, time +import argparse, glob, math, os, re, shlex, shutil, subprocess, sys, time HERE = os.path.dirname(os.path.abspath(__file__)) STATES = {"tpfa": "1.0e+20", "mixed": "0.1", "mfd": "1.0e-20"} @@ -99,12 +100,15 @@ def partition(np, cells): p[axis] *= f return p -def run_case(geosx, workdir, timeout, np=1, mpirun="mpirun", cells=(1, 1, 1)): +def run_case(geosx, workdir, timeout, np=1, launcher=None, cells=(1, 1, 1)): t0 = time.time() cmd = [geosx, "-i", "deck.xml"] if np > 1: px, py, pz = partition(np, cells) - cmd = [mpirun, "-np", str(np), geosx, "-i", "deck.xml", "-x", str(px), "-y", str(py), "-z", str(pz)] + cmd += ["-x", str(px), "-y", str(py), "-z", str(pz)] + if launcher: + # "-n" is understood by mpirun, mpiexec and srun + cmd = launcher + ["-n", str(np)] + cmd with open(os.path.join(workdir, "run.log"), "w") as log: try: subprocess.run(cmd, cwd=workdir, stdout=log, stderr=subprocess.STDOUT, timeout=timeout) @@ -186,8 +190,9 @@ def main(): ap.add_argument("--table", action="store_true", help="4-D table mesh x mfd%% x n x contrast (layers in series), entries 'rel L2 error / iterations'") ap.add_argument("--table-percents", default="0,25,75,100"); ap.add_argument("--table-levels", default="2,4,8,16,32,64"); ap.add_argument("--table-contrasts", default="1,1e4,1e7") ap.add_argument("--no-output", action="store_true", help="no VTK output: iterations only, no pressure error (use for the large levels)") - ap.add_argument("--np", type=int, default=1, help="number of MPI ranks (1 = run geosx directly)") - ap.add_argument("--mpirun", default="mpirun", help="MPI launcher used when --np > 1") + ap.add_argument("--np", type=int, default=1, help="number of MPI ranks (1 = run geosx directly unless --mpirun is given)") + ap.add_argument("--mpirun", default=None, help="MPI launcher, options allowed, e.g. 'srun --overlap'; default: mpirun when --np > 1, " + "none when --np 1. Give it explicitly to launch the serial runs too (needed inside a Slurm step)") ap.add_argument("--timeout", type=int, default=3600) ap.add_argument("--write-only", action="store_true", help="generate the decks without running") a = ap.parse_args() @@ -197,11 +202,12 @@ def main(): if not geosx or not os.access(geosx, os.X_OK): sys.exit(f"geosx executable not found or not executable: '{a.geosx}' (pass --geosx with a valid path or set GEOSX)") a.geosx = geosx - if a.np > 1: - mpirun = os.path.abspath(a.mpirun) if os.path.exists(a.mpirun) else shutil.which(a.mpirun) - if not mpirun: - sys.exit(f"MPI launcher not found: '{a.mpirun}' (pass --mpirun)") - a.mpirun = mpirun + if a.np > 1 or a.mpirun: + words = shlex.split(a.mpirun or "mpirun") + exe = os.path.abspath(words[0]) if os.path.exists(words[0]) else shutil.which(words[0]) + if not exe: + sys.exit(f"MPI launcher not found: '{words[0]}' (pass --mpirun)") + a.mpirun = [exe] + words[1:] a.template = os.path.abspath(a.template) if not os.path.exists(a.template): sys.exit(f"template not found: {a.template}") From d98775baf5f4dcce3fb62e42b28879649ae3eacb Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Thu, 17 Sep 2026 00:43:21 -0700 Subject: [PATCH 41/64] wip: reporting wall time on srun --overlap --- .../polyhedralDiscretizations/mfd_robustness.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py index 3d6e5d18b55..3479d71f1f3 100644 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py @@ -85,7 +85,8 @@ def write_case(template, workdir, mesh, n, pattern, contrast, state, rate, p0, p with open(os.path.join(workdir, "deck.xml"), "w") as f: f.write(text) -SOLVE = re.compile(r"Linear Solver \| (\w+) \| Unknowns: ([\d,]+) \| Nonzeros: [\d,]+ \| Iterations: (\d+)") +SOLVE = re.compile(r"Linear Solver \| (\w+) \| Unknowns: ([\d,]+) \| Nonzeros: [\d,]+ \| Iterations: (\d+)" + r" \| Final Rel Res: \S+ \| Setup Time: ([\d.eE+-]+) s \| Solve Time: ([\d.eE+-]+) s") def partition(np, cells): """split np ranks over the three axes of the internal mesh, each prime factor going to the axis @@ -123,6 +124,7 @@ def run_case(geosx, workdir, timeout, np=1, launcher=None, cells=(1, 1, 1)): s = SOLVE.findall(text) if s: r["status"], r["unknowns"], r["iterations"] = s[0][0], int(s[0][1].replace(",", "")), int(s[0][2]) + r["setupTime"], r["solveTime"] = float(s[0][3]), float(s[0][4]) r["numSolves"] = len(s) else: r["error"] = True @@ -224,8 +226,12 @@ def do(mesh, n, pattern, contrast, state, percent=None): if a.table: contrasts = [float(x) for x in a.table_contrasts.split(",")] - print(f"\nrelative L2 pressure error / GMRES iterations of the first solve; permeability layers in series along the flow") - print(f"{'mesh':>4} {'mfd%':>5} {'n':>3} {'cells':>8} | " + " | ".join(f"{'contrast ' + format(c, 'g'):>20}" for c in contrasts) + " | wall") + tol = re.search(r'krylovTol="([^"]+)"', open(a.template).read()) + print(f"\nrelative L2 pressure error / GMRES iterations / linear solve time [s] (setup excluded) of the first solve;" + f" krylovTol = {tol.group(1) if tol else '?'}") + print(f"isotropic permeability in series along the flow (x): k = {K_REF:g} m^2 on x < 0.5, k = {K_REF:g} / contrast on x > 0.5," + f" interface on mesh faces; p = p0 on x = 0 and x = 1, uniform source") + print(f"{'mesh':>4} {'mfd%':>5} {'n':>3} {'cells':>8} | " + " | ".join(f"{'contrast ' + format(c, 'g'):>32}" for c in contrasts) + " | wall") for mesh in a.mesh.split(","): for percent in [float(x) for x in a.table_percents.split(",")]: for n in [int(x) for x in a.table_levels.split(",")]: @@ -236,11 +242,12 @@ def do(mesh, n, pattern, contrast, state, percent=None): continue got = 100.0 * r.get("mfdCells", 0) / max(r.get("cells", 1), 1) err = f"{r['l2']:.2e}" if r["l2"] is not None else " n/a " - cells.append(f"{err} / {r.get('iterations', '-'):>3}" + ("!" if r["error"] else " ") + (f"({got:.0f}%)" if abs(got - percent) > 0.5 else " ")) + solve = f"{r['solveTime']:9.3f}" if "solveTime" in r else " n/a" + cells.append(f"{err} / {r.get('iterations', '-'):>3} / {solve}" + ("!" if r["error"] else " ") + (f"({got:.0f}%)" if abs(got - percent) > 0.5 else " ")) walls.append(r["wall"]) if not cells: continue - print(f"{mesh:>4} {percent:>5g} {n:>3} {r.get('cells', '-'):>8} | " + " | ".join(f"{c:>20}" for c in cells) + f" | {sum(walls):6.0f} s", flush=True) + print(f"{mesh:>4} {percent:>5g} {n:>3} {r.get('cells', '-'):>8} | " + " | ".join(f"{c:>32}" for c in cells) + f" | {sum(walls):6.0f} s", flush=True) return for mesh in a.mesh.split(","): From 54b9f9a1e610802dbfec4abea704ab309825d459 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Thu, 17 Sep 2026 00:53:41 -0700 Subject: [PATCH 42/64] wip: srun improved output --- .../polyhedralDiscretizations/mfd_robustness.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py index 3479d71f1f3..c6aad151211 100644 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py @@ -231,7 +231,9 @@ def do(mesh, n, pattern, contrast, state, percent=None): f" krylovTol = {tol.group(1) if tol else '?'}") print(f"isotropic permeability in series along the flow (x): k = {K_REF:g} m^2 on x < 0.5, k = {K_REF:g} / contrast on x > 0.5," f" interface on mesh faces; p = p0 on x = 0 and x = 1, uniform source") - print(f"{'mesh':>4} {'mfd%':>5} {'n':>3} {'cells':>8} | " + " | ".join(f"{'contrast ' + format(c, 'g'):>32}" for c in contrasts) + " | wall") + print("unknowns = cells + faces of the assembled matrix; reduced = cells + non-condensed flux dofs, the coupled system after the" + " condensation (a condensed flux is a one-way closure row)") + print(f"{'mesh':>4} {'mfd%':>5} {'n':>3} {'cells':>8} {'unknowns':>9} {'reduced':>9} | " + " | ".join(f"{'contrast ' + format(c, 'g'):>32}" for c in contrasts) + " | wall") for mesh in a.mesh.split(","): for percent in [float(x) for x in a.table_percents.split(",")]: for n in [int(x) for x in a.table_levels.split(",")]: @@ -247,7 +249,9 @@ def do(mesh, n, pattern, contrast, state, percent=None): walls.append(r["wall"]) if not cells: continue - print(f"{mesh:>4} {percent:>5g} {n:>3} {r.get('cells', '-'):>8} | " + " | ".join(f"{c:>32}" for c in cells) + f" | {sum(walls):6.0f} s", flush=True) + reduced = r["cells"] + r["live"] if "cells" in r and "live" in r else "-" + print(f"{mesh:>4} {percent:>5g} {n:>3} {r.get('cells', '-'):>8} {r.get('unknowns', '-'):>9} {reduced:>9} | " + + " | ".join(f"{c:>32}" for c in cells) + f" | {sum(walls):6.0f} s", flush=True) return for mesh in a.mesh.split(","): From ee395f6d63a83e9fdec62d544618e00747ae35f6 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Thu, 17 Sep 2026 12:39:08 -0700 Subject: [PATCH 43/64] Trigger CI From bb67aef55cfe727887a38784fc4b1c0811b22b52 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Thu, 17 Sep 2026 12:54:29 -0700 Subject: [PATCH 44/64] wip: make submodules up to date --- src/coreComponents/LvArray | 2 +- src/coreComponents/constitutive/HPCReact | 2 +- .../kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/coreComponents/LvArray b/src/coreComponents/LvArray index 057b1229c4d..b8bc3ef762b 160000 --- a/src/coreComponents/LvArray +++ b/src/coreComponents/LvArray @@ -1 +1 @@ -Subproject commit 057b1229c4d3227971a546b07fdf28bbddf53be6 +Subproject commit b8bc3ef762bf0236d7f94965b3fbbd458e6d537d diff --git a/src/coreComponents/constitutive/HPCReact b/src/coreComponents/constitutive/HPCReact index 5268dc5c2a5..cee75f0309b 160000 --- a/src/coreComponents/constitutive/HPCReact +++ b/src/coreComponents/constitutive/HPCReact @@ -1 +1 @@ -Subproject commit 5268dc5c2a59e9dae23f435fba9aecf5a5c5de33 +Subproject commit cee75f0309b113845c5f7c48a46f4d14f376c844 diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp index cab481b9f7c..f4f1a0c3cf1 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp @@ -611,11 +611,11 @@ class TpfaCondensedFluxKernel m_elemPerm[er[k]][esr[k]][ei[k]][0][2] }; real64 const t = LvArray::math::max( mimeticInnerProduct::TPFAInnerProduct::computeOneSidedTrans( m_nodePosition, - m_faceToNodes, - kf, - m_elemCenter[er[k]][esr[k]][ei[k]], - perm, - areaTolerance ), + m_faceToNodes, + kf, + m_elemCenter[er[k]][esr[k]][ei[k]], + perm, + areaTolerance ), weightTolerance ); real64 const mob = m_mob[er[k]][esr[k]][ei[k]]; From 5f4c62198f1023053e098833d4ec6e381ff2b026 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Thu, 17 Sep 2026 13:24:12 -0700 Subject: [PATCH 45/64] wip: make code compliant with code rules --- .../fluidFlow/SinglePhaseMixedMFD.cpp | 94 ++++++++++++++++--- .../fluidFlow/SinglePhaseMixedMFD.hpp | 24 +++++ 2 files changed, 104 insertions(+), 14 deletions(-) diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index de8bd283413..e1bf58bc909 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -19,7 +19,7 @@ #include "SinglePhaseMixedMFD.hpp" -#include +#include #include "common/logger/Logger.hpp" #include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" @@ -358,6 +358,9 @@ void SinglePhaseMixedMFD::setupSystem( DomainPartition & domain, // with the dof numbering finalized, build the per-dof labels of the MGR reduction computeMgrPointMarkers( domain, dofManager ); + + // and the sorted list of the ghost dofs, the off-rank columns of the local rows + computeGhostDofs( domain, dofManager ); } void SinglePhaseMixedMFD::computeMgrPointMarkers( DomainPartition const & domain, @@ -590,6 +593,60 @@ void SinglePhaseMixedMFD::applyBoundaryConditions( real64 const time_n, // only the cell-centered boundary conditions (Dirichlet cells, source fluxes) remain to be applied here SinglePhaseBase::applyBoundaryConditions( time_n, dt, domain, dofManager, localMatrix, localRhs ); + // the matrix is final: the residual-norm weights are a function of it and are recomputed with it + computeResidualWeights( domain, dofManager, localMatrix.toViewConst() ); +} + +void SinglePhaseMixedMFD::computeGhostDofs( DomainPartition const & domain, + DofManager const & dofManager ) +{ + GEOS_MARK_FUNCTION; + + string const elemDofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); + string const faceDofKey = dofManager.getKey( mixedMimetic::faceMassFlux::key() ); + + array1d< globalIndex > dofs; + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel const & mesh, + string_array const & regionNames ) + { + FaceManager const & faceManager = mesh.getFaceManager(); + arrayView1d< globalIndex const > const faceDofNumber = faceManager.getReference< array1d< globalIndex > >( faceDofKey ); + arrayView1d< integer const > const faceGhostRank = faceManager.ghostRank(); + for( localIndex kf = 0; kf < faceManager.size(); ++kf ) + { + if( faceGhostRank[kf] >= 0 && faceDofNumber[kf] >= 0 ) + { + dofs.emplace_back( faceDofNumber[kf] ); + } + } + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase const & subRegion ) + { + arrayView1d< globalIndex const > const elemDofNumber = subRegion.getReference< array1d< globalIndex > >( elemDofKey ); + arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + if( elemGhostRank[ei] >= 0 && elemDofNumber[ei] >= 0 ) + { + dofs.emplace_back( elemDofNumber[ei] ); + } + } + } ); + } ); + + std::sort( dofs.begin(), dofs.end() ); + m_ghostDofs.clear(); + m_ghostDofs.insert( dofs.begin(), std::unique( dofs.begin(), dofs.end() ) ); +} + +void SinglePhaseMixedMFD::computeResidualWeights( DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64 const, globalIndex const > const & localMatrix ) +{ + GEOS_MARK_FUNCTION; + // residual-norm weights w_i = ||A_i S||_inf = max_j |A_ij| s_j, S = diag(s_j) the // characteristic scales of the unknowns: each term |A_ij| s_j has the units of r_i, // and w_i is invariant under row rescaling and under the TPFA-face condensation @@ -681,9 +738,13 @@ void SinglePhaseMixedMFD::applyBoundaryConditions( real64 const time_n, } ); } ); - // the scales of the ghost dofs: a cell may own none of its faces, so the face scales of its - // owners are synchronized; the pressure scale of a ghost cell is its synchronized |p_n| - std::unordered_map< globalIndex, real64 > ghostScale; + // s_j of the ghost dofs: a cell may own none of its faces, so the face scales come from their owners; + // the pressure scale of a ghost cell is its synchronized |p_n| + m_ghostDofScale.resize( m_ghostDofs.size() ); + arrayView1d< real64 > const ghostDofScale = m_ghostDofScale.toView(); + ghostDofScale.zero(); + SortedArrayView< globalIndex const > const ghostDofs = m_ghostDofs.toViewConst(); + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, MeshLevel & mesh, string_array const & regionNames ) @@ -696,13 +757,15 @@ void SinglePhaseMixedMFD::applyBoundaryConditions( real64 const time_n, arrayView1d< globalIndex const > const faceDofNumber = faceManager.getReference< array1d< globalIndex > >( faceDofKey ); arrayView1d< integer const > const faceGhostRank = faceManager.ghostRank(); arrayView1d< real64 const > const faceScale = faceManager.getField< mixedMimetic::faceDofScale >(); - for( localIndex kf = 0; kf < faceManager.size(); ++kf ) + forAll< parallelDevicePolicy<> >( faceManager.size(), [=] GEOS_HOST_DEVICE ( localIndex const kf ) { if( faceGhostRank[kf] >= 0 && faceDofNumber[kf] >= 0 ) { - ghostScale[faceDofNumber[kf]] = faceScale[kf]; + localIndex const slot = LvArray::sortedArrayManipulation::find( ghostDofs.begin(), ghostDofs.size(), faceDofNumber[kf] ); + ghostDofScale[slot] = faceScale[kf]; } - } + } ); + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, [&]( localIndex const, ElementSubRegionBase const & subRegion ) @@ -710,18 +773,21 @@ void SinglePhaseMixedMFD::applyBoundaryConditions( real64 const time_n, arrayView1d< globalIndex const > const elemDofNumber = subRegion.getReference< array1d< globalIndex > >( elemDofKey ); arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); arrayView1d< real64 const > const presN = subRegion.getField< fields::flow::pressure_n >(); - for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) { if( elemGhostRank[ei] >= 0 && elemDofNumber[ei] >= 0 ) { - ghostScale[elemDofNumber[ei]] = LvArray::math::abs( presN[ei] ); + localIndex const slot = LvArray::sortedArrayManipulation::find( ghostDofs.begin(), ghostDofs.size(), elemDofNumber[ei] ); + ghostDofScale[slot] = LvArray::math::abs( presN[ei] ); } - } + } ); } ); } ); // w_i = max_j |A_ij| s_j over every column of row i - forAll< serialPolicy >( numRows, [&]( localIndex const i ) + arrayView1d< real64 const > const ghostScale = m_ghostDofScale.toViewConst(); + arrayView1d< real64 const > const localScale = m_dofScale.toViewConst(); + forAll< parallelDevicePolicy<> >( numRows, [=] GEOS_HOST_DEVICE ( localIndex const i ) { real64 w = 0.0; arraySlice1d< globalIndex const > const columns = localMatrix.getColumns( i ); @@ -732,12 +798,12 @@ void SinglePhaseMixedMFD::applyBoundaryConditions( real64 const time_n, real64 scale = 0.0; if( localCol >= 0 && localCol < numRows ) { - scale = dofScale[localCol]; + scale = localScale[localCol]; } else { - auto const it = ghostScale.find( columns[k] ); - scale = it != ghostScale.end() ? it->second : 0.0; + localIndex const slot = LvArray::sortedArrayManipulation::find( ghostDofs.begin(), ghostDofs.size(), columns[k] ); + scale = ( slot < ghostDofs.size() && ghostDofs[slot] == columns[k] ) ? ghostScale[slot] : 0.0; } w = LvArray::math::max( w, LvArray::math::abs( entries[k] ) * scale ); } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp index 1665183c2c5..138e2566967 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp @@ -192,6 +192,24 @@ class SinglePhaseMixedMFD : public SinglePhaseBase void computeMgrPointMarkers( DomainPartition const & domain, DofManager const & dofManager ); + /** + * @brief Build the sorted list of the ghost dofs, the off-rank columns of the local rows. + * @param domain the domain + * @param dofManager the dof manager + */ + void computeGhostDofs( DomainPartition const & domain, + DofManager const & dofManager ); + + /** + * @brief Compute the residual-norm weights w_i = max_j |A_ij| s_j, s_j the characteristic scale of unknown j. + * @param domain the domain + * @param dofManager the dof manager + * @param localMatrix the assembled local matrix + */ + void computeResidualWeights( DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64 const, globalIndex const > const & localMatrix ); + /// relative tolerance used in the mass matrix computations real64 m_areaRelTol; @@ -205,6 +223,12 @@ class SinglePhaseMixedMFD : public SinglePhaseBase /// characteristic scale s_j of unknown x_j: s_p = |p_n|, s_m = p_scale / |M_ff| array1d< real64 > m_dofScale; + /// sorted global indices of the ghost dofs, the off-rank columns of the local rows + SortedArray< globalIndex > m_ghostDofs; + + /// characteristic scale of the ghost dofs, in the order of m_ghostDofs + array1d< real64 > m_ghostDofScale; + }; } /* namespace geos */ From 7361c992b7418396686c64074d671eee81c8838d Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Thu, 17 Sep 2026 14:18:27 -0700 Subject: [PATCH 46/64] wip: clean example input files and fix validation checks --- .../hybrid_mesh_l_0.vtu | 32 ++ .../incompressible_consistency_mixed.xml | 4 +- .../incompressible_hybridMesh_mixed_base.xml | 121 ++++++++ ...sible_hybridMesh_mixed_iterative_smoke.xml | 38 +++ ...ible_hybridMesh_mixed_prescribed_smoke.xml | 70 +++++ .../incompressible_hybridMesh_mixed_smoke.xml | 36 +++ .../mfd_robustness.py | 290 ------------------ .../mfd_robustness_template.xml.in | 91 ------ .../polyhedralDiscretizations/mixedMFD.ats | 34 ++ .../spe11b_mixed_mfd.xml | 154 ---------- .../structured_smoke_mixed.xml | 140 --------- src/coreComponents/schema/schema.xsd | 85 +++++ src/coreComponents/schema/schema.xsd.other | 67 ++-- 13 files changed, 460 insertions(+), 702 deletions(-) create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_mesh_l_0.vtu create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_base.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_iterative_smoke.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_prescribed_smoke.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_smoke.xml delete mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py delete mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness_template.xml.in create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/mixedMFD.ats delete mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/spe11b_mixed_mfd.xml delete mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/structured_smoke_mixed.xml diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_mesh_l_0.vtu b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_mesh_l_0.vtu new file mode 100644 index 00000000000..cc01a246bae --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/hybrid_mesh_l_0.vtu @@ -0,0 +1,32 @@ + + + + + + + +AQAAAACAAACQDAAASwMAAA==eJyVlV1IVEEUx2+SEEKgkj3khqkFQWYF0UYPzfWhhzQiJCuISCG1l8gUEaOgLAoCP+iTCnqoKPCDIkWyl4hFtBKM1UjLctP1a4McW9RFJfM693/vztlZ3eZlOb/5nzP/MzN3VtOWGpwtzZebX64u1VEeqS5k6GHmCbfmdXV9TXety1kYn0PWKdhl8KGw/avzOKN5A6XBse13qjk4P7RPOc8ecp6mRepDXof6t7mcx5naP2dq//y/+/ao918vUe+/HibP4iRP71P70MfV+69jITnP5nIevVfhzoPeJx7m/Gyu1kd6bhiR31OMwwbOmQzh9bXGmAnxKettrtZzptZzJuttn2o/tr77oOhvpELuc8iMX+fKfdZkiRh54NBhHlxdnzN1fc7U9TlT1w89H8SU47zouUFHOb0/6ro2p3rEVE990O8cesqhp98/9JSHe0fpvoBTPepRPTj0br/zRJprmHld8wuj3/que1fV7Hte7ma1m43QY/FrCSLu6C/8GVgzavGebpG//XbueUd9p8XvxqaczVnZyeJvOp/69tr6rnIRx31JXpwHL0wR8aMymZc0Cn3qY1In3cF9mV3k+wiw2WJt48DFYYvPGvbmp1n6q6Pjg9FWP9r7341FF2Lm2CbCnXEHqq9M+tnX69J6Wq/T23DD42cewre0J2aXJs2xuuOSb636k+CXCD+3TdTBect9BCyOPuAfHH3APzj6gH9w9AH/4OgD/sHRB/yDow/490WdSfpQOWzNW/5bdx4r8bjtPkz/o1lb7x+x7hlnnbHC5xDhO8ZFX4emgveNs+TdYt0MwgfbhM+TL4LPhbOMLjV/mxa8D5om9xGw75PZh7z/moY+4B8cfcA/OPqAf3D0Af/g8Av/lMO/m7zP+N7x/o5VyN874qZc+T0ZIe859JXkPYe+yuRYP9/UvzHzMQ99C+GoH3tH92pRHpbQ7q0q6ll4P1hHQ/Pfabbn5eyGmRYXy189t/jbtuJeU+KzCdZ32shrZQMOYzc87KrjYcyP7xOWzr/2XdnluF9MN/V538S71G/m5a0X+v3RIj/T/D31JPVWQcEfq258nfH/6mMPEoV+NErUha9ssz50WKfY9AMfY2beR03o/wEVOKNP + + + + +AQAAAACAAAC0EAAA5wQAAA==eJxtlgdXW0cQhTfFIXZ6TDM4BkN63KtASZyeOAZDKAKMMAaDhUMJGHc/OvrfmT367tnRs3TOPXp3y+zMnZl9byaEsGy4Z8gMm4YlwyL8Ac9L8AXGluEVw23DMPxfeAX+kLlHjlfc+jnDKPsy/Jg1zDh/Vgxrzv4oZ2T4MYNfGb6usC/j+aE7fw4bfn2FNbJfcfOThrLhPnzdsGqYdvbuGgbgs6wtw1fZI/8X8HfO6bHi9J7D1ij8jmHDMOLmZzgzI67/sBH5lOEfwzx8yFDCTsZcifHIxwwTLp5xYp5wvMyY9k9hQ/uniTPDzyF33gKxPYAPknvlb5R4xRfZU3HrN4hR8c07fe9w9oZbP+L4KnvF59FO+8voOevWrzk9y2gw5c4bc/GWmTuPbdXLuVCrg6jFSfaVnPafoWWMs5v4VXunnUbRt1jjF0KtN+bZd4o4VN+XQ60W1L+X0FK50nk9rBtGF52lvlR/XCQPd/FBeq2wT/GrF7vZp1hVB6sh1fBZw5lQq5d1N1dCU+V3PKT6GsSO6j8+DxCL1pfR39fqObjuhwvEs0Bs6iPVzxj+TeOjcqz+vIyei2ire2mCteP4cwrfBtBumBh60O02c4Mh9W884z3WHiXH6mPdj8fQN/r7bkg1XuJs6TeEbd1HUee3DVvEFmvyCBodEMeOoSnU6uYemr1jqOa0jZoWDfuGPux7rc6GdBdfRHPdbZfQsMWwi27Rh1nObDMcMhf9bGbNLOOt+N4Zav0Z49o2nAi1u2UJv9YY7yA2vaviXHuo1af6r4Tu6ocep796bIq1RxlTr8R9p8lNdy4/I+TopMtdF7mM470uf9GGvwOaQn0P6yz1t3Kj++BYqO/ZLvxW/W2EVDOqb9XEmZB65Qj6xtjPE2+M3dduL9oWQsp/P/kpwnfIieopnrXFeZ34Hv2OtdXGuS0h1WPVxdiOT62sO8BuB+dsYrc5pPqOvu3il97fLfAD/KyGVHMF9k9jY4fYTuB/XONrsDnU1/B+SDXYjj6q0Q7W6fxJzi2GVO/N+DDN2iV3vvpsz+nbx/o9eJFnxdsPL4T674lYF6PwrlBfH4OhviZ7GYs513tCd3Hmxh9hQ3ex3q36ThgghszFpe/I/ZC+CZZzfBMd19BI34XFkL5TD0N6Z8e1/hvrvptXXWk8g0+GVBOHuflDxjXfypjstrr56E/VxSH/qi5nVeLTN/O+m9dze6i/pzSvutZdnoVUqwX+vQbbcD+vmlznf9vNx3zsON2VH/WBnrdD+qbedvN61n2ru9fv3w2pN+V/HJvgX32qHsncfPy9b7hiOG743PC94bHh7wb8C8MPhieGWw247osCWhzH9lXDp9iItp4a/mrAb2HrmeFmA94Z0v3Ygc1o+xPDW9iItv4wfNuA38TWn4bvGvCOkO6HJrT5wHCNOKIWPxqe43eeRy1uGF7gd57rfusnjivYvk4cj7H1Er/z/Am2XuF3nhewXSSOq9j+GI2eYus3NMnzZ9j6HU3yvA/b7WgUtXnD8CFxRC2+NPyE33ketfjK8DN+53nUJr6LWonjGrY/Io7n2PoFv/P8BbZ+xe8878d2G3Fcx/abaPQSW1+jSZ6/wtY3aJLnevfo/Zrvh63weg2rp33d7YTXa2W3QX73GuRkv4GOuk997Pqu8P7Ge/F/AmtfKA== + + +AQAAAACAAAC4AwAAaAEAAA==eJwNzxGTogEAANDv7rqZ5qaZC4Juptlp5oKgmwkWgiAIgiAIgiBYCBaCIAiCIAiCIFgIgiAIgmAhWAiCIAiCIAgWgiBYuAfvB7xQEARhIkSJESdBkhRpMmR5JkeeAkVKlKlQpUadBi80eaVFmw5devQZMGTEmAlvTJkxZ8GSFWve2fDBlh17Dhw5cebClU9u3HnwRfAtCEKEiRAlRpwESVKkyZDlmRx5ChQpUaZClRp1GrzQ5JUWbTp06dFnwJARYya8MWXGnAVLVqx5Z8MHW3bsOXDkxJkLVz65cefBF8F3f8JEiBIjToIkKdJkyPJMjjwFipQoU6FKjToNXmjySos2Hbr06DNgyIgxE96YMmPOgiUr1ryz4YMtO/YcOHLizIUrn9y48+CL4Ic/YSJEiREnQZIUabLkKFCiQo0GTVp06DFgxIQpc5as2bBlz5EzV248CEJB8JNf/CbGH574S5p//Ae693Cd + + +AQAAAACAAABwBwAAHwAAAA==eJzjYoAArlF6lB6lR+lRepSmgOYZpjQflWgA7a4Jpw== + + + + +AQAAAACAAAC4AwAAGQAAAA==eJxjYmBgYBrFo3gU48WMdMTEuAcAcvkBwg== + + +AQAAAACAAAC4AwAAGQAAAA==eJxjYmBgYBrFo3gU48WMdMTEuAcAcvkBwg== + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_consistency_mixed.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_consistency_mixed.xml index 347ae0811f7..40f8a234c37 100644 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_consistency_mixed.xml +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_consistency_mixed.xml @@ -11,8 +11,8 @@ diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_base.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_base.xml new file mode 100644 index 00000000000..2644f3eef26 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_base.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_iterative_smoke.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_iterative_smoke.xml new file mode 100644 index 00000000000..48ac2cd6df1 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_iterative_smoke.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_prescribed_smoke.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_prescribed_smoke.xml new file mode 100644 index 00000000000..86f91937978 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_prescribed_smoke.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_smoke.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_smoke.xml new file mode 100644 index 00000000000..3e17a9113cf --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_smoke.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py deleted file mode 100644 index c6aad151211..00000000000 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness.py +++ /dev/null @@ -1,290 +0,0 @@ -#!/usr/bin/env python3 -"""h-robustness, contrast-robustness and convergence of SinglePhaseMixedMFD on regular hexahedral and tetrahedral meshes. - -Standard library only. Each case is a deck generated from mfd_robustness_template.xml.in, run serially -with geosx, and parsed from the log and the ASCII VTK output. - - python3 mfd_robustness.py --geosx /path/to/geosx # everything: h-sweep + contrast sweep, hex and tet - python3 mfd_robustness.py --geosx geosx --mesh tet --levels 4,8,16 - python3 mfd_robustness.py --geosx geosx --contrasts 1,1e2,1e4,1e6 --pattern layersX --level 16 - python3 mfd_robustness.py --geosx geosx --mesh tet --mfd-percents 0,25,50,75,100 --level 8 # prescribed MFD fraction - python3 mfd_robustness.py --geosx geosx --mfd-percent 50 # sweeps at a fixed 50 % MFD - python3 mfd_robustness.py --geosx geosx --table # mesh x mfd% x n x contrast table, n up to 64 - python3 mfd_robustness.py --geosx geosx --table --np 8 --table-levels 32,64 --no-output # the large levels on 8 MPI ranks, iterations only - python3 mfd_robustness.py --geosx geosx --table --mpirun "srun --overlap" # inside a Slurm step: every run through srun, serial included - -Exact solution used for the convergence check (homogeneous permeability k, injection rate density q): - p(x) = p0 + q mu / (2 rho k) x (1 - x) -""" -import argparse, glob, math, os, re, shlex, shutil, subprocess, sys, time - -HERE = os.path.dirname(os.path.abspath(__file__)) -STATES = {"tpfa": "1.0e+20", "mixed": "0.1", "mfd": "1.0e-20"} -RHO, MU, K_REF, L = 1000.0, 1.0e-3, 1.0e-14, 1.0 -VOLUME = 1.0 * 1.0 * 2.0 - -def linspace(a, b, m): - return [a + (b - a) * i / m for i in range(m + 1)] - -def mesh_params(mesh, n, pattern): - """four cell blocks: layers of thickness 0.5 along z (parallel to the flow) or two bands along x (in series); - returns the deck tokens and the cell edges along x, y, z""" - elem = "C3D8" if mesh == "hex" else "C3D4" - ye = linspace(0.0, 1.0, n) - if pattern == "layersX": - m = max(n // 2, 1) - xc, nx = "0, 0.5, 1", f"{m}, {m}" - zc, nz = "0, 2", str(2 * n) - xe = [0.0] + [0.5 * b + 0.5 * i / m for b in range(2) for i in range(1, m + 1)] - ze = linspace(0.0, 2.0, 2 * n) - else: - m = max(n // 2, 1) - xc, nx = "0, 1", str(n) - zc, nz = "0, 0.5, 1, 1.5, 2", ", ".join(str(m) for _ in range(4)) - xe = linspace(0.0, 1.0, n) - ze = [0.0] + [0.5 * b + 0.5 * i / m for b in range(4) for i in range(1, m + 1)] - nblocks = 2 if pattern == "layersX" else 4 - names = ", ".join(f"cb{i}" for i in range(nblocks)) - tokens = dict(elementType=elem, xCoords=xc, nx=nx, ny=str(n), zCoords=zc, nz=nz, cellBlockNames=names, - blocksA="cb0, cb2" if nblocks == 4 else "cb0", blocksB="cb1, cb3" if nblocks == 4 else "cb1") - return tokens, (xe, ye, ze) - -def prescription(edges, percent): - """prescribedMfdFlag = 0 on every cell, then 1 on the first m cells in z-major order (m = percent of the - cells, rounded), selected with up to three boxes; the resulting percentage is exact""" - if percent is None: - return "", "" - xe, ye, ze = edges; nx, ny, nz = len(xe) - 1, len(ye) - 1, len(ze) - 1 - m = int(round(percent / 100.0 * nx * ny * nz)) - a, rem = divmod(m, nx * ny); b, c = divmod(rem, nx) - eps = 1.0e-6 - boxes = [] - if a > 0: boxes.append((xe[0], xe[-1], ye[0], ye[-1], ze[0], ze[a])) - if a < nz and b > 0: boxes.append((xe[0], xe[-1], ye[0], ye[b], ze[a], ze[a + 1])) - if a < nz and b < ny and c > 0: boxes.append((xe[0], xe[c], ye[b], ye[b + 1], ze[a], ze[a + 1])) - box_xml = "".join(f' \n' for i, (x0, x1, y0, y1, z0, z1) in enumerate(boxes)) - spec_xml = (' \n' - + "".join(f' \n' - for i in range(len(boxes)))) - return box_xml, spec_xml - -def write_case(template, workdir, mesh, n, pattern, contrast, state, rate, p0, percent=None, output=True): - tokens, edges = mesh_params(mesh, n, pattern) - tokens["prescriptionBoxes"], tokens["prescription"] = prescription(edges, percent) - write_case.cells = tuple(len(e) - 1 for e in edges) # cells per axis, for the MPI partitioning - tokens.update(consistencyTolerance=STATES[state], permA=f"{K_REF:.6e}", permB=f"{K_REF / contrast:.6e}", sourceRate=f"{rate:.6e}", p0=f"{p0:.6e}") - text = open(template).read() - if not output: - text = re.sub(r'\s*]*/>', "", text) - text = re.sub(r'\s*.*?', "", text, flags=re.S) - for k, v in tokens.items(): - text = text.replace(f"@{k}@", v) - assert "@" not in text, "unreplaced token in the template" - os.makedirs(workdir, exist_ok=True) - with open(os.path.join(workdir, "deck.xml"), "w") as f: - f.write(text) - -SOLVE = re.compile(r"Linear Solver \| (\w+) \| Unknowns: ([\d,]+) \| Nonzeros: [\d,]+ \| Iterations: (\d+)" - r" \| Final Rel Res: \S+ \| Setup Time: ([\d.eE+-]+) s \| Solve Time: ([\d.eE+-]+) s") - -def partition(np, cells): - """split np ranks over the three axes of the internal mesh, each prime factor going to the axis - with the most cells per rank, so that no axis gets more partitions than cells""" - p = [1, 1, 1]; f = 2; m = np - factors = [] - while m > 1: - while m % f == 0: factors.append(f); m //= f - f += 1 - for f in sorted(factors, reverse=True): - axis = max(range(3), key=lambda i: cells[i] / p[i]) - p[axis] *= f - return p - -def run_case(geosx, workdir, timeout, np=1, launcher=None, cells=(1, 1, 1)): - t0 = time.time() - cmd = [geosx, "-i", "deck.xml"] - if np > 1: - px, py, pz = partition(np, cells) - cmd += ["-x", str(px), "-y", str(py), "-z", str(pz)] - if launcher: - # "-n" is understood by mpirun, mpiexec and srun - cmd = launcher + ["-n", str(np)] + cmd - with open(os.path.join(workdir, "run.log"), "w") as log: - try: - subprocess.run(cmd, cwd=workdir, stdout=log, stderr=subprocess.STDOUT, timeout=timeout) - except subprocess.TimeoutExpired: - pass - text = open(os.path.join(workdir, "run.log"), errors="replace").read() - r = dict(wall=time.time() - t0, error="Error" in text or "Signal encountered" in text) - m = re.search(r"eta = 1 on (\d+) / (\d+) cells", text) - if m: r["mfdCells"], r["cells"] = int(m.group(1)), int(m.group(2)) - m = re.search(r"flux dofs (\d+) non-condensed \(saddle point\), (\d+) condensed", text) - if m: r["live"], r["condensed"] = int(m.group(1)), int(m.group(2)) - s = SOLVE.findall(text) - if s: - r["status"], r["unknowns"], r["iterations"] = s[0][0], int(s[0][1].replace(",", "")), int(s[0][2]) - r["setupTime"], r["solveTime"] = float(s[0][3]), float(s[0][4]) - r["numSolves"] = len(s) - else: - r["error"] = True - lines = [l for l in text.splitlines() if l.strip() and not l.startswith("-")] - print(f" no linear solve in {os.path.join(workdir, 'run.log')}: {lines[-1][:120] if lines else 'empty log'}", file=sys.stderr, flush=True) - return r - -def read_vtu_arrays(path, names): - text = open(path).read() - out = {} - for name in names: - m = re.search(r']*Name="%s"[^>]*>(.*?)' % name, text, re.S) - if m: - body = re.sub(r"", "", m.group(1), flags=re.S) - out[name] = [float(v) for v in body.split()] - return out - -def exact_series(x, q, contrast): - """-(k p')' = q on [0,1], p(0) = p(1) = 0, k = K_REF on [0, 0.5), K_REF/contrast on [0.5, 1]""" - k = [K_REF, K_REF / contrast] - # u = -k p' = q x + c ; p(x) = -int_0^x (q s + c)/k(s) ds ; p(1) = 0 fixes c - def integrals(x): - i0 = i1 = 0.0; t0 = 0.0 - for j in range(2): - t1 = min(0.5 * (j + 1), x) - if t1 <= t0: break - i0 += (t1 - t0) / k[j]; i1 += 0.5 * (t1 * t1 - t0 * t0) / k[j]; t0 = t1 - return i0, i1 - I0, I1 = integrals(1.0); c = -q * I1 / I0 - i0, i1 = integrals(x) - return -(q * i1 + c * i0) * MU / RHO - -def pressure_error(workdir, rate, contrast, p0, pattern): - """relative L2 error of the cell pressures against the exact solution (homogeneous, or layers in series)""" - files = sorted(glob.glob(os.path.join(workdir, "robustness", "*", "mesh", "Level0", "*", "rank_*.vtu"))) - files = [f for f in files if "/000000/" not in f] # skip the initial-time output - if not files or (contrast != 1.0 and pattern != "layersX"): - return None, None - q = -rate / VOLUME # injected mass rate per unit volume - num = den = 0.0 - for f in files: - a = read_vtu_arrays(f, ["pressure", "elementCenter"]) - p, c = a.get("pressure", []), a.get("elementCenter", []) - for i, pi in enumerate(p): - x = c[3 * i] - pe = exact_series(x, q, contrast) if contrast != 1.0 else q * MU / (2.0 * RHO * K_REF) * x * (L - x) - num += (pi - p0 - pe) ** 2; den += pe ** 2 - return math.sqrt(num / den) if den > 0 else None, q * MU / (8.0 * RHO * K_REF) - -def main(): - ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) - ap.add_argument("--geosx", default=os.environ.get("GEOSX", shutil.which("geosx") or ""), help="geosx executable") - ap.add_argument("--template", default=os.path.join(HERE, "mfd_robustness_template.xml.in")) - ap.add_argument("--workdir", default="mfd_robustness_runs") - ap.add_argument("--mesh", default="hex,tet", help="hex, tet or both (comma separated)") - ap.add_argument("--levels", default="4,8,16,32", help="cells per unit length for the h-sweep (even numbers)") - ap.add_argument("--level", type=int, default=16, help="mesh size of the contrast sweep") - ap.add_argument("--contrasts", default="1,1e2,1e4,1e6") - ap.add_argument("--pattern", default="layersZ", choices=["layersZ", "layersX"], help="contrast layers parallel (Z) or in series (X) with the flow") - ap.add_argument("--states", default="tpfa,mixed,mfd") - ap.add_argument("--rate", type=float, default=-0.16, help="total mass rate [kg/s], negative = injection; -0.16 gives p_max - p0 = 1e6 Pa") - ap.add_argument("--p0", type=float, default=1.0e7, help="reference pressure of the initial state and of the two Dirichlet faces") - ap.add_argument("--mfd-percent", type=float, default=None, help="prescribe the MFD product on this percentage of the cells (a y-slab) instead of classifying; 100 = full MFD") - ap.add_argument("--mfd-percents", default=None, help="extra sweep over prescribed MFD percentages at --level, e.g. 0,25,50,75,100") - ap.add_argument("--table", action="store_true", help="4-D table mesh x mfd%% x n x contrast (layers in series), entries 'rel L2 error / iterations'") - ap.add_argument("--table-percents", default="0,25,75,100"); ap.add_argument("--table-levels", default="2,4,8,16,32,64"); ap.add_argument("--table-contrasts", default="1,1e4,1e7") - ap.add_argument("--no-output", action="store_true", help="no VTK output: iterations only, no pressure error (use for the large levels)") - ap.add_argument("--np", type=int, default=1, help="number of MPI ranks (1 = run geosx directly unless --mpirun is given)") - ap.add_argument("--mpirun", default=None, help="MPI launcher, options allowed, e.g. 'srun --overlap'; default: mpirun when --np > 1, " - "none when --np 1. Give it explicitly to launch the serial runs too (needed inside a Slurm step)") - ap.add_argument("--timeout", type=int, default=3600) - ap.add_argument("--write-only", action="store_true", help="generate the decks without running") - a = ap.parse_args() - # geosx runs with the case directory as working directory: resolve the paths given on the command line now - if not a.write_only: - geosx = os.path.abspath(a.geosx) if os.path.exists(a.geosx) else shutil.which(a.geosx or "geosx") - if not geosx or not os.access(geosx, os.X_OK): - sys.exit(f"geosx executable not found or not executable: '{a.geosx}' (pass --geosx with a valid path or set GEOSX)") - a.geosx = geosx - if a.np > 1 or a.mpirun: - words = shlex.split(a.mpirun or "mpirun") - exe = os.path.abspath(words[0]) if os.path.exists(words[0]) else shutil.which(words[0]) - if not exe: - sys.exit(f"MPI launcher not found: '{words[0]}' (pass --mpirun)") - a.mpirun = [exe] + words[1:] - a.template = os.path.abspath(a.template) - if not os.path.exists(a.template): - sys.exit(f"template not found: {a.template}") - - def do(mesh, n, pattern, contrast, state, percent=None): - tag = state if percent is None else f"mfd{percent:g}pct" - d = os.path.join(a.workdir, f"{mesh}_n{n}_{pattern}_c{contrast:g}_{tag}") - write_case(a.template, d, mesh, n, pattern, contrast, state, a.rate, a.p0, percent, not a.no_output) - if a.write_only: - return None - r = run_case(a.geosx, d, a.timeout, a.np, a.mpirun, write_case.cells) - r["l2"], r["pmax"] = pressure_error(d, a.rate, contrast, a.p0, pattern) - return r - - if a.table: - contrasts = [float(x) for x in a.table_contrasts.split(",")] - tol = re.search(r'krylovTol="([^"]+)"', open(a.template).read()) - print(f"\nrelative L2 pressure error / GMRES iterations / linear solve time [s] (setup excluded) of the first solve;" - f" krylovTol = {tol.group(1) if tol else '?'}") - print(f"isotropic permeability in series along the flow (x): k = {K_REF:g} m^2 on x < 0.5, k = {K_REF:g} / contrast on x > 0.5," - f" interface on mesh faces; p = p0 on x = 0 and x = 1, uniform source") - print("unknowns = cells + faces of the assembled matrix; reduced = cells + non-condensed flux dofs, the coupled system after the" - " condensation (a condensed flux is a one-way closure row)") - print(f"{'mesh':>4} {'mfd%':>5} {'n':>3} {'cells':>8} {'unknowns':>9} {'reduced':>9} | " + " | ".join(f"{'contrast ' + format(c, 'g'):>32}" for c in contrasts) + " | wall") - for mesh in a.mesh.split(","): - for percent in [float(x) for x in a.table_percents.split(",")]: - for n in [int(x) for x in a.table_levels.split(",")]: - cells, walls = [], [] - for contrast in contrasts: - r = do(mesh, n, "layersX", contrast, "mfd", percent) - if r is None: - continue - got = 100.0 * r.get("mfdCells", 0) / max(r.get("cells", 1), 1) - err = f"{r['l2']:.2e}" if r["l2"] is not None else " n/a " - solve = f"{r['solveTime']:9.3f}" if "solveTime" in r else " n/a" - cells.append(f"{err} / {r.get('iterations', '-'):>3} / {solve}" + ("!" if r["error"] else " ") + (f"({got:.0f}%)" if abs(got - percent) > 0.5 else " ")) - walls.append(r["wall"]) - if not cells: - continue - reduced = r["cells"] + r["live"] if "cells" in r and "live" in r else "-" - print(f"{mesh:>4} {percent:>5g} {n:>3} {r.get('cells', '-'):>8} {r.get('unknowns', '-'):>9} {reduced:>9} | " - + " | ".join(f"{c:>32}" for c in cells) + f" | {sum(walls):6.0f} s", flush=True) - return - - for mesh in a.mesh.split(","): - print(f"\n=== {mesh}: h-sweep, contrast 1 (iterations of the first solve; L2 error vs the quadratic solution, observed order) ===") - print(f"{'state':>6} {'n':>4} {'cells':>8} {'unknowns':>9} {'mfd%':>5} {'its':>4} {'rel L2 err':>11} {'order':>6} {'s':>6}") - states = a.states.split(",") if a.mfd_percent is None else [f"mfd{a.mfd_percent:g}pct"] - for state in states: - prev = None - for n in [int(x) for x in a.levels.split(",")]: - r = do(mesh, n, "layersZ", 1.0, "mfd", a.mfd_percent) if a.mfd_percent is not None else do(mesh, n, "layersZ", 1.0, state) - if r is None: continue - order = math.log(prev / r["l2"]) / math.log(2.0) if (prev and r["l2"]) else float("nan") - prev = r["l2"] - frac = 100.0 * r.get("mfdCells", 0) / max(r.get("cells", 1), 1) - print(f"{state:>6} {n:>4} {r.get('cells','-'):>8} {r.get('unknowns','-'):>9} {frac:>5.0f} {r.get('iterations','-'):>4} " - f"{(r['l2'] if r['l2'] is not None else float('nan')):>11.3e} {order:>6.2f} {r['wall']:>6.1f}{' ERROR' if r['error'] else ''}", flush=True) - print(f"\n=== {mesh}: contrast sweep at n = {a.level}, pattern {a.pattern} ===") - print(f"{'state':>6} {'contrast':>9} {'mfd%':>5} {'live':>8} {'its':>4} {'s':>6}") - for state in states: - for contrast in [float(x) for x in a.contrasts.split(",")]: - r = do(mesh, a.level, a.pattern, contrast, "mfd", a.mfd_percent) if a.mfd_percent is not None else do(mesh, a.level, a.pattern, contrast, state) - if r is None: continue - frac = 100.0 * r.get("mfdCells", 0) / max(r.get("cells", 1), 1) - print(f"{state:>6} {contrast:>9g} {frac:>5.0f} {r.get('live','-'):>8} {r.get('iterations','-'):>4} {r['wall']:>6.1f}{' ERROR' if r['error'] else ''}", flush=True) - if a.mfd_percents: - print(f"\n=== {mesh}: prescribed MFD percentage sweep at n = {a.level}, contrast 1 (y-slab of MFD cells) ===") - print(f"{'asked%':>7} {'mfd%':>5} {'live':>8} {'condensed':>9} {'its':>4} {'rel L2 err':>11} {'s':>6}") - for percent in [float(x) for x in a.mfd_percents.split(",")]: - r = do(mesh, a.level, "layersZ", 1.0, "mfd", percent) - if r is None: continue - frac = 100.0 * r.get("mfdCells", 0) / max(r.get("cells", 1), 1) - print(f"{percent:>7g} {frac:>5.0f} {r.get('live','-'):>8} {r.get('condensed','-'):>9} {r.get('iterations','-'):>4} " - f"{(r['l2'] if r['l2'] is not None else float('nan')):>11.3e} {r['wall']:>6.1f}{' ERROR' if r['error'] else ''}", flush=True) - -if __name__ == "__main__": - main() diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness_template.xml.in b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness_template.xml.in deleted file mode 100644 index 9bc25afbda3..00000000000 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mfd_robustness_template.xml.in +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - -@prescriptionBoxes@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -@prescription@ - - - - - - - - - - - diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mixedMFD.ats b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mixedMFD.ats new file mode 100644 index 00000000000..7f7ead0f46c --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mixedMFD.ats @@ -0,0 +1,34 @@ +from geos.ats.test_builder import TestDeck, RestartcheckParameters, generate_geos_tests + +restartcheck_params = {} +restartcheck_params['atol'] = 1e-08 +restartcheck_params['rtol'] = 4e-07 + +decks = [ + TestDeck( + name="incompressible_hybridMesh_mixed_smoke", + description= + 'Adaptive mixed MFD single phase flow test (3D hybrid mesh, incompressible, TPFA on hexahedra and RT elsewhere, direct solver)', + partitions=((1, 1, 1), (2, 1, 1), (3, 1, 1)), + restart_step=0, + check_step=1, + restartcheck_params=RestartcheckParameters(**restartcheck_params)), + TestDeck( + name="incompressible_hybridMesh_mixed_iterative_smoke", + description= + 'Adaptive mixed MFD single phase flow test (3D hybrid mesh, incompressible, GMRES with the MGR strategy)', + partitions=((1, 1, 1), (2, 1, 1), (3, 1, 1)), + restart_step=0, + check_step=1, + restartcheck_params=RestartcheckParameters(**restartcheck_params)), + TestDeck( + name="incompressible_hybridMesh_mixed_prescribed_smoke", + description= + 'Adaptive mixed MFD single phase flow test (3D hybrid mesh, incompressible, prescribed inner product and degeneracy layer)', + partitions=((1, 1, 1), (2, 1, 1), (3, 1, 1)), + restart_step=0, + check_step=1, + restartcheck_params=RestartcheckParameters(**restartcheck_params)), +] + +generate_geos_tests(decks) diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/spe11b_mixed_mfd.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/spe11b_mixed_mfd.xml deleted file mode 100644 index 55d972cb7aa..00000000000 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/spe11b_mixed_mfd.xml +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/structured_smoke_mixed.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/structured_smoke_mixed.xml deleted file mode 100644 index 3ff0ad0ba45..00000000000 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/structured_smoke_mixed.xml +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/coreComponents/schema/schema.xsd b/src/coreComponents/schema/schema.xsd index a516ce0f2f4..3a7c6984d07 100644 --- a/src/coreComponents/schema/schema.xsd +++ b/src/coreComponents/schema/schema.xsd @@ -459,6 +459,10 @@ + + + + @@ -2133,6 +2137,12 @@ Information output from lower logLevels is added with the desired log level + + + + + + @@ -2591,6 +2601,25 @@ Information output from lower logLevels is added with the desired log level + + + + + + + + + + + + + + + + + + + @@ -2808,6 +2837,7 @@ Information output from lower logLevels is added with the desired log level + @@ -4957,6 +4987,61 @@ Information output from lower logLevels is added with the desired log level + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/coreComponents/schema/schema.xsd.other b/src/coreComponents/schema/schema.xsd.other index cf0ac4ad56b..9fd1ffefc8a 100644 --- a/src/coreComponents/schema/schema.xsd.other +++ b/src/coreComponents/schema/schema.xsd.other @@ -451,6 +451,7 @@ A field can represent a physical variable. (pressure, temperature, global compos + @@ -526,8 +527,14 @@ A field can represent a physical variable. (pressure, temperature, global compos - + + + + + + + @@ -594,6 +601,7 @@ A field can represent a physical variable. (pressure, temperature, global compos + @@ -1192,6 +1200,15 @@ A field can represent a physical variable. (pressure, temperature, global compos + + + + + + + + + @@ -1638,7 +1655,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3561,7 +3578,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3589,7 +3606,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3608,11 +3625,11 @@ A field can represent a physical variable. (pressure, temperature, global compos - + - + @@ -3622,7 +3639,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3632,11 +3649,11 @@ A field can represent a physical variable. (pressure, temperature, global compos - + - + @@ -3646,7 +3663,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3656,7 +3673,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3666,7 +3683,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3696,7 +3713,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3724,7 +3741,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3738,7 +3755,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3750,7 +3767,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3758,11 +3775,11 @@ A field can represent a physical variable. (pressure, temperature, global compos - + - + @@ -3785,7 +3802,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3811,7 +3828,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3832,7 +3849,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3862,7 +3879,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3876,7 +3893,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3907,7 +3924,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3952,7 +3969,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + From 66a651a03bba12a15f543ad41227f39f2fc31e3d Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Thu, 17 Sep 2026 14:21:25 -0700 Subject: [PATCH 47/64] wip: undo changes on src/coreComponents/schema/schema.xsd.other --- src/coreComponents/schema/schema.xsd.other | 67 ++++++++-------------- 1 file changed, 25 insertions(+), 42 deletions(-) diff --git a/src/coreComponents/schema/schema.xsd.other b/src/coreComponents/schema/schema.xsd.other index 9fd1ffefc8a..cf0ac4ad56b 100644 --- a/src/coreComponents/schema/schema.xsd.other +++ b/src/coreComponents/schema/schema.xsd.other @@ -451,7 +451,6 @@ A field can represent a physical variable. (pressure, temperature, global compos - @@ -527,14 +526,8 @@ A field can represent a physical variable. (pressure, temperature, global compos - + - - - - - - @@ -601,7 +594,6 @@ A field can represent a physical variable. (pressure, temperature, global compos - @@ -1200,15 +1192,6 @@ A field can represent a physical variable. (pressure, temperature, global compos - - - - - - - - - @@ -1655,7 +1638,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3578,7 +3561,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3606,7 +3589,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3625,11 +3608,11 @@ A field can represent a physical variable. (pressure, temperature, global compos - + - + @@ -3639,7 +3622,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3649,11 +3632,11 @@ A field can represent a physical variable. (pressure, temperature, global compos - + - + @@ -3663,7 +3646,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3673,7 +3656,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3683,7 +3666,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3713,7 +3696,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3741,7 +3724,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3755,7 +3738,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3767,7 +3750,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3775,11 +3758,11 @@ A field can represent a physical variable. (pressure, temperature, global compos - + - + @@ -3802,7 +3785,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3828,7 +3811,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3849,7 +3832,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3879,7 +3862,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3893,7 +3876,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3924,7 +3907,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + @@ -3969,7 +3952,7 @@ A field can represent a physical variable. (pressure, temperature, global compos - + From 88624c4c8d3c25f9b82952082c978456d4277be4 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 18 Sep 2026 19:03:03 -0700 Subject: [PATCH 48/64] wip: mixed-MFD for thermal single phase flow --- .../polyhedral/lapwoodConvection_Ra30.xml | 64 + .../polyhedral/lapwoodConvection_Ra50.xml | 64 + .../lapwoodConvection_Ra50_smoke.xml | 70 ++ .../polyhedral/lapwoodConvection_base.xml | 215 ++++ .../lapwoodConvection_tables/pressure.geos | 1 + .../lapwoodConvection_tables/temperature.geos | 1 + .../lapwoodConvection_tables/x.geos | 1 + .../lapwoodConvection_tables/xMid.geos | 1 + .../lapwoodConvection_tables/y.geos | 1 + .../lapwoodConvection_tables/z.geos | 1 + .../polyhedral/thermalConduction_base.xml | 169 +++ .../thermalConduction_consistent_smoke.xml | 41 + .../polyhedral/thermalConduction_smoke.xml | 37 + .../polyhedral/thermalMixedMFD.ats | 34 + src/cmake/GeosxOptions.cmake | 2 +- .../thirdparty/SetupGeosxThirdParty.cmake | 6 + .../mimeticInnerProducts/RTInnerProduct.hpp | 40 +- .../unitTests/testMimeticInnerProducts.cpp | 62 + .../fluidFlowTests/CMakeLists.txt | 1 + .../testSinglePhaseMixedMFD.cpp | 1078 +++++++++++++++++ .../linearAlgebra/CMakeLists.txt | 1 + .../interfaces/hypre/HypreMGR.cpp | 8 + .../interfaces/hypre/hypredrive.cpp | 4 + .../ThermalSinglePhaseMixedMFD.hpp | 108 ++ .../unitTests/testHypredrive.cpp | 2 +- .../utilities/LinearSolverParameters.hpp | 2 + .../MixedMimeticBoundaryConditions.hpp | 135 +++ .../mixedMimetic/MixedMimeticFields.hpp | 136 +++ .../mixedMimetic/unitTests/CMakeLists.txt | 17 +- .../testMixedMimeticBoundaryConditions.cpp | 157 +++ .../physicsSolvers/fluidFlow/CMakeLists.txt | 1 + .../fluidFlow/SinglePhaseMixedMFD.cpp | 709 ++++++++--- .../fluidFlow/SinglePhaseMixedMFD.hpp | 35 +- .../SinglePhaseMixedMFDKernels.hpp | 337 +++--- .../ThermalSinglePhaseMixedMFDKernels.hpp | 812 +++++++++++++ 35 files changed, 4038 insertions(+), 315 deletions(-) create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra30.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra50.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra50_smoke.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_base.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/pressure.geos create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/temperature.geos create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/x.geos create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/xMid.geos create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/y.geos create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/z.geos create mode 100644 inputFiles/singlePhaseFlow/polyhedral/thermalConduction_base.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedral/thermalConduction_consistent_smoke.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedral/thermalConduction_smoke.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedral/thermalMixedMFD.ats create mode 100644 src/coreComponents/integrationTests/fluidFlowTests/testSinglePhaseMixedMFD.cpp create mode 100644 src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/ThermalSinglePhaseMixedMFD.hpp create mode 100644 src/coreComponents/mixedMimetic/MixedMimeticBoundaryConditions.hpp create mode 100644 src/coreComponents/mixedMimetic/unitTests/testMixedMimeticBoundaryConditions.cpp create mode 100644 src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/ThermalSinglePhaseMixedMFDKernels.hpp diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra30.xml b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra30.xml new file mode 100644 index 00000000000..0dcb83ded22 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra30.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra50.xml b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra50.xml new file mode 100644 index 00000000000..49e29da073f --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra50.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra50_smoke.xml b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra50_smoke.xml new file mode 100644 index 00000000000..8da9594843c --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra50_smoke.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_base.xml b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_base.xml new file mode 100644 index 00000000000..6c4d8f757a2 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_base.xml @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/pressure.geos b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/pressure.geos new file mode 100644 index 00000000000..b8e40bf236b --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/pressure.geos @@ -0,0 +1 @@ +10981000 10971190 10961380 10951570 10941760 10931950 10922140 10912330 10902520 10892710 10882900 10873090 10863280 10853470 10843660 10833850 10824040 10814230 10804420 10794610 10784800 10774990 10765180 10755370 10745560 10735750 10725940 10716130 10706320 10696510 10686700 10676890 10667080 10657270 10647460 10637650 10627840 10618030 10608220 10598410 10588600 10578790 10568980 10559170 10549360 10539550 10529740 10519930 10510120 10500310 10490500 10480690 10470880 10461070 10451260 10441450 10431640 10421830 10412020 10402210 10392400 10382590 10372780 10362970 10353160 10343350 10333540 10323730 10313920 10304110 10294300 10284490 10274680 10264870 10255060 10245250 10235440 10225630 10215820 10206010 10196200 10186390 10176580 10166770 10156960 10147150 10137340 10127530 10117720 10107910 10098100 10088290 10078480 10068670 10058860 10049050 10039240 10029430 10019620 10009810 10000000 diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/temperature.geos b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/temperature.geos new file mode 100644 index 00000000000..ef8b4819eba --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/temperature.geos @@ -0,0 +1 @@ +320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 319.800314108 319.800313953 319.800313488 319.800312714 319.800311631 319.80031024 319.800308544 319.800306543 319.800304239 319.800301636 319.800298734 319.800295538 319.80029205 319.800288274 319.800284213 319.800279872 319.800275255 319.800270366 319.80026521 319.800259792 319.800254118 319.800248194 319.800242024 319.800235616 319.800228975 319.800222108 319.800215021 319.800207723 319.80020022 319.800192519 319.800184628 319.800176555 319.800168307 319.800159894 319.800151322 319.800142602 319.800133741 319.800124747 319.800115631 319.8001064 319.800097065 319.800087633 319.800078115 319.80006852 319.800058858 319.800049137 319.800039368 319.80002956 319.800019723 319.800009866 319.8 319.799990134 319.799980277 319.79997044 319.799960632 319.799950863 319.799941142 319.79993148 319.799921885 319.799912367 319.799902935 319.7998936 319.799884369 319.799875253 319.799866259 319.799857398 319.799848678 319.799840106 319.799831693 319.799823445 319.799815372 319.799807481 319.79979978 319.799792277 319.799784979 319.799777892 319.799771025 319.799764384 319.799757976 319.799751806 319.799745882 319.799740208 319.79973479 319.799729634 319.799724745 319.799720128 319.799715787 319.799711726 319.79970795 319.799704462 319.799701266 319.799698364 319.799695761 319.799693457 319.799691456 319.79968976 319.799688369 319.799687286 319.799686512 319.799686047 319.799685892 319.799686047 319.799686512 319.799687286 319.799688369 319.79968976 319.799691456 319.799693457 319.799695761 319.799698364 319.799701266 319.799704462 319.79970795 319.799711726 319.799715787 319.799720128 319.799724745 319.799729634 319.79973479 319.799740208 319.799745882 319.799751806 319.799757976 319.799764384 319.799771025 319.799777892 319.799784979 319.799792277 319.79979978 319.799807481 319.799815372 319.799823445 319.799831693 319.799840106 319.799848678 319.799857398 319.799866259 319.799875253 319.799884369 319.7998936 319.799902935 319.799912367 319.799921885 319.79993148 319.799941142 319.799950863 319.799960632 319.79997044 319.799980277 319.799990134 319.8 319.800009866 319.800019723 319.80002956 319.800039368 319.800049137 319.800058858 319.80006852 319.800078115 319.800087633 319.800097065 319.8001064 319.800115631 319.800124747 319.800133741 319.800142602 319.800151322 319.800159894 319.800168307 319.800176555 319.800184628 319.800192519 319.80020022 319.800207723 319.800215021 319.800222108 319.800228975 319.800235616 319.800242024 319.800248194 319.800254118 319.800259792 319.80026521 319.800270366 319.800275255 319.800279872 319.800284213 319.800288274 319.80029205 319.800295538 319.800298734 319.800301636 319.800304239 319.800306543 319.800308544 319.80031024 319.800311631 319.800312714 319.800313488 319.800313953 319.800314108 319.600627905 319.600627595 319.600626666 319.600625119 319.600622954 319.600620175 319.600616783 319.600612783 319.600608178 319.600602973 319.600597173 319.600590784 319.600583811 319.600576263 319.600568146 319.600559468 319.600550238 319.600540464 319.600530158 319.600519328 319.600507986 319.600496142 319.600483809 319.600470999 319.600457723 319.600443996 319.600429831 319.600415241 319.600400242 319.600384848 319.600369073 319.600352935 319.600336448 319.60031963 319.600302496 319.600285063 319.600267349 319.600249371 319.600231147 319.600212695 319.600194033 319.60017518 319.600156154 319.600136973 319.600117658 319.600098226 319.600078697 319.600059091 319.600039426 319.600019723 319.6 319.599980277 319.599960574 319.599940909 319.599921303 319.599901774 319.599882342 319.599863027 319.599843846 319.59982482 319.599805967 319.599787305 319.599768853 319.599750629 319.599732651 319.599714937 319.599697504 319.59968037 319.599663552 319.599647065 319.599630927 319.599615152 319.599599758 319.599584759 319.599570169 319.599556004 319.599542277 319.599529001 319.599516191 319.599503858 319.599492014 319.599480672 319.599469842 319.599459536 319.599449762 319.599440532 319.599431854 319.599423737 319.599416189 319.599409216 319.599402827 319.599397027 319.599391822 319.599387217 319.599383217 319.599379825 319.599377046 319.599374881 319.599373334 319.599372405 319.599372095 319.599372405 319.599373334 319.599374881 319.599377046 319.599379825 319.599383217 319.599387217 319.599391822 319.599397027 319.599402827 319.599409216 319.599416189 319.599423737 319.599431854 319.599440532 319.599449762 319.599459536 319.599469842 319.599480672 319.599492014 319.599503858 319.599516191 319.599529001 319.599542277 319.599556004 319.599570169 319.599584759 319.599599758 319.599615152 319.599630927 319.599647065 319.599663552 319.59968037 319.599697504 319.599714937 319.599732651 319.599750629 319.599768853 319.599787305 319.599805967 319.59982482 319.599843846 319.599863027 319.599882342 319.599901774 319.599921303 319.599940909 319.599960574 319.599980277 319.6 319.600019723 319.600039426 319.600059091 319.600078697 319.600098226 319.600117658 319.600136973 319.600156154 319.60017518 319.600194033 319.600212695 319.600231147 319.600249371 319.600267349 319.600285063 319.600302496 319.60031963 319.600336448 319.600352935 319.600369073 319.600384848 319.600400242 319.600415241 319.600429831 319.600443996 319.600457723 319.600470999 319.600483809 319.600496142 319.600507986 319.600519328 319.600530158 319.600540464 319.600550238 319.600559468 319.600568146 319.600576263 319.600583811 319.600590784 319.600597173 319.600602973 319.600608178 319.600612783 319.600616783 319.600620175 319.600622954 319.600625119 319.600626666 319.600627595 319.600627905 319.400941083 319.400940619 319.400939226 319.400936907 319.400933662 319.400929497 319.400924414 319.400918419 319.400911517 319.400903716 319.400895023 319.400885447 319.400874997 319.400863683 319.400851517 319.400838511 319.400824677 319.40081003 319.400794583 319.400778352 319.400761352 319.400743602 319.400725117 319.400705917 319.40068602 319.400665446 319.400644216 319.400622349 319.400599869 319.400576796 319.400553155 319.400528967 319.400504258 319.40047905 319.40045337 319.400427243 319.400400694 319.400373749 319.400346436 319.400318781 319.400290811 319.400262554 319.400234038 319.400205291 319.400176341 319.400147218 319.400117949 319.400088564 319.400059091 319.40002956 319.4 319.39997044 319.399940909 319.399911436 319.399882051 319.399852782 319.399823659 319.399794709 319.399765962 319.399737446 319.399709189 319.399681219 319.399653564 319.399626251 319.399599306 319.399572757 319.39954663 319.39952095 319.399495742 319.399471033 319.399446845 319.399423204 319.399400131 319.399377651 319.399355784 319.399334554 319.39931398 319.399294083 319.399274883 319.399256398 319.399238648 319.399221648 319.399205417 319.39918997 319.399175323 319.399161489 319.399148483 319.399136317 319.399125003 319.399114553 319.399104977 319.399096284 319.399088483 319.399081581 319.399075586 319.399070503 319.399066338 319.399063093 319.399060774 319.399059381 319.399058917 319.399059381 319.399060774 319.399063093 319.399066338 319.399070503 319.399075586 319.399081581 319.399088483 319.399096284 319.399104977 319.399114553 319.399125003 319.399136317 319.399148483 319.399161489 319.399175323 319.39918997 319.399205417 319.399221648 319.399238648 319.399256398 319.399274883 319.399294083 319.39931398 319.399334554 319.399355784 319.399377651 319.399400131 319.399423204 319.399446845 319.399471033 319.399495742 319.39952095 319.39954663 319.399572757 319.399599306 319.399626251 319.399653564 319.399681219 319.399709189 319.399737446 319.399765962 319.399794709 319.399823659 319.399852782 319.399882051 319.399911436 319.399940909 319.39997044 319.4 319.40002956 319.400059091 319.400088564 319.400117949 319.400147218 319.400176341 319.400205291 319.400234038 319.400262554 319.400290811 319.400318781 319.400346436 319.400373749 319.400400694 319.400427243 319.40045337 319.40047905 319.400504258 319.400528967 319.400553155 319.400576796 319.400599869 319.400622349 319.400644216 319.400665446 319.40068602 319.400705917 319.400725117 319.400743602 319.400761352 319.400778352 319.400794583 319.40081003 319.400824677 319.400838511 319.400851517 319.400863683 319.400874997 319.400885447 319.400895023 319.400903716 319.400911517 319.400918419 319.400924414 319.400929497 319.400933662 319.400936907 319.400939226 319.400940619 319.400941083 319.201253332 319.201252714 319.201250859 319.20124777 319.201243449 319.201237902 319.201231132 319.201223148 319.201213957 319.201203567 319.20119199 319.201179236 319.201165319 319.201150252 319.201134049 319.201116727 319.201098303 319.201078796 319.201058223 319.201036607 319.201013967 319.200990327 319.200965709 319.200940138 319.20091364 319.20088624 319.200857965 319.200828844 319.200798904 319.200768176 319.20073669 319.200704477 319.200671569 319.200637998 319.200603797 319.200569001 319.200533643 319.200497758 319.200461382 319.200424551 319.200387301 319.200349669 319.200311691 319.200273406 319.200234851 319.200196064 319.200157084 319.200117949 319.200078697 319.200039368 319.2 319.199960632 319.199921303 319.199882051 319.199842916 319.199803936 319.199765149 319.199726594 319.199688309 319.199650331 319.199612699 319.199575449 319.199538618 319.199502242 319.199466357 319.199430999 319.199396203 319.199362002 319.199328431 319.199295523 319.19926331 319.199231824 319.199201096 319.199171156 319.199142035 319.19911376 319.19908636 319.199059862 319.199034291 319.199009673 319.198986033 319.198963393 319.198941777 319.198921204 319.198901697 319.198883273 319.198865951 319.198849748 319.198834681 319.198820764 319.19880801 319.198796433 319.198786043 319.198776852 319.198768868 319.198762098 319.198756551 319.19875223 319.198749141 319.198747286 319.198746668 319.198747286 319.198749141 319.19875223 319.198756551 319.198762098 319.198768868 319.198776852 319.198786043 319.198796433 319.19880801 319.198820764 319.198834681 319.198849748 319.198865951 319.198883273 319.198901697 319.198921204 319.198941777 319.198963393 319.198986033 319.199009673 319.199034291 319.199059862 319.19908636 319.19911376 319.199142035 319.199171156 319.199201096 319.199231824 319.19926331 319.199295523 319.199328431 319.199362002 319.199396203 319.199430999 319.199466357 319.199502242 319.199538618 319.199575449 319.199612699 319.199650331 319.199688309 319.199726594 319.199765149 319.199803936 319.199842916 319.199882051 319.199921303 319.199960632 319.2 319.200039368 319.200078697 319.200117949 319.200157084 319.200196064 319.200234851 319.200273406 319.200311691 319.200349669 319.200387301 319.200424551 319.200461382 319.200497758 319.200533643 319.200569001 319.200603797 319.200637998 319.200671569 319.200704477 319.20073669 319.200768176 319.200798904 319.200828844 319.200857965 319.20088624 319.20091364 319.200940138 319.200965709 319.200990327 319.201013967 319.201036607 319.201058223 319.201078796 319.201098303 319.201116727 319.201134049 319.201150252 319.201165319 319.201179236 319.20119199 319.201203567 319.201213957 319.201223148 319.201231132 319.201237902 319.201243449 319.20124777 319.201250859 319.201252714 319.201253332 319.001564345 319.001563573 319.001561258 319.001557402 319.001552009 319.001545085 319.001536636 319.00152667 319.001515198 319.00150223 319.00148778 319.001471862 319.001454491 319.001435685 319.001415461 319.001393841 319.001370846 319.001346497 319.00132082 319.001293839 319.001265581 319.001236075 319.001205348 319.001173432 319.001140358 319.001106159 319.001070868 319.00103452 319.000997151 319.000958798 319.000919499 319.000879292 319.000838218 319.000796316 319.000753629 319.000710198 319.000666066 319.000621276 319.000575874 319.000529903 319.000483409 319.000436438 319.000389037 319.000341251 319.000293129 319.000244717 319.000196064 319.000147218 319.000098226 319.000049137 319 318.999950863 318.999901774 318.999852782 318.999803936 318.999755283 318.999706871 318.999658749 318.999610963 318.999563562 318.999516591 318.999470097 318.999424126 318.999378724 318.999333934 318.999289802 318.999246371 318.999203684 318.999161782 318.999120708 318.999080501 318.999041202 318.999002849 318.99896548 318.998929132 318.998893841 318.998859642 318.998826568 318.998794652 318.998763925 318.998734419 318.998706161 318.99867918 318.998653503 318.998629154 318.998606159 318.998584539 318.998564315 318.998545509 318.998528138 318.99851222 318.99849777 318.998484802 318.99847333 318.998463364 318.998454915 318.998447991 318.998442598 318.998438742 318.998436427 318.998435655 318.998436427 318.998438742 318.998442598 318.998447991 318.998454915 318.998463364 318.99847333 318.998484802 318.99849777 318.99851222 318.998528138 318.998545509 318.998564315 318.998584539 318.998606159 318.998629154 318.998653503 318.99867918 318.998706161 318.998734419 318.998763925 318.998794652 318.998826568 318.998859642 318.998893841 318.998929132 318.99896548 318.999002849 318.999041202 318.999080501 318.999120708 318.999161782 318.999203684 318.999246371 318.999289802 318.999333934 318.999378724 318.999424126 318.999470097 318.999516591 318.999563562 318.999610963 318.999658749 318.999706871 318.999755283 318.999803936 318.999852782 318.999901774 318.999950863 319 319.000049137 319.000098226 319.000147218 319.000196064 319.000244717 319.000293129 319.000341251 319.000389037 319.000436438 319.000483409 319.000529903 319.000575874 319.000621276 319.000666066 319.000710198 319.000753629 319.000796316 319.000838218 319.000879292 319.000919499 319.000958798 319.000997151 319.00103452 319.001070868 319.001106159 319.001140358 319.001173432 319.001205348 319.001236075 319.001265581 319.001293839 319.00132082 319.001346497 319.001370846 319.001393841 319.001415461 319.001435685 319.001454491 319.001471862 319.00148778 319.00150223 319.001515198 319.00152667 319.001536636 319.001545085 319.001552009 319.001557402 319.001561258 319.001563573 319.001564345 318.801873813 318.801872889 318.801870116 318.801865497 318.801859038 318.801850743 318.801840623 318.801828686 318.801814944 318.801799411 318.801782102 318.801763035 318.801742227 318.801719701 318.801695477 318.80166958 318.801642035 318.80161287 318.801582113 318.801549794 318.801515947 318.801480603 318.801443798 318.801405568 318.801365951 318.801324986 318.801282713 318.801239175 318.801194413 318.801148473 318.8011014 318.801053239 318.801004039 318.800953848 318.800902716 318.800850693 318.800797831 318.800744181 318.800689797 318.800634732 318.80057904 318.800522777 318.800465998 318.80040876 318.800351118 318.800293129 318.800234851 318.800176341 318.800117658 318.800058858 318.8 318.799941142 318.799882342 318.799823659 318.799765149 318.799706871 318.799648882 318.79959124 318.799534002 318.799477223 318.79942096 318.799365268 318.799310203 318.799255819 318.799202169 318.799149307 318.799097284 318.799046152 318.798995961 318.798946761 318.7988986 318.798851527 318.798805587 318.798760825 318.798717287 318.798675014 318.798634049 318.798594432 318.798556202 318.798519397 318.798484053 318.798450206 318.798417887 318.79838713 318.798357965 318.79833042 318.798304523 318.798280299 318.798257773 318.798236965 318.798217898 318.798200589 318.798185056 318.798171314 318.798159377 318.798149257 318.798140962 318.798134503 318.798129884 318.798127111 318.798126187 318.798127111 318.798129884 318.798134503 318.798140962 318.798149257 318.798159377 318.798171314 318.798185056 318.798200589 318.798217898 318.798236965 318.798257773 318.798280299 318.798304523 318.79833042 318.798357965 318.79838713 318.798417887 318.798450206 318.798484053 318.798519397 318.798556202 318.798594432 318.798634049 318.798675014 318.798717287 318.798760825 318.798805587 318.798851527 318.7988986 318.798946761 318.798995961 318.799046152 318.799097284 318.799149307 318.799202169 318.799255819 318.799310203 318.799365268 318.79942096 318.799477223 318.799534002 318.79959124 318.799648882 318.799706871 318.799765149 318.799823659 318.799882342 318.799941142 318.8 318.800058858 318.800117658 318.800176341 318.800234851 318.800293129 318.800351118 318.80040876 318.800465998 318.800522777 318.80057904 318.800634732 318.800689797 318.800744181 318.800797831 318.800850693 318.800902716 318.800953848 318.801004039 318.801053239 318.8011014 318.801148473 318.801194413 318.801239175 318.801282713 318.801324986 318.801365951 318.801405568 318.801443798 318.801480603 318.801515947 318.801549794 318.801582113 318.80161287 318.801642035 318.80166958 318.801695477 318.801719701 318.801742227 318.801763035 318.801782102 318.801799411 318.801814944 318.801828686 318.801840623 318.801850743 318.801859038 318.801865497 318.801870116 318.801872889 318.801873813 318.602181432 318.602180356 318.602177128 318.602171751 318.602164231 318.602154575 318.602142793 318.602128896 318.602112899 318.602094816 318.602074666 318.602052468 318.602028245 318.60200202 318.601973819 318.601943671 318.601911604 318.601877651 318.601841844 318.60180422 318.601764816 318.60172367 318.601680823 318.601636317 318.601590196 318.601542506 318.601493293 318.601442607 318.601390497 318.601337015 318.601282214 318.601226147 318.60116887 318.601110439 318.601050913 318.60099035 318.600928809 318.600866351 318.600803039 318.600738934 318.6006741 318.6006086 318.6005425 318.600475865 318.60040876 318.600341251 318.600273406 318.600205291 318.600136973 318.60006852 318.6 318.59993148 318.599863027 318.599794709 318.599726594 318.599658749 318.59959124 318.599524135 318.5994575 318.5993914 318.5993259 318.599261066 318.599196961 318.599133649 318.599071191 318.59900965 318.598949087 318.598889561 318.59883113 318.598773853 318.598717786 318.598662985 318.598609503 318.598557393 318.598506707 318.598457494 318.598409804 318.598363683 318.598319177 318.59827633 318.598235184 318.59819578 318.598158156 318.598122349 318.598088396 318.598056329 318.598026181 318.59799798 318.597971755 318.597947532 318.597925334 318.597905184 318.597887101 318.597871104 318.597857207 318.597845425 318.597835769 318.597828249 318.597822872 318.597819644 318.597818568 318.597819644 318.597822872 318.597828249 318.597835769 318.597845425 318.597857207 318.597871104 318.597887101 318.597905184 318.597925334 318.597947532 318.597971755 318.59799798 318.598026181 318.598056329 318.598088396 318.598122349 318.598158156 318.59819578 318.598235184 318.59827633 318.598319177 318.598363683 318.598409804 318.598457494 318.598506707 318.598557393 318.598609503 318.598662985 318.598717786 318.598773853 318.59883113 318.598889561 318.598949087 318.59900965 318.599071191 318.599133649 318.599196961 318.599261066 318.5993259 318.5993914 318.5994575 318.599524135 318.59959124 318.599658749 318.599726594 318.599794709 318.599863027 318.59993148 318.6 318.60006852 318.600136973 318.600205291 318.600273406 318.600341251 318.60040876 318.600475865 318.6005425 318.6006086 318.6006741 318.600738934 318.600803039 318.600866351 318.600928809 318.60099035 318.601050913 318.601110439 318.60116887 318.601226147 318.601282214 318.601337015 318.601390497 318.601442607 318.601493293 318.601542506 318.601590196 318.601636317 318.601680823 318.60172367 318.601764816 318.60180422 318.601841844 318.601877651 318.601911604 318.601943671 318.601973819 318.60200202 318.602028245 318.602052468 318.602074666 318.602094816 318.602112899 318.602128896 318.602142793 318.602154575 318.602164231 318.602171751 318.602177128 318.602180356 318.602181432 318.402486899 318.402485672 318.402481992 318.402475862 318.402467289 318.402456281 318.402442849 318.402427006 318.402408768 318.402388153 318.402365181 318.402339875 318.40231226 318.402282363 318.402250213 318.402215843 318.402179286 318.402140578 318.402099758 318.402056866 318.402011943 318.401965036 318.401916189 318.40186545 318.401812871 318.401758503 318.401702399 318.401644616 318.401585209 318.401524238 318.401461762 318.401397845 318.401332547 318.401265935 318.401198073 318.401129028 318.40105887 318.400987667 318.400915489 318.400842407 318.400768494 318.400693823 318.400618467 318.4005425 318.400465998 318.400389037 318.400311691 318.400234038 318.400156154 318.400078115 318.4 318.399921885 318.399843846 318.399765962 318.399688309 318.399610963 318.399534002 318.3994575 318.399381533 318.399306177 318.399231506 318.399157593 318.399084511 318.399012333 318.39894113 318.398870972 318.398801927 318.398734065 318.398667453 318.398602155 318.398538238 318.398475762 318.398414791 318.398355384 318.398297601 318.398241497 318.398187129 318.39813455 318.398083811 318.398034964 318.397988057 318.397943134 318.397900242 318.397859422 318.397820714 318.397784157 318.397749787 318.397717637 318.39768774 318.397660125 318.397634819 318.397611847 318.397591232 318.397572994 318.397557151 318.397543719 318.397532711 318.397524138 318.397518008 318.397514328 318.397513101 318.397514328 318.397518008 318.397524138 318.397532711 318.397543719 318.397557151 318.397572994 318.397591232 318.397611847 318.397634819 318.397660125 318.39768774 318.397717637 318.397749787 318.397784157 318.397820714 318.397859422 318.397900242 318.397943134 318.397988057 318.398034964 318.398083811 318.39813455 318.398187129 318.398241497 318.398297601 318.398355384 318.398414791 318.398475762 318.398538238 318.398602155 318.398667453 318.398734065 318.398801927 318.398870972 318.39894113 318.399012333 318.399084511 318.399157593 318.399231506 318.399306177 318.399381533 318.3994575 318.399534002 318.399610963 318.399688309 318.399765962 318.399843846 318.399921885 318.4 318.400078115 318.400156154 318.400234038 318.400311691 318.400389037 318.400465998 318.4005425 318.400618467 318.400693823 318.400768494 318.400842407 318.400915489 318.400987667 318.40105887 318.401129028 318.401198073 318.401265935 318.401332547 318.401397845 318.401461762 318.401524238 318.401585209 318.401644616 318.401702399 318.401758503 318.401812871 318.40186545 318.401916189 318.401965036 318.402011943 318.402056866 318.402099758 318.402140578 318.402179286 318.402215843 318.402250213 318.402282363 318.40231226 318.402339875 318.402365181 318.402388153 318.402408768 318.402427006 318.402442849 318.402456281 318.402467289 318.402475862 318.402481992 318.402485672 318.402486899 318.202789911 318.202788534 318.202784406 318.202777529 318.202767912 318.202755563 318.202740494 318.202722721 318.202702261 318.202679134 318.202653363 318.202624974 318.202593994 318.202560454 318.202524387 318.202485829 318.202444818 318.202401394 318.2023556 318.202307481 318.202257085 318.202204462 318.202149663 318.202092743 318.202033758 318.201972765 318.201909826 318.201845001 318.201778356 318.201709956 318.201639869 318.201568163 318.201494909 318.20142018 318.20134405 318.201266593 318.201187886 318.201108007 318.201027035 318.200945049 318.20086213 318.20077836 318.200693823 318.2006086 318.200522777 318.200436438 318.200349669 318.200262554 318.20017518 318.200087633 318.2 318.199912367 318.19982482 318.199737446 318.199650331 318.199563562 318.199477223 318.1993914 318.199306177 318.19922164 318.19913787 318.199054951 318.198972965 318.198891993 318.198812114 318.198733407 318.19865595 318.19857982 318.198505091 318.198431837 318.198360131 318.198290044 318.198221644 318.198154999 318.198090174 318.198027235 318.197966242 318.197907257 318.197850337 318.197795538 318.197742915 318.197692519 318.1976444 318.197598606 318.197555182 318.197514171 318.197475613 318.197439546 318.197406006 318.197375026 318.197346637 318.197320866 318.197297739 318.197277279 318.197259506 318.197244437 318.197232088 318.197222471 318.197215594 318.197211466 318.197210089 318.197211466 318.197215594 318.197222471 318.197232088 318.197244437 318.197259506 318.197277279 318.197297739 318.197320866 318.197346637 318.197375026 318.197406006 318.197439546 318.197475613 318.197514171 318.197555182 318.197598606 318.1976444 318.197692519 318.197742915 318.197795538 318.197850337 318.197907257 318.197966242 318.198027235 318.198090174 318.198154999 318.198221644 318.198290044 318.198360131 318.198431837 318.198505091 318.19857982 318.19865595 318.198733407 318.198812114 318.198891993 318.198972965 318.199054951 318.19913787 318.19922164 318.199306177 318.1993914 318.199477223 318.199563562 318.199650331 318.199737446 318.19982482 318.199912367 318.2 318.200087633 318.20017518 318.200262554 318.200349669 318.200436438 318.200522777 318.2006086 318.200693823 318.20077836 318.20086213 318.200945049 318.201027035 318.201108007 318.201187886 318.201266593 318.20134405 318.20142018 318.201494909 318.201568163 318.201639869 318.201709956 318.201778356 318.201845001 318.201909826 318.201972765 318.202033758 318.202092743 318.202149663 318.202204462 318.202257085 318.202307481 318.2023556 318.202401394 318.202444818 318.202485829 318.202524387 318.202560454 318.202593994 318.202624974 318.202653363 318.202679134 318.202702261 318.202722721 318.202740494 318.202755563 318.202767912 318.202777529 318.202784406 318.202788534 318.202789911 318.00309017 318.003088645 318.003084072 318.003076456 318.003065803 318.003052125 318.003035435 318.003015749 318.002993087 318.002967471 318.002938926 318.002907481 318.002873167 318.002836018 318.002796069 318.002753362 318.002707937 318.002659839 318.002609117 318.00255582 318.0025 318.002441713 318.002381017 318.002317971 318.002252637 318.00218508 318.002115367 318.002043566 318.001969748 318.001893987 318.001816356 318.001736933 318.001655796 318.001573024 318.001488701 318.001402908 318.00131573 318.001227254 318.001137567 318.001046758 318.000954915 318.00086213 318.000768494 318.0006741 318.00057904 318.000483409 318.000387301 318.000290811 318.000194033 318.000097065 318 317.999902935 317.999805967 317.999709189 317.999612699 317.999516591 317.99942096 317.9993259 317.999231506 317.99913787 317.999045085 317.998953242 317.998862433 317.998772746 317.99868427 317.998597092 317.998511299 317.998426976 317.998344204 317.998263067 317.998183644 317.998106013 317.998030252 317.997956434 317.997884633 317.99781492 317.997747363 317.997682029 317.997618983 317.997558287 317.9975 317.99744418 317.997390883 317.997340161 317.997292063 317.997246638 317.997203931 317.997163982 317.997126833 317.997092519 317.997061074 317.997032529 317.997006913 317.996984251 317.996964565 317.996947875 317.996934197 317.996923544 317.996915928 317.996911355 317.99690983 317.996911355 317.996915928 317.996923544 317.996934197 317.996947875 317.996964565 317.996984251 317.997006913 317.997032529 317.997061074 317.997092519 317.997126833 317.997163982 317.997203931 317.997246638 317.997292063 317.997340161 317.997390883 317.99744418 317.9975 317.997558287 317.997618983 317.997682029 317.997747363 317.99781492 317.997884633 317.997956434 317.998030252 317.998106013 317.998183644 317.998263067 317.998344204 317.998426976 317.998511299 317.998597092 317.99868427 317.998772746 317.998862433 317.998953242 317.999045085 317.99913787 317.999231506 317.9993259 317.99942096 317.999516591 317.999612699 317.999709189 317.999805967 317.999902935 318 318.000097065 318.000194033 318.000290811 318.000387301 318.000483409 318.00057904 318.0006741 318.000768494 318.00086213 318.000954915 318.001046758 318.001137567 318.001227254 318.00131573 318.001402908 318.001488701 318.001573024 318.001655796 318.001736933 318.001816356 318.001893987 318.001969748 318.002043566 318.002115367 318.00218508 318.002252637 318.002317971 318.002381017 318.002441713 318.0025 318.00255582 318.002609117 318.002659839 318.002707937 318.002753362 318.002796069 318.002836018 318.002873167 318.002907481 318.002938926 318.002967471 318.002993087 318.003015749 318.003035435 318.003052125 318.003065803 318.003076456 318.003084072 318.003088645 318.00309017 317.803387379 317.803385708 317.803380695 317.803372346 317.803360669 317.803345675 317.803327379 317.8033058 317.803280958 317.803252879 317.803221589 317.80318712 317.803149506 317.803108783 317.803064992 317.803018177 317.802968383 317.80291566 317.802860059 317.802801636 317.802740447 317.802676555 317.802610021 317.802540911 317.802469293 317.802395239 317.802318821 317.802240114 317.802159197 317.802076149 317.801991052 317.80190399 317.801815049 317.801724316 317.801631882 317.801537838 317.801442276 317.801345291 317.801246977 317.801147434 317.801046758 317.800945049 317.800842407 317.800738934 317.800634732 317.800529903 317.800424551 317.800318781 317.800212695 317.8001064 317.8 317.7998936 317.799787305 317.799681219 317.799575449 317.799470097 317.799365268 317.799261066 317.799157593 317.799054951 317.798953242 317.798852566 317.798753023 317.798654709 317.798557724 317.798462162 317.798368118 317.798275684 317.798184951 317.79809601 317.798008948 317.797923851 317.797840803 317.797759886 317.797681179 317.797604761 317.797530707 317.797459089 317.797389979 317.797323445 317.797259553 317.797198364 317.797139941 317.79708434 317.797031617 317.796981823 317.796935008 317.796891217 317.796850494 317.79681288 317.796778411 317.796747121 317.796719042 317.7966942 317.796672621 317.796654325 317.796639331 317.796627654 317.796619305 317.796614292 317.796612621 317.796614292 317.796619305 317.796627654 317.796639331 317.796654325 317.796672621 317.7966942 317.796719042 317.796747121 317.796778411 317.79681288 317.796850494 317.796891217 317.796935008 317.796981823 317.797031617 317.79708434 317.797139941 317.797198364 317.797259553 317.797323445 317.797389979 317.797459089 317.797530707 317.797604761 317.797681179 317.797759886 317.797840803 317.797923851 317.798008948 317.79809601 317.798184951 317.798275684 317.798368118 317.798462162 317.798557724 317.798654709 317.798753023 317.798852566 317.798953242 317.799054951 317.799157593 317.799261066 317.799365268 317.799470097 317.799575449 317.799681219 317.799787305 317.7998936 317.8 317.8001064 317.800212695 317.800318781 317.800424551 317.800529903 317.800634732 317.800738934 317.800842407 317.800945049 317.801046758 317.801147434 317.801246977 317.801345291 317.801442276 317.801537838 317.801631882 317.801724316 317.801815049 317.80190399 317.801991052 317.802076149 317.802159197 317.802240114 317.802318821 317.802395239 317.802469293 317.802540911 317.802610021 317.802676555 317.802740447 317.802801636 317.802860059 317.80291566 317.802968383 317.803018177 317.803064992 317.803108783 317.803149506 317.80318712 317.803221589 317.803252879 317.803280958 317.8033058 317.803327379 317.803345675 317.803360669 317.803372346 317.803380695 317.803385708 317.803387379 317.603681246 317.603679429 317.603673981 317.603664908 317.603652218 317.603635923 317.603616041 317.603592589 317.603565592 317.603535077 317.603501073 317.603463613 317.603422736 317.60337848 317.603330891 317.603280014 317.6032259 317.603168603 317.603108178 317.603044687 317.60297819 317.602908755 317.602836448 317.602761343 317.602683512 317.602603034 317.602519986 317.602434451 317.602346514 317.602256261 317.602163782 317.602069167 317.60197251 317.601873906 317.601773454 317.60167125 317.601567398 317.601461999 317.601355157 317.601246977 317.601137567 317.601027035 317.600915489 317.600803039 317.600689797 317.600575874 317.600461382 317.600346436 317.600231147 317.600115631 317.6 317.599884369 317.599768853 317.599653564 317.599538618 317.599424126 317.599310203 317.599196961 317.599084511 317.598972965 317.598862433 317.598753023 317.598644843 317.598538001 317.598432602 317.59832875 317.598226546 317.598126094 317.59802749 317.597930833 317.597836218 317.597743739 317.597653486 317.597565549 317.597480014 317.597396966 317.597316488 317.597238657 317.597163552 317.597091245 317.59702181 317.596955313 317.596891822 317.596831397 317.5967741 317.596719986 317.596669109 317.59662152 317.596577264 317.596536387 317.596498927 317.596464923 317.596434408 317.596407411 317.596383959 317.596364077 317.596347782 317.596335092 317.596326019 317.596320571 317.596318754 317.596320571 317.596326019 317.596335092 317.596347782 317.596364077 317.596383959 317.596407411 317.596434408 317.596464923 317.596498927 317.596536387 317.596577264 317.59662152 317.596669109 317.596719986 317.5967741 317.596831397 317.596891822 317.596955313 317.59702181 317.597091245 317.597163552 317.597238657 317.597316488 317.597396966 317.597480014 317.597565549 317.597653486 317.597743739 317.597836218 317.597930833 317.59802749 317.598126094 317.598226546 317.59832875 317.598432602 317.598538001 317.598644843 317.598753023 317.598862433 317.598972965 317.599084511 317.599196961 317.599310203 317.599424126 317.599538618 317.599653564 317.599768853 317.599884369 317.6 317.600115631 317.600231147 317.600346436 317.600461382 317.600575874 317.600689797 317.600803039 317.600915489 317.601027035 317.601137567 317.601246977 317.601355157 317.601461999 317.601567398 317.60167125 317.601773454 317.601873906 317.60197251 317.602069167 317.602163782 317.602256261 317.602346514 317.602434451 317.602519986 317.602603034 317.602683512 317.602761343 317.602836448 317.602908755 317.60297819 317.603044687 317.603108178 317.603168603 317.6032259 317.603280014 317.603330891 317.60337848 317.603422736 317.603463613 317.603501073 317.603535077 317.603565592 317.603592589 317.603616041 317.603635923 317.603652218 317.603664908 317.603673981 317.603679429 317.603681246 317.403971479 317.403969519 317.403963642 317.403953853 317.403940163 317.403922583 317.403901133 317.403875833 317.403846708 317.403813786 317.403777101 317.403736688 317.403692588 317.403644843 317.403593502 317.403538614 317.403480233 317.403418419 317.403353231 317.403284733 317.403212994 317.403138084 317.403060077 317.40297905 317.402895084 317.40280826 317.402718664 317.402626386 317.402531516 317.402434147 317.402334377 317.402232302 317.402128025 317.402021647 317.401913275 317.401803014 317.401690973 317.401577264 317.401461999 317.401345291 317.401227254 317.401108007 317.400987667 317.400866351 317.400744181 317.400621276 317.400497758 317.400373749 317.400249371 317.400124747 317.4 317.399875253 317.399750629 317.399626251 317.399502242 317.399378724 317.399255819 317.399133649 317.399012333 317.398891993 317.398772746 317.398654709 317.398538001 317.398422736 317.398309027 317.398196986 317.398086725 317.397978353 317.397871975 317.397767698 317.397665623 317.397565853 317.397468484 317.397373614 317.397281336 317.39719174 317.397104916 317.39702095 317.396939923 317.396861916 317.396787006 317.396715267 317.396646769 317.396581581 317.396519767 317.396461386 317.396406498 317.396355157 317.396307412 317.396263312 317.396222899 317.396186214 317.396153292 317.396124167 317.396098867 317.396077417 317.396059837 317.396046147 317.396036358 317.396030481 317.396028521 317.396030481 317.396036358 317.396046147 317.396059837 317.396077417 317.396098867 317.396124167 317.396153292 317.396186214 317.396222899 317.396263312 317.396307412 317.396355157 317.396406498 317.396461386 317.396519767 317.396581581 317.396646769 317.396715267 317.396787006 317.396861916 317.396939923 317.39702095 317.397104916 317.39719174 317.397281336 317.397373614 317.397468484 317.397565853 317.397665623 317.397767698 317.397871975 317.397978353 317.398086725 317.398196986 317.398309027 317.398422736 317.398538001 317.398654709 317.398772746 317.398891993 317.399012333 317.399133649 317.399255819 317.399378724 317.399502242 317.399626251 317.399750629 317.399875253 317.4 317.400124747 317.400249371 317.400373749 317.400497758 317.400621276 317.400744181 317.400866351 317.400987667 317.401108007 317.401227254 317.401345291 317.401461999 317.401577264 317.401690973 317.401803014 317.401913275 317.402021647 317.402128025 317.402232302 317.402334377 317.402434147 317.402531516 317.402626386 317.402718664 317.40280826 317.402895084 317.40297905 317.403060077 317.403138084 317.403212994 317.403284733 317.403353231 317.403418419 317.403480233 317.403538614 317.403593502 317.403644843 317.403692588 317.403736688 317.403777101 317.403813786 317.403846708 317.403875833 317.403901133 317.403922583 317.403940163 317.403953853 317.403963642 317.403969519 317.403971479 317.204257793 317.204255692 317.204249391 317.204238897 317.204224219 317.204205372 317.204182376 317.204155251 317.204124027 317.204088732 317.204049402 317.204006075 317.203958796 317.203907609 317.203852566 317.203793721 317.203731132 317.203664861 317.203594973 317.203521538 317.203444627 317.203364316 317.203280686 317.203193818 317.203103797 317.203010714 317.20291466 317.202815729 317.202714019 317.202609631 317.202502668 317.202393235 317.20228144 317.202167393 317.202051207 317.201932998 317.20181288 317.201690973 317.201567398 317.201442276 317.20131573 317.201187886 317.20105887 317.200928809 317.200797831 317.200666066 317.200533643 317.200400694 317.200267349 317.200133741 317.2 317.199866259 317.199732651 317.199599306 317.199466357 317.199333934 317.199202169 317.199071191 317.19894113 317.198812114 317.19868427 317.198557724 317.198432602 317.198309027 317.19818712 317.198067002 317.197948793 317.197832607 317.19771856 317.197606765 317.197497332 317.197390369 317.197285981 317.197184271 317.19708534 317.196989286 317.196896203 317.196806182 317.196719314 317.196635684 317.196555373 317.196478462 317.196405027 317.196335139 317.196268868 317.196206279 317.196147434 317.196092391 317.196041204 317.195993925 317.195950598 317.195911268 317.195875973 317.195844749 317.195817624 317.195794628 317.195775781 317.195761103 317.195750609 317.195744308 317.195742207 317.195744308 317.195750609 317.195761103 317.195775781 317.195794628 317.195817624 317.195844749 317.195875973 317.195911268 317.195950598 317.195993925 317.196041204 317.196092391 317.196147434 317.196206279 317.196268868 317.196335139 317.196405027 317.196478462 317.196555373 317.196635684 317.196719314 317.196806182 317.196896203 317.196989286 317.19708534 317.197184271 317.197285981 317.197390369 317.197497332 317.197606765 317.19771856 317.197832607 317.197948793 317.198067002 317.19818712 317.198309027 317.198432602 317.198557724 317.19868427 317.198812114 317.19894113 317.199071191 317.199202169 317.199333934 317.199466357 317.199599306 317.199732651 317.199866259 317.2 317.200133741 317.200267349 317.200400694 317.200533643 317.200666066 317.200797831 317.200928809 317.20105887 317.201187886 317.20131573 317.201442276 317.201567398 317.201690973 317.20181288 317.201932998 317.202051207 317.202167393 317.20228144 317.202393235 317.202502668 317.202609631 317.202714019 317.202815729 317.20291466 317.203010714 317.203103797 317.203193818 317.203280686 317.203364316 317.203444627 317.203521538 317.203594973 317.203664861 317.203731132 317.203793721 317.203852566 317.203907609 317.203958796 317.204006075 317.204049402 317.204088732 317.204124027 317.204155251 317.204182376 317.204205372 317.204224219 317.204238897 317.204249391 317.204255692 317.204257793 317.004539905 317.004537665 317.004530947 317.004519757 317.004504106 317.004484011 317.004459491 317.004430569 317.004397276 317.004359642 317.004317706 317.004271509 317.004221097 317.004166519 317.004107829 317.004045085 317.003978349 317.003907687 317.003833169 317.003754867 317.00367286 317.003587229 317.003498057 317.003405433 317.003309448 317.003210198 317.003107779 317.003002293 317.002893844 317.00278254 317.002668489 317.002551805 317.002432603 317.002311 317.002187116 317.002061074 317.001932998 317.001803014 317.00167125 317.001537838 317.001402908 317.001266593 317.001129028 317.00099035 317.000850693 317.000710198 317.000569001 317.000427243 317.000285063 317.000142602 317 316.999857398 316.999714937 316.999572757 316.999430999 316.999289802 316.999149307 316.99900965 316.998870972 316.998733407 316.998597092 316.998462162 316.99832875 316.998196986 316.998067002 316.997938926 316.997812884 316.997689 316.997567397 316.997448195 316.997331511 316.99721746 316.997106156 316.996997707 316.996892221 316.996789802 316.996690552 316.996594567 316.996501943 316.996412771 316.99632714 316.996245133 316.996166831 316.996092313 316.996021651 316.995954915 316.995892171 316.995833481 316.995778903 316.995728491 316.995682294 316.995640358 316.995602724 316.995569431 316.995540509 316.995515989 316.995495894 316.995480243 316.995469053 316.995462335 316.995460095 316.995462335 316.995469053 316.995480243 316.995495894 316.995515989 316.995540509 316.995569431 316.995602724 316.995640358 316.995682294 316.995728491 316.995778903 316.995833481 316.995892171 316.995954915 316.996021651 316.996092313 316.996166831 316.996245133 316.99632714 316.996412771 316.996501943 316.996594567 316.996690552 316.996789802 316.996892221 316.996997707 316.997106156 316.99721746 316.997331511 316.997448195 316.997567397 316.997689 316.997812884 316.997938926 316.998067002 316.998196986 316.99832875 316.998462162 316.998597092 316.998733407 316.998870972 316.99900965 316.999149307 316.999289802 316.999430999 316.999572757 316.999714937 316.999857398 317 317.000142602 317.000285063 317.000427243 317.000569001 317.000710198 317.000850693 317.00099035 317.001129028 317.001266593 317.001402908 317.001537838 317.00167125 317.001803014 317.001932998 317.002061074 317.002187116 317.002311 317.002432603 317.002551805 317.002668489 317.00278254 317.002893844 317.003002293 317.003107779 317.003210198 317.003309448 317.003405433 317.003498057 317.003587229 317.00367286 317.003754867 317.003833169 317.003907687 317.003978349 317.004045085 317.004107829 317.004166519 317.004221097 317.004271509 317.004317706 317.004359642 317.004397276 317.004430569 317.004459491 317.004484011 317.004504106 317.004519757 317.004530947 317.004537665 317.004539905 316.804817537 316.80481516 316.80480803 316.804796156 316.804779549 316.804758225 316.804732205 316.804701515 316.804666185 316.80462625 316.80458175 316.804532728 316.804479232 316.804421317 316.804359038 316.804292457 316.80422164 316.804146656 316.804067581 316.803984491 316.803897469 316.803806601 316.803711976 316.803613688 316.803511833 316.803406513 316.803297831 316.803185894 316.803070813 316.802952702 316.802831677 316.802707857 316.802581365 316.802452326 316.802320866 316.802187116 316.802051207 316.801913275 316.801773454 316.801631882 316.801488701 316.80134405 316.801198073 316.801050913 316.800902716 316.800753629 316.800603797 316.80045337 316.800302496 316.800151322 316.8 316.799848678 316.799697504 316.79954663 316.799396203 316.799246371 316.799097284 316.798949087 316.798801927 316.79865595 316.798511299 316.798368118 316.798226546 316.798086725 316.797948793 316.797812884 316.797679134 316.797547674 316.797418635 316.797292143 316.797168323 316.797047298 316.796929187 316.796814106 316.796702169 316.796593487 316.796488167 316.796386312 316.796288024 316.796193399 316.796102531 316.796015509 316.795932419 316.795853344 316.79577836 316.795707543 316.795640962 316.795578683 316.795520768 316.795467272 316.79541825 316.79537375 316.795333815 316.795298485 316.795267795 316.795241775 316.795220451 316.795203844 316.79519197 316.79518484 316.795182463 316.79518484 316.79519197 316.795203844 316.795220451 316.795241775 316.795267795 316.795298485 316.795333815 316.79537375 316.79541825 316.795467272 316.795520768 316.795578683 316.795640962 316.795707543 316.79577836 316.795853344 316.795932419 316.796015509 316.796102531 316.796193399 316.796288024 316.796386312 316.796488167 316.796593487 316.796702169 316.796814106 316.796929187 316.797047298 316.797168323 316.797292143 316.797418635 316.797547674 316.797679134 316.797812884 316.797948793 316.798086725 316.798226546 316.798368118 316.798511299 316.79865595 316.798801927 316.798949087 316.799097284 316.799246371 316.799396203 316.79954663 316.799697504 316.799848678 316.8 316.800151322 316.800302496 316.80045337 316.800603797 316.800753629 316.800902716 316.801050913 316.801198073 316.80134405 316.801488701 316.801631882 316.801773454 316.801913275 316.802051207 316.802187116 316.802320866 316.802452326 316.802581365 316.802707857 316.802831677 316.802952702 316.803070813 316.803185894 316.803297831 316.803406513 316.803511833 316.803613688 316.803711976 316.803806601 316.803897469 316.803984491 316.804067581 316.804146656 316.80422164 316.804292457 316.804359038 316.804421317 316.804479232 316.804532728 316.80458175 316.80462625 316.804666185 316.804701515 316.804732205 316.804758225 316.804779549 316.804796156 316.80480803 316.80481516 316.804817537 316.605090414 316.605087902 316.605080369 316.605067823 316.605050275 316.605027743 316.605000249 316.604967821 316.604930489 316.604888293 316.604841272 316.604789473 316.604732947 316.604671751 316.604605944 316.604535592 316.604460764 316.604381533 316.604297979 316.604210183 316.604118232 316.604022216 316.603922232 316.603818376 316.603710752 316.603599466 316.603484628 316.603366351 316.603244752 316.603119951 316.60299207 316.602861237 316.60272758 316.602591232 316.602452326 316.602311 316.602167393 316.602021647 316.601873906 316.601724316 316.601573024 316.60142018 316.601265935 316.601110439 316.600953848 316.600796316 316.600637998 316.60047905 316.60031963 316.600159894 316.6 316.599840106 316.59968037 316.59952095 316.599362002 316.599203684 316.599046152 316.598889561 316.598734065 316.59857982 316.598426976 316.598275684 316.598126094 316.597978353 316.597832607 316.597689 316.597547674 316.597408768 316.59727242 316.597138763 316.59700793 316.596880049 316.596755248 316.596633649 316.596515372 316.596400534 316.596289248 316.596181624 316.596077768 316.595977784 316.595881768 316.595789817 316.595702021 316.595618467 316.595539236 316.595464408 316.595394056 316.595328249 316.595267053 316.595210527 316.595158728 316.595111707 316.595069511 316.595032179 316.594999751 316.594972257 316.594949725 316.594932177 316.594919631 316.594912098 316.594909586 316.594912098 316.594919631 316.594932177 316.594949725 316.594972257 316.594999751 316.595032179 316.595069511 316.595111707 316.595158728 316.595210527 316.595267053 316.595328249 316.595394056 316.595464408 316.595539236 316.595618467 316.595702021 316.595789817 316.595881768 316.595977784 316.596077768 316.596181624 316.596289248 316.596400534 316.596515372 316.596633649 316.596755248 316.596880049 316.59700793 316.597138763 316.59727242 316.597408768 316.597547674 316.597689 316.597832607 316.597978353 316.598126094 316.598275684 316.598426976 316.59857982 316.598734065 316.598889561 316.599046152 316.599203684 316.599362002 316.59952095 316.59968037 316.599840106 316.6 316.600159894 316.60031963 316.60047905 316.600637998 316.600796316 316.600953848 316.601110439 316.601265935 316.60142018 316.601573024 316.601724316 316.601873906 316.602021647 316.602167393 316.602311 316.602452326 316.602591232 316.60272758 316.602861237 316.60299207 316.603119951 316.603244752 316.603366351 316.603484628 316.603599466 316.603710752 316.603818376 316.603922232 316.604022216 316.604118232 316.604210183 316.604297979 316.604381533 316.604460764 316.604535592 316.604605944 316.604671751 316.604732947 316.604789473 316.604841272 316.604888293 316.604930489 316.604967821 316.605000249 316.605027743 316.605050275 316.605067823 316.605080369 316.605087902 316.605090414 316.405358268 316.405355624 316.405347695 316.405334488 316.405316016 316.405292299 316.405263358 316.405229224 316.405189928 316.405145511 316.405096016 316.405041491 316.404981992 316.404917575 316.404848306 316.404774252 316.404695486 316.404612086 316.404524135 316.404431719 316.40433493 316.404233862 316.404128616 316.404019296 316.403906009 316.403788868 316.403667987 316.403543486 316.403415489 316.40328412 316.403149511 316.403011793 316.402871104 316.40272758 316.402581365 316.402432603 316.40228144 316.402128025 316.40197251 316.401815049 316.401655796 316.401494909 316.401332547 316.40116887 316.401004039 316.400838218 316.400671569 316.400504258 316.400336448 316.400168307 316.4 316.399831693 316.399663552 316.399495742 316.399328431 316.399161782 316.398995961 316.39883113 316.398667453 316.398505091 316.398344204 316.398184951 316.39802749 316.397871975 316.39771856 316.397567397 316.397418635 316.39727242 316.397128896 316.396988207 316.396850489 316.39671588 316.396584511 316.396456514 316.396332013 316.396211132 316.396093991 316.395980704 316.395871384 316.395766138 316.39566507 316.395568281 316.395475865 316.395387914 316.395304514 316.395225748 316.395151694 316.395082425 316.395018008 316.394958509 316.394903984 316.394854489 316.394810072 316.394770776 316.394736642 316.394707701 316.394683984 316.394665512 316.394652305 316.394644376 316.394641732 316.394644376 316.394652305 316.394665512 316.394683984 316.394707701 316.394736642 316.394770776 316.394810072 316.394854489 316.394903984 316.394958509 316.395018008 316.395082425 316.395151694 316.395225748 316.395304514 316.395387914 316.395475865 316.395568281 316.39566507 316.395766138 316.395871384 316.395980704 316.396093991 316.396211132 316.396332013 316.396456514 316.396584511 316.39671588 316.396850489 316.396988207 316.397128896 316.39727242 316.397418635 316.397567397 316.39771856 316.397871975 316.39802749 316.398184951 316.398344204 316.398505091 316.398667453 316.39883113 316.398995961 316.399161782 316.399328431 316.399495742 316.399663552 316.399831693 316.4 316.400168307 316.400336448 316.400504258 316.400671569 316.400838218 316.401004039 316.40116887 316.401332547 316.401494909 316.401655796 316.401815049 316.40197251 316.402128025 316.40228144 316.402432603 316.402581365 316.40272758 316.402871104 316.403011793 316.403149511 316.40328412 316.403415489 316.403543486 316.403667987 316.403788868 316.403906009 316.404019296 316.404128616 316.404233862 316.40433493 316.404431719 316.404524135 316.404612086 316.404695486 316.404774252 316.404848306 316.404917575 316.404981992 316.405041491 316.405096016 316.405145511 316.405189928 316.405229224 316.405263358 316.405292299 316.405316016 316.405334488 316.405347695 316.405355624 316.405358268 316.205620834 316.20561806 316.205609742 316.205595888 316.205576512 316.205551632 316.205521273 316.205485466 316.205444245 316.205397651 316.205345731 316.205288534 316.205226119 316.205158546 316.205085882 316.2050082 316.204925574 316.204838088 316.204745827 316.204648882 316.20454735 316.20444133 316.204330927 316.20421625 316.204097411 316.20397453 316.203847725 316.203717124 316.203582854 316.203445049 316.203303843 316.203159377 316.203011793 316.202861237 316.202707857 316.202551805 316.202393235 316.202232302 316.202069167 316.20190399 316.201736933 316.201568163 316.201397845 316.201226147 316.201053239 316.200879292 316.200704477 316.200528967 316.200352935 316.200176555 316.2 316.199823445 316.199647065 316.199471033 316.199295523 316.199120708 316.198946761 316.198773853 316.198602155 316.198431837 316.198263067 316.19809601 316.197930833 316.197767698 316.197606765 316.197448195 316.197292143 316.197138763 316.196988207 316.196840623 316.196696157 316.196554951 316.196417146 316.196282876 316.196152275 316.19602547 316.195902589 316.19578375 316.195669073 316.19555867 316.19545265 316.195351118 316.195254173 316.195161912 316.195074426 316.1949918 316.194914118 316.194841454 316.194773881 316.194711466 316.194654269 316.194602349 316.194555755 316.194514534 316.194478727 316.194448368 316.194423488 316.194404112 316.194390258 316.19438194 316.194379166 316.19438194 316.194390258 316.194404112 316.194423488 316.194448368 316.194478727 316.194514534 316.194555755 316.194602349 316.194654269 316.194711466 316.194773881 316.194841454 316.194914118 316.1949918 316.195074426 316.195161912 316.195254173 316.195351118 316.19545265 316.19555867 316.195669073 316.19578375 316.195902589 316.19602547 316.196152275 316.196282876 316.196417146 316.196554951 316.196696157 316.196840623 316.196988207 316.197138763 316.197292143 316.197448195 316.197606765 316.197767698 316.197930833 316.19809601 316.198263067 316.198431837 316.198602155 316.198773853 316.198946761 316.199120708 316.199295523 316.199471033 316.199647065 316.199823445 316.2 316.200176555 316.200352935 316.200528967 316.200704477 316.200879292 316.201053239 316.201226147 316.201397845 316.201568163 316.201736933 316.20190399 316.202069167 316.202232302 316.202393235 316.202551805 316.202707857 316.202861237 316.203011793 316.203159377 316.203303843 316.203445049 316.203582854 316.203717124 316.203847725 316.20397453 316.204097411 316.20421625 316.204330927 316.20444133 316.20454735 316.204648882 316.204745827 316.204838088 316.204925574 316.2050082 316.205085882 316.205158546 316.205226119 316.205288534 316.205345731 316.205397651 316.205444245 316.205485466 316.205521273 316.205551632 316.205576512 316.205595888 316.205609742 316.20561806 316.205620834 316.005877853 316.005874952 316.005866254 316.005851766 316.005831504 316.005805486 316.00577374 316.005736295 316.005693189 316.005644465 316.00559017 316.005530358 316.005465089 316.005394426 316.00531844 316.005237205 316.005150801 316.005059315 316.004962835 316.004861458 316.004755283 316.004644415 316.004528963 316.004409042 316.00428477 316.004156269 316.004023667 316.003887094 316.003746684 316.003602577 316.003454915 316.003303843 316.003149511 316.00299207 316.002831677 316.002668489 316.002502668 316.002334377 316.002163782 316.001991052 316.001816356 316.001639869 316.001461762 316.001282214 316.0011014 316.000919499 316.00073669 316.000553155 316.000369073 316.000184628 316 315.999815372 315.999630927 315.999446845 315.99926331 315.999080501 315.9988986 315.998717786 315.998538238 315.998360131 315.998183644 315.998008948 315.997836218 315.997665623 315.997497332 315.997331511 315.997168323 315.99700793 315.996850489 315.996696157 315.996545085 315.996397423 315.996253316 315.996112906 315.995976333 315.995843731 315.99571523 315.995590958 315.995471037 315.995355585 315.995244717 315.995138542 315.995037165 315.994940685 315.994849199 315.994762795 315.99468156 315.994605574 315.994534911 315.994469642 315.99440983 315.994355535 315.994306811 315.994263705 315.99422626 315.994194514 315.994168496 315.994148234 315.994133746 315.994125048 315.994122147 315.994125048 315.994133746 315.994148234 315.994168496 315.994194514 315.99422626 315.994263705 315.994306811 315.994355535 315.99440983 315.994469642 315.994534911 315.994605574 315.99468156 315.994762795 315.994849199 315.994940685 315.995037165 315.995138542 315.995244717 315.995355585 315.995471037 315.995590958 315.99571523 315.995843731 315.995976333 315.996112906 315.996253316 315.996397423 315.996545085 315.996696157 315.996850489 315.99700793 315.997168323 315.997331511 315.997497332 315.997665623 315.997836218 315.998008948 315.998183644 315.998360131 315.998538238 315.998717786 315.9988986 315.999080501 315.99926331 315.999446845 315.999630927 315.999815372 316 316.000184628 316.000369073 316.000553155 316.00073669 316.000919499 316.0011014 316.001282214 316.001461762 316.001639869 316.001816356 316.001991052 316.002163782 316.002334377 316.002502668 316.002668489 316.002831677 316.00299207 316.003149511 316.003303843 316.003454915 316.003602577 316.003746684 316.003887094 316.004023667 316.004156269 316.00428477 316.004409042 316.004528963 316.004644415 316.004755283 316.004861458 316.004962835 316.005059315 316.005150801 316.005237205 316.00531844 316.005394426 316.005465089 316.005530358 316.00559017 316.005644465 316.005693189 316.005736295 316.00577374 316.005805486 316.005831504 316.005851766 316.005866254 316.005874952 316.005877853 315.806129071 315.806126046 315.806116976 315.80610187 315.806080741 315.806053612 315.806020508 315.805981463 315.805936515 315.805885708 315.805829092 315.805766725 315.805698666 315.805624983 315.805545749 315.805461042 315.805370945 315.805275549 315.805174945 315.805069235 315.804958522 315.804842916 315.80472253 315.804597484 315.8044679 315.804333907 315.804195637 315.804053227 315.803906817 315.803756551 315.803602577 315.803445049 315.80328412 315.803119951 315.802952702 315.80278254 315.802609631 315.802434147 315.802256261 315.802076149 315.801893987 315.801709956 315.801524238 315.801337015 315.801148473 315.800958798 315.800768176 315.800576796 315.800384848 315.800192519 315.8 315.799807481 315.799615152 315.799423204 315.799231824 315.799041202 315.798851527 315.798662985 315.798475762 315.798290044 315.798106013 315.797923851 315.797743739 315.797565853 315.797390369 315.79721746 315.797047298 315.796880049 315.79671588 315.796554951 315.796397423 315.796243449 315.796093183 315.795946773 315.795804363 315.795666093 315.7955321 315.795402516 315.79527747 315.795157084 315.795041478 315.794930765 315.794825055 315.794724451 315.794629055 315.794538958 315.794454251 315.794375017 315.794301334 315.794233275 315.794170908 315.794114292 315.794063485 315.794018537 315.793979492 315.793946388 315.793919259 315.79389813 315.793883024 315.793873954 315.793870929 315.793873954 315.793883024 315.79389813 315.793919259 315.793946388 315.793979492 315.794018537 315.794063485 315.794114292 315.794170908 315.794233275 315.794301334 315.794375017 315.794454251 315.794538958 315.794629055 315.794724451 315.794825055 315.794930765 315.795041478 315.795157084 315.79527747 315.795402516 315.7955321 315.795666093 315.795804363 315.795946773 315.796093183 315.796243449 315.796397423 315.796554951 315.79671588 315.796880049 315.797047298 315.79721746 315.797390369 315.797565853 315.797743739 315.797923851 315.798106013 315.798290044 315.798475762 315.798662985 315.798851527 315.799041202 315.799231824 315.799423204 315.799615152 315.799807481 315.8 315.800192519 315.800384848 315.800576796 315.800768176 315.800958798 315.801148473 315.801337015 315.801524238 315.801709956 315.801893987 315.802076149 315.802256261 315.802434147 315.802609631 315.80278254 315.802952702 315.803119951 315.80328412 315.803445049 315.803602577 315.803756551 315.803906817 315.804053227 315.804195637 315.804333907 315.8044679 315.804597484 315.80472253 315.804842916 315.804958522 315.805069235 315.805174945 315.805275549 315.805370945 315.805461042 315.805545749 315.805624983 315.805698666 315.805766725 315.805829092 315.805885708 315.805936515 315.805981463 315.806020508 315.806053612 315.806080741 315.80610187 315.806116976 315.806126046 315.806129071 315.60637424 315.606371095 315.606361662 315.606345951 315.606323977 315.606295762 315.606261335 315.606220728 315.606173981 315.606121142 315.606062262 315.6059974 315.605926618 315.605849988 315.605767585 315.605679489 315.605585789 315.605486576 315.605381949 315.60527201 315.605156868 315.605036638 315.604911436 315.604781388 315.604646621 315.604507268 315.604363467 315.60421536 315.604063093 315.603906817 315.603746684 315.603582854 315.603415489 315.603244752 315.603070813 315.602893844 315.602714019 315.602531516 315.602346514 315.602159197 315.601969748 315.601778356 315.601585209 315.601390497 315.601194413 315.600997151 315.600798904 315.600599869 315.600400242 315.60020022 315.6 315.59979978 315.599599758 315.599400131 315.599201096 315.599002849 315.598805587 315.598609503 315.598414791 315.598221644 315.598030252 315.597840803 315.597653486 315.597468484 315.597285981 315.597106156 315.596929187 315.596755248 315.596584511 315.596417146 315.596253316 315.596093183 315.595936907 315.59578464 315.595636533 315.595492732 315.595353379 315.595218612 315.595088564 315.594963362 315.594843132 315.59472799 315.594618051 315.594513424 315.594414211 315.594320511 315.594232415 315.594150012 315.594073382 315.5940026 315.593937738 315.593878858 315.593826019 315.593779272 315.593738665 315.593704238 315.593676023 315.593654049 315.593638338 315.593628905 315.59362576 315.593628905 315.593638338 315.593654049 315.593676023 315.593704238 315.593738665 315.593779272 315.593826019 315.593878858 315.593937738 315.5940026 315.594073382 315.594150012 315.594232415 315.594320511 315.594414211 315.594513424 315.594618051 315.59472799 315.594843132 315.594963362 315.595088564 315.595218612 315.595353379 315.595492732 315.595636533 315.59578464 315.595936907 315.596093183 315.596253316 315.596417146 315.596584511 315.596755248 315.596929187 315.597106156 315.597285981 315.597468484 315.597653486 315.597840803 315.598030252 315.598221644 315.598414791 315.598609503 315.598805587 315.599002849 315.599201096 315.599400131 315.599599758 315.59979978 315.6 315.60020022 315.600400242 315.600599869 315.600798904 315.600997151 315.601194413 315.601390497 315.601585209 315.601778356 315.601969748 315.602159197 315.602346514 315.602531516 315.602714019 315.602893844 315.603070813 315.603244752 315.603415489 315.603582854 315.603746684 315.603906817 315.604063093 315.60421536 315.604363467 315.604507268 315.604646621 315.604781388 315.604911436 315.605036638 315.605156868 315.60527201 315.605381949 315.605486576 315.605585789 315.605679489 315.605767585 315.605849988 315.605926618 315.6059974 315.606062262 315.606121142 315.606173981 315.606220728 315.606261335 315.606295762 315.606323977 315.606345951 315.606361662 315.606371095 315.60637424 315.406613119 315.406609855 315.406600069 315.406583769 315.406560972 315.4065317 315.406495982 315.406453853 315.406405355 315.406350536 315.40628945 315.406222156 315.406148722 315.40606922 315.405983729 315.405892332 315.40579512 315.405692189 315.405583641 315.405469582 315.405350125 315.405225389 315.405095495 315.404960574 315.404820756 315.404676181 315.404526991 315.404373334 315.40421536 315.404053227 315.403887094 315.403717124 315.403543486 315.403366351 315.403185894 315.403002293 315.402815729 315.402626386 315.402434451 315.402240114 315.402043566 315.401845001 315.401644616 315.401442607 315.401239175 315.40103452 315.400828844 315.400622349 315.400415241 315.400207723 315.4 315.399792277 315.399584759 315.399377651 315.399171156 315.39896548 315.398760825 315.398557393 315.398355384 315.398154999 315.397956434 315.397759886 315.397565549 315.397373614 315.397184271 315.396997707 315.396814106 315.396633649 315.396456514 315.396282876 315.396112906 315.395946773 315.39578464 315.395626666 315.395473009 315.395323819 315.395179244 315.395039426 315.394904505 315.394774611 315.394649875 315.394530418 315.394416359 315.394307811 315.39420488 315.394107668 315.394016271 315.39393078 315.393851278 315.393777844 315.39371055 315.393649464 315.393594645 315.393546147 315.393504018 315.3934683 315.393439028 315.393416231 315.393399931 315.393390145 315.393386881 315.393390145 315.393399931 315.393416231 315.393439028 315.3934683 315.393504018 315.393546147 315.393594645 315.393649464 315.39371055 315.393777844 315.393851278 315.39393078 315.394016271 315.394107668 315.39420488 315.394307811 315.394416359 315.394530418 315.394649875 315.394774611 315.394904505 315.395039426 315.395179244 315.395323819 315.395473009 315.395626666 315.39578464 315.395946773 315.396112906 315.396282876 315.396456514 315.396633649 315.396814106 315.396997707 315.397184271 315.397373614 315.397565549 315.397759886 315.397956434 315.398154999 315.398355384 315.398557393 315.398760825 315.39896548 315.399171156 315.399377651 315.399584759 315.399792277 315.4 315.400207723 315.400415241 315.400622349 315.400828844 315.40103452 315.401239175 315.401442607 315.401644616 315.401845001 315.402043566 315.402240114 315.402434451 315.402626386 315.402815729 315.403002293 315.403185894 315.403366351 315.403543486 315.403717124 315.403887094 315.404053227 315.40421536 315.404373334 315.404526991 315.404676181 315.404820756 315.404960574 315.405095495 315.405225389 315.405350125 315.405469582 315.405583641 315.405692189 315.40579512 315.405892332 315.405983729 315.40606922 315.406148722 315.406222156 315.40628945 315.406350536 315.406405355 315.406453853 315.406495982 315.4065317 315.406560972 315.406583769 315.406600069 315.406609855 315.406613119 315.206845471 315.206842093 315.206831963 315.206815091 315.206791492 315.206761192 315.206724219 315.20668061 315.206630408 315.206573663 315.20651043 315.206440772 315.206364758 315.206282463 315.206193967 315.206099359 315.205998732 315.205892185 315.205779822 315.205661756 315.205538102 315.205408983 315.205274526 315.205134864 315.204990134 315.204840479 315.204686047 315.204526991 315.204363467 315.204195637 315.204023667 315.203847725 315.203667987 315.203484628 315.203297831 315.203107779 315.20291466 315.202718664 315.202519986 315.202318821 315.202115367 315.201909826 315.201702399 315.201493293 315.201282713 315.201070868 315.200857965 315.200644216 315.200429831 315.200215021 315.2 315.199784979 315.199570169 315.199355784 315.199142035 315.198929132 315.198717287 315.198506707 315.198297601 315.198090174 315.197884633 315.197681179 315.197480014 315.197281336 315.19708534 315.196892221 315.196702169 315.196515372 315.196332013 315.196152275 315.195976333 315.195804363 315.195636533 315.195473009 315.195313953 315.195159521 315.195009866 315.194865136 315.194725474 315.194591017 315.194461898 315.194338244 315.194220178 315.194107815 315.194001268 315.193900641 315.193806033 315.193717537 315.193635242 315.193559228 315.19348957 315.193426337 315.193369592 315.19331939 315.193275781 315.193238808 315.193208508 315.193184909 315.193168037 315.193157907 315.193154529 315.193157907 315.193168037 315.193184909 315.193208508 315.193238808 315.193275781 315.19331939 315.193369592 315.193426337 315.19348957 315.193559228 315.193635242 315.193717537 315.193806033 315.193900641 315.194001268 315.194107815 315.194220178 315.194338244 315.194461898 315.194591017 315.194725474 315.194865136 315.195009866 315.195159521 315.195313953 315.195473009 315.195636533 315.195804363 315.195976333 315.196152275 315.196332013 315.196515372 315.196702169 315.196892221 315.19708534 315.197281336 315.197480014 315.197681179 315.197884633 315.198090174 315.198297601 315.198506707 315.198717287 315.198929132 315.199142035 315.199355784 315.199570169 315.199784979 315.2 315.200215021 315.200429831 315.200644216 315.200857965 315.201070868 315.201282713 315.201493293 315.201702399 315.201909826 315.202115367 315.202318821 315.202519986 315.202718664 315.20291466 315.203107779 315.203297831 315.203484628 315.203667987 315.203847725 315.204023667 315.204195637 315.204363467 315.204526991 315.204686047 315.204840479 315.204990134 315.205134864 315.205274526 315.205408983 315.205538102 315.205661756 315.205779822 315.205892185 315.205998732 315.206099359 315.206193967 315.206282463 315.206364758 315.206440772 315.20651043 315.206573663 315.206630408 315.20668061 315.206724219 315.206761192 315.206791492 315.206815091 315.206831963 315.206842093 315.206845471 315.007071068 315.007067579 315.007057115 315.007039686 315.00701531 315.006984011 315.00694582 315.006900774 315.006848917 315.006790302 315.006724985 315.006653032 315.006574513 315.006489505 315.006398093 315.006300368 315.006196424 315.006086365 315.0059703 315.005848343 315.005720614 315.00558724 315.005448351 315.005304086 315.005154587 315.005 315.004840479 315.004676181 315.004507268 315.004333907 315.004156269 315.00397453 315.003788868 315.003599466 315.003406513 315.003210198 315.003010714 315.00280826 315.002603034 315.002395239 315.00218508 315.001972765 315.001758503 315.001542506 315.001324986 315.001106159 315.00088624 315.000665446 315.000443996 315.000222108 315 314.999777892 314.999556004 314.999334554 314.99911376 314.998893841 314.998675014 314.998457494 314.998241497 314.998027235 314.99781492 314.997604761 314.997396966 314.99719174 314.996989286 314.996789802 314.996593487 314.996400534 314.996211132 314.99602547 314.995843731 314.995666093 314.995492732 314.995323819 314.995159521 314.995 314.994845413 314.994695914 314.994551649 314.99441276 314.994279386 314.994151657 314.9940297 314.993913635 314.993803576 314.993699632 314.993601907 314.993510495 314.993425487 314.993346968 314.993275015 314.993209698 314.993151083 314.993099226 314.99305418 314.993015989 314.99298469 314.992960314 314.992942885 314.992932421 314.992928932 314.992932421 314.992942885 314.992960314 314.99298469 314.993015989 314.99305418 314.993099226 314.993151083 314.993209698 314.993275015 314.993346968 314.993425487 314.993510495 314.993601907 314.993699632 314.993803576 314.993913635 314.9940297 314.994151657 314.994279386 314.99441276 314.994551649 314.994695914 314.994845413 314.995 314.995159521 314.995323819 314.995492732 314.995666093 314.995843731 314.99602547 314.996211132 314.996400534 314.996593487 314.996789802 314.996989286 314.99719174 314.997396966 314.997604761 314.99781492 314.998027235 314.998241497 314.998457494 314.998675014 314.998893841 314.99911376 314.999334554 314.999556004 314.999777892 315 315.000222108 315.000443996 315.000665446 315.00088624 315.001106159 315.001324986 315.001542506 315.001758503 315.001972765 315.00218508 315.002395239 315.002603034 315.00280826 315.003010714 315.003210198 315.003406513 315.003599466 315.003788868 315.00397453 315.004156269 315.004333907 315.004507268 315.004676181 315.004840479 315.005 315.005154587 315.005304086 315.005448351 315.00558724 315.005720614 315.005848343 315.0059703 315.006086365 315.006196424 315.006300368 315.006398093 315.006489505 315.006574513 315.006653032 315.006724985 315.006790302 315.006848917 315.006900774 315.00694582 315.006984011 315.00701531 315.007039686 315.007057115 315.007067579 315.007071068 314.807289686 314.807286089 314.807275302 314.807257334 314.807232205 314.807199938 314.807160566 314.807114127 314.807060667 314.80700024 314.806932904 314.806858726 314.806777779 314.806690143 314.806595905 314.806495158 314.806388001 314.806274539 314.806154886 314.806029158 314.80589748 314.805759982 314.8056168 314.805468074 314.805313953 314.805154587 314.804990134 314.804820756 314.804646621 314.8044679 314.80428477 314.804097411 314.803906009 314.803710752 314.803511833 314.803309448 314.803103797 314.802895084 314.802683512 314.802469293 314.802252637 314.802033758 314.801812871 314.801590196 314.801365951 314.801140358 314.80091364 314.80068602 314.800457723 314.800228975 314.8 314.799771025 314.799542277 314.79931398 314.79908636 314.798859642 314.798634049 314.798409804 314.798187129 314.797966242 314.797747363 314.797530707 314.797316488 314.797104916 314.796896203 314.796690552 314.796488167 314.796289248 314.796093991 314.795902589 314.79571523 314.7955321 314.795353379 314.795179244 314.795009866 314.794845413 314.794686047 314.794531926 314.7943832 314.794240018 314.79410252 314.793970842 314.793845114 314.793725461 314.793611999 314.793504842 314.793404095 314.793309857 314.793222221 314.793141274 314.793067096 314.79299976 314.792939333 314.792885873 314.792839434 314.792800062 314.792767795 314.792742666 314.792724698 314.792713911 314.792710314 314.792713911 314.792724698 314.792742666 314.792767795 314.792800062 314.792839434 314.792885873 314.792939333 314.79299976 314.793067096 314.793141274 314.793222221 314.793309857 314.793404095 314.793504842 314.793611999 314.793725461 314.793845114 314.793970842 314.79410252 314.794240018 314.7943832 314.794531926 314.794686047 314.794845413 314.795009866 314.795179244 314.795353379 314.7955321 314.79571523 314.795902589 314.796093991 314.796289248 314.796488167 314.796690552 314.796896203 314.797104916 314.797316488 314.797530707 314.797747363 314.797966242 314.798187129 314.798409804 314.798634049 314.798859642 314.79908636 314.79931398 314.799542277 314.799771025 314.8 314.800228975 314.800457723 314.80068602 314.80091364 314.801140358 314.801365951 314.801590196 314.801812871 314.802033758 314.802252637 314.802469293 314.802683512 314.802895084 314.803103797 314.803309448 314.803511833 314.803710752 314.803906009 314.804097411 314.80428477 314.8044679 314.804646621 314.804820756 314.804990134 314.805154587 314.805313953 314.805468074 314.8056168 314.805759982 314.80589748 314.806029158 314.806154886 314.806274539 314.806388001 314.806495158 314.806595905 314.806690143 314.806777779 314.806858726 314.806932904 314.80700024 314.807060667 314.807114127 314.807160566 314.807199938 314.807232205 314.807257334 314.807275302 314.807286089 314.807289686 314.607501111 314.607497409 314.607486309 314.607467821 314.607441962 314.60740876 314.607368245 314.60732046 314.60726545 314.607203269 314.60713398 314.607057651 314.606974356 314.606884179 314.606787208 314.606683539 314.606573273 314.606456521 314.606333397 314.606204023 314.606068526 314.60592704 314.605779705 314.605626666 314.605468074 314.605304086 314.605134864 314.604960574 314.604781388 314.604597484 314.604409042 314.60421625 314.604019296 314.603818376 314.603613688 314.603405433 314.603193818 314.60297905 314.602761343 314.602540911 314.602317971 314.602092743 314.60186545 314.601636317 314.601405568 314.601173432 314.600940138 314.600705917 314.600470999 314.600235616 314.6 314.599764384 314.599529001 314.599294083 314.599059862 314.598826568 314.598594432 314.598363683 314.59813455 314.597907257 314.597682029 314.597459089 314.597238657 314.59702095 314.596806182 314.596594567 314.596386312 314.596181624 314.595980704 314.59578375 314.595590958 314.595402516 314.595218612 314.595039426 314.594865136 314.594695914 314.594531926 314.594373334 314.594220295 314.59407296 314.593931474 314.593795977 314.593666603 314.593543479 314.593426727 314.593316461 314.593212792 314.593115821 314.593025644 314.592942349 314.59286602 314.592796731 314.59273455 314.59267954 314.592631755 314.59259124 314.592558038 314.592532179 314.592513691 314.592502591 314.592498889 314.592502591 314.592513691 314.592532179 314.592558038 314.59259124 314.592631755 314.59267954 314.59273455 314.592796731 314.59286602 314.592942349 314.593025644 314.593115821 314.593212792 314.593316461 314.593426727 314.593543479 314.593666603 314.593795977 314.593931474 314.59407296 314.594220295 314.594373334 314.594531926 314.594695914 314.594865136 314.595039426 314.595218612 314.595402516 314.595590958 314.59578375 314.595980704 314.596181624 314.596386312 314.596594567 314.596806182 314.59702095 314.597238657 314.597459089 314.597682029 314.597907257 314.59813455 314.598363683 314.598594432 314.598826568 314.599059862 314.599294083 314.599529001 314.599764384 314.6 314.600235616 314.600470999 314.600705917 314.600940138 314.601173432 314.601405568 314.601636317 314.60186545 314.602092743 314.602317971 314.602540911 314.602761343 314.60297905 314.603193818 314.603405433 314.603613688 314.603818376 314.604019296 314.60421625 314.604409042 314.604597484 314.604781388 314.604960574 314.605134864 314.605304086 314.605468074 314.605626666 314.605779705 314.60592704 314.606068526 314.606204023 314.606333397 314.606456521 314.606573273 314.606683539 314.606787208 314.606884179 314.606974356 314.607057651 314.60713398 314.607203269 314.60726545 314.60732046 314.607368245 314.60740876 314.607441962 314.607467821 314.607486309 314.607497409 314.607501111 314.407705132 314.40770133 314.407689928 314.407670937 314.407644375 314.407610269 314.407568653 314.407519568 314.407463062 314.40739919 314.407328016 314.407249611 314.407164051 314.407071421 314.406971812 314.406865323 314.406752059 314.406632131 314.406505658 314.406372765 314.406233583 314.406088249 314.405936907 314.405779705 314.4056168 314.405448351 314.405274526 314.405095495 314.404911436 314.40472253 314.404528963 314.404330927 314.404128616 314.403922232 314.403711976 314.403498057 314.403280686 314.403060077 314.402836448 314.402610021 314.402381017 314.402149663 314.401916189 314.401680823 314.401443798 314.401205348 314.400965709 314.400725117 314.400483809 314.400242024 314.4 314.399757976 314.399516191 314.399274883 314.399034291 314.398794652 314.398556202 314.398319177 314.398083811 314.397850337 314.397618983 314.397389979 314.397163552 314.396939923 314.396719314 314.396501943 314.396288024 314.396077768 314.395871384 314.395669073 314.395471037 314.39527747 314.395088564 314.394904505 314.394725474 314.394551649 314.3943832 314.394220295 314.394063093 314.393911751 314.393766417 314.393627235 314.393494342 314.393367869 314.393247941 314.393134677 314.393028188 314.392928579 314.392835949 314.392750389 314.392671984 314.39260081 314.392536938 314.392480432 314.392431347 314.392389731 314.392355625 314.392329063 314.392310072 314.39229867 314.392294868 314.39229867 314.392310072 314.392329063 314.392355625 314.392389731 314.392431347 314.392480432 314.392536938 314.39260081 314.392671984 314.392750389 314.392835949 314.392928579 314.393028188 314.393134677 314.393247941 314.393367869 314.393494342 314.393627235 314.393766417 314.393911751 314.394063093 314.394220295 314.3943832 314.394551649 314.394725474 314.394904505 314.395088564 314.39527747 314.395471037 314.395669073 314.395871384 314.396077768 314.396288024 314.396501943 314.396719314 314.396939923 314.397163552 314.397389979 314.397618983 314.397850337 314.398083811 314.398319177 314.398556202 314.398794652 314.399034291 314.399274883 314.399516191 314.399757976 314.4 314.400242024 314.400483809 314.400725117 314.400965709 314.401205348 314.401443798 314.401680823 314.401916189 314.402149663 314.402381017 314.402610021 314.402836448 314.403060077 314.403280686 314.403498057 314.403711976 314.403922232 314.404128616 314.404330927 314.404528963 314.40472253 314.404911436 314.405095495 314.405274526 314.405448351 314.4056168 314.405779705 314.405936907 314.406088249 314.406233583 314.406372765 314.406505658 314.406632131 314.406752059 314.406865323 314.406971812 314.407071421 314.407164051 314.407249611 314.407328016 314.40739919 314.407463062 314.407519568 314.407568653 314.407610269 314.407644375 314.407670937 314.407689928 314.40770133 314.407705132 314.20790155 314.207897651 314.207885958 314.207866483 314.207839244 314.207804269 314.207761592 314.207711255 314.207653308 314.207587809 314.207514821 314.207434417 314.207346676 314.207251684 314.207149536 314.207040333 314.206924181 314.206801196 314.206671499 314.206535219 314.206392488 314.206243449 314.206088249 314.20592704 314.205759982 314.20558724 314.205408983 314.205225389 314.205036638 314.204842916 314.204644415 314.20444133 314.204233862 314.204022216 314.203806601 314.203587229 314.203364316 314.203138084 314.202908755 314.202676555 314.202441713 314.202204462 314.201965036 314.20172367 314.201480603 314.201236075 314.200990327 314.200743602 314.200496142 314.200248194 314.2 314.199751806 314.199503858 314.199256398 314.199009673 314.198763925 314.198519397 314.19827633 314.198034964 314.197795538 314.197558287 314.197323445 314.197091245 314.196861916 314.196635684 314.196412771 314.196193399 314.195977784 314.195766138 314.19555867 314.195355585 314.195157084 314.194963362 314.194774611 314.194591017 314.19441276 314.194240018 314.19407296 314.193911751 314.193756551 314.193607512 314.193464781 314.193328501 314.193198804 314.193075819 314.192959667 314.192850464 314.192748316 314.192653324 314.192565583 314.192485179 314.192412191 314.192346692 314.192288745 314.192238408 314.192195731 314.192160756 314.192133517 314.192114042 314.192102349 314.19209845 314.192102349 314.192114042 314.192133517 314.192160756 314.192195731 314.192238408 314.192288745 314.192346692 314.192412191 314.192485179 314.192565583 314.192653324 314.192748316 314.192850464 314.192959667 314.193075819 314.193198804 314.193328501 314.193464781 314.193607512 314.193756551 314.193911751 314.19407296 314.194240018 314.19441276 314.194591017 314.194774611 314.194963362 314.195157084 314.195355585 314.19555867 314.195766138 314.195977784 314.196193399 314.196412771 314.196635684 314.196861916 314.197091245 314.197323445 314.197558287 314.197795538 314.198034964 314.19827633 314.198519397 314.198763925 314.199009673 314.199256398 314.199503858 314.199751806 314.2 314.200248194 314.200496142 314.200743602 314.200990327 314.201236075 314.201480603 314.20172367 314.201965036 314.202204462 314.202441713 314.202676555 314.202908755 314.203138084 314.203364316 314.203587229 314.203806601 314.204022216 314.204233862 314.20444133 314.204644415 314.204842916 314.205036638 314.205225389 314.205408983 314.20558724 314.205759982 314.20592704 314.206088249 314.206243449 314.206392488 314.206535219 314.206671499 314.206801196 314.206924181 314.207040333 314.207149536 314.207251684 314.207346676 314.207434417 314.207514821 314.207587809 314.207653308 314.207711255 314.207761592 314.207804269 314.207839244 314.207866483 314.207885958 314.207897651 314.20790155 314.00809017 314.008086178 314.008074206 314.008054265 314.008026377 314.007990567 314.007946871 314.007895332 314.007836002 314.007768939 314.007694209 314.007611885 314.00752205 314.007424791 314.007320205 314.007208394 314.00708947 314.006963549 314.006830756 314.006691222 314.006545085 314.006392488 314.006233583 314.006068526 314.00589748 314.005720614 314.005538102 314.005350125 314.005156868 314.004958522 314.004755283 314.00454735 314.00433493 314.004118232 314.003897469 314.00367286 314.003444627 314.003212994 314.00297819 314.002740447 314.0025 314.002257085 314.002011943 314.001764816 314.001515947 314.001265581 314.001013967 314.000761352 314.000507986 314.000254118 314 313.999745882 313.999492014 313.999238648 313.998986033 313.998734419 313.998484053 313.998235184 313.997988057 313.997742915 313.9975 313.997259553 313.99702181 313.996787006 313.996555373 313.99632714 313.996102531 313.995881768 313.99566507 313.99545265 313.995244717 313.995041478 313.994843132 313.994649875 313.994461898 313.994279386 313.99410252 313.993931474 313.993766417 313.993607512 313.993454915 313.993308778 313.993169244 313.993036451 313.99291053 313.992791606 313.992679795 313.992575209 313.99247795 313.992388115 313.992305791 313.992231061 313.992163998 313.992104668 313.992053129 313.992009433 313.991973623 313.991945735 313.991925794 313.991913822 313.99190983 313.991913822 313.991925794 313.991945735 313.991973623 313.992009433 313.992053129 313.992104668 313.992163998 313.992231061 313.992305791 313.992388115 313.99247795 313.992575209 313.992679795 313.992791606 313.99291053 313.993036451 313.993169244 313.993308778 313.993454915 313.993607512 313.993766417 313.993931474 313.99410252 313.994279386 313.994461898 313.994649875 313.994843132 313.995041478 313.995244717 313.99545265 313.99566507 313.995881768 313.996102531 313.99632714 313.996555373 313.996787006 313.99702181 313.997259553 313.9975 313.997742915 313.997988057 313.998235184 313.998484053 313.998734419 313.998986033 313.999238648 313.999492014 313.999745882 314 314.000254118 314.000507986 314.000761352 314.001013967 314.001265581 314.001515947 314.001764816 314.002011943 314.002257085 314.0025 314.002740447 314.00297819 314.003212994 314.003444627 314.00367286 314.003897469 314.004118232 314.00433493 314.00454735 314.004755283 314.004958522 314.005156868 314.005350125 314.005538102 314.005720614 314.00589748 314.006068526 314.006233583 314.006392488 314.006545085 314.006691222 314.006830756 314.006963549 314.00708947 314.007208394 314.007320205 314.007424791 314.00752205 314.007611885 314.007694209 314.007768939 314.007836002 314.007895332 314.007946871 314.007990567 314.008026377 314.008054265 314.008074206 314.008086178 314.00809017 313.808270806 313.808266725 313.808254485 313.8082341 313.808205588 313.808168978 313.808124307 313.808071618 313.808010963 313.807942403 313.807866004 313.807781842 313.807690001 313.80759057 313.807483649 313.807369342 313.807247762 313.80711903 313.806983272 313.806840623 313.806691222 313.806535219 313.806372765 313.806204023 313.806029158 313.805848343 313.805661756 313.805469582 313.80527201 313.805069235 313.804861458 313.804648882 313.804431719 313.804210183 313.803984491 313.803754867 313.803521538 313.803284733 313.803044687 313.802801636 313.80255582 313.802307481 313.802056866 313.80180422 313.801549794 313.801293839 313.801036607 313.800778352 313.800519328 313.800259792 313.8 313.799740208 313.799480672 313.799221648 313.798963393 313.798706161 313.798450206 313.79819578 313.797943134 313.797692519 313.79744418 313.797198364 313.796955313 313.796715267 313.796478462 313.796245133 313.796015509 313.795789817 313.795568281 313.795351118 313.795138542 313.794930765 313.79472799 313.794530418 313.794338244 313.794151657 313.793970842 313.793795977 313.793627235 313.793464781 313.793308778 313.793159377 313.793016728 313.79288097 313.792752238 313.792630658 313.792516351 313.79240943 313.792309999 313.792218158 313.792133996 313.792057597 313.791989037 313.791928382 313.791875693 313.791831022 313.791794412 313.7917659 313.791745515 313.791733275 313.791729194 313.791733275 313.791745515 313.7917659 313.791794412 313.791831022 313.791875693 313.791928382 313.791989037 313.792057597 313.792133996 313.792218158 313.792309999 313.79240943 313.792516351 313.792630658 313.792752238 313.79288097 313.793016728 313.793159377 313.793308778 313.793464781 313.793627235 313.793795977 313.793970842 313.794151657 313.794338244 313.794530418 313.79472799 313.794930765 313.795138542 313.795351118 313.795568281 313.795789817 313.796015509 313.796245133 313.796478462 313.796715267 313.796955313 313.797198364 313.79744418 313.797692519 313.797943134 313.79819578 313.798450206 313.798706161 313.798963393 313.799221648 313.799480672 313.799740208 313.8 313.800259792 313.800519328 313.800778352 313.801036607 313.801293839 313.801549794 313.80180422 313.802056866 313.802307481 313.80255582 313.802801636 313.803044687 313.803284733 313.803521538 313.803754867 313.803984491 313.804210183 313.804431719 313.804648882 313.804861458 313.805069235 313.80527201 313.805469582 313.805661756 313.805848343 313.806029158 313.806204023 313.806372765 313.806535219 313.806691222 313.806840623 313.806983272 313.80711903 313.807247762 313.807369342 313.807483649 313.80759057 313.807690001 313.807781842 313.807866004 313.807942403 313.808010963 313.808071618 313.808124307 313.808168978 313.808205588 313.8082341 313.808254485 313.808266725 313.808270806 313.608443279 313.608439113 313.608426618 313.608405808 313.608376701 313.608339328 313.608293726 313.608239938 313.608178018 313.608108028 313.608030036 313.607944119 313.607850363 313.607748859 313.607639707 313.607523017 313.607398902 313.607267485 313.607128896 313.606983272 313.606830756 313.606671499 313.606505658 313.606333397 313.606154886 313.6059703 313.605779822 313.605583641 313.605381949 313.605174945 313.604962835 313.604745827 313.604524135 313.604297979 313.604067581 313.603833169 313.603594973 313.603353231 313.603108178 313.602860059 313.602609117 313.6023556 313.602099758 313.601841844 313.601582113 313.60132082 313.601058223 313.600794583 313.600530158 313.60026521 313.6 313.59973479 313.599469842 313.599205417 313.598941777 313.59867918 313.598417887 313.598158156 313.597900242 313.5976444 313.597390883 313.597139941 313.596891822 313.596646769 313.596405027 313.596166831 313.595932419 313.595702021 313.595475865 313.595254173 313.595037165 313.594825055 313.594618051 313.594416359 313.594220178 313.5940297 313.593845114 313.593666603 313.593494342 313.593328501 313.593169244 313.593016728 313.592871104 313.592732515 313.592601098 313.592476983 313.592360293 313.592251141 313.592149637 313.592055881 313.591969964 313.591891972 313.591821982 313.591760062 313.591706274 313.591660672 313.591623299 313.591594192 313.591573382 313.591560887 313.591556721 313.591560887 313.591573382 313.591594192 313.591623299 313.591660672 313.591706274 313.591760062 313.591821982 313.591891972 313.591969964 313.592055881 313.592149637 313.592251141 313.592360293 313.592476983 313.592601098 313.592732515 313.592871104 313.593016728 313.593169244 313.593328501 313.593494342 313.593666603 313.593845114 313.5940297 313.594220178 313.594416359 313.594618051 313.594825055 313.595037165 313.595254173 313.595475865 313.595702021 313.595932419 313.596166831 313.596405027 313.596646769 313.596891822 313.597139941 313.597390883 313.5976444 313.597900242 313.598158156 313.598417887 313.59867918 313.598941777 313.599205417 313.599469842 313.59973479 313.6 313.60026521 313.600530158 313.600794583 313.601058223 313.60132082 313.601582113 313.601841844 313.602099758 313.6023556 313.602609117 313.602860059 313.603108178 313.603353231 313.603594973 313.603833169 313.604067581 313.604297979 313.604524135 313.604745827 313.604962835 313.605174945 313.605381949 313.605583641 313.605779822 313.6059703 313.606154886 313.606333397 313.606505658 313.606671499 313.606830756 313.606983272 313.607128896 313.607267485 313.607398902 313.607523017 313.607639707 313.607748859 313.607850363 313.607944119 313.608030036 313.608108028 313.608178018 313.608239938 313.608293726 313.608339328 313.608376701 313.608405808 313.608426618 313.608439113 313.608443279 313.40860742 313.408603173 313.408590435 313.40856922 313.408539548 313.408501449 313.408454959 313.408400126 313.408337002 313.408265651 313.408186143 313.408098556 313.408002977 313.4078995 313.407788227 313.407669268 313.40754274 313.407408768 313.407267485 313.40711903 313.406963549 313.406801196 313.406632131 313.406456521 313.406274539 313.406086365 313.405892185 313.405692189 313.405486576 313.405275549 313.405059315 313.404838088 313.404612086 313.404381533 313.404146656 313.403907687 313.403664861 313.403418419 313.403168603 313.40291566 313.402659839 313.402401394 313.402140578 313.401877651 313.40161287 313.401346497 313.401078796 313.40081003 313.400540464 313.400270366 313.4 313.399729634 313.399459536 313.39918997 313.398921204 313.398653503 313.39838713 313.398122349 313.397859422 313.397598606 313.397340161 313.39708434 313.396831397 313.396581581 313.396335139 313.396092313 313.395853344 313.395618467 313.395387914 313.395161912 313.394940685 313.394724451 313.394513424 313.394307811 313.394107815 313.393913635 313.393725461 313.393543479 313.393367869 313.393198804 313.393036451 313.39288097 313.392732515 313.392591232 313.39245726 313.392330732 313.392211773 313.3921005 313.391997023 313.391901444 313.391813857 313.391734349 313.391662998 313.391599874 313.391545041 313.391498551 313.391460452 313.39143078 313.391409565 313.391396827 313.39139258 313.391396827 313.391409565 313.39143078 313.391460452 313.391498551 313.391545041 313.391599874 313.391662998 313.391734349 313.391813857 313.391901444 313.391997023 313.3921005 313.392211773 313.392330732 313.39245726 313.392591232 313.392732515 313.39288097 313.393036451 313.393198804 313.393367869 313.393543479 313.393725461 313.393913635 313.394107815 313.394307811 313.394513424 313.394724451 313.394940685 313.395161912 313.395387914 313.395618467 313.395853344 313.396092313 313.396335139 313.396581581 313.396831397 313.39708434 313.397340161 313.397598606 313.397859422 313.398122349 313.39838713 313.398653503 313.398921204 313.39918997 313.399459536 313.399729634 313.4 313.400270366 313.400540464 313.40081003 313.401078796 313.401346497 313.40161287 313.401877651 313.402140578 313.402401394 313.402659839 313.40291566 313.403168603 313.403418419 313.403664861 313.403907687 313.404146656 313.404381533 313.404612086 313.404838088 313.405059315 313.405275549 313.405486576 313.405692189 313.405892185 313.406086365 313.406274539 313.406456521 313.406632131 313.406801196 313.406963549 313.40711903 313.407267485 313.407408768 313.40754274 313.407669268 313.407788227 313.4078995 313.408002977 313.408098556 313.408186143 313.408265651 313.408337002 313.408400126 313.408454959 313.408501449 313.408539548 313.40856922 313.408590435 313.408603173 313.40860742 313.208763067 313.208758743 313.208745775 313.208724176 313.208693967 313.208655179 313.208607849 313.208552024 313.208487759 313.208415118 313.208334172 313.208245001 313.208147693 313.208042345 313.20792906 313.20780795 313.207679134 313.20754274 313.207398902 313.207247762 313.20708947 313.206924181 313.206752059 313.206573273 313.206388001 313.206196424 313.205998732 313.20579512 313.205585789 313.205370945 313.205150801 313.204925574 313.204695486 313.204460764 313.20422164 313.203978349 313.203731132 313.203480233 313.2032259 313.202968383 313.202707937 313.202444818 313.202179286 313.201911604 313.201642035 313.201370846 313.201098303 313.200824677 313.200550238 313.200275255 313.2 313.199724745 313.199449762 313.199175323 313.198901697 313.198629154 313.198357965 313.198088396 313.197820714 313.197555182 313.197292063 313.197031617 313.1967741 313.196519767 313.196268868 313.196021651 313.19577836 313.195539236 313.195304514 313.195074426 313.194849199 313.194629055 313.194414211 313.19420488 313.194001268 313.193803576 313.193611999 313.193426727 313.193247941 313.193075819 313.19291053 313.192752238 313.192601098 313.19245726 313.192320866 313.19219205 313.19207094 313.191957655 313.191852307 313.191754999 313.191665828 313.191584882 313.191512241 313.191447976 313.191392151 313.191344821 313.191306033 313.191275824 313.191254225 313.191241257 313.191236933 313.191241257 313.191254225 313.191275824 313.191306033 313.191344821 313.191392151 313.191447976 313.191512241 313.191584882 313.191665828 313.191754999 313.191852307 313.191957655 313.19207094 313.19219205 313.192320866 313.19245726 313.192601098 313.192752238 313.19291053 313.193075819 313.193247941 313.193426727 313.193611999 313.193803576 313.194001268 313.19420488 313.194414211 313.194629055 313.194849199 313.195074426 313.195304514 313.195539236 313.19577836 313.196021651 313.196268868 313.196519767 313.1967741 313.197031617 313.197292063 313.197555182 313.197820714 313.198088396 313.198357965 313.198629154 313.198901697 313.199175323 313.199449762 313.199724745 313.2 313.200275255 313.200550238 313.200824677 313.201098303 313.201370846 313.201642035 313.201911604 313.202179286 313.202444818 313.202707937 313.202968383 313.2032259 313.203480233 313.203731132 313.203978349 313.20422164 313.204460764 313.204695486 313.204925574 313.205150801 313.205370945 313.205585789 313.20579512 313.205998732 313.206196424 313.206388001 313.206573273 313.206752059 313.206924181 313.20708947 313.207247762 313.207398902 313.20754274 313.207679134 313.20780795 313.20792906 313.208042345 313.208147693 313.208245001 313.208334172 313.208415118 313.208487759 313.208552024 313.208607849 313.208655179 313.208693967 313.208724176 313.208745775 313.208758743 313.208763067 313.008910065 313.008905669 313.008892483 313.008870522 313.008839807 313.008800368 313.008752243 313.008695482 313.008630139 313.008556279 313.008473976 313.008383309 313.008284369 313.008177254 313.008062068 313.007938926 313.00780795 313.007669268 313.007523017 313.007369342 313.007208394 313.007040333 313.006865323 313.006683539 313.006495158 313.006300368 313.006099359 313.005892332 313.005679489 313.005461042 313.005237205 313.0050082 313.004774252 313.004535592 313.004292457 313.004045085 313.003793721 313.003538614 313.003280014 313.003018177 313.002753362 313.002485829 313.002215843 313.001943671 313.00166958 313.001393841 313.001116727 313.000838511 313.000559468 313.000279872 313 312.999720128 312.999440532 312.999161489 312.998883273 312.998606159 312.99833042 312.998056329 312.997784157 312.997514171 312.997246638 312.996981823 312.996719986 312.996461386 312.996206279 312.995954915 312.995707543 312.995464408 312.995225748 312.9949918 312.994762795 312.994538958 312.994320511 312.994107668 312.993900641 312.993699632 312.993504842 312.993316461 312.993134677 312.992959667 312.992791606 312.992630658 312.992476983 312.992330732 312.99219205 312.992061074 312.991937932 312.991822746 312.991715631 312.991616691 312.991526024 312.991443721 312.991369861 312.991304518 312.991247757 312.991199632 312.991160193 312.991129478 312.991107517 312.991094331 312.991089935 312.991094331 312.991107517 312.991129478 312.991160193 312.991199632 312.991247757 312.991304518 312.991369861 312.991443721 312.991526024 312.991616691 312.991715631 312.991822746 312.991937932 312.992061074 312.99219205 312.992330732 312.992476983 312.992630658 312.992791606 312.992959667 312.993134677 312.993316461 312.993504842 312.993699632 312.993900641 312.994107668 312.994320511 312.994538958 312.994762795 312.9949918 312.995225748 312.995464408 312.995707543 312.995954915 312.996206279 312.996461386 312.996719986 312.996981823 312.997246638 312.997514171 312.997784157 312.998056329 312.99833042 312.998606159 312.998883273 312.999161489 312.999440532 312.999720128 313 313.000279872 313.000559468 313.000838511 313.001116727 313.001393841 313.00166958 313.001943671 313.002215843 313.002485829 313.002753362 313.003018177 313.003280014 313.003538614 313.003793721 313.004045085 313.004292457 313.004535592 313.004774252 313.0050082 313.005237205 313.005461042 313.005679489 313.005892332 313.006099359 313.006300368 313.006495158 313.006683539 313.006865323 313.007040333 313.007208394 313.007369342 313.007523017 313.007669268 313.00780795 313.007938926 313.008062068 313.008177254 313.008284369 313.008383309 313.008473976 313.008556279 313.008630139 313.008695482 313.008752243 313.008800368 313.008839807 313.008870522 313.008892483 313.008905669 313.008910065 312.809048271 312.809043806 312.809030416 312.809008114 312.808976922 312.808936871 312.808888001 312.808830359 312.808764002 312.808688997 312.808605417 312.808513344 312.808412869 312.808304092 312.80818712 312.808062068 312.80792906 312.807788227 312.807639707 312.807483649 312.807320205 312.807149536 312.806971812 312.806787208 312.806595905 312.806398093 312.806193967 312.805983729 312.805767585 312.805545749 312.80531844 312.805085882 312.804848306 312.804605944 312.804359038 312.804107829 312.803852566 312.803593502 312.803330891 312.803064992 312.802796069 312.802524387 312.802250213 312.801973819 312.801695477 312.801415461 312.801134049 312.800851517 312.800568146 312.800284213 312.8 312.799715787 312.799431854 312.799148483 312.798865951 312.798584539 312.798304523 312.798026181 312.797749787 312.797475613 312.797203931 312.796935008 312.796669109 312.796406498 312.796147434 312.795892171 312.795640962 312.795394056 312.795151694 312.794914118 312.79468156 312.794454251 312.794232415 312.794016271 312.793806033 312.793601907 312.793404095 312.793212792 312.793028188 312.792850464 312.792679795 312.792516351 312.792360293 312.792211773 312.79207094 312.791937932 312.79181288 312.791695908 312.791587131 312.791486656 312.791394583 312.791311003 312.791235998 312.791169641 312.791111999 312.791063129 312.791023078 312.790991886 312.790969584 312.790956194 312.790951729 312.790956194 312.790969584 312.790991886 312.791023078 312.791063129 312.791111999 312.791169641 312.791235998 312.791311003 312.791394583 312.791486656 312.791587131 312.791695908 312.79181288 312.791937932 312.79207094 312.792211773 312.792360293 312.792516351 312.792679795 312.792850464 312.793028188 312.793212792 312.793404095 312.793601907 312.793806033 312.794016271 312.794232415 312.794454251 312.79468156 312.794914118 312.795151694 312.795394056 312.795640962 312.795892171 312.796147434 312.796406498 312.796669109 312.796935008 312.797203931 312.797475613 312.797749787 312.798026181 312.798304523 312.798584539 312.798865951 312.799148483 312.799431854 312.799715787 312.8 312.800284213 312.800568146 312.800851517 312.801134049 312.801415461 312.801695477 312.801973819 312.802250213 312.802524387 312.802796069 312.803064992 312.803330891 312.803593502 312.803852566 312.804107829 312.804359038 312.804605944 312.804848306 312.805085882 312.80531844 312.805545749 312.805767585 312.805983729 312.806193967 312.806398093 312.806595905 312.806787208 312.806971812 312.807149536 312.807320205 312.807483649 312.807639707 312.807788227 312.80792906 312.808062068 312.80818712 312.808304092 312.808412869 312.808513344 312.808605417 312.808688997 312.808764002 312.808830359 312.808888001 312.808936871 312.808976922 312.809008114 312.809030416 312.809043806 312.809048271 312.609177546 312.609173018 312.609159436 312.609136816 312.609105179 312.609064555 312.609014987 312.608956521 312.608889217 312.60881314 312.608728365 312.608634977 312.608533067 312.608422736 312.608304092 312.608177254 312.608042345 312.6078995 312.607748859 312.60759057 312.607424791 312.607251684 312.607071421 312.606884179 312.606690143 312.606489505 312.606282463 312.60606922 312.605849988 312.605624983 312.605394426 312.605158546 312.604917575 312.604671751 312.604421317 312.604166519 312.603907609 312.603644843 312.60337848 312.603108783 312.602836018 312.602560454 312.602282363 312.60200202 312.601719701 312.601435685 312.601150252 312.600863683 312.600576263 312.600288274 312.6 312.599711726 312.599423737 312.599136317 312.598849748 312.598564315 312.598280299 312.59799798 312.597717637 312.597439546 312.597163982 312.596891217 312.59662152 312.596355157 312.596092391 312.595833481 312.595578683 312.595328249 312.595082425 312.594841454 312.594605574 312.594375017 312.594150012 312.59393078 312.593717537 312.593510495 312.593309857 312.593115821 312.592928579 312.592748316 312.592575209 312.59240943 312.592251141 312.5921005 312.591957655 312.591822746 312.591695908 312.591577264 312.591466933 312.591365023 312.591271635 312.59118686 312.591110783 312.591043479 312.590985013 312.590935445 312.590894821 312.590863184 312.590840564 312.590826982 312.590822454 312.590826982 312.590840564 312.590863184 312.590894821 312.590935445 312.590985013 312.591043479 312.591110783 312.59118686 312.591271635 312.591365023 312.591466933 312.591577264 312.591695908 312.591822746 312.591957655 312.5921005 312.592251141 312.59240943 312.592575209 312.592748316 312.592928579 312.593115821 312.593309857 312.593510495 312.593717537 312.59393078 312.594150012 312.594375017 312.594605574 312.594841454 312.595082425 312.595328249 312.595578683 312.595833481 312.596092391 312.596355157 312.59662152 312.596891217 312.597163982 312.597439546 312.597717637 312.59799798 312.598280299 312.598564315 312.598849748 312.599136317 312.599423737 312.599711726 312.6 312.600288274 312.600576263 312.600863683 312.601150252 312.601435685 312.601719701 312.60200202 312.602282363 312.602560454 312.602836018 312.603108783 312.60337848 312.603644843 312.603907609 312.604166519 312.604421317 312.604671751 312.604917575 312.605158546 312.605394426 312.605624983 312.605849988 312.60606922 312.606282463 312.606489505 312.606690143 312.606884179 312.607071421 312.607251684 312.607424791 312.60759057 312.607748859 312.6078995 312.608042345 312.608177254 312.608304092 312.608422736 312.608533067 312.608634977 312.608728365 312.60881314 312.608889217 312.608956521 312.609014987 312.609064555 312.609105179 312.609136816 312.609159436 312.609173018 312.609177546 312.409297765 312.409293177 312.409279418 312.409256501 312.409224449 312.409183294 312.409133076 312.409073845 312.409005658 312.408928585 312.4088427 312.408748088 312.408644843 312.408533067 312.408412869 312.408284369 312.408147693 312.408002977 312.407850363 312.407690001 312.40752205 312.407346676 312.407164051 312.406974356 312.406777779 312.406574513 312.406364758 312.406148722 312.405926618 312.405698666 312.405465089 312.405226119 312.404981992 312.404732947 312.404479232 312.404221097 312.403958796 312.403692588 312.403422736 312.403149506 312.402873167 312.402593994 312.40231226 312.402028245 312.401742227 312.401454491 312.401165319 312.400874997 312.400583811 312.40029205 312.4 312.39970795 312.399416189 312.399125003 312.398834681 312.398545509 312.398257773 312.397971755 312.39768774 312.397406006 312.397126833 312.396850494 312.396577264 312.396307412 312.396041204 312.395778903 312.395520768 312.395267053 312.395018008 312.394773881 312.394534911 312.394301334 312.394073382 312.393851278 312.393635242 312.393425487 312.393222221 312.393025644 312.392835949 312.392653324 312.39247795 312.392309999 312.392149637 312.391997023 312.391852307 312.391715631 312.391587131 312.391466933 312.391355157 312.391251912 312.3911573 312.391071415 312.390994342 312.390926155 312.390866924 312.390816706 312.390775551 312.390743499 312.390720582 312.390706823 312.390702235 312.390706823 312.390720582 312.390743499 312.390775551 312.390816706 312.390866924 312.390926155 312.390994342 312.391071415 312.3911573 312.391251912 312.391355157 312.391466933 312.391587131 312.391715631 312.391852307 312.391997023 312.392149637 312.392309999 312.39247795 312.392653324 312.392835949 312.393025644 312.393222221 312.393425487 312.393635242 312.393851278 312.394073382 312.394301334 312.394534911 312.394773881 312.395018008 312.395267053 312.395520768 312.395778903 312.396041204 312.396307412 312.396577264 312.396850494 312.397126833 312.397406006 312.39768774 312.397971755 312.398257773 312.398545509 312.398834681 312.399125003 312.399416189 312.39970795 312.4 312.40029205 312.400583811 312.400874997 312.401165319 312.401454491 312.401742227 312.402028245 312.40231226 312.402593994 312.402873167 312.403149506 312.403422736 312.403692588 312.403958796 312.404221097 312.404479232 312.404732947 312.404981992 312.405226119 312.405465089 312.405698666 312.405926618 312.406148722 312.406364758 312.406574513 312.406777779 312.406974356 312.407164051 312.407346676 312.40752205 312.407690001 312.407850363 312.408002977 312.408147693 312.408284369 312.408412869 312.408533067 312.408644843 312.408748088 312.4088427 312.408928585 312.409005658 312.409073845 312.409133076 312.409183294 312.409224449 312.409256501 312.409279418 312.409293177 312.409297765 312.209408808 312.209404165 312.209390242 312.209367051 312.209334616 312.20929297 312.209242152 312.209182213 312.209113213 312.209035219 312.208948308 312.208852566 312.208748088 312.208634977 312.208513344 312.208383309 312.208245001 312.208098556 312.207944119 312.207781842 312.207611885 312.207434417 312.207249611 312.207057651 312.206858726 312.206653032 312.206440772 312.206222156 312.2059974 312.205766725 312.205530358 312.205288534 312.205041491 312.204789473 312.204532728 312.204271509 312.204006075 312.203736688 312.203463613 312.20318712 312.202907481 312.202624974 312.202339875 312.202052468 312.201763035 312.201471862 312.201179236 312.200885447 312.200590784 312.200295538 312.2 312.199704462 312.199409216 312.199114553 312.198820764 312.198528138 312.198236965 312.197947532 312.197660125 312.197375026 312.197092519 312.19681288 312.196536387 312.196263312 312.195993925 312.195728491 312.195467272 312.195210527 312.194958509 312.194711466 312.194469642 312.194233275 312.1940026 312.193777844 312.193559228 312.193346968 312.193141274 312.192942349 312.192750389 312.192565583 312.192388115 312.192218158 312.192055881 312.191901444 312.191754999 312.191616691 312.191486656 312.191365023 312.191251912 312.191147434 312.191051692 312.190964781 312.190886787 312.190817787 312.190757848 312.19070703 312.190665384 312.190632949 312.190609758 312.190595835 312.190591192 312.190595835 312.190609758 312.190632949 312.190665384 312.19070703 312.190757848 312.190817787 312.190886787 312.190964781 312.191051692 312.191147434 312.191251912 312.191365023 312.191486656 312.191616691 312.191754999 312.191901444 312.192055881 312.192218158 312.192388115 312.192565583 312.192750389 312.192942349 312.193141274 312.193346968 312.193559228 312.193777844 312.1940026 312.194233275 312.194469642 312.194711466 312.194958509 312.195210527 312.195467272 312.195728491 312.195993925 312.196263312 312.196536387 312.19681288 312.197092519 312.197375026 312.197660125 312.197947532 312.198236965 312.198528138 312.198820764 312.199114553 312.199409216 312.199704462 312.2 312.200295538 312.200590784 312.200885447 312.201179236 312.201471862 312.201763035 312.202052468 312.202339875 312.202624974 312.202907481 312.20318712 312.203463613 312.203736688 312.204006075 312.204271509 312.204532728 312.204789473 312.205041491 312.205288534 312.205530358 312.205766725 312.2059974 312.206222156 312.206440772 312.206653032 312.206858726 312.207057651 312.207249611 312.207434417 312.207611885 312.207781842 312.207944119 312.208098556 312.208245001 312.208383309 312.208513344 312.208634977 312.208748088 312.208852566 312.208948308 312.209035219 312.209113213 312.209182213 312.209242152 312.20929297 312.209334616 312.209367051 312.209390242 312.209404165 312.209408808 312.009510565 312.009505872 312.009491798 312.009468357 312.009435572 312.009393474 312.009342107 312.00928152 312.009211773 312.009132936 312.009045085 312.008948308 312.0088427 312.008728365 312.008605417 312.008473976 312.008334172 312.008186143 312.008030036 312.007866004 312.007694209 312.007514821 312.007328016 312.00713398 312.006932904 312.006724985 312.00651043 312.00628945 312.006062262 312.005829092 312.00559017 312.005345731 312.005096016 312.004841272 312.00458175 312.004317706 312.004049402 312.003777101 312.003501073 312.003221589 312.002938926 312.002653363 312.002365181 312.002074666 312.001782102 312.00148778 312.00119199 312.000895023 312.000597173 312.000298734 312 311.999701266 311.999402827 311.999104977 311.99880801 311.99851222 311.998217898 311.997925334 311.997634819 311.997346637 311.997061074 311.996778411 311.996498927 311.996222899 311.995950598 311.995682294 311.99541825 311.995158728 311.994903984 311.994654269 311.99440983 311.994170908 311.993937738 311.99371055 311.99348957 311.993275015 311.993067096 311.99286602 311.992671984 311.992485179 311.992305791 311.992133996 311.991969964 311.991813857 311.991665828 311.991526024 311.991394583 311.991271635 311.9911573 311.991051692 311.990954915 311.990867064 311.990788227 311.99071848 311.990657893 311.990606526 311.990564428 311.990531643 311.990508202 311.990494128 311.990489435 311.990494128 311.990508202 311.990531643 311.990564428 311.990606526 311.990657893 311.99071848 311.990788227 311.990867064 311.990954915 311.991051692 311.9911573 311.991271635 311.991394583 311.991526024 311.991665828 311.991813857 311.991969964 311.992133996 311.992305791 311.992485179 311.992671984 311.99286602 311.993067096 311.993275015 311.99348957 311.99371055 311.993937738 311.994170908 311.99440983 311.994654269 311.994903984 311.995158728 311.99541825 311.995682294 311.995950598 311.996222899 311.996498927 311.996778411 311.997061074 311.997346637 311.997634819 311.997925334 311.998217898 311.99851222 311.99880801 311.999104977 311.999402827 311.999701266 312 312.000298734 312.000597173 312.000895023 312.00119199 312.00148778 312.001782102 312.002074666 312.002365181 312.002653363 312.002938926 312.003221589 312.003501073 312.003777101 312.004049402 312.004317706 312.00458175 312.004841272 312.005096016 312.005345731 312.00559017 312.005829092 312.006062262 312.00628945 312.00651043 312.006724985 312.006932904 312.00713398 312.007328016 312.007514821 312.007694209 312.007866004 312.008030036 312.008186143 312.008334172 312.008473976 312.008605417 312.008728365 312.0088427 312.008948308 312.009045085 312.009132936 312.009211773 312.00928152 312.009342107 312.009393474 312.009435572 312.009468357 312.009491798 312.009505872 312.009510565 311.809602937 311.809598198 311.809583988 311.809560319 311.809527215 311.809484709 311.809432842 311.809371667 311.809301243 311.80922164 311.809132936 311.809035219 311.808928585 311.80881314 311.808688997 311.808556279 311.808415118 311.808265651 311.808108028 311.807942403 311.807768939 311.807587809 311.80739919 311.807203269 311.80700024 311.806790302 311.806573663 311.806350536 311.806121142 311.805885708 311.805644465 311.805397651 311.805145511 311.804888293 311.80462625 311.804359642 311.804088732 311.803813786 311.803535077 311.803252879 311.802967471 311.802679134 311.802388153 311.802094816 311.801799411 311.80150223 311.801203567 311.800903716 311.800602973 311.800301636 311.8 311.799698364 311.799397027 311.799096284 311.798796433 311.79849777 311.798200589 311.797905184 311.797611847 311.797320866 311.797032529 311.796747121 311.796464923 311.796186214 311.795911268 311.795640358 311.79537375 311.795111707 311.794854489 311.794602349 311.794355535 311.794114292 311.793878858 311.793649464 311.793426337 311.793209698 311.79299976 311.792796731 311.79260081 311.792412191 311.792231061 311.792057597 311.791891972 311.791734349 311.791584882 311.791443721 311.791311003 311.79118686 311.791071415 311.790964781 311.790867064 311.79077836 311.790698757 311.790628333 311.790567158 311.790515291 311.790472785 311.790439681 311.790416012 311.790401802 311.790397063 311.790401802 311.790416012 311.790439681 311.790472785 311.790515291 311.790567158 311.790628333 311.790698757 311.79077836 311.790867064 311.790964781 311.791071415 311.79118686 311.791311003 311.791443721 311.791584882 311.791734349 311.791891972 311.792057597 311.792231061 311.792412191 311.79260081 311.792796731 311.79299976 311.793209698 311.793426337 311.793649464 311.793878858 311.794114292 311.794355535 311.794602349 311.794854489 311.795111707 311.79537375 311.795640358 311.795911268 311.796186214 311.796464923 311.796747121 311.797032529 311.797320866 311.797611847 311.797905184 311.798200589 311.79849777 311.798796433 311.799096284 311.799397027 311.799698364 311.8 311.800301636 311.800602973 311.800903716 311.801203567 311.80150223 311.801799411 311.802094816 311.802388153 311.802679134 311.802967471 311.803252879 311.803535077 311.803813786 311.804088732 311.804359642 311.80462625 311.804888293 311.805145511 311.805397651 311.805644465 311.805885708 311.806121142 311.806350536 311.806573663 311.806790302 311.80700024 311.807203269 311.80739919 311.807587809 311.807768939 311.807942403 311.808108028 311.808265651 311.808415118 311.808556279 311.808688997 311.80881314 311.808928585 311.809035219 311.809132936 311.80922164 311.809301243 311.809371667 311.809432842 311.809484709 311.809527215 311.809560319 311.809583988 311.809598198 311.809602937 311.609685832 311.609681052 311.609666719 311.609642846 311.609609456 311.609566583 311.609514269 311.609452565 311.609381533 311.609301243 311.609211773 311.609113213 311.609005658 311.608889217 311.608764002 311.608630139 311.608487759 311.608337002 311.608178018 311.608010963 311.607836002 311.607653308 311.607463062 311.60726545 311.607060667 311.606848917 311.606630408 311.606405355 311.606173981 311.605936515 311.605693189 311.605444245 311.605189928 311.604930489 311.604666185 311.604397276 311.604124027 311.603846708 311.603565592 311.603280958 311.602993087 311.602702261 311.602408768 311.602112899 311.601814944 311.601515198 311.601213957 311.600911517 311.600608178 311.600304239 311.6 311.599695761 311.599391822 311.599088483 311.598786043 311.598484802 311.598185056 311.597887101 311.597591232 311.597297739 311.597006913 311.596719042 311.596434408 311.596153292 311.595875973 311.595602724 311.595333815 311.595069511 311.594810072 311.594555755 311.594306811 311.594063485 311.593826019 311.593594645 311.593369592 311.593151083 311.592939333 311.59273455 311.592536938 311.592346692 311.592163998 311.591989037 311.591821982 311.591662998 311.591512241 311.591369861 311.591235998 311.591110783 311.590994342 311.590886787 311.590788227 311.590698757 311.590618467 311.590547435 311.590485731 311.590433417 311.590390544 311.590357154 311.590333281 311.590318948 311.590314168 311.590318948 311.590333281 311.590357154 311.590390544 311.590433417 311.590485731 311.590547435 311.590618467 311.590698757 311.590788227 311.590886787 311.590994342 311.591110783 311.591235998 311.591369861 311.591512241 311.591662998 311.591821982 311.591989037 311.592163998 311.592346692 311.592536938 311.59273455 311.592939333 311.593151083 311.593369592 311.593594645 311.593826019 311.594063485 311.594306811 311.594555755 311.594810072 311.595069511 311.595333815 311.595602724 311.595875973 311.596153292 311.596434408 311.596719042 311.597006913 311.597297739 311.597591232 311.597887101 311.598185056 311.598484802 311.598786043 311.599088483 311.599391822 311.599695761 311.6 311.600304239 311.600608178 311.600911517 311.601213957 311.601515198 311.601814944 311.602112899 311.602408768 311.602702261 311.602993087 311.603280958 311.603565592 311.603846708 311.604124027 311.604397276 311.604666185 311.604930489 311.605189928 311.605444245 311.605693189 311.605936515 311.606173981 311.606405355 311.606630408 311.606848917 311.607060667 311.60726545 311.607463062 311.607653308 311.607836002 311.608010963 311.608178018 311.608337002 311.608487759 311.608630139 311.608764002 311.608889217 311.609005658 311.609113213 311.609211773 311.609301243 311.609381533 311.609452565 311.609514269 311.609566583 311.609609456 311.609642846 311.609666719 311.609681052 311.609685832 311.409759168 311.409754352 311.40973991 311.409715856 311.409682214 311.409639016 311.409586306 311.409524135 311.409452565 311.409371667 311.40928152 311.409182213 311.409073845 311.408956521 311.408830359 311.408695482 311.408552024 311.408400126 311.408239938 311.408071618 311.407895332 311.407711255 311.407519568 311.40732046 311.407114127 311.406900774 311.40668061 311.406453853 311.406220728 311.405981463 311.405736295 311.405485466 311.405229224 311.404967821 311.404701515 311.404430569 311.404155251 311.403875833 311.403592589 311.4033058 311.403015749 311.402722721 311.402427006 311.402128896 311.401828686 311.40152667 311.401223148 311.400918419 311.400612783 311.400306543 311.4 311.399693457 311.399387217 311.399081581 311.398776852 311.39847333 311.398171314 311.397871104 311.397572994 311.397277279 311.396984251 311.3966942 311.396407411 311.396124167 311.395844749 311.395569431 311.395298485 311.395032179 311.394770776 311.394514534 311.394263705 311.394018537 311.393779272 311.393546147 311.39331939 311.393099226 311.392885873 311.39267954 311.392480432 311.392288745 311.392104668 311.391928382 311.391760062 311.391599874 311.391447976 311.391304518 311.391169641 311.391043479 311.390926155 311.390817787 311.39071848 311.390628333 311.390547435 311.390475865 311.390413694 311.390360984 311.390317786 311.390284144 311.39026009 311.390245648 311.390240832 311.390245648 311.39026009 311.390284144 311.390317786 311.390360984 311.390413694 311.390475865 311.390547435 311.390628333 311.39071848 311.390817787 311.390926155 311.391043479 311.391169641 311.391304518 311.391447976 311.391599874 311.391760062 311.391928382 311.392104668 311.392288745 311.392480432 311.39267954 311.392885873 311.393099226 311.39331939 311.393546147 311.393779272 311.394018537 311.394263705 311.394514534 311.394770776 311.395032179 311.395298485 311.395569431 311.395844749 311.396124167 311.396407411 311.3966942 311.396984251 311.397277279 311.397572994 311.397871104 311.398171314 311.39847333 311.398776852 311.399081581 311.399387217 311.399693457 311.4 311.400306543 311.400612783 311.400918419 311.401223148 311.40152667 311.401828686 311.402128896 311.402427006 311.402722721 311.403015749 311.4033058 311.403592589 311.403875833 311.404155251 311.404430569 311.404701515 311.404967821 311.405229224 311.405485466 311.405736295 311.405981463 311.406220728 311.406453853 311.40668061 311.406900774 311.407114127 311.40732046 311.407519568 311.407711255 311.407895332 311.408071618 311.408239938 311.408400126 311.408552024 311.408695482 311.408830359 311.408956521 311.409073845 311.409182213 311.40928152 311.409371667 311.409452565 311.409524135 311.409586306 311.409639016 311.409682214 311.409715856 311.40973991 311.409754352 311.409759168 311.209822873 311.209818026 311.209803489 311.209779278 311.209745416 311.209701937 311.209648882 311.209586306 311.209514269 311.209432842 311.209342107 311.209242152 311.209133076 311.209014987 311.208888001 311.208752243 311.208607849 311.208454959 311.208293726 311.208124307 311.207946871 311.207761592 311.207568653 311.207368245 311.207160566 311.20694582 311.206724219 311.206495982 311.206261335 311.206020508 311.20577374 311.205521273 311.205263358 311.205000249 311.204732205 311.204459491 311.204182376 311.203901133 311.203616041 311.203327379 311.203035435 311.202740494 311.202442849 311.202142793 311.201840623 311.201536636 311.201231132 311.200924414 311.200616783 311.200308544 311.2 311.199691456 311.199383217 311.199075586 311.198768868 311.198463364 311.198159377 311.197857207 311.197557151 311.197259506 311.196964565 311.196672621 311.196383959 311.196098867 311.195817624 311.195540509 311.195267795 311.194999751 311.194736642 311.194478727 311.19422626 311.193979492 311.193738665 311.193504018 311.193275781 311.19305418 311.192839434 311.192631755 311.192431347 311.192238408 311.192053129 311.191875693 311.191706274 311.191545041 311.191392151 311.191247757 311.191111999 311.190985013 311.190866924 311.190757848 311.190657893 311.190567158 311.190485731 311.190413694 311.190351118 311.190298063 311.190254584 311.190220722 311.190196511 311.190181974 311.190177127 311.190181974 311.190196511 311.190220722 311.190254584 311.190298063 311.190351118 311.190413694 311.190485731 311.190567158 311.190657893 311.190757848 311.190866924 311.190985013 311.191111999 311.191247757 311.191392151 311.191545041 311.191706274 311.191875693 311.192053129 311.192238408 311.192431347 311.192631755 311.192839434 311.19305418 311.193275781 311.193504018 311.193738665 311.193979492 311.19422626 311.194478727 311.194736642 311.194999751 311.195267795 311.195540509 311.195817624 311.196098867 311.196383959 311.196672621 311.196964565 311.197259506 311.197557151 311.197857207 311.198159377 311.198463364 311.198768868 311.199075586 311.199383217 311.199691456 311.2 311.200308544 311.200616783 311.200924414 311.201231132 311.201536636 311.201840623 311.202142793 311.202442849 311.202740494 311.203035435 311.203327379 311.203616041 311.203901133 311.204182376 311.204459491 311.204732205 311.205000249 311.205263358 311.205521273 311.20577374 311.206020508 311.206261335 311.206495982 311.206724219 311.20694582 311.207160566 311.207368245 311.207568653 311.207761592 311.207946871 311.208124307 311.208293726 311.208454959 311.208607849 311.208752243 311.208888001 311.209014987 311.209133076 311.209242152 311.209342107 311.209432842 311.209514269 311.209586306 311.209648882 311.209701937 311.209745416 311.209779278 311.209803489 311.209818026 311.209822873 311.009876883 311.00987201 311.009857394 311.009833049 311.009799001 311.009755283 311.009701937 311.009639016 311.009566583 311.009484709 311.009393474 311.00929297 311.009183294 311.009064555 311.008936871 311.008800368 311.008655179 311.008501449 311.008339328 311.008168978 311.007990567 311.007804269 311.007610269 311.00740876 311.007199938 311.006984011 311.006761192 311.0065317 311.006295762 311.006053612 311.005805486 311.005551632 311.005292299 311.005027743 311.004758225 311.004484011 311.004205372 311.003922583 311.003635923 311.003345675 311.003052125 311.002755563 311.002456281 311.002154575 311.001850743 311.001545085 311.001237902 311.000929497 311.000620175 311.00031024 311 310.99968976 310.999379825 310.999070503 310.998762098 310.998454915 310.998149257 310.997845425 310.997543719 310.997244437 310.996947875 310.996654325 310.996364077 310.996077417 310.995794628 310.995515989 310.995241775 310.994972257 310.994707701 310.994448368 310.994194514 310.993946388 310.993704238 310.9934683 310.993238808 310.993015989 310.992800062 310.99259124 310.992389731 310.992195731 310.992009433 310.991831022 310.991660672 310.991498551 310.991344821 310.991199632 310.991063129 310.990935445 310.990816706 310.99070703 310.990606526 310.990515291 310.990433417 310.990360984 310.990298063 310.990244717 310.990200999 310.990166951 310.990142606 310.99012799 310.990123117 310.99012799 310.990142606 310.990166951 310.990200999 310.990244717 310.990298063 310.990360984 310.990433417 310.990515291 310.990606526 310.99070703 310.990816706 310.990935445 310.991063129 310.991199632 310.991344821 310.991498551 310.991660672 310.991831022 310.992009433 310.992195731 310.992389731 310.99259124 310.992800062 310.993015989 310.993238808 310.9934683 310.993704238 310.993946388 310.994194514 310.994448368 310.994707701 310.994972257 310.995241775 310.995515989 310.995794628 310.996077417 310.996364077 310.996654325 310.996947875 310.997244437 310.997543719 310.997845425 310.998149257 310.998454915 310.998762098 310.999070503 310.999379825 310.99968976 311 311.00031024 311.000620175 311.000929497 311.001237902 311.001545085 311.001850743 311.002154575 311.002456281 311.002755563 311.003052125 311.003345675 311.003635923 311.003922583 311.004205372 311.004484011 311.004758225 311.005027743 311.005292299 311.005551632 311.005805486 311.006053612 311.006295762 311.0065317 311.006761192 311.006984011 311.007199938 311.00740876 311.007610269 311.007804269 311.007990567 311.008168978 311.008339328 311.008501449 311.008655179 311.008800368 311.008936871 311.009064555 311.009183294 311.00929297 311.009393474 311.009484709 311.009566583 311.009639016 311.009701937 311.009755283 311.009799001 311.009833049 311.009857394 311.00987201 311.009876883 310.809921147 310.809916252 310.80990157 310.809877117 310.809842916 310.809799001 310.809745416 310.809682214 310.809609456 310.809527215 310.809435572 310.809334616 310.809224449 310.809105179 310.808976922 310.808839807 310.808693967 310.808539548 310.808376701 310.808205588 310.808026377 310.807839244 310.807644375 310.807441962 310.807232205 310.80701531 310.806791492 310.806560972 310.806323977 310.806080741 310.805831504 310.805576512 310.805316016 310.805050275 310.804779549 310.804504106 310.804224219 310.803940163 310.803652218 310.803360669 310.803065803 310.802767912 310.802467289 310.802164231 310.801859038 310.801552009 310.801243449 310.800933662 310.800622954 310.800311631 310.8 310.799688369 310.799377046 310.799066338 310.798756551 310.798447991 310.798140962 310.797835769 310.797532711 310.797232088 310.796934197 310.796639331 310.796347782 310.796059837 310.795775781 310.795495894 310.795220451 310.794949725 310.794683984 310.794423488 310.794168496 310.793919259 310.793676023 310.793439028 310.793208508 310.79298469 310.792767795 310.792558038 310.792355625 310.792160756 310.791973623 310.791794412 310.791623299 310.791460452 310.791306033 310.791160193 310.791023078 310.790894821 310.790775551 310.790665384 310.790564428 310.790472785 310.790390544 310.790317786 310.790254584 310.790200999 310.790157084 310.790122883 310.79009843 310.790083748 310.790078853 310.790083748 310.79009843 310.790122883 310.790157084 310.790200999 310.790254584 310.790317786 310.790390544 310.790472785 310.790564428 310.790665384 310.790775551 310.790894821 310.791023078 310.791160193 310.791306033 310.791460452 310.791623299 310.791794412 310.791973623 310.792160756 310.792355625 310.792558038 310.792767795 310.79298469 310.793208508 310.793439028 310.793676023 310.793919259 310.794168496 310.794423488 310.794683984 310.794949725 310.795220451 310.795495894 310.795775781 310.796059837 310.796347782 310.796639331 310.796934197 310.797232088 310.797532711 310.797835769 310.798140962 310.798447991 310.798756551 310.799066338 310.799377046 310.799688369 310.8 310.800311631 310.800622954 310.800933662 310.801243449 310.801552009 310.801859038 310.802164231 310.802467289 310.802767912 310.803065803 310.803360669 310.803652218 310.803940163 310.804224219 310.804504106 310.804779549 310.805050275 310.805316016 310.805576512 310.805831504 310.806080741 310.806323977 310.806560972 310.806791492 310.80701531 310.807232205 310.807441962 310.807644375 310.807839244 310.808026377 310.808205588 310.808376701 310.808539548 310.808693967 310.808839807 310.808976922 310.809105179 310.809224449 310.809334616 310.809435572 310.809527215 310.809609456 310.809682214 310.809745416 310.809799001 310.809842916 310.809877117 310.80990157 310.809916252 310.809921147 310.60995562 310.609950707 310.609935975 310.609911436 310.609877117 310.609833049 310.609779278 310.609715856 310.609642846 310.609560319 310.609468357 310.609367051 310.609256501 310.609136816 310.609008114 310.608870522 310.608724176 310.60856922 310.608405808 310.6082341 310.608054265 310.607866483 310.607670937 310.607467821 310.607257334 310.607039686 310.606815091 310.606583769 310.606345951 310.60610187 310.605851766 310.605595888 310.605334488 310.605067823 310.604796156 310.604519757 310.604238897 310.603953853 310.603664908 310.603372346 310.603076456 310.602777529 310.602475862 310.602171751 310.601865497 310.601557402 310.60124777 310.600936907 310.600625119 310.600312714 310.6 310.599687286 310.599374881 310.599063093 310.59875223 310.598442598 310.598134503 310.597828249 310.597524138 310.597222471 310.596923544 310.596627654 310.596335092 310.596046147 310.595761103 310.595480243 310.595203844 310.594932177 310.594665512 310.594404112 310.594148234 310.59389813 310.593654049 310.593416231 310.593184909 310.592960314 310.592742666 310.592532179 310.592329063 310.592133517 310.591945735 310.5917659 310.591594192 310.59143078 310.591275824 310.591129478 310.590991886 310.590863184 310.590743499 310.590632949 310.590531643 310.590439681 310.590357154 310.590284144 310.590220722 310.590166951 310.590122883 310.590088564 310.590064025 310.590049293 310.59004438 310.590049293 310.590064025 310.590088564 310.590122883 310.590166951 310.590220722 310.590284144 310.590357154 310.590439681 310.590531643 310.590632949 310.590743499 310.590863184 310.590991886 310.591129478 310.591275824 310.59143078 310.591594192 310.5917659 310.591945735 310.592133517 310.592329063 310.592532179 310.592742666 310.592960314 310.593184909 310.593416231 310.593654049 310.59389813 310.594148234 310.594404112 310.594665512 310.594932177 310.595203844 310.595480243 310.595761103 310.596046147 310.596335092 310.596627654 310.596923544 310.597222471 310.597524138 310.597828249 310.598134503 310.598442598 310.59875223 310.599063093 310.599374881 310.599687286 310.6 310.600312714 310.600625119 310.600936907 310.60124777 310.601557402 310.601865497 310.602171751 310.602475862 310.602777529 310.603076456 310.603372346 310.603664908 310.603953853 310.604238897 310.604519757 310.604796156 310.605067823 310.605334488 310.605595888 310.605851766 310.60610187 310.606345951 310.606583769 310.606815091 310.607039686 310.607257334 310.607467821 310.607670937 310.607866483 310.608054265 310.6082341 310.608405808 310.60856922 310.608724176 310.608870522 310.609008114 310.609136816 310.609256501 310.609367051 310.609468357 310.609560319 310.609642846 310.609715856 310.609779278 310.609833049 310.609877117 310.609911436 310.609935975 310.609950707 310.60995562 310.409980267 310.409975343 310.409960574 310.409935975 310.40990157 310.409857394 310.409803489 310.40973991 310.409666719 310.409583988 310.409491798 310.409390242 310.409279418 310.409159436 310.409030416 310.408892483 310.408745775 310.408590435 310.408426618 310.408254485 310.408074206 310.407885958 310.407689928 310.407486309 310.407275302 310.407057115 310.406831963 310.406600069 310.406361662 310.406116976 310.405866254 310.405609742 310.405347695 310.405080369 310.40480803 310.404530947 310.404249391 310.403963642 310.403673981 310.403380695 310.403084072 310.402784406 310.402481992 310.402177128 310.401870116 310.401561258 310.401250859 310.400939226 310.400626666 310.400313488 310.4 310.399686512 310.399373334 310.399060774 310.398749141 310.398438742 310.398129884 310.397822872 310.397518008 310.397215594 310.396915928 310.396619305 310.396326019 310.396036358 310.395750609 310.395469053 310.39519197 310.394919631 310.394652305 310.394390258 310.394133746 310.393883024 310.393638338 310.393399931 310.393168037 310.392942885 310.392724698 310.392513691 310.392310072 310.392114042 310.391925794 310.391745515 310.391573382 310.391409565 310.391254225 310.391107517 310.390969584 310.390840564 310.390720582 310.390609758 310.390508202 310.390416012 310.390333281 310.39026009 310.390196511 310.390142606 310.39009843 310.390064025 310.390039426 310.390024657 310.390019733 310.390024657 310.390039426 310.390064025 310.39009843 310.390142606 310.390196511 310.39026009 310.390333281 310.390416012 310.390508202 310.390609758 310.390720582 310.390840564 310.390969584 310.391107517 310.391254225 310.391409565 310.391573382 310.391745515 310.391925794 310.392114042 310.392310072 310.392513691 310.392724698 310.392942885 310.393168037 310.393399931 310.393638338 310.393883024 310.394133746 310.394390258 310.394652305 310.394919631 310.39519197 310.395469053 310.395750609 310.396036358 310.396326019 310.396619305 310.396915928 310.397215594 310.397518008 310.397822872 310.398129884 310.398438742 310.398749141 310.399060774 310.399373334 310.399686512 310.4 310.400313488 310.400626666 310.400939226 310.401250859 310.401561258 310.401870116 310.402177128 310.402481992 310.402784406 310.403084072 310.403380695 310.403673981 310.403963642 310.404249391 310.404530947 310.40480803 310.405080369 310.405347695 310.405609742 310.405866254 310.406116976 310.406361662 310.406600069 310.406831963 310.407057115 310.407275302 310.407486309 310.407689928 310.407885958 310.408074206 310.408254485 310.408426618 310.408590435 310.408745775 310.408892483 310.409030416 310.409159436 310.409279418 310.409390242 310.409491798 310.409583988 310.409666719 310.40973991 310.409803489 310.409857394 310.40990157 310.409935975 310.409960574 310.409975343 310.409980267 310.209995066 310.209990134 310.209975343 310.209950707 310.209916252 310.20987201 310.209818026 310.209754352 310.209681052 310.209598198 310.209505872 310.209404165 310.209293177 310.209173018 310.209043806 310.208905669 310.208758743 310.208603173 310.208439113 310.208266725 310.208086178 310.207897651 310.20770133 310.207497409 310.207286089 310.207067579 310.206842093 310.206609855 310.206371095 310.206126046 310.205874952 310.20561806 310.205355624 310.205087902 310.20481516 310.204537665 310.204255692 310.203969519 310.203679429 310.203385708 310.203088645 310.202788534 310.202485672 310.202180356 310.201872889 310.201563573 310.201252714 310.200940619 310.200627595 310.200313953 310.2 310.199686047 310.199372405 310.199059381 310.198747286 310.198436427 310.198127111 310.197819644 310.197514328 310.197211466 310.196911355 310.196614292 310.196320571 310.196030481 310.195744308 310.195462335 310.19518484 310.194912098 310.194644376 310.19438194 310.194125048 310.193873954 310.193628905 310.193390145 310.193157907 310.192932421 310.192713911 310.192502591 310.19229867 310.192102349 310.191913822 310.191733275 310.191560887 310.191396827 310.191241257 310.191094331 310.190956194 310.190826982 310.190706823 310.190595835 310.190494128 310.190401802 310.190318948 310.190245648 310.190181974 310.19012799 310.190083748 310.190049293 310.190024657 310.190009866 310.190004934 310.190009866 310.190024657 310.190049293 310.190083748 310.19012799 310.190181974 310.190245648 310.190318948 310.190401802 310.190494128 310.190595835 310.190706823 310.190826982 310.190956194 310.191094331 310.191241257 310.191396827 310.191560887 310.191733275 310.191913822 310.192102349 310.19229867 310.192502591 310.192713911 310.192932421 310.193157907 310.193390145 310.193628905 310.193873954 310.194125048 310.19438194 310.194644376 310.194912098 310.19518484 310.195462335 310.195744308 310.196030481 310.196320571 310.196614292 310.196911355 310.197211466 310.197514328 310.197819644 310.198127111 310.198436427 310.198747286 310.199059381 310.199372405 310.199686047 310.2 310.200313953 310.200627595 310.200940619 310.201252714 310.201563573 310.201872889 310.202180356 310.202485672 310.202788534 310.203088645 310.203385708 310.203679429 310.203969519 310.204255692 310.204537665 310.20481516 310.205087902 310.205355624 310.20561806 310.205874952 310.206126046 310.206371095 310.206609855 310.206842093 310.207067579 310.207286089 310.207497409 310.20770133 310.207897651 310.208086178 310.208266725 310.208439113 310.208603173 310.208758743 310.208905669 310.209043806 310.209173018 310.209293177 310.209404165 310.209505872 310.209598198 310.209681052 310.209754352 310.209818026 310.20987201 310.209916252 310.209950707 310.209975343 310.209990134 310.209995066 310.01 310.009995066 310.009980267 310.00995562 310.009921147 310.009876883 310.009822873 310.009759168 310.009685832 310.009602937 310.009510565 310.009408808 310.009297765 310.009177546 310.009048271 310.008910065 310.008763067 310.00860742 310.008443279 310.008270806 310.00809017 310.00790155 310.007705132 310.007501111 310.007289686 310.007071068 310.006845471 310.006613119 310.00637424 310.006129071 310.005877853 310.005620834 310.005358268 310.005090414 310.004817537 310.004539905 310.004257793 310.003971479 310.003681246 310.003387379 310.00309017 310.002789911 310.002486899 310.002181432 310.001873813 310.001564345 310.001253332 310.000941083 310.000627905 310.000314108 310 309.999685892 309.999372095 309.999058917 309.998746668 309.998435655 309.998126187 309.997818568 309.997513101 309.997210089 309.99690983 309.996612621 309.996318754 309.996028521 309.995742207 309.995460095 309.995182463 309.994909586 309.994641732 309.994379166 309.994122147 309.993870929 309.99362576 309.993386881 309.993154529 309.992928932 309.992710314 309.992498889 309.992294868 309.99209845 309.99190983 309.991729194 309.991556721 309.99139258 309.991236933 309.991089935 309.990951729 309.990822454 309.990702235 309.990591192 309.990489435 309.990397063 309.990314168 309.990240832 309.990177127 309.990123117 309.990078853 309.99004438 309.990019733 309.990004934 309.99 309.990004934 309.990019733 309.99004438 309.990078853 309.990123117 309.990177127 309.990240832 309.990314168 309.990397063 309.990489435 309.990591192 309.990702235 309.990822454 309.990951729 309.991089935 309.991236933 309.99139258 309.991556721 309.991729194 309.99190983 309.99209845 309.992294868 309.992498889 309.992710314 309.992928932 309.993154529 309.993386881 309.99362576 309.993870929 309.994122147 309.994379166 309.994641732 309.994909586 309.995182463 309.995460095 309.995742207 309.996028521 309.996318754 309.996612621 309.99690983 309.997210089 309.997513101 309.997818568 309.998126187 309.998435655 309.998746668 309.999058917 309.999372095 309.999685892 310 310.000314108 310.000627905 310.000941083 310.001253332 310.001564345 310.001873813 310.002181432 310.002486899 310.002789911 310.00309017 310.003387379 310.003681246 310.003971479 310.004257793 310.004539905 310.004817537 310.005090414 310.005358268 310.005620834 310.005877853 310.006129071 310.00637424 310.006613119 310.006845471 310.007071068 310.007289686 310.007501111 310.007705132 310.00790155 310.00809017 310.008270806 310.008443279 310.00860742 310.008763067 310.008910065 310.009048271 310.009177546 310.009297765 310.009408808 310.009510565 310.009602937 310.009685832 310.009759168 310.009822873 310.009876883 310.009921147 310.00995562 310.009980267 310.009995066 310.01 309.809995066 309.809990134 309.809975343 309.809950707 309.809916252 309.80987201 309.809818026 309.809754352 309.809681052 309.809598198 309.809505872 309.809404165 309.809293177 309.809173018 309.809043806 309.808905669 309.808758743 309.808603173 309.808439113 309.808266725 309.808086178 309.807897651 309.80770133 309.807497409 309.807286089 309.807067579 309.806842093 309.806609855 309.806371095 309.806126046 309.805874952 309.80561806 309.805355624 309.805087902 309.80481516 309.804537665 309.804255692 309.803969519 309.803679429 309.803385708 309.803088645 309.802788534 309.802485672 309.802180356 309.801872889 309.801563573 309.801252714 309.800940619 309.800627595 309.800313953 309.8 309.799686047 309.799372405 309.799059381 309.798747286 309.798436427 309.798127111 309.797819644 309.797514328 309.797211466 309.796911355 309.796614292 309.796320571 309.796030481 309.795744308 309.795462335 309.79518484 309.794912098 309.794644376 309.79438194 309.794125048 309.793873954 309.793628905 309.793390145 309.793157907 309.792932421 309.792713911 309.792502591 309.79229867 309.792102349 309.791913822 309.791733275 309.791560887 309.791396827 309.791241257 309.791094331 309.790956194 309.790826982 309.790706823 309.790595835 309.790494128 309.790401802 309.790318948 309.790245648 309.790181974 309.79012799 309.790083748 309.790049293 309.790024657 309.790009866 309.790004934 309.790009866 309.790024657 309.790049293 309.790083748 309.79012799 309.790181974 309.790245648 309.790318948 309.790401802 309.790494128 309.790595835 309.790706823 309.790826982 309.790956194 309.791094331 309.791241257 309.791396827 309.791560887 309.791733275 309.791913822 309.792102349 309.79229867 309.792502591 309.792713911 309.792932421 309.793157907 309.793390145 309.793628905 309.793873954 309.794125048 309.79438194 309.794644376 309.794912098 309.79518484 309.795462335 309.795744308 309.796030481 309.796320571 309.796614292 309.796911355 309.797211466 309.797514328 309.797819644 309.798127111 309.798436427 309.798747286 309.799059381 309.799372405 309.799686047 309.8 309.800313953 309.800627595 309.800940619 309.801252714 309.801563573 309.801872889 309.802180356 309.802485672 309.802788534 309.803088645 309.803385708 309.803679429 309.803969519 309.804255692 309.804537665 309.80481516 309.805087902 309.805355624 309.80561806 309.805874952 309.806126046 309.806371095 309.806609855 309.806842093 309.807067579 309.807286089 309.807497409 309.80770133 309.807897651 309.808086178 309.808266725 309.808439113 309.808603173 309.808758743 309.808905669 309.809043806 309.809173018 309.809293177 309.809404165 309.809505872 309.809598198 309.809681052 309.809754352 309.809818026 309.80987201 309.809916252 309.809950707 309.809975343 309.809990134 309.809995066 309.609980267 309.609975343 309.609960574 309.609935975 309.60990157 309.609857394 309.609803489 309.60973991 309.609666719 309.609583988 309.609491798 309.609390242 309.609279418 309.609159436 309.609030416 309.608892483 309.608745775 309.608590435 309.608426618 309.608254485 309.608074206 309.607885958 309.607689928 309.607486309 309.607275302 309.607057115 309.606831963 309.606600069 309.606361662 309.606116976 309.605866254 309.605609742 309.605347695 309.605080369 309.60480803 309.604530947 309.604249391 309.603963642 309.603673981 309.603380695 309.603084072 309.602784406 309.602481992 309.602177128 309.601870116 309.601561258 309.601250859 309.600939226 309.600626666 309.600313488 309.6 309.599686512 309.599373334 309.599060774 309.598749141 309.598438742 309.598129884 309.597822872 309.597518008 309.597215594 309.596915928 309.596619305 309.596326019 309.596036358 309.595750609 309.595469053 309.59519197 309.594919631 309.594652305 309.594390258 309.594133746 309.593883024 309.593638338 309.593399931 309.593168037 309.592942885 309.592724698 309.592513691 309.592310072 309.592114042 309.591925794 309.591745515 309.591573382 309.591409565 309.591254225 309.591107517 309.590969584 309.590840564 309.590720582 309.590609758 309.590508202 309.590416012 309.590333281 309.59026009 309.590196511 309.590142606 309.59009843 309.590064025 309.590039426 309.590024657 309.590019733 309.590024657 309.590039426 309.590064025 309.59009843 309.590142606 309.590196511 309.59026009 309.590333281 309.590416012 309.590508202 309.590609758 309.590720582 309.590840564 309.590969584 309.591107517 309.591254225 309.591409565 309.591573382 309.591745515 309.591925794 309.592114042 309.592310072 309.592513691 309.592724698 309.592942885 309.593168037 309.593399931 309.593638338 309.593883024 309.594133746 309.594390258 309.594652305 309.594919631 309.59519197 309.595469053 309.595750609 309.596036358 309.596326019 309.596619305 309.596915928 309.597215594 309.597518008 309.597822872 309.598129884 309.598438742 309.598749141 309.599060774 309.599373334 309.599686512 309.6 309.600313488 309.600626666 309.600939226 309.601250859 309.601561258 309.601870116 309.602177128 309.602481992 309.602784406 309.603084072 309.603380695 309.603673981 309.603963642 309.604249391 309.604530947 309.60480803 309.605080369 309.605347695 309.605609742 309.605866254 309.606116976 309.606361662 309.606600069 309.606831963 309.607057115 309.607275302 309.607486309 309.607689928 309.607885958 309.608074206 309.608254485 309.608426618 309.608590435 309.608745775 309.608892483 309.609030416 309.609159436 309.609279418 309.609390242 309.609491798 309.609583988 309.609666719 309.60973991 309.609803489 309.609857394 309.60990157 309.609935975 309.609960574 309.609975343 309.609980267 309.40995562 309.409950707 309.409935975 309.409911436 309.409877117 309.409833049 309.409779278 309.409715856 309.409642846 309.409560319 309.409468357 309.409367051 309.409256501 309.409136816 309.409008114 309.408870522 309.408724176 309.40856922 309.408405808 309.4082341 309.408054265 309.407866483 309.407670937 309.407467821 309.407257334 309.407039686 309.406815091 309.406583769 309.406345951 309.40610187 309.405851766 309.405595888 309.405334488 309.405067823 309.404796156 309.404519757 309.404238897 309.403953853 309.403664908 309.403372346 309.403076456 309.402777529 309.402475862 309.402171751 309.401865497 309.401557402 309.40124777 309.400936907 309.400625119 309.400312714 309.4 309.399687286 309.399374881 309.399063093 309.39875223 309.398442598 309.398134503 309.397828249 309.397524138 309.397222471 309.396923544 309.396627654 309.396335092 309.396046147 309.395761103 309.395480243 309.395203844 309.394932177 309.394665512 309.394404112 309.394148234 309.39389813 309.393654049 309.393416231 309.393184909 309.392960314 309.392742666 309.392532179 309.392329063 309.392133517 309.391945735 309.3917659 309.391594192 309.39143078 309.391275824 309.391129478 309.390991886 309.390863184 309.390743499 309.390632949 309.390531643 309.390439681 309.390357154 309.390284144 309.390220722 309.390166951 309.390122883 309.390088564 309.390064025 309.390049293 309.39004438 309.390049293 309.390064025 309.390088564 309.390122883 309.390166951 309.390220722 309.390284144 309.390357154 309.390439681 309.390531643 309.390632949 309.390743499 309.390863184 309.390991886 309.391129478 309.391275824 309.39143078 309.391594192 309.3917659 309.391945735 309.392133517 309.392329063 309.392532179 309.392742666 309.392960314 309.393184909 309.393416231 309.393654049 309.39389813 309.394148234 309.394404112 309.394665512 309.394932177 309.395203844 309.395480243 309.395761103 309.396046147 309.396335092 309.396627654 309.396923544 309.397222471 309.397524138 309.397828249 309.398134503 309.398442598 309.39875223 309.399063093 309.399374881 309.399687286 309.4 309.400312714 309.400625119 309.400936907 309.40124777 309.401557402 309.401865497 309.402171751 309.402475862 309.402777529 309.403076456 309.403372346 309.403664908 309.403953853 309.404238897 309.404519757 309.404796156 309.405067823 309.405334488 309.405595888 309.405851766 309.40610187 309.406345951 309.406583769 309.406815091 309.407039686 309.407257334 309.407467821 309.407670937 309.407866483 309.408054265 309.4082341 309.408405808 309.40856922 309.408724176 309.408870522 309.409008114 309.409136816 309.409256501 309.409367051 309.409468357 309.409560319 309.409642846 309.409715856 309.409779278 309.409833049 309.409877117 309.409911436 309.409935975 309.409950707 309.40995562 309.209921147 309.209916252 309.20990157 309.209877117 309.209842916 309.209799001 309.209745416 309.209682214 309.209609456 309.209527215 309.209435572 309.209334616 309.209224449 309.209105179 309.208976922 309.208839807 309.208693967 309.208539548 309.208376701 309.208205588 309.208026377 309.207839244 309.207644375 309.207441962 309.207232205 309.20701531 309.206791492 309.206560972 309.206323977 309.206080741 309.205831504 309.205576512 309.205316016 309.205050275 309.204779549 309.204504106 309.204224219 309.203940163 309.203652218 309.203360669 309.203065803 309.202767912 309.202467289 309.202164231 309.201859038 309.201552009 309.201243449 309.200933662 309.200622954 309.200311631 309.2 309.199688369 309.199377046 309.199066338 309.198756551 309.198447991 309.198140962 309.197835769 309.197532711 309.197232088 309.196934197 309.196639331 309.196347782 309.196059837 309.195775781 309.195495894 309.195220451 309.194949725 309.194683984 309.194423488 309.194168496 309.193919259 309.193676023 309.193439028 309.193208508 309.19298469 309.192767795 309.192558038 309.192355625 309.192160756 309.191973623 309.191794412 309.191623299 309.191460452 309.191306033 309.191160193 309.191023078 309.190894821 309.190775551 309.190665384 309.190564428 309.190472785 309.190390544 309.190317786 309.190254584 309.190200999 309.190157084 309.190122883 309.19009843 309.190083748 309.190078853 309.190083748 309.19009843 309.190122883 309.190157084 309.190200999 309.190254584 309.190317786 309.190390544 309.190472785 309.190564428 309.190665384 309.190775551 309.190894821 309.191023078 309.191160193 309.191306033 309.191460452 309.191623299 309.191794412 309.191973623 309.192160756 309.192355625 309.192558038 309.192767795 309.19298469 309.193208508 309.193439028 309.193676023 309.193919259 309.194168496 309.194423488 309.194683984 309.194949725 309.195220451 309.195495894 309.195775781 309.196059837 309.196347782 309.196639331 309.196934197 309.197232088 309.197532711 309.197835769 309.198140962 309.198447991 309.198756551 309.199066338 309.199377046 309.199688369 309.2 309.200311631 309.200622954 309.200933662 309.201243449 309.201552009 309.201859038 309.202164231 309.202467289 309.202767912 309.203065803 309.203360669 309.203652218 309.203940163 309.204224219 309.204504106 309.204779549 309.205050275 309.205316016 309.205576512 309.205831504 309.206080741 309.206323977 309.206560972 309.206791492 309.20701531 309.207232205 309.207441962 309.207644375 309.207839244 309.208026377 309.208205588 309.208376701 309.208539548 309.208693967 309.208839807 309.208976922 309.209105179 309.209224449 309.209334616 309.209435572 309.209527215 309.209609456 309.209682214 309.209745416 309.209799001 309.209842916 309.209877117 309.20990157 309.209916252 309.209921147 309.009876883 309.00987201 309.009857394 309.009833049 309.009799001 309.009755283 309.009701937 309.009639016 309.009566583 309.009484709 309.009393474 309.00929297 309.009183294 309.009064555 309.008936871 309.008800368 309.008655179 309.008501449 309.008339328 309.008168978 309.007990567 309.007804269 309.007610269 309.00740876 309.007199938 309.006984011 309.006761192 309.0065317 309.006295762 309.006053612 309.005805486 309.005551632 309.005292299 309.005027743 309.004758225 309.004484011 309.004205372 309.003922583 309.003635923 309.003345675 309.003052125 309.002755563 309.002456281 309.002154575 309.001850743 309.001545085 309.001237902 309.000929497 309.000620175 309.00031024 309 308.99968976 308.999379825 308.999070503 308.998762098 308.998454915 308.998149257 308.997845425 308.997543719 308.997244437 308.996947875 308.996654325 308.996364077 308.996077417 308.995794628 308.995515989 308.995241775 308.994972257 308.994707701 308.994448368 308.994194514 308.993946388 308.993704238 308.9934683 308.993238808 308.993015989 308.992800062 308.99259124 308.992389731 308.992195731 308.992009433 308.991831022 308.991660672 308.991498551 308.991344821 308.991199632 308.991063129 308.990935445 308.990816706 308.99070703 308.990606526 308.990515291 308.990433417 308.990360984 308.990298063 308.990244717 308.990200999 308.990166951 308.990142606 308.99012799 308.990123117 308.99012799 308.990142606 308.990166951 308.990200999 308.990244717 308.990298063 308.990360984 308.990433417 308.990515291 308.990606526 308.99070703 308.990816706 308.990935445 308.991063129 308.991199632 308.991344821 308.991498551 308.991660672 308.991831022 308.992009433 308.992195731 308.992389731 308.99259124 308.992800062 308.993015989 308.993238808 308.9934683 308.993704238 308.993946388 308.994194514 308.994448368 308.994707701 308.994972257 308.995241775 308.995515989 308.995794628 308.996077417 308.996364077 308.996654325 308.996947875 308.997244437 308.997543719 308.997845425 308.998149257 308.998454915 308.998762098 308.999070503 308.999379825 308.99968976 309 309.00031024 309.000620175 309.000929497 309.001237902 309.001545085 309.001850743 309.002154575 309.002456281 309.002755563 309.003052125 309.003345675 309.003635923 309.003922583 309.004205372 309.004484011 309.004758225 309.005027743 309.005292299 309.005551632 309.005805486 309.006053612 309.006295762 309.0065317 309.006761192 309.006984011 309.007199938 309.00740876 309.007610269 309.007804269 309.007990567 309.008168978 309.008339328 309.008501449 309.008655179 309.008800368 309.008936871 309.009064555 309.009183294 309.00929297 309.009393474 309.009484709 309.009566583 309.009639016 309.009701937 309.009755283 309.009799001 309.009833049 309.009857394 309.00987201 309.009876883 308.809822873 308.809818026 308.809803489 308.809779278 308.809745416 308.809701937 308.809648882 308.809586306 308.809514269 308.809432842 308.809342107 308.809242152 308.809133076 308.809014987 308.808888001 308.808752243 308.808607849 308.808454959 308.808293726 308.808124307 308.807946871 308.807761592 308.807568653 308.807368245 308.807160566 308.80694582 308.806724219 308.806495982 308.806261335 308.806020508 308.80577374 308.805521273 308.805263358 308.805000249 308.804732205 308.804459491 308.804182376 308.803901133 308.803616041 308.803327379 308.803035435 308.802740494 308.802442849 308.802142793 308.801840623 308.801536636 308.801231132 308.800924414 308.800616783 308.800308544 308.8 308.799691456 308.799383217 308.799075586 308.798768868 308.798463364 308.798159377 308.797857207 308.797557151 308.797259506 308.796964565 308.796672621 308.796383959 308.796098867 308.795817624 308.795540509 308.795267795 308.794999751 308.794736642 308.794478727 308.79422626 308.793979492 308.793738665 308.793504018 308.793275781 308.79305418 308.792839434 308.792631755 308.792431347 308.792238408 308.792053129 308.791875693 308.791706274 308.791545041 308.791392151 308.791247757 308.791111999 308.790985013 308.790866924 308.790757848 308.790657893 308.790567158 308.790485731 308.790413694 308.790351118 308.790298063 308.790254584 308.790220722 308.790196511 308.790181974 308.790177127 308.790181974 308.790196511 308.790220722 308.790254584 308.790298063 308.790351118 308.790413694 308.790485731 308.790567158 308.790657893 308.790757848 308.790866924 308.790985013 308.791111999 308.791247757 308.791392151 308.791545041 308.791706274 308.791875693 308.792053129 308.792238408 308.792431347 308.792631755 308.792839434 308.79305418 308.793275781 308.793504018 308.793738665 308.793979492 308.79422626 308.794478727 308.794736642 308.794999751 308.795267795 308.795540509 308.795817624 308.796098867 308.796383959 308.796672621 308.796964565 308.797259506 308.797557151 308.797857207 308.798159377 308.798463364 308.798768868 308.799075586 308.799383217 308.799691456 308.8 308.800308544 308.800616783 308.800924414 308.801231132 308.801536636 308.801840623 308.802142793 308.802442849 308.802740494 308.803035435 308.803327379 308.803616041 308.803901133 308.804182376 308.804459491 308.804732205 308.805000249 308.805263358 308.805521273 308.80577374 308.806020508 308.806261335 308.806495982 308.806724219 308.80694582 308.807160566 308.807368245 308.807568653 308.807761592 308.807946871 308.808124307 308.808293726 308.808454959 308.808607849 308.808752243 308.808888001 308.809014987 308.809133076 308.809242152 308.809342107 308.809432842 308.809514269 308.809586306 308.809648882 308.809701937 308.809745416 308.809779278 308.809803489 308.809818026 308.809822873 308.609759168 308.609754352 308.60973991 308.609715856 308.609682214 308.609639016 308.609586306 308.609524135 308.609452565 308.609371667 308.60928152 308.609182213 308.609073845 308.608956521 308.608830359 308.608695482 308.608552024 308.608400126 308.608239938 308.608071618 308.607895332 308.607711255 308.607519568 308.60732046 308.607114127 308.606900774 308.60668061 308.606453853 308.606220728 308.605981463 308.605736295 308.605485466 308.605229224 308.604967821 308.604701515 308.604430569 308.604155251 308.603875833 308.603592589 308.6033058 308.603015749 308.602722721 308.602427006 308.602128896 308.601828686 308.60152667 308.601223148 308.600918419 308.600612783 308.600306543 308.6 308.599693457 308.599387217 308.599081581 308.598776852 308.59847333 308.598171314 308.597871104 308.597572994 308.597277279 308.596984251 308.5966942 308.596407411 308.596124167 308.595844749 308.595569431 308.595298485 308.595032179 308.594770776 308.594514534 308.594263705 308.594018537 308.593779272 308.593546147 308.59331939 308.593099226 308.592885873 308.59267954 308.592480432 308.592288745 308.592104668 308.591928382 308.591760062 308.591599874 308.591447976 308.591304518 308.591169641 308.591043479 308.590926155 308.590817787 308.59071848 308.590628333 308.590547435 308.590475865 308.590413694 308.590360984 308.590317786 308.590284144 308.59026009 308.590245648 308.590240832 308.590245648 308.59026009 308.590284144 308.590317786 308.590360984 308.590413694 308.590475865 308.590547435 308.590628333 308.59071848 308.590817787 308.590926155 308.591043479 308.591169641 308.591304518 308.591447976 308.591599874 308.591760062 308.591928382 308.592104668 308.592288745 308.592480432 308.59267954 308.592885873 308.593099226 308.59331939 308.593546147 308.593779272 308.594018537 308.594263705 308.594514534 308.594770776 308.595032179 308.595298485 308.595569431 308.595844749 308.596124167 308.596407411 308.5966942 308.596984251 308.597277279 308.597572994 308.597871104 308.598171314 308.59847333 308.598776852 308.599081581 308.599387217 308.599693457 308.6 308.600306543 308.600612783 308.600918419 308.601223148 308.60152667 308.601828686 308.602128896 308.602427006 308.602722721 308.603015749 308.6033058 308.603592589 308.603875833 308.604155251 308.604430569 308.604701515 308.604967821 308.605229224 308.605485466 308.605736295 308.605981463 308.606220728 308.606453853 308.60668061 308.606900774 308.607114127 308.60732046 308.607519568 308.607711255 308.607895332 308.608071618 308.608239938 308.608400126 308.608552024 308.608695482 308.608830359 308.608956521 308.609073845 308.609182213 308.60928152 308.609371667 308.609452565 308.609524135 308.609586306 308.609639016 308.609682214 308.609715856 308.60973991 308.609754352 308.609759168 308.409685832 308.409681052 308.409666719 308.409642846 308.409609456 308.409566583 308.409514269 308.409452565 308.409381533 308.409301243 308.409211773 308.409113213 308.409005658 308.408889217 308.408764002 308.408630139 308.408487759 308.408337002 308.408178018 308.408010963 308.407836002 308.407653308 308.407463062 308.40726545 308.407060667 308.406848917 308.406630408 308.406405355 308.406173981 308.405936515 308.405693189 308.405444245 308.405189928 308.404930489 308.404666185 308.404397276 308.404124027 308.403846708 308.403565592 308.403280958 308.402993087 308.402702261 308.402408768 308.402112899 308.401814944 308.401515198 308.401213957 308.400911517 308.400608178 308.400304239 308.4 308.399695761 308.399391822 308.399088483 308.398786043 308.398484802 308.398185056 308.397887101 308.397591232 308.397297739 308.397006913 308.396719042 308.396434408 308.396153292 308.395875973 308.395602724 308.395333815 308.395069511 308.394810072 308.394555755 308.394306811 308.394063485 308.393826019 308.393594645 308.393369592 308.393151083 308.392939333 308.39273455 308.392536938 308.392346692 308.392163998 308.391989037 308.391821982 308.391662998 308.391512241 308.391369861 308.391235998 308.391110783 308.390994342 308.390886787 308.390788227 308.390698757 308.390618467 308.390547435 308.390485731 308.390433417 308.390390544 308.390357154 308.390333281 308.390318948 308.390314168 308.390318948 308.390333281 308.390357154 308.390390544 308.390433417 308.390485731 308.390547435 308.390618467 308.390698757 308.390788227 308.390886787 308.390994342 308.391110783 308.391235998 308.391369861 308.391512241 308.391662998 308.391821982 308.391989037 308.392163998 308.392346692 308.392536938 308.39273455 308.392939333 308.393151083 308.393369592 308.393594645 308.393826019 308.394063485 308.394306811 308.394555755 308.394810072 308.395069511 308.395333815 308.395602724 308.395875973 308.396153292 308.396434408 308.396719042 308.397006913 308.397297739 308.397591232 308.397887101 308.398185056 308.398484802 308.398786043 308.399088483 308.399391822 308.399695761 308.4 308.400304239 308.400608178 308.400911517 308.401213957 308.401515198 308.401814944 308.402112899 308.402408768 308.402702261 308.402993087 308.403280958 308.403565592 308.403846708 308.404124027 308.404397276 308.404666185 308.404930489 308.405189928 308.405444245 308.405693189 308.405936515 308.406173981 308.406405355 308.406630408 308.406848917 308.407060667 308.40726545 308.407463062 308.407653308 308.407836002 308.408010963 308.408178018 308.408337002 308.408487759 308.408630139 308.408764002 308.408889217 308.409005658 308.409113213 308.409211773 308.409301243 308.409381533 308.409452565 308.409514269 308.409566583 308.409609456 308.409642846 308.409666719 308.409681052 308.409685832 308.209602937 308.209598198 308.209583988 308.209560319 308.209527215 308.209484709 308.209432842 308.209371667 308.209301243 308.20922164 308.209132936 308.209035219 308.208928585 308.20881314 308.208688997 308.208556279 308.208415118 308.208265651 308.208108028 308.207942403 308.207768939 308.207587809 308.20739919 308.207203269 308.20700024 308.206790302 308.206573663 308.206350536 308.206121142 308.205885708 308.205644465 308.205397651 308.205145511 308.204888293 308.20462625 308.204359642 308.204088732 308.203813786 308.203535077 308.203252879 308.202967471 308.202679134 308.202388153 308.202094816 308.201799411 308.20150223 308.201203567 308.200903716 308.200602973 308.200301636 308.2 308.199698364 308.199397027 308.199096284 308.198796433 308.19849777 308.198200589 308.197905184 308.197611847 308.197320866 308.197032529 308.196747121 308.196464923 308.196186214 308.195911268 308.195640358 308.19537375 308.195111707 308.194854489 308.194602349 308.194355535 308.194114292 308.193878858 308.193649464 308.193426337 308.193209698 308.19299976 308.192796731 308.19260081 308.192412191 308.192231061 308.192057597 308.191891972 308.191734349 308.191584882 308.191443721 308.191311003 308.19118686 308.191071415 308.190964781 308.190867064 308.19077836 308.190698757 308.190628333 308.190567158 308.190515291 308.190472785 308.190439681 308.190416012 308.190401802 308.190397063 308.190401802 308.190416012 308.190439681 308.190472785 308.190515291 308.190567158 308.190628333 308.190698757 308.19077836 308.190867064 308.190964781 308.191071415 308.19118686 308.191311003 308.191443721 308.191584882 308.191734349 308.191891972 308.192057597 308.192231061 308.192412191 308.19260081 308.192796731 308.19299976 308.193209698 308.193426337 308.193649464 308.193878858 308.194114292 308.194355535 308.194602349 308.194854489 308.195111707 308.19537375 308.195640358 308.195911268 308.196186214 308.196464923 308.196747121 308.197032529 308.197320866 308.197611847 308.197905184 308.198200589 308.19849777 308.198796433 308.199096284 308.199397027 308.199698364 308.2 308.200301636 308.200602973 308.200903716 308.201203567 308.20150223 308.201799411 308.202094816 308.202388153 308.202679134 308.202967471 308.203252879 308.203535077 308.203813786 308.204088732 308.204359642 308.20462625 308.204888293 308.205145511 308.205397651 308.205644465 308.205885708 308.206121142 308.206350536 308.206573663 308.206790302 308.20700024 308.207203269 308.20739919 308.207587809 308.207768939 308.207942403 308.208108028 308.208265651 308.208415118 308.208556279 308.208688997 308.20881314 308.208928585 308.209035219 308.209132936 308.20922164 308.209301243 308.209371667 308.209432842 308.209484709 308.209527215 308.209560319 308.209583988 308.209598198 308.209602937 308.009510565 308.009505872 308.009491798 308.009468357 308.009435572 308.009393474 308.009342107 308.00928152 308.009211773 308.009132936 308.009045085 308.008948308 308.0088427 308.008728365 308.008605417 308.008473976 308.008334172 308.008186143 308.008030036 308.007866004 308.007694209 308.007514821 308.007328016 308.00713398 308.006932904 308.006724985 308.00651043 308.00628945 308.006062262 308.005829092 308.00559017 308.005345731 308.005096016 308.004841272 308.00458175 308.004317706 308.004049402 308.003777101 308.003501073 308.003221589 308.002938926 308.002653363 308.002365181 308.002074666 308.001782102 308.00148778 308.00119199 308.000895023 308.000597173 308.000298734 308 307.999701266 307.999402827 307.999104977 307.99880801 307.99851222 307.998217898 307.997925334 307.997634819 307.997346637 307.997061074 307.996778411 307.996498927 307.996222899 307.995950598 307.995682294 307.99541825 307.995158728 307.994903984 307.994654269 307.99440983 307.994170908 307.993937738 307.99371055 307.99348957 307.993275015 307.993067096 307.99286602 307.992671984 307.992485179 307.992305791 307.992133996 307.991969964 307.991813857 307.991665828 307.991526024 307.991394583 307.991271635 307.9911573 307.991051692 307.990954915 307.990867064 307.990788227 307.99071848 307.990657893 307.990606526 307.990564428 307.990531643 307.990508202 307.990494128 307.990489435 307.990494128 307.990508202 307.990531643 307.990564428 307.990606526 307.990657893 307.99071848 307.990788227 307.990867064 307.990954915 307.991051692 307.9911573 307.991271635 307.991394583 307.991526024 307.991665828 307.991813857 307.991969964 307.992133996 307.992305791 307.992485179 307.992671984 307.99286602 307.993067096 307.993275015 307.99348957 307.99371055 307.993937738 307.994170908 307.99440983 307.994654269 307.994903984 307.995158728 307.99541825 307.995682294 307.995950598 307.996222899 307.996498927 307.996778411 307.997061074 307.997346637 307.997634819 307.997925334 307.998217898 307.99851222 307.99880801 307.999104977 307.999402827 307.999701266 308 308.000298734 308.000597173 308.000895023 308.00119199 308.00148778 308.001782102 308.002074666 308.002365181 308.002653363 308.002938926 308.003221589 308.003501073 308.003777101 308.004049402 308.004317706 308.00458175 308.004841272 308.005096016 308.005345731 308.00559017 308.005829092 308.006062262 308.00628945 308.00651043 308.006724985 308.006932904 308.00713398 308.007328016 308.007514821 308.007694209 308.007866004 308.008030036 308.008186143 308.008334172 308.008473976 308.008605417 308.008728365 308.0088427 308.008948308 308.009045085 308.009132936 308.009211773 308.00928152 308.009342107 308.009393474 308.009435572 308.009468357 308.009491798 308.009505872 308.009510565 307.809408808 307.809404165 307.809390242 307.809367051 307.809334616 307.80929297 307.809242152 307.809182213 307.809113213 307.809035219 307.808948308 307.808852566 307.808748088 307.808634977 307.808513344 307.808383309 307.808245001 307.808098556 307.807944119 307.807781842 307.807611885 307.807434417 307.807249611 307.807057651 307.806858726 307.806653032 307.806440772 307.806222156 307.8059974 307.805766725 307.805530358 307.805288534 307.805041491 307.804789473 307.804532728 307.804271509 307.804006075 307.803736688 307.803463613 307.80318712 307.802907481 307.802624974 307.802339875 307.802052468 307.801763035 307.801471862 307.801179236 307.800885447 307.800590784 307.800295538 307.8 307.799704462 307.799409216 307.799114553 307.798820764 307.798528138 307.798236965 307.797947532 307.797660125 307.797375026 307.797092519 307.79681288 307.796536387 307.796263312 307.795993925 307.795728491 307.795467272 307.795210527 307.794958509 307.794711466 307.794469642 307.794233275 307.7940026 307.793777844 307.793559228 307.793346968 307.793141274 307.792942349 307.792750389 307.792565583 307.792388115 307.792218158 307.792055881 307.791901444 307.791754999 307.791616691 307.791486656 307.791365023 307.791251912 307.791147434 307.791051692 307.790964781 307.790886787 307.790817787 307.790757848 307.79070703 307.790665384 307.790632949 307.790609758 307.790595835 307.790591192 307.790595835 307.790609758 307.790632949 307.790665384 307.79070703 307.790757848 307.790817787 307.790886787 307.790964781 307.791051692 307.791147434 307.791251912 307.791365023 307.791486656 307.791616691 307.791754999 307.791901444 307.792055881 307.792218158 307.792388115 307.792565583 307.792750389 307.792942349 307.793141274 307.793346968 307.793559228 307.793777844 307.7940026 307.794233275 307.794469642 307.794711466 307.794958509 307.795210527 307.795467272 307.795728491 307.795993925 307.796263312 307.796536387 307.79681288 307.797092519 307.797375026 307.797660125 307.797947532 307.798236965 307.798528138 307.798820764 307.799114553 307.799409216 307.799704462 307.8 307.800295538 307.800590784 307.800885447 307.801179236 307.801471862 307.801763035 307.802052468 307.802339875 307.802624974 307.802907481 307.80318712 307.803463613 307.803736688 307.804006075 307.804271509 307.804532728 307.804789473 307.805041491 307.805288534 307.805530358 307.805766725 307.8059974 307.806222156 307.806440772 307.806653032 307.806858726 307.807057651 307.807249611 307.807434417 307.807611885 307.807781842 307.807944119 307.808098556 307.808245001 307.808383309 307.808513344 307.808634977 307.808748088 307.808852566 307.808948308 307.809035219 307.809113213 307.809182213 307.809242152 307.80929297 307.809334616 307.809367051 307.809390242 307.809404165 307.809408808 307.609297765 307.609293177 307.609279418 307.609256501 307.609224449 307.609183294 307.609133076 307.609073845 307.609005658 307.608928585 307.6088427 307.608748088 307.608644843 307.608533067 307.608412869 307.608284369 307.608147693 307.608002977 307.607850363 307.607690001 307.60752205 307.607346676 307.607164051 307.606974356 307.606777779 307.606574513 307.606364758 307.606148722 307.605926618 307.605698666 307.605465089 307.605226119 307.604981992 307.604732947 307.604479232 307.604221097 307.603958796 307.603692588 307.603422736 307.603149506 307.602873167 307.602593994 307.60231226 307.602028245 307.601742227 307.601454491 307.601165319 307.600874997 307.600583811 307.60029205 307.6 307.59970795 307.599416189 307.599125003 307.598834681 307.598545509 307.598257773 307.597971755 307.59768774 307.597406006 307.597126833 307.596850494 307.596577264 307.596307412 307.596041204 307.595778903 307.595520768 307.595267053 307.595018008 307.594773881 307.594534911 307.594301334 307.594073382 307.593851278 307.593635242 307.593425487 307.593222221 307.593025644 307.592835949 307.592653324 307.59247795 307.592309999 307.592149637 307.591997023 307.591852307 307.591715631 307.591587131 307.591466933 307.591355157 307.591251912 307.5911573 307.591071415 307.590994342 307.590926155 307.590866924 307.590816706 307.590775551 307.590743499 307.590720582 307.590706823 307.590702235 307.590706823 307.590720582 307.590743499 307.590775551 307.590816706 307.590866924 307.590926155 307.590994342 307.591071415 307.5911573 307.591251912 307.591355157 307.591466933 307.591587131 307.591715631 307.591852307 307.591997023 307.592149637 307.592309999 307.59247795 307.592653324 307.592835949 307.593025644 307.593222221 307.593425487 307.593635242 307.593851278 307.594073382 307.594301334 307.594534911 307.594773881 307.595018008 307.595267053 307.595520768 307.595778903 307.596041204 307.596307412 307.596577264 307.596850494 307.597126833 307.597406006 307.59768774 307.597971755 307.598257773 307.598545509 307.598834681 307.599125003 307.599416189 307.59970795 307.6 307.60029205 307.600583811 307.600874997 307.601165319 307.601454491 307.601742227 307.602028245 307.60231226 307.602593994 307.602873167 307.603149506 307.603422736 307.603692588 307.603958796 307.604221097 307.604479232 307.604732947 307.604981992 307.605226119 307.605465089 307.605698666 307.605926618 307.606148722 307.606364758 307.606574513 307.606777779 307.606974356 307.607164051 307.607346676 307.60752205 307.607690001 307.607850363 307.608002977 307.608147693 307.608284369 307.608412869 307.608533067 307.608644843 307.608748088 307.6088427 307.608928585 307.609005658 307.609073845 307.609133076 307.609183294 307.609224449 307.609256501 307.609279418 307.609293177 307.609297765 307.409177546 307.409173018 307.409159436 307.409136816 307.409105179 307.409064555 307.409014987 307.408956521 307.408889217 307.40881314 307.408728365 307.408634977 307.408533067 307.408422736 307.408304092 307.408177254 307.408042345 307.4078995 307.407748859 307.40759057 307.407424791 307.407251684 307.407071421 307.406884179 307.406690143 307.406489505 307.406282463 307.40606922 307.405849988 307.405624983 307.405394426 307.405158546 307.404917575 307.404671751 307.404421317 307.404166519 307.403907609 307.403644843 307.40337848 307.403108783 307.402836018 307.402560454 307.402282363 307.40200202 307.401719701 307.401435685 307.401150252 307.400863683 307.400576263 307.400288274 307.4 307.399711726 307.399423737 307.399136317 307.398849748 307.398564315 307.398280299 307.39799798 307.397717637 307.397439546 307.397163982 307.396891217 307.39662152 307.396355157 307.396092391 307.395833481 307.395578683 307.395328249 307.395082425 307.394841454 307.394605574 307.394375017 307.394150012 307.39393078 307.393717537 307.393510495 307.393309857 307.393115821 307.392928579 307.392748316 307.392575209 307.39240943 307.392251141 307.3921005 307.391957655 307.391822746 307.391695908 307.391577264 307.391466933 307.391365023 307.391271635 307.39118686 307.391110783 307.391043479 307.390985013 307.390935445 307.390894821 307.390863184 307.390840564 307.390826982 307.390822454 307.390826982 307.390840564 307.390863184 307.390894821 307.390935445 307.390985013 307.391043479 307.391110783 307.39118686 307.391271635 307.391365023 307.391466933 307.391577264 307.391695908 307.391822746 307.391957655 307.3921005 307.392251141 307.39240943 307.392575209 307.392748316 307.392928579 307.393115821 307.393309857 307.393510495 307.393717537 307.39393078 307.394150012 307.394375017 307.394605574 307.394841454 307.395082425 307.395328249 307.395578683 307.395833481 307.396092391 307.396355157 307.39662152 307.396891217 307.397163982 307.397439546 307.397717637 307.39799798 307.398280299 307.398564315 307.398849748 307.399136317 307.399423737 307.399711726 307.4 307.400288274 307.400576263 307.400863683 307.401150252 307.401435685 307.401719701 307.40200202 307.402282363 307.402560454 307.402836018 307.403108783 307.40337848 307.403644843 307.403907609 307.404166519 307.404421317 307.404671751 307.404917575 307.405158546 307.405394426 307.405624983 307.405849988 307.40606922 307.406282463 307.406489505 307.406690143 307.406884179 307.407071421 307.407251684 307.407424791 307.40759057 307.407748859 307.4078995 307.408042345 307.408177254 307.408304092 307.408422736 307.408533067 307.408634977 307.408728365 307.40881314 307.408889217 307.408956521 307.409014987 307.409064555 307.409105179 307.409136816 307.409159436 307.409173018 307.409177546 307.209048271 307.209043806 307.209030416 307.209008114 307.208976922 307.208936871 307.208888001 307.208830359 307.208764002 307.208688997 307.208605417 307.208513344 307.208412869 307.208304092 307.20818712 307.208062068 307.20792906 307.207788227 307.207639707 307.207483649 307.207320205 307.207149536 307.206971812 307.206787208 307.206595905 307.206398093 307.206193967 307.205983729 307.205767585 307.205545749 307.20531844 307.205085882 307.204848306 307.204605944 307.204359038 307.204107829 307.203852566 307.203593502 307.203330891 307.203064992 307.202796069 307.202524387 307.202250213 307.201973819 307.201695477 307.201415461 307.201134049 307.200851517 307.200568146 307.200284213 307.2 307.199715787 307.199431854 307.199148483 307.198865951 307.198584539 307.198304523 307.198026181 307.197749787 307.197475613 307.197203931 307.196935008 307.196669109 307.196406498 307.196147434 307.195892171 307.195640962 307.195394056 307.195151694 307.194914118 307.19468156 307.194454251 307.194232415 307.194016271 307.193806033 307.193601907 307.193404095 307.193212792 307.193028188 307.192850464 307.192679795 307.192516351 307.192360293 307.192211773 307.19207094 307.191937932 307.19181288 307.191695908 307.191587131 307.191486656 307.191394583 307.191311003 307.191235998 307.191169641 307.191111999 307.191063129 307.191023078 307.190991886 307.190969584 307.190956194 307.190951729 307.190956194 307.190969584 307.190991886 307.191023078 307.191063129 307.191111999 307.191169641 307.191235998 307.191311003 307.191394583 307.191486656 307.191587131 307.191695908 307.19181288 307.191937932 307.19207094 307.192211773 307.192360293 307.192516351 307.192679795 307.192850464 307.193028188 307.193212792 307.193404095 307.193601907 307.193806033 307.194016271 307.194232415 307.194454251 307.19468156 307.194914118 307.195151694 307.195394056 307.195640962 307.195892171 307.196147434 307.196406498 307.196669109 307.196935008 307.197203931 307.197475613 307.197749787 307.198026181 307.198304523 307.198584539 307.198865951 307.199148483 307.199431854 307.199715787 307.2 307.200284213 307.200568146 307.200851517 307.201134049 307.201415461 307.201695477 307.201973819 307.202250213 307.202524387 307.202796069 307.203064992 307.203330891 307.203593502 307.203852566 307.204107829 307.204359038 307.204605944 307.204848306 307.205085882 307.20531844 307.205545749 307.205767585 307.205983729 307.206193967 307.206398093 307.206595905 307.206787208 307.206971812 307.207149536 307.207320205 307.207483649 307.207639707 307.207788227 307.20792906 307.208062068 307.20818712 307.208304092 307.208412869 307.208513344 307.208605417 307.208688997 307.208764002 307.208830359 307.208888001 307.208936871 307.208976922 307.209008114 307.209030416 307.209043806 307.209048271 307.008910065 307.008905669 307.008892483 307.008870522 307.008839807 307.008800368 307.008752243 307.008695482 307.008630139 307.008556279 307.008473976 307.008383309 307.008284369 307.008177254 307.008062068 307.007938926 307.00780795 307.007669268 307.007523017 307.007369342 307.007208394 307.007040333 307.006865323 307.006683539 307.006495158 307.006300368 307.006099359 307.005892332 307.005679489 307.005461042 307.005237205 307.0050082 307.004774252 307.004535592 307.004292457 307.004045085 307.003793721 307.003538614 307.003280014 307.003018177 307.002753362 307.002485829 307.002215843 307.001943671 307.00166958 307.001393841 307.001116727 307.000838511 307.000559468 307.000279872 307 306.999720128 306.999440532 306.999161489 306.998883273 306.998606159 306.99833042 306.998056329 306.997784157 306.997514171 306.997246638 306.996981823 306.996719986 306.996461386 306.996206279 306.995954915 306.995707543 306.995464408 306.995225748 306.9949918 306.994762795 306.994538958 306.994320511 306.994107668 306.993900641 306.993699632 306.993504842 306.993316461 306.993134677 306.992959667 306.992791606 306.992630658 306.992476983 306.992330732 306.99219205 306.992061074 306.991937932 306.991822746 306.991715631 306.991616691 306.991526024 306.991443721 306.991369861 306.991304518 306.991247757 306.991199632 306.991160193 306.991129478 306.991107517 306.991094331 306.991089935 306.991094331 306.991107517 306.991129478 306.991160193 306.991199632 306.991247757 306.991304518 306.991369861 306.991443721 306.991526024 306.991616691 306.991715631 306.991822746 306.991937932 306.992061074 306.99219205 306.992330732 306.992476983 306.992630658 306.992791606 306.992959667 306.993134677 306.993316461 306.993504842 306.993699632 306.993900641 306.994107668 306.994320511 306.994538958 306.994762795 306.9949918 306.995225748 306.995464408 306.995707543 306.995954915 306.996206279 306.996461386 306.996719986 306.996981823 306.997246638 306.997514171 306.997784157 306.998056329 306.99833042 306.998606159 306.998883273 306.999161489 306.999440532 306.999720128 307 307.000279872 307.000559468 307.000838511 307.001116727 307.001393841 307.00166958 307.001943671 307.002215843 307.002485829 307.002753362 307.003018177 307.003280014 307.003538614 307.003793721 307.004045085 307.004292457 307.004535592 307.004774252 307.0050082 307.005237205 307.005461042 307.005679489 307.005892332 307.006099359 307.006300368 307.006495158 307.006683539 307.006865323 307.007040333 307.007208394 307.007369342 307.007523017 307.007669268 307.00780795 307.007938926 307.008062068 307.008177254 307.008284369 307.008383309 307.008473976 307.008556279 307.008630139 307.008695482 307.008752243 307.008800368 307.008839807 307.008870522 307.008892483 307.008905669 307.008910065 306.808763067 306.808758743 306.808745775 306.808724176 306.808693967 306.808655179 306.808607849 306.808552024 306.808487759 306.808415118 306.808334172 306.808245001 306.808147693 306.808042345 306.80792906 306.80780795 306.807679134 306.80754274 306.807398902 306.807247762 306.80708947 306.806924181 306.806752059 306.806573273 306.806388001 306.806196424 306.805998732 306.80579512 306.805585789 306.805370945 306.805150801 306.804925574 306.804695486 306.804460764 306.80422164 306.803978349 306.803731132 306.803480233 306.8032259 306.802968383 306.802707937 306.802444818 306.802179286 306.801911604 306.801642035 306.801370846 306.801098303 306.800824677 306.800550238 306.800275255 306.8 306.799724745 306.799449762 306.799175323 306.798901697 306.798629154 306.798357965 306.798088396 306.797820714 306.797555182 306.797292063 306.797031617 306.7967741 306.796519767 306.796268868 306.796021651 306.79577836 306.795539236 306.795304514 306.795074426 306.794849199 306.794629055 306.794414211 306.79420488 306.794001268 306.793803576 306.793611999 306.793426727 306.793247941 306.793075819 306.79291053 306.792752238 306.792601098 306.79245726 306.792320866 306.79219205 306.79207094 306.791957655 306.791852307 306.791754999 306.791665828 306.791584882 306.791512241 306.791447976 306.791392151 306.791344821 306.791306033 306.791275824 306.791254225 306.791241257 306.791236933 306.791241257 306.791254225 306.791275824 306.791306033 306.791344821 306.791392151 306.791447976 306.791512241 306.791584882 306.791665828 306.791754999 306.791852307 306.791957655 306.79207094 306.79219205 306.792320866 306.79245726 306.792601098 306.792752238 306.79291053 306.793075819 306.793247941 306.793426727 306.793611999 306.793803576 306.794001268 306.79420488 306.794414211 306.794629055 306.794849199 306.795074426 306.795304514 306.795539236 306.79577836 306.796021651 306.796268868 306.796519767 306.7967741 306.797031617 306.797292063 306.797555182 306.797820714 306.798088396 306.798357965 306.798629154 306.798901697 306.799175323 306.799449762 306.799724745 306.8 306.800275255 306.800550238 306.800824677 306.801098303 306.801370846 306.801642035 306.801911604 306.802179286 306.802444818 306.802707937 306.802968383 306.8032259 306.803480233 306.803731132 306.803978349 306.80422164 306.804460764 306.804695486 306.804925574 306.805150801 306.805370945 306.805585789 306.80579512 306.805998732 306.806196424 306.806388001 306.806573273 306.806752059 306.806924181 306.80708947 306.807247762 306.807398902 306.80754274 306.807679134 306.80780795 306.80792906 306.808042345 306.808147693 306.808245001 306.808334172 306.808415118 306.808487759 306.808552024 306.808607849 306.808655179 306.808693967 306.808724176 306.808745775 306.808758743 306.808763067 306.60860742 306.608603173 306.608590435 306.60856922 306.608539548 306.608501449 306.608454959 306.608400126 306.608337002 306.608265651 306.608186143 306.608098556 306.608002977 306.6078995 306.607788227 306.607669268 306.60754274 306.607408768 306.607267485 306.60711903 306.606963549 306.606801196 306.606632131 306.606456521 306.606274539 306.606086365 306.605892185 306.605692189 306.605486576 306.605275549 306.605059315 306.604838088 306.604612086 306.604381533 306.604146656 306.603907687 306.603664861 306.603418419 306.603168603 306.60291566 306.602659839 306.602401394 306.602140578 306.601877651 306.60161287 306.601346497 306.601078796 306.60081003 306.600540464 306.600270366 306.6 306.599729634 306.599459536 306.59918997 306.598921204 306.598653503 306.59838713 306.598122349 306.597859422 306.597598606 306.597340161 306.59708434 306.596831397 306.596581581 306.596335139 306.596092313 306.595853344 306.595618467 306.595387914 306.595161912 306.594940685 306.594724451 306.594513424 306.594307811 306.594107815 306.593913635 306.593725461 306.593543479 306.593367869 306.593198804 306.593036451 306.59288097 306.592732515 306.592591232 306.59245726 306.592330732 306.592211773 306.5921005 306.591997023 306.591901444 306.591813857 306.591734349 306.591662998 306.591599874 306.591545041 306.591498551 306.591460452 306.59143078 306.591409565 306.591396827 306.59139258 306.591396827 306.591409565 306.59143078 306.591460452 306.591498551 306.591545041 306.591599874 306.591662998 306.591734349 306.591813857 306.591901444 306.591997023 306.5921005 306.592211773 306.592330732 306.59245726 306.592591232 306.592732515 306.59288097 306.593036451 306.593198804 306.593367869 306.593543479 306.593725461 306.593913635 306.594107815 306.594307811 306.594513424 306.594724451 306.594940685 306.595161912 306.595387914 306.595618467 306.595853344 306.596092313 306.596335139 306.596581581 306.596831397 306.59708434 306.597340161 306.597598606 306.597859422 306.598122349 306.59838713 306.598653503 306.598921204 306.59918997 306.599459536 306.599729634 306.6 306.600270366 306.600540464 306.60081003 306.601078796 306.601346497 306.60161287 306.601877651 306.602140578 306.602401394 306.602659839 306.60291566 306.603168603 306.603418419 306.603664861 306.603907687 306.604146656 306.604381533 306.604612086 306.604838088 306.605059315 306.605275549 306.605486576 306.605692189 306.605892185 306.606086365 306.606274539 306.606456521 306.606632131 306.606801196 306.606963549 306.60711903 306.607267485 306.607408768 306.60754274 306.607669268 306.607788227 306.6078995 306.608002977 306.608098556 306.608186143 306.608265651 306.608337002 306.608400126 306.608454959 306.608501449 306.608539548 306.60856922 306.608590435 306.608603173 306.60860742 306.408443279 306.408439113 306.408426618 306.408405808 306.408376701 306.408339328 306.408293726 306.408239938 306.408178018 306.408108028 306.408030036 306.407944119 306.407850363 306.407748859 306.407639707 306.407523017 306.407398902 306.407267485 306.407128896 306.406983272 306.406830756 306.406671499 306.406505658 306.406333397 306.406154886 306.4059703 306.405779822 306.405583641 306.405381949 306.405174945 306.404962835 306.404745827 306.404524135 306.404297979 306.404067581 306.403833169 306.403594973 306.403353231 306.403108178 306.402860059 306.402609117 306.4023556 306.402099758 306.401841844 306.401582113 306.40132082 306.401058223 306.400794583 306.400530158 306.40026521 306.4 306.39973479 306.399469842 306.399205417 306.398941777 306.39867918 306.398417887 306.398158156 306.397900242 306.3976444 306.397390883 306.397139941 306.396891822 306.396646769 306.396405027 306.396166831 306.395932419 306.395702021 306.395475865 306.395254173 306.395037165 306.394825055 306.394618051 306.394416359 306.394220178 306.3940297 306.393845114 306.393666603 306.393494342 306.393328501 306.393169244 306.393016728 306.392871104 306.392732515 306.392601098 306.392476983 306.392360293 306.392251141 306.392149637 306.392055881 306.391969964 306.391891972 306.391821982 306.391760062 306.391706274 306.391660672 306.391623299 306.391594192 306.391573382 306.391560887 306.391556721 306.391560887 306.391573382 306.391594192 306.391623299 306.391660672 306.391706274 306.391760062 306.391821982 306.391891972 306.391969964 306.392055881 306.392149637 306.392251141 306.392360293 306.392476983 306.392601098 306.392732515 306.392871104 306.393016728 306.393169244 306.393328501 306.393494342 306.393666603 306.393845114 306.3940297 306.394220178 306.394416359 306.394618051 306.394825055 306.395037165 306.395254173 306.395475865 306.395702021 306.395932419 306.396166831 306.396405027 306.396646769 306.396891822 306.397139941 306.397390883 306.3976444 306.397900242 306.398158156 306.398417887 306.39867918 306.398941777 306.399205417 306.399469842 306.39973479 306.4 306.40026521 306.400530158 306.400794583 306.401058223 306.40132082 306.401582113 306.401841844 306.402099758 306.4023556 306.402609117 306.402860059 306.403108178 306.403353231 306.403594973 306.403833169 306.404067581 306.404297979 306.404524135 306.404745827 306.404962835 306.405174945 306.405381949 306.405583641 306.405779822 306.4059703 306.406154886 306.406333397 306.406505658 306.406671499 306.406830756 306.406983272 306.407128896 306.407267485 306.407398902 306.407523017 306.407639707 306.407748859 306.407850363 306.407944119 306.408030036 306.408108028 306.408178018 306.408239938 306.408293726 306.408339328 306.408376701 306.408405808 306.408426618 306.408439113 306.408443279 306.208270806 306.208266725 306.208254485 306.2082341 306.208205588 306.208168978 306.208124307 306.208071618 306.208010963 306.207942403 306.207866004 306.207781842 306.207690001 306.20759057 306.207483649 306.207369342 306.207247762 306.20711903 306.206983272 306.206840623 306.206691222 306.206535219 306.206372765 306.206204023 306.206029158 306.205848343 306.205661756 306.205469582 306.20527201 306.205069235 306.204861458 306.204648882 306.204431719 306.204210183 306.203984491 306.203754867 306.203521538 306.203284733 306.203044687 306.202801636 306.20255582 306.202307481 306.202056866 306.20180422 306.201549794 306.201293839 306.201036607 306.200778352 306.200519328 306.200259792 306.2 306.199740208 306.199480672 306.199221648 306.198963393 306.198706161 306.198450206 306.19819578 306.197943134 306.197692519 306.19744418 306.197198364 306.196955313 306.196715267 306.196478462 306.196245133 306.196015509 306.195789817 306.195568281 306.195351118 306.195138542 306.194930765 306.19472799 306.194530418 306.194338244 306.194151657 306.193970842 306.193795977 306.193627235 306.193464781 306.193308778 306.193159377 306.193016728 306.19288097 306.192752238 306.192630658 306.192516351 306.19240943 306.192309999 306.192218158 306.192133996 306.192057597 306.191989037 306.191928382 306.191875693 306.191831022 306.191794412 306.1917659 306.191745515 306.191733275 306.191729194 306.191733275 306.191745515 306.1917659 306.191794412 306.191831022 306.191875693 306.191928382 306.191989037 306.192057597 306.192133996 306.192218158 306.192309999 306.19240943 306.192516351 306.192630658 306.192752238 306.19288097 306.193016728 306.193159377 306.193308778 306.193464781 306.193627235 306.193795977 306.193970842 306.194151657 306.194338244 306.194530418 306.19472799 306.194930765 306.195138542 306.195351118 306.195568281 306.195789817 306.196015509 306.196245133 306.196478462 306.196715267 306.196955313 306.197198364 306.19744418 306.197692519 306.197943134 306.19819578 306.198450206 306.198706161 306.198963393 306.199221648 306.199480672 306.199740208 306.2 306.200259792 306.200519328 306.200778352 306.201036607 306.201293839 306.201549794 306.20180422 306.202056866 306.202307481 306.20255582 306.202801636 306.203044687 306.203284733 306.203521538 306.203754867 306.203984491 306.204210183 306.204431719 306.204648882 306.204861458 306.205069235 306.20527201 306.205469582 306.205661756 306.205848343 306.206029158 306.206204023 306.206372765 306.206535219 306.206691222 306.206840623 306.206983272 306.20711903 306.207247762 306.207369342 306.207483649 306.20759057 306.207690001 306.207781842 306.207866004 306.207942403 306.208010963 306.208071618 306.208124307 306.208168978 306.208205588 306.2082341 306.208254485 306.208266725 306.208270806 306.00809017 306.008086178 306.008074206 306.008054265 306.008026377 306.007990567 306.007946871 306.007895332 306.007836002 306.007768939 306.007694209 306.007611885 306.00752205 306.007424791 306.007320205 306.007208394 306.00708947 306.006963549 306.006830756 306.006691222 306.006545085 306.006392488 306.006233583 306.006068526 306.00589748 306.005720614 306.005538102 306.005350125 306.005156868 306.004958522 306.004755283 306.00454735 306.00433493 306.004118232 306.003897469 306.00367286 306.003444627 306.003212994 306.00297819 306.002740447 306.0025 306.002257085 306.002011943 306.001764816 306.001515947 306.001265581 306.001013967 306.000761352 306.000507986 306.000254118 306 305.999745882 305.999492014 305.999238648 305.998986033 305.998734419 305.998484053 305.998235184 305.997988057 305.997742915 305.9975 305.997259553 305.99702181 305.996787006 305.996555373 305.99632714 305.996102531 305.995881768 305.99566507 305.99545265 305.995244717 305.995041478 305.994843132 305.994649875 305.994461898 305.994279386 305.99410252 305.993931474 305.993766417 305.993607512 305.993454915 305.993308778 305.993169244 305.993036451 305.99291053 305.992791606 305.992679795 305.992575209 305.99247795 305.992388115 305.992305791 305.992231061 305.992163998 305.992104668 305.992053129 305.992009433 305.991973623 305.991945735 305.991925794 305.991913822 305.99190983 305.991913822 305.991925794 305.991945735 305.991973623 305.992009433 305.992053129 305.992104668 305.992163998 305.992231061 305.992305791 305.992388115 305.99247795 305.992575209 305.992679795 305.992791606 305.99291053 305.993036451 305.993169244 305.993308778 305.993454915 305.993607512 305.993766417 305.993931474 305.99410252 305.994279386 305.994461898 305.994649875 305.994843132 305.995041478 305.995244717 305.99545265 305.99566507 305.995881768 305.996102531 305.99632714 305.996555373 305.996787006 305.99702181 305.997259553 305.9975 305.997742915 305.997988057 305.998235184 305.998484053 305.998734419 305.998986033 305.999238648 305.999492014 305.999745882 306 306.000254118 306.000507986 306.000761352 306.001013967 306.001265581 306.001515947 306.001764816 306.002011943 306.002257085 306.0025 306.002740447 306.00297819 306.003212994 306.003444627 306.00367286 306.003897469 306.004118232 306.00433493 306.00454735 306.004755283 306.004958522 306.005156868 306.005350125 306.005538102 306.005720614 306.00589748 306.006068526 306.006233583 306.006392488 306.006545085 306.006691222 306.006830756 306.006963549 306.00708947 306.007208394 306.007320205 306.007424791 306.00752205 306.007611885 306.007694209 306.007768939 306.007836002 306.007895332 306.007946871 306.007990567 306.008026377 306.008054265 306.008074206 306.008086178 306.00809017 305.80790155 305.807897651 305.807885958 305.807866483 305.807839244 305.807804269 305.807761592 305.807711255 305.807653308 305.807587809 305.807514821 305.807434417 305.807346676 305.807251684 305.807149536 305.807040333 305.806924181 305.806801196 305.806671499 305.806535219 305.806392488 305.806243449 305.806088249 305.80592704 305.805759982 305.80558724 305.805408983 305.805225389 305.805036638 305.804842916 305.804644415 305.80444133 305.804233862 305.804022216 305.803806601 305.803587229 305.803364316 305.803138084 305.802908755 305.802676555 305.802441713 305.802204462 305.801965036 305.80172367 305.801480603 305.801236075 305.800990327 305.800743602 305.800496142 305.800248194 305.8 305.799751806 305.799503858 305.799256398 305.799009673 305.798763925 305.798519397 305.79827633 305.798034964 305.797795538 305.797558287 305.797323445 305.797091245 305.796861916 305.796635684 305.796412771 305.796193399 305.795977784 305.795766138 305.79555867 305.795355585 305.795157084 305.794963362 305.794774611 305.794591017 305.79441276 305.794240018 305.79407296 305.793911751 305.793756551 305.793607512 305.793464781 305.793328501 305.793198804 305.793075819 305.792959667 305.792850464 305.792748316 305.792653324 305.792565583 305.792485179 305.792412191 305.792346692 305.792288745 305.792238408 305.792195731 305.792160756 305.792133517 305.792114042 305.792102349 305.79209845 305.792102349 305.792114042 305.792133517 305.792160756 305.792195731 305.792238408 305.792288745 305.792346692 305.792412191 305.792485179 305.792565583 305.792653324 305.792748316 305.792850464 305.792959667 305.793075819 305.793198804 305.793328501 305.793464781 305.793607512 305.793756551 305.793911751 305.79407296 305.794240018 305.79441276 305.794591017 305.794774611 305.794963362 305.795157084 305.795355585 305.79555867 305.795766138 305.795977784 305.796193399 305.796412771 305.796635684 305.796861916 305.797091245 305.797323445 305.797558287 305.797795538 305.798034964 305.79827633 305.798519397 305.798763925 305.799009673 305.799256398 305.799503858 305.799751806 305.8 305.800248194 305.800496142 305.800743602 305.800990327 305.801236075 305.801480603 305.80172367 305.801965036 305.802204462 305.802441713 305.802676555 305.802908755 305.803138084 305.803364316 305.803587229 305.803806601 305.804022216 305.804233862 305.80444133 305.804644415 305.804842916 305.805036638 305.805225389 305.805408983 305.80558724 305.805759982 305.80592704 305.806088249 305.806243449 305.806392488 305.806535219 305.806671499 305.806801196 305.806924181 305.807040333 305.807149536 305.807251684 305.807346676 305.807434417 305.807514821 305.807587809 305.807653308 305.807711255 305.807761592 305.807804269 305.807839244 305.807866483 305.807885958 305.807897651 305.80790155 305.607705132 305.60770133 305.607689928 305.607670937 305.607644375 305.607610269 305.607568653 305.607519568 305.607463062 305.60739919 305.607328016 305.607249611 305.607164051 305.607071421 305.606971812 305.606865323 305.606752059 305.606632131 305.606505658 305.606372765 305.606233583 305.606088249 305.605936907 305.605779705 305.6056168 305.605448351 305.605274526 305.605095495 305.604911436 305.60472253 305.604528963 305.604330927 305.604128616 305.603922232 305.603711976 305.603498057 305.603280686 305.603060077 305.602836448 305.602610021 305.602381017 305.602149663 305.601916189 305.601680823 305.601443798 305.601205348 305.600965709 305.600725117 305.600483809 305.600242024 305.6 305.599757976 305.599516191 305.599274883 305.599034291 305.598794652 305.598556202 305.598319177 305.598083811 305.597850337 305.597618983 305.597389979 305.597163552 305.596939923 305.596719314 305.596501943 305.596288024 305.596077768 305.595871384 305.595669073 305.595471037 305.59527747 305.595088564 305.594904505 305.594725474 305.594551649 305.5943832 305.594220295 305.594063093 305.593911751 305.593766417 305.593627235 305.593494342 305.593367869 305.593247941 305.593134677 305.593028188 305.592928579 305.592835949 305.592750389 305.592671984 305.59260081 305.592536938 305.592480432 305.592431347 305.592389731 305.592355625 305.592329063 305.592310072 305.59229867 305.592294868 305.59229867 305.592310072 305.592329063 305.592355625 305.592389731 305.592431347 305.592480432 305.592536938 305.59260081 305.592671984 305.592750389 305.592835949 305.592928579 305.593028188 305.593134677 305.593247941 305.593367869 305.593494342 305.593627235 305.593766417 305.593911751 305.594063093 305.594220295 305.5943832 305.594551649 305.594725474 305.594904505 305.595088564 305.59527747 305.595471037 305.595669073 305.595871384 305.596077768 305.596288024 305.596501943 305.596719314 305.596939923 305.597163552 305.597389979 305.597618983 305.597850337 305.598083811 305.598319177 305.598556202 305.598794652 305.599034291 305.599274883 305.599516191 305.599757976 305.6 305.600242024 305.600483809 305.600725117 305.600965709 305.601205348 305.601443798 305.601680823 305.601916189 305.602149663 305.602381017 305.602610021 305.602836448 305.603060077 305.603280686 305.603498057 305.603711976 305.603922232 305.604128616 305.604330927 305.604528963 305.60472253 305.604911436 305.605095495 305.605274526 305.605448351 305.6056168 305.605779705 305.605936907 305.606088249 305.606233583 305.606372765 305.606505658 305.606632131 305.606752059 305.606865323 305.606971812 305.607071421 305.607164051 305.607249611 305.607328016 305.60739919 305.607463062 305.607519568 305.607568653 305.607610269 305.607644375 305.607670937 305.607689928 305.60770133 305.607705132 305.407501111 305.407497409 305.407486309 305.407467821 305.407441962 305.40740876 305.407368245 305.40732046 305.40726545 305.407203269 305.40713398 305.407057651 305.406974356 305.406884179 305.406787208 305.406683539 305.406573273 305.406456521 305.406333397 305.406204023 305.406068526 305.40592704 305.405779705 305.405626666 305.405468074 305.405304086 305.405134864 305.404960574 305.404781388 305.404597484 305.404409042 305.40421625 305.404019296 305.403818376 305.403613688 305.403405433 305.403193818 305.40297905 305.402761343 305.402540911 305.402317971 305.402092743 305.40186545 305.401636317 305.401405568 305.401173432 305.400940138 305.400705917 305.400470999 305.400235616 305.4 305.399764384 305.399529001 305.399294083 305.399059862 305.398826568 305.398594432 305.398363683 305.39813455 305.397907257 305.397682029 305.397459089 305.397238657 305.39702095 305.396806182 305.396594567 305.396386312 305.396181624 305.395980704 305.39578375 305.395590958 305.395402516 305.395218612 305.395039426 305.394865136 305.394695914 305.394531926 305.394373334 305.394220295 305.39407296 305.393931474 305.393795977 305.393666603 305.393543479 305.393426727 305.393316461 305.393212792 305.393115821 305.393025644 305.392942349 305.39286602 305.392796731 305.39273455 305.39267954 305.392631755 305.39259124 305.392558038 305.392532179 305.392513691 305.392502591 305.392498889 305.392502591 305.392513691 305.392532179 305.392558038 305.39259124 305.392631755 305.39267954 305.39273455 305.392796731 305.39286602 305.392942349 305.393025644 305.393115821 305.393212792 305.393316461 305.393426727 305.393543479 305.393666603 305.393795977 305.393931474 305.39407296 305.394220295 305.394373334 305.394531926 305.394695914 305.394865136 305.395039426 305.395218612 305.395402516 305.395590958 305.39578375 305.395980704 305.396181624 305.396386312 305.396594567 305.396806182 305.39702095 305.397238657 305.397459089 305.397682029 305.397907257 305.39813455 305.398363683 305.398594432 305.398826568 305.399059862 305.399294083 305.399529001 305.399764384 305.4 305.400235616 305.400470999 305.400705917 305.400940138 305.401173432 305.401405568 305.401636317 305.40186545 305.402092743 305.402317971 305.402540911 305.402761343 305.40297905 305.403193818 305.403405433 305.403613688 305.403818376 305.404019296 305.40421625 305.404409042 305.404597484 305.404781388 305.404960574 305.405134864 305.405304086 305.405468074 305.405626666 305.405779705 305.40592704 305.406068526 305.406204023 305.406333397 305.406456521 305.406573273 305.406683539 305.406787208 305.406884179 305.406974356 305.407057651 305.40713398 305.407203269 305.40726545 305.40732046 305.407368245 305.40740876 305.407441962 305.407467821 305.407486309 305.407497409 305.407501111 305.207289686 305.207286089 305.207275302 305.207257334 305.207232205 305.207199938 305.207160566 305.207114127 305.207060667 305.20700024 305.206932904 305.206858726 305.206777779 305.206690143 305.206595905 305.206495158 305.206388001 305.206274539 305.206154886 305.206029158 305.20589748 305.205759982 305.2056168 305.205468074 305.205313953 305.205154587 305.204990134 305.204820756 305.204646621 305.2044679 305.20428477 305.204097411 305.203906009 305.203710752 305.203511833 305.203309448 305.203103797 305.202895084 305.202683512 305.202469293 305.202252637 305.202033758 305.201812871 305.201590196 305.201365951 305.201140358 305.20091364 305.20068602 305.200457723 305.200228975 305.2 305.199771025 305.199542277 305.19931398 305.19908636 305.198859642 305.198634049 305.198409804 305.198187129 305.197966242 305.197747363 305.197530707 305.197316488 305.197104916 305.196896203 305.196690552 305.196488167 305.196289248 305.196093991 305.195902589 305.19571523 305.1955321 305.195353379 305.195179244 305.195009866 305.194845413 305.194686047 305.194531926 305.1943832 305.194240018 305.19410252 305.193970842 305.193845114 305.193725461 305.193611999 305.193504842 305.193404095 305.193309857 305.193222221 305.193141274 305.193067096 305.19299976 305.192939333 305.192885873 305.192839434 305.192800062 305.192767795 305.192742666 305.192724698 305.192713911 305.192710314 305.192713911 305.192724698 305.192742666 305.192767795 305.192800062 305.192839434 305.192885873 305.192939333 305.19299976 305.193067096 305.193141274 305.193222221 305.193309857 305.193404095 305.193504842 305.193611999 305.193725461 305.193845114 305.193970842 305.19410252 305.194240018 305.1943832 305.194531926 305.194686047 305.194845413 305.195009866 305.195179244 305.195353379 305.1955321 305.19571523 305.195902589 305.196093991 305.196289248 305.196488167 305.196690552 305.196896203 305.197104916 305.197316488 305.197530707 305.197747363 305.197966242 305.198187129 305.198409804 305.198634049 305.198859642 305.19908636 305.19931398 305.199542277 305.199771025 305.2 305.200228975 305.200457723 305.20068602 305.20091364 305.201140358 305.201365951 305.201590196 305.201812871 305.202033758 305.202252637 305.202469293 305.202683512 305.202895084 305.203103797 305.203309448 305.203511833 305.203710752 305.203906009 305.204097411 305.20428477 305.2044679 305.204646621 305.204820756 305.204990134 305.205154587 305.205313953 305.205468074 305.2056168 305.205759982 305.20589748 305.206029158 305.206154886 305.206274539 305.206388001 305.206495158 305.206595905 305.206690143 305.206777779 305.206858726 305.206932904 305.20700024 305.207060667 305.207114127 305.207160566 305.207199938 305.207232205 305.207257334 305.207275302 305.207286089 305.207289686 305.007071068 305.007067579 305.007057115 305.007039686 305.00701531 305.006984011 305.00694582 305.006900774 305.006848917 305.006790302 305.006724985 305.006653032 305.006574513 305.006489505 305.006398093 305.006300368 305.006196424 305.006086365 305.0059703 305.005848343 305.005720614 305.00558724 305.005448351 305.005304086 305.005154587 305.005 305.004840479 305.004676181 305.004507268 305.004333907 305.004156269 305.00397453 305.003788868 305.003599466 305.003406513 305.003210198 305.003010714 305.00280826 305.002603034 305.002395239 305.00218508 305.001972765 305.001758503 305.001542506 305.001324986 305.001106159 305.00088624 305.000665446 305.000443996 305.000222108 305 304.999777892 304.999556004 304.999334554 304.99911376 304.998893841 304.998675014 304.998457494 304.998241497 304.998027235 304.99781492 304.997604761 304.997396966 304.99719174 304.996989286 304.996789802 304.996593487 304.996400534 304.996211132 304.99602547 304.995843731 304.995666093 304.995492732 304.995323819 304.995159521 304.995 304.994845413 304.994695914 304.994551649 304.99441276 304.994279386 304.994151657 304.9940297 304.993913635 304.993803576 304.993699632 304.993601907 304.993510495 304.993425487 304.993346968 304.993275015 304.993209698 304.993151083 304.993099226 304.99305418 304.993015989 304.99298469 304.992960314 304.992942885 304.992932421 304.992928932 304.992932421 304.992942885 304.992960314 304.99298469 304.993015989 304.99305418 304.993099226 304.993151083 304.993209698 304.993275015 304.993346968 304.993425487 304.993510495 304.993601907 304.993699632 304.993803576 304.993913635 304.9940297 304.994151657 304.994279386 304.99441276 304.994551649 304.994695914 304.994845413 304.995 304.995159521 304.995323819 304.995492732 304.995666093 304.995843731 304.99602547 304.996211132 304.996400534 304.996593487 304.996789802 304.996989286 304.99719174 304.997396966 304.997604761 304.99781492 304.998027235 304.998241497 304.998457494 304.998675014 304.998893841 304.99911376 304.999334554 304.999556004 304.999777892 305 305.000222108 305.000443996 305.000665446 305.00088624 305.001106159 305.001324986 305.001542506 305.001758503 305.001972765 305.00218508 305.002395239 305.002603034 305.00280826 305.003010714 305.003210198 305.003406513 305.003599466 305.003788868 305.00397453 305.004156269 305.004333907 305.004507268 305.004676181 305.004840479 305.005 305.005154587 305.005304086 305.005448351 305.00558724 305.005720614 305.005848343 305.0059703 305.006086365 305.006196424 305.006300368 305.006398093 305.006489505 305.006574513 305.006653032 305.006724985 305.006790302 305.006848917 305.006900774 305.00694582 305.006984011 305.00701531 305.007039686 305.007057115 305.007067579 305.007071068 304.806845471 304.806842093 304.806831963 304.806815091 304.806791492 304.806761192 304.806724219 304.80668061 304.806630408 304.806573663 304.80651043 304.806440772 304.806364758 304.806282463 304.806193967 304.806099359 304.805998732 304.805892185 304.805779822 304.805661756 304.805538102 304.805408983 304.805274526 304.805134864 304.804990134 304.804840479 304.804686047 304.804526991 304.804363467 304.804195637 304.804023667 304.803847725 304.803667987 304.803484628 304.803297831 304.803107779 304.80291466 304.802718664 304.802519986 304.802318821 304.802115367 304.801909826 304.801702399 304.801493293 304.801282713 304.801070868 304.800857965 304.800644216 304.800429831 304.800215021 304.8 304.799784979 304.799570169 304.799355784 304.799142035 304.798929132 304.798717287 304.798506707 304.798297601 304.798090174 304.797884633 304.797681179 304.797480014 304.797281336 304.79708534 304.796892221 304.796702169 304.796515372 304.796332013 304.796152275 304.795976333 304.795804363 304.795636533 304.795473009 304.795313953 304.795159521 304.795009866 304.794865136 304.794725474 304.794591017 304.794461898 304.794338244 304.794220178 304.794107815 304.794001268 304.793900641 304.793806033 304.793717537 304.793635242 304.793559228 304.79348957 304.793426337 304.793369592 304.79331939 304.793275781 304.793238808 304.793208508 304.793184909 304.793168037 304.793157907 304.793154529 304.793157907 304.793168037 304.793184909 304.793208508 304.793238808 304.793275781 304.79331939 304.793369592 304.793426337 304.79348957 304.793559228 304.793635242 304.793717537 304.793806033 304.793900641 304.794001268 304.794107815 304.794220178 304.794338244 304.794461898 304.794591017 304.794725474 304.794865136 304.795009866 304.795159521 304.795313953 304.795473009 304.795636533 304.795804363 304.795976333 304.796152275 304.796332013 304.796515372 304.796702169 304.796892221 304.79708534 304.797281336 304.797480014 304.797681179 304.797884633 304.798090174 304.798297601 304.798506707 304.798717287 304.798929132 304.799142035 304.799355784 304.799570169 304.799784979 304.8 304.800215021 304.800429831 304.800644216 304.800857965 304.801070868 304.801282713 304.801493293 304.801702399 304.801909826 304.802115367 304.802318821 304.802519986 304.802718664 304.80291466 304.803107779 304.803297831 304.803484628 304.803667987 304.803847725 304.804023667 304.804195637 304.804363467 304.804526991 304.804686047 304.804840479 304.804990134 304.805134864 304.805274526 304.805408983 304.805538102 304.805661756 304.805779822 304.805892185 304.805998732 304.806099359 304.806193967 304.806282463 304.806364758 304.806440772 304.80651043 304.806573663 304.806630408 304.80668061 304.806724219 304.806761192 304.806791492 304.806815091 304.806831963 304.806842093 304.806845471 304.606613119 304.606609855 304.606600069 304.606583769 304.606560972 304.6065317 304.606495982 304.606453853 304.606405355 304.606350536 304.60628945 304.606222156 304.606148722 304.60606922 304.605983729 304.605892332 304.60579512 304.605692189 304.605583641 304.605469582 304.605350125 304.605225389 304.605095495 304.604960574 304.604820756 304.604676181 304.604526991 304.604373334 304.60421536 304.604053227 304.603887094 304.603717124 304.603543486 304.603366351 304.603185894 304.603002293 304.602815729 304.602626386 304.602434451 304.602240114 304.602043566 304.601845001 304.601644616 304.601442607 304.601239175 304.60103452 304.600828844 304.600622349 304.600415241 304.600207723 304.6 304.599792277 304.599584759 304.599377651 304.599171156 304.59896548 304.598760825 304.598557393 304.598355384 304.598154999 304.597956434 304.597759886 304.597565549 304.597373614 304.597184271 304.596997707 304.596814106 304.596633649 304.596456514 304.596282876 304.596112906 304.595946773 304.59578464 304.595626666 304.595473009 304.595323819 304.595179244 304.595039426 304.594904505 304.594774611 304.594649875 304.594530418 304.594416359 304.594307811 304.59420488 304.594107668 304.594016271 304.59393078 304.593851278 304.593777844 304.59371055 304.593649464 304.593594645 304.593546147 304.593504018 304.5934683 304.593439028 304.593416231 304.593399931 304.593390145 304.593386881 304.593390145 304.593399931 304.593416231 304.593439028 304.5934683 304.593504018 304.593546147 304.593594645 304.593649464 304.59371055 304.593777844 304.593851278 304.59393078 304.594016271 304.594107668 304.59420488 304.594307811 304.594416359 304.594530418 304.594649875 304.594774611 304.594904505 304.595039426 304.595179244 304.595323819 304.595473009 304.595626666 304.59578464 304.595946773 304.596112906 304.596282876 304.596456514 304.596633649 304.596814106 304.596997707 304.597184271 304.597373614 304.597565549 304.597759886 304.597956434 304.598154999 304.598355384 304.598557393 304.598760825 304.59896548 304.599171156 304.599377651 304.599584759 304.599792277 304.6 304.600207723 304.600415241 304.600622349 304.600828844 304.60103452 304.601239175 304.601442607 304.601644616 304.601845001 304.602043566 304.602240114 304.602434451 304.602626386 304.602815729 304.603002293 304.603185894 304.603366351 304.603543486 304.603717124 304.603887094 304.604053227 304.60421536 304.604373334 304.604526991 304.604676181 304.604820756 304.604960574 304.605095495 304.605225389 304.605350125 304.605469582 304.605583641 304.605692189 304.60579512 304.605892332 304.605983729 304.60606922 304.606148722 304.606222156 304.60628945 304.606350536 304.606405355 304.606453853 304.606495982 304.6065317 304.606560972 304.606583769 304.606600069 304.606609855 304.606613119 304.40637424 304.406371095 304.406361662 304.406345951 304.406323977 304.406295762 304.406261335 304.406220728 304.406173981 304.406121142 304.406062262 304.4059974 304.405926618 304.405849988 304.405767585 304.405679489 304.405585789 304.405486576 304.405381949 304.40527201 304.405156868 304.405036638 304.404911436 304.404781388 304.404646621 304.404507268 304.404363467 304.40421536 304.404063093 304.403906817 304.403746684 304.403582854 304.403415489 304.403244752 304.403070813 304.402893844 304.402714019 304.402531516 304.402346514 304.402159197 304.401969748 304.401778356 304.401585209 304.401390497 304.401194413 304.400997151 304.400798904 304.400599869 304.400400242 304.40020022 304.4 304.39979978 304.399599758 304.399400131 304.399201096 304.399002849 304.398805587 304.398609503 304.398414791 304.398221644 304.398030252 304.397840803 304.397653486 304.397468484 304.397285981 304.397106156 304.396929187 304.396755248 304.396584511 304.396417146 304.396253316 304.396093183 304.395936907 304.39578464 304.395636533 304.395492732 304.395353379 304.395218612 304.395088564 304.394963362 304.394843132 304.39472799 304.394618051 304.394513424 304.394414211 304.394320511 304.394232415 304.394150012 304.394073382 304.3940026 304.393937738 304.393878858 304.393826019 304.393779272 304.393738665 304.393704238 304.393676023 304.393654049 304.393638338 304.393628905 304.39362576 304.393628905 304.393638338 304.393654049 304.393676023 304.393704238 304.393738665 304.393779272 304.393826019 304.393878858 304.393937738 304.3940026 304.394073382 304.394150012 304.394232415 304.394320511 304.394414211 304.394513424 304.394618051 304.39472799 304.394843132 304.394963362 304.395088564 304.395218612 304.395353379 304.395492732 304.395636533 304.39578464 304.395936907 304.396093183 304.396253316 304.396417146 304.396584511 304.396755248 304.396929187 304.397106156 304.397285981 304.397468484 304.397653486 304.397840803 304.398030252 304.398221644 304.398414791 304.398609503 304.398805587 304.399002849 304.399201096 304.399400131 304.399599758 304.39979978 304.4 304.40020022 304.400400242 304.400599869 304.400798904 304.400997151 304.401194413 304.401390497 304.401585209 304.401778356 304.401969748 304.402159197 304.402346514 304.402531516 304.402714019 304.402893844 304.403070813 304.403244752 304.403415489 304.403582854 304.403746684 304.403906817 304.404063093 304.40421536 304.404363467 304.404507268 304.404646621 304.404781388 304.404911436 304.405036638 304.405156868 304.40527201 304.405381949 304.405486576 304.405585789 304.405679489 304.405767585 304.405849988 304.405926618 304.4059974 304.406062262 304.406121142 304.406173981 304.406220728 304.406261335 304.406295762 304.406323977 304.406345951 304.406361662 304.406371095 304.40637424 304.206129071 304.206126046 304.206116976 304.20610187 304.206080741 304.206053612 304.206020508 304.205981463 304.205936515 304.205885708 304.205829092 304.205766725 304.205698666 304.205624983 304.205545749 304.205461042 304.205370945 304.205275549 304.205174945 304.205069235 304.204958522 304.204842916 304.20472253 304.204597484 304.2044679 304.204333907 304.204195637 304.204053227 304.203906817 304.203756551 304.203602577 304.203445049 304.20328412 304.203119951 304.202952702 304.20278254 304.202609631 304.202434147 304.202256261 304.202076149 304.201893987 304.201709956 304.201524238 304.201337015 304.201148473 304.200958798 304.200768176 304.200576796 304.200384848 304.200192519 304.2 304.199807481 304.199615152 304.199423204 304.199231824 304.199041202 304.198851527 304.198662985 304.198475762 304.198290044 304.198106013 304.197923851 304.197743739 304.197565853 304.197390369 304.19721746 304.197047298 304.196880049 304.19671588 304.196554951 304.196397423 304.196243449 304.196093183 304.195946773 304.195804363 304.195666093 304.1955321 304.195402516 304.19527747 304.195157084 304.195041478 304.194930765 304.194825055 304.194724451 304.194629055 304.194538958 304.194454251 304.194375017 304.194301334 304.194233275 304.194170908 304.194114292 304.194063485 304.194018537 304.193979492 304.193946388 304.193919259 304.19389813 304.193883024 304.193873954 304.193870929 304.193873954 304.193883024 304.19389813 304.193919259 304.193946388 304.193979492 304.194018537 304.194063485 304.194114292 304.194170908 304.194233275 304.194301334 304.194375017 304.194454251 304.194538958 304.194629055 304.194724451 304.194825055 304.194930765 304.195041478 304.195157084 304.19527747 304.195402516 304.1955321 304.195666093 304.195804363 304.195946773 304.196093183 304.196243449 304.196397423 304.196554951 304.19671588 304.196880049 304.197047298 304.19721746 304.197390369 304.197565853 304.197743739 304.197923851 304.198106013 304.198290044 304.198475762 304.198662985 304.198851527 304.199041202 304.199231824 304.199423204 304.199615152 304.199807481 304.2 304.200192519 304.200384848 304.200576796 304.200768176 304.200958798 304.201148473 304.201337015 304.201524238 304.201709956 304.201893987 304.202076149 304.202256261 304.202434147 304.202609631 304.20278254 304.202952702 304.203119951 304.20328412 304.203445049 304.203602577 304.203756551 304.203906817 304.204053227 304.204195637 304.204333907 304.2044679 304.204597484 304.20472253 304.204842916 304.204958522 304.205069235 304.205174945 304.205275549 304.205370945 304.205461042 304.205545749 304.205624983 304.205698666 304.205766725 304.205829092 304.205885708 304.205936515 304.205981463 304.206020508 304.206053612 304.206080741 304.20610187 304.206116976 304.206126046 304.206129071 304.005877853 304.005874952 304.005866254 304.005851766 304.005831504 304.005805486 304.00577374 304.005736295 304.005693189 304.005644465 304.00559017 304.005530358 304.005465089 304.005394426 304.00531844 304.005237205 304.005150801 304.005059315 304.004962835 304.004861458 304.004755283 304.004644415 304.004528963 304.004409042 304.00428477 304.004156269 304.004023667 304.003887094 304.003746684 304.003602577 304.003454915 304.003303843 304.003149511 304.00299207 304.002831677 304.002668489 304.002502668 304.002334377 304.002163782 304.001991052 304.001816356 304.001639869 304.001461762 304.001282214 304.0011014 304.000919499 304.00073669 304.000553155 304.000369073 304.000184628 304 303.999815372 303.999630927 303.999446845 303.99926331 303.999080501 303.9988986 303.998717786 303.998538238 303.998360131 303.998183644 303.998008948 303.997836218 303.997665623 303.997497332 303.997331511 303.997168323 303.99700793 303.996850489 303.996696157 303.996545085 303.996397423 303.996253316 303.996112906 303.995976333 303.995843731 303.99571523 303.995590958 303.995471037 303.995355585 303.995244717 303.995138542 303.995037165 303.994940685 303.994849199 303.994762795 303.99468156 303.994605574 303.994534911 303.994469642 303.99440983 303.994355535 303.994306811 303.994263705 303.99422626 303.994194514 303.994168496 303.994148234 303.994133746 303.994125048 303.994122147 303.994125048 303.994133746 303.994148234 303.994168496 303.994194514 303.99422626 303.994263705 303.994306811 303.994355535 303.99440983 303.994469642 303.994534911 303.994605574 303.99468156 303.994762795 303.994849199 303.994940685 303.995037165 303.995138542 303.995244717 303.995355585 303.995471037 303.995590958 303.99571523 303.995843731 303.995976333 303.996112906 303.996253316 303.996397423 303.996545085 303.996696157 303.996850489 303.99700793 303.997168323 303.997331511 303.997497332 303.997665623 303.997836218 303.998008948 303.998183644 303.998360131 303.998538238 303.998717786 303.9988986 303.999080501 303.99926331 303.999446845 303.999630927 303.999815372 304 304.000184628 304.000369073 304.000553155 304.00073669 304.000919499 304.0011014 304.001282214 304.001461762 304.001639869 304.001816356 304.001991052 304.002163782 304.002334377 304.002502668 304.002668489 304.002831677 304.00299207 304.003149511 304.003303843 304.003454915 304.003602577 304.003746684 304.003887094 304.004023667 304.004156269 304.00428477 304.004409042 304.004528963 304.004644415 304.004755283 304.004861458 304.004962835 304.005059315 304.005150801 304.005237205 304.00531844 304.005394426 304.005465089 304.005530358 304.00559017 304.005644465 304.005693189 304.005736295 304.00577374 304.005805486 304.005831504 304.005851766 304.005866254 304.005874952 304.005877853 303.805620834 303.80561806 303.805609742 303.805595888 303.805576512 303.805551632 303.805521273 303.805485466 303.805444245 303.805397651 303.805345731 303.805288534 303.805226119 303.805158546 303.805085882 303.8050082 303.804925574 303.804838088 303.804745827 303.804648882 303.80454735 303.80444133 303.804330927 303.80421625 303.804097411 303.80397453 303.803847725 303.803717124 303.803582854 303.803445049 303.803303843 303.803159377 303.803011793 303.802861237 303.802707857 303.802551805 303.802393235 303.802232302 303.802069167 303.80190399 303.801736933 303.801568163 303.801397845 303.801226147 303.801053239 303.800879292 303.800704477 303.800528967 303.800352935 303.800176555 303.8 303.799823445 303.799647065 303.799471033 303.799295523 303.799120708 303.798946761 303.798773853 303.798602155 303.798431837 303.798263067 303.79809601 303.797930833 303.797767698 303.797606765 303.797448195 303.797292143 303.797138763 303.796988207 303.796840623 303.796696157 303.796554951 303.796417146 303.796282876 303.796152275 303.79602547 303.795902589 303.79578375 303.795669073 303.79555867 303.79545265 303.795351118 303.795254173 303.795161912 303.795074426 303.7949918 303.794914118 303.794841454 303.794773881 303.794711466 303.794654269 303.794602349 303.794555755 303.794514534 303.794478727 303.794448368 303.794423488 303.794404112 303.794390258 303.79438194 303.794379166 303.79438194 303.794390258 303.794404112 303.794423488 303.794448368 303.794478727 303.794514534 303.794555755 303.794602349 303.794654269 303.794711466 303.794773881 303.794841454 303.794914118 303.7949918 303.795074426 303.795161912 303.795254173 303.795351118 303.79545265 303.79555867 303.795669073 303.79578375 303.795902589 303.79602547 303.796152275 303.796282876 303.796417146 303.796554951 303.796696157 303.796840623 303.796988207 303.797138763 303.797292143 303.797448195 303.797606765 303.797767698 303.797930833 303.79809601 303.798263067 303.798431837 303.798602155 303.798773853 303.798946761 303.799120708 303.799295523 303.799471033 303.799647065 303.799823445 303.8 303.800176555 303.800352935 303.800528967 303.800704477 303.800879292 303.801053239 303.801226147 303.801397845 303.801568163 303.801736933 303.80190399 303.802069167 303.802232302 303.802393235 303.802551805 303.802707857 303.802861237 303.803011793 303.803159377 303.803303843 303.803445049 303.803582854 303.803717124 303.803847725 303.80397453 303.804097411 303.80421625 303.804330927 303.80444133 303.80454735 303.804648882 303.804745827 303.804838088 303.804925574 303.8050082 303.805085882 303.805158546 303.805226119 303.805288534 303.805345731 303.805397651 303.805444245 303.805485466 303.805521273 303.805551632 303.805576512 303.805595888 303.805609742 303.80561806 303.805620834 303.605358268 303.605355624 303.605347695 303.605334488 303.605316016 303.605292299 303.605263358 303.605229224 303.605189928 303.605145511 303.605096016 303.605041491 303.604981992 303.604917575 303.604848306 303.604774252 303.604695486 303.604612086 303.604524135 303.604431719 303.60433493 303.604233862 303.604128616 303.604019296 303.603906009 303.603788868 303.603667987 303.603543486 303.603415489 303.60328412 303.603149511 303.603011793 303.602871104 303.60272758 303.602581365 303.602432603 303.60228144 303.602128025 303.60197251 303.601815049 303.601655796 303.601494909 303.601332547 303.60116887 303.601004039 303.600838218 303.600671569 303.600504258 303.600336448 303.600168307 303.6 303.599831693 303.599663552 303.599495742 303.599328431 303.599161782 303.598995961 303.59883113 303.598667453 303.598505091 303.598344204 303.598184951 303.59802749 303.597871975 303.59771856 303.597567397 303.597418635 303.59727242 303.597128896 303.596988207 303.596850489 303.59671588 303.596584511 303.596456514 303.596332013 303.596211132 303.596093991 303.595980704 303.595871384 303.595766138 303.59566507 303.595568281 303.595475865 303.595387914 303.595304514 303.595225748 303.595151694 303.595082425 303.595018008 303.594958509 303.594903984 303.594854489 303.594810072 303.594770776 303.594736642 303.594707701 303.594683984 303.594665512 303.594652305 303.594644376 303.594641732 303.594644376 303.594652305 303.594665512 303.594683984 303.594707701 303.594736642 303.594770776 303.594810072 303.594854489 303.594903984 303.594958509 303.595018008 303.595082425 303.595151694 303.595225748 303.595304514 303.595387914 303.595475865 303.595568281 303.59566507 303.595766138 303.595871384 303.595980704 303.596093991 303.596211132 303.596332013 303.596456514 303.596584511 303.59671588 303.596850489 303.596988207 303.597128896 303.59727242 303.597418635 303.597567397 303.59771856 303.597871975 303.59802749 303.598184951 303.598344204 303.598505091 303.598667453 303.59883113 303.598995961 303.599161782 303.599328431 303.599495742 303.599663552 303.599831693 303.6 303.600168307 303.600336448 303.600504258 303.600671569 303.600838218 303.601004039 303.60116887 303.601332547 303.601494909 303.601655796 303.601815049 303.60197251 303.602128025 303.60228144 303.602432603 303.602581365 303.60272758 303.602871104 303.603011793 303.603149511 303.60328412 303.603415489 303.603543486 303.603667987 303.603788868 303.603906009 303.604019296 303.604128616 303.604233862 303.60433493 303.604431719 303.604524135 303.604612086 303.604695486 303.604774252 303.604848306 303.604917575 303.604981992 303.605041491 303.605096016 303.605145511 303.605189928 303.605229224 303.605263358 303.605292299 303.605316016 303.605334488 303.605347695 303.605355624 303.605358268 303.405090414 303.405087902 303.405080369 303.405067823 303.405050275 303.405027743 303.405000249 303.404967821 303.404930489 303.404888293 303.404841272 303.404789473 303.404732947 303.404671751 303.404605944 303.404535592 303.404460764 303.404381533 303.404297979 303.404210183 303.404118232 303.404022216 303.403922232 303.403818376 303.403710752 303.403599466 303.403484628 303.403366351 303.403244752 303.403119951 303.40299207 303.402861237 303.40272758 303.402591232 303.402452326 303.402311 303.402167393 303.402021647 303.401873906 303.401724316 303.401573024 303.40142018 303.401265935 303.401110439 303.400953848 303.400796316 303.400637998 303.40047905 303.40031963 303.400159894 303.4 303.399840106 303.39968037 303.39952095 303.399362002 303.399203684 303.399046152 303.398889561 303.398734065 303.39857982 303.398426976 303.398275684 303.398126094 303.397978353 303.397832607 303.397689 303.397547674 303.397408768 303.39727242 303.397138763 303.39700793 303.396880049 303.396755248 303.396633649 303.396515372 303.396400534 303.396289248 303.396181624 303.396077768 303.395977784 303.395881768 303.395789817 303.395702021 303.395618467 303.395539236 303.395464408 303.395394056 303.395328249 303.395267053 303.395210527 303.395158728 303.395111707 303.395069511 303.395032179 303.394999751 303.394972257 303.394949725 303.394932177 303.394919631 303.394912098 303.394909586 303.394912098 303.394919631 303.394932177 303.394949725 303.394972257 303.394999751 303.395032179 303.395069511 303.395111707 303.395158728 303.395210527 303.395267053 303.395328249 303.395394056 303.395464408 303.395539236 303.395618467 303.395702021 303.395789817 303.395881768 303.395977784 303.396077768 303.396181624 303.396289248 303.396400534 303.396515372 303.396633649 303.396755248 303.396880049 303.39700793 303.397138763 303.39727242 303.397408768 303.397547674 303.397689 303.397832607 303.397978353 303.398126094 303.398275684 303.398426976 303.39857982 303.398734065 303.398889561 303.399046152 303.399203684 303.399362002 303.39952095 303.39968037 303.399840106 303.4 303.400159894 303.40031963 303.40047905 303.400637998 303.400796316 303.400953848 303.401110439 303.401265935 303.40142018 303.401573024 303.401724316 303.401873906 303.402021647 303.402167393 303.402311 303.402452326 303.402591232 303.40272758 303.402861237 303.40299207 303.403119951 303.403244752 303.403366351 303.403484628 303.403599466 303.403710752 303.403818376 303.403922232 303.404022216 303.404118232 303.404210183 303.404297979 303.404381533 303.404460764 303.404535592 303.404605944 303.404671751 303.404732947 303.404789473 303.404841272 303.404888293 303.404930489 303.404967821 303.405000249 303.405027743 303.405050275 303.405067823 303.405080369 303.405087902 303.405090414 303.204817537 303.20481516 303.20480803 303.204796156 303.204779549 303.204758225 303.204732205 303.204701515 303.204666185 303.20462625 303.20458175 303.204532728 303.204479232 303.204421317 303.204359038 303.204292457 303.20422164 303.204146656 303.204067581 303.203984491 303.203897469 303.203806601 303.203711976 303.203613688 303.203511833 303.203406513 303.203297831 303.203185894 303.203070813 303.202952702 303.202831677 303.202707857 303.202581365 303.202452326 303.202320866 303.202187116 303.202051207 303.201913275 303.201773454 303.201631882 303.201488701 303.20134405 303.201198073 303.201050913 303.200902716 303.200753629 303.200603797 303.20045337 303.200302496 303.200151322 303.2 303.199848678 303.199697504 303.19954663 303.199396203 303.199246371 303.199097284 303.198949087 303.198801927 303.19865595 303.198511299 303.198368118 303.198226546 303.198086725 303.197948793 303.197812884 303.197679134 303.197547674 303.197418635 303.197292143 303.197168323 303.197047298 303.196929187 303.196814106 303.196702169 303.196593487 303.196488167 303.196386312 303.196288024 303.196193399 303.196102531 303.196015509 303.195932419 303.195853344 303.19577836 303.195707543 303.195640962 303.195578683 303.195520768 303.195467272 303.19541825 303.19537375 303.195333815 303.195298485 303.195267795 303.195241775 303.195220451 303.195203844 303.19519197 303.19518484 303.195182463 303.19518484 303.19519197 303.195203844 303.195220451 303.195241775 303.195267795 303.195298485 303.195333815 303.19537375 303.19541825 303.195467272 303.195520768 303.195578683 303.195640962 303.195707543 303.19577836 303.195853344 303.195932419 303.196015509 303.196102531 303.196193399 303.196288024 303.196386312 303.196488167 303.196593487 303.196702169 303.196814106 303.196929187 303.197047298 303.197168323 303.197292143 303.197418635 303.197547674 303.197679134 303.197812884 303.197948793 303.198086725 303.198226546 303.198368118 303.198511299 303.19865595 303.198801927 303.198949087 303.199097284 303.199246371 303.199396203 303.19954663 303.199697504 303.199848678 303.2 303.200151322 303.200302496 303.20045337 303.200603797 303.200753629 303.200902716 303.201050913 303.201198073 303.20134405 303.201488701 303.201631882 303.201773454 303.201913275 303.202051207 303.202187116 303.202320866 303.202452326 303.202581365 303.202707857 303.202831677 303.202952702 303.203070813 303.203185894 303.203297831 303.203406513 303.203511833 303.203613688 303.203711976 303.203806601 303.203897469 303.203984491 303.204067581 303.204146656 303.20422164 303.204292457 303.204359038 303.204421317 303.204479232 303.204532728 303.20458175 303.20462625 303.204666185 303.204701515 303.204732205 303.204758225 303.204779549 303.204796156 303.20480803 303.20481516 303.204817537 303.004539905 303.004537665 303.004530947 303.004519757 303.004504106 303.004484011 303.004459491 303.004430569 303.004397276 303.004359642 303.004317706 303.004271509 303.004221097 303.004166519 303.004107829 303.004045085 303.003978349 303.003907687 303.003833169 303.003754867 303.00367286 303.003587229 303.003498057 303.003405433 303.003309448 303.003210198 303.003107779 303.003002293 303.002893844 303.00278254 303.002668489 303.002551805 303.002432603 303.002311 303.002187116 303.002061074 303.001932998 303.001803014 303.00167125 303.001537838 303.001402908 303.001266593 303.001129028 303.00099035 303.000850693 303.000710198 303.000569001 303.000427243 303.000285063 303.000142602 303 302.999857398 302.999714937 302.999572757 302.999430999 302.999289802 302.999149307 302.99900965 302.998870972 302.998733407 302.998597092 302.998462162 302.99832875 302.998196986 302.998067002 302.997938926 302.997812884 302.997689 302.997567397 302.997448195 302.997331511 302.99721746 302.997106156 302.996997707 302.996892221 302.996789802 302.996690552 302.996594567 302.996501943 302.996412771 302.99632714 302.996245133 302.996166831 302.996092313 302.996021651 302.995954915 302.995892171 302.995833481 302.995778903 302.995728491 302.995682294 302.995640358 302.995602724 302.995569431 302.995540509 302.995515989 302.995495894 302.995480243 302.995469053 302.995462335 302.995460095 302.995462335 302.995469053 302.995480243 302.995495894 302.995515989 302.995540509 302.995569431 302.995602724 302.995640358 302.995682294 302.995728491 302.995778903 302.995833481 302.995892171 302.995954915 302.996021651 302.996092313 302.996166831 302.996245133 302.99632714 302.996412771 302.996501943 302.996594567 302.996690552 302.996789802 302.996892221 302.996997707 302.997106156 302.99721746 302.997331511 302.997448195 302.997567397 302.997689 302.997812884 302.997938926 302.998067002 302.998196986 302.99832875 302.998462162 302.998597092 302.998733407 302.998870972 302.99900965 302.999149307 302.999289802 302.999430999 302.999572757 302.999714937 302.999857398 303 303.000142602 303.000285063 303.000427243 303.000569001 303.000710198 303.000850693 303.00099035 303.001129028 303.001266593 303.001402908 303.001537838 303.00167125 303.001803014 303.001932998 303.002061074 303.002187116 303.002311 303.002432603 303.002551805 303.002668489 303.00278254 303.002893844 303.003002293 303.003107779 303.003210198 303.003309448 303.003405433 303.003498057 303.003587229 303.00367286 303.003754867 303.003833169 303.003907687 303.003978349 303.004045085 303.004107829 303.004166519 303.004221097 303.004271509 303.004317706 303.004359642 303.004397276 303.004430569 303.004459491 303.004484011 303.004504106 303.004519757 303.004530947 303.004537665 303.004539905 302.804257793 302.804255692 302.804249391 302.804238897 302.804224219 302.804205372 302.804182376 302.804155251 302.804124027 302.804088732 302.804049402 302.804006075 302.803958796 302.803907609 302.803852566 302.803793721 302.803731132 302.803664861 302.803594973 302.803521538 302.803444627 302.803364316 302.803280686 302.803193818 302.803103797 302.803010714 302.80291466 302.802815729 302.802714019 302.802609631 302.802502668 302.802393235 302.80228144 302.802167393 302.802051207 302.801932998 302.80181288 302.801690973 302.801567398 302.801442276 302.80131573 302.801187886 302.80105887 302.800928809 302.800797831 302.800666066 302.800533643 302.800400694 302.800267349 302.800133741 302.8 302.799866259 302.799732651 302.799599306 302.799466357 302.799333934 302.799202169 302.799071191 302.79894113 302.798812114 302.79868427 302.798557724 302.798432602 302.798309027 302.79818712 302.798067002 302.797948793 302.797832607 302.79771856 302.797606765 302.797497332 302.797390369 302.797285981 302.797184271 302.79708534 302.796989286 302.796896203 302.796806182 302.796719314 302.796635684 302.796555373 302.796478462 302.796405027 302.796335139 302.796268868 302.796206279 302.796147434 302.796092391 302.796041204 302.795993925 302.795950598 302.795911268 302.795875973 302.795844749 302.795817624 302.795794628 302.795775781 302.795761103 302.795750609 302.795744308 302.795742207 302.795744308 302.795750609 302.795761103 302.795775781 302.795794628 302.795817624 302.795844749 302.795875973 302.795911268 302.795950598 302.795993925 302.796041204 302.796092391 302.796147434 302.796206279 302.796268868 302.796335139 302.796405027 302.796478462 302.796555373 302.796635684 302.796719314 302.796806182 302.796896203 302.796989286 302.79708534 302.797184271 302.797285981 302.797390369 302.797497332 302.797606765 302.79771856 302.797832607 302.797948793 302.798067002 302.79818712 302.798309027 302.798432602 302.798557724 302.79868427 302.798812114 302.79894113 302.799071191 302.799202169 302.799333934 302.799466357 302.799599306 302.799732651 302.799866259 302.8 302.800133741 302.800267349 302.800400694 302.800533643 302.800666066 302.800797831 302.800928809 302.80105887 302.801187886 302.80131573 302.801442276 302.801567398 302.801690973 302.80181288 302.801932998 302.802051207 302.802167393 302.80228144 302.802393235 302.802502668 302.802609631 302.802714019 302.802815729 302.80291466 302.803010714 302.803103797 302.803193818 302.803280686 302.803364316 302.803444627 302.803521538 302.803594973 302.803664861 302.803731132 302.803793721 302.803852566 302.803907609 302.803958796 302.804006075 302.804049402 302.804088732 302.804124027 302.804155251 302.804182376 302.804205372 302.804224219 302.804238897 302.804249391 302.804255692 302.804257793 302.603971479 302.603969519 302.603963642 302.603953853 302.603940163 302.603922583 302.603901133 302.603875833 302.603846708 302.603813786 302.603777101 302.603736688 302.603692588 302.603644843 302.603593502 302.603538614 302.603480233 302.603418419 302.603353231 302.603284733 302.603212994 302.603138084 302.603060077 302.60297905 302.602895084 302.60280826 302.602718664 302.602626386 302.602531516 302.602434147 302.602334377 302.602232302 302.602128025 302.602021647 302.601913275 302.601803014 302.601690973 302.601577264 302.601461999 302.601345291 302.601227254 302.601108007 302.600987667 302.600866351 302.600744181 302.600621276 302.600497758 302.600373749 302.600249371 302.600124747 302.6 302.599875253 302.599750629 302.599626251 302.599502242 302.599378724 302.599255819 302.599133649 302.599012333 302.598891993 302.598772746 302.598654709 302.598538001 302.598422736 302.598309027 302.598196986 302.598086725 302.597978353 302.597871975 302.597767698 302.597665623 302.597565853 302.597468484 302.597373614 302.597281336 302.59719174 302.597104916 302.59702095 302.596939923 302.596861916 302.596787006 302.596715267 302.596646769 302.596581581 302.596519767 302.596461386 302.596406498 302.596355157 302.596307412 302.596263312 302.596222899 302.596186214 302.596153292 302.596124167 302.596098867 302.596077417 302.596059837 302.596046147 302.596036358 302.596030481 302.596028521 302.596030481 302.596036358 302.596046147 302.596059837 302.596077417 302.596098867 302.596124167 302.596153292 302.596186214 302.596222899 302.596263312 302.596307412 302.596355157 302.596406498 302.596461386 302.596519767 302.596581581 302.596646769 302.596715267 302.596787006 302.596861916 302.596939923 302.59702095 302.597104916 302.59719174 302.597281336 302.597373614 302.597468484 302.597565853 302.597665623 302.597767698 302.597871975 302.597978353 302.598086725 302.598196986 302.598309027 302.598422736 302.598538001 302.598654709 302.598772746 302.598891993 302.599012333 302.599133649 302.599255819 302.599378724 302.599502242 302.599626251 302.599750629 302.599875253 302.6 302.600124747 302.600249371 302.600373749 302.600497758 302.600621276 302.600744181 302.600866351 302.600987667 302.601108007 302.601227254 302.601345291 302.601461999 302.601577264 302.601690973 302.601803014 302.601913275 302.602021647 302.602128025 302.602232302 302.602334377 302.602434147 302.602531516 302.602626386 302.602718664 302.60280826 302.602895084 302.60297905 302.603060077 302.603138084 302.603212994 302.603284733 302.603353231 302.603418419 302.603480233 302.603538614 302.603593502 302.603644843 302.603692588 302.603736688 302.603777101 302.603813786 302.603846708 302.603875833 302.603901133 302.603922583 302.603940163 302.603953853 302.603963642 302.603969519 302.603971479 302.403681246 302.403679429 302.403673981 302.403664908 302.403652218 302.403635923 302.403616041 302.403592589 302.403565592 302.403535077 302.403501073 302.403463613 302.403422736 302.40337848 302.403330891 302.403280014 302.4032259 302.403168603 302.403108178 302.403044687 302.40297819 302.402908755 302.402836448 302.402761343 302.402683512 302.402603034 302.402519986 302.402434451 302.402346514 302.402256261 302.402163782 302.402069167 302.40197251 302.401873906 302.401773454 302.40167125 302.401567398 302.401461999 302.401355157 302.401246977 302.401137567 302.401027035 302.400915489 302.400803039 302.400689797 302.400575874 302.400461382 302.400346436 302.400231147 302.400115631 302.4 302.399884369 302.399768853 302.399653564 302.399538618 302.399424126 302.399310203 302.399196961 302.399084511 302.398972965 302.398862433 302.398753023 302.398644843 302.398538001 302.398432602 302.39832875 302.398226546 302.398126094 302.39802749 302.397930833 302.397836218 302.397743739 302.397653486 302.397565549 302.397480014 302.397396966 302.397316488 302.397238657 302.397163552 302.397091245 302.39702181 302.396955313 302.396891822 302.396831397 302.3967741 302.396719986 302.396669109 302.39662152 302.396577264 302.396536387 302.396498927 302.396464923 302.396434408 302.396407411 302.396383959 302.396364077 302.396347782 302.396335092 302.396326019 302.396320571 302.396318754 302.396320571 302.396326019 302.396335092 302.396347782 302.396364077 302.396383959 302.396407411 302.396434408 302.396464923 302.396498927 302.396536387 302.396577264 302.39662152 302.396669109 302.396719986 302.3967741 302.396831397 302.396891822 302.396955313 302.39702181 302.397091245 302.397163552 302.397238657 302.397316488 302.397396966 302.397480014 302.397565549 302.397653486 302.397743739 302.397836218 302.397930833 302.39802749 302.398126094 302.398226546 302.39832875 302.398432602 302.398538001 302.398644843 302.398753023 302.398862433 302.398972965 302.399084511 302.399196961 302.399310203 302.399424126 302.399538618 302.399653564 302.399768853 302.399884369 302.4 302.400115631 302.400231147 302.400346436 302.400461382 302.400575874 302.400689797 302.400803039 302.400915489 302.401027035 302.401137567 302.401246977 302.401355157 302.401461999 302.401567398 302.40167125 302.401773454 302.401873906 302.40197251 302.402069167 302.402163782 302.402256261 302.402346514 302.402434451 302.402519986 302.402603034 302.402683512 302.402761343 302.402836448 302.402908755 302.40297819 302.403044687 302.403108178 302.403168603 302.4032259 302.403280014 302.403330891 302.40337848 302.403422736 302.403463613 302.403501073 302.403535077 302.403565592 302.403592589 302.403616041 302.403635923 302.403652218 302.403664908 302.403673981 302.403679429 302.403681246 302.203387379 302.203385708 302.203380695 302.203372346 302.203360669 302.203345675 302.203327379 302.2033058 302.203280958 302.203252879 302.203221589 302.20318712 302.203149506 302.203108783 302.203064992 302.203018177 302.202968383 302.20291566 302.202860059 302.202801636 302.202740447 302.202676555 302.202610021 302.202540911 302.202469293 302.202395239 302.202318821 302.202240114 302.202159197 302.202076149 302.201991052 302.20190399 302.201815049 302.201724316 302.201631882 302.201537838 302.201442276 302.201345291 302.201246977 302.201147434 302.201046758 302.200945049 302.200842407 302.200738934 302.200634732 302.200529903 302.200424551 302.200318781 302.200212695 302.2001064 302.2 302.1998936 302.199787305 302.199681219 302.199575449 302.199470097 302.199365268 302.199261066 302.199157593 302.199054951 302.198953242 302.198852566 302.198753023 302.198654709 302.198557724 302.198462162 302.198368118 302.198275684 302.198184951 302.19809601 302.198008948 302.197923851 302.197840803 302.197759886 302.197681179 302.197604761 302.197530707 302.197459089 302.197389979 302.197323445 302.197259553 302.197198364 302.197139941 302.19708434 302.197031617 302.196981823 302.196935008 302.196891217 302.196850494 302.19681288 302.196778411 302.196747121 302.196719042 302.1966942 302.196672621 302.196654325 302.196639331 302.196627654 302.196619305 302.196614292 302.196612621 302.196614292 302.196619305 302.196627654 302.196639331 302.196654325 302.196672621 302.1966942 302.196719042 302.196747121 302.196778411 302.19681288 302.196850494 302.196891217 302.196935008 302.196981823 302.197031617 302.19708434 302.197139941 302.197198364 302.197259553 302.197323445 302.197389979 302.197459089 302.197530707 302.197604761 302.197681179 302.197759886 302.197840803 302.197923851 302.198008948 302.19809601 302.198184951 302.198275684 302.198368118 302.198462162 302.198557724 302.198654709 302.198753023 302.198852566 302.198953242 302.199054951 302.199157593 302.199261066 302.199365268 302.199470097 302.199575449 302.199681219 302.199787305 302.1998936 302.2 302.2001064 302.200212695 302.200318781 302.200424551 302.200529903 302.200634732 302.200738934 302.200842407 302.200945049 302.201046758 302.201147434 302.201246977 302.201345291 302.201442276 302.201537838 302.201631882 302.201724316 302.201815049 302.20190399 302.201991052 302.202076149 302.202159197 302.202240114 302.202318821 302.202395239 302.202469293 302.202540911 302.202610021 302.202676555 302.202740447 302.202801636 302.202860059 302.20291566 302.202968383 302.203018177 302.203064992 302.203108783 302.203149506 302.20318712 302.203221589 302.203252879 302.203280958 302.2033058 302.203327379 302.203345675 302.203360669 302.203372346 302.203380695 302.203385708 302.203387379 302.00309017 302.003088645 302.003084072 302.003076456 302.003065803 302.003052125 302.003035435 302.003015749 302.002993087 302.002967471 302.002938926 302.002907481 302.002873167 302.002836018 302.002796069 302.002753362 302.002707937 302.002659839 302.002609117 302.00255582 302.0025 302.002441713 302.002381017 302.002317971 302.002252637 302.00218508 302.002115367 302.002043566 302.001969748 302.001893987 302.001816356 302.001736933 302.001655796 302.001573024 302.001488701 302.001402908 302.00131573 302.001227254 302.001137567 302.001046758 302.000954915 302.00086213 302.000768494 302.0006741 302.00057904 302.000483409 302.000387301 302.000290811 302.000194033 302.000097065 302 301.999902935 301.999805967 301.999709189 301.999612699 301.999516591 301.99942096 301.9993259 301.999231506 301.99913787 301.999045085 301.998953242 301.998862433 301.998772746 301.99868427 301.998597092 301.998511299 301.998426976 301.998344204 301.998263067 301.998183644 301.998106013 301.998030252 301.997956434 301.997884633 301.99781492 301.997747363 301.997682029 301.997618983 301.997558287 301.9975 301.99744418 301.997390883 301.997340161 301.997292063 301.997246638 301.997203931 301.997163982 301.997126833 301.997092519 301.997061074 301.997032529 301.997006913 301.996984251 301.996964565 301.996947875 301.996934197 301.996923544 301.996915928 301.996911355 301.99690983 301.996911355 301.996915928 301.996923544 301.996934197 301.996947875 301.996964565 301.996984251 301.997006913 301.997032529 301.997061074 301.997092519 301.997126833 301.997163982 301.997203931 301.997246638 301.997292063 301.997340161 301.997390883 301.99744418 301.9975 301.997558287 301.997618983 301.997682029 301.997747363 301.99781492 301.997884633 301.997956434 301.998030252 301.998106013 301.998183644 301.998263067 301.998344204 301.998426976 301.998511299 301.998597092 301.99868427 301.998772746 301.998862433 301.998953242 301.999045085 301.99913787 301.999231506 301.9993259 301.99942096 301.999516591 301.999612699 301.999709189 301.999805967 301.999902935 302 302.000097065 302.000194033 302.000290811 302.000387301 302.000483409 302.00057904 302.0006741 302.000768494 302.00086213 302.000954915 302.001046758 302.001137567 302.001227254 302.00131573 302.001402908 302.001488701 302.001573024 302.001655796 302.001736933 302.001816356 302.001893987 302.001969748 302.002043566 302.002115367 302.00218508 302.002252637 302.002317971 302.002381017 302.002441713 302.0025 302.00255582 302.002609117 302.002659839 302.002707937 302.002753362 302.002796069 302.002836018 302.002873167 302.002907481 302.002938926 302.002967471 302.002993087 302.003015749 302.003035435 302.003052125 302.003065803 302.003076456 302.003084072 302.003088645 302.00309017 301.802789911 301.802788534 301.802784406 301.802777529 301.802767912 301.802755563 301.802740494 301.802722721 301.802702261 301.802679134 301.802653363 301.802624974 301.802593994 301.802560454 301.802524387 301.802485829 301.802444818 301.802401394 301.8023556 301.802307481 301.802257085 301.802204462 301.802149663 301.802092743 301.802033758 301.801972765 301.801909826 301.801845001 301.801778356 301.801709956 301.801639869 301.801568163 301.801494909 301.80142018 301.80134405 301.801266593 301.801187886 301.801108007 301.801027035 301.800945049 301.80086213 301.80077836 301.800693823 301.8006086 301.800522777 301.800436438 301.800349669 301.800262554 301.80017518 301.800087633 301.8 301.799912367 301.79982482 301.799737446 301.799650331 301.799563562 301.799477223 301.7993914 301.799306177 301.79922164 301.79913787 301.799054951 301.798972965 301.798891993 301.798812114 301.798733407 301.79865595 301.79857982 301.798505091 301.798431837 301.798360131 301.798290044 301.798221644 301.798154999 301.798090174 301.798027235 301.797966242 301.797907257 301.797850337 301.797795538 301.797742915 301.797692519 301.7976444 301.797598606 301.797555182 301.797514171 301.797475613 301.797439546 301.797406006 301.797375026 301.797346637 301.797320866 301.797297739 301.797277279 301.797259506 301.797244437 301.797232088 301.797222471 301.797215594 301.797211466 301.797210089 301.797211466 301.797215594 301.797222471 301.797232088 301.797244437 301.797259506 301.797277279 301.797297739 301.797320866 301.797346637 301.797375026 301.797406006 301.797439546 301.797475613 301.797514171 301.797555182 301.797598606 301.7976444 301.797692519 301.797742915 301.797795538 301.797850337 301.797907257 301.797966242 301.798027235 301.798090174 301.798154999 301.798221644 301.798290044 301.798360131 301.798431837 301.798505091 301.79857982 301.79865595 301.798733407 301.798812114 301.798891993 301.798972965 301.799054951 301.79913787 301.79922164 301.799306177 301.7993914 301.799477223 301.799563562 301.799650331 301.799737446 301.79982482 301.799912367 301.8 301.800087633 301.80017518 301.800262554 301.800349669 301.800436438 301.800522777 301.8006086 301.800693823 301.80077836 301.80086213 301.800945049 301.801027035 301.801108007 301.801187886 301.801266593 301.80134405 301.80142018 301.801494909 301.801568163 301.801639869 301.801709956 301.801778356 301.801845001 301.801909826 301.801972765 301.802033758 301.802092743 301.802149663 301.802204462 301.802257085 301.802307481 301.8023556 301.802401394 301.802444818 301.802485829 301.802524387 301.802560454 301.802593994 301.802624974 301.802653363 301.802679134 301.802702261 301.802722721 301.802740494 301.802755563 301.802767912 301.802777529 301.802784406 301.802788534 301.802789911 301.602486899 301.602485672 301.602481992 301.602475862 301.602467289 301.602456281 301.602442849 301.602427006 301.602408768 301.602388153 301.602365181 301.602339875 301.60231226 301.602282363 301.602250213 301.602215843 301.602179286 301.602140578 301.602099758 301.602056866 301.602011943 301.601965036 301.601916189 301.60186545 301.601812871 301.601758503 301.601702399 301.601644616 301.601585209 301.601524238 301.601461762 301.601397845 301.601332547 301.601265935 301.601198073 301.601129028 301.60105887 301.600987667 301.600915489 301.600842407 301.600768494 301.600693823 301.600618467 301.6005425 301.600465998 301.600389037 301.600311691 301.600234038 301.600156154 301.600078115 301.6 301.599921885 301.599843846 301.599765962 301.599688309 301.599610963 301.599534002 301.5994575 301.599381533 301.599306177 301.599231506 301.599157593 301.599084511 301.599012333 301.59894113 301.598870972 301.598801927 301.598734065 301.598667453 301.598602155 301.598538238 301.598475762 301.598414791 301.598355384 301.598297601 301.598241497 301.598187129 301.59813455 301.598083811 301.598034964 301.597988057 301.597943134 301.597900242 301.597859422 301.597820714 301.597784157 301.597749787 301.597717637 301.59768774 301.597660125 301.597634819 301.597611847 301.597591232 301.597572994 301.597557151 301.597543719 301.597532711 301.597524138 301.597518008 301.597514328 301.597513101 301.597514328 301.597518008 301.597524138 301.597532711 301.597543719 301.597557151 301.597572994 301.597591232 301.597611847 301.597634819 301.597660125 301.59768774 301.597717637 301.597749787 301.597784157 301.597820714 301.597859422 301.597900242 301.597943134 301.597988057 301.598034964 301.598083811 301.59813455 301.598187129 301.598241497 301.598297601 301.598355384 301.598414791 301.598475762 301.598538238 301.598602155 301.598667453 301.598734065 301.598801927 301.598870972 301.59894113 301.599012333 301.599084511 301.599157593 301.599231506 301.599306177 301.599381533 301.5994575 301.599534002 301.599610963 301.599688309 301.599765962 301.599843846 301.599921885 301.6 301.600078115 301.600156154 301.600234038 301.600311691 301.600389037 301.600465998 301.6005425 301.600618467 301.600693823 301.600768494 301.600842407 301.600915489 301.600987667 301.60105887 301.601129028 301.601198073 301.601265935 301.601332547 301.601397845 301.601461762 301.601524238 301.601585209 301.601644616 301.601702399 301.601758503 301.601812871 301.60186545 301.601916189 301.601965036 301.602011943 301.602056866 301.602099758 301.602140578 301.602179286 301.602215843 301.602250213 301.602282363 301.60231226 301.602339875 301.602365181 301.602388153 301.602408768 301.602427006 301.602442849 301.602456281 301.602467289 301.602475862 301.602481992 301.602485672 301.602486899 301.402181432 301.402180356 301.402177128 301.402171751 301.402164231 301.402154575 301.402142793 301.402128896 301.402112899 301.402094816 301.402074666 301.402052468 301.402028245 301.40200202 301.401973819 301.401943671 301.401911604 301.401877651 301.401841844 301.40180422 301.401764816 301.40172367 301.401680823 301.401636317 301.401590196 301.401542506 301.401493293 301.401442607 301.401390497 301.401337015 301.401282214 301.401226147 301.40116887 301.401110439 301.401050913 301.40099035 301.400928809 301.400866351 301.400803039 301.400738934 301.4006741 301.4006086 301.4005425 301.400475865 301.40040876 301.400341251 301.400273406 301.400205291 301.400136973 301.40006852 301.4 301.39993148 301.399863027 301.399794709 301.399726594 301.399658749 301.39959124 301.399524135 301.3994575 301.3993914 301.3993259 301.399261066 301.399196961 301.399133649 301.399071191 301.39900965 301.398949087 301.398889561 301.39883113 301.398773853 301.398717786 301.398662985 301.398609503 301.398557393 301.398506707 301.398457494 301.398409804 301.398363683 301.398319177 301.39827633 301.398235184 301.39819578 301.398158156 301.398122349 301.398088396 301.398056329 301.398026181 301.39799798 301.397971755 301.397947532 301.397925334 301.397905184 301.397887101 301.397871104 301.397857207 301.397845425 301.397835769 301.397828249 301.397822872 301.397819644 301.397818568 301.397819644 301.397822872 301.397828249 301.397835769 301.397845425 301.397857207 301.397871104 301.397887101 301.397905184 301.397925334 301.397947532 301.397971755 301.39799798 301.398026181 301.398056329 301.398088396 301.398122349 301.398158156 301.39819578 301.398235184 301.39827633 301.398319177 301.398363683 301.398409804 301.398457494 301.398506707 301.398557393 301.398609503 301.398662985 301.398717786 301.398773853 301.39883113 301.398889561 301.398949087 301.39900965 301.399071191 301.399133649 301.399196961 301.399261066 301.3993259 301.3993914 301.3994575 301.399524135 301.39959124 301.399658749 301.399726594 301.399794709 301.399863027 301.39993148 301.4 301.40006852 301.400136973 301.400205291 301.400273406 301.400341251 301.40040876 301.400475865 301.4005425 301.4006086 301.4006741 301.400738934 301.400803039 301.400866351 301.400928809 301.40099035 301.401050913 301.401110439 301.40116887 301.401226147 301.401282214 301.401337015 301.401390497 301.401442607 301.401493293 301.401542506 301.401590196 301.401636317 301.401680823 301.40172367 301.401764816 301.40180422 301.401841844 301.401877651 301.401911604 301.401943671 301.401973819 301.40200202 301.402028245 301.402052468 301.402074666 301.402094816 301.402112899 301.402128896 301.402142793 301.402154575 301.402164231 301.402171751 301.402177128 301.402180356 301.402181432 301.201873813 301.201872889 301.201870116 301.201865497 301.201859038 301.201850743 301.201840623 301.201828686 301.201814944 301.201799411 301.201782102 301.201763035 301.201742227 301.201719701 301.201695477 301.20166958 301.201642035 301.20161287 301.201582113 301.201549794 301.201515947 301.201480603 301.201443798 301.201405568 301.201365951 301.201324986 301.201282713 301.201239175 301.201194413 301.201148473 301.2011014 301.201053239 301.201004039 301.200953848 301.200902716 301.200850693 301.200797831 301.200744181 301.200689797 301.200634732 301.20057904 301.200522777 301.200465998 301.20040876 301.200351118 301.200293129 301.200234851 301.200176341 301.200117658 301.200058858 301.2 301.199941142 301.199882342 301.199823659 301.199765149 301.199706871 301.199648882 301.19959124 301.199534002 301.199477223 301.19942096 301.199365268 301.199310203 301.199255819 301.199202169 301.199149307 301.199097284 301.199046152 301.198995961 301.198946761 301.1988986 301.198851527 301.198805587 301.198760825 301.198717287 301.198675014 301.198634049 301.198594432 301.198556202 301.198519397 301.198484053 301.198450206 301.198417887 301.19838713 301.198357965 301.19833042 301.198304523 301.198280299 301.198257773 301.198236965 301.198217898 301.198200589 301.198185056 301.198171314 301.198159377 301.198149257 301.198140962 301.198134503 301.198129884 301.198127111 301.198126187 301.198127111 301.198129884 301.198134503 301.198140962 301.198149257 301.198159377 301.198171314 301.198185056 301.198200589 301.198217898 301.198236965 301.198257773 301.198280299 301.198304523 301.19833042 301.198357965 301.19838713 301.198417887 301.198450206 301.198484053 301.198519397 301.198556202 301.198594432 301.198634049 301.198675014 301.198717287 301.198760825 301.198805587 301.198851527 301.1988986 301.198946761 301.198995961 301.199046152 301.199097284 301.199149307 301.199202169 301.199255819 301.199310203 301.199365268 301.19942096 301.199477223 301.199534002 301.19959124 301.199648882 301.199706871 301.199765149 301.199823659 301.199882342 301.199941142 301.2 301.200058858 301.200117658 301.200176341 301.200234851 301.200293129 301.200351118 301.20040876 301.200465998 301.200522777 301.20057904 301.200634732 301.200689797 301.200744181 301.200797831 301.200850693 301.200902716 301.200953848 301.201004039 301.201053239 301.2011014 301.201148473 301.201194413 301.201239175 301.201282713 301.201324986 301.201365951 301.201405568 301.201443798 301.201480603 301.201515947 301.201549794 301.201582113 301.20161287 301.201642035 301.20166958 301.201695477 301.201719701 301.201742227 301.201763035 301.201782102 301.201799411 301.201814944 301.201828686 301.201840623 301.201850743 301.201859038 301.201865497 301.201870116 301.201872889 301.201873813 301.001564345 301.001563573 301.001561258 301.001557402 301.001552009 301.001545085 301.001536636 301.00152667 301.001515198 301.00150223 301.00148778 301.001471862 301.001454491 301.001435685 301.001415461 301.001393841 301.001370846 301.001346497 301.00132082 301.001293839 301.001265581 301.001236075 301.001205348 301.001173432 301.001140358 301.001106159 301.001070868 301.00103452 301.000997151 301.000958798 301.000919499 301.000879292 301.000838218 301.000796316 301.000753629 301.000710198 301.000666066 301.000621276 301.000575874 301.000529903 301.000483409 301.000436438 301.000389037 301.000341251 301.000293129 301.000244717 301.000196064 301.000147218 301.000098226 301.000049137 301 300.999950863 300.999901774 300.999852782 300.999803936 300.999755283 300.999706871 300.999658749 300.999610963 300.999563562 300.999516591 300.999470097 300.999424126 300.999378724 300.999333934 300.999289802 300.999246371 300.999203684 300.999161782 300.999120708 300.999080501 300.999041202 300.999002849 300.99896548 300.998929132 300.998893841 300.998859642 300.998826568 300.998794652 300.998763925 300.998734419 300.998706161 300.99867918 300.998653503 300.998629154 300.998606159 300.998584539 300.998564315 300.998545509 300.998528138 300.99851222 300.99849777 300.998484802 300.99847333 300.998463364 300.998454915 300.998447991 300.998442598 300.998438742 300.998436427 300.998435655 300.998436427 300.998438742 300.998442598 300.998447991 300.998454915 300.998463364 300.99847333 300.998484802 300.99849777 300.99851222 300.998528138 300.998545509 300.998564315 300.998584539 300.998606159 300.998629154 300.998653503 300.99867918 300.998706161 300.998734419 300.998763925 300.998794652 300.998826568 300.998859642 300.998893841 300.998929132 300.99896548 300.999002849 300.999041202 300.999080501 300.999120708 300.999161782 300.999203684 300.999246371 300.999289802 300.999333934 300.999378724 300.999424126 300.999470097 300.999516591 300.999563562 300.999610963 300.999658749 300.999706871 300.999755283 300.999803936 300.999852782 300.999901774 300.999950863 301 301.000049137 301.000098226 301.000147218 301.000196064 301.000244717 301.000293129 301.000341251 301.000389037 301.000436438 301.000483409 301.000529903 301.000575874 301.000621276 301.000666066 301.000710198 301.000753629 301.000796316 301.000838218 301.000879292 301.000919499 301.000958798 301.000997151 301.00103452 301.001070868 301.001106159 301.001140358 301.001173432 301.001205348 301.001236075 301.001265581 301.001293839 301.00132082 301.001346497 301.001370846 301.001393841 301.001415461 301.001435685 301.001454491 301.001471862 301.00148778 301.00150223 301.001515198 301.00152667 301.001536636 301.001545085 301.001552009 301.001557402 301.001561258 301.001563573 301.001564345 300.801253332 300.801252714 300.801250859 300.80124777 300.801243449 300.801237902 300.801231132 300.801223148 300.801213957 300.801203567 300.80119199 300.801179236 300.801165319 300.801150252 300.801134049 300.801116727 300.801098303 300.801078796 300.801058223 300.801036607 300.801013967 300.800990327 300.800965709 300.800940138 300.80091364 300.80088624 300.800857965 300.800828844 300.800798904 300.800768176 300.80073669 300.800704477 300.800671569 300.800637998 300.800603797 300.800569001 300.800533643 300.800497758 300.800461382 300.800424551 300.800387301 300.800349669 300.800311691 300.800273406 300.800234851 300.800196064 300.800157084 300.800117949 300.800078697 300.800039368 300.8 300.799960632 300.799921303 300.799882051 300.799842916 300.799803936 300.799765149 300.799726594 300.799688309 300.799650331 300.799612699 300.799575449 300.799538618 300.799502242 300.799466357 300.799430999 300.799396203 300.799362002 300.799328431 300.799295523 300.79926331 300.799231824 300.799201096 300.799171156 300.799142035 300.79911376 300.79908636 300.799059862 300.799034291 300.799009673 300.798986033 300.798963393 300.798941777 300.798921204 300.798901697 300.798883273 300.798865951 300.798849748 300.798834681 300.798820764 300.79880801 300.798796433 300.798786043 300.798776852 300.798768868 300.798762098 300.798756551 300.79875223 300.798749141 300.798747286 300.798746668 300.798747286 300.798749141 300.79875223 300.798756551 300.798762098 300.798768868 300.798776852 300.798786043 300.798796433 300.79880801 300.798820764 300.798834681 300.798849748 300.798865951 300.798883273 300.798901697 300.798921204 300.798941777 300.798963393 300.798986033 300.799009673 300.799034291 300.799059862 300.79908636 300.79911376 300.799142035 300.799171156 300.799201096 300.799231824 300.79926331 300.799295523 300.799328431 300.799362002 300.799396203 300.799430999 300.799466357 300.799502242 300.799538618 300.799575449 300.799612699 300.799650331 300.799688309 300.799726594 300.799765149 300.799803936 300.799842916 300.799882051 300.799921303 300.799960632 300.8 300.800039368 300.800078697 300.800117949 300.800157084 300.800196064 300.800234851 300.800273406 300.800311691 300.800349669 300.800387301 300.800424551 300.800461382 300.800497758 300.800533643 300.800569001 300.800603797 300.800637998 300.800671569 300.800704477 300.80073669 300.800768176 300.800798904 300.800828844 300.800857965 300.80088624 300.80091364 300.800940138 300.800965709 300.800990327 300.801013967 300.801036607 300.801058223 300.801078796 300.801098303 300.801116727 300.801134049 300.801150252 300.801165319 300.801179236 300.80119199 300.801203567 300.801213957 300.801223148 300.801231132 300.801237902 300.801243449 300.80124777 300.801250859 300.801252714 300.801253332 300.600941083 300.600940619 300.600939226 300.600936907 300.600933662 300.600929497 300.600924414 300.600918419 300.600911517 300.600903716 300.600895023 300.600885447 300.600874997 300.600863683 300.600851517 300.600838511 300.600824677 300.60081003 300.600794583 300.600778352 300.600761352 300.600743602 300.600725117 300.600705917 300.60068602 300.600665446 300.600644216 300.600622349 300.600599869 300.600576796 300.600553155 300.600528967 300.600504258 300.60047905 300.60045337 300.600427243 300.600400694 300.600373749 300.600346436 300.600318781 300.600290811 300.600262554 300.600234038 300.600205291 300.600176341 300.600147218 300.600117949 300.600088564 300.600059091 300.60002956 300.6 300.59997044 300.599940909 300.599911436 300.599882051 300.599852782 300.599823659 300.599794709 300.599765962 300.599737446 300.599709189 300.599681219 300.599653564 300.599626251 300.599599306 300.599572757 300.59954663 300.59952095 300.599495742 300.599471033 300.599446845 300.599423204 300.599400131 300.599377651 300.599355784 300.599334554 300.59931398 300.599294083 300.599274883 300.599256398 300.599238648 300.599221648 300.599205417 300.59918997 300.599175323 300.599161489 300.599148483 300.599136317 300.599125003 300.599114553 300.599104977 300.599096284 300.599088483 300.599081581 300.599075586 300.599070503 300.599066338 300.599063093 300.599060774 300.599059381 300.599058917 300.599059381 300.599060774 300.599063093 300.599066338 300.599070503 300.599075586 300.599081581 300.599088483 300.599096284 300.599104977 300.599114553 300.599125003 300.599136317 300.599148483 300.599161489 300.599175323 300.59918997 300.599205417 300.599221648 300.599238648 300.599256398 300.599274883 300.599294083 300.59931398 300.599334554 300.599355784 300.599377651 300.599400131 300.599423204 300.599446845 300.599471033 300.599495742 300.59952095 300.59954663 300.599572757 300.599599306 300.599626251 300.599653564 300.599681219 300.599709189 300.599737446 300.599765962 300.599794709 300.599823659 300.599852782 300.599882051 300.599911436 300.599940909 300.59997044 300.6 300.60002956 300.600059091 300.600088564 300.600117949 300.600147218 300.600176341 300.600205291 300.600234038 300.600262554 300.600290811 300.600318781 300.600346436 300.600373749 300.600400694 300.600427243 300.60045337 300.60047905 300.600504258 300.600528967 300.600553155 300.600576796 300.600599869 300.600622349 300.600644216 300.600665446 300.60068602 300.600705917 300.600725117 300.600743602 300.600761352 300.600778352 300.600794583 300.60081003 300.600824677 300.600838511 300.600851517 300.600863683 300.600874997 300.600885447 300.600895023 300.600903716 300.600911517 300.600918419 300.600924414 300.600929497 300.600933662 300.600936907 300.600939226 300.600940619 300.600941083 300.400627905 300.400627595 300.400626666 300.400625119 300.400622954 300.400620175 300.400616783 300.400612783 300.400608178 300.400602973 300.400597173 300.400590784 300.400583811 300.400576263 300.400568146 300.400559468 300.400550238 300.400540464 300.400530158 300.400519328 300.400507986 300.400496142 300.400483809 300.400470999 300.400457723 300.400443996 300.400429831 300.400415241 300.400400242 300.400384848 300.400369073 300.400352935 300.400336448 300.40031963 300.400302496 300.400285063 300.400267349 300.400249371 300.400231147 300.400212695 300.400194033 300.40017518 300.400156154 300.400136973 300.400117658 300.400098226 300.400078697 300.400059091 300.400039426 300.400019723 300.4 300.399980277 300.399960574 300.399940909 300.399921303 300.399901774 300.399882342 300.399863027 300.399843846 300.39982482 300.399805967 300.399787305 300.399768853 300.399750629 300.399732651 300.399714937 300.399697504 300.39968037 300.399663552 300.399647065 300.399630927 300.399615152 300.399599758 300.399584759 300.399570169 300.399556004 300.399542277 300.399529001 300.399516191 300.399503858 300.399492014 300.399480672 300.399469842 300.399459536 300.399449762 300.399440532 300.399431854 300.399423737 300.399416189 300.399409216 300.399402827 300.399397027 300.399391822 300.399387217 300.399383217 300.399379825 300.399377046 300.399374881 300.399373334 300.399372405 300.399372095 300.399372405 300.399373334 300.399374881 300.399377046 300.399379825 300.399383217 300.399387217 300.399391822 300.399397027 300.399402827 300.399409216 300.399416189 300.399423737 300.399431854 300.399440532 300.399449762 300.399459536 300.399469842 300.399480672 300.399492014 300.399503858 300.399516191 300.399529001 300.399542277 300.399556004 300.399570169 300.399584759 300.399599758 300.399615152 300.399630927 300.399647065 300.399663552 300.39968037 300.399697504 300.399714937 300.399732651 300.399750629 300.399768853 300.399787305 300.399805967 300.39982482 300.399843846 300.399863027 300.399882342 300.399901774 300.399921303 300.399940909 300.399960574 300.399980277 300.4 300.400019723 300.400039426 300.400059091 300.400078697 300.400098226 300.400117658 300.400136973 300.400156154 300.40017518 300.400194033 300.400212695 300.400231147 300.400249371 300.400267349 300.400285063 300.400302496 300.40031963 300.400336448 300.400352935 300.400369073 300.400384848 300.400400242 300.400415241 300.400429831 300.400443996 300.400457723 300.400470999 300.400483809 300.400496142 300.400507986 300.400519328 300.400530158 300.400540464 300.400550238 300.400559468 300.400568146 300.400576263 300.400583811 300.400590784 300.400597173 300.400602973 300.400608178 300.400612783 300.400616783 300.400620175 300.400622954 300.400625119 300.400626666 300.400627595 300.400627905 300.200314108 300.200313953 300.200313488 300.200312714 300.200311631 300.20031024 300.200308544 300.200306543 300.200304239 300.200301636 300.200298734 300.200295538 300.20029205 300.200288274 300.200284213 300.200279872 300.200275255 300.200270366 300.20026521 300.200259792 300.200254118 300.200248194 300.200242024 300.200235616 300.200228975 300.200222108 300.200215021 300.200207723 300.20020022 300.200192519 300.200184628 300.200176555 300.200168307 300.200159894 300.200151322 300.200142602 300.200133741 300.200124747 300.200115631 300.2001064 300.200097065 300.200087633 300.200078115 300.20006852 300.200058858 300.200049137 300.200039368 300.20002956 300.200019723 300.200009866 300.2 300.199990134 300.199980277 300.19997044 300.199960632 300.199950863 300.199941142 300.19993148 300.199921885 300.199912367 300.199902935 300.1998936 300.199884369 300.199875253 300.199866259 300.199857398 300.199848678 300.199840106 300.199831693 300.199823445 300.199815372 300.199807481 300.19979978 300.199792277 300.199784979 300.199777892 300.199771025 300.199764384 300.199757976 300.199751806 300.199745882 300.199740208 300.19973479 300.199729634 300.199724745 300.199720128 300.199715787 300.199711726 300.19970795 300.199704462 300.199701266 300.199698364 300.199695761 300.199693457 300.199691456 300.19968976 300.199688369 300.199687286 300.199686512 300.199686047 300.199685892 300.199686047 300.199686512 300.199687286 300.199688369 300.19968976 300.199691456 300.199693457 300.199695761 300.199698364 300.199701266 300.199704462 300.19970795 300.199711726 300.199715787 300.199720128 300.199724745 300.199729634 300.19973479 300.199740208 300.199745882 300.199751806 300.199757976 300.199764384 300.199771025 300.199777892 300.199784979 300.199792277 300.19979978 300.199807481 300.199815372 300.199823445 300.199831693 300.199840106 300.199848678 300.199857398 300.199866259 300.199875253 300.199884369 300.1998936 300.199902935 300.199912367 300.199921885 300.19993148 300.199941142 300.199950863 300.199960632 300.19997044 300.199980277 300.199990134 300.2 300.200009866 300.200019723 300.20002956 300.200039368 300.200049137 300.200058858 300.20006852 300.200078115 300.200087633 300.200097065 300.2001064 300.200115631 300.200124747 300.200133741 300.200142602 300.200151322 300.200159894 300.200168307 300.200176555 300.200184628 300.200192519 300.20020022 300.200207723 300.200215021 300.200222108 300.200228975 300.200235616 300.200242024 300.200248194 300.200254118 300.200259792 300.20026521 300.200270366 300.200275255 300.200279872 300.200284213 300.200288274 300.20029205 300.200295538 300.200298734 300.200301636 300.200304239 300.200306543 300.200308544 300.20031024 300.200311631 300.200312714 300.200313488 300.200313953 300.200314108 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/x.geos b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/x.geos new file mode 100644 index 00000000000..b6ad03b89e4 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/x.geos @@ -0,0 +1 @@ +0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/xMid.geos b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/xMid.geos new file mode 100644 index 00000000000..29d6383b52c --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/xMid.geos @@ -0,0 +1 @@ +100 diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/y.geos b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/y.geos new file mode 100644 index 00000000000..5e2b9500276 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/y.geos @@ -0,0 +1 @@ +1.25 diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/z.geos b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/z.geos new file mode 100644 index 00000000000..258942065d8 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/z.geos @@ -0,0 +1 @@ +0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 diff --git a/inputFiles/singlePhaseFlow/polyhedral/thermalConduction_base.xml b/inputFiles/singlePhaseFlow/polyhedral/thermalConduction_base.xml new file mode 100644 index 00000000000..1a21d299195 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/thermalConduction_base.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedral/thermalConduction_consistent_smoke.xml b/inputFiles/singlePhaseFlow/polyhedral/thermalConduction_consistent_smoke.xml new file mode 100644 index 00000000000..11bcafd9106 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/thermalConduction_consistent_smoke.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedral/thermalConduction_smoke.xml b/inputFiles/singlePhaseFlow/polyhedral/thermalConduction_smoke.xml new file mode 100644 index 00000000000..0778bbe8910 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/thermalConduction_smoke.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedral/thermalMixedMFD.ats b/inputFiles/singlePhaseFlow/polyhedral/thermalMixedMFD.ats new file mode 100644 index 00000000000..f3913e89542 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/thermalMixedMFD.ats @@ -0,0 +1,34 @@ +from geos.ats.test_builder import TestDeck, RestartcheckParameters, generate_geos_tests + +restartcheck_params = {} +restartcheck_params['atol'] = 1e-08 +restartcheck_params['rtol'] = 4e-07 + +decks = [ + TestDeck( + name="thermalConduction_smoke", + description= + 'Thermal mixed MFD single phase flow test (1D transient heat conduction, TPFA selected in every cell, direct solver)', + partitions=((1, 1, 1), (2, 1, 1)), + restart_step=10, + check_step=20, + restartcheck_params=RestartcheckParameters(**restartcheck_params)), + TestDeck( + name="thermalConduction_consistent_smoke", + description= + 'Thermal mixed MFD single phase flow test (1D transient heat conduction, consistent inner product in every cell, GMRES with the thermal MGR strategy)', + partitions=((1, 1, 1), (2, 1, 1)), + restart_step=10, + check_step=20, + restartcheck_params=RestartcheckParameters(**restartcheck_params)), + TestDeck( + name="lapwoodConvection_Ra50_smoke", + description= + 'Thermal mixed MFD single phase flow test (onset of natural convection in a porous layer, tetrahedral mesh, consistent inner product, GMRES with the thermal MGR strategy)', + partitions=((1, 1, 1), (2, 1, 1), (2, 1, 2)), + restart_step=5, + check_step=10, + restartcheck_params=RestartcheckParameters(**restartcheck_params)), +] + +generate_geos_tests(decks) diff --git a/src/cmake/GeosxOptions.cmake b/src/cmake/GeosxOptions.cmake index a10fef3ebcc..0ff8e7fab43 100644 --- a/src/cmake/GeosxOptions.cmake +++ b/src/cmake/GeosxOptions.cmake @@ -152,7 +152,7 @@ message( "CMAKE_CXX_COMPILER_ID = ${CMAKE_CXX_COMPILER_ID}" ) blt_append_custom_compiler_flag( FLAGS_VAR CMAKE_CXX_FLAGS DEFAULT "${OpenMP_CXX_FLAGS}" ) blt_append_custom_compiler_flag( FLAGS_VAR CMAKE_CXX_FLAGS GNU "-Wpedantic -pedantic-errors -Wshadow -Wfloat-equal -Wcast-align -Wcast-qual" - CLANG "-Wpedantic -pedantic-errors -Wshadow -Wfloat-equal -Wno-cast-align -Wcast-qual" + CLANG "-Wpedantic -pedantic-errors -Wshadow -Wfloat-equal -Wno-cast-align -Wcast-qual -Wno-shorten-64-to-32" ) if( ENABLE_HIP ) diff --git a/src/cmake/thirdparty/SetupGeosxThirdParty.cmake b/src/cmake/thirdparty/SetupGeosxThirdParty.cmake index 904e2f7a37b..dcf06233b9c 100644 --- a/src/cmake/thirdparty/SetupGeosxThirdParty.cmake +++ b/src/cmake/thirdparty/SetupGeosxThirdParty.cmake @@ -790,6 +790,12 @@ if( ENABLE_HYPREDRV ) list( PREPEND CMAKE_PREFIX_PATH "${HYPRE_DIR}" ) endif() + # expose the imported TPL hypre as HYPRE::HYPRE so HYPREDRVConfig does not search for another hypre + if( TARGET hypre AND NOT TARGET HYPRE::HYPRE ) + add_library( HYPRE::HYPRE INTERFACE IMPORTED ) + target_link_libraries( HYPRE::HYPRE INTERFACE hypre ) + endif() + find_package( HYPREDRV CONFIG QUIET PATHS ${HYPREDRV_DIR} ${HYPREDRV_DIR}/lib/cmake/HYPREDRV diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp index 5972a59edf9..36178b9c66a 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp @@ -31,8 +31,8 @@ namespace mimeticInnerProduct /** * @class RTInnerProduct * - * Provides the mimetic inner product that reproduces the lowest-order Raviart-Thomas mass matrix - * exactly on simplices, and extends it consistently to general polyhedra + * Mimetic inner product that coincides with the mass matrix of the lowest-order Raviart-Thomas element on + * simplices and on rectangular parallelepipeds, and extends it consistently to general polyhedra */ class RTInnerProduct : public MimeticInnerProductBase { @@ -75,9 +75,11 @@ class RTInnerProduct : public MimeticInnerProductBase * @param[in] lengthTolerance the tolerance used in the trans calculations * @param[inout] M the output inner product matrix * - * @details M = C K^{-1} C^T / V + s ( I - Q Q^T ) with Q an orthonormal basis of the range of the - * K-weighted area normals; s = tr( C K^{-1} C^T / V ) / (d+2) is the unique scale matching the - * conforming RT0 element on simplices. + * @details M = M1 + P D P, with the consistency term M1 = C K^{-1} C^T / V, the projector P = I - Q Q^T, Q an + * orthonormal basis of the range of N K, and D a positive diagonal matrix. On a simplex D = tr( M1 ) / ( d + 2 ) I, + * the unique scaling for which M is the mass matrix of the RT0 element. On the other cells D = 2/3 diag( M1 ), for + * which M is the mass matrix of the RT[0] element on a rectangular parallelepiped with K diagonal in its axes, + * for every aspect ratio. */ template< localIndex NF > GEOS_HOST_DEVICE @@ -133,15 +135,35 @@ RTInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION_U LvArray::tensorOps::Rij_add_AikAjk< NF, 3 >( P, Qmat ); LvArray::tensorOps::scale< NF, NF >( P, -1.0 ); - // 4) s = tr(M1)/(d+2), the unique RT0-matching stabilization scale on simplices (d = 3) - real64 const s = LvArray::tensorOps::trace< NF >( M1 ) / 5.0; + // 4) diagonal scaling D of the stabilization P D P: tr(M1)/(d+2) I on a simplex (d = 3), 2/3 diag(M1) otherwise + real64 D[ NF ]; + if( NF == 4 ) + { + real64 const s = LvArray::tensorOps::trace< NF >( M1 ) / 5.0; + for( localIndex i = 0; i < NF; ++i ) + { + D[i] = s; + } + } + else + { + for( localIndex i = 0; i < NF; ++i ) + { + D[i] = 2.0 / 3.0 * M1[i][i]; + } + } - // 5) assemble M = M1 + s P + // 5) assemble M = M1 + P D P for( localIndex i = 0; i < NF; ++i ) { for( localIndex j = 0; j < NF; ++j ) { - M[i][j] = M1[i][j] + s * P[i][j]; + real64 stab = 0.0; + for( localIndex k = 0; k < NF; ++k ) + { + stab += P[i][k] * D[k] * P[k][j]; + } + M[i][j] = M1[i][j] + stab; } } } diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp index 879d21da964..ad73be31322 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp @@ -2525,6 +2525,68 @@ TEST( MimeticIP_RT0Exactness, Tetrahedron ) } } +TEST( MimeticIP_RT0Exactness, RectangularParallelepiped ) +{ + // anisotropic rectangular parallelepiped [0,Lx] x [0,Ly] x [0,Lz], K diagonal in its axes: the mass matrix of the + // RT[0] element couples the two faces normal to each axis only, ( L_i / ( k_i A_i ) ) [ 1/3, -1/6; -1/6, 1/3 ] + // in outward-flux dofs + localIndex constexpr numNodes = 8; + localIndex constexpr numFaces = 6; + real64 const L[3] = { 0.5, 4.0, 1.0 }; + real64 const perm[3] = { 1.0, 3.0, 0.5 }; + + array2d< real64, nodes::REFERENCE_POSITION_PERM > nodePosition; + nodePosition.resize( numNodes, 3 ); + for( localIndex n = 0; n < numNodes; ++n ) + { + nodePosition( n, 0 ) = ( ( n == 1 || n == 2 || n == 5 || n == 6 ) ? 1.0 : 0.0 ) * L[0]; + nodePosition( n, 1 ) = ( ( n == 2 || n == 3 || n == 6 || n == 7 ) ? 1.0 : 0.0 ) * L[1]; + nodePosition( n, 2 ) = ( n >= 4 ? 1.0 : 0.0 ) * L[2]; + } + + // faces ordered by axis: x = 0, x = Lx, y = 0, y = Ly, z = 0, z = Lz + localIndex const faceNodes[numFaces][4] = { { 0, 3, 7, 4 }, { 1, 2, 6, 5 }, { 0, 1, 5, 4 }, { 3, 2, 6, 7 }, { 0, 1, 2, 3 }, { 4, 5, 6, 7 } }; + FaceManager::NodeMapType faceToNodes; + faceToNodes.resize( numFaces ); + array1d< localIndex > elemToFaces; + elemToFaces.resize( numFaces ); + for( localIndex f = 0; f < numFaces; ++f ) + { + faceToNodes.resizeArray( f, 4 ); + for( localIndex i = 0; i < 4; ++i ) + faceToNodes( f, i ) = faceNodes[f][i]; + elemToFaces( f ) = f; + } + + stackArray1d< real64, 3 > center( 3 ); + for( int d = 0; d < 3; ++d ) + center[d] = 0.5 * L[d]; + real64 const vol = L[0] * L[1] * L[2]; + + stackArray2d< real64, 36 > M( 6, 6 ); + M.template setValues< parallelHostPolicy >( 0.0 ); + RTInnerProduct::computeM< 6 >( nodePosition.toViewConst(), + faceToNodes.toViewConst(), + elemToFaces.toSliceConst(), + center.toSliceConst(), + vol, perm, 1e-12, M.toSlice() ); + + for( int i = 0; i < 6; ++i ) + { + for( int j = 0; j < 6; ++j ) + { + real64 ref = 0.0; + if( i / 2 == j / 2 ) + { + int const d = i / 2; + real64 const scale = L[d] / ( perm[d] * vol / L[d] ); + ref = ( i == j ? 1.0 / 3.0 : -1.0 / 6.0 ) * scale; + } + EXPECT_NEAR( M( i, j ), ref, rt0_exactness_tol ) << "entry " << i << "," << j; + } + } +} + TEST( MimeticIP_RT0Exactness, Triangle2D ) { // The stabilization rule s = tr(M1)/(d+2) is dimension generic; computeM() hard-codes d = 3, diff --git a/src/coreComponents/integrationTests/fluidFlowTests/CMakeLists.txt b/src/coreComponents/integrationTests/fluidFlowTests/CMakeLists.txt index 35166c392e6..9b8f4df8849 100644 --- a/src/coreComponents/integrationTests/fluidFlowTests/CMakeLists.txt +++ b/src/coreComponents/integrationTests/fluidFlowTests/CMakeLists.txt @@ -9,6 +9,7 @@ set( gtest_geosx_tests testImmiscibleMultiphaseFlow.cpp testReactiveCompositionalMultiphaseOBL.cpp testSinglePhaseMFDPolyhedral.cpp + testSinglePhaseMixedMFD.cpp testSinglePhaseReactiveTransport.cpp ) set( tplDependencyList ${parallelDeps} gtest ) diff --git a/src/coreComponents/integrationTests/fluidFlowTests/testSinglePhaseMixedMFD.cpp b/src/coreComponents/integrationTests/fluidFlowTests/testSinglePhaseMixedMFD.cpp new file mode 100644 index 00000000000..c56c6f84193 --- /dev/null +++ b/src/coreComponents/integrationTests/fluidFlowTests/testSinglePhaseMixedMFD.cpp @@ -0,0 +1,1078 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file testSinglePhaseMixedMFD.cpp + * + * Jacobian of the mixed MFD solver against finite differences of its residual, on a hybrid mesh of hexahedra, + * pyramids and tetrahedra, isothermal and thermal, with the three classifications (TPFA everywhere, consistent + * everywhere, adaptive) and the three kinds of boundary faces (Dirichlet, Neumann with a value, Robin). + * The thermal system is then checked after the row operation that removes the enthalpy reference. + * Exact solutions: linear steady states with Neumann and Robin conditions, and the hydrostatic equilibrium on + * distorted cells. + */ + +#include "integrationTests/fluidFlowTests/testSingleFlowUtils.hpp" + +#include "mainInterface/initialization.hpp" +#include "mainInterface/GeosxState.hpp" +#include "mainInterface/ProblemManager.hpp" +#include "mesh/DomainPartition.hpp" +#include "mesh/MeshBody.hpp" +#include "mixedMimetic/MixedMimeticBoundaryConditions.hpp" +#include "mixedMimetic/MixedMimeticFields.hpp" +#include "physicsSolvers/PhysicsSolverManager.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseBaseFields.hpp" +#include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" +#include "physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp" + +#include + +#include +#include + +using namespace geos; +using namespace geos::dataRepository; +using namespace geos::testing; + +CommandLineOptions g_commandLineOptions; + +namespace +{ + +real64 constexpr time_n = 0.0; +real64 constexpr dt = 1.0e4; +real64 constexpr specificHeat = 4000.0; + +struct Setup +{ + bool isThermal; + string consistencyTolerance; ///< 1.0e+20 TPFA everywhere, 1.0e-20 consistent everywhere, 0.1 adaptive + real64 referenceTemperature; + bool temperatureDependentFluid; ///< thermal expansion and temperature-dependent viscosity +}; + +string generateXml( string const & meshFile, Setup const & setup ) +{ + std::ostringstream os; + os << + R"xml( + + + + + + + + + + + + + + + + + + + + + + + + + + + )xml"; + if( setup.isThermal ) + { + os << + R"xml( + + + + "; + } + else + { + os << + R"xml( + + )xml"; + } + os << + R"xml( + + + + + + )xml"; + if( setup.isThermal ) + { + os << + R"xml( + + + + + + "; + } + os << + R"xml( + + + )xml"; + return os.str(); +} + +/** + * @brief Problem set up from the XML, with a non-equilibrium state stored as the beginning of the step. + */ +class MixedMFDProblem +{ +public: + + MixedMFDProblem( Setup const & setup ): + m_setup( setup ), + m_state( std::make_unique< CommandLineOptions >( g_commandLineOptions ) ) + { + string const meshFile = string( TEST_BINARY_DIR ) + "/hex_pyr_tet_nested_mixed.vtu"; + setupProblemFromXML( m_state.getProblemManager(), generateXml( meshFile, setup ).c_str() ); + m_solver = &m_state.getProblemManager().getPhysicsSolverManager().getGroup< SinglePhaseMixedMFD >( "flow" ); + + m_solver->setupSystem( domain(), m_solver->getDofManager(), m_solver->getLocalMatrix(), + m_solver->getSystemRhs(), m_solver->getSystemSolution() ); + m_solver->implicitStepSetup( time_n, dt, domain() ); + + // fluxes, pressures and temperatures vary from one entity to the next: no term of the residual vanishes + // and the upwind directions are invariant under the perturbations + setState(); + m_solver->updateState( domain() ); + m_solver->implicitStepSetup( time_n, dt, domain() ); + } + + DomainPartition & domain() { return m_state.getProblemManager().getDomainPartition(); } + + SinglePhaseMixedMFD & solver() { return *m_solver; } + + /// residual and jacobian of the step, without the boundary conditions of the cells + void assemble( CRSMatrixView< real64, globalIndex const > const & matrix, arrayView1d< real64 > const & rhs ) + { + m_solver->assembleSystem( time_n, dt, domain(), m_solver->getDofManager(), matrix, rhs ); + } + + template< typename LAMBDA > + void forSubRegions( LAMBDA && lambda ) + { + m_solver->forDiscretizationOnMeshTargets( domain().getMeshBodies(), [&]( string const &, MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, [&]( localIndex const, ElementSubRegionBase & subRegion ) + { + lambda( subRegion ); + } ); + } ); + } + + FaceManager & faceManager() + { + FaceManager * result = nullptr; + m_solver->forDiscretizationOnMeshTargets( domain().getMeshBodies(), [&]( string const &, MeshLevel & mesh, string_array const & ) + { + result = &mesh.getFaceManager(); + } ); + return *result; + } + +private: + + void setState() + { + real64 const tRef = m_setup.referenceTemperature; + bool const isThermal = m_setup.isThermal; + forSubRegions( [&]( ElementSubRegionBase & subRegion ) + { + arrayView2d< real64 const > const center = subRegion.getElementCenter(); + arrayView1d< real64 > const pres = subRegion.getField< fields::flow::pressure >(); + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + real64 const s = std::sin( 7.0 * center[ei][0] + 3.0 * center[ei][1] + 5.0 * center[ei][2] ); + pres[ei] = 1.0e7 + 2.0e5 * s - 1.5e5 * center[ei][0]; + if( isThermal ) + { + real64 const temp = 350.0 + 15.0 * std::cos( 4.0 * center[ei][0] - 6.0 * center[ei][2] ); + subRegion.getField< fields::flow::temperature >()[ei] = temp; + // the enthalpy does not satisfy the equation of state: the closure equations have a nonzero residual + subRegion.getField< fields::mixedMimetic::enthalpy >()[ei] = specificHeat * ( temp - tRef ) + 500.0 * s; + } + } + } ); + + FaceManager & faces = faceManager(); + arrayView2d< real64 const > const faceCenter = faces.faceCenter(); + arrayView1d< real64 > const massFlux = faces.getField< fields::mixedMimetic::faceMassFlux >(); + for( localIndex kf = 0; kf < faces.size(); ++kf ) + { + real64 const s = std::sin( 11.0 * faceCenter[kf][0] + 13.0 * faceCenter[kf][1] + 17.0 * faceCenter[kf][2] ); + massFlux[kf] = ( s >= 0.0 ? 1.0 : -1.0 ) * 1.0e-3 * ( 1.0 + LvArray::math::abs( s ) ); + if( isThermal ) + { + faces.getField< fields::mixedMimetic::faceHeatFlux >()[kf] = 3.0 * std::cos( 5.0 * faceCenter[kf][0] + 2.0 * faceCenter[kf][2] ); + } + } + } + + Setup const m_setup; + GeosxState m_state; + SinglePhaseMixedMFD * m_solver; +}; + +/** + * @brief Column of the finite-difference jacobian for one perturbed value. + */ +void fillColumn( MixedMFDProblem & problem, + real64 & value, + real64 const perturbation, + globalIndex const dofIndex, + arrayView1d< real64 > const & residual, + arrayView1d< real64 const > const & residualOrig, + CRSMatrix< real64, globalIndex > & jacobian, + CRSMatrix< real64, globalIndex > & jacobianFD ) +{ + problem.solver().resetStateToBeginningOfStep( problem.domain() ); + value += perturbation; + problem.solver().updateState( problem.domain() ); + + residual.zero(); + jacobian.zero(); + problem.assemble( jacobian.toViewConstSizes(), residual ); + fillNumericalJacobian( residual.toViewConst(), residualOrig, dofIndex, perturbation, jacobianFD.toViewConstSizes() ); +} + +void testNumericalJacobian( Setup const & setup, real64 const relTol ) +{ + MixedMFDProblem problem( setup ); + SinglePhaseMixedMFD & solver = problem.solver(); + DofManager const & dofManager = solver.getDofManager(); + real64 const perturb = std::sqrt( std::numeric_limits< real64 >::epsilon() ); + + CRSMatrix< real64, globalIndex > & jacobian = solver.getLocalMatrix(); + array1d< real64 > residual( jacobian.numRows() ); + + solver.resetStateToBeginningOfStep( problem.domain() ); + solver.updateState( problem.domain() ); + residual.zero(); + jacobian.zero(); + problem.assemble( jacobian.toViewConstSizes(), residual.toView() ); + array1d< real64 > const residualOrig( residual ); + + CRSMatrix< real64, globalIndex > jacobianFD( jacobian ); + jacobianFD.zero(); + + // face unknowns + FaceManager & faces = problem.faceManager(); + // the residual is linear in the face fluxes: a perturbation of 10 % keeps the upwind directions and resolves the + // small couplings of the inner product, which a perturbation of sqrt( eps ) loses against residuals of 1.0e7 Pa + real64 const fluxPerturb = 0.1; + stdVector< std::pair< string, real64 > > faceFields = { { fields::mixedMimetic::faceMassFlux::key(), 0.0 } }; + if( setup.isThermal ) + { + faceFields.emplace_back( fields::mixedMimetic::faceHeatFlux::key(), 1.0 ); + } + for( auto const & faceField : faceFields ) + { + arrayView1d< real64 > const flux = faces.getReference< array1d< real64 > >( faceField.first ); + arrayView1d< globalIndex const > const dofNumber = faces.getReference< array1d< globalIndex > >( dofManager.getKey( faceField.first ) ); + for( localIndex kf = 0; kf < faces.size(); ++kf ) + { + if( dofNumber[kf] < 0 ) + { + continue; + } + real64 const scale = LvArray::math::abs( flux[kf] ); + fillColumn( problem, flux[kf], fluxPerturb * ( scale + faceField.second ), dofNumber[kf], + residual.toView(), residualOrig.toViewConst(), jacobian, jacobianFD ); + } + } + + // cell unknowns + string const elemDofKey = dofManager.getKey( SinglePhaseMixedMFD::viewKeyStruct::elemDofFieldString() ); + problem.forSubRegions( [&]( ElementSubRegionBase & subRegion ) + { + arrayView1d< globalIndex const > const dofNumber = subRegion.getReference< array1d< globalIndex > >( elemDofKey ); + stdVector< arrayView1d< real64 > > unknowns = { subRegion.getField< fields::flow::pressure >() }; + if( setup.isThermal ) + { + unknowns.push_back( subRegion.getField< fields::flow::temperature >() ); + unknowns.push_back( subRegion.getField< fields::mixedMimetic::enthalpy >() ); + } + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + for( std::size_t c = 0; c < unknowns.size(); ++c ) + { + real64 const scale = LvArray::math::abs( unknowns[c][ei] ); + fillColumn( problem, unknowns[c][ei], perturb * ( scale + 1.0 ), dofNumber[ei] + c, + residual.toView(), residualOrig.toViewConst(), jacobian, jacobianFD ); + } + } + } ); + + // analytical jacobian at the unperturbed state + solver.resetStateToBeginningOfStep( problem.domain() ); + solver.updateState( problem.domain() ); + residual.zero(); + jacobian.zero(); + problem.assemble( jacobian.toViewConstSizes(), residual.toView() ); + + compareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), relTol, 1.0e-6 ); +} + +/** + * @brief System of the thermal step before and after the boundary conditions, which carry the row operation. + */ +struct ReducedSystem +{ + CRSMatrix< real64, globalIndex > jacobian; + array1d< real64 > residual; + CRSMatrix< real64, globalIndex > reducedJacobian; + array1d< real64 > reducedResidual; + array1d< globalIndex > cellDof; + array1d< real64 > cellEnthalpy; +}; + +ReducedSystem assembleReducedSystem( Setup const & setup ) +{ + MixedMFDProblem problem( setup ); + SinglePhaseMixedMFD & solver = problem.solver(); + CRSMatrix< real64, globalIndex > & jacobian = solver.getLocalMatrix(); + array1d< real64 > residual( jacobian.numRows() ); + + ReducedSystem result; + residual.zero(); + jacobian.zero(); + problem.assemble( jacobian.toViewConstSizes(), residual.toView() ); + result.jacobian = jacobian; + result.residual = residual; + + solver.applyBoundaryConditions( time_n, dt, problem.domain(), solver.getDofManager(), jacobian.toViewConstSizes(), residual.toView() ); + result.reducedJacobian = jacobian; + result.reducedResidual = residual; + + string const elemDofKey = solver.getDofManager().getKey( SinglePhaseMixedMFD::viewKeyStruct::elemDofFieldString() ); + problem.forSubRegions( [&]( ElementSubRegionBase & subRegion ) + { + arrayView1d< globalIndex const > const dofNumber = subRegion.getReference< array1d< globalIndex > >( elemDofKey ); + arrayView1d< real64 const > const enthalpy = subRegion.getField< fields::mixedMimetic::enthalpy >(); + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + result.cellDof.emplace_back( dofNumber[ei] ); + result.cellEnthalpy.emplace_back( enthalpy[ei] ); + } + } ); + return result; +} + +} // namespace + +TEST( SinglePhaseMixedMFDJacobian, isothermal_tpfa ) +{ + testNumericalJacobian( { false, "1.0e+20", 350.0, true }, 1.0e-3 ); +} + +TEST( SinglePhaseMixedMFDJacobian, isothermal_consistent ) +{ + testNumericalJacobian( { false, "1.0e-20", 350.0, true }, 1.0e-3 ); +} + +TEST( SinglePhaseMixedMFDJacobian, isothermal_adaptive ) +{ + testNumericalJacobian( { false, "0.1", 350.0, true }, 1.0e-3 ); +} + +TEST( SinglePhaseMixedMFDJacobian, thermal_tpfa ) +{ + testNumericalJacobian( { true, "1.0e+20", 350.0, true }, 1.0e-3 ); +} + +TEST( SinglePhaseMixedMFDJacobian, thermal_consistent ) +{ + testNumericalJacobian( { true, "1.0e-20", 350.0, true }, 1.0e-3 ); +} + +TEST( SinglePhaseMixedMFDJacobian, thermal_adaptive ) +{ + testNumericalJacobian( { true, "0.1", 350.0, true }, 1.0e-3 ); +} + +// the field specifications of the deck end up in the right descriptor of the right operator on every face: +// flow Dirichlet west and east, Neumann 1.0e-4 on top; heat Dirichlet west, Robin east, Neumann 20 on top; +// Neumann 0 (no-flow, adiabatic) on the other boundary faces and no condition inside +TEST( SinglePhaseMixedMFDBoundary, descriptors ) +{ + using namespace geos::mixedMimeticBoundary; + + MixedMFDProblem problem( { true, "0.1", 350.0, true } ); + FaceManager & faces = problem.faceManager(); + arrayView2d< real64 const > const center = faces.faceCenter(); + arrayView2d< localIndex const > const elemList = faces.elementList(); + + arrayView1d< integer const > const flowType = faces.getField< fields::mixedMimetic::flowBoundaryType >(); + arrayView1d< real64 const > const flowValue = faces.getField< fields::mixedMimetic::flowBoundaryValue >(); + arrayView1d< integer const > const heatType = faces.getField< fields::mixedMimetic::heatBoundaryType >(); + arrayView1d< real64 const > const heatValue = faces.getField< fields::mixedMimetic::heatBoundaryValue >(); + arrayView1d< real64 const > const heatCoefficient = faces.getField< fields::mixedMimetic::heatBoundaryCoefficient >(); + arrayView1d< integer const > const isEnthalpyBcFace = faces.getField< fields::mixedMimetic::isEnthalpyBcFace >(); + + localIndex numWest = 0, numEast = 0, numTop = 0, numHomogeneousNeumann = 0, numInterior = 0; + for( localIndex kf = 0; kf < faces.size(); ++kf ) + { + SCOPED_TRACE( GEOS_FMT( "face {} at ( {}, {}, {} )", kf, center[kf][0], center[kf][1], center[kf][2] ) ); + bool const onBoundary = elemList[kf][0] < 0 || elemList[kf][1] < 0; + if( !onBoundary ) + { + ++numInterior; + EXPECT_EQ( flowType[kf], BoundaryType::interior ); + EXPECT_EQ( heatType[kf], BoundaryType::interior ); + } + else if( center[kf][0] < 0.01 ) + { + ++numWest; + EXPECT_EQ( flowType[kf], BoundaryType::dirichlet ); + EXPECT_DOUBLE_EQ( flowValue[kf], 1.1e7 ); + EXPECT_EQ( heatType[kf], BoundaryType::dirichlet ); + EXPECT_DOUBLE_EQ( heatValue[kf], 380.0 ); + EXPECT_EQ( isEnthalpyBcFace[kf], 1 ); + } + else if( center[kf][0] > 0.99 ) + { + ++numEast; + EXPECT_EQ( flowType[kf], BoundaryType::dirichlet ); + EXPECT_DOUBLE_EQ( flowValue[kf], 0.9e7 ); + EXPECT_EQ( heatType[kf], BoundaryType::robin ); + EXPECT_DOUBLE_EQ( heatValue[kf], 300.0 ); + EXPECT_DOUBLE_EQ( heatCoefficient[kf], 5.0 ); + EXPECT_EQ( isEnthalpyBcFace[kf], 0 ); + } + else if( center[kf][2] > 0.99 ) + { + ++numTop; + EXPECT_EQ( flowType[kf], BoundaryType::neumann ); + EXPECT_DOUBLE_EQ( flowValue[kf], 1.0e-4 ); + EXPECT_EQ( heatType[kf], BoundaryType::neumann ); + EXPECT_DOUBLE_EQ( heatValue[kf], 20.0 ); + } + else + { + ++numHomogeneousNeumann; + EXPECT_EQ( flowType[kf], BoundaryType::neumann ); + EXPECT_DOUBLE_EQ( flowValue[kf], 0.0 ); + EXPECT_EQ( heatType[kf], BoundaryType::neumann ); + EXPECT_DOUBLE_EQ( heatValue[kf], 0.0 ); + } + } + + // every kind is present: the jacobian tests above do exercise them + EXPECT_GT( numWest, 0 ); + EXPECT_GT( numEast, 0 ); + EXPECT_GT( numTop, 0 ); + EXPECT_GT( numHomogeneousNeumann, 0 ); + EXPECT_GT( numInterior, 0 ); +} + +// energy row <- energy row - h_K * mass row, every other row untouched +TEST( SinglePhaseMixedMFDEnthalpyReference, rowOperation ) +{ + ReducedSystem const system = assembleReducedSystem( { true, "0.1", 350.0, true } ); + CRSMatrixView< real64 const, globalIndex const > const J = system.jacobian.toViewConst(); + CRSMatrixView< real64 const, globalIndex const > const Jr = system.reducedJacobian.toViewConst(); + + array1d< integer > isEnergyRow( J.numRows() ); + isEnergyRow.zero(); + for( localIndex k = 0; k < system.cellDof.size(); ++k ) + { + localIndex const massRow = system.cellDof[k]; + localIndex const energyRow = massRow + 1; + real64 const h = system.cellEnthalpy[k]; + isEnergyRow[energyRow] = 1; + + // the pattern of the mass row is contained in the energy row + ASSERT_EQ( J.numNonZeros( massRow ), J.numNonZeros( energyRow ) ); + for( localIndex j = 0; j < J.numNonZeros( energyRow ); ++j ) + { + ASSERT_EQ( J.getColumns( massRow )[j], J.getColumns( energyRow )[j] ); + real64 const expected = J.getEntries( energyRow )[j] - h * J.getEntries( massRow )[j]; + EXPECT_NEAR( Jr.getEntries( energyRow )[j], expected, 1.0e-12 * ( LvArray::math::abs( expected ) + 1.0 ) ); + } + real64 const expectedResidual = system.residual[energyRow] - h * system.residual[massRow]; + EXPECT_NEAR( system.reducedResidual[energyRow], expectedResidual, 1.0e-12 * ( LvArray::math::abs( expectedResidual ) + 1.0 ) ); + } + + for( localIndex i = 0; i < J.numRows(); ++i ) + { + if( isEnergyRow[i] == 1 ) + { + continue; + } + EXPECT_DOUBLE_EQ( system.reducedResidual[i], system.residual[i] ); + for( localIndex j = 0; j < J.numNonZeros( i ); ++j ) + { + EXPECT_DOUBLE_EQ( Jr.getEntries( i )[j], J.getEntries( i )[j] ); + } + } +} + +// a shift of the enthalpy reference changes h by a constant and adds that constant times the mass row to the +// energy row: the reduced system does not see it. The density and the viscosity do not depend on the temperature +// here, so that the reference temperature of the fluid only shifts its enthalpy and its internal energy +TEST( SinglePhaseMixedMFDEnthalpyReference, referenceInvariance ) +{ + ReducedSystem const systemA = assembleReducedSystem( { true, "0.1", 350.0, false } ); + ReducedSystem const systemB = assembleReducedSystem( { true, "0.1", 0.0, false } ); + + // the unreduced energy rows do differ + real64 maxDifference = 0.0; + for( localIndex k = 0; k < systemA.cellDof.size(); ++k ) + { + localIndex const energyRow = systemA.cellDof[k] + 1; + maxDifference = LvArray::math::max( maxDifference, LvArray::math::abs( systemA.residual[energyRow] - systemB.residual[energyRow] ) ); + } + EXPECT_GT( maxDifference, 1.0 ); + + compareLocalMatrices( systemA.reducedJacobian.toViewConst(), systemB.reducedJacobian.toViewConst(), 1.0e-7, 1.0e-6 ); + for( localIndex i = 0; i < systemA.reducedResidual.size(); ++i ) + { + real64 const scale = LvArray::math::abs( systemA.reducedResidual[i] ) + 1.0; + EXPECT_NEAR( systemA.reducedResidual[i], systemB.reducedResidual[i], 1.0e-7 * scale ) << "row " << i; + } +} + +//======================== exact steady states in one dimension ====================== +// Domain [0,1] x [0,0.1] x [0,0.1] with 5 cells along x, Dirichlet value at x = 0 and the tested condition at x = 1. +// The steady solutions are linear in x: both inner products reproduce them to round-off in a single time step +// much larger than the diffusion time: +// Neumann sigma f = g |f| x( s ) = x_0 - g s / c +// Robin sigma f = alpha |f| ( x_f - g ) x( 1 ) = ( c x_0 + alpha g ) / ( c + alpha ), linear in between +// with c the conductance per unit area: rho k / mu for the flow operator, the conductivity for the heat operator. + +namespace +{ + +enum class BarCase : integer { flowNeumann, heatNeumann, heatRobin, thermalViscosity }; + +real64 constexpr barArea = 0.01; +real64 constexpr barPressure = 1.0e7; +real64 constexpr barMassFlux = 1.0e-3; +real64 constexpr barMobility = 1000.0 * 1.0e-13 / 1.0e-3; +real64 constexpr barTemperature = 400.0; +real64 constexpr barConductivity = 2.0; +real64 constexpr barHeatFlux = 30.0; +real64 constexpr barAlpha = 4.0; +real64 constexpr barAmbient = 300.0; +real64 constexpr barPressureDrop = 1.0e4; +real64 constexpr barTemperatureDrop = 100.0; +real64 constexpr barViscosityCoefficient = 5.0e-3; + +string generateBarXml( BarCase const barCase, string const & consistencyTolerance ) +{ + bool const isThermal = barCase != BarCase::flowNeumann; + std::ostringstream os; + os << + R"xml( + + + + + + + + + + + + + + + + + + + + + + + + + + )xml"; + if( isThermal ) + { + os << + R"xml( + + + + "; + } + else + { + os << + R"xml( + + )xml"; + } + os << + R"xml( + + + + )xml"; + if( isThermal ) + { + os << + R"xml( + + )xml"; + } + switch( barCase ) + { + case BarCase::flowNeumann: + os << R"xml( + )xml"; + break; + case BarCase::heatNeumann: + os << R"xml( + )xml"; + break; + case BarCase::thermalViscosity: + os << + R"xml( + + )xml"; + break; + case BarCase::heatRobin: + os << + R"xml( + + )xml"; + break; + } + os << + R"xml( + + + )xml"; + return os.str(); +} + +/** + * @brief Solve one time step, then compare the cell values and the face fluxes with the exact steady solution. + * @param value0 the Dirichlet value at x = 0 + * @param outwardFlux the exact outward flux per unit area at x = 1 + * @param conductance the conductance per unit area + */ +void testBar( BarCase const barCase, + string const & consistencyTolerance, + real64 const value0, + real64 const outwardFlux, + real64 const conductance ) +{ + GeosxState state( std::make_unique< CommandLineOptions >( g_commandLineOptions ) ); + setupProblemFromXML( state.getProblemManager(), generateBarXml( barCase, consistencyTolerance ).c_str() ); + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + SinglePhaseMixedMFD & solver = state.getProblemManager().getPhysicsSolverManager().getGroup< SinglePhaseMixedMFD >( "flow" ); + + // time step much larger than the diffusion time: the accumulation term is below round-off + solver.execute( 0.0, 1.0e15, 0, 0, 0, domain ); + + bool const isFlow = barCase == BarCase::flowNeumann; + MeshLevel & mesh = domain.getMeshBody( 0 ).getBaseDiscretization(); + CellElementSubRegion const & subRegion = mesh.getElemManager().getRegion( 0 ).getSubRegion< CellElementSubRegion >( 0 ); + arrayView2d< real64 const > const center = subRegion.getElementCenter(); + arrayView1d< real64 const > const value = isFlow ? subRegion.getField< fields::flow::pressure >().toViewConst() + : subRegion.getField< fields::flow::temperature >().toViewConst(); + real64 const drop = LvArray::math::abs( outwardFlux / conductance ); + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + real64 const exact = value0 - outwardFlux * center[ei][0] / conductance; + EXPECT_NEAR( value[ei], exact, 1.0e-7 * drop ) << "cell at x = " << center[ei][0]; + } + + // the flux unknown of every face normal to x equals the exact flux, the others vanish + FaceManager const & faces = mesh.getFaceManager(); + arrayView2d< real64 const > const faceNormal = faces.faceNormal(); + arrayView1d< real64 const > const flux = isFlow ? faces.getField< fields::mixedMimetic::faceMassFlux >().toViewConst() + : faces.getField< fields::mixedMimetic::faceHeatFlux >().toViewConst(); + for( localIndex kf = 0; kf < faces.size(); ++kf ) + { + bool const normalToBar = LvArray::math::abs( faceNormal[kf][0] ) > 0.5; + real64 const exact = normalToBar ? LvArray::math::abs( outwardFlux ) * barArea : 0.0; + EXPECT_NEAR( LvArray::math::abs( flux[kf] ), exact, 1.0e-7 * LvArray::math::abs( outwardFlux ) * barArea ) << "face " << kf; + } +} + +/** + * @brief Flow driven by a pressure drop with mu = mu_ref ( 1 - beta ( T - T_ref ) ) and a fluid of zero heat capacity. + * + * Without advection T = T_0 - dT x exactly, so mu( x ) = mu_0 + mu' x is linear. The mass flux per unit area m is + * uniform and p( x ) = p_0 - ( m / ( rho k ) ) int_0^x mu. The two-point relations integrate mu by the midpoint rule, + * exact for a linear function over whole cells: m = rho k dp / int_0^1 mu is exact, and the half cell that ends at a + * cell centre gives p_K = p( x_K ) - m mu' h^2 / ( 8 rho k ). + */ +void testTemperatureDependentViscosity( string const & consistencyTolerance ) +{ + GeosxState state( std::make_unique< CommandLineOptions >( g_commandLineOptions ) ); + setupProblemFromXML( state.getProblemManager(), generateBarXml( BarCase::thermalViscosity, consistencyTolerance ).c_str() ); + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + SinglePhaseMixedMFD & solver = state.getProblemManager().getPhysicsSolverManager().getGroup< SinglePhaseMixedMFD >( "flow" ); + + solver.execute( 0.0, 1.0e15, 0, 0, 0, domain ); + + real64 const referenceViscosity = 1.0e-3; + real64 const mobilityCoefficient = 1000.0 * 1.0e-13; // rho k + real64 const mu0 = referenceViscosity * ( 1.0 - barViscosityCoefficient * ( barTemperature - 350.0 ) ); + real64 const dMu = referenceViscosity * barViscosityCoefficient * barTemperatureDrop; + real64 const massFlux = mobilityCoefficient * barPressureDrop / ( mu0 + 0.5 * dMu ); + real64 const h = 0.2; + + MeshLevel & mesh = domain.getMeshBody( 0 ).getBaseDiscretization(); + CellElementSubRegion const & subRegion = mesh.getElemManager().getRegion( 0 ).getSubRegion< CellElementSubRegion >( 0 ); + arrayView2d< real64 const > const center = subRegion.getElementCenter(); + arrayView1d< real64 const > const temp = subRegion.getField< fields::flow::temperature >(); + arrayView1d< real64 const > const pres = subRegion.getField< fields::flow::pressure >(); + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + real64 const x = center[ei][0]; + EXPECT_NEAR( temp[ei], barTemperature - barTemperatureDrop * x, 1.0e-7 * barTemperatureDrop ) << "cell at x = " << x; + real64 const exactPressure = barPressure - massFlux / mobilityCoefficient * ( mu0 * x + 0.5 * dMu * x * x ); + real64 const midpointDefect = massFlux * dMu * h * h / ( 8.0 * mobilityCoefficient ); + EXPECT_NEAR( pres[ei], exactPressure - midpointDefect, 1.0e-7 * barPressureDrop ) << "cell at x = " << x; + } + + FaceManager const & faces = mesh.getFaceManager(); + arrayView2d< real64 const > const faceNormal = faces.faceNormal(); + arrayView1d< real64 const > const flux = faces.getField< fields::mixedMimetic::faceMassFlux >(); + for( localIndex kf = 0; kf < faces.size(); ++kf ) + { + real64 const exact = LvArray::math::abs( faceNormal[kf][0] ) > 0.5 ? massFlux * barArea : 0.0; + EXPECT_NEAR( LvArray::math::abs( flux[kf] ), exact, 1.0e-7 * massFlux * barArea ) << "face " << kf; + } +} + +// outward heat flux of the Robin face: alpha ( x_f - g ) with x_f = ( c x_0 + alpha g ) / ( c + alpha ) +real64 constexpr barRobinFlux = barAlpha * ( ( barConductivity * barTemperature + barAlpha * barAmbient ) / ( barConductivity + barAlpha ) - barAmbient ); + +} // namespace + +TEST( SinglePhaseMixedMFDExactBar, temperatureDependentViscosity_tpfa ) +{ + testTemperatureDependentViscosity( "1.0e+20" ); +} + +TEST( SinglePhaseMixedMFDExactBar, temperatureDependentViscosity_consistent ) +{ + testTemperatureDependentViscosity( "1.0e-20" ); +} + +TEST( SinglePhaseMixedMFDExactBar, flowNeumann_tpfa ) +{ + testBar( BarCase::flowNeumann, "1.0e+20", barPressure, barMassFlux, barMobility ); +} + +TEST( SinglePhaseMixedMFDExactBar, flowNeumann_consistent ) +{ + testBar( BarCase::flowNeumann, "1.0e-20", barPressure, barMassFlux, barMobility ); +} + +TEST( SinglePhaseMixedMFDExactBar, heatNeumann_tpfa ) +{ + testBar( BarCase::heatNeumann, "1.0e+20", barTemperature, barHeatFlux, barConductivity ); +} + +TEST( SinglePhaseMixedMFDExactBar, heatNeumann_consistent ) +{ + testBar( BarCase::heatNeumann, "1.0e-20", barTemperature, barHeatFlux, barConductivity ); +} + +TEST( SinglePhaseMixedMFDExactBar, heatRobin_tpfa ) +{ + testBar( BarCase::heatRobin, "1.0e+20", barTemperature, barRobinFlux, barConductivity ); +} + +TEST( SinglePhaseMixedMFDExactBar, heatRobin_consistent ) +{ + testBar( BarCase::heatRobin, "1.0e-20", barTemperature, barRobinFlux, barConductivity ); +} + +//======================== hydrostatic equilibrium =================================== +// Fluid at rest in the unit cube: p = p_top on z = 1, every other boundary face impervious, constant density. +// The exact solution is m_f = 0 and p = p_top + rho |g| ( 1 - z ). With m = 0 the Darcy law of each cell reduces to +// pi_f = p_K - rho ( gamma_K - gamma_f ) for every inner product and every cell shape: the discrete pressure is exact +// at the cell centres, on planar and on non-planar faces alike. + +namespace +{ + +real64 constexpr hydroTopPressure = 1.0e7; +real64 constexpr hydroDensity = 1000.0; +real64 constexpr hydroGravity = 9.81; + +/** + * @brief Write a mesh of n^3 hexahedra of the unit cube whose nodes are displaced by at most amplitude / n. + * A node of a boundary plane stays on that plane: the domain is unchanged and the interior faces are non-planar. + */ +string writeDistortedHexMesh( integer const n, real64 const amplitude ) +{ + string const fileName = GEOS_FMT( "{}/hydrostatic_hexahedra_{}_{}.vtu", TEST_BINARY_DIR, n, integer( 100 * amplitude ) ); + integer const numNodes1d = n + 1; + real64 const h = 1.0 / n; + std::uint64_t seed = 12345; + auto const random = [&seed]() + { + seed = seed * 6364136223846793005ULL + 1442695040888963407ULL; + return 2.0 * ( real64( seed >> 11 ) / real64( 1ULL << 53 ) ) - 1.0; + }; + + std::ofstream os( fileName ); + os << std::setprecision( 17 ); + os << "\n\n" + << "\n\n" + << "\n\n"; + for( integer k = 0; k < numNodes1d; ++k ) + { + for( integer j = 0; j < numNodes1d; ++j ) + { + for( integer i = 0; i < numNodes1d; ++i ) + { + integer const index[3] = { i, j, k }; + for( integer d = 0; d < 3; ++d ) + { + bool const onBoundaryPlane = index[d] == 0 || index[d] == n; + real64 const shift = amplitude * h * random(); + os << index[d] * h + ( onBoundaryPlane ? 0.0 : shift ) << " "; + } + os << "\n"; + } + } + } + os << "\n\n\n\n"; + auto const node = [numNodes1d]( integer const i, integer const j, integer const k ) + { + return i + numNodes1d * ( j + numNodes1d * k ); + }; + for( integer k = 0; k < n; ++k ) + { + for( integer j = 0; j < n; ++j ) + { + for( integer i = 0; i < n; ++i ) + { + os << node( i, j, k ) << " " << node( i + 1, j, k ) << " " << node( i + 1, j + 1, k ) << " " << node( i, j + 1, k ) << " " + << node( i, j, k + 1 ) << " " << node( i + 1, j, k + 1 ) << " " << node( i + 1, j + 1, k + 1 ) << " " << node( i, j + 1, k + 1 ) << "\n"; + } + } + } + os << "\n\n"; + for( integer c = 1; c <= n * n * n; ++c ) + { + os << 8 * c << " "; + } + os << "\n\n\n"; + for( integer c = 0; c < n * n * n; ++c ) + { + os << "12 "; + } + os << "\n\n\n\n\n\n"; + return fileName; +} + +string generateHydrostaticXml( string const & meshFile, string const & consistencyTolerance ) +{ + std::ostringstream os; + os << + R"xml( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )xml"; + return os.str(); +} + +void testHydrostaticEquilibrium( string const & meshFile, string const & consistencyTolerance ) +{ + SCOPED_TRACE( GEOS_FMT( "mesh {}, consistencyTolerance {}", meshFile, consistencyTolerance ) ); + GeosxState state( std::make_unique< CommandLineOptions >( g_commandLineOptions ) ); + setupProblemFromXML( state.getProblemManager(), generateHydrostaticXml( meshFile, consistencyTolerance ).c_str() ); + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + SinglePhaseMixedMFD & solver = state.getProblemManager().getPhysicsSolverManager().getGroup< SinglePhaseMixedMFD >( "flow" ); + + solver.execute( 0.0, 1.0e6, 0, 0, 0, domain ); + + real64 const gradient = hydroDensity * hydroGravity; + real64 const tolerance = 1.0e-12 * hydroTopPressure; + MeshLevel & mesh = domain.getMeshBody( 0 ).getBaseDiscretization(); + + // least-squares line p = a + b z through the cell values + real64 sum1 = 0.0, sumZ = 0.0, sumZZ = 0.0, sumP = 0.0, sumZP = 0.0; + mesh.getElemManager().forElementSubRegions< CellElementSubRegion >( [&]( CellElementSubRegion const & subRegion ) + { + arrayView2d< real64 const > const center = subRegion.getElementCenter(); + arrayView1d< real64 const > const pres = subRegion.getField< fields::flow::pressure >(); + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + // exact pressure at the cell centre + real64 const z = center[ei][2]; + EXPECT_NEAR( pres[ei], hydroTopPressure + gradient * ( 1.0 - z ), tolerance ) << "cell at z = " << z; + + real64 const dp = pres[ei] - hydroTopPressure; + sum1 += 1.0; sumZ += z; sumZZ += z * z; sumP += dp; sumZP += z * dp; + } + } ); + + // the pressure is linear in z with the slope - rho |g| and the value p_top at z = 1 + real64 const slope = ( sum1 * sumZP - sumZ * sumP ) / ( sum1 * sumZZ - sumZ * sumZ ); + real64 const intercept = ( sumP - slope * sumZ ) / sum1; + EXPECT_NEAR( slope, -gradient, 1.0e-9 * gradient ); + EXPECT_NEAR( intercept + slope, 0.0, 1.0e-9 * gradient ); + mesh.getElemManager().forElementSubRegions< CellElementSubRegion >( [&]( CellElementSubRegion const & subRegion ) + { + arrayView2d< real64 const > const center = subRegion.getElementCenter(); + arrayView1d< real64 const > const pres = subRegion.getField< fields::flow::pressure >(); + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + EXPECT_NEAR( pres[ei] - hydroTopPressure, intercept + slope * center[ei][2], tolerance ); + } + } ); + + // the fluid is at rest: the scale is the flux driven by the hydrostatic pressure difference + arrayView1d< real64 const > const flux = mesh.getFaceManager().getField< fields::mixedMimetic::faceMassFlux >(); + real64 const fluxScale = hydroDensity * 1.0e-13 / 1.0e-3 * gradient; + for( localIndex kf = 0; kf < flux.size(); ++kf ) + { + EXPECT_NEAR( flux[kf], 0.0, 1.0e-10 * fluxScale ) << "face " << kf; + } +} + +} // namespace + +TEST( SinglePhaseMixedMFDHydrostatic, hexahedraPyramidsTetrahedra ) +{ + string const meshFile = string( TEST_BINARY_DIR ) + "/hex_pyr_tet_nested_mixed.vtu"; + for( string const tolerance : { "1.0e+20", "0.1", "1.0e-20" } ) + { + testHydrostaticEquilibrium( meshFile, tolerance ); + } +} + +TEST( SinglePhaseMixedMFDHydrostatic, distortedHexahedra ) +{ + for( real64 const amplitude : { 0.0, 0.15, 0.3 } ) + { + string const meshFile = writeDistortedHexMesh( 4, amplitude ); + for( string const tolerance : { "1.0e+20", "0.1", "1.0e-20" } ) + { + testHydrostaticEquilibrium( meshFile, tolerance ); + } + } +} + +int main( int argc, char * * argv ) +{ + ::testing::InitGoogleTest( &argc, argv ); + g_commandLineOptions = *geos::basicSetup( argc, argv ); + int const result = RUN_ALL_TESTS(); + geos::basicCleanup(); + return result; +} diff --git a/src/coreComponents/linearAlgebra/CMakeLists.txt b/src/coreComponents/linearAlgebra/CMakeLists.txt index c22ce09869e..56d320f7b98 100644 --- a/src/coreComponents/linearAlgebra/CMakeLists.txt +++ b/src/coreComponents/linearAlgebra/CMakeLists.txt @@ -162,6 +162,7 @@ if( ENABLE_HYPRE ) interfaces/hypre/mgrStrategies/CompositionalMultiphaseReservoirHybridFVM.hpp interfaces/hypre/mgrStrategies/HybridSinglePhasePoromechanics.hpp interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp + interfaces/hypre/mgrStrategies/ThermalSinglePhaseMixedMFD.hpp interfaces/hypre/mgrStrategies/Hydrofracture.hpp interfaces/hypre/mgrStrategies/ImmiscibleMultiphaseFVM.hpp interfaces/hypre/mgrStrategies/LagrangianContactMechanics.hpp diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp index 066e7ad7e4c..67872f21d97 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp @@ -35,6 +35,7 @@ #include "linearAlgebra/interfaces/hypre/mgrStrategies/ReactiveCompositionalMultiphaseOBL.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseHybridFVM.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp" +#include "linearAlgebra/interfaces/hypre/mgrStrategies/ThermalSinglePhaseMixedMFD.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhasePoromechanics.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhasePoromechanicsEmbeddedFractures.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhasePoromechanicsConformingFractures.hpp" @@ -197,6 +198,13 @@ void hypre::mgr::createMGR( LinearSolverParameters const & params, setStrategy< SinglePhaseMixedMFD >( params.mgr, numComponentsPerField, precond, mgrData ); break; } + case LinearSolverParameters::MGR::StrategyType::thermalSinglePhaseMixedMFD: + { + GEOS_ERROR_IF( params.mgr.customPointMarkers.empty(), + "The thermalSinglePhaseMixedMFD MGR strategy requires solver-provided point markers" ); + setStrategy< ThermalSinglePhaseMixedMFD >( params.mgr, numComponentsPerField, precond, mgrData ); + break; + } case LinearSolverParameters::MGR::StrategyType::singlePhasePoromechanics: { setStrategy< SinglePhasePoromechanics >( params.mgr, numComponentsPerField, precond, mgrData ); diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp b/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp index 616fcc76307..88ce2b46d9a 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp @@ -23,6 +23,7 @@ #include "linearAlgebra/interfaces/hypre/mgrStrategies/ReactiveCompositionalMultiphaseOBL.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseHybridFVM.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseMixedMFD.hpp" +#include "linearAlgebra/interfaces/hypre/mgrStrategies/ThermalSinglePhaseMixedMFD.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhasePoromechanics.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhasePoromechanicsConformingFractures.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhasePoromechanicsConformingFracturesALM.hpp" @@ -446,6 +447,7 @@ bool strategyUsesCompositionalSemanticLabels( LinearSolverParameters::MGR::Strat case StrategyType::thermalSinglePhaseReservoirFVM: case StrategyType::singlePhaseHybridFVM: case StrategyType::singlePhaseMixedMFD: + case StrategyType::thermalSinglePhaseMixedMFD: case StrategyType::singlePhaseReservoirHybridFVM: case StrategyType::singlePhasePoromechanics: case StrategyType::thermalSinglePhasePoromechanics: @@ -1063,6 +1065,7 @@ MGRSpecialization getSpecialization( LinearSolverParameters::MGR::StrategyType c case StrategyType::singlePhaseReservoirFVM: case StrategyType::singlePhaseHybridFVM: case StrategyType::singlePhaseMixedMFD: + case StrategyType::thermalSinglePhaseMixedMFD: case StrategyType::singlePhaseReservoirHybridFVM: case StrategyType::compositionalMultiphaseFVM: case StrategyType::compositionalMultiphaseHybridFVM: @@ -1522,6 +1525,7 @@ bool buildMGRPreconditionerYaml( LinearSolverParameters const & params, GEOS_HYPREDRIVE_MGR_CASE( thermalSinglePhaseReservoirFVM, ThermalSinglePhaseReservoirFVM ); GEOS_HYPREDRIVE_MGR_CASE( singlePhaseHybridFVM, SinglePhaseHybridFVM ); GEOS_HYPREDRIVE_MGR_CASE( singlePhaseMixedMFD, SinglePhaseMixedMFD ); + GEOS_HYPREDRIVE_MGR_CASE( thermalSinglePhaseMixedMFD, ThermalSinglePhaseMixedMFD ); GEOS_HYPREDRIVE_MGR_CASE( singlePhaseReservoirHybridFVM, SinglePhaseReservoirHybridFVM ); GEOS_HYPREDRIVE_MGR_CASE( singlePhasePoromechanics, SinglePhasePoromechanics ); GEOS_HYPREDRIVE_MGR_CASE( thermalSinglePhasePoromechanics, ThermalSinglePhasePoromechanics ); diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/ThermalSinglePhaseMixedMFD.hpp b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/ThermalSinglePhaseMixedMFD.hpp new file mode 100644 index 00000000000..d27c7ba914d --- /dev/null +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/ThermalSinglePhaseMixedMFD.hpp @@ -0,0 +1,108 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file ThermalSinglePhaseMixedMFD.hpp + */ + +#ifndef GEOS_LINEARALGEBRA_INTERFACES_HYPREMGRTHERMALSINGLEPHASEMIXEDMFD_HPP_ +#define GEOS_LINEARALGEBRA_INTERFACES_HYPREMGRTHERMALSINGLEPHASEMIXEDMFD_HPP_ + +#include "linearAlgebra/interfaces/hypre/HypreMGR.hpp" + +namespace geos +{ + +namespace hypre +{ + +namespace mgr +{ + +/** + * @brief MGR strategy of the thermal mixed mimetic single-phase solver. + * + * Unknowns: face mass and heat fluxes, cell pressure, temperature and enthalpy. Level 0 eliminates + * every face flux as in the isothermal strategy (symmetric Gauss-Seidel on the flux blocks, Jacobi + * interpolation from their diagonals); level 1 eliminates the enthalpy, whose closure rows + * h - h_eos(p, T) = 0 have a unit diagonal, exactly; the coarse solve is one BoomerAMG V-cycle on + * the pressure-temperature system. + * + * Point markers provided by the solver (LinearSolverParameters::MGR::customPointMarkers): + * 0 = condensed face mass flux, 1 = saddle-point face mass flux, + * 2 = cell pressure, 3 = cell temperature, 4 = cell enthalpy, + * 5 = condensed face heat flux, 6 = saddle-point face heat flux + */ +class ThermalSinglePhaseMixedMFD : public MGRStrategyBase< 2 > +{ +public: + /** + * @brief Constructor. + */ + explicit ThermalSinglePhaseMixedMFD( arrayView1d< int const > const & ) + : MGRStrategyBase( LvArray::integerConversion< HYPRE_Int >( 7 ) ) + { + // Level 0: eliminate every face flux, keep the cell unknowns + m_labels[0].push_back( 2 ); + m_labels[0].push_back( 3 ); + m_labels[0].push_back( 4 ); + // Level 1: eliminate the enthalpy, keep pressure and temperature + m_labels[1].push_back( 2 ); + m_labels[1].push_back( 3 ); + setupLabels(); + + m_levelFRelaxType[0] = MGRFRelaxationType::hybridSymmetricGaussSeidel; + m_levelFRelaxIters[0] = 1; + m_levelInterpType[0] = MGRInterpolationType::jacobi; + m_levelRestrictType[0] = MGRRestrictionType::injection; + m_levelCoarseGridMethod[0] = MGRCoarseGridMethod::galerkin; + m_levelGlobalSmootherType[0] = MGRGlobalSmootherType::none; + + m_levelFRelaxType[1] = MGRFRelaxationType::jacobi; + m_levelFRelaxIters[1] = 1; + m_levelInterpType[1] = MGRInterpolationType::jacobi; + m_levelRestrictType[1] = MGRRestrictionType::injection; + m_levelCoarseGridMethod[1] = MGRCoarseGridMethod::galerkin; + m_levelGlobalSmootherType[1] = MGRGlobalSmootherType::none; + } + + /** + * @brief Setup the MGR strategy. + * @param precond preconditioner wrapper + * @param mgrData auxiliary MGR data + */ + void setup( LinearSolverParameters::MGR const &, + HyprePrecWrapper & precond, + HypreMGRData & mgrData ) + { + setReduction( precond, mgrData ); + + // one BoomerAMG V-cycle on the pressure-temperature Schur complement, without aggressive coarsening + BoomerAMGParameters amgParameters = pressureTemperatureAMGParameters(); + amgParameters.aggressiveNumLevels = 0; + configureBoomerAMG( mgrData.coarseSolver, amgParameters ); + mgrData.coarseSolver.setup = HYPRE_BoomerAMGSetup; + mgrData.coarseSolver.solve = HYPRE_BoomerAMGSolve; + mgrData.coarseSolver.destroy = HYPRE_BoomerAMGDestroy; + } +}; + +} // namespace mgr + +} // namespace hypre + +} // namespace geos + +#endif /*GEOS_LINEARALGEBRA_INTERFACES_HYPREMGRTHERMALSINGLEPHASEMIXEDMFD_HPP_*/ diff --git a/src/coreComponents/linearAlgebra/unitTests/testHypredrive.cpp b/src/coreComponents/linearAlgebra/unitTests/testHypredrive.cpp index 0d33a104257..3bb8e9692b0 100644 --- a/src/coreComponents/linearAlgebra/unitTests/testHypredrive.cpp +++ b/src/coreComponents/linearAlgebra/unitTests/testHypredrive.cpp @@ -289,7 +289,7 @@ TEST( HypredriveYaml, BuildsGeneratedYamlForEveryMGRStrategy ) } hypre::hypredrive::InputArgsParseTarget target; - if( strategy == StrategyType::singlePhaseMixedMFD ) + if( strategy == StrategyType::singlePhaseMixedMFD || strategy == StrategyType::thermalSinglePhaseMixedMFD ) { // solver-provided point markers have no field-label form: this strategy stays on legacy hypre EXPECT_FALSE( hypre::hypredrive::buildInputArgsParseTarget( makeMgrParameters( strategy ), diff --git a/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp b/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp index 4ab68c33926..7e4d6592950 100644 --- a/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp +++ b/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp @@ -300,6 +300,7 @@ struct LinearSolverParameters thermalSinglePhaseReservoirFVM, ///< finite volume thermal single-phase flow with wells singlePhaseHybridFVM, ///< hybrid finite volume single-phase flow singlePhaseMixedMFD, ///< mixed mimetic finite difference single-phase flow + thermalSinglePhaseMixedMFD, ///< mixed mimetic finite difference thermal single-phase flow singlePhaseReservoirHybridFVM, ///< hybrid finite volume single-phase flow with wells singlePhasePoromechanics, ///< single phase poromechanics with finite volume single phase flow thermalSinglePhasePoromechanics, ///< thermal single phase poromechanics with finite volume single phase flow @@ -586,6 +587,7 @@ ENUM_STRINGS( LinearSolverParameters::MGR::StrategyType, "thermalSinglePhaseReservoirFVM", "singlePhaseHybridFVM", "singlePhaseMixedMFD", + "thermalSinglePhaseMixedMFD", "singlePhaseReservoirHybridFVM", "singlePhasePoromechanics", "thermalSinglePhasePoromechanics", diff --git a/src/coreComponents/mixedMimetic/MixedMimeticBoundaryConditions.hpp b/src/coreComponents/mixedMimetic/MixedMimeticBoundaryConditions.hpp new file mode 100644 index 00000000000..3591dbdd0d8 --- /dev/null +++ b/src/coreComponents/mixedMimetic/MixedMimeticBoundaryConditions.hpp @@ -0,0 +1,135 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file MixedMimeticBoundaryConditions.hpp + * + * Boundary conditions of an operator of the mixed formulation. Each operator relates the normal flux f through a + * face to the trace x_f of its primal variable x: M ( sigma f ) = x_K - x_f in the face equations, or + * A f = x_L - x_R on a face condensed into a two-point flux relation. A boundary condition is a relation between + * the trace and the normal flux, with sigma f the outward normal flux and |f| the face area: + * + * Dirichlet x_f = g g the prescribed trace + * Neumann sigma f = g |f| g the prescribed outward normal flux per unit area + * Robin sigma f = alpha |f| ( x_f - g ) alpha > 0 the transfer coefficient, g the exterior value + * + * The Robin condition contains the other two as limits: Dirichlet for alpha -> infinity and the homogeneous + * Neumann condition for alpha -> 0. In the mixed formulation the Dirichlet and Robin conditions are natural and + * the Neumann condition is essential: the flux degree of freedom is prescribed, sigma f - g |f| = 0. On a + * non-condensed face the Robin trace is eliminated, x_f = g + sigma f / ( alpha |f| ), which adds one diagonal + * term to the face equation; on a condensed face the resistance 1 / ( alpha |f| ) is added to A, with x_R = g. + * The homogeneous Neumann condition applies to an impervious or adiabatic face. Data ( g, alpha ) that depend on the + * time or on the solution history, as for a Carter-Tracy aquifer, are updated at every time step. + */ + +#ifndef GEOS_MIXEDMIMETIC_MIXEDMIMETICBOUNDARYCONDITIONS_HPP_ +#define GEOS_MIXEDMIMETIC_MIXEDMIMETICBOUNDARYCONDITIONS_HPP_ + +#include "common/DataTypes.hpp" +#include "common/GEOS_RAJA_Interface.hpp" + +namespace geos +{ + +namespace mixedMimeticBoundary +{ + +/// type of the condition on a face +struct BoundaryType +{ + static constexpr integer interior = 0; ///< interior face, no condition + static constexpr integer dirichlet = 1; ///< prescribed trace + static constexpr integer neumann = 2; ///< prescribed outward normal flux per unit area + static constexpr integer robin = 3; ///< linear relation between the outward normal flux and the trace +}; + +/** + * @struct FaceBoundaryCondition + * @brief Boundary condition of one operator on one face. + */ +struct FaceBoundaryCondition +{ + integer type = BoundaryType::interior; ///< BoundaryType + real64 value = 0.0; ///< g: prescribed trace (Dirichlet), outward normal flux per unit area (Neumann), exterior value + ///< (Robin) + real64 coefficient = 0.0; ///< transfer coefficient alpha of the Robin condition + + /// essential condition: the flux degree of freedom is prescribed + GEOS_HOST_DEVICE + bool isEssential() const { return type == BoundaryType::neumann; } + + /// natural condition: the face may be condensed into a two-point flux relation + GEOS_HOST_DEVICE + bool isCondensable() const { return type == BoundaryType::dirichlet || type == BoundaryType::robin; } + + /** + * @brief Prescribed value of the flux degree of freedom of an essential face, from sigma f = g |f|. + * @param sigma orientation of the face relative to the cell + * @param area face area + */ + GEOS_HOST_DEVICE + real64 essentialFlux( real64 const sigma, real64 const area ) const + { + return sigma * value * area; + } + + /** + * @brief Trace in the equation of a non-condensed face: g (Dirichlet), g + sigma f / ( alpha |f| ) (Robin). + * @param sigma orientation of the face relative to the cell + * @param area face area + * @param outwardFlux the outward normal flux sigma f + * @param dTrace_dOutwardFlux the derivative of the trace with respect to sigma f + */ + GEOS_HOST_DEVICE + real64 trace( real64 const sigma, real64 const area, real64 const outwardFlux, real64 & dTrace_dOutwardFlux ) const + { + GEOS_UNUSED_VAR( sigma ); + dTrace_dOutwardFlux = ( type == BoundaryType::robin ) ? 1.0 / ( coefficient * area ) : 0.0; + return value + dTrace_dOutwardFlux * outwardFlux; + } + + /** + * @brief Resistance added to the two-point flux relation of a condensed face: 1 / ( alpha |f| ) (Robin), 0 otherwise. + * @param area face area + */ + GEOS_HOST_DEVICE + real64 resistance( real64 const area ) const + { + return ( type == BoundaryType::robin ) ? 1.0 / ( coefficient * area ) : 0.0; + } +}; + +/** + * @struct FaceBoundaryView + * @brief Device view on the boundary conditions of one operator, stored per face. + */ +struct FaceBoundaryView +{ + arrayView1d< integer const > type; + arrayView1d< real64 const > value; + arrayView1d< real64 const > coefficient; + + GEOS_HOST_DEVICE + FaceBoundaryCondition operator[]( localIndex const kf ) const + { + return { type[kf], value[kf], coefficient[kf] }; + } +}; + +} // namespace mixedMimeticBoundary + +} // namespace geos + +#endif // GEOS_MIXEDMIMETIC_MIXEDMIMETICBOUNDARYCONDITIONS_HPP_ diff --git a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp index a27d1b1286a..2d2c2bf1918 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticFields.hpp @@ -41,6 +41,142 @@ DECLARE_FIELD( faceMassFlux, WRITE_AND_READ, "Face mass flux unknown of the mixed mimetic formulation (positive in the direction of the global face normal)" ); +DECLARE_FIELD( faceHeatFlux, + "faceHeatFlux", + array1d< real64 >, + 0, + LEVEL_0, + WRITE_AND_READ, + "Face conductive heat flux unknown of the mixed mimetic formulation (positive in the direction of the global face normal)" ); + +DECLARE_FIELD( faceHeatFlux_n, + "faceHeatFlux_n", + array1d< real64 >, + 0, + NOPLOT, + WRITE_AND_READ, + "Face conductive heat flux at the previous converged time step" ); + +DECLARE_FIELD( enthalpy, + "enthalpy", + array1d< real64 >, + 0, + LEVEL_0, + WRITE_AND_READ, + "Cell specific enthalpy unknown h, closed by h - h_eos(p, T) = 0" ); + +DECLARE_FIELD( enthalpy_n, + "enthalpy_n", + array1d< real64 >, + 0, + NOPLOT, + WRITE_AND_READ, + "Cell specific enthalpy at the previous converged time step" ); + +DECLARE_FIELD( bcEnthalpy, + "bcEnthalpy", + array1d< real64 >, + 0, + NOPLOT, + WRITE_AND_READ, + "Specific enthalpy of the fluid entering through a boundary face (used where the Darcy flux enters the domain)" ); + +DECLARE_FIELD( bcHeatFlux, + "bcHeatFlux", + array1d< real64 >, + 0, + NOPLOT, + WRITE_AND_READ, + "Neumann condition: prescribed outward conductive heat flux per unit area of a boundary face" ); + +DECLARE_FIELD( bcHeatTransferCoefficient, + "bcHeatTransferCoefficient", + array1d< real64 >, + 0, + NOPLOT, + WRITE_AND_READ, + "Robin condition: heat transfer coefficient h_c of a boundary face, q . n = h_c (T - T_inf)" ); + +DECLARE_FIELD( bcAmbientTemperature, + "bcAmbientTemperature", + array1d< real64 >, + 0, + NOPLOT, + WRITE_AND_READ, + "Robin condition: ambient temperature T_inf of a boundary face" ); + +DECLARE_FIELD( bcMassFlux, + "bcMassFlux", + array1d< real64 >, + 0, + NOPLOT, + WRITE_AND_READ, + "Neumann condition: prescribed outward mass flux per unit area of a boundary face" ); + +DECLARE_FIELD( flowBoundaryType, + "flowBoundaryType", + array1d< integer >, + 0, + NOPLOT, + NO_WRITE, + "Condition of the flow operator on a face: 0 = interior, 1 = Dirichlet (bcPressure), 2 = Neumann (bcMassFlux, 0 = no flow), 3 = Robin" ); + +DECLARE_FIELD( flowBoundaryValue, + "flowBoundaryValue", + array1d< real64 >, + 0, + NOPLOT, + NO_WRITE, + "Value g of the flow condition: the pressure trace (Dirichlet, Robin) or the outward mass flux per unit area (Neumann)" ); + +DECLARE_FIELD( flowBoundaryCoefficient, + "flowBoundaryCoefficient", + array1d< real64 >, + 0, + NOPLOT, + NO_WRITE, + "Coefficient alpha of a Robin flow condition, sigma m = alpha |f| (pi_f - g)" ); + +DECLARE_FIELD( heatBoundaryType, + "heatBoundaryType", + array1d< integer >, + 0, + NOPLOT, + NO_WRITE, + "Condition of the heat operator on a face: 0 = interior, 1 = Dirichlet (bcTemperature), 2 = Neumann (bcHeatFlux, 0 = adiabatic), 3 = Robin" ); + +DECLARE_FIELD( heatBoundaryValue, + "heatBoundaryValue", + array1d< real64 >, + 0, + NOPLOT, + NO_WRITE, + "Value g of the heat condition: the temperature trace (Dirichlet), the ambient temperature (Robin) or the outward heat flux per unit area (Neumann)" ); + +DECLARE_FIELD( heatBoundaryCoefficient, + "heatBoundaryCoefficient", + array1d< real64 >, + 0, + NOPLOT, + NO_WRITE, + "Coefficient alpha of a Robin heat condition, the heat transfer coefficient h_c" ); + +DECLARE_FIELD( isEnthalpyBcFace, + "isEnthalpyBcFace", + array1d< integer >, + 0, + NOPLOT, + NO_WRITE, + "1 on a boundary face carrying a prescribed inlet enthalpy (bcEnthalpy)" ); + +DECLARE_FIELD( faceHeatDofScale, + "faceHeatDofScale", + array1d< real64 >, + 0, + NOPLOT, + NO_WRITE, + "Characteristic scale of the face heat flux unknown, T_scale / |M_ff|, used by the residual-norm weights" ); + DECLARE_FIELD( faceMassFlux_n, "faceMassFlux_n", array1d< real64 >, diff --git a/src/coreComponents/mixedMimetic/unitTests/CMakeLists.txt b/src/coreComponents/mixedMimetic/unitTests/CMakeLists.txt index 5c681f4068c..5d478f0322f 100644 --- a/src/coreComponents/mixedMimetic/unitTests/CMakeLists.txt +++ b/src/coreComponents/mixedMimetic/unitTests/CMakeLists.txt @@ -1,11 +1,15 @@ #[[ Package: mixedMimetic unitTests -Builds the unit tests of the cell classification (ConsistencyAdaptation) +Builds the unit tests of the cell classification (ConsistencyAdaptation) and of the boundary conditions #]] set( mixedMimetic_gtest_tests testConsistencyAdaptation.cpp ) +# tests without a mesh: no parallel variant +set( mixedMimetic_serial_gtest_tests + testMixedMimeticBoundaryConditions.cpp ) + set( dependencyList mainInterface gtest ${parallelDeps} ) foreach( test_source ${mixedMimetic_gtest_tests} ) @@ -25,3 +29,14 @@ foreach( test_source ${mixedMimetic_gtest_tests} ) NUM_MPI_TASKS ${nranks} ) endif() endforeach() + +foreach( test_source ${mixedMimetic_serial_gtest_tests} ) + get_filename_component( test_name ${test_source} NAME_WE ) + blt_add_executable( NAME ${test_name} + SOURCES ${test_source} + OUTPUT_DIR ${TEST_OUTPUT_DIRECTORY} + DEPENDS_ON ${dependencyList} + FOLDER UnitTests ) + geos_add_test( NAME ${test_name} + COMMAND ${test_name} ) +endforeach() diff --git a/src/coreComponents/mixedMimetic/unitTests/testMixedMimeticBoundaryConditions.cpp b/src/coreComponents/mixedMimetic/unitTests/testMixedMimeticBoundaryConditions.cpp new file mode 100644 index 00000000000..75e1c09f315 --- /dev/null +++ b/src/coreComponents/mixedMimetic/unitTests/testMixedMimeticBoundaryConditions.cpp @@ -0,0 +1,157 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +// Source includes +#include "mixedMimetic/MixedMimeticBoundaryConditions.hpp" +#include "mainInterface/initialization.hpp" + +// TPL includes +#include + +using namespace geos; +using namespace geos::mixedMimeticBoundary; + +namespace +{ + +real64 constexpr area = 2.5; +real64 constexpr tol = 1.0e-13; + +// outward flux of a cell of value xK through a non-condensed boundary face: r F - xK + trace( F ) = 0 +real64 liveRowFlux( FaceBoundaryCondition const & bc, real64 const halfResistance, real64 const xK ) +{ + real64 dTrace = 0.0; + real64 const trace0 = bc.trace( 1.0, area, 0.0, dTrace ); + return ( xK - trace0 ) / ( halfResistance + dTrace ); +} + +// the same flux from the condensed two-point closure: ( r + resistance ) F = xK - g +real64 condensedFlux( FaceBoundaryCondition const & bc, real64 const halfResistance, real64 const xK ) +{ + return ( xK - bc.value ) / ( halfResistance + bc.resistance( area ) ); +} + +} + +TEST( MixedMimeticBoundaryConditions, interiorByDefault ) +{ + FaceBoundaryCondition const bc; + EXPECT_EQ( bc.type, BoundaryType::interior ); + EXPECT_FALSE( bc.isEssential() ); + EXPECT_FALSE( bc.isCondensable() ); +} + +TEST( MixedMimeticBoundaryConditions, dirichlet ) +{ + FaceBoundaryCondition const bc{ BoundaryType::dirichlet, 3.0e5, 0.0 }; + EXPECT_FALSE( bc.isEssential() ); + EXPECT_TRUE( bc.isCondensable() ); + + // x_f = g independently of the flux + real64 dTrace = 1.0; + EXPECT_DOUBLE_EQ( bc.trace( -1.0, area, 7.0, dTrace ), 3.0e5 ); + EXPECT_DOUBLE_EQ( dTrace, 0.0 ); + EXPECT_DOUBLE_EQ( bc.resistance( area ), 0.0 ); +} + +TEST( MixedMimeticBoundaryConditions, neumann ) +{ + FaceBoundaryCondition const bc{ BoundaryType::neumann, -4.0, 0.0 }; + EXPECT_TRUE( bc.isEssential() ); + EXPECT_FALSE( bc.isCondensable() ); + + // the face dof is sigma g |f|, so that the outward flux sigma f is g |f| for both orientations + for( real64 const sigma : { 1.0, -1.0 } ) + { + EXPECT_DOUBLE_EQ( sigma * bc.essentialFlux( sigma, area ), -4.0 * area ); + } + + // no-flow + FaceBoundaryCondition const noFlow{ BoundaryType::neumann, 0.0, 0.0 }; + EXPECT_DOUBLE_EQ( noFlow.essentialFlux( -1.0, area ), 0.0 ); +} + +TEST( MixedMimeticBoundaryConditions, robin ) +{ + real64 const alpha = 25.0; + real64 const g = 290.0; + FaceBoundaryCondition const bc{ BoundaryType::robin, g, alpha }; + EXPECT_FALSE( bc.isEssential() ); + EXPECT_TRUE( bc.isCondensable() ); + + // the trace satisfies the law sigma f = alpha |f| ( x_f - g ) + real64 const outwardFlux = 12.0; + real64 dTrace = 0.0; + real64 const trace = bc.trace( 1.0, area, outwardFlux, dTrace ); + EXPECT_NEAR( alpha * area * ( trace - g ), outwardFlux, tol * outwardFlux ); + EXPECT_DOUBLE_EQ( dTrace, 1.0 / ( alpha * area ) ); + EXPECT_DOUBLE_EQ( bc.resistance( area ), 1.0 / ( alpha * area ) ); +} + +TEST( MixedMimeticBoundaryConditions, liveAndCondensedClosuresAgree ) +{ + real64 const halfResistance = 0.4; + real64 const xK = 350.0; + FaceBoundaryCondition const conditions[2] = { { BoundaryType::dirichlet, 300.0, 0.0 }, + { BoundaryType::robin, 300.0, 5.0 } }; + for( FaceBoundaryCondition const & bc : conditions ) + { + real64 const flux = liveRowFlux( bc, halfResistance, xK ); + EXPECT_NEAR( flux, condensedFlux( bc, halfResistance, xK ), tol * flux ); + } +} + +TEST( MixedMimeticBoundaryConditions, robinLimits ) +{ + real64 const halfResistance = 0.4; + real64 const xK = 350.0; + real64 const g = 300.0; + FaceBoundaryCondition const dirichlet{ BoundaryType::dirichlet, g, 0.0 }; + real64 const dirichletFlux = liveRowFlux( dirichlet, halfResistance, xK ); + + // alpha -> infinity: Dirichlet + FaceBoundaryCondition const stiff{ BoundaryType::robin, g, 1.0e14 }; + EXPECT_NEAR( liveRowFlux( stiff, halfResistance, xK ), dirichletFlux, 1.0e-12 * dirichletFlux ); + + // alpha -> 0: the flux vanishes as alpha |f| ( x_K - g ) + real64 const alpha = 1.0e-10; + FaceBoundaryCondition const soft{ BoundaryType::robin, g, alpha }; + EXPECT_NEAR( liveRowFlux( soft, halfResistance, xK ), alpha * area * ( xK - g ), 1.0e-9 * alpha * area * ( xK - g ) ); +} + +TEST( MixedMimeticBoundaryConditions, faceView ) +{ + array1d< integer > type( 3 ); + array1d< real64 > value( 3 ); + array1d< real64 > coefficient( 3 ); + type[0] = BoundaryType::interior; + type[1] = BoundaryType::neumann; value[1] = 2.0; + type[2] = BoundaryType::robin; value[2] = 290.0; coefficient[2] = 25.0; + + FaceBoundaryView const view{ type.toViewConst(), value.toViewConst(), coefficient.toViewConst() }; + EXPECT_EQ( view[0].type, BoundaryType::interior ); + EXPECT_TRUE( view[1].isEssential() ); + EXPECT_DOUBLE_EQ( view[1].value, 2.0 ); + EXPECT_DOUBLE_EQ( view[2].resistance( area ), 1.0 / ( 25.0 * area ) ); +} + +int main( int argc, char * * argv ) +{ + ::testing::InitGoogleTest( &argc, argv ); + geos::basicSetup( argc, argv ); + int const result = RUN_ALL_TESTS(); + geos::basicCleanup(); + return result; +} diff --git a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt index 9d34bf19582..465f29b069b 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt +++ b/src/coreComponents/physicsSolvers/fluidFlow/CMakeLists.txt @@ -61,6 +61,7 @@ set( fluidFlowSolvers_headers kernels/singlePhase/ResidualNormKernel.hpp kernels/singlePhase/SinglePhaseHybridFVMKernels.hpp kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp + kernels/singlePhase/ThermalSinglePhaseMixedMFDKernels.hpp kernels/singlePhase/SolidInternalEnergyUpdateKernel.hpp kernels/singlePhase/SolutionCheckKernel.hpp kernels/singlePhase/SolutionScalingKernel.hpp diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index e1bf58bc909..fce3632bf81 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -24,6 +24,7 @@ #include "common/logger/Logger.hpp" #include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" #include "constitutive/permeability/PermeabilityFields.hpp" +#include "constitutive/thermalConductivity/SinglePhaseThermalConductivityBase.hpp" #include "fieldSpecification/AquiferBoundaryCondition.hpp" #include "fieldSpecification/FieldSpecificationImpl.hpp" #include "fieldSpecification/FieldSpecificationManager.hpp" @@ -33,9 +34,11 @@ #include "mixedMimetic/MixedMimeticDiscretization.hpp" #include "mixedMimetic/MixedMimeticDiscretizationManager.hpp" #include "mixedMimetic/MixedMimeticFields.hpp" +#include "mixedMimetic/MixedMimeticBoundaryConditions.hpp" #include "mixedMimetic/consistency/ConsistencyAdaptation.hpp" #include "physicsSolvers/LogLevelsInfo.hpp" #include "physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/ThermalSinglePhaseMixedMFDKernels.hpp" #include "physicsSolvers/fluidFlow/kernels/singlePhase/ResidualNormKernel.hpp" namespace geos @@ -45,6 +48,8 @@ using namespace dataRepository; using namespace constitutive; using namespace fields; using namespace mimeticInnerProduct; +using BoundaryType = mixedMimeticBoundary::BoundaryType; +using CellDof = thermalSinglePhaseMixedMFDKernels::CellDof; SinglePhaseMixedMFD::SinglePhaseMixedMFD( const string & name, Group * const parent ): @@ -74,6 +79,11 @@ void SinglePhaseMixedMFD::registerDataOnMesh( Group & meshBodies ) subRegion.registerField< mixedMimetic::prescribedMfdFlag >( getName() ); subRegion.registerField< mixedMimetic::consistencyIndicator >( getName() ); subRegion.registerField< mixedMimetic::degeneracyIndicator >( getName() ); + if( m_isThermal ) + { + subRegion.registerField< mixedMimetic::enthalpy >( getName() ); + subRegion.registerField< mixedMimetic::enthalpy_n >( getName() ); + } } ); // 2) Register the face data @@ -83,9 +93,12 @@ void SinglePhaseMixedMFD::registerDataOnMesh( Group & meshBodies ) faceManager.registerField< mixedMimetic::faceMassFlux >( getName() ); faceManager.registerField< mixedMimetic::faceMassFlux_n >( getName() ); - // boundary condition data + // boundary conditions of the flow operator: the user values and the per-face condition faceManager.registerField< flow::bcPressure >( getName() ); - faceManager.registerField< flow::isBoundaryFace >( getName() ); + faceManager.registerField< mixedMimetic::bcMassFlux >( getName() ); + faceManager.registerField< mixedMimetic::flowBoundaryType >( getName() ); + faceManager.registerField< mixedMimetic::flowBoundaryValue >( getName() ); + faceManager.registerField< mixedMimetic::flowBoundaryCoefficient >( getName() ); // face residual of the consistency layer faceManager.registerField< mixedMimetic::faceResidual >( getName() ); @@ -94,6 +107,23 @@ void SinglePhaseMixedMFD::registerDataOnMesh( Group & meshBodies ) faceManager.registerField< mixedMimetic::faceStencilLabel >( getName() ); faceManager.registerField< mixedMimetic::faceOrientationCell >( getName() ); faceManager.registerField< mixedMimetic::faceDofScale >( getName() ); + + if( m_isThermal ) + { + // energy module: face heat flux unknown and the thermal boundary data + faceManager.registerField< mixedMimetic::faceHeatFlux >( getName() ); + faceManager.registerField< mixedMimetic::faceHeatFlux_n >( getName() ); + faceManager.registerField< flow::bcTemperature >( getName() ); + faceManager.registerField< mixedMimetic::bcEnthalpy >( getName() ); + faceManager.registerField< mixedMimetic::bcHeatFlux >( getName() ); + faceManager.registerField< mixedMimetic::bcHeatTransferCoefficient >( getName() ); + faceManager.registerField< mixedMimetic::bcAmbientTemperature >( getName() ); + faceManager.registerField< mixedMimetic::heatBoundaryType >( getName() ); + faceManager.registerField< mixedMimetic::heatBoundaryValue >( getName() ); + faceManager.registerField< mixedMimetic::heatBoundaryCoefficient >( getName() ); + faceManager.registerField< mixedMimetic::isEnthalpyBcFace >( getName() ); + faceManager.registerField< mixedMimetic::faceHeatDofScale >( getName() ); + } } } ); } @@ -102,9 +132,10 @@ void SinglePhaseMixedMFD::initializePreSubGroups() { SinglePhaseBase::initializePreSubGroups(); - GEOS_THROW_IF( m_isThermal, - "The thermal option is not supported by SinglePhaseMixedMFD", - InputError, getDataContext() ); + if( m_isThermal ) + { + m_linearSolverParameters.get().mgr.strategy = LinearSolverParameters::MGR::StrategyType::thermalSinglePhaseMixedMFD; + } DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); @@ -139,26 +170,6 @@ void SinglePhaseMixedMFD::initializePostInitialConditionsPreSubGroups() m_regionFilter.insert( elemManager.getRegions().getIndex( regionName ) ); } - // flag the faces on which a pressure boundary condition is imposed: - // all the remaining domain-boundary faces are treated as no-flow faces - FaceManager & faceManager = mesh.getFaceManager(); - arrayView1d< integer > const isPresBcFace = faceManager.getField< flow::isBoundaryFace >(); - - fsManager.apply< FaceManager >( 0.0, - mesh, - flow::bcPressure::key(), - [&] ( FieldSpecification const &, - string const &, - SortedArrayView< localIndex const > const & targetSet, - FaceManager &, - string const & ) - { - forAll< parallelDevicePolicy<> >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const a ) - { - isPresBcFace[targetSet[a]] = 1; - } ); - } ); - fsManager.forSubGroups< AquiferBoundaryCondition >( [&] ( AquiferBoundaryCondition const & bc ) { GEOS_UNUSED_VAR( bc ); @@ -168,11 +179,100 @@ void SinglePhaseMixedMFD::initializePostInitialConditionsPreSubGroups() } ); } ); + markBoundaryFaces( domain ); + if( m_isThermal ) + { + initializeEnthalpy( domain ); + } + computeFaceOrientation( domain ); classifyCells( domain ); } +void SinglePhaseMixedMFD::markBoundaryFaces( DomainPartition & domain ) +{ + FieldSpecificationManager & fsManager = FieldSpecificationManager::getInstance(); + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & ) + { + FaceManager & faceManager = mesh.getFaceManager(); + arrayView2d< localIndex const > const elemRegionList = faceManager.elementRegionList(); + arrayView2d< localIndex const > const elemSubRegionList = faceManager.elementSubRegionList(); + arrayView2d< localIndex const > const elemList = faceManager.elementList(); + SortedArrayView< localIndex const > const regionFilter = m_regionFilter.toViewConst(); + + // the field specifications define the type of the condition on a face; a boundary face without specification + // has the homogeneous Neumann condition (impervious, adiabatic), an interior face has no condition + auto const initialize = [&]( arrayView1d< integer > const & type ) + { + forAll< parallelDevicePolicy<> >( faceManager.size(), [=] GEOS_HOST_DEVICE ( localIndex const kf ) + { + integer numTargetCells = 0; + for( integer k = 0; k < elemRegionList.size( 1 ); ++k ) + { + localIndex const er = elemRegionList[kf][k]; + numTargetCells += ( er >= 0 && elemSubRegionList[kf][k] >= 0 && elemList[kf][k] >= 0 && regionFilter.contains( er ) ) ? 1 : 0; + } + type[kf] = ( numTargetCells == 1 ) ? BoundaryType::neumann : BoundaryType::interior; + } ); + }; + auto const mark = [&]( string const & key, arrayView1d< integer > const & flag, integer const value ) + { + fsManager.apply< FaceManager >( 0.0, mesh, key, + [&] ( FieldSpecification const &, string const &, + SortedArrayView< localIndex const > const & targetSet, FaceManager &, string const & ) + { + forAll< parallelDevicePolicy<> >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const a ) + { + flag[targetSet[a]] = value; + } ); + } ); + }; + + arrayView1d< integer > const flowType = faceManager.getField< mixedMimetic::flowBoundaryType >(); + initialize( flowType ); + mark( flow::bcPressure::key(), flowType, BoundaryType::dirichlet ); + mark( mixedMimetic::bcMassFlux::key(), flowType, BoundaryType::neumann ); + + if( m_isThermal ) + { + arrayView1d< integer > const heatType = faceManager.getField< mixedMimetic::heatBoundaryType >(); + initialize( heatType ); + mark( flow::bcTemperature::key(), heatType, BoundaryType::dirichlet ); + mark( mixedMimetic::bcHeatFlux::key(), heatType, BoundaryType::neumann ); + mark( mixedMimetic::bcHeatTransferCoefficient::key(), heatType, BoundaryType::robin ); + mark( mixedMimetic::bcEnthalpy::key(), faceManager.getField< mixedMimetic::isEnthalpyBcFace >(), 1 ); + } + } ); +} + +void SinglePhaseMixedMFD::initializeEnthalpy( DomainPartition & domain ) +{ + // h = h_eos( p, T ) at the initial state + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase const & fluid = getConstitutiveModel< SingleFluidBase >( subRegion, fluidName ); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const eosEnthalpy = fluid.enthalpy(); + arrayView1d< real64 > const enthalpy = subRegion.getField< mixedMimetic::enthalpy >(); + arrayView1d< real64 > const enthalpy_n = subRegion.getField< mixedMimetic::enthalpy_n >(); + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + enthalpy[ei] = eosEnthalpy[ei][0]; + enthalpy_n[ei] = eosEnthalpy[ei][0]; + } ); + } ); + } ); +} + void SinglePhaseMixedMFD::computeFaceOrientation( DomainPartition & domain ) { GEOS_MARK_FUNCTION; @@ -281,7 +381,7 @@ void SinglePhaseMixedMFD::implicitStepSetup( real64 const & time_n, // setup the face fields forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, MeshLevel & mesh, - string_array const & ) + string_array const & regionNames ) { FaceManager & faceManager = mesh.getFaceManager(); @@ -290,10 +390,23 @@ void SinglePhaseMixedMFD::implicitStepSetup( real64 const & time_n, arrayView1d< real64 > const & faceFlux_n = faceManager.getField< mixedMimetic::faceMassFlux_n >(); faceFlux_n.setValues< parallelDevicePolicy<> >( faceFlux ); + + if( m_isThermal ) + { + faceManager.getField< mixedMimetic::faceHeatFlux_n >().setValues< parallelDevicePolicy<> >( + faceManager.getField< mixedMimetic::faceHeatFlux >().toViewConst() ); + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + subRegion.getField< mixedMimetic::enthalpy_n >().setValues< parallelDevicePolicy<> >( + subRegion.getField< mixedMimetic::enthalpy >().toViewConst() ); + } ); + } } ); - // evaluate the boundary face pressure values used in the constitutive rows - applyFacePressureBCValues( time_n + dt, domain ); + // evaluate the boundary face values used in the constitutive rows + applyFaceBoundaryValues( time_n + dt, domain ); } namespace @@ -305,45 +418,90 @@ char const faceBcLogMessage[] = "\nNote that if this number is equal to zero, the boundary condition will not be applied on this face set."; } -void SinglePhaseMixedMFD::applyFacePressureBCValues( real64 const time, - DomainPartition & domain ) +void SinglePhaseMixedMFD::applyFaceBoundaryValues( real64 const time, + DomainPartition & domain ) { GEOS_MARK_FUNCTION; FieldSpecificationManager & fsManager = FieldSpecificationManager::getInstance(); + stdVector< string > keys = { flow::bcPressure::key(), mixedMimetic::bcMassFlux::key() }; + if( m_isThermal ) + { + for( string const & key : { flow::bcTemperature::key(), mixedMimetic::bcHeatFlux::key(), mixedMimetic::bcHeatTransferCoefficient::key(), + mixedMimetic::bcAmbientTemperature::key(), mixedMimetic::bcEnthalpy::key() } ) + { + keys.push_back( key ); + } + } + this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, MeshLevel & mesh, string_array const & ) { - fsManager.apply< FaceManager >( time, - mesh, - flow::bcPressure::key(), - [&] ( FieldSpecification const & fs, - string const & setName, - SortedArrayView< localIndex const > const & targetSet, - FaceManager & targetGroup, - string const & ) - { - // provide some logging at the first nonlinear iteration - if( m_nonlinearSolverParameters.m_numNewtonIterations == 0 ) + // 1) the user values of the face specifications + for( string const & key : keys ) + { + fsManager.apply< FaceManager >( time, + mesh, + key, + [&] ( FieldSpecification const & fs, + string const & setName, + SortedArrayView< localIndex const > const & targetSet, + FaceManager & targetGroup, + string const & ) { - globalIndex const numTargetFaces = MpiWrapper::sum< globalIndex >( targetSet.size() ); - GEOS_LOG_LEVEL_RANK_0_ON_GROUP( logInfo::BoundaryConditions, - GEOS_FMT_RUNTIME( faceBcLogMessage, - this->getName(), time, fs.getCatalogName(), fs.getName(), - setName, targetGroup.getName(), numTargetFaces ), - fs ); - } + // provide some logging at the first nonlinear iteration + if( m_nonlinearSolverParameters.m_numNewtonIterations == 0 ) + { + globalIndex const numTargetFaces = MpiWrapper::sum< globalIndex >( targetSet.size() ); + GEOS_LOG_LEVEL_RANK_0_ON_GROUP( logInfo::BoundaryConditions, + GEOS_FMT_RUNTIME( faceBcLogMessage, + this->getName(), time, fs.getCatalogName(), fs.getName(), + setName, targetGroup.getName(), numTargetFaces ), + fs ); + } + FieldSpecificationImpl::applyFieldValue< FieldSpecificationEqual, + parallelDevicePolicy<> >( fs, targetSet, time, targetGroup, key ); + } ); + } - // populate the boundary face pressure values: they enter the residual directly during assembly - FieldSpecificationImpl::applyFieldValue< FieldSpecificationEqual, - parallelDevicePolicy<> >( fs, - targetSet, - time, - targetGroup, - flow::bcPressure::key() ); - } ); + // 2) the condition of each operator on each face: (g, alpha) from the user values according to the type + FaceManager & faceManager = mesh.getFaceManager(); + auto const fill = [&]( arrayView1d< integer const > const & type, arrayView1d< real64 > const & value, arrayView1d< real64 > const & coefficient, + arrayView1d< real64 const > const & dirichletValue, arrayView1d< real64 const > const & neumannValue, + arrayView1d< real64 const > const & robinValue, arrayView1d< real64 const > const & robinCoefficient ) + { + forAll< parallelDevicePolicy<> >( faceManager.size(), [=] GEOS_HOST_DEVICE ( localIndex const kf ) + { + value[kf] = ( type[kf] == BoundaryType::dirichlet ) ? dirichletValue[kf] + : ( type[kf] == BoundaryType::neumann ) ? neumannValue[kf] + : ( type[kf] == BoundaryType::robin ) ? robinValue[kf] : 0.0; + coefficient[kf] = ( type[kf] == BoundaryType::robin ) ? robinCoefficient[kf] : 0.0; + } ); + }; + // TODO: Robin data of the flow operator from the Carter-Tracy aquifer. Over a time step the influx rate is + // q = a - b ( p_f - p_f^n ), with a, b functions of the dimensionless time and of the cumulative influx W^n. + // With f_A the fraction of the aquifer area on the face, sigma m_f = - rho f_A q = alpha |f| ( p_f - g ), + // alpha |f| = rho f_A b, g = p_f^n + a / b. ( g, alpha ) is evaluated once per step; at convergence + // W^{n+1} = W^n + dt q( p_f^{n+1} ). Until then no face of the flow operator has the Robin type + arrayView1d< real64 const > const zero = faceManager.getField< mixedMimetic::flowBoundaryCoefficient >(); + fill( faceManager.getField< mixedMimetic::flowBoundaryType >(), + faceManager.getField< mixedMimetic::flowBoundaryValue >(), + faceManager.getField< mixedMimetic::flowBoundaryCoefficient >(), + faceManager.getField< flow::bcPressure >(), + faceManager.getField< mixedMimetic::bcMassFlux >(), + zero, zero ); + if( m_isThermal ) + { + fill( faceManager.getField< mixedMimetic::heatBoundaryType >(), + faceManager.getField< mixedMimetic::heatBoundaryValue >(), + faceManager.getField< mixedMimetic::heatBoundaryCoefficient >(), + faceManager.getField< flow::bcTemperature >(), + faceManager.getField< mixedMimetic::bcHeatFlux >(), + faceManager.getField< mixedMimetic::bcAmbientTemperature >(), + faceManager.getField< mixedMimetic::bcHeatTransferCoefficient >() ); + } } ); } @@ -427,7 +585,23 @@ void SinglePhaseMixedMFD::computeMgrPointMarkers( DomainPartition const & domain markers[faceDofNumber[kf] - rankOffset] = faceStencilLabel[kf]; } ); - // cell-pressure dofs: label 2 + // heat-flux dofs: labels 5 (condensed) and 6 (saddle point) + if( m_isThermal ) + { + arrayView1d< globalIndex const > const faceHeatDofNumber = + faceManager.getReference< array1d< globalIndex > >( dofManager.getKey( mixedMimetic::faceHeatFlux::key() ) ); + forAll< parallelHostPolicy >( faceManager.size(), [=]( localIndex const kf ) + { + if( faceGhostRank[kf] >= 0 || faceHeatDofNumber[kf] < 0 ) + { + return; + } + markers[faceHeatDofNumber[kf] - rankOffset] = 5 + faceStencilLabel[kf]; + } ); + } + + // cell dofs: pressure 2, temperature 3, enthalpy 4 + integer const numCellDofs = numCellDofComponents(); elemManager.forElementSubRegions< ElementSubRegionBase >( regionNames, [&]( localIndex const, ElementSubRegionBase const & subRegion ) @@ -435,12 +609,14 @@ void SinglePhaseMixedMFD::computeMgrPointMarkers( DomainPartition const & domain arrayView1d< globalIndex const > const elemDofNumber = subRegion.getReference< array1d< globalIndex > >( elemDofKey ); arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); - forAll< parallelHostPolicy >( subRegion.size(), [=]( localIndex const ei ) { if( elemGhostRank[ei] < 0 ) { - markers[elemDofNumber[ei] - rankOffset] = 2; + for( integer c = 0; c < numCellDofs; ++c ) + { + markers[elemDofNumber[ei] - rankOffset + c] = 2 + c; + } } } ); } ); @@ -461,22 +637,40 @@ void SinglePhaseMixedMFD::setupDofs( DomainPartition const & GEOS_UNUSED_PARAM( mixedMimetic::faceMassFlux::key(), DofManager::Connector::Elem ); - // cell pressure unknowns: the TPFA-face condensation writes two-point stencil - // entries directly into the mass conservation rows, so cell-to-cell coupling - // through the faces is required + // face heat flux unknowns, with the same structure as the mass fluxes + if( m_isThermal ) + { + dofManager.addField( mixedMimetic::faceHeatFlux::key(), + FieldLocation::Face, + 1, + getMeshTargets() ); + dofManager.addCoupling( mixedMimetic::faceHeatFlux::key(), + mixedMimetic::faceHeatFlux::key(), + DofManager::Connector::Elem ); + } + + // cell unknowns (pressure, and temperature and enthalpy when thermal): the TPFA-face + // condensation writes two-point stencil entries directly into the conservation rows, + // so cell-to-cell coupling through the faces is required dofManager.addField( viewKeyStruct::elemDofFieldString(), FieldLocation::Elem, - 1, + numCellDofComponents(), getMeshTargets() ); dofManager.addCoupling( viewKeyStruct::elemDofFieldString(), viewKeyStruct::elemDofFieldString(), DofManager::Connector::Face ); - // coupling between the face fluxes and the cell pressures + // coupling between the face fluxes and the cell unknowns dofManager.addCoupling( mixedMimetic::faceMassFlux::key(), viewKeyStruct::elemDofFieldString(), DofManager::Connector::Elem ); + if( m_isThermal ) + { + dofManager.addCoupling( mixedMimetic::faceHeatFlux::key(), + viewKeyStruct::elemDofFieldString(), + DofManager::Connector::Elem ); + } } void SinglePhaseMixedMFD::assembleFluxTerms( real64 const dt, @@ -516,39 +710,93 @@ void SinglePhaseMixedMFD::assembleFluxTerms( real64 const dt, string const & permName = subRegion.getReference< string >( viewKeyStruct::permeabilityNamesString() ); PermeabilityBase const & permeability = getConstitutiveModel< PermeabilityBase >( subRegion, permName ); + if( m_isThermal ) + { + string const & condName = subRegion.getReference< string >( viewKeyStruct::thermalConductivityNamesString() ); + SinglePhaseThermalConductivityBase const & conductivity = getConstitutiveModel< SinglePhaseThermalConductivityBase >( subRegion, condName ); + string const faceHeatDofKey = dofManager.getKey( mixedMimetic::faceHeatFlux::key() ); + + thermalSinglePhaseMixedMFDKernels:: + ElementBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(), + lengthTolerance, + elemDofKey, + faceDofKey, + faceHeatDofKey, + nodeManager, + faceManager, + mesh.getElemManager(), + subRegion, + mimeticInnerProductBase, + fluid, + permeability, + conductivity, + m_regionFilter.toViewConst(), + dt, + localMatrix, + localRhs ); + + // cell closure of the enthalpy unknown + thermalSinglePhaseMixedMFDKernels:: + EnthalpyClosureKernel::launch< parallelDevicePolicy<> >( dofManager.rankOffset(), elemDofKey, subRegion, fluid, localMatrix, localRhs ); + } + else + { + singlePhaseMixedMFDKernels:: + ElementBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(), + lengthTolerance, + elemDofKey, + faceDofKey, + nodeManager, + faceManager, + subRegion, + mimeticInnerProductBase, + fluid, + permeability, + dt, + localMatrix, + localRhs ); + } + } ); + + // condensed (label-0) faces: two-point flux contributions to the conservation + // rows and one-way closure rows, assembled in a single face-based sweep + if( m_isThermal ) + { + thermalSinglePhaseMixedMFDKernels:: + TpfaCondensedFluxKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(), + lengthTolerance, + elemDofKey, + faceDofKey, + dofManager.getKey( mixedMimetic::faceHeatFlux::key() ), + getName(), + nodeManager, + faceManager, + mesh.getElemManager(), + m_regionFilter.toViewConst(), + dt, + localMatrix, + localRhs ); + } + else + { singlePhaseMixedMFDKernels:: - ElementBasedAssemblyKernelFactory:: + TpfaCondensedFluxKernelFactory:: createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(), lengthTolerance, elemDofKey, faceDofKey, + getName(), nodeManager, faceManager, - subRegion, - mimeticInnerProductBase, - fluid, - permeability, + mesh.getElemManager(), + m_regionFilter.toViewConst(), dt, localMatrix, localRhs ); - } ); - - // condensed (label-0) faces: two-point flux contributions to the mass conservation - // rows and one-way closure rows, assembled in a single face-based sweep - singlePhaseMixedMFDKernels:: - TpfaCondensedFluxKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(), - lengthTolerance, - elemDofKey, - faceDofKey, - getName(), - nodeManager, - faceManager, - mesh.getElemManager(), - m_regionFilter.toViewConst(), - dt, - localMatrix, - localRhs ); + } } ); } @@ -593,17 +841,92 @@ void SinglePhaseMixedMFD::applyBoundaryConditions( real64 const time_n, // only the cell-centered boundary conditions (Dirichlet cells, source fluxes) remain to be applied here SinglePhaseBase::applyBoundaryConditions( time_n, dt, domain, dofManager, localMatrix, localRhs ); + if( m_isThermal ) + { + removeEnthalpyReference( domain, dofManager, localMatrix, localRhs ); + } + // the matrix is final: the residual-norm weights are a function of it and are recomputed with it computeResidualWeights( domain, dofManager, localMatrix.toViewConst() ); } +void SinglePhaseMixedMFD::removeEnthalpyReference( DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const +{ + GEOS_MARK_FUNCTION; + + // energy row <- energy row - h_K * mass row. A shift h_0 of the enthalpy reference adds h_0 * (mass row) to the + // energy row: the reduced system is invariant under that shift, and the Newton update is unchanged + globalIndex const rankOffset = dofManager.rankOffset(); + string const elemDofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase const & subRegion ) + { + arrayView1d< globalIndex const > const elemDofNumber = subRegion.getReference< array1d< globalIndex > >( elemDofKey ); + arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); + arrayView1d< real64 const > const enthalpy = subRegion.getField< mixedMimetic::enthalpy >(); + + RAJA::ReduceSum< parallelDeviceReduce, localIndex > numMissing( 0 ); + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + if( elemGhostRank[ei] >= 0 ) + { + return; + } + localIndex const massRow = elemDofNumber[ei] - rankOffset + CellDof::pressure; + localIndex const energyRow = elemDofNumber[ei] - rankOffset + CellDof::temperature; + real64 const h = enthalpy[ei]; + + arraySlice1d< globalIndex const > const massCols = localMatrix.getColumns( massRow ); + arraySlice1d< real64 const > const massVals = localMatrix.getEntries( massRow ); + arraySlice1d< globalIndex const > const energyCols = localMatrix.getColumns( energyRow ); + arraySlice1d< real64 > const energyVals = localMatrix.getEntries( energyRow ); + localIndex const numEnergy = localMatrix.numNonZeros( energyRow ); + + // both rows are sorted by column + localIndex j = 0; + for( localIndex k = 0; k < localMatrix.numNonZeros( massRow ); ++k ) + { + while( j < numEnergy && energyCols[j] < massCols[k] ) + { + ++j; + } + if( j < numEnergy && energyCols[j] == massCols[k] ) + { + energyVals[j] -= h * massVals[k]; + } + else if( LvArray::math::abs( massVals[k] ) > 0.0 ) + { + numMissing += 1; + } + } + localRhs[energyRow] -= h * localRhs[massRow]; + } ); + GEOS_ERROR_IF( numMissing.get() > 0, getDataContext() << ": the energy rows do not contain the pattern of the mass rows" ); + } ); + } ); +} + void SinglePhaseMixedMFD::computeGhostDofs( DomainPartition const & domain, DofManager const & dofManager ) { GEOS_MARK_FUNCTION; string const elemDofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); - string const faceDofKey = dofManager.getKey( mixedMimetic::faceMassFlux::key() ); + integer const numCellDofs = numCellDofComponents(); + stdVector< string > faceDofKeys = { dofManager.getKey( mixedMimetic::faceMassFlux::key() ) }; + if( m_isThermal ) + { + faceDofKeys.push_back( dofManager.getKey( mixedMimetic::faceHeatFlux::key() ) ); + } array1d< globalIndex > dofs; forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, @@ -611,13 +934,16 @@ void SinglePhaseMixedMFD::computeGhostDofs( DomainPartition const & domain, string_array const & regionNames ) { FaceManager const & faceManager = mesh.getFaceManager(); - arrayView1d< globalIndex const > const faceDofNumber = faceManager.getReference< array1d< globalIndex > >( faceDofKey ); arrayView1d< integer const > const faceGhostRank = faceManager.ghostRank(); - for( localIndex kf = 0; kf < faceManager.size(); ++kf ) + for( string const & key : faceDofKeys ) { - if( faceGhostRank[kf] >= 0 && faceDofNumber[kf] >= 0 ) + arrayView1d< globalIndex const > const faceDofNumber = faceManager.getReference< array1d< globalIndex > >( key ); + for( localIndex kf = 0; kf < faceManager.size(); ++kf ) { - dofs.emplace_back( faceDofNumber[kf] ); + if( faceGhostRank[kf] >= 0 && faceDofNumber[kf] >= 0 ) + { + dofs.emplace_back( faceDofNumber[kf] ); + } } } mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, @@ -630,7 +956,10 @@ void SinglePhaseMixedMFD::computeGhostDofs( DomainPartition const & domain, { if( elemGhostRank[ei] >= 0 && elemDofNumber[ei] >= 0 ) { - dofs.emplace_back( elemDofNumber[ei] ); + for( integer c = 0; c < numCellDofs; ++c ) + { + dofs.emplace_back( elemDofNumber[ei] + c ); + } } } } ); @@ -660,7 +989,20 @@ void SinglePhaseMixedMFD::computeResidualWeights( DomainPartition & domain, globalIndex const rankOffset = dofManager.rankOffset(); string const elemDofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); - string const faceDofKey = dofManager.getKey( mixedMimetic::faceMassFlux::key() ); + integer const numCellDofs = numCellDofComponents(); + + // the cell scales |p_n|, and |T_n|, |h_n| when thermal; the face fields with the cell scale + // their rows are measured with and the face field storing their scale + stdVector< string > cellScaleKeys = { flow::pressure_n::key() }; + struct FaceField { string dofKey; string cellScaleKey; string scaleFieldKey; }; + stdVector< FaceField > faceFields = + { { dofManager.getKey( mixedMimetic::faceMassFlux::key() ), flow::pressure_n::key(), mixedMimetic::faceDofScale::key() } }; + if( m_isThermal ) + { + cellScaleKeys.push_back( flow::temperature_n::key() ); + cellScaleKeys.push_back( mixedMimetic::enthalpy_n::key() ); + faceFields.push_back( { dofManager.getKey( mixedMimetic::faceHeatFlux::key() ), flow::temperature_n::key(), mixedMimetic::faceHeatDofScale::key() } ); + } forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, MeshLevel & mesh, @@ -669,7 +1011,7 @@ void SinglePhaseMixedMFD::computeResidualWeights( DomainPartition & domain, ElementRegionManager & elemManager = mesh.getElemManager(); FaceManager & faceManager = mesh.getFaceManager(); - // s_p = |p_n| for the cell-pressure dofs + // s of the cell dofs elemManager.forElementSubRegions< ElementSubRegionBase >( regionNames, [&]( localIndex const, ElementSubRegionBase const & subRegion ) @@ -677,69 +1019,72 @@ void SinglePhaseMixedMFD::computeResidualWeights( DomainPartition & domain, arrayView1d< globalIndex const > const elemDofNumber = subRegion.getReference< array1d< globalIndex > >( elemDofKey ); arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); - arrayView1d< real64 const > const presN = subRegion.getField< fields::flow::pressure_n >(); - - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + for( integer c = 0; c < numCellDofs; ++c ) { - if( elemGhostRank[ei] < 0 ) + arrayView1d< real64 const > const valueN = subRegion.getReference< array1d< real64 > >( cellScaleKeys[c] ); + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) { - dofScale[elemDofNumber[ei] - rankOffset] = LvArray::math::abs( presN[ei] ); - } - } ); + if( elemGhostRank[ei] < 0 ) + { + dofScale[elemDofNumber[ei] - rankOffset + c] = LvArray::math::abs( valueN[ei] ); + } + } ); + } } ); - // s_m = p_scale / |M_ff| for the face-flux dofs, p_scale the mean adjacent |p_n| - ElementRegionManager::ElementViewAccessor< arrayView1d< real64 const > > const presNAccessor = - elemManager.constructArrayViewAccessor< real64, 1 >( fields::flow::pressure_n::key() ); - ElementRegionManager::ElementViewConst< arrayView1d< real64 const > > const presN = presNAccessor.toNestedViewConst(); - - arrayView1d< globalIndex const > const faceDofNumber = - faceManager.getReference< array1d< globalIndex > >( faceDofKey ); + // s_f = x_scale / |M_ff| for the face dofs, x_scale the mean adjacent cell scale arrayView1d< integer const > const faceGhostRank = faceManager.ghostRank(); arrayView2d< localIndex const > const elemRegionList = faceManager.elementRegionList(); arrayView2d< localIndex const > const elemSubRegionList = faceManager.elementSubRegionList(); arrayView2d< localIndex const > const elemList = faceManager.elementList(); SortedArrayView< localIndex const > const regionFilter = m_regionFilter.toViewConst(); - arrayView1d< real64 > const faceScale = faceManager.getField< mixedMimetic::faceDofScale >(); - - forAll< parallelDevicePolicy<> >( faceManager.size(), [=] GEOS_HOST_DEVICE ( localIndex const kf ) + for( FaceField const & ff : faceFields ) { - if( faceGhostRank[kf] >= 0 || faceDofNumber[kf] < 0 ) - { - return; - } - real64 presSum = 0.0; - integer elemCounter = 0; - for( integer k = 0; k < elemRegionList.size( 1 ); ++k ) + ElementRegionManager::ElementViewAccessor< arrayView1d< real64 const > > const valueNAccessor = + elemManager.constructArrayViewAccessor< real64, 1 >( ff.cellScaleKey ); + ElementRegionManager::ElementViewConst< arrayView1d< real64 const > > const valueN = valueNAccessor.toNestedViewConst(); + arrayView1d< globalIndex const > const faceDofNumber = faceManager.getReference< array1d< globalIndex > >( ff.dofKey ); + arrayView1d< real64 > const faceScale = faceManager.getReference< array1d< real64 > >( ff.scaleFieldKey ); + + forAll< parallelDevicePolicy<> >( faceManager.size(), [=] GEOS_HOST_DEVICE ( localIndex const kf ) { - localIndex const er = elemRegionList[kf][k]; - localIndex const esr = elemSubRegionList[kf][k]; - localIndex const ei = elemList[kf][k]; - if( er >= 0 && esr >= 0 && ei >= 0 && regionFilter.contains( er ) ) + if( faceGhostRank[kf] >= 0 || faceDofNumber[kf] < 0 ) { - presSum += LvArray::math::abs( presN[er][esr][ei] ); - elemCounter++; + return; } - } - real64 const pScale = elemCounter > 0 ? presSum / elemCounter : 0.0; - localIndex const localRow = faceDofNumber[kf] - rankOffset; - real64 diag = 0.0; - arraySlice1d< globalIndex const > const columns = localMatrix.getColumns( localRow ); - arraySlice1d< real64 const > const entries = localMatrix.getEntries( localRow ); - for( localIndex k = 0; k < localMatrix.numNonZeros( localRow ); ++k ) - { - if( columns[k] == faceDofNumber[kf] ) + real64 sum = 0.0; + integer elemCounter = 0; + for( integer k = 0; k < elemRegionList.size( 1 ); ++k ) { - diag = LvArray::math::abs( entries[k] ); + localIndex const er = elemRegionList[kf][k]; + localIndex const esr = elemSubRegionList[kf][k]; + localIndex const ei = elemList[kf][k]; + if( er >= 0 && esr >= 0 && ei >= 0 && regionFilter.contains( er ) ) + { + sum += LvArray::math::abs( valueN[er][esr][ei] ); + elemCounter++; + } } - } - dofScale[localRow] = diag > 0.0 ? pScale / diag : pScale; - faceScale[kf] = dofScale[localRow]; - } ); + real64 const xScale = elemCounter > 0 ? sum / elemCounter : 0.0; + localIndex const localRow = faceDofNumber[kf] - rankOffset; + real64 diag = 0.0; + arraySlice1d< globalIndex const > const columns = localMatrix.getColumns( localRow ); + arraySlice1d< real64 const > const entries = localMatrix.getEntries( localRow ); + for( localIndex k = 0; k < localMatrix.numNonZeros( localRow ); ++k ) + { + if( columns[k] == faceDofNumber[kf] ) + { + diag = LvArray::math::abs( entries[k] ); + } + } + dofScale[localRow] = diag > 0.0 ? xScale / diag : xScale; + faceScale[kf] = dofScale[localRow]; + } ); + } } ); // s_j of the ghost dofs: a cell may own none of its faces, so the face scales come from their owners; - // the pressure scale of a ghost cell is its synchronized |p_n| + // the scale of a ghost cell dof is its synchronized previous value m_ghostDofScale.resize( m_ghostDofs.size() ); arrayView1d< real64 > const ghostDofScale = m_ghostDofScale.toView(); ghostDofScale.zero(); @@ -750,21 +1095,27 @@ void SinglePhaseMixedMFD::computeResidualWeights( DomainPartition & domain, string_array const & regionNames ) { FieldIdentifiers fieldsToBeSync; - fieldsToBeSync.addFields( FieldLocation::Face, { mixedMimetic::faceDofScale::key() } ); + for( FaceField const & ff : faceFields ) + { + fieldsToBeSync.addFields( FieldLocation::Face, { ff.scaleFieldKey } ); + } CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), false ); FaceManager const & faceManager = mesh.getFaceManager(); - arrayView1d< globalIndex const > const faceDofNumber = faceManager.getReference< array1d< globalIndex > >( faceDofKey ); arrayView1d< integer const > const faceGhostRank = faceManager.ghostRank(); - arrayView1d< real64 const > const faceScale = faceManager.getField< mixedMimetic::faceDofScale >(); - forAll< parallelDevicePolicy<> >( faceManager.size(), [=] GEOS_HOST_DEVICE ( localIndex const kf ) + for( FaceField const & ff : faceFields ) { - if( faceGhostRank[kf] >= 0 && faceDofNumber[kf] >= 0 ) + arrayView1d< globalIndex const > const faceDofNumber = faceManager.getReference< array1d< globalIndex > >( ff.dofKey ); + arrayView1d< real64 const > const faceScale = faceManager.getReference< array1d< real64 > >( ff.scaleFieldKey ); + forAll< parallelDevicePolicy<> >( faceManager.size(), [=] GEOS_HOST_DEVICE ( localIndex const kf ) { - localIndex const slot = LvArray::sortedArrayManipulation::find( ghostDofs.begin(), ghostDofs.size(), faceDofNumber[kf] ); - ghostDofScale[slot] = faceScale[kf]; - } - } ); + if( faceGhostRank[kf] >= 0 && faceDofNumber[kf] >= 0 ) + { + localIndex const slot = LvArray::sortedArrayManipulation::find( ghostDofs.begin(), ghostDofs.size(), faceDofNumber[kf] ); + ghostDofScale[slot] = faceScale[kf]; + } + } ); + } mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, [&]( localIndex const, @@ -772,15 +1123,18 @@ void SinglePhaseMixedMFD::computeResidualWeights( DomainPartition & domain, { arrayView1d< globalIndex const > const elemDofNumber = subRegion.getReference< array1d< globalIndex > >( elemDofKey ); arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); - arrayView1d< real64 const > const presN = subRegion.getField< fields::flow::pressure_n >(); - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + for( integer c = 0; c < numCellDofs; ++c ) { - if( elemGhostRank[ei] >= 0 && elemDofNumber[ei] >= 0 ) + arrayView1d< real64 const > const valueN = subRegion.getReference< array1d< real64 > >( cellScaleKeys[c] ); + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) { - localIndex const slot = LvArray::sortedArrayManipulation::find( ghostDofs.begin(), ghostDofs.size(), elemDofNumber[ei] ); - ghostDofScale[slot] = LvArray::math::abs( presN[ei] ); - } - } ); + if( elemGhostRank[ei] >= 0 && elemDofNumber[ei] >= 0 ) + { + localIndex const slot = LvArray::sortedArrayManipulation::find( ghostDofs.begin(), ghostDofs.size(), elemDofNumber[ei] + c ); + ghostDofScale[slot] = LvArray::math::abs( valueN[ei] ); + } + } ); + } } ); } ); @@ -819,6 +1173,7 @@ void SinglePhaseMixedMFD::applyAquiferBC( real64 const time, arrayView1d< real64 > const & localRhs ) const { GEOS_MARK_FUNCTION; + // no contribution: the aquifer is a Robin condition of the flow operator (see applyFaceBoundaryValues) GEOS_UNUSED_VAR( time, dt, dofManager, domain, localMatrix, localRhs ); } @@ -827,6 +1182,7 @@ void SinglePhaseMixedMFD::saveAquiferConvergedState( real64 const & time, DomainPartition & domain ) { GEOS_MARK_FUNCTION; + // TODO: W^{n+1} = W^n + dt q( p_f^{n+1} ) on the faces of each aquifer (see applyFaceBoundaryValues) GEOS_UNUSED_VAR( time, dt, domain ); } @@ -890,7 +1246,8 @@ void SinglePhaseMixedMFD::applySystemSolution( DofManager const & dofManager, dofManager.addVectorToField( localSolution, viewKeyStruct::elemDofFieldString(), flow::pressure::key(), - scalingFactor ); + scalingFactor, + DofManager::CompMask( numCellDofComponents(), CellDof::pressure, CellDof::pressure + 1 ) ); // 2. apply the face-based update @@ -899,6 +1256,24 @@ void SinglePhaseMixedMFD::applySystemSolution( DofManager const & dofManager, mixedMimetic::faceMassFlux::key(), scalingFactor ); + if( m_isThermal ) + { + dofManager.addVectorToField( localSolution, + viewKeyStruct::elemDofFieldString(), + flow::temperature::key(), + scalingFactor, + DofManager::CompMask( CellDof::num, CellDof::temperature, CellDof::temperature + 1 ) ); + dofManager.addVectorToField( localSolution, + viewKeyStruct::elemDofFieldString(), + mixedMimetic::enthalpy::key(), + scalingFactor, + DofManager::CompMask( CellDof::num, CellDof::enthalpy, CellDof::enthalpy + 1 ) ); + dofManager.addVectorToField( localSolution, + mixedMimetic::faceHeatFlux::key(), + mixedMimetic::faceHeatFlux::key(), + scalingFactor ); + } + // 3. synchronize forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, MeshLevel & mesh, @@ -908,6 +1283,11 @@ void SinglePhaseMixedMFD::applySystemSolution( DofManager const & dofManager, fieldsToBeSync.addElementFields( { flow::pressure::key() }, regionNames ); fieldsToBeSync.addFields( FieldLocation::Face, { mixedMimetic::faceMassFlux::key() } ); + if( m_isThermal ) + { + fieldsToBeSync.addElementFields( { flow::temperature::key(), mixedMimetic::enthalpy::key() }, regionNames ); + fieldsToBeSync.addFields( FieldLocation::Face, { mixedMimetic::faceHeatFlux::key() } ); + } CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), true ); } ); @@ -921,7 +1301,7 @@ void SinglePhaseMixedMFD::resetStateToBeginningOfStep( DomainPartition & domain // Reset the face-based fields forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, MeshLevel & mesh, - string_array const & ) + string_array const & regionNames ) { FaceManager & faceManager = mesh.getFaceManager(); @@ -930,6 +1310,19 @@ void SinglePhaseMixedMFD::resetStateToBeginningOfStep( DomainPartition & domain arrayView1d< real64 const > const & faceFlux_n = faceManager.getField< mixedMimetic::faceMassFlux_n >(); faceFlux.setValues< parallelDevicePolicy<> >( faceFlux_n ); + + if( m_isThermal ) + { + faceManager.getField< mixedMimetic::faceHeatFlux >().setValues< parallelDevicePolicy<> >( + faceManager.getField< mixedMimetic::faceHeatFlux_n >().toViewConst() ); + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + subRegion.getField< mixedMimetic::enthalpy >().setValues< parallelDevicePolicy<> >( + subRegion.getField< mixedMimetic::enthalpy_n >().toViewConst() ); + } ); + } } ); } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp index 138e2566967..98e86b8f9ea 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp @@ -163,12 +163,29 @@ class SinglePhaseMixedMFD : public SinglePhaseBase private: /** - * @brief Populate the boundary face pressure values from the field specifications. + * @brief Evaluate the face specifications at the given time and fill the condition (type, g, alpha) of every + * operator on every face. * @param[in] time the time at which the boundary values are evaluated * @param[in] domain the domain */ - void applyFacePressureBCValues( real64 const time, - DomainPartition & domain ); + void applyFaceBoundaryValues( real64 const time, + DomainPartition & domain ); + + /// number of cell dof components: pressure, and temperature and enthalpy when thermal + integer numCellDofComponents() const { return m_isThermal ? 3 : 1; } + + /** + * @brief Define the type of the boundary condition of every operator on every face from the field + * specifications (Dirichlet, Neumann or Robin), and the faces with a prescribed inflow enthalpy. + * @param domain the domain + */ + void markBoundaryFaces( DomainPartition & domain ); + + /** + * @brief Initialize the enthalpy unknown from the fluid model, h = h_eos( p, T ). + * @param domain the domain + */ + void initializeEnthalpy( DomainPartition & domain ); /** * @brief Set the orienting cell E_min of every face: sigma_{E,f} = +1 if E = E_min, -1 otherwise. @@ -200,6 +217,18 @@ class SinglePhaseMixedMFD : public SinglePhaseBase void computeGhostDofs( DomainPartition const & domain, DofManager const & dofManager ); + /** + * @brief Subtract h_K times the mass row from the energy row of every cell. + * @param domain the domain + * @param dofManager the dof manager + * @param localMatrix the system matrix + * @param localRhs the system right-hand side + */ + void removeEnthalpyReference( DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const; + /** * @brief Compute the residual-norm weights w_i = max_j |A_ij| s_j, s_j the characteristic scale of unknown j. * @param domain the domain diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp index f4f1a0c3cf1..526057aaed7 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp @@ -15,6 +15,16 @@ /** * @file SinglePhaseMixedMFDKernels.hpp + * + * Flow equations of the mixed mimetic single-phase solver. Unknowns: face mass flux m_f and cell pressure p_K. + * With sigma_f the orientation of the face f relative to the cell K, M the inner product weighted by K^{-1}, + * gamma = g . x the gravity coefficient and pi_f the pressure trace: + * + * Darcy law ( mu / rho )_K sum_j M_ij sigma_j m_j - p_K + rho_K ( gamma_K - gamma_f ) + pi_f = 0 + * mass balance ( phi rho V )_K - ( phi rho V )_K^n + dt sum_f sigma_f m_f = 0 + * + * M = chi M_mfd + ( 1 - chi ) M_tpfa, chi the classification of the cell. A face whose cells all have chi = 0 is + * condensed: its Darcy law reduces to the two-point relation A m_f = Phi_L - Phi_R, Phi = p - rho ( gamma_K - gamma_f ). */ #ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_MIXEDMFDKERNELS_HPP @@ -29,6 +39,7 @@ #include "linearAlgebra/interfaces/InterfaceTypes.hpp" #include "mesh/MeshLevel.hpp" #include "mixedMimetic/MixedMimeticDispatch.hpp" +#include "mixedMimetic/MixedMimeticBoundaryConditions.hpp" #include "mixedMimetic/MixedMimeticFields.hpp" #include "mixedMimetic/consistency/ConsistencyAdaptationKernels.hpp" #include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" @@ -45,14 +56,11 @@ namespace singlePhaseMixedMFDKernels /** * @class ElementBasedAssemblyKernel * @tparam NUM_FACE number of faces per element - * @tparam IP the type of MFD inner product used in the MFD-compatible cells - * @brief Assemble the cell-wise contributions of the mixed mimetic saddle-point system: - * the face-based constitutive (Darcy closure) rows and the cell-based divergence terms. + * @tparam IP the consistent inner product M_mfd + * @brief Cell contributions: Darcy law on the non-condensed faces and mass balance. * - * On each cell, the local mass matrix is selected according to the adaptive stencil flag: - * TPFA-compatible cells use the diagonal TPFA mass matrix, MFD-compatible cells use IP. - * The face rows are assembled with respect to the fixed global orientation of the face normal, - * so that the (unknown) interface pressure cancels across interior interfaces. + * The Darcy law of a face is multiplied by sigma_f: the contributions of its two cells add up to the face + * equation, in which pi_f cancels. */ template< integer NUM_FACE, typename IP > class ElementBasedAssemblyKernel @@ -101,8 +109,10 @@ class ElementBasedAssemblyKernel m_elemPerm( permeability.permeability() ), m_elemPres( subRegion.getField< fields::flow::pressure >() ), m_faceFlux( faceManager.getField< fields::mixedMimetic::faceMassFlux >() ), - m_bcPres( faceManager.getField< fields::flow::bcPressure >() ), - m_isPresBcFace( faceManager.getField< fields::flow::isBoundaryFace >() ), + m_faceArea( faceManager.faceArea() ), + m_flowBc{ faceManager.getField< fields::mixedMimetic::flowBoundaryType >(), + faceManager.getField< fields::mixedMimetic::flowBoundaryValue >(), + faceManager.getField< fields::mixedMimetic::flowBoundaryCoefficient >() }, m_faceStencilLabel( faceManager.getField< fields::mixedMimetic::faceStencilLabel >() ), m_mfdFlag( subRegion.getField< fields::mixedMimetic::mfdFlag >() ), m_elemDens( fluid.density() ), @@ -115,7 +125,7 @@ class ElementBasedAssemblyKernel /** * @struct StackVariables - * @brief Kernel variables (dof numbers, jacobian and residual) located on the stack + * @brief Stack variables of the flow equations */ struct StackVariables { @@ -124,25 +134,30 @@ class ElementBasedAssemblyKernel : massMatrix( NUM_FACE, NUM_FACE ) {} - /// adaptive local mass matrix: chi * M_mfd + (1 - chi) * M_tpfa + /// M = chi M_mfd + ( 1 - chi ) M_tpfa stackArray2d< real64, NUM_FACE *NUM_FACE > massMatrix; - /// sign relating the cell-outward normal to the fixed global face normal + /// sigma_i real64 orientation[NUM_FACE]{}; - /// 1 if the face is a domain-boundary face without a pressure boundary condition (no-flow) - integer isNoFlowFace[NUM_FACE]{}; + /// 1 if m_f is prescribed (Neumann) + integer isEssentialFace[NUM_FACE]{}; - /// 1 if the face flux is condensed into a two-point expression (all-TPFA neighborhood): - /// its closure row and its divergence contribution are assembled by the face-based kernel + /// 1 if m_f is condensed into a two-point relation integer isCondensedFace[NUM_FACE]{}; - /// face constitutive residuals (local, outward-oriented) and their derivatives + /// sigma_i m_i, d( sigma_i m_i )/dm_i (0 if prescribed), ( M sigma m )_i and gamma_K - gamma_f + real64 localFlux[NUM_FACE]{}; + real64 dLocalFlux[NUM_FACE]{}; + real64 mDotFlux[NUM_FACE]{}; + real64 gravCoefDif[NUM_FACE]{}; + + /// Darcy residuals and their derivatives real64 faceResidual[NUM_FACE]{}; real64 dFaceResidual_dFlux[NUM_FACE][NUM_FACE]{}; real64 dFaceResidual_dPres[NUM_FACE]{}; - /// divergence of the face fluxes and its derivatives + /// dt sum_f sigma_f m_f and its derivatives real64 divFlux = 0.0; real64 dDivFlux_dFlux[NUM_FACE]{}; @@ -153,7 +168,7 @@ class ElementBasedAssemblyKernel }; /** - * @brief Performs the setup phase for the kernel. + * @brief Row and column indices of the cell. */ GEOS_HOST_DEVICE void setup( localIndex const ei, @@ -170,7 +185,7 @@ class ElementBasedAssemblyKernel } /** - * @brief Compute the local constitutive rows and the flux divergence in a given element. + * @brief Darcy residuals and flux divergence of the cell. */ GEOS_HOST_DEVICE void compute( localIndex const ei, @@ -178,7 +193,7 @@ class ElementBasedAssemblyKernel { real64 const perm[ 3 ] = { m_elemPerm[ei][0][0], m_elemPerm[ei][0][1], m_elemPerm[ei][0][2] }; - // step 2: cell mobility scaling of the inverse Darcy coefficient + // ( mu / rho )_K and its pressure derivative real64 const mob = m_mob[ei]; real64 const dMob_dPres = m_dMob[ei][DerivOffset::dP]; real64 const invMob = 1.0 / mob; @@ -187,35 +202,32 @@ class ElementBasedAssemblyKernel real64 const ccDens = m_elemDens[ei][0]; real64 const dCcDens_dPres = m_dElemDens[ei][0][DerivOffset::dP]; - // step 3: face orientations, localized fluxes and no-flow flags. - // The orientation must be identical on every MPI rank sharing the face (the face - // mass-flux unknown is shared across ranks): the face points out of the adjacent cell - // with the smallest global element index, stored per face by the face owner and - // synchronized, so that a ghost cell whose neighbour is absent on this rank still - // orients the face like its owner - real64 localFlux[NUM_FACE]{}; + // sigma_f = +1 for the cell of smallest global index of the face, -1 for the other. That cell is stored by + // the owner of the face and synchronized: sigma_f is the same on every rank + real64 ( &localFlux )[NUM_FACE] = stack.localFlux; globalIndex const myGlobalElem = m_myElemLocalToGlobal[ei]; for( integer i = 0; i < NUM_FACE; ++i ) { localIndex const kf = m_elemToFaces[ei][i]; stack.orientation[i] = ( myGlobalElem == m_faceOrientationCell[kf] ) ? 1.0 : -1.0; - localFlux[i] = stack.orientation[i] * m_faceFlux[kf]; + stack.gravCoefDif[i] = m_elemGravCoef[ei] - m_faceGravCoef[kf]; + + stack.isEssentialFace[i] = m_flowBc[kf].isEssential() ? 1 : 0; + stack.isCondensedFace[i] = ( m_faceStencilLabel[kf] == 0 && stack.isEssentialFace[i] == 0 ) ? 1 : 0; - bool const valid0 = ( m_elemRegionList[kf][0] >= 0 && m_elemSubRegionList[kf][0] >= 0 && m_elemList[kf][0] >= 0 ); - bool const valid1 = ( m_elemRegionList[kf][1] >= 0 && m_elemSubRegionList[kf][1] >= 0 && m_elemList[kf][1] >= 0 ); - bool const onBoundary = !( valid0 && valid1 ); - stack.isNoFlowFace[i] = ( onBoundary && m_isPresBcFace[kf] == 0 ) ? 1 : 0; - stack.isCondensedFace[i] = ( m_faceStencilLabel[kf] == 0 && stack.isNoFlowFace[i] == 0 ) ? 1 : 0; + // prescribed flux: sigma m_f = g |f|, d( sigma m_f )/dm_f = 0 + real64 const faceFlux = stack.isEssentialFace[i] == 1 ? m_flowBc[kf].essentialFlux( stack.orientation[i], m_faceArea[kf] ) + : m_faceFlux[kf]; + localFlux[i] = stack.orientation[i] * faceFlux; + stack.dLocalFlux[i] = stack.isEssentialFace[i] == 1 ? 0.0 : stack.orientation[i]; } - // step 1: adaptive inner product, M = chi * M_mfd + (1 - chi) * M_tpfa. - // Only needed if the cell has at least one live (non-condensed, non-no-flow) face: - // cells fully surrounded by TPFA-condensed faces skip the local operator entirely + // M, needed if one face of the cell is non-condensed bool anyLiveFace = false; for( integer i = 0; i < NUM_FACE; ++i ) { - anyLiveFace = anyLiveFace || ( stack.isNoFlowFace[i] == 0 && stack.isCondensedFace[i] == 0 ); + anyLiveFace = anyLiveFace || ( stack.isEssentialFace[i] == 0 && stack.isCondensedFace[i] == 0 ); } if( anyLiveFace ) { @@ -231,14 +243,11 @@ class ElementBasedAssemblyKernel stack.massMatrix ); } - // step 4: constitutive (Darcy closure) rows: - // invMob * sum_j M_ij (sigma_j m_j) - p_K + pi_f + rho * ( g_K - g_f ) = 0 - // the interface pressure pi_f is dropped on interior faces (it cancels upon global - // orientation-signed assembly) and replaced by the prescribed value on boundary faces; - // condensed faces are assembled by the face-based two-point kernel instead + // Darcy law on the non-condensed faces: ( mu / rho )_K sum_j M_ij sigma_j m_j - p_K + rho_K ( gamma_K - gamma_f ) + pi_f = 0. + // pi_f cancels between the two cells of an interior face; the boundary condition defines it on a boundary face for( integer i = 0; i < NUM_FACE; ++i ) { - if( stack.isNoFlowFace[i] == 1 || stack.isCondensedFace[i] == 1 ) + if( stack.isEssentialFace[i] == 1 || stack.isCondensedFace[i] == 1 ) { continue; } @@ -249,44 +258,45 @@ class ElementBasedAssemblyKernel for( integer j = 0; j < NUM_FACE; ++j ) { mDotFlux += stack.massMatrix( i, j ) * localFlux[j]; - stack.dFaceResidual_dFlux[i][j] = invMob * stack.massMatrix( i, j ) * stack.orientation[j]; + stack.dFaceResidual_dFlux[i][j] = invMob * stack.massMatrix( i, j ) * stack.dLocalFlux[j]; } + stack.mDotFlux[i] = mDotFlux; - real64 const gravCoefDif = m_elemGravCoef[ei] - m_faceGravCoef[kf]; + real64 const gravCoefDif = stack.gravCoefDif[i]; stack.faceResidual[i] = invMob * mDotFlux - m_elemPres[ei] + ccDens * gravCoefDif; stack.dFaceResidual_dPres[i] = dInvMob_dPres * mDotFlux - 1.0 + dCcDens_dPres * gravCoefDif; - bool const onBoundary = ( m_elemRegionList[kf][0] == -1 || m_elemRegionList[kf][1] == -1 ); - if( onBoundary && m_isPresBcFace[kf] == 1 ) + mixedMimeticBoundary::FaceBoundaryCondition const bc = m_flowBc[kf]; + if( bc.type != mixedMimeticBoundary::BoundaryType::interior ) { - stack.faceResidual[i] += m_bcPres[kf]; + real64 dTrace_dFlux = 0.0; + stack.faceResidual[i] += bc.trace( stack.orientation[i], m_faceArea[kf], localFlux[i], dTrace_dFlux ); + stack.dFaceResidual_dFlux[i][i] += dTrace_dFlux * stack.orientation[i]; } } - // step 5: divergence of the face fluxes for the cell-centered mass conservation equation. - // No-flow faces carry m = 0 exactly and condensed faces are handled by the face-based - // kernel (two-point expression in the pressures), so only live faces contribute here + // dt sum_f sigma_f m_f over the non-condensed faces for( integer i = 0; i < NUM_FACE; ++i ) { - if( stack.isNoFlowFace[i] == 1 || stack.isCondensedFace[i] == 1 ) + if( stack.isCondensedFace[i] == 1 ) { stack.dDivFlux_dFlux[i] = 0.0; continue; } stack.divFlux += m_dt * localFlux[i]; - stack.dDivFlux_dFlux[i] = m_dt * stack.orientation[i]; + stack.dDivFlux_dFlux[i] = m_dt * stack.dLocalFlux[i]; } } /** - * @brief Assemble the local contributions into the global system. + * @brief Add the contributions of the cell to the system. */ GEOS_HOST_DEVICE void complete( localIndex const ei, StackVariables & stack ) const { - // step 1: cell-centered mass conservation contribution (single thread per row) + // mass balance of the cell if( m_elemGhostRank[ei] < 0 ) { m_localRhs[stack.cellCenteredEqnRowIndex] += stack.divFlux; @@ -297,8 +307,7 @@ class ElementBasedAssemblyKernel NUM_FACE ); } - // step 2: face-centered constitutive rows, signed by the global face orientation - // (condensed faces are assembled by the face-based two-point kernel) + // Darcy law, multiplied by sigma_f for( integer i = 0; i < NUM_FACE; ++i ) { localIndex const kf = m_elemToFaces[ei][i]; @@ -307,11 +316,12 @@ class ElementBasedAssemblyKernel continue; } - if( stack.isNoFlowFace[i] == 1 ) + if( stack.isEssentialFace[i] == 1 ) { - // essential no-flow condition: m_f = 0 (row owned by the unique adjacent cell) + // Neumann condition: m_f - sigma g |f| = 0 real64 const one = 1.0; - RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[stack.faceCenteredEqnRowIndex[i]], m_faceFlux[kf] ); + real64 const target = m_flowBc[kf].essentialFlux( stack.orientation[i], m_faceArea[kf] ); + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[stack.faceCenteredEqnRowIndex[i]], m_faceFlux[kf] - target ); m_localMatrix.addToRow< parallelDeviceAtomic >( stack.faceCenteredEqnRowIndex[i], &stack.faceDofColIndices[i], &one, @@ -342,7 +352,7 @@ class ElementBasedAssemblyKernel } /** - * @brief Performs the kernel launch + * @brief Launch the kernel over the cells. */ template< typename POLICY, typename KERNEL_TYPE > static void @@ -363,7 +373,7 @@ class ElementBasedAssemblyKernel protected: - /// offset for my MPI rank + /// first global row of the rank globalIndex const m_rankOffset; /// length tolerance @@ -389,7 +399,7 @@ class ElementBasedAssemblyKernel arrayView2d< localIndex const > const m_elemSubRegionList; arrayView2d< localIndex const > const m_elemList; arrayView1d< globalIndex const > const m_myElemLocalToGlobal; - /// global index of the cell each face points out of (rank-invariant orientation) + /// global index of the cell with sigma_f = +1 arrayView1d< globalIndex const > const m_faceOrientationCell; arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const m_nodePosition; @@ -399,11 +409,11 @@ class ElementBasedAssemblyKernel /// primary variables and boundary data arrayView1d< real64 const > const m_elemPres; arrayView1d< real64 const > const m_faceFlux; - arrayView1d< real64 const > const m_bcPres; - arrayView1d< integer const > const m_isPresBcFace; + arrayView1d< real64 const > const m_faceArea; + mixedMimeticBoundary::FaceBoundaryView const m_flowBc; arrayView1d< integer const > const m_faceStencilLabel; - /// adaptive stencil flag + /// chi arrayView1d< integer const > const m_mfdFlag; /// fluid data @@ -462,20 +472,11 @@ class ElementBasedAssemblyKernelFactory /** * @class TpfaCondensedFluxKernel - * @brief Face-based assembly of the condensed (label-0) fluxes: on faces whose adjacent - * cells are all TPFA-compatible, the closure row is exactly diagonal and the flux - * can be substituted into the mass conservation rows as the standard two-point - * expression m_f = pot / A with A = sum_K invMob_K / t_K. - * - * The kernel writes: - * - the TPFA two-point contribution dt * m_f (+full Jacobian wrt the two cell pressures) - * into both adjacent cell rows (the pressure block gains standard TPFA stencil entries); - * - the closure row A * m_f - pot = 0 as a one-way *definition* of the face flux - * (no other equation references m_f, so the flux dof decouples from the Krylov - * iteration and reduces the live system to cells + MFD faces). - * - * The scheme is algebraically identical to the fully-coupled assembly: the closure row is - * unchanged, only its exact substitution into the divergence is performed ahead of time. + * @brief Contributions of the condensed faces, with L and R the cells of the face. M_tpfa is diagonal, so the + * Darcy laws of L and R give m_f = ( Phi_L - Phi_R ) / A, A = sum_K ( mu / rho )_K / t_K, t_K the one-sided + * transmissibility. The kernel adds +/- dt m_f( p_L, p_R ) to the mass balances of L and R and the face + * equation A m_f = Phi_L - Phi_R. No other equation depends on m_f: the system is equivalent to the + * non-condensed one, and m_f is decoupled from the cell unknowns. */ class TpfaCondensedFluxKernel { @@ -530,8 +531,10 @@ class TpfaCondensedFluxKernel m_faceToNodes( faceManager.nodeList().toViewConst() ), m_faceGravCoef( faceManager.getField< fields::flow::gravityCoefficient >() ), m_faceFlux( faceManager.getField< fields::mixedMimetic::faceMassFlux >() ), - m_bcPres( faceManager.getField< fields::flow::bcPressure >() ), - m_isPresBcFace( faceManager.getField< fields::flow::isBoundaryFace >() ), + m_faceArea( faceManager.faceArea() ), + m_flowBc{ faceManager.getField< fields::mixedMimetic::flowBoundaryType >(), + faceManager.getField< fields::mixedMimetic::flowBoundaryValue >(), + faceManager.getField< fields::mixedMimetic::flowBoundaryCoefficient >() }, m_faceStencilLabel( faceManager.getField< fields::mixedMimetic::faceStencilLabel >() ), m_elemRegionList( faceManager.elementRegionList() ), m_elemSubRegionList( faceManager.elementSubRegionList() ), @@ -553,16 +556,16 @@ class TpfaCondensedFluxKernel m_localRhs( localRhs ) {} + /** + * @brief Cells of a face in the target regions, the cell with sigma_f = +1 first. + * @return the number of cells + */ GEOS_HOST_DEVICE - void compute( localIndex const kf ) const + integer gatherCells( localIndex const kf, + localIndex ( & er )[2], + localIndex ( & esr )[2], + localIndex ( & ei )[2] ) const { - if( m_faceStencilLabel[kf] != 0 ) - { - return; - } - - // gather the (at most two) adjacent target cells - localIndex er[2]{}, esr[2]{}, ei[2]{}; integer numElems = 0; for( integer k = 0; k < m_elemRegionList.size( 1 ); ++k ) { @@ -575,20 +578,7 @@ class TpfaCondensedFluxKernel numElems++; } } - if( numElems == 0 ) - { - return; - } - - bool const onBoundary = ( numElems == 1 ); - if( onBoundary && m_isPresBcFace[kf] == 0 ) - { - // no-flow face: the identity row m_f = 0 is assembled by the cell-based kernel - return; - } - - // global orientation: the face points out of the adjacent cell with the smallest - // global element index (must match the cell-based kernel and be rank-invariant) + // L is the cell of smallest global index: sigma_f = +1, as in the cell kernel if( numElems == 2 && m_elemLocalToGlobal[er[1]][esr[1]][ei[1]] < m_elemLocalToGlobal[er[0]][esr[0]][ei[0]] ) { @@ -597,62 +587,125 @@ class TpfaCondensedFluxKernel tmp = esr[0]; esr[0] = esr[1]; esr[1] = tmp; tmp = ei[0]; ei[0] = ei[1]; ei[1] = tmp; } + return numElems; + } - // the same one-sided conductance and floor as the diagonal entries of TPFAInnerProduct::computeM + /// A, pot = Phi_L - Phi_R, m_f = pot / A and their derivatives with respect to the cell unknowns + template< integer NUM_DERIV > + struct TwoPointFlux + { + real64 A = 0.0; + real64 pot = 0.0; + real64 flux = 0.0; + real64 dA[2][NUM_DERIV]{}; + real64 dPot[2][NUM_DERIV]{}; + real64 dFlux[2][NUM_DERIV]{}; + }; + + /** + * @brief One-sided transmissibility t_K of a cell through a face for a diagonal coefficient, as in TPFAInnerProduct::computeM. + */ + GEOS_HOST_DEVICE + real64 oneSidedTrans( localIndex const kf, + localIndex const er, localIndex const esr, localIndex const ei, + real64 const (&coef)[3] ) const + { real64 const areaTolerance = m_lengthTolerance * m_lengthTolerance; real64 const weightTolerance = 1e-30 * m_lengthTolerance; + return LvArray::math::max( mimeticInnerProduct::TPFAInnerProduct::computeOneSidedTrans( m_nodePosition, + m_faceToNodes, + kf, + m_elemCenter[er][esr][ei], + coef, + areaTolerance ), + weightTolerance ); + } - // per-side one-sided coefficients: a_K = invMob_K / t_K and gravity terms - real64 a[2]{}, dA_dp[2]{}, grav[2]{}, dGrav_dp[2]{}; + /** + * @brief Two-point mass flux m_f = ( Phi_L - Phi_R ) / A, A = sum_K ( mu / rho )_K / t_K. On a boundary face + * Phi_R is the boundary value and A includes 1 / ( alpha |f| ). + * @tparam NUM_DERIV 1 (pressure) or 2 (pressure and temperature) derivatives of the cell unknowns + */ + template< integer NUM_DERIV > + GEOS_HOST_DEVICE + void computeTwoPointMassFlux( localIndex const kf, + localIndex const (&er)[2], localIndex const (&esr)[2], localIndex const (&ei)[2], + integer const numElems, + TwoPointFlux< NUM_DERIV > & tp ) const + { + real64 grav[2]{}, dGrav[2][NUM_DERIV]{}; for( integer k = 0; k < numElems; ++k ) { real64 const perm[3] = { m_elemPerm[er[k]][esr[k]][ei[k]][0][0], m_elemPerm[er[k]][esr[k]][ei[k]][0][1], m_elemPerm[er[k]][esr[k]][ei[k]][0][2] }; - - real64 const t = LvArray::math::max( mimeticInnerProduct::TPFAInnerProduct::computeOneSidedTrans( m_nodePosition, - m_faceToNodes, - kf, - m_elemCenter[er[k]][esr[k]][ei[k]], - perm, - areaTolerance ), - weightTolerance ); - - real64 const mob = m_mob[er[k]][esr[k]][ei[k]]; - real64 const invMob = 1.0 / mob; - real64 const dInvMob = -m_dMob[er[k]][esr[k]][ei[k]][DerivOffset::dP] * invMob * invMob; - - a[k] = invMob / t; - dA_dp[k] = dInvMob / t; - + real64 const t = oneSidedTrans( kf, er[k], esr[k], ei[k], perm ); + real64 const invMob = 1.0 / m_mob[er[k]][esr[k]][ei[k]]; real64 const gravCoefDif = m_elemGravCoef[er[k]][esr[k]][ei[k]] - m_faceGravCoef[kf]; + tp.A += invMob / t; grav[k] = m_dens[er[k]][esr[k]][ei[k]][0] * gravCoefDif; - dGrav_dp[k] = m_dDens[er[k]][esr[k]][ei[k]][0][DerivOffset::dP] * gravCoefDif; + for( integer d = 0; d < NUM_DERIV; ++d ) + { + tp.dA[k][d] = -m_dMob[er[k]][esr[k]][ei[k]][d] * invMob * invMob / t; + dGrav[k][d] = m_dDens[er[k]][esr[k]][ei[k]][0][d] * gravCoefDif; + } + } + if( numElems == 1 ) + { + tp.A += m_flowBc[kf].resistance( m_faceArea[kf] ); } - - // assembled closure: A * m_f = pot, - // interior: pot = (p_L - p_R) - (grav_L - grav_R) - // boundary: pot = p_L - pi_bc - grav_L - real64 const A = ( numElems == 2 ) ? a[0] + a[1] : a[0]; real64 const pL = m_pres[er[0]][esr[0]][ei[0]]; - real64 const pR = ( numElems == 2 ) ? m_pres[er[1]][esr[1]][ei[1]] : m_bcPres[kf]; - real64 const pot = ( pL - pR ) - grav[0] + ( ( numElems == 2 ) ? grav[1] : 0.0 ); - - real64 const mFlux = pot / A; - real64 const dPot_dp[2] = { 1.0 - dGrav_dp[0], -1.0 + dGrav_dp[1] }; - real64 dFlux_dp[2]; + real64 const pR = ( numElems == 2 ) ? m_pres[er[1]][esr[1]][ei[1]] : m_flowBc[kf].value; + tp.pot = ( pL - pR ) - grav[0] + ( ( numElems == 2 ) ? grav[1] : 0.0 ); + tp.flux = tp.pot / tp.A; for( integer k = 0; k < numElems; ++k ) { - dFlux_dp[k] = ( dPot_dp[k] - mFlux * dA_dp[k] ) / A; + real64 const sign = ( k == 0 ) ? 1.0 : -1.0; + for( integer d = 0; d < NUM_DERIV; ++d ) + { + tp.dPot[k][d] = ( d == 0 ? sign : 0.0 ) - sign * dGrav[k][d]; + tp.dFlux[k][d] = ( tp.dPot[k][d] - tp.flux * tp.dA[k][d] ) / tp.A; + } + } + } + + GEOS_HOST_DEVICE + void compute( localIndex const kf ) const + { + if( m_faceStencilLabel[kf] != 0 ) + { + return; + } + + localIndex er[2]{}, esr[2]{}, ei[2]{}; + integer const numElems = gatherCells( kf, er, esr, ei ); + if( numElems == 0 ) + { + return; } + if( m_flowBc[kf].isEssential() ) + { + // prescribed flux: assembled by the cell kernel + return; + } + + TwoPointFlux< 1 > tp; + computeTwoPointMassFlux< 1 >( kf, er, esr, ei, numElems, tp ); + real64 const A = tp.A; + real64 const pot = tp.pot; + real64 const mFlux = tp.flux; + real64 const dA_dp[2] = { tp.dA[0][0], tp.dA[1][0] }; + real64 const dPot_dp[2] = { tp.dPot[0][0], tp.dPot[1][0] }; + real64 const dFlux_dp[2] = { tp.dFlux[0][0], tp.dFlux[1][0] }; + globalIndex elemDofCols[2]; for( integer k = 0; k < numElems; ++k ) { elemDofCols[k] = m_elemDofNumber[er[k]][esr[k]][ei[k]]; } - // 1) two-point contributions to the mass conservation rows: +dt*m to L, -dt*m to R + // mass balances of L and R: +/- dt m_f for( integer k = 0; k < numElems; ++k ) { if( m_elemGhostRank[er[k]][esr[k]][ei[k]] >= 0 ) @@ -675,7 +728,7 @@ class TpfaCondensedFluxKernel numElems ); } - // 2) one-way closure row defining the (decoupled) face flux: A * m_f - pot = 0 + // face equation: A m_f - ( Phi_L - Phi_R ) = 0 if( m_faceGhostRank[kf] < 0 ) { localIndex const row = m_faceDofNumber[kf] - m_rankOffset; @@ -723,8 +776,8 @@ class TpfaCondensedFluxKernel ArrayOfArraysView< localIndex const > const m_faceToNodes; arrayView1d< real64 const > const m_faceGravCoef; arrayView1d< real64 const > const m_faceFlux; - arrayView1d< real64 const > const m_bcPres; - arrayView1d< integer const > const m_isPresBcFace; + arrayView1d< real64 const > const m_faceArea; + mixedMimeticBoundary::FaceBoundaryView const m_flowBc; arrayView1d< integer const > const m_faceStencilLabel; arrayView2d< localIndex const > const m_elemRegionList; arrayView2d< localIndex const > const m_elemSubRegionList; @@ -732,7 +785,7 @@ class TpfaCondensedFluxKernel arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const m_nodePosition; SortedArrayView< localIndex const > const m_regionFilter; - /// element data (cross-region accessors) + /// cell data ElementViewConst< arrayView1d< globalIndex const > > const m_elemDofNumber; ElementViewConst< arrayView1d< globalIndex const > > const m_elemLocalToGlobal; ElementViewConst< arrayView1d< integer const > > const m_elemGhostRank; diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/ThermalSinglePhaseMixedMFDKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/ThermalSinglePhaseMixedMFDKernels.hpp new file mode 100644 index 00000000000..c5340334076 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/ThermalSinglePhaseMixedMFDKernels.hpp @@ -0,0 +1,812 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file ThermalSinglePhaseMixedMFDKernels.hpp + * + * Energy equations of the mixed mimetic single-phase solver. Unknowns: face heat flux q_f, cell temperature T_K + * and cell specific enthalpy h_K. With sigma_f the orientation of the face f relative to the cell K and m_f the + * face mass flux: + * + * Fourier law sum_j (M_e)_ij sigma_j q_j - T_K + T_f = 0 M_e the inner product weighted by K_e^{-1} + * energy balance E_K - E_K^n + dt sum_f sigma_f ( m_f h_f^up + q_f ) = 0 + * closure h_K - h_eos( p_K, T_K ) = 0 + * + * h_f^up is the enthalpy of the upstream cell, or the prescribed value on an inflow boundary face. + * The heat operator uses the cell classification eta of the flow operator. + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_THERMALMIXEDMFDKERNELS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_THERMALMIXEDMFDKERNELS_HPP + +#include "constitutive/thermalConductivity/SinglePhaseThermalConductivityBase.hpp" +#include "constitutive/thermalConductivity/ThermalConductivityFields.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp" + +namespace geos +{ + +namespace thermalSinglePhaseMixedMFDKernels +{ + +/// components of the cell unknown: p_K, T_K, h_K +struct CellDof +{ + static constexpr integer pressure = 0; + static constexpr integer temperature = 1; + static constexpr integer enthalpy = 2; + static constexpr integer num = 3; +}; + +/******************************** ElementBasedAssemblyKernel ********************************/ + +/** + * @class ElementBasedAssemblyKernel + * @brief Cell contributions: temperature derivatives of the isothermal equations, Fourier law on the + * non-condensed faces, energy balance. + */ +template< integer NUM_FACE, typename IP > +class ElementBasedAssemblyKernel : public singlePhaseMixedMFDKernels::ElementBasedAssemblyKernel< NUM_FACE, IP > +{ +public: + + using Base = singlePhaseMixedMFDKernels::ElementBasedAssemblyKernel< NUM_FACE, IP >; + using ThermalDerivOffset = constitutive::singlefluid::DerivativeOffsetC< 1 >; + + template< typename VIEWTYPE > + using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; + + using Base::m_rankOffset; + using Base::m_dt; + using Base::m_elemGhostRank; + using Base::m_elemDofNumber; + using Base::m_faceGhostRank; + using Base::m_elemToFaces; + using Base::m_elemCenter; + using Base::m_elemVolume; + using Base::m_faceToNodes; + using Base::m_elemRegionList; + using Base::m_elemSubRegionList; + using Base::m_elemList; + using Base::m_nodePosition; + using Base::m_lengthTolerance; + using Base::m_mfdFlag; + using Base::m_myElemLocalToGlobal; + using Base::m_faceStencilLabel; + using Base::m_mob; + using Base::m_dMob; + using Base::m_dElemDens; + using Base::m_localMatrix; + using Base::m_localRhs; + + ElementBasedAssemblyKernel( globalIndex const rankOffset, + real64 const & lengthTolerance, + string const elemDofKey, + string const faceDofKey, + string const faceHeatDofKey, + NodeManager const & nodeManager, + FaceManager const & faceManager, + ElementRegionManager const & elemManager, + CellElementSubRegion const & subRegion, + constitutive::SingleFluidBase const & fluid, + constitutive::PermeabilityBase const & permeability, + constitutive::SinglePhaseThermalConductivityBase const & conductivity, + ElementRegionManager::ElementViewAccessor< arrayView1d< real64 const > > const & enthalpyAcc, + ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > const & elemDofNumberAcc, + ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > const & elemLocalToGlobalAcc, + SortedArrayView< localIndex const > const & regionFilter, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + : Base( rankOffset, lengthTolerance, elemDofKey, faceDofKey, nodeManager, faceManager, + subRegion, fluid, permeability, dt, localMatrix, localRhs ), + m_faceHeatDofNumber( faceManager.getReference< array1d< globalIndex > >( faceHeatDofKey ) ), + m_faceArea( faceManager.faceArea() ), + m_faceHeatFlux( faceManager.getField< fields::mixedMimetic::faceHeatFlux >() ), + m_heatBc{ faceManager.getField< fields::mixedMimetic::heatBoundaryType >(), + faceManager.getField< fields::mixedMimetic::heatBoundaryValue >(), + faceManager.getField< fields::mixedMimetic::heatBoundaryCoefficient >() }, + m_isEnthalpyBcFace( faceManager.getField< fields::mixedMimetic::isEnthalpyBcFace >() ), + m_bcEnthalpy( faceManager.getField< fields::mixedMimetic::bcEnthalpy >() ), + m_elemTemp( subRegion.getField< fields::flow::temperature >() ), + m_elemEnthalpy( subRegion.getField< fields::mixedMimetic::enthalpy >() ), + m_effCond( conductivity.effectiveConductivity() ), + m_regionFilter( regionFilter ), + m_enthalpyAcc( enthalpyAcc.toNestedViewConst() ), + m_elemDofNumberAcc( elemDofNumberAcc.toNestedViewConst() ), + m_elemLocalToGlobalAcc( elemLocalToGlobalAcc.toNestedViewConst() ) + { + GEOS_UNUSED_VAR( elemManager ); + } + + /** + * @struct StackVariables + * @brief Stack variables of the energy equations + */ + struct StackVariables : public Base::StackVariables + { + GEOS_HOST_DEVICE + StackVariables() + : Base::StackVariables(), + heatMatrix( NUM_FACE, NUM_FACE ) + {} + + /// M_e = chi M_mfd( K_e ) + ( 1 - chi ) M_tpfa( K_e ) + stackArray2d< real64, NUM_FACE *NUM_FACE > heatMatrix; + + /// d( Darcy residual )/dT_K + real64 dFaceResidual_dTemp[NUM_FACE]{}; + + /// 1 if q_f is prescribed (Neumann); 1 if q_f is condensed into a two-point relation + integer isEssentialHeatFace[NUM_FACE]{}; + integer isCondensedHeatFace[NUM_FACE]{}; + + /// sigma_i q_i, d( sigma_i q_i )/dq_i (0 if prescribed), Fourier residuals and their derivatives + real64 localHeatFlux[NUM_FACE]{}; + real64 dLocalHeatFlux[NUM_FACE]{}; + real64 heatResidual[NUM_FACE]{}; + real64 dHeatResidual_dHeatFlux[NUM_FACE][NUM_FACE]{}; + + /// dt sum_f sigma_f ( m_f h_f^up + q_f ) and its derivatives + real64 energyFlux = 0.0; + real64 dEnergyFlux_dFlux[NUM_FACE]{}; + real64 dEnergyFlux_dHeatFlux[NUM_FACE]{}; + real64 dEnergyFlux_dOwnH = 0.0; + real64 dEnergyFlux_dNbrH[NUM_FACE]{}; + globalIndex nbrHDofColIndex[NUM_FACE]{}; + + localIndex faceHeatEqnRowIndex[NUM_FACE]{}; + globalIndex faceHeatDofColIndices[NUM_FACE]{}; + }; + + GEOS_HOST_DEVICE + void setup( localIndex const ei, + StackVariables & stack ) const + { + Base::setup( ei, stack ); + for( integer i = 0; i < NUM_FACE; ++i ) + { + localIndex const kf = m_elemToFaces[ei][i]; + stack.faceHeatEqnRowIndex[i] = m_faceHeatDofNumber[kf] - m_rankOffset; + stack.faceHeatDofColIndices[i] = m_faceHeatDofNumber[kf]; + stack.nbrHDofColIndex[i] = -1; + } + } + + GEOS_HOST_DEVICE + void compute( localIndex const ei, + StackVariables & stack ) const + { + Base::compute( ei, stack ); + + // d( Darcy residual )/dT_K = d( mu / rho )/dT ( M sigma m )_i + d( rho )/dT ( gamma_K - gamma_f ) + real64 const invMob = 1.0 / m_mob[ei]; + real64 const dInvMob_dTemp = -m_dMob[ei][ThermalDerivOffset::dT] * invMob * invMob; + real64 const dDens_dTemp = m_dElemDens[ei][0][ThermalDerivOffset::dT]; + for( integer i = 0; i < NUM_FACE; ++i ) + { + stack.dFaceResidual_dTemp[i] = dInvMob_dTemp * stack.mDotFlux[i] + dDens_dTemp * stack.gravCoefDif[i]; + } + + // status of q_f: prescribed, condensed or non-condensed + bool anyLiveHeatFace = false; + for( integer i = 0; i < NUM_FACE; ++i ) + { + localIndex const kf = m_elemToFaces[ei][i]; + stack.isEssentialHeatFace[i] = m_heatBc[kf].isEssential() ? 1 : 0; + stack.isCondensedHeatFace[i] = ( m_faceStencilLabel[kf] == 0 && stack.isEssentialHeatFace[i] == 0 ) ? 1 : 0; + anyLiveHeatFace = anyLiveHeatFace || ( stack.isEssentialHeatFace[i] == 0 && stack.isCondensedHeatFace[i] == 0 ); + + // prescribed flux: sigma q_f = g |f|, d( sigma q_f )/dq_f = 0 + real64 const heatFlux = stack.isEssentialHeatFace[i] == 1 ? m_heatBc[kf].essentialFlux( stack.orientation[i], m_faceArea[kf] ) + : m_faceHeatFlux[kf]; + stack.localHeatFlux[i] = stack.orientation[i] * heatFlux; + stack.dLocalHeatFlux[i] = stack.isEssentialHeatFace[i] == 1 ? 0.0 : stack.orientation[i]; + } + + // M_e, needed if one face of the cell is non-condensed + if( anyLiveHeatFace ) + { + real64 const cond[3] = { m_effCond[ei][0][0], m_effCond[ei][0][1], m_effCond[ei][0][2] }; + real64 const chi = static_cast< real64 >( m_mfdFlag[ei] ); + mimeticInnerProduct::AdaptiveInnerProduct< IP >::template computeM< NUM_FACE >( m_nodePosition, + m_faceToNodes, + m_elemToFaces[ei], + m_elemCenter[ei], + m_elemVolume[ei], + cond, + m_lengthTolerance, + chi, + stack.heatMatrix ); + } + + // Fourier law on the non-condensed faces: sum_j (M_e)_ij sigma_j q_j - T_K + T_f = 0. + // T_f cancels between the two cells of an interior face; the boundary condition defines it on a boundary face + for( integer i = 0; i < NUM_FACE; ++i ) + { + if( stack.isEssentialHeatFace[i] == 1 || stack.isCondensedHeatFace[i] == 1 ) + { + continue; + } + localIndex const kf = m_elemToFaces[ei][i]; + real64 res = -m_elemTemp[ei]; + for( integer j = 0; j < NUM_FACE; ++j ) + { + res += stack.heatMatrix( i, j ) * stack.localHeatFlux[j]; + stack.dHeatResidual_dHeatFlux[i][j] = stack.heatMatrix( i, j ) * stack.dLocalHeatFlux[j]; + } + mixedMimeticBoundary::FaceBoundaryCondition const bc = m_heatBc[kf]; + if( bc.type != mixedMimeticBoundary::BoundaryType::interior ) + { + real64 dTrace_dFlux = 0.0; + res += bc.trace( stack.orientation[i], m_faceArea[kf], stack.localHeatFlux[i], dTrace_dFlux ); + stack.dHeatResidual_dHeatFlux[i][i] += dTrace_dFlux * stack.orientation[i]; + } + stack.heatResidual[i] = res; + } + + // dt sum_f sigma_f ( m_f h_f^up + q_f ) over the non-condensed faces of each operator + stack.energyFlux = 0.0; + stack.dEnergyFlux_dOwnH = 0.0; + for( integer i = 0; i < NUM_FACE; ++i ) + { + localIndex const kf = m_elemToFaces[ei][i]; + stack.dEnergyFlux_dFlux[i] = 0.0; + stack.dEnergyFlux_dHeatFlux[i] = 0.0; + stack.dEnergyFlux_dNbrH[i] = 0.0; + + if( stack.isCondensedFace[i] == 0 ) + { + real64 const outflow = stack.localFlux[i]; + real64 hUp = m_elemEnthalpy[ei]; + bool upwindIsOwn = true; + if( outflow < 0.0 ) + { + bool const onBoundary = ( m_elemRegionList[kf][0] < 0 || m_elemRegionList[kf][1] < 0 ); + if( onBoundary ) + { + if( m_isEnthalpyBcFace[kf] == 1 ) + { + hUp = m_bcEnthalpy[kf]; + upwindIsOwn = false; + } + } + else + { + // upstream cell: the other cell of the face + integer const k = ( m_elemLocalToGlobalAcc[m_elemRegionList[kf][0]][m_elemSubRegionList[kf][0]][m_elemList[kf][0]] + == m_myElemLocalToGlobal[ei] ) ? 1 : 0; + localIndex const er = m_elemRegionList[kf][k]; + localIndex const esr = m_elemSubRegionList[kf][k]; + localIndex const en = m_elemList[kf][k]; + hUp = m_enthalpyAcc[er][esr][en]; + stack.nbrHDofColIndex[i] = m_elemDofNumberAcc[er][esr][en] + CellDof::enthalpy; + stack.dEnergyFlux_dNbrH[i] = m_dt * outflow; + upwindIsOwn = false; + } + } + stack.energyFlux += m_dt * outflow * hUp; + stack.dEnergyFlux_dFlux[i] += m_dt * stack.dLocalFlux[i] * hUp; + if( upwindIsOwn ) + { + stack.dEnergyFlux_dOwnH += m_dt * outflow; + } + } + + if( stack.isCondensedHeatFace[i] == 0 ) + { + stack.energyFlux += m_dt * stack.localHeatFlux[i]; + stack.dEnergyFlux_dHeatFlux[i] = m_dt * stack.dLocalHeatFlux[i]; + } + } + } + + GEOS_HOST_DEVICE + void complete( localIndex const ei, + StackVariables & stack ) const + { + Base::complete( ei, stack ); + + globalIndex const tempDofCol = stack.elemDofColIndex + CellDof::temperature; + globalIndex const ownHDofCol = stack.elemDofColIndex + CellDof::enthalpy; + + // d( Darcy residual )/dT_K on the non-condensed faces + for( integer i = 0; i < NUM_FACE; ++i ) + { + localIndex const kf = m_elemToFaces[ei][i]; + if( m_faceGhostRank[kf] >= 0 || stack.isCondensedFace[i] == 1 || stack.isEssentialFace[i] == 1 ) + { + continue; + } + real64 const dRes_dTemp = stack.orientation[i] * stack.dFaceResidual_dTemp[i]; + m_localMatrix.template addToRow< parallelDeviceAtomic >( stack.faceCenteredEqnRowIndex[i], &tempDofCol, &dRes_dTemp, 1 ); + } + + // energy balance of the cell + if( m_elemGhostRank[ei] < 0 ) + { + localIndex const row = stack.cellCenteredEqnRowIndex + CellDof::temperature; + m_localRhs[row] += stack.energyFlux; + m_localMatrix.template addToRowBinarySearchUnsorted< serialAtomic >( row, &stack.faceDofColIndices[0], + &stack.dEnergyFlux_dFlux[0], NUM_FACE ); + m_localMatrix.template addToRowBinarySearchUnsorted< serialAtomic >( row, &stack.faceHeatDofColIndices[0], + &stack.dEnergyFlux_dHeatFlux[0], NUM_FACE ); + m_localMatrix.template addToRow< serialAtomic >( row, &ownHDofCol, &stack.dEnergyFlux_dOwnH, 1 ); + for( integer i = 0; i < NUM_FACE; ++i ) + { + if( stack.nbrHDofColIndex[i] >= 0 ) + { + m_localMatrix.template addToRow< serialAtomic >( row, &stack.nbrHDofColIndex[i], &stack.dEnergyFlux_dNbrH[i], 1 ); + } + } + } + + // Fourier law, multiplied by sigma_f: the contributions of the two cells add up to the face equation + for( integer i = 0; i < NUM_FACE; ++i ) + { + localIndex const kf = m_elemToFaces[ei][i]; + if( m_faceGhostRank[kf] >= 0 || stack.isCondensedHeatFace[i] == 1 ) + { + continue; + } + localIndex const row = stack.faceHeatEqnRowIndex[i]; + real64 const sigma = stack.orientation[i]; + + if( stack.isEssentialHeatFace[i] == 1 ) + { + // Neumann condition: q_f - sigma g |f| = 0 + real64 const one = 1.0; + real64 const target = m_heatBc[kf].essentialFlux( sigma, m_faceArea[kf] ); + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[row], m_faceHeatFlux[kf] - target ); + m_localMatrix.template addToRow< parallelDeviceAtomic >( row, &stack.faceHeatDofColIndices[i], &one, 1 ); + continue; + } + + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[row], sigma * stack.heatResidual[i] ); + real64 const dRes_dTemp = -sigma; + m_localMatrix.template addToRow< parallelDeviceAtomic >( row, &tempDofCol, &dRes_dTemp, 1 ); + real64 dRes_dHeatFlux[NUM_FACE]{}; + for( integer j = 0; j < NUM_FACE; ++j ) + { + dRes_dHeatFlux[j] = sigma * stack.dHeatResidual_dHeatFlux[i][j]; + } + m_localMatrix.template addToRowBinarySearchUnsorted< parallelDeviceAtomic >( row, &stack.faceHeatDofColIndices[0], + &dRes_dHeatFlux[0], NUM_FACE ); + } + } + +protected: + + arrayView1d< globalIndex const > const m_faceHeatDofNumber; + arrayView1d< real64 const > const m_faceArea; + arrayView1d< real64 const > const m_faceHeatFlux; + mixedMimeticBoundary::FaceBoundaryView const m_heatBc; + arrayView1d< integer const > const m_isEnthalpyBcFace; + arrayView1d< real64 const > const m_bcEnthalpy; + arrayView1d< real64 const > const m_elemTemp; + arrayView1d< real64 const > const m_elemEnthalpy; + arrayView3d< real64 const > const m_effCond; + SortedArrayView< localIndex const > const m_regionFilter; + ElementViewConst< arrayView1d< real64 const > > const m_enthalpyAcc; + ElementViewConst< arrayView1d< globalIndex const > > const m_elemDofNumberAcc; + ElementViewConst< arrayView1d< globalIndex const > > const m_elemLocalToGlobalAcc; +}; + +/** + * @class ElementBasedAssemblyKernelFactory + */ +class ElementBasedAssemblyKernelFactory +{ +public: + + template< typename POLICY > + static void + createAndLaunch( globalIndex const rankOffset, + real64 const lengthTolerance, + string const elemDofKey, + string const faceDofKey, + string const faceHeatDofKey, + NodeManager const & nodeManager, + FaceManager const & faceManager, + ElementRegionManager const & elemManager, + CellElementSubRegion const & subRegion, + mimeticInnerProduct::MimeticInnerProductBase const & mimeticInnerProductBase, + constitutive::SingleFluidBase const & fluid, + constitutive::PermeabilityBase const & permeability, + constitutive::SinglePhaseThermalConductivityBase const & conductivity, + SortedArrayView< localIndex const > const & regionFilter, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + ElementRegionManager::ElementViewAccessor< arrayView1d< real64 const > > const enthalpyAcc = + elemManager.constructArrayViewAccessor< real64, 1 >( fields::mixedMimetic::enthalpy::key() ); + ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > const elemDofNumberAcc = + elemManager.constructArrayViewAccessor< globalIndex, 1 >( elemDofKey ); + ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > const elemLocalToGlobalAcc = + elemManager.constructArrayViewAccessor< globalIndex, 1 >( ObjectManagerBase::viewKeyStruct::localToGlobalMapString() ); + + mixedMimeticInnerProductDispatch( mimeticInnerProductBase, + [&] ( auto const mimeticInnerProduct ) + { + using IP = TYPEOFREF( mimeticInnerProduct ); + + mixedMimeticKernels::internal::kernelLaunchSelectorFaceSwitch( subRegion.numFacesPerElement(), [&] ( auto NUM_FACES ) + { + ElementBasedAssemblyKernel< NUM_FACES, IP > + kernel( rankOffset, lengthTolerance, elemDofKey, faceDofKey, faceHeatDofKey, nodeManager, faceManager, elemManager, + subRegion, fluid, permeability, conductivity, enthalpyAcc, elemDofNumberAcc, elemLocalToGlobalAcc, + regionFilter, dt, localMatrix, localRhs ); + ElementBasedAssemblyKernel< NUM_FACES, IP >::template launch< POLICY >( subRegion.size(), kernel ); + } ); + } ); + } +}; + +/******************************** TpfaCondensedFluxKernel ********************************/ + +/** + * @class TpfaCondensedFluxKernel + * @brief Contributions of the condensed faces, with L and R the cells of the face and Phi = p - rho ( gamma_K - gamma_f ) + * the potential: two-point fluxes m_f = ( Phi_L - Phi_R ) / A and q_f = ( T_L - T_R ) / A_q, terms + * +/- dt m_f and +/- dt ( m_f h_f^up + q_f ) in the balances of L and R, and the face equations + * A m_f = Phi_L - Phi_R, A_q q_f = T_L - T_R. + */ +class TpfaCondensedFluxKernel : public singlePhaseMixedMFDKernels::TpfaCondensedFluxKernel +{ +public: + + using Base = singlePhaseMixedMFDKernels::TpfaCondensedFluxKernel; + using ThermalDerivOffset = constitutive::singlefluid::DerivativeOffsetC< 1 >; + using TwoPoint = Base::TwoPointFlux< 2 >; + + template< typename VIEWTYPE > + using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; + + using ConductivityAccessors = + StencilMaterialAccessors< constitutive::SinglePhaseThermalConductivityBase, + fields::thermalconductivity::effectiveConductivity >; + + TpfaCondensedFluxKernel( globalIndex const rankOffset, + real64 const & lengthTolerance, + string const faceDofKey, + string const faceHeatDofKey, + NodeManager const & nodeManager, + FaceManager const & faceManager, + Base::DofNumberAccessor const & elemDofNumber, + Base::DofNumberAccessor const & elemLocalToGlobal, + Base::GhostRankAccessor const & elemGhostRank, + ElementRegionManager::ElementViewAccessor< arrayView2d< real64 const > > const & elemCenter, + Base::FlowAccessors const & flowAccessors, + Base::FluidAccessors const & fluidAccessors, + Base::PermeabilityAccessors const & permAccessors, + ConductivityAccessors const & condAccessors, + ElementRegionManager::ElementViewAccessor< arrayView1d< real64 const > > const & temperature, + ElementRegionManager::ElementViewAccessor< arrayView1d< real64 const > > const & enthalpy, + SortedArrayView< localIndex const > const & regionFilter, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + : Base( rankOffset, lengthTolerance, faceDofKey, nodeManager, faceManager, elemDofNumber, elemLocalToGlobal, + elemGhostRank, elemCenter, flowAccessors, fluidAccessors, permAccessors, regionFilter, dt, localMatrix, localRhs ), + m_faceHeatDofNumber( faceManager.getReference< array1d< globalIndex > >( faceHeatDofKey ) ), + m_faceArea( faceManager.faceArea() ), + m_faceHeatFlux( faceManager.getField< fields::mixedMimetic::faceHeatFlux >() ), + m_heatBc{ faceManager.getField< fields::mixedMimetic::heatBoundaryType >(), + faceManager.getField< fields::mixedMimetic::heatBoundaryValue >(), + faceManager.getField< fields::mixedMimetic::heatBoundaryCoefficient >() }, + m_isEnthalpyBcFace( faceManager.getField< fields::mixedMimetic::isEnthalpyBcFace >() ), + m_bcEnthalpy( faceManager.getField< fields::mixedMimetic::bcEnthalpy >() ), + m_effCond( condAccessors.get( fields::thermalconductivity::effectiveConductivity {} ) ), + m_temp( temperature.toNestedViewConst() ), + m_enthalpy( enthalpy.toNestedViewConst() ) + {} + + GEOS_HOST_DEVICE + void compute( localIndex const kf ) const + { + if( m_faceStencilLabel[kf] != 0 ) + { + return; + } + + localIndex er[2]{}, esr[2]{}, ei[2]{}; + integer const numElems = gatherCells( kf, er, esr, ei ); + if( numElems == 0 ) + { + return; + } + + // an operator is condensed on the face unless its flux is prescribed + bool const flowCondensed = !m_flowBc[kf].isEssential(); + bool const heatCondensed = !m_heatBc[kf].isEssential(); + + globalIndex elemDofCols[2][CellDof::num]; + localIndex cellRow[2]; + bool cellOwned[2]; + for( integer k = 0; k < numElems; ++k ) + { + for( integer d = 0; d < CellDof::num; ++d ) + { + elemDofCols[k][d] = m_elemDofNumber[er[k]][esr[k]][ei[k]] + d; + } + cellRow[k] = m_elemDofNumber[er[k]][esr[k]][ei[k]] - m_rankOffset; + cellOwned[k] = m_elemGhostRank[er[k]][esr[k]][ei[k]] < 0; + } + // columns ( p_L, T_L, p_R, T_R ) + globalIndex ptCols[4]; + for( integer k = 0; k < numElems; ++k ) + { + ptCols[2 * k] = elemDofCols[k][CellDof::pressure]; + ptCols[2 * k + 1] = elemDofCols[k][CellDof::temperature]; + } + + // mass flux m_f( p_L, T_L, p_R, T_R ) + if( flowCondensed ) + { + TwoPoint tp; + computeTwoPointMassFlux< 2 >( kf, er, esr, ei, numElems, tp ); + + // h_f^up = h_L if m_f >= 0, else h_R, or the prescribed value on an inflow boundary face + real64 hUp; + integer hUpCell = -1; + if( tp.flux >= 0.0 ) + { + hUp = m_enthalpy[er[0]][esr[0]][ei[0]]; + hUpCell = 0; + } + else if( numElems == 2 ) + { + hUp = m_enthalpy[er[1]][esr[1]][ei[1]]; + hUpCell = 1; + } + else + { + hUp = ( m_isEnthalpyBcFace[kf] == 1 ) ? m_bcEnthalpy[kf] : m_enthalpy[er[0]][esr[0]][ei[0]]; + hUpCell = ( m_isEnthalpyBcFace[kf] == 1 ) ? -1 : 0; + } + + // balances of L and R: +/- dt m_f (mass), +/- dt m_f h_f^up (energy) + for( integer k = 0; k < numElems; ++k ) + { + if( !cellOwned[k] ) + { + continue; + } + real64 const sign = ( k == 0 ) ? 1.0 : -1.0; + real64 massVals[4], energyVals[4]; + for( integer j = 0; j < numElems; ++j ) + { + for( integer d = 0; d < 2; ++d ) + { + massVals[2 * j + d] = sign * m_dt * tp.dFlux[j][d]; + energyVals[2 * j + d] = sign * m_dt * tp.dFlux[j][d] * hUp; + } + } + localIndex const massRow = cellRow[k] + CellDof::pressure; + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[massRow], sign * m_dt * tp.flux ); + m_localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic >( massRow, ptCols, massVals, 2 * numElems ); + + localIndex const energyRow = cellRow[k] + CellDof::temperature; + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[energyRow], sign * m_dt * tp.flux * hUp ); + m_localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic >( energyRow, ptCols, energyVals, 2 * numElems ); + if( hUpCell >= 0 ) + { + real64 const dh = sign * m_dt * tp.flux; + m_localMatrix.addToRow< parallelDeviceAtomic >( energyRow, &elemDofCols[hUpCell][CellDof::enthalpy], &dh, 1 ); + } + } + + // face equation: A m_f - ( Phi_L - Phi_R ) = 0 + if( m_faceGhostRank[kf] < 0 ) + { + localIndex const row = m_faceDofNumber[kf] - m_rankOffset; + globalIndex const faceDofCol = m_faceDofNumber[kf]; + real64 const mCurrent = m_faceFlux[kf]; + m_localRhs[row] += tp.A * mCurrent - tp.pot; + m_localMatrix.addToRow< serialAtomic >( row, &faceDofCol, &tp.A, 1 ); + real64 vals[4]; + for( integer k = 0; k < numElems; ++k ) + { + for( integer d = 0; d < 2; ++d ) + { + vals[2 * k + d] = tp.dA[k][d] * mCurrent - tp.dPot[k][d]; + } + } + m_localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( row, ptCols, vals, 2 * numElems ); + } + } + + // heat flux: A_q q_f = T_L - T_R, A_q = sum_K 1 / t_K; on a boundary face T_R = g and A_q includes 1 / ( alpha |f| ) + if( heatCondensed ) + { + real64 Aq = 0.0; + for( integer k = 0; k < numElems; ++k ) + { + real64 const cond[3] = { m_effCond[er[k]][esr[k]][ei[k]][0][0], + m_effCond[er[k]][esr[k]][ei[k]][0][1], + m_effCond[er[k]][esr[k]][ei[k]][0][2] }; + Aq += 1.0 / oneSidedTrans( kf, er[k], esr[k], ei[k], cond ); + } + real64 const TL = m_temp[er[0]][esr[0]][ei[0]]; + real64 TR; + if( numElems == 2 ) + { + TR = m_temp[er[1]][esr[1]][ei[1]]; + } + else + { + TR = m_heatBc[kf].value; + Aq += m_heatBc[kf].resistance( m_faceArea[kf] ); + } + real64 const potq = TL - TR; + real64 const qFlux = potq / Aq; + real64 const dq_dT[2] = { 1.0 / Aq, -1.0 / Aq }; + globalIndex tCols[2] = { elemDofCols[0][CellDof::temperature], elemDofCols[1][CellDof::temperature] }; + + // energy balances of L and R: +/- dt q_f + for( integer k = 0; k < numElems; ++k ) + { + if( !cellOwned[k] ) + { + continue; + } + real64 const sign = ( k == 0 ) ? 1.0 : -1.0; + localIndex const energyRow = cellRow[k] + CellDof::temperature; + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[energyRow], sign * m_dt * qFlux ); + real64 vals[2] = { sign * m_dt * dq_dT[0], sign * m_dt * dq_dT[1] }; + m_localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic >( energyRow, tCols, vals, numElems ); + } + + // face equation: A_q q_f - ( T_L - T_R ) = 0 + if( m_faceGhostRank[kf] < 0 ) + { + localIndex const row = m_faceHeatDofNumber[kf] - m_rankOffset; + globalIndex const faceHeatDofCol = m_faceHeatDofNumber[kf]; + m_localRhs[row] += Aq * m_faceHeatFlux[kf] - potq; + m_localMatrix.addToRow< serialAtomic >( row, &faceHeatDofCol, &Aq, 1 ); + real64 vals[2] = { -1.0, 1.0 }; + m_localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( row, tCols, vals, numElems ); + } + } + } + + template< typename POLICY, typename KERNEL_TYPE > + static void + launch( localIndex const numFaces, + KERNEL_TYPE const & kernelComponent ) + { + GEOS_MARK_FUNCTION; + forAll< POLICY >( numFaces, [=] GEOS_HOST_DEVICE ( localIndex const kf ) + { + kernelComponent.compute( kf ); + } ); + } + +protected: + + arrayView1d< globalIndex const > const m_faceHeatDofNumber; + arrayView1d< real64 const > const m_faceArea; + arrayView1d< real64 const > const m_faceHeatFlux; + mixedMimeticBoundary::FaceBoundaryView const m_heatBc; + arrayView1d< integer const > const m_isEnthalpyBcFace; + arrayView1d< real64 const > const m_bcEnthalpy; + ElementViewConst< arrayView3d< real64 const > > const m_effCond; + ElementViewConst< arrayView1d< real64 const > > const m_temp; + ElementViewConst< arrayView1d< real64 const > > const m_enthalpy; +}; + +/** + * @class TpfaCondensedFluxKernelFactory + */ +class TpfaCondensedFluxKernelFactory +{ +public: + + template< typename POLICY > + static void + createAndLaunch( globalIndex const rankOffset, + real64 const lengthTolerance, + string const elemDofKey, + string const faceDofKey, + string const faceHeatDofKey, + string const solverName, + NodeManager const & nodeManager, + FaceManager const & faceManager, + ElementRegionManager const & elemManager, + SortedArrayView< localIndex const > const & regionFilter, + real64 const & dt, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + using Base = singlePhaseMixedMFDKernels::TpfaCondensedFluxKernel; + Base::DofNumberAccessor elemDofNumber = elemManager.constructArrayViewAccessor< globalIndex, 1 >( elemDofKey ); + elemDofNumber.setName( solverName + "/accessors/" + elemDofKey ); + Base::DofNumberAccessor const elemLocalToGlobal = + elemManager.constructArrayViewAccessor< globalIndex, 1 >( ObjectManagerBase::viewKeyStruct::localToGlobalMapString() ); + Base::GhostRankAccessor const elemGhostRank = + elemManager.constructArrayViewAccessor< integer, 1 >( ObjectManagerBase::viewKeyStruct::ghostRankString() ); + ElementRegionManager::ElementViewAccessor< arrayView2d< real64 const > > const elemCenter = + elemManager.constructViewAccessor< array2d< real64 >, arrayView2d< real64 const > >( ElementSubRegionBase::viewKeyStruct::elementCenterString() ); + ElementRegionManager::ElementViewAccessor< arrayView1d< real64 const > > const temperature = + elemManager.constructArrayViewAccessor< real64, 1 >( fields::flow::temperature::key() ); + ElementRegionManager::ElementViewAccessor< arrayView1d< real64 const > > const enthalpy = + elemManager.constructArrayViewAccessor< real64, 1 >( fields::mixedMimetic::enthalpy::key() ); + Base::FlowAccessors flowAccessors( elemManager, solverName ); + Base::FluidAccessors fluidAccessors( elemManager, solverName ); + Base::PermeabilityAccessors permAccessors( elemManager, solverName ); + TpfaCondensedFluxKernel::ConductivityAccessors condAccessors( elemManager, solverName ); + + TpfaCondensedFluxKernel kernel( rankOffset, lengthTolerance, faceDofKey, faceHeatDofKey, + nodeManager, faceManager, + elemDofNumber, elemLocalToGlobal, elemGhostRank, elemCenter, + flowAccessors, fluidAccessors, permAccessors, condAccessors, + temperature, enthalpy, + regionFilter, dt, localMatrix, localRhs ); + TpfaCondensedFluxKernel::launch< POLICY >( faceManager.size(), kernel ); + } +}; + +/******************************** EnthalpyClosureKernel ********************************/ + +/** + * @class EnthalpyClosureKernel + * @brief Closure h_K - h_eos( p_K, T_K ) = 0. + */ +class EnthalpyClosureKernel +{ +public: + + using ThermalDerivOffset = constitutive::singlefluid::DerivativeOffsetC< 1 >; + + template< typename POLICY > + static void + launch( globalIndex const rankOffset, + string const elemDofKey, + ElementSubRegionBase const & subRegion, + constitutive::SingleFluidBase const & fluid, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + arrayView1d< globalIndex const > const elemDofNumber = subRegion.getReference< array1d< globalIndex > >( elemDofKey ); + arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); + arrayView1d< real64 const > const enthalpy = subRegion.getField< fields::mixedMimetic::enthalpy >(); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const eosEnthalpy = fluid.enthalpy(); + arrayView3d< real64 const, constitutive::singlefluid::USD_FLUID_DER > const dEosEnthalpy = fluid.dEnthalpy(); + + forAll< POLICY >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + if( elemGhostRank[ei] >= 0 ) + { + return; + } + localIndex const row = elemDofNumber[ei] - rankOffset + CellDof::enthalpy; + globalIndex const cols[3] = { elemDofNumber[ei] + CellDof::pressure, + elemDofNumber[ei] + CellDof::temperature, + elemDofNumber[ei] + CellDof::enthalpy }; + real64 const vals[3] = { -dEosEnthalpy[ei][0][ThermalDerivOffset::dP], + -dEosEnthalpy[ei][0][ThermalDerivOffset::dT], + 1.0 }; + localRhs[row] += enthalpy[ei] - eosEnthalpy[ei][0]; + localMatrix.template addToRow< serialAtomic >( row, cols, vals, 3 ); + } ); + } +}; + +} // namespace thermalSinglePhaseMixedMFDKernels + +} // namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_THERMALMIXEDMFDKERNELS_HPP From 10dbec3abb7c06ab44942cdfcf3ae23f29e84d39 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 18 Sep 2026 20:04:17 -0700 Subject: [PATCH 49/64] wip: minor improvement for MGR --- .../hypre/mgrStrategies/ThermalSinglePhaseMixedMFD.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/ThermalSinglePhaseMixedMFD.hpp b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/ThermalSinglePhaseMixedMFD.hpp index d27c7ba914d..17edf28e3f6 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/ThermalSinglePhaseMixedMFD.hpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/ThermalSinglePhaseMixedMFD.hpp @@ -89,9 +89,12 @@ class ThermalSinglePhaseMixedMFD : public MGRStrategyBase< 2 > { setReduction( precond, mgrData ); - // one BoomerAMG V-cycle on the pressure-temperature Schur complement, without aggressive coarsening + // one BoomerAMG V-cycle on the pressure-temperature Schur complement, without aggressive coarsening. + // C/F Jacobi relaxation: independent of the partition, unlike the hybrid Gauss-Seidel default BoomerAMGParameters amgParameters = pressureTemperatureAMGParameters(); amgParameters.aggressiveNumLevels = 0; + amgParameters.relaxType = hypre::getAMGRelaxationType( LinearSolverParameters::AMG::SmootherType::jacobi ); + amgParameters.numSweeps = 2; configureBoomerAMG( mgrData.coarseSolver, amgParameters ); mgrData.coarseSolver.setup = HYPRE_BoomerAMGSetup; mgrData.coarseSolver.solve = HYPRE_BoomerAMGSolve; From 2875cfe6242b2ebe038517c54da32df2d2dee3ca Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Tue, 22 Sep 2026 13:23:58 -0700 Subject: [PATCH 50/64] fix: make computeResidualWeights public for the CUDA builds --- .../fluidFlow/SinglePhaseMixedMFD.hpp | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp index 138e2566967..295f81f35d8 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp @@ -160,6 +160,17 @@ class SinglePhaseMixedMFD : public SinglePhaseBase virtual void initializePostInitialConditionsPreSubGroups() override; + /** + * @brief Compute the residual-norm weights w_i = max_j |A_ij| s_j, s_j the characteristic scale of unknown j. + * @param domain the domain + * @param dofManager the dof manager + * @param localMatrix the assembled local matrix + * @note public: it contains device lambdas, which nvcc rejects in a private member function + */ + void computeResidualWeights( DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64 const, globalIndex const > const & localMatrix ); + private: /** @@ -200,16 +211,6 @@ class SinglePhaseMixedMFD : public SinglePhaseBase void computeGhostDofs( DomainPartition const & domain, DofManager const & dofManager ); - /** - * @brief Compute the residual-norm weights w_i = max_j |A_ij| s_j, s_j the characteristic scale of unknown j. - * @param domain the domain - * @param dofManager the dof manager - * @param localMatrix the assembled local matrix - */ - void computeResidualWeights( DomainPartition & domain, - DofManager const & dofManager, - CRSMatrixView< real64 const, globalIndex const > const & localMatrix ); - /// relative tolerance used in the mass matrix computations real64 m_areaRelTol; From 5f439972e21f7568e3f760baa8b50d3d933bebe7 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 30 Oct 2026 15:33:17 -0700 Subject: [PATCH 51/64] Update src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp Co-authored-by: Jacques Franc <49998870+jafranc@users.noreply.github.com> --- .../mixedMimetic/MixedMimeticDiscretization.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp index 4bab7fcbef4..5ac1908119e 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp @@ -105,10 +105,8 @@ void MixedMimeticDiscretization::postInputInitialization() void MixedMimeticDiscretization::initializePostInitialConditionsPreSubGroups() { Group::initializePostInitialConditionsPreSubGroups(); - - std::unique_ptr< MimeticInnerProductBase > newMimeticIP = factory( m_innerProductType ); - - registerWrapper< MimeticInnerProductBase >( viewKeyStruct::innerProductString(), std::move( newMimeticIP ) ). + + registerWrapper< MimeticInnerProductBase >( viewKeyStruct::innerProductString(), factory( m_innerProductType ) ). setRestartFlags( dataRepository::RestartFlags::NO_WRITE ); } From 9e8dfd8843b11c1454b6ac9c443c3401cb1b283a Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 30 Oct 2026 15:33:28 -0700 Subject: [PATCH 52/64] Update src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp Co-authored-by: Jacques Franc <49998870+jafranc@users.noreply.github.com> --- .../unitTests/testMimeticInnerProducts.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp index 879d21da964..f3a9f0af135 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp @@ -2127,17 +2127,16 @@ static double mixedIPConsistencyError( array2d< real64, nodes::REFERENCE_POSITIO } // err = max | ( M NK - C )_fd | - double err = 0.0; + real64 err = 0.0; + LvArray::tensorOps::AikBkj< NF, 3, NF >( R, M, NK ); // R_fd = M_fk NK_kd + LvArray::tensorOps::subtract< NF, 3 >( R, C ); // R -= C + + // err = max | R | for( int f = 0; f < NF; ++f ) { for( int d = 0; d < 3; ++d ) { - real64 s = 0.0; - for( int k = 0; k < NF; ++k ) - { - s += M( f, k ) * NK[k][d]; - } - err = std::max( err, std::abs( s - C[f][d] ) ); + err = LvArray::math::max( err, LvArray::math::abs( R[f][d] ) ); } } return err; From b2fe048af6970cecba7115f8d02d357a8c145286 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 30 Oct 2026 18:26:52 -0700 Subject: [PATCH 53/64] wip: apply review comments --- BASELINE_NOTES.md | 4 ++ .../mimeticInnerProducts/RTInnerProduct.hpp | 2 + .../mimeticInnerProducts/TPFAInnerProduct.hpp | 7 ++- .../unitTests/testMimeticInnerProducts.cpp | 5 +- .../interfaces/hypre/hypredrive.cpp | 3 ++ .../mesh/generators/VTKUtilities.cpp | 8 ++-- .../MixedMimeticDiscretization.cpp | 48 +++++++++---------- .../SinglePhaseMixedMFDKernels.hpp | 2 +- 8 files changed, 45 insertions(+), 34 deletions(-) diff --git a/BASELINE_NOTES.md b/BASELINE_NOTES.md index d18697ae942..add552d1e46 100644 --- a/BASELINE_NOTES.md +++ b/BASELINE_NOTES.md @@ -31,6 +31,10 @@ PR #3996 (2026-09-20) +===================== +Add the mixed mimetic discretization: new `NumericalMethods/MixedMimetic` group in every restart file and new mixed MFD tests. + PR #3994 (2026-09-03) ===================== Introduce mimetic inner products for saddle-point operators and align face-centroid computation with exact volume integration on planar polyhedra. diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp index 5972a59edf9..6178d966ef7 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/RTInnerProduct.hpp @@ -174,8 +174,10 @@ RTInnerProduct::compute( arrayView2d< real64 const, nodes::REFERENCE_POSITION_US A[i][j] = transMatrix[i][j]; } } + // M is symmetric positive definite: without pivoting, every pivot is bounded below by its smallest eigenvalue for( localIndex k = 0; k < NF; ++k ) { + GEOS_ASSERT_GT( A[k][k], 0.0 ); real64 const invPivot = 1.0 / A[k][k]; for( localIndex j = 0; j < NF; ++j ) { diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp index b448c61feba..8df78e0f331 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/TPFAInnerProduct.hpp @@ -36,6 +36,9 @@ class TPFAInnerProduct : public MimeticInnerProductBase { public: + /// floor of the one-sided conductance, relative to the length tolerance + static constexpr real64 weightToleranceFactor = 1e-30; + /** * @brief In a given element, recompute the transmissibility matrix in a cell using TPFA. * @param[in] nodePosition the position of the nodes @@ -155,7 +158,7 @@ TPFAInnerProduct::compute( arrayView2d< real64 const, nodes::REFERENCE_POSITION_ GEOS_UNUSED_VAR( elemVolume ); real64 const areaTolerance = lengthTolerance * lengthTolerance; - real64 const weightTolerance = 1e-30 * lengthTolerance; + real64 const weightTolerance = weightToleranceFactor * lengthTolerance; // we are ready to compute the transmissibility matrix for( localIndex ifaceLoc = 0; ifaceLoc < NF; ++ifaceLoc ) @@ -195,7 +198,7 @@ TPFAInnerProduct::computeM( arrayView2d< real64 const, nodes::REFERENCE_POSITION GEOS_UNUSED_VAR( elemVolume ); real64 const areaTolerance = lengthTolerance * lengthTolerance; - real64 const weightTolerance = 1e-30 * lengthTolerance; + real64 const weightTolerance = weightToleranceFactor * lengthTolerance; // initialize M to zero LvArray::tensorOps::fill< NF, NF >( M, 0.0 ); diff --git a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp index f3a9f0af135..64d5448ca76 100644 --- a/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp +++ b/src/coreComponents/finiteVolume/mimeticInnerProducts/unitTests/testMimeticInnerProducts.cpp @@ -2127,9 +2127,10 @@ static double mixedIPConsistencyError( array2d< real64, nodes::REFERENCE_POSITIO } // err = max | ( M NK - C )_fd | + real64 R[NF][3]; + LvArray::tensorOps::Rij_eq_AikBkj< NF, 3, NF >( R, M, NK ); // R_fd = M_fk NK_kd + LvArray::tensorOps::scaledAdd< NF, 3 >( R, C, -1.0 ); // R -= C real64 err = 0.0; - LvArray::tensorOps::AikBkj< NF, 3, NF >( R, M, NK ); // R_fd = M_fk NK_kd - LvArray::tensorOps::subtract< NF, 3 >( R, C ); // R -= C // err = max | R | for( int f = 0; f < NF; ++f ) diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp b/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp index 982d249a095..d57bd1f414a 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp @@ -1434,6 +1434,9 @@ bool buildStrategyYaml( LinearSolverParameters const & params, // custom point markers use more blocks than dof fields: no field-name representation, skip the YAML if( strategy.m_numBlocks > LvArray::integerConversion< HYPRE_Int >( labelNames.size() ) ) { + GEOS_ERROR_IF( params.mgr.strategy != LinearSolverParameters::MGR::StrategyType::singlePhaseMixedMFD, + GEOS_FMT( "hypredrive: the MGR strategy {} has {} blocks for {} dof labels", + params.mgr.strategy, strategy.m_numBlocks, labelNames.size() ) ); return false; } diff --git a/src/coreComponents/mesh/generators/VTKUtilities.cpp b/src/coreComponents/mesh/generators/VTKUtilities.cpp index 9ee1e80d3f9..5733cbce6d0 100644 --- a/src/coreComponents/mesh/generators/VTKUtilities.cpp +++ b/src/coreComponents/mesh/generators/VTKUtilities.cpp @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -3503,11 +3504,12 @@ void importRegularField( stdVector< vtkIdType > const & cellIds, { using DstType = std::remove_reference_t< decltype( val ) >; auto const src = data.Get( cellIdx, componentIdx++ ); - // an integer field only accepts whole numbers from a floating point array + // an integer field only accepts whole numbers of its range [lowest, -lowest) from a floating point array if constexpr ( std::is_integral< DstType >::value && std::is_floating_point< decltype( src ) >::value ) { - GEOS_THROW_IF( !std::isfinite( src ) || std::fabs( src - std::round( src ) ) > 0.0, - GEOS_FMT( "Field '{}' targets an integer field but holds the non-integer value {} at cell {}", + auto const lowest = static_cast< std::remove_cv_t< decltype( src ) > >( std::numeric_limits< DstType >::lowest() ); + GEOS_THROW_IF( !std::isfinite( src ) || std::fabs( src - std::round( src ) ) > 0.0 || src < lowest || src >= -lowest, + GEOS_FMT( "Field '{}' targets an integer field but holds the non-integer or out-of-range value {} at cell {}", vtkArray->GetName(), src, cellIdx ), InputError ); } diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp index 5ac1908119e..eb51a1f86c1 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp @@ -92,7 +92,7 @@ void MixedMimeticDiscretization::postInputInitialization() InputError ); // with a TPFA inner product both branches of the selection coincide: the indicators are only a diagnostic - if( isAdaptiveConsistency() && m_innerProductType == mimeticInnerProduct::MimeticInnerProductTypeStrings::TPFA ) + if( isAdaptiveConsistency() && isTpfaInnerProduct() ) { GEOS_WARNING( GEOS_FMT( "{}: 'adaptiveConsistency' is enabled but 'innerProductType' is TPFA: the adaptation has no " "effect on the discretization (both operators of the adaptive blend coincide), and " @@ -105,8 +105,8 @@ void MixedMimeticDiscretization::postInputInitialization() void MixedMimeticDiscretization::initializePostInitialConditionsPreSubGroups() { Group::initializePostInitialConditionsPreSubGroups(); - - registerWrapper< MimeticInnerProductBase >( viewKeyStruct::innerProductString(), factory( m_innerProductType ) ). + + registerWrapper< MimeticInnerProductBase >( viewKeyStruct::innerProductString(), factory( m_innerProductType ) ). setRestartFlags( dataRepository::RestartFlags::NO_WRITE ); } @@ -125,40 +125,36 @@ MixedMimeticDiscretization::getCatalog() std::unique_ptr< MimeticInnerProductBase > MixedMimeticDiscretization::factory( string const & mimeticInnerProductType ) const { - std::unique_ptr< MimeticInnerProductBase > rval; if( mimeticInnerProductType == MimeticInnerProductTypeStrings::TPFA ) { - rval = std::make_unique< TPFAInnerProduct >(); - } - else if( mimeticInnerProductType == MimeticInnerProductTypeStrings::QuasiTPFA ) - { - rval = std::make_unique< QuasiTPFAInnerProduct >(); + return std::make_unique< TPFAInnerProduct >(); } - else if( mimeticInnerProductType == MimeticInnerProductTypeStrings::Simple ) + if( mimeticInnerProductType == MimeticInnerProductTypeStrings::QuasiTPFA ) { - rval = std::make_unique< SimpleInnerProduct >(); + return std::make_unique< QuasiTPFAInnerProduct >(); } - else if( mimeticInnerProductType == MimeticInnerProductTypeStrings::BdVLM ) + if( mimeticInnerProductType == MimeticInnerProductTypeStrings::Simple ) { - rval = std::make_unique< BdVLMInnerProduct >(); + return std::make_unique< SimpleInnerProduct >(); } - else if( mimeticInnerProductType == MimeticInnerProductTypeStrings::RT ) + if( mimeticInnerProductType == MimeticInnerProductTypeStrings::BdVLM ) { - rval = std::make_unique< RTInnerProduct >(); + return std::make_unique< BdVLMInnerProduct >(); } - else + if( mimeticInnerProductType == MimeticInnerProductTypeStrings::RT ) { - GEOS_ERROR( GEOS_FMT( "Key value of {} does not have an associated mimetic inner product implementing the mixed-form " - "mass matrix (valid options: {}, {}, {}, {}, {}).", - mimeticInnerProductType, - MimeticInnerProductTypeStrings::TPFA, - MimeticInnerProductTypeStrings::QuasiTPFA, - MimeticInnerProductTypeStrings::Simple, - MimeticInnerProductTypeStrings::BdVLM, - MimeticInnerProductTypeStrings::RT ), - getDataContext() ); + return std::make_unique< RTInnerProduct >(); } - return rval; + GEOS_ERROR( GEOS_FMT( "Key value of {} does not have an associated mimetic inner product implementing the mixed-form " + "mass matrix (valid options: {}, {}, {}, {}, {}).", + mimeticInnerProductType, + MimeticInnerProductTypeStrings::TPFA, + MimeticInnerProductTypeStrings::QuasiTPFA, + MimeticInnerProductTypeStrings::Simple, + MimeticInnerProductTypeStrings::BdVLM, + MimeticInnerProductTypeStrings::RT ), + getDataContext() ); + return nullptr; } REGISTER_CATALOG_ENTRY( MixedMimeticDiscretization, MixedMimeticDiscretization, string const &, Group * const ) diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp index f4f1a0c3cf1..ee2ca61e944 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp @@ -600,7 +600,7 @@ class TpfaCondensedFluxKernel // the same one-sided conductance and floor as the diagonal entries of TPFAInnerProduct::computeM real64 const areaTolerance = m_lengthTolerance * m_lengthTolerance; - real64 const weightTolerance = 1e-30 * m_lengthTolerance; + real64 const weightTolerance = mimeticInnerProduct::TPFAInnerProduct::weightToleranceFactor * m_lengthTolerance; // per-side one-sided coefficients: a_K = invMob_K / t_K and gravity terms real64 a[2]{}, dA_dp[2]{}, grav[2]{}, dGrav_dp[2]{}; From 45dc4cb8280889a3cee01c689b8589904fd6165e Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 30 Oct 2026 23:05:53 -0700 Subject: [PATCH 54/64] Update src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp Co-authored-by: Jacques Franc <49998870+jafranc@users.noreply.github.com> --- src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp index 5ac1908119e..221467e7d76 100644 --- a/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp +++ b/src/coreComponents/mixedMimetic/MixedMimeticDiscretization.cpp @@ -92,7 +92,7 @@ void MixedMimeticDiscretization::postInputInitialization() InputError ); // with a TPFA inner product both branches of the selection coincide: the indicators are only a diagnostic - if( isAdaptiveConsistency() && m_innerProductType == mimeticInnerProduct::MimeticInnerProductTypeStrings::TPFA ) + if( isAdaptiveConsistency() && isTpfaInnerProduct() ) { GEOS_WARNING( GEOS_FMT( "{}: 'adaptiveConsistency' is enabled but 'innerProductType' is TPFA: the adaptation has no " "effect on the discretization (both operators of the adaptive blend coincide), and " From 0adc539a85e61e501f43d38a794ffc31b1ffd498 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 30 Oct 2026 23:13:05 -0700 Subject: [PATCH 55/64] wip: apply review comments II --- ...e_hybridMesh_mixed_partialRegion_smoke.xml | 150 ++++++++++++++++++ .../polyhedralDiscretizations/mixedMFD.ats | 8 + .../consistency/ConsistencyAdaptation.cpp | 5 +- .../fluidFlow/SinglePhaseMixedMFD.cpp | 63 ++++---- .../fluidFlow/SinglePhaseMixedMFD.hpp | 8 + .../SinglePhaseMixedMFDKernels.hpp | 28 +++- 6 files changed, 227 insertions(+), 35 deletions(-) create mode 100644 inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_partialRegion_smoke.xml diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_partialRegion_smoke.xml b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_partialRegion_smoke.xml new file mode 100644 index 00000000000..02987edb99a --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/incompressible_hybridMesh_mixed_partialRegion_smoke.xml @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mixedMFD.ats b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mixedMFD.ats index 7f7ead0f46c..fa74abdf0c8 100644 --- a/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mixedMFD.ats +++ b/inputFiles/singlePhaseFlow/polyhedralDiscretizations/mixedMFD.ats @@ -29,6 +29,14 @@ decks = [ restart_step=0, check_step=1, restartcheck_params=RestartcheckParameters(**restartcheck_params)), + TestDeck( + name="incompressible_hybridMesh_mixed_partialRegion_smoke", + description= + 'Adaptive mixed MFD single phase flow test (3D hybrid mesh, incompressible, solver restricted to the hexahedral region)', + partitions=((1, 1, 1), (2, 1, 1), (3, 1, 1)), + restart_step=0, + check_step=1, + restartcheck_params=RestartcheckParameters(**restartcheck_params)), ] generate_geos_tests(decks) diff --git a/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp b/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp index dcabf6de820..cd686a18afb 100644 --- a/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp +++ b/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp @@ -124,6 +124,9 @@ localIndex ConsistencyAdaptation::applyConsistencyLayer( MeshLevel & mesh, params.lengthTolerance, projFaceFlux ); + // the projection kernel moved the nested permeability view to the device: restore host-indexable views + permeability.move( hostMemorySpace, false ); + // steps 2-3: localized normalized residuals, assembled on the global face orientation elemManager.forElementSubRegionsComplete< CellElementSubRegion >( regionNames, [&]( localIndex const, @@ -175,7 +178,7 @@ localIndex ConsistencyAdaptation::applyConsistencyLayer( MeshLevel & mesh, FieldIdentifiers fieldsToBeSync; fieldsToBeSync.addElementFields( { mixedMimetic::mfdFlag::key(), mixedMimetic::consistencyIndicator::key() }, regionNames ); - CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, neighbors, false ); + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, neighbors, true ); return numConsistent; } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index e1bf58bc909..fc04500423c 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -139,26 +139,6 @@ void SinglePhaseMixedMFD::initializePostInitialConditionsPreSubGroups() m_regionFilter.insert( elemManager.getRegions().getIndex( regionName ) ); } - // flag the faces on which a pressure boundary condition is imposed: - // all the remaining domain-boundary faces are treated as no-flow faces - FaceManager & faceManager = mesh.getFaceManager(); - arrayView1d< integer > const isPresBcFace = faceManager.getField< flow::isBoundaryFace >(); - - fsManager.apply< FaceManager >( 0.0, - mesh, - flow::bcPressure::key(), - [&] ( FieldSpecification const &, - string const &, - SortedArrayView< localIndex const > const & targetSet, - FaceManager &, - string const & ) - { - forAll< parallelDevicePolicy<> >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const a ) - { - isPresBcFace[targetSet[a]] = 1; - } ); - } ); - fsManager.forSubGroups< AquiferBoundaryCondition >( [&] ( AquiferBoundaryCondition const & bc ) { GEOS_UNUSED_VAR( bc ); @@ -190,23 +170,24 @@ void SinglePhaseMixedMFD::computeFaceOrientation( DomainPartition & domain ) elemManager.constructArrayViewAccessor< globalIndex, 1 >( ObjectManagerBase::viewKeyStruct::localToGlobalMapString() ); ElementRegionManager::ElementViewConst< arrayView1d< globalIndex const > > const l2g = elemLocalToGlobal.toNestedViewConst(); arrayView1d< globalIndex > const orientationCell = faceManager.getField< mixedMimetic::faceOrientationCell >(); + SortedArrayView< localIndex const > const regionFilter = m_regionFilter.toViewConst(); - // the owner of a face sees both of its cells; ghost faces take the owner's value through the sync + // the owner of a face sees both of its cells; ghost faces take the owner's value through the sync. + // Only target cells are considered, as in the assembly kernels; -1 marks a face with no target cell forAll< parallelHostPolicy >( faceManager.size(), [=]( localIndex const kf ) { - globalIndex gMin = -1; + globalIndex gMin = LvArray::NumericLimits< globalIndex >::max; for( localIndex k = 0; k < 2; ++k ) { localIndex const er = elemRegionList[kf][k]; localIndex const esr = elemSubRegionList[kf][k]; localIndex const ei = elemList[kf][k]; - if( er >= 0 && esr >= 0 && ei >= 0 ) + if( er >= 0 && esr >= 0 && ei >= 0 && regionFilter.contains( er ) ) { - globalIndex const g = l2g[er][esr][ei]; - gMin = ( gMin < 0 || g < gMin ) ? g : gMin; + gMin = LvArray::math::min( gMin, l2g[er][esr][ei] ); } } - orientationCell[kf] = gMin; + orientationCell[kf] = ( gMin == LvArray::NumericLimits< globalIndex >::max ) ? -1 : gMin; } ); FieldIdentifiers fieldsToBeSync; @@ -291,9 +272,21 @@ void SinglePhaseMixedMFD::implicitStepSetup( real64 const & time_n, faceManager.getField< mixedMimetic::faceMassFlux_n >(); faceFlux_n.setValues< parallelDevicePolicy<> >( faceFlux ); } ); +} - // evaluate the boundary face pressure values used in the constitutive rows +void SinglePhaseMixedMFD::assembleSystem( real64 const time_n, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + GEOS_MARK_FUNCTION; + + // evaluate the boundary face pressure values used in the constitutive rows at the end of the attempted step applyFacePressureBCValues( time_n + dt, domain ); + + SinglePhaseBase::assembleSystem( time_n, dt, domain, dofManager, localMatrix, localRhs ); } namespace @@ -316,6 +309,11 @@ void SinglePhaseMixedMFD::applyFacePressureBCValues( real64 const time, MeshLevel & mesh, string_array const & ) { + // flag the faces on which a pressure boundary condition is imposed at this time: + // all the remaining domain-boundary faces are treated as no-flow faces + arrayView1d< integer > const isPresBcFace = mesh.getFaceManager().getField< flow::isBoundaryFace >(); + isPresBcFace.zero(); + fsManager.apply< FaceManager >( time, mesh, flow::bcPressure::key(), @@ -325,6 +323,11 @@ void SinglePhaseMixedMFD::applyFacePressureBCValues( real64 const time, FaceManager & targetGroup, string const & ) { + forAll< parallelDevicePolicy<> >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const a ) + { + isPresBcFace[targetSet[a]] = 1; + } ); + // provide some logging at the first nonlinear iteration if( m_nonlinearSolverParameters.m_numNewtonIterations == 0 ) { @@ -404,6 +407,9 @@ void SinglePhaseMixedMFD::computeMgrPointMarkers( DomainPartition const & domain globalIndex const globalNumCondensedFaces = MpiWrapper::sum< globalIndex >( numCondensedFaces ); GEOS_LOG_RANK_0( GEOS_FMT( "mixedMFD Flow: flux dofs {} non-condensed (saddle point), {} condensed (two-point closure)", globalNumLiveFaces, globalNumCondensedFaces ) ); + GEOS_ERROR_IF_NE_MSG( globalNumLiveFaces + globalNumCondensedFaces, + dofManager.numGlobalDofs( mixedMimetic::faceMassFlux::key() ), + "mixedMFD Flow: the face stencil labels do not cover every flux dof" ); forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, MeshLevel const & mesh, @@ -528,6 +534,7 @@ void SinglePhaseMixedMFD::assembleFluxTerms( real64 const dt, mimeticInnerProductBase, fluid, permeability, + m_regionFilter.toViewConst(), dt, localMatrix, localRhs ); @@ -751,7 +758,7 @@ void SinglePhaseMixedMFD::computeResidualWeights( DomainPartition & domain, { FieldIdentifiers fieldsToBeSync; fieldsToBeSync.addFields( FieldLocation::Face, { mixedMimetic::faceDofScale::key() } ); - CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), false ); + CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), true ); FaceManager const & faceManager = mesh.getFaceManager(); arrayView1d< globalIndex const > const faceDofNumber = faceManager.getReference< array1d< globalIndex > >( faceDofKey ); diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp index 295f81f35d8..f7e09cea756 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp @@ -90,6 +90,14 @@ class SinglePhaseMixedMFD : public SinglePhaseBase ParallelVector & solution, bool const setSparsity = true ) override; + virtual void + assembleSystem( real64 const time_n, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; + virtual void applyBoundaryConditions( real64 const time_n, real64 const dt, diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp index ee2ca61e944..c1fa000c6fc 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp @@ -75,6 +75,7 @@ class ElementBasedAssemblyKernel CellElementSubRegion const & subRegion, constitutive::SingleFluidBase const & fluid, constitutive::PermeabilityBase const & permeability, + SortedArrayView< localIndex const > const & regionFilter, real64 const & dt, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) @@ -109,10 +110,19 @@ class ElementBasedAssemblyKernel m_dElemDens( fluid.dDensity() ), m_mob( subRegion.getField< fields::flow::mobility >() ), m_dMob( subRegion.getField< fields::flow::dMobility >() ), + m_regionFilter( regionFilter ), m_localMatrix( localMatrix ), m_localRhs( localRhs ) {} + /// true if the k-th cell adjacent to face kf exists and belongs to a target region + GEOS_HOST_DEVICE + bool isTargetCell( localIndex const kf, integer const k ) const + { + localIndex const er = m_elemRegionList[kf][k]; + return er >= 0 && m_elemSubRegionList[kf][k] >= 0 && m_elemList[kf][k] >= 0 && m_regionFilter.contains( er ); + } + /** * @struct StackVariables * @brief Kernel variables (dof numbers, jacobian and residual) located on the stack @@ -133,6 +143,9 @@ class ElementBasedAssemblyKernel /// 1 if the face is a domain-boundary face without a pressure boundary condition (no-flow) integer isNoFlowFace[NUM_FACE]{}; + /// 1 if the face is a domain-boundary face with a pressure boundary condition + integer isPresBcFace[NUM_FACE]{}; + /// 1 if the face flux is condensed into a two-point expression (all-TPFA neighborhood): /// its closure row and its divergence contribution are assembled by the face-based kernel integer isCondensedFace[NUM_FACE]{}; @@ -202,10 +215,10 @@ class ElementBasedAssemblyKernel stack.orientation[i] = ( myGlobalElem == m_faceOrientationCell[kf] ) ? 1.0 : -1.0; localFlux[i] = stack.orientation[i] * m_faceFlux[kf]; - bool const valid0 = ( m_elemRegionList[kf][0] >= 0 && m_elemSubRegionList[kf][0] >= 0 && m_elemList[kf][0] >= 0 ); - bool const valid1 = ( m_elemRegionList[kf][1] >= 0 && m_elemSubRegionList[kf][1] >= 0 && m_elemList[kf][1] >= 0 ); - bool const onBoundary = !( valid0 && valid1 ); + // the boundary is that of the target regions: a non-target neighbour counts as absent + bool const onBoundary = !( isTargetCell( kf, 0 ) && isTargetCell( kf, 1 ) ); stack.isNoFlowFace[i] = ( onBoundary && m_isPresBcFace[kf] == 0 ) ? 1 : 0; + stack.isPresBcFace[i] = ( onBoundary && m_isPresBcFace[kf] == 1 ) ? 1 : 0; stack.isCondensedFace[i] = ( m_faceStencilLabel[kf] == 0 && stack.isNoFlowFace[i] == 0 ) ? 1 : 0; } @@ -257,8 +270,7 @@ class ElementBasedAssemblyKernel stack.faceResidual[i] = invMob * mDotFlux - m_elemPres[ei] + ccDens * gravCoefDif; stack.dFaceResidual_dPres[i] = dInvMob_dPres * mDotFlux - 1.0 + dCcDens_dPres * gravCoefDif; - bool const onBoundary = ( m_elemRegionList[kf][0] == -1 || m_elemRegionList[kf][1] == -1 ); - if( onBoundary && m_isPresBcFace[kf] == 1 ) + if( stack.isPresBcFace[i] == 1 ) { stack.faceResidual[i] += m_bcPres[kf]; } @@ -412,6 +424,9 @@ class ElementBasedAssemblyKernel arrayView1d< real64 const > const m_mob; arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const m_dMob; + /// target regions of the solver + SortedArrayView< localIndex const > const m_regionFilter; + /// View on the local CRS matrix and RHS CRSMatrixView< real64, globalIndex const > const m_localMatrix; arrayView1d< real64 > const m_localRhs; @@ -437,6 +452,7 @@ class ElementBasedAssemblyKernelFactory mimeticInnerProduct::MimeticInnerProductBase const & mimeticInnerProductBase, constitutive::SingleFluidBase const & fluid, constitutive::PermeabilityBase const & permeability, + SortedArrayView< localIndex const > const & regionFilter, real64 const & dt, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) @@ -450,7 +466,7 @@ class ElementBasedAssemblyKernelFactory { ElementBasedAssemblyKernel< NUM_FACES, IP > kernel( rankOffset, lengthTolerance, elemDofKey, faceDofKey, nodeManager, faceManager, - subRegion, fluid, permeability, dt, localMatrix, localRhs ); + subRegion, fluid, permeability, regionFilter, dt, localMatrix, localRhs ); ElementBasedAssemblyKernel< NUM_FACES, IP >::template launch< POLICY >( subRegion.size(), kernel ); } ); } ); From 7b10fc7623bdf684f3cd2be2be4609785dfe5cb9 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Fri, 30 Oct 2026 23:49:28 -0700 Subject: [PATCH 56/64] wip: make member public for the nvcc device lambda; move the PR #4139 baseline note to the top --- BASELINE_NOTES.md | 8 ++++---- .../physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/BASELINE_NOTES.md b/BASELINE_NOTES.md index e9014f709a3..49346ff0ded 100644 --- a/BASELINE_NOTES.md +++ b/BASELINE_NOTES.md @@ -5,6 +5,10 @@ This file is designed to track changes to the integrated test baselines. Any developer who updates the baseline ID in the .integrated_tests.yaml file is expected to create an entry in this file with the pull request number, date, and their justification for rebaselining. These notes should be in reverse-chronological order, and use the following time format: (YYYY-MM-DD). +PR #4139 (2026-10-30) +===================== +Add the mixed mimetic discretization: new `NumericalMethods/MixedMimetic` group in every restart file and new mixed MFD tests. + PR #4169 (2026-10-30) ===================== Fix surface generator splitting for contact mechanics case with wells @@ -35,10 +39,6 @@ PR #3996 (2026-09-20) -===================== -Add the mixed mimetic discretization: new `NumericalMethods/MixedMimetic` group in every restart file and new mixed MFD tests. - PR #3994 (2026-09-03) ===================== Introduce mimetic inner products for saddle-point operators and align face-centroid computation with exact volume integration on planar polyhedra. diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp index f7e09cea756..ea8c4e98a59 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp @@ -179,16 +179,17 @@ class SinglePhaseMixedMFD : public SinglePhaseBase DofManager const & dofManager, CRSMatrixView< real64 const, globalIndex const > const & localMatrix ); -private: - /** - * @brief Populate the boundary face pressure values from the field specifications. + * @brief Populate the boundary face mask and pressure values from the field specifications. * @param[in] time the time at which the boundary values are evaluated * @param[in] domain the domain + * @note public: it contains a device lambda, which nvcc rejects in a private member function */ void applyFacePressureBCValues( real64 const time, DomainPartition & domain ); +private: + /** * @brief Set the orienting cell E_min of every face: sigma_{E,f} = +1 if E = E_min, -1 otherwise. * @param domain the domain From 1f6031e74cdad2ef94ff85e9965a78df17cda789 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Sat, 31 Oct 2026 11:46:42 -0700 Subject: [PATCH 57/64] wip: update the integrated test baseline for PR #4139 --- .integrated_tests.yaml | 2 +- BASELINE_NOTES.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.integrated_tests.yaml b/.integrated_tests.yaml index 41fe051adaa..dd29b8f9e71 100644 --- a/.integrated_tests.yaml +++ b/.integrated_tests.yaml @@ -1,6 +1,6 @@ baselines: bucket: geosx - baseline: integratedTests/baseline_integratedTests-pr4169-17867-5d0c7de + baseline: integratedTests/baseline_integratedTests-pr4139-17884-0f6d03e allow_fail: all: '' streak: '' diff --git a/BASELINE_NOTES.md b/BASELINE_NOTES.md index 49346ff0ded..90caa852c88 100644 --- a/BASELINE_NOTES.md +++ b/BASELINE_NOTES.md @@ -5,7 +5,7 @@ This file is designed to track changes to the integrated test baselines. Any developer who updates the baseline ID in the .integrated_tests.yaml file is expected to create an entry in this file with the pull request number, date, and their justification for rebaselining. These notes should be in reverse-chronological order, and use the following time format: (YYYY-MM-DD). -PR #4139 (2026-10-30) +PR #4139 (2026-10-31) ===================== Add the mixed mimetic discretization: new `NumericalMethods/MixedMimetic` group in every restart file and new mixed MFD tests. From 623283cb976a73a57e55570d19b40f8f0ffaa8b5 Mon Sep 17 00:00:00 2001 From: DENEL Bertrand Date: Tue, 3 Nov 2026 15:51:35 -0600 Subject: [PATCH 58/64] Fixed GPU --- .../consistency/ConsistencyAdaptation.cpp | 33 +++++++++++++++++-- .../unitTests/testConsistencyAdaptation.cpp | 30 +++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp b/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp index cd686a18afb..fb2f83a0a41 100644 --- a/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp +++ b/src/coreComponents/mixedMimetic/consistency/ConsistencyAdaptation.cpp @@ -74,11 +74,15 @@ ConsistencyAdaptation::Report ConsistencyAdaptation::classify( MeshLevel & mesh, arrayView1d< integer const > const mfdFlag = subRegion.getField< mixedMimetic::mfdFlag >(); arrayView1d< integer const > const prescribed = subRegion.getField< mixedMimetic::prescribedMfdFlag >(); arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + arrayView1d< globalIndex const > const localToGlobal = subRegion.localToGlobalMap(); + mfdFlag.move( hostMemorySpace, false ); + ghostRank.move( hostMemorySpace, false ); + localToGlobal.move( hostMemorySpace, false ); for( localIndex ei = 0; ei < subRegion.size(); ++ei ) { GEOS_ERROR_IF( prescribed[ei] >= 0 && mfdFlag[ei] != prescribed[ei], GEOS_FMT( "ConsistencyAdaptation: the prescription of cell {} of {} was altered by the layers", - subRegion.localToGlobalMap()[ei], subRegion.getName() ) ); + localToGlobal[ei], subRegion.getName() ) ); report.numConsistentFinal += ( ghostRank[ei] < 0 && mfdFlag[ei] == 1 ) ? 1 : 0; } } ); @@ -186,6 +190,15 @@ std::pair< localIndex, localIndex > ConsistencyAdaptation::applyPrescription( Me string_array const & regionNames, stdVector< NeighborCommunicator > & neighbors ) { + // Initial conditions may have written prescriptions on the device. Host packing and + // the plain host loops below need coherent host data, even when there are no neighbors. + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + subRegion.getField< mixedMimetic::prescribedMfdFlag >().move( hostMemorySpace ); + } ); + FieldIdentifiers prescribedToBeSync; prescribedToBeSync.addElementFields( { mixedMimetic::prescribedMfdFlag::key() }, regionNames ); CommunicationTools::getInstance().synchronizeFields( prescribedToBeSync, mesh, neighbors, false ); @@ -197,12 +210,14 @@ std::pair< localIndex, localIndex > ConsistencyAdaptation::applyPrescription( Me ElementSubRegionBase & subRegion ) { arrayView1d< integer const > const prescribed = subRegion.getField< mixedMimetic::prescribedMfdFlag >(); + arrayView1d< globalIndex const > const localToGlobal = subRegion.localToGlobalMap(); + localToGlobal.move( hostMemorySpace, false ); for( localIndex ei = 0; ei < subRegion.size(); ++ei ) { if( prescribed[ei] < -1 || prescribed[ei] > 1 ) { GEOS_LOG_RANK( GEOS_FMT( "{}: prescribedMfdFlag = {} on cell {} is not one of -1 (free), 0 (diagonal product), 1 (consistent product)", - subRegion.getName(), prescribed[ei], subRegion.localToGlobalMap()[ei] ) ); + subRegion.getName(), prescribed[ei], localToGlobal[ei] ) ); ++numInvalid; } } @@ -220,6 +235,9 @@ std::pair< localIndex, localIndex > ConsistencyAdaptation::applyPrescription( Me arrayView1d< integer const > const prescribed = subRegion.getField< mixedMimetic::prescribedMfdFlag >(); arrayView1d< integer > const mfdFlag = subRegion.getField< mixedMimetic::mfdFlag >(); arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + ghostRank.move( hostMemorySpace, false ); + // Touch the host copy: the later device face-label kernel must see these writes. + mfdFlag.move( hostMemorySpace ); for( localIndex ei = 0; ei < subRegion.size(); ++ei ) { if( prescribed[ei] < 0 ) @@ -250,6 +268,11 @@ std::pair< localIndex, localIndex > ConsistencyAdaptation::applyDegeneracyLayer( ElementRegionManager::ElementViewAccessor< arrayView1d< real64 const > > const elemVolume = elemManager.constructArrayViewAccessor< real64, 1 >( ElementSubRegionBase::viewKeyStruct::elementVolumeString() ); + nodeToRegion.move( hostMemorySpace, false ); + nodeToSubRegion.move( hostMemorySpace, false ); + nodeToElem.move( hostMemorySpace, false ); + elemVolume.toNestedViewConst().move( hostMemorySpace, false ); + localIndex numDegenerate = 0; localIndex numPrescribedDegenerate = 0; elemManager.forElementSubRegions< CellElementSubRegion >( regionNames, @@ -264,6 +287,12 @@ std::pair< localIndex, localIndex > ConsistencyAdaptation::applyDegeneracyLayer( arrayView1d< integer const > const prescribed = subRegion.getField< mixedMimetic::prescribedMfdFlag >(); localIndex const numNodes = subRegion.numNodesPerElement(); + elemToNodes.move( hostMemorySpace, false ); + volume.move( hostMemorySpace, false ); + ghostRank.move( hostMemorySpace, false ); + indicator.move( hostMemorySpace ); + mfdFlag.move( hostMemorySpace ); + // the node star of a cell: every cell sharing a vertex with it, counted once stdVector< std::array< localIndex, 3 > > star; for( localIndex ei = 0; ei < subRegion.size(); ++ei ) diff --git a/src/coreComponents/mixedMimetic/unitTests/testConsistencyAdaptation.cpp b/src/coreComponents/mixedMimetic/unitTests/testConsistencyAdaptation.cpp index b9a5c51ee26..f0b9b6fdde8 100644 --- a/src/coreComponents/mixedMimetic/unitTests/testConsistencyAdaptation.cpp +++ b/src/coreComponents/mixedMimetic/unitTests/testConsistencyAdaptation.cpp @@ -15,6 +15,7 @@ // Source includes #include "codingUtilities/UnitTestUtilities.hpp" +#include "common/GEOS_RAJA_Interface.hpp" #include "common/MpiWrapper.hpp" #include "constitutive/ConstitutiveManager.hpp" #include "constitutive/permeability/PermeabilityBase.hpp" @@ -168,6 +169,20 @@ class ConsistencyAdaptationTest r.numDegenerate = MpiWrapper::sum( r.numDegenerate ); r.numPrescribedDegenerate = MpiWrapper::sum( r.numPrescribedDegenerate ); r.numConsistentFinal = MpiWrapper::sum( r.numConsistentFinal ); + + // The device must see the final host-side prescription and degeneracy changes, + // including on repeated classifications. Check before any host-side test reads. + RAJA::ReduceSum< parallelDeviceReduce, localIndex > numDeviceMfd( 0 ); + elemManager().forElementSubRegions< ElementSubRegionBase >( m_regionNames, [&]( localIndex const, ElementSubRegionBase & subRegion ) + { + arrayView1d< integer const > const mfdFlag = subRegion.getField< fields::mixedMimetic::mfdFlag >(); + arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + forAll< geos::parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + numDeviceMfd += ( ghostRank[ei] < 0 && mfdFlag[ei] == 1 ) ? 1 : 0; + } ); + } ); + EXPECT_EQ( MpiWrapper::sum( numDeviceMfd.get() ), r.numConsistentFinal ); return r; } @@ -179,6 +194,8 @@ class ConsistencyAdaptationTest { arrayView1d< integer const > const mfdFlag = subRegion.getField< fields::mixedMimetic::mfdFlag >(); arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + mfdFlag.move( hostMemorySpace, false ); + ghostRank.move( hostMemorySpace, false ); for( localIndex ei = 0; ei < subRegion.size(); ++ei ) { count += ( ghostRank[ei] < 0 && mfdFlag[ei] == value ) ? 1 : 0; @@ -195,6 +212,8 @@ class ConsistencyAdaptationTest { arrayView1d< real64 const > const indicator = subRegion.getField< fields::mixedMimetic::degeneracyIndicator >(); arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + indicator.move( hostMemorySpace, false ); + ghostRank.move( hostMemorySpace, false ); for( localIndex ei = 0; ei < subRegion.size(); ++ei ) { count += ( ghostRank[ei] < 0 && indicator[ei] < percent ) ? 1 : 0; @@ -215,6 +234,15 @@ class ConsistencyAdaptationTest ElementRegionManager::ElementViewAccessor< arrayView1d< integer const > > const mfdFlag = elemManager().constructArrayViewAccessor< integer, 1 >( fields::mixedMimetic::mfdFlag::key() ); + // Classification ends with a device kernel. Check its results, not stale host copies. + elemRegionList.move( hostMemorySpace, false ); + elemSubRegionList.move( hostMemorySpace, false ); + elemList.move( hostMemorySpace, false ); + label.move( hostMemorySpace, false ); + ghostRank.move( hostMemorySpace, false ); + mfdFlag.toNestedViewConst().move( hostMemorySpace, false ); + m_regionFilter.move( hostMemorySpace, false ); + localIndex count = 0; for( localIndex kf = 0; kf < faceManager.size(); ++kf ) { @@ -268,6 +296,7 @@ TEST( ConsistencyAdaptation, ConsistencyLayer_CartesianHexahedra ) EXPECT_EQ( test.countFlag( 1 ), 40 ); FaceManager const & faceManager = test.mesh().getFaceManager(); arrayView1d< integer const > const label = faceManager.getField< fields::mixedMimetic::faceStencilLabel >(); + label.move( hostMemorySpace, false ); for( localIndex kf = 0; kf < faceManager.size(); ++kf ) { EXPECT_EQ( label[kf], 0 ); @@ -288,6 +317,7 @@ TEST( ConsistencyAdaptation, ConsistencyLayer_Tetrahedra ) EXPECT_EQ( test.checkFaceLabels(), MpiWrapper::sum( [&] { arrayView1d< integer const > const ghostRank = test.mesh().getFaceManager().ghostRank(); + ghostRank.move( hostMemorySpace, false ); localIndex owned = 0; for( localIndex kf = 0; kf < ghostRank.size(); ++kf ) { From 54b35baf62bdaa6455cd6117b04059684ff435d4 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Wed, 4 Nov 2026 09:20:46 -0800 Subject: [PATCH 59/64] Retrigger CI From d1e6ea4718c06a4fd9555b95f39a7c6517405618 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Wed, 4 Nov 2026 12:45:45 -0800 Subject: [PATCH 60/64] wip: further fixes after mixed-MFD merge --- .../interfaces/hypre/hypredrive.cpp | 3 +- .../fluidFlow/SinglePhaseMixedMFD.cpp | 697 ++++++++++++++---- .../fluidFlow/SinglePhaseMixedMFD.hpp | 38 +- .../SinglePhaseMixedMFDKernels.hpp | 355 +++++---- 4 files changed, 784 insertions(+), 309 deletions(-) diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp b/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp index 89b4c3fa991..172f1ce9b56 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/hypredrive.cpp @@ -1437,7 +1437,8 @@ bool buildStrategyYaml( LinearSolverParameters const & params, // custom point markers use more blocks than dof fields: no field-name representation, skip the YAML if( strategy.m_numBlocks > LvArray::integerConversion< HYPRE_Int >( labelNames.size() ) ) { - GEOS_ERROR_IF( params.mgr.strategy != LinearSolverParameters::MGR::StrategyType::singlePhaseMixedMFD, + GEOS_ERROR_IF( params.mgr.strategy != LinearSolverParameters::MGR::StrategyType::singlePhaseMixedMFD && + params.mgr.strategy != LinearSolverParameters::MGR::StrategyType::thermalSinglePhaseMixedMFD, GEOS_FMT( "hypredrive: the MGR strategy {} has {} blocks for {} dof labels", params.mgr.strategy, strategy.m_numBlocks, labelNames.size() ) ); return false; diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index fc04500423c..8ee3df0f85b 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -24,6 +24,7 @@ #include "common/logger/Logger.hpp" #include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" #include "constitutive/permeability/PermeabilityFields.hpp" +#include "constitutive/thermalConductivity/SinglePhaseThermalConductivityBase.hpp" #include "fieldSpecification/AquiferBoundaryCondition.hpp" #include "fieldSpecification/FieldSpecificationImpl.hpp" #include "fieldSpecification/FieldSpecificationManager.hpp" @@ -33,9 +34,11 @@ #include "mixedMimetic/MixedMimeticDiscretization.hpp" #include "mixedMimetic/MixedMimeticDiscretizationManager.hpp" #include "mixedMimetic/MixedMimeticFields.hpp" +#include "mixedMimetic/MixedMimeticBoundaryConditions.hpp" #include "mixedMimetic/consistency/ConsistencyAdaptation.hpp" #include "physicsSolvers/LogLevelsInfo.hpp" #include "physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp" +#include "physicsSolvers/fluidFlow/kernels/singlePhase/ThermalSinglePhaseMixedMFDKernels.hpp" #include "physicsSolvers/fluidFlow/kernels/singlePhase/ResidualNormKernel.hpp" namespace geos @@ -45,6 +48,8 @@ using namespace dataRepository; using namespace constitutive; using namespace fields; using namespace mimeticInnerProduct; +using BoundaryType = mixedMimeticBoundary::BoundaryType; +using CellDof = thermalSinglePhaseMixedMFDKernels::CellDof; SinglePhaseMixedMFD::SinglePhaseMixedMFD( const string & name, Group * const parent ): @@ -74,6 +79,11 @@ void SinglePhaseMixedMFD::registerDataOnMesh( Group & meshBodies ) subRegion.registerField< mixedMimetic::prescribedMfdFlag >( getName() ); subRegion.registerField< mixedMimetic::consistencyIndicator >( getName() ); subRegion.registerField< mixedMimetic::degeneracyIndicator >( getName() ); + if( m_isThermal ) + { + subRegion.registerField< mixedMimetic::enthalpy >( getName() ); + subRegion.registerField< mixedMimetic::enthalpy_n >( getName() ); + } } ); // 2) Register the face data @@ -83,9 +93,12 @@ void SinglePhaseMixedMFD::registerDataOnMesh( Group & meshBodies ) faceManager.registerField< mixedMimetic::faceMassFlux >( getName() ); faceManager.registerField< mixedMimetic::faceMassFlux_n >( getName() ); - // boundary condition data + // boundary conditions of the flow operator: the user values and the per-face condition faceManager.registerField< flow::bcPressure >( getName() ); - faceManager.registerField< flow::isBoundaryFace >( getName() ); + faceManager.registerField< mixedMimetic::bcMassFlux >( getName() ); + faceManager.registerField< mixedMimetic::flowBoundaryType >( getName() ); + faceManager.registerField< mixedMimetic::flowBoundaryValue >( getName() ); + faceManager.registerField< mixedMimetic::flowBoundaryCoefficient >( getName() ); // face residual of the consistency layer faceManager.registerField< mixedMimetic::faceResidual >( getName() ); @@ -94,6 +107,23 @@ void SinglePhaseMixedMFD::registerDataOnMesh( Group & meshBodies ) faceManager.registerField< mixedMimetic::faceStencilLabel >( getName() ); faceManager.registerField< mixedMimetic::faceOrientationCell >( getName() ); faceManager.registerField< mixedMimetic::faceDofScale >( getName() ); + + if( m_isThermal ) + { + // energy module: face heat flux unknown and the thermal boundary data + faceManager.registerField< mixedMimetic::faceHeatFlux >( getName() ); + faceManager.registerField< mixedMimetic::faceHeatFlux_n >( getName() ); + faceManager.registerField< flow::bcTemperature >( getName() ); + faceManager.registerField< mixedMimetic::bcEnthalpy >( getName() ); + faceManager.registerField< mixedMimetic::bcHeatFlux >( getName() ); + faceManager.registerField< mixedMimetic::bcHeatTransferCoefficient >( getName() ); + faceManager.registerField< mixedMimetic::bcAmbientTemperature >( getName() ); + faceManager.registerField< mixedMimetic::heatBoundaryType >( getName() ); + faceManager.registerField< mixedMimetic::heatBoundaryValue >( getName() ); + faceManager.registerField< mixedMimetic::heatBoundaryCoefficient >( getName() ); + faceManager.registerField< mixedMimetic::isEnthalpyBcFace >( getName() ); + faceManager.registerField< mixedMimetic::faceHeatDofScale >( getName() ); + } } } ); } @@ -102,9 +132,10 @@ void SinglePhaseMixedMFD::initializePreSubGroups() { SinglePhaseBase::initializePreSubGroups(); - GEOS_THROW_IF( m_isThermal, - "The thermal option is not supported by SinglePhaseMixedMFD", - InputError, getDataContext() ); + if( m_isThermal ) + { + m_linearSolverParameters.get().mgr.strategy = LinearSolverParameters::MGR::StrategyType::thermalSinglePhaseMixedMFD; + } DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); NumericalMethodsManager const & numericalMethodManager = domain.getNumericalMethodManager(); @@ -148,11 +179,100 @@ void SinglePhaseMixedMFD::initializePostInitialConditionsPreSubGroups() } ); } ); + if( m_isThermal ) + { + initializeEnthalpy( domain ); + } + computeFaceOrientation( domain ); classifyCells( domain ); } +void SinglePhaseMixedMFD::markBoundaryFaces( real64 const time, + DomainPartition & domain ) +{ + FieldSpecificationManager & fsManager = FieldSpecificationManager::getInstance(); + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & ) + { + FaceManager & faceManager = mesh.getFaceManager(); + arrayView2d< localIndex const > const elemRegionList = faceManager.elementRegionList(); + arrayView2d< localIndex const > const elemSubRegionList = faceManager.elementSubRegionList(); + arrayView2d< localIndex const > const elemList = faceManager.elementList(); + SortedArrayView< localIndex const > const regionFilter = m_regionFilter.toViewConst(); + + // the field specifications active at this time define the type of the condition on a face; a boundary face + // without specification has the homogeneous Neumann condition (impervious, adiabatic), an interior face has none + auto const initialize = [&]( arrayView1d< integer > const & type ) + { + forAll< parallelDevicePolicy<> >( faceManager.size(), [=] GEOS_HOST_DEVICE ( localIndex const kf ) + { + integer numTargetCells = 0; + for( integer k = 0; k < elemRegionList.size( 1 ); ++k ) + { + localIndex const er = elemRegionList[kf][k]; + numTargetCells += ( er >= 0 && elemSubRegionList[kf][k] >= 0 && elemList[kf][k] >= 0 && regionFilter.contains( er ) ) ? 1 : 0; + } + type[kf] = ( numTargetCells == 1 ) ? BoundaryType::neumann : BoundaryType::interior; + } ); + }; + auto const mark = [&]( string const & key, arrayView1d< integer > const & flag, integer const value ) + { + fsManager.apply< FaceManager >( time, mesh, key, + [&] ( FieldSpecification const &, string const &, + SortedArrayView< localIndex const > const & targetSet, FaceManager &, string const & ) + { + forAll< parallelDevicePolicy<> >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const a ) + { + flag[targetSet[a]] = value; + } ); + } ); + }; + + arrayView1d< integer > const flowType = faceManager.getField< mixedMimetic::flowBoundaryType >(); + initialize( flowType ); + mark( flow::bcPressure::key(), flowType, BoundaryType::dirichlet ); + mark( mixedMimetic::bcMassFlux::key(), flowType, BoundaryType::neumann ); + + if( m_isThermal ) + { + arrayView1d< integer > const heatType = faceManager.getField< mixedMimetic::heatBoundaryType >(); + initialize( heatType ); + mark( flow::bcTemperature::key(), heatType, BoundaryType::dirichlet ); + mark( mixedMimetic::bcHeatFlux::key(), heatType, BoundaryType::neumann ); + mark( mixedMimetic::bcHeatTransferCoefficient::key(), heatType, BoundaryType::robin ); + mark( mixedMimetic::bcEnthalpy::key(), faceManager.getField< mixedMimetic::isEnthalpyBcFace >(), 1 ); + } + } ); +} + +void SinglePhaseMixedMFD::initializeEnthalpy( DomainPartition & domain ) +{ + // h = h_eos( p, T ) at the initial state + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + SingleFluidBase const & fluid = getConstitutiveModel< SingleFluidBase >( subRegion, fluidName ); + arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const eosEnthalpy = fluid.enthalpy(); + arrayView1d< real64 > const enthalpy = subRegion.getField< mixedMimetic::enthalpy >(); + arrayView1d< real64 > const enthalpy_n = subRegion.getField< mixedMimetic::enthalpy_n >(); + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + enthalpy[ei] = eosEnthalpy[ei][0]; + enthalpy_n[ei] = eosEnthalpy[ei][0]; + } ); + } ); + } ); +} + void SinglePhaseMixedMFD::computeFaceOrientation( DomainPartition & domain ) { GEOS_MARK_FUNCTION; @@ -262,7 +382,7 @@ void SinglePhaseMixedMFD::implicitStepSetup( real64 const & time_n, // setup the face fields forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, MeshLevel & mesh, - string_array const & ) + string_array const & regionNames ) { FaceManager & faceManager = mesh.getFaceManager(); @@ -271,6 +391,19 @@ void SinglePhaseMixedMFD::implicitStepSetup( real64 const & time_n, arrayView1d< real64 > const & faceFlux_n = faceManager.getField< mixedMimetic::faceMassFlux_n >(); faceFlux_n.setValues< parallelDevicePolicy<> >( faceFlux ); + + if( m_isThermal ) + { + faceManager.getField< mixedMimetic::faceHeatFlux_n >().setValues< parallelDevicePolicy<> >( + faceManager.getField< mixedMimetic::faceHeatFlux >().toViewConst() ); + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + subRegion.getField< mixedMimetic::enthalpy_n >().setValues< parallelDevicePolicy<> >( + subRegion.getField< mixedMimetic::enthalpy >().toViewConst() ); + } ); + } } ); } @@ -283,8 +416,8 @@ void SinglePhaseMixedMFD::assembleSystem( real64 const time_n, { GEOS_MARK_FUNCTION; - // evaluate the boundary face pressure values used in the constitutive rows at the end of the attempted step - applyFacePressureBCValues( time_n + dt, domain ); + // evaluate the boundary face values used in the constitutive rows at the end of the attempted step + applyFaceBoundaryValues( time_n + dt, domain ); SinglePhaseBase::assembleSystem( time_n, dt, domain, dofManager, localMatrix, localRhs ); } @@ -298,55 +431,93 @@ char const faceBcLogMessage[] = "\nNote that if this number is equal to zero, the boundary condition will not be applied on this face set."; } -void SinglePhaseMixedMFD::applyFacePressureBCValues( real64 const time, - DomainPartition & domain ) +void SinglePhaseMixedMFD::applyFaceBoundaryValues( real64 const time, + DomainPartition & domain ) { GEOS_MARK_FUNCTION; FieldSpecificationManager & fsManager = FieldSpecificationManager::getInstance(); + // the type of the condition on each face follows the specifications active at this time + markBoundaryFaces( time, domain ); + + stdVector< string > keys = { flow::bcPressure::key(), mixedMimetic::bcMassFlux::key() }; + if( m_isThermal ) + { + for( string const & key : { flow::bcTemperature::key(), mixedMimetic::bcHeatFlux::key(), mixedMimetic::bcHeatTransferCoefficient::key(), + mixedMimetic::bcAmbientTemperature::key(), mixedMimetic::bcEnthalpy::key() } ) + { + keys.push_back( key ); + } + } + this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, MeshLevel & mesh, string_array const & ) { - // flag the faces on which a pressure boundary condition is imposed at this time: - // all the remaining domain-boundary faces are treated as no-flow faces - arrayView1d< integer > const isPresBcFace = mesh.getFaceManager().getField< flow::isBoundaryFace >(); - isPresBcFace.zero(); - - fsManager.apply< FaceManager >( time, - mesh, - flow::bcPressure::key(), - [&] ( FieldSpecification const & fs, - string const & setName, - SortedArrayView< localIndex const > const & targetSet, - FaceManager & targetGroup, - string const & ) - { - forAll< parallelDevicePolicy<> >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const a ) + // 1) the user values of the face specifications + for( string const & key : keys ) + { + fsManager.apply< FaceManager >( time, + mesh, + key, + [&] ( FieldSpecification const & fs, + string const & setName, + SortedArrayView< localIndex const > const & targetSet, + FaceManager & targetGroup, + string const & ) { - isPresBcFace[targetSet[a]] = 1; + // provide some logging at the first nonlinear iteration + if( m_nonlinearSolverParameters.m_numNewtonIterations == 0 ) + { + globalIndex const numTargetFaces = MpiWrapper::sum< globalIndex >( targetSet.size() ); + GEOS_LOG_LEVEL_RANK_0_ON_GROUP( logInfo::BoundaryConditions, + GEOS_FMT_RUNTIME( faceBcLogMessage, + this->getName(), time, fs.getCatalogName(), fs.getName(), + setName, targetGroup.getName(), numTargetFaces ), + fs ); + } + FieldSpecificationImpl::applyFieldValue< FieldSpecificationEqual, + parallelDevicePolicy<> >( fs, targetSet, time, targetGroup, key ); } ); + } - // provide some logging at the first nonlinear iteration - if( m_nonlinearSolverParameters.m_numNewtonIterations == 0 ) + // 2) the condition of each operator on each face: (g, alpha) from the user values according to the type + FaceManager & faceManager = mesh.getFaceManager(); + auto const fill = [&]( arrayView1d< integer const > const & type, arrayView1d< real64 > const & value, arrayView1d< real64 > const & coefficient, + arrayView1d< real64 const > const & dirichletValue, arrayView1d< real64 const > const & neumannValue, + arrayView1d< real64 const > const & robinValue, arrayView1d< real64 const > const & robinCoefficient ) + { + forAll< parallelDevicePolicy<> >( faceManager.size(), [=] GEOS_HOST_DEVICE ( localIndex const kf ) { - globalIndex const numTargetFaces = MpiWrapper::sum< globalIndex >( targetSet.size() ); - GEOS_LOG_LEVEL_RANK_0_ON_GROUP( logInfo::BoundaryConditions, - GEOS_FMT_RUNTIME( faceBcLogMessage, - this->getName(), time, fs.getCatalogName(), fs.getName(), - setName, targetGroup.getName(), numTargetFaces ), - fs ); - } - - // populate the boundary face pressure values: they enter the residual directly during assembly - FieldSpecificationImpl::applyFieldValue< FieldSpecificationEqual, - parallelDevicePolicy<> >( fs, - targetSet, - time, - targetGroup, - flow::bcPressure::key() ); - } ); + value[kf] = ( type[kf] == BoundaryType::dirichlet ) ? dirichletValue[kf] + : ( type[kf] == BoundaryType::neumann ) ? neumannValue[kf] + : ( type[kf] == BoundaryType::robin ) ? robinValue[kf] : 0.0; + coefficient[kf] = ( type[kf] == BoundaryType::robin ) ? robinCoefficient[kf] : 0.0; + } ); + }; + // TODO: Robin data of the flow operator from the Carter-Tracy aquifer. Over a time step the influx rate is + // q = a - b ( p_f - p_f^n ), with a, b functions of the dimensionless time and of the cumulative influx W^n. + // With f_A the fraction of the aquifer area on the face, sigma m_f = - rho f_A q = alpha |f| ( p_f - g ), + // alpha |f| = rho f_A b, g = p_f^n + a / b. ( g, alpha ) is evaluated once per step; at convergence + // W^{n+1} = W^n + dt q( p_f^{n+1} ). Until then no face of the flow operator has the Robin type + arrayView1d< real64 const > const zero = faceManager.getField< mixedMimetic::flowBoundaryCoefficient >(); + fill( faceManager.getField< mixedMimetic::flowBoundaryType >(), + faceManager.getField< mixedMimetic::flowBoundaryValue >(), + faceManager.getField< mixedMimetic::flowBoundaryCoefficient >(), + faceManager.getField< flow::bcPressure >(), + faceManager.getField< mixedMimetic::bcMassFlux >(), + zero, zero ); + if( m_isThermal ) + { + fill( faceManager.getField< mixedMimetic::heatBoundaryType >(), + faceManager.getField< mixedMimetic::heatBoundaryValue >(), + faceManager.getField< mixedMimetic::heatBoundaryCoefficient >(), + faceManager.getField< flow::bcTemperature >(), + faceManager.getField< mixedMimetic::bcHeatFlux >(), + faceManager.getField< mixedMimetic::bcAmbientTemperature >(), + faceManager.getField< mixedMimetic::bcHeatTransferCoefficient >() ); + } } ); } @@ -433,7 +604,23 @@ void SinglePhaseMixedMFD::computeMgrPointMarkers( DomainPartition const & domain markers[faceDofNumber[kf] - rankOffset] = faceStencilLabel[kf]; } ); - // cell-pressure dofs: label 2 + // heat-flux dofs: labels 5 (condensed) and 6 (saddle point) + if( m_isThermal ) + { + arrayView1d< globalIndex const > const faceHeatDofNumber = + faceManager.getReference< array1d< globalIndex > >( dofManager.getKey( mixedMimetic::faceHeatFlux::key() ) ); + forAll< parallelHostPolicy >( faceManager.size(), [=]( localIndex const kf ) + { + if( faceGhostRank[kf] >= 0 || faceHeatDofNumber[kf] < 0 ) + { + return; + } + markers[faceHeatDofNumber[kf] - rankOffset] = 5 + faceStencilLabel[kf]; + } ); + } + + // cell dofs: pressure 2, temperature 3, enthalpy 4 + integer const numCellDofs = numCellDofComponents(); elemManager.forElementSubRegions< ElementSubRegionBase >( regionNames, [&]( localIndex const, ElementSubRegionBase const & subRegion ) @@ -441,12 +628,14 @@ void SinglePhaseMixedMFD::computeMgrPointMarkers( DomainPartition const & domain arrayView1d< globalIndex const > const elemDofNumber = subRegion.getReference< array1d< globalIndex > >( elemDofKey ); arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); - forAll< parallelHostPolicy >( subRegion.size(), [=]( localIndex const ei ) { if( elemGhostRank[ei] < 0 ) { - markers[elemDofNumber[ei] - rankOffset] = 2; + for( integer c = 0; c < numCellDofs; ++c ) + { + markers[elemDofNumber[ei] - rankOffset + c] = 2 + c; + } } } ); } ); @@ -467,22 +656,40 @@ void SinglePhaseMixedMFD::setupDofs( DomainPartition const & GEOS_UNUSED_PARAM( mixedMimetic::faceMassFlux::key(), DofManager::Connector::Elem ); - // cell pressure unknowns: the TPFA-face condensation writes two-point stencil - // entries directly into the mass conservation rows, so cell-to-cell coupling - // through the faces is required + // face heat flux unknowns, with the same structure as the mass fluxes + if( m_isThermal ) + { + dofManager.addField( mixedMimetic::faceHeatFlux::key(), + FieldLocation::Face, + 1, + getMeshTargets() ); + dofManager.addCoupling( mixedMimetic::faceHeatFlux::key(), + mixedMimetic::faceHeatFlux::key(), + DofManager::Connector::Elem ); + } + + // cell unknowns (pressure, and temperature and enthalpy when thermal): the TPFA-face + // condensation writes two-point stencil entries directly into the conservation rows, + // so cell-to-cell coupling through the faces is required dofManager.addField( viewKeyStruct::elemDofFieldString(), FieldLocation::Elem, - 1, + numCellDofComponents(), getMeshTargets() ); dofManager.addCoupling( viewKeyStruct::elemDofFieldString(), viewKeyStruct::elemDofFieldString(), DofManager::Connector::Face ); - // coupling between the face fluxes and the cell pressures + // coupling between the face fluxes and the cell unknowns dofManager.addCoupling( mixedMimetic::faceMassFlux::key(), viewKeyStruct::elemDofFieldString(), DofManager::Connector::Elem ); + if( m_isThermal ) + { + dofManager.addCoupling( mixedMimetic::faceHeatFlux::key(), + viewKeyStruct::elemDofFieldString(), + DofManager::Connector::Elem ); + } } void SinglePhaseMixedMFD::assembleFluxTerms( real64 const dt, @@ -522,40 +729,93 @@ void SinglePhaseMixedMFD::assembleFluxTerms( real64 const dt, string const & permName = subRegion.getReference< string >( viewKeyStruct::permeabilityNamesString() ); PermeabilityBase const & permeability = getConstitutiveModel< PermeabilityBase >( subRegion, permName ); + if( m_isThermal ) + { + string const & condName = subRegion.getReference< string >( viewKeyStruct::thermalConductivityNamesString() ); + SinglePhaseThermalConductivityBase const & conductivity = getConstitutiveModel< SinglePhaseThermalConductivityBase >( subRegion, condName ); + string const faceHeatDofKey = dofManager.getKey( mixedMimetic::faceHeatFlux::key() ); + + thermalSinglePhaseMixedMFDKernels:: + ElementBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(), + lengthTolerance, + elemDofKey, + faceDofKey, + faceHeatDofKey, + nodeManager, + faceManager, + mesh.getElemManager(), + subRegion, + mimeticInnerProductBase, + fluid, + permeability, + conductivity, + m_regionFilter.toViewConst(), + dt, + localMatrix, + localRhs ); + + // cell closure of the enthalpy unknown + thermalSinglePhaseMixedMFDKernels:: + EnthalpyClosureKernel::launch< parallelDevicePolicy<> >( dofManager.rankOffset(), elemDofKey, subRegion, fluid, localMatrix, localRhs ); + } + else + { + singlePhaseMixedMFDKernels:: + ElementBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(), + lengthTolerance, + elemDofKey, + faceDofKey, + nodeManager, + faceManager, + subRegion, + mimeticInnerProductBase, + fluid, + permeability, + dt, + localMatrix, + localRhs ); + } + } ); + + // condensed (label-0) faces: two-point flux contributions to the conservation + // rows and one-way closure rows, assembled in a single face-based sweep + if( m_isThermal ) + { + thermalSinglePhaseMixedMFDKernels:: + TpfaCondensedFluxKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(), + lengthTolerance, + elemDofKey, + faceDofKey, + dofManager.getKey( mixedMimetic::faceHeatFlux::key() ), + getName(), + nodeManager, + faceManager, + mesh.getElemManager(), + m_regionFilter.toViewConst(), + dt, + localMatrix, + localRhs ); + } + else + { singlePhaseMixedMFDKernels:: - ElementBasedAssemblyKernelFactory:: + TpfaCondensedFluxKernelFactory:: createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(), lengthTolerance, elemDofKey, faceDofKey, + getName(), nodeManager, faceManager, - subRegion, - mimeticInnerProductBase, - fluid, - permeability, + mesh.getElemManager(), m_regionFilter.toViewConst(), dt, localMatrix, localRhs ); - } ); - - // condensed (label-0) faces: two-point flux contributions to the mass conservation - // rows and one-way closure rows, assembled in a single face-based sweep - singlePhaseMixedMFDKernels:: - TpfaCondensedFluxKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( dofManager.rankOffset(), - lengthTolerance, - elemDofKey, - faceDofKey, - getName(), - nodeManager, - faceManager, - mesh.getElemManager(), - m_regionFilter.toViewConst(), - dt, - localMatrix, - localRhs ); + } } ); } @@ -600,17 +860,92 @@ void SinglePhaseMixedMFD::applyBoundaryConditions( real64 const time_n, // only the cell-centered boundary conditions (Dirichlet cells, source fluxes) remain to be applied here SinglePhaseBase::applyBoundaryConditions( time_n, dt, domain, dofManager, localMatrix, localRhs ); + if( m_isThermal ) + { + removeEnthalpyReference( domain, dofManager, localMatrix, localRhs ); + } + // the matrix is final: the residual-norm weights are a function of it and are recomputed with it computeResidualWeights( domain, dofManager, localMatrix.toViewConst() ); } +void SinglePhaseMixedMFD::removeEnthalpyReference( DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const +{ + GEOS_MARK_FUNCTION; + + // energy row <- energy row - h_K * mass row. A shift h_0 of the enthalpy reference adds h_0 * (mass row) to the + // energy row: the reduced system is invariant under that shift, and the Newton update is unchanged + globalIndex const rankOffset = dofManager.rankOffset(); + string const elemDofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + string_array const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase const & subRegion ) + { + arrayView1d< globalIndex const > const elemDofNumber = subRegion.getReference< array1d< globalIndex > >( elemDofKey ); + arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); + arrayView1d< real64 const > const enthalpy = subRegion.getField< mixedMimetic::enthalpy >(); + + RAJA::ReduceSum< parallelDeviceReduce, localIndex > numMissing( 0 ); + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + if( elemGhostRank[ei] >= 0 ) + { + return; + } + localIndex const massRow = elemDofNumber[ei] - rankOffset + CellDof::pressure; + localIndex const energyRow = elemDofNumber[ei] - rankOffset + CellDof::temperature; + real64 const h = enthalpy[ei]; + + arraySlice1d< globalIndex const > const massCols = localMatrix.getColumns( massRow ); + arraySlice1d< real64 const > const massVals = localMatrix.getEntries( massRow ); + arraySlice1d< globalIndex const > const energyCols = localMatrix.getColumns( energyRow ); + arraySlice1d< real64 > const energyVals = localMatrix.getEntries( energyRow ); + localIndex const numEnergy = localMatrix.numNonZeros( energyRow ); + + // both rows are sorted by column + localIndex j = 0; + for( localIndex k = 0; k < localMatrix.numNonZeros( massRow ); ++k ) + { + while( j < numEnergy && energyCols[j] < massCols[k] ) + { + ++j; + } + if( j < numEnergy && energyCols[j] == massCols[k] ) + { + energyVals[j] -= h * massVals[k]; + } + else if( LvArray::math::abs( massVals[k] ) > 0.0 ) + { + numMissing += 1; + } + } + localRhs[energyRow] -= h * localRhs[massRow]; + } ); + GEOS_ERROR_IF( numMissing.get() > 0, getDataContext() << ": the energy rows do not contain the pattern of the mass rows" ); + } ); + } ); +} + void SinglePhaseMixedMFD::computeGhostDofs( DomainPartition const & domain, DofManager const & dofManager ) { GEOS_MARK_FUNCTION; string const elemDofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); - string const faceDofKey = dofManager.getKey( mixedMimetic::faceMassFlux::key() ); + integer const numCellDofs = numCellDofComponents(); + stdVector< string > faceDofKeys = { dofManager.getKey( mixedMimetic::faceMassFlux::key() ) }; + if( m_isThermal ) + { + faceDofKeys.push_back( dofManager.getKey( mixedMimetic::faceHeatFlux::key() ) ); + } array1d< globalIndex > dofs; forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, @@ -618,13 +953,16 @@ void SinglePhaseMixedMFD::computeGhostDofs( DomainPartition const & domain, string_array const & regionNames ) { FaceManager const & faceManager = mesh.getFaceManager(); - arrayView1d< globalIndex const > const faceDofNumber = faceManager.getReference< array1d< globalIndex > >( faceDofKey ); arrayView1d< integer const > const faceGhostRank = faceManager.ghostRank(); - for( localIndex kf = 0; kf < faceManager.size(); ++kf ) + for( string const & key : faceDofKeys ) { - if( faceGhostRank[kf] >= 0 && faceDofNumber[kf] >= 0 ) + arrayView1d< globalIndex const > const faceDofNumber = faceManager.getReference< array1d< globalIndex > >( key ); + for( localIndex kf = 0; kf < faceManager.size(); ++kf ) { - dofs.emplace_back( faceDofNumber[kf] ); + if( faceGhostRank[kf] >= 0 && faceDofNumber[kf] >= 0 ) + { + dofs.emplace_back( faceDofNumber[kf] ); + } } } mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, @@ -637,7 +975,10 @@ void SinglePhaseMixedMFD::computeGhostDofs( DomainPartition const & domain, { if( elemGhostRank[ei] >= 0 && elemDofNumber[ei] >= 0 ) { - dofs.emplace_back( elemDofNumber[ei] ); + for( integer c = 0; c < numCellDofs; ++c ) + { + dofs.emplace_back( elemDofNumber[ei] + c ); + } } } } ); @@ -667,7 +1008,20 @@ void SinglePhaseMixedMFD::computeResidualWeights( DomainPartition & domain, globalIndex const rankOffset = dofManager.rankOffset(); string const elemDofKey = dofManager.getKey( viewKeyStruct::elemDofFieldString() ); - string const faceDofKey = dofManager.getKey( mixedMimetic::faceMassFlux::key() ); + integer const numCellDofs = numCellDofComponents(); + + // the cell scales |p_n|, and |T_n|, |h_n| when thermal; the face fields with the cell scale + // their rows are measured with and the face field storing their scale + stdVector< string > cellScaleKeys = { flow::pressure_n::key() }; + struct FaceField { string dofKey; string cellScaleKey; string scaleFieldKey; }; + stdVector< FaceField > faceFields = + { { dofManager.getKey( mixedMimetic::faceMassFlux::key() ), flow::pressure_n::key(), mixedMimetic::faceDofScale::key() } }; + if( m_isThermal ) + { + cellScaleKeys.push_back( flow::temperature_n::key() ); + cellScaleKeys.push_back( mixedMimetic::enthalpy_n::key() ); + faceFields.push_back( { dofManager.getKey( mixedMimetic::faceHeatFlux::key() ), flow::temperature_n::key(), mixedMimetic::faceHeatDofScale::key() } ); + } forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, MeshLevel & mesh, @@ -676,7 +1030,7 @@ void SinglePhaseMixedMFD::computeResidualWeights( DomainPartition & domain, ElementRegionManager & elemManager = mesh.getElemManager(); FaceManager & faceManager = mesh.getFaceManager(); - // s_p = |p_n| for the cell-pressure dofs + // s of the cell dofs elemManager.forElementSubRegions< ElementSubRegionBase >( regionNames, [&]( localIndex const, ElementSubRegionBase const & subRegion ) @@ -684,69 +1038,72 @@ void SinglePhaseMixedMFD::computeResidualWeights( DomainPartition & domain, arrayView1d< globalIndex const > const elemDofNumber = subRegion.getReference< array1d< globalIndex > >( elemDofKey ); arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); - arrayView1d< real64 const > const presN = subRegion.getField< fields::flow::pressure_n >(); - - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + for( integer c = 0; c < numCellDofs; ++c ) { - if( elemGhostRank[ei] < 0 ) + arrayView1d< real64 const > const valueN = subRegion.getReference< array1d< real64 > >( cellScaleKeys[c] ); + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) { - dofScale[elemDofNumber[ei] - rankOffset] = LvArray::math::abs( presN[ei] ); - } - } ); + if( elemGhostRank[ei] < 0 ) + { + dofScale[elemDofNumber[ei] - rankOffset + c] = LvArray::math::abs( valueN[ei] ); + } + } ); + } } ); - // s_m = p_scale / |M_ff| for the face-flux dofs, p_scale the mean adjacent |p_n| - ElementRegionManager::ElementViewAccessor< arrayView1d< real64 const > > const presNAccessor = - elemManager.constructArrayViewAccessor< real64, 1 >( fields::flow::pressure_n::key() ); - ElementRegionManager::ElementViewConst< arrayView1d< real64 const > > const presN = presNAccessor.toNestedViewConst(); - - arrayView1d< globalIndex const > const faceDofNumber = - faceManager.getReference< array1d< globalIndex > >( faceDofKey ); + // s_f = x_scale / |M_ff| for the face dofs, x_scale the mean adjacent cell scale arrayView1d< integer const > const faceGhostRank = faceManager.ghostRank(); arrayView2d< localIndex const > const elemRegionList = faceManager.elementRegionList(); arrayView2d< localIndex const > const elemSubRegionList = faceManager.elementSubRegionList(); arrayView2d< localIndex const > const elemList = faceManager.elementList(); SortedArrayView< localIndex const > const regionFilter = m_regionFilter.toViewConst(); - arrayView1d< real64 > const faceScale = faceManager.getField< mixedMimetic::faceDofScale >(); - - forAll< parallelDevicePolicy<> >( faceManager.size(), [=] GEOS_HOST_DEVICE ( localIndex const kf ) + for( FaceField const & ff : faceFields ) { - if( faceGhostRank[kf] >= 0 || faceDofNumber[kf] < 0 ) - { - return; - } - real64 presSum = 0.0; - integer elemCounter = 0; - for( integer k = 0; k < elemRegionList.size( 1 ); ++k ) + ElementRegionManager::ElementViewAccessor< arrayView1d< real64 const > > const valueNAccessor = + elemManager.constructArrayViewAccessor< real64, 1 >( ff.cellScaleKey ); + ElementRegionManager::ElementViewConst< arrayView1d< real64 const > > const valueN = valueNAccessor.toNestedViewConst(); + arrayView1d< globalIndex const > const faceDofNumber = faceManager.getReference< array1d< globalIndex > >( ff.dofKey ); + arrayView1d< real64 > const faceScale = faceManager.getReference< array1d< real64 > >( ff.scaleFieldKey ); + + forAll< parallelDevicePolicy<> >( faceManager.size(), [=] GEOS_HOST_DEVICE ( localIndex const kf ) { - localIndex const er = elemRegionList[kf][k]; - localIndex const esr = elemSubRegionList[kf][k]; - localIndex const ei = elemList[kf][k]; - if( er >= 0 && esr >= 0 && ei >= 0 && regionFilter.contains( er ) ) + if( faceGhostRank[kf] >= 0 || faceDofNumber[kf] < 0 ) { - presSum += LvArray::math::abs( presN[er][esr][ei] ); - elemCounter++; + return; } - } - real64 const pScale = elemCounter > 0 ? presSum / elemCounter : 0.0; - localIndex const localRow = faceDofNumber[kf] - rankOffset; - real64 diag = 0.0; - arraySlice1d< globalIndex const > const columns = localMatrix.getColumns( localRow ); - arraySlice1d< real64 const > const entries = localMatrix.getEntries( localRow ); - for( localIndex k = 0; k < localMatrix.numNonZeros( localRow ); ++k ) - { - if( columns[k] == faceDofNumber[kf] ) + real64 sum = 0.0; + integer elemCounter = 0; + for( integer k = 0; k < elemRegionList.size( 1 ); ++k ) { - diag = LvArray::math::abs( entries[k] ); + localIndex const er = elemRegionList[kf][k]; + localIndex const esr = elemSubRegionList[kf][k]; + localIndex const ei = elemList[kf][k]; + if( er >= 0 && esr >= 0 && ei >= 0 && regionFilter.contains( er ) ) + { + sum += LvArray::math::abs( valueN[er][esr][ei] ); + elemCounter++; + } } - } - dofScale[localRow] = diag > 0.0 ? pScale / diag : pScale; - faceScale[kf] = dofScale[localRow]; - } ); + real64 const xScale = elemCounter > 0 ? sum / elemCounter : 0.0; + localIndex const localRow = faceDofNumber[kf] - rankOffset; + real64 diag = 0.0; + arraySlice1d< globalIndex const > const columns = localMatrix.getColumns( localRow ); + arraySlice1d< real64 const > const entries = localMatrix.getEntries( localRow ); + for( localIndex k = 0; k < localMatrix.numNonZeros( localRow ); ++k ) + { + if( columns[k] == faceDofNumber[kf] ) + { + diag = LvArray::math::abs( entries[k] ); + } + } + dofScale[localRow] = diag > 0.0 ? xScale / diag : xScale; + faceScale[kf] = dofScale[localRow]; + } ); + } } ); // s_j of the ghost dofs: a cell may own none of its faces, so the face scales come from their owners; - // the pressure scale of a ghost cell is its synchronized |p_n| + // the scale of a ghost cell dof is its synchronized previous value m_ghostDofScale.resize( m_ghostDofs.size() ); arrayView1d< real64 > const ghostDofScale = m_ghostDofScale.toView(); ghostDofScale.zero(); @@ -757,21 +1114,27 @@ void SinglePhaseMixedMFD::computeResidualWeights( DomainPartition & domain, string_array const & regionNames ) { FieldIdentifiers fieldsToBeSync; - fieldsToBeSync.addFields( FieldLocation::Face, { mixedMimetic::faceDofScale::key() } ); + for( FaceField const & ff : faceFields ) + { + fieldsToBeSync.addFields( FieldLocation::Face, { ff.scaleFieldKey } ); + } CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), true ); FaceManager const & faceManager = mesh.getFaceManager(); - arrayView1d< globalIndex const > const faceDofNumber = faceManager.getReference< array1d< globalIndex > >( faceDofKey ); arrayView1d< integer const > const faceGhostRank = faceManager.ghostRank(); - arrayView1d< real64 const > const faceScale = faceManager.getField< mixedMimetic::faceDofScale >(); - forAll< parallelDevicePolicy<> >( faceManager.size(), [=] GEOS_HOST_DEVICE ( localIndex const kf ) + for( FaceField const & ff : faceFields ) { - if( faceGhostRank[kf] >= 0 && faceDofNumber[kf] >= 0 ) + arrayView1d< globalIndex const > const faceDofNumber = faceManager.getReference< array1d< globalIndex > >( ff.dofKey ); + arrayView1d< real64 const > const faceScale = faceManager.getReference< array1d< real64 > >( ff.scaleFieldKey ); + forAll< parallelDevicePolicy<> >( faceManager.size(), [=] GEOS_HOST_DEVICE ( localIndex const kf ) { - localIndex const slot = LvArray::sortedArrayManipulation::find( ghostDofs.begin(), ghostDofs.size(), faceDofNumber[kf] ); - ghostDofScale[slot] = faceScale[kf]; - } - } ); + if( faceGhostRank[kf] >= 0 && faceDofNumber[kf] >= 0 ) + { + localIndex const slot = LvArray::sortedArrayManipulation::find( ghostDofs.begin(), ghostDofs.size(), faceDofNumber[kf] ); + ghostDofScale[slot] = faceScale[kf]; + } + } ); + } mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, [&]( localIndex const, @@ -779,15 +1142,18 @@ void SinglePhaseMixedMFD::computeResidualWeights( DomainPartition & domain, { arrayView1d< globalIndex const > const elemDofNumber = subRegion.getReference< array1d< globalIndex > >( elemDofKey ); arrayView1d< integer const > const elemGhostRank = subRegion.ghostRank(); - arrayView1d< real64 const > const presN = subRegion.getField< fields::flow::pressure_n >(); - forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + for( integer c = 0; c < numCellDofs; ++c ) { - if( elemGhostRank[ei] >= 0 && elemDofNumber[ei] >= 0 ) + arrayView1d< real64 const > const valueN = subRegion.getReference< array1d< real64 > >( cellScaleKeys[c] ); + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) { - localIndex const slot = LvArray::sortedArrayManipulation::find( ghostDofs.begin(), ghostDofs.size(), elemDofNumber[ei] ); - ghostDofScale[slot] = LvArray::math::abs( presN[ei] ); - } - } ); + if( elemGhostRank[ei] >= 0 && elemDofNumber[ei] >= 0 ) + { + localIndex const slot = LvArray::sortedArrayManipulation::find( ghostDofs.begin(), ghostDofs.size(), elemDofNumber[ei] + c ); + ghostDofScale[slot] = LvArray::math::abs( valueN[ei] ); + } + } ); + } } ); } ); @@ -826,6 +1192,7 @@ void SinglePhaseMixedMFD::applyAquiferBC( real64 const time, arrayView1d< real64 > const & localRhs ) const { GEOS_MARK_FUNCTION; + // no contribution: the aquifer is a Robin condition of the flow operator (see applyFaceBoundaryValues) GEOS_UNUSED_VAR( time, dt, dofManager, domain, localMatrix, localRhs ); } @@ -834,6 +1201,7 @@ void SinglePhaseMixedMFD::saveAquiferConvergedState( real64 const & time, DomainPartition & domain ) { GEOS_MARK_FUNCTION; + // TODO: W^{n+1} = W^n + dt q( p_f^{n+1} ) on the faces of each aquifer (see applyFaceBoundaryValues) GEOS_UNUSED_VAR( time, dt, domain ); } @@ -897,7 +1265,8 @@ void SinglePhaseMixedMFD::applySystemSolution( DofManager const & dofManager, dofManager.addVectorToField( localSolution, viewKeyStruct::elemDofFieldString(), flow::pressure::key(), - scalingFactor ); + scalingFactor, + DofManager::CompMask( numCellDofComponents(), CellDof::pressure, CellDof::pressure + 1 ) ); // 2. apply the face-based update @@ -906,6 +1275,24 @@ void SinglePhaseMixedMFD::applySystemSolution( DofManager const & dofManager, mixedMimetic::faceMassFlux::key(), scalingFactor ); + if( m_isThermal ) + { + dofManager.addVectorToField( localSolution, + viewKeyStruct::elemDofFieldString(), + flow::temperature::key(), + scalingFactor, + DofManager::CompMask( CellDof::num, CellDof::temperature, CellDof::temperature + 1 ) ); + dofManager.addVectorToField( localSolution, + viewKeyStruct::elemDofFieldString(), + mixedMimetic::enthalpy::key(), + scalingFactor, + DofManager::CompMask( CellDof::num, CellDof::enthalpy, CellDof::enthalpy + 1 ) ); + dofManager.addVectorToField( localSolution, + mixedMimetic::faceHeatFlux::key(), + mixedMimetic::faceHeatFlux::key(), + scalingFactor ); + } + // 3. synchronize forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, MeshLevel & mesh, @@ -915,6 +1302,11 @@ void SinglePhaseMixedMFD::applySystemSolution( DofManager const & dofManager, fieldsToBeSync.addElementFields( { flow::pressure::key() }, regionNames ); fieldsToBeSync.addFields( FieldLocation::Face, { mixedMimetic::faceMassFlux::key() } ); + if( m_isThermal ) + { + fieldsToBeSync.addElementFields( { flow::temperature::key(), mixedMimetic::enthalpy::key() }, regionNames ); + fieldsToBeSync.addFields( FieldLocation::Face, { mixedMimetic::faceHeatFlux::key() } ); + } CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), true ); } ); @@ -928,7 +1320,7 @@ void SinglePhaseMixedMFD::resetStateToBeginningOfStep( DomainPartition & domain // Reset the face-based fields forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, MeshLevel & mesh, - string_array const & ) + string_array const & regionNames ) { FaceManager & faceManager = mesh.getFaceManager(); @@ -937,6 +1329,19 @@ void SinglePhaseMixedMFD::resetStateToBeginningOfStep( DomainPartition & domain arrayView1d< real64 const > const & faceFlux_n = faceManager.getField< mixedMimetic::faceMassFlux_n >(); faceFlux.setValues< parallelDevicePolicy<> >( faceFlux_n ); + + if( m_isThermal ) + { + faceManager.getField< mixedMimetic::faceHeatFlux >().setValues< parallelDevicePolicy<> >( + faceManager.getField< mixedMimetic::faceHeatFlux_n >().toViewConst() ); + mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + subRegion.getField< mixedMimetic::enthalpy >().setValues< parallelDevicePolicy<> >( + subRegion.getField< mixedMimetic::enthalpy_n >().toViewConst() ); + } ); + } } ); } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp index ea8c4e98a59..a8311628955 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.hpp @@ -180,13 +180,33 @@ class SinglePhaseMixedMFD : public SinglePhaseBase CRSMatrixView< real64 const, globalIndex const > const & localMatrix ); /** - * @brief Populate the boundary face mask and pressure values from the field specifications. + * @brief Evaluate the face specifications at the given time and fill the condition (type, g, alpha) of every + * operator on every face. * @param[in] time the time at which the boundary values are evaluated * @param[in] domain the domain * @note public: it contains a device lambda, which nvcc rejects in a private member function */ - void applyFacePressureBCValues( real64 const time, - DomainPartition & domain ); + void applyFaceBoundaryValues( real64 const time, + DomainPartition & domain ); + + /// number of cell dof components: pressure, and temperature and enthalpy when thermal + integer numCellDofComponents() const { return m_isThermal ? 3 : 1; } + + /** + * @brief Define the type of the boundary condition of every operator on every face from the field + * specifications active at the given time (Dirichlet, Neumann or Robin), and the faces with a + * prescribed inflow enthalpy. + * @param[in] time the time at which the specifications are evaluated + * @param[in] domain the domain + */ + void markBoundaryFaces( real64 const time, + DomainPartition & domain ); + + /** + * @brief Initialize the enthalpy unknown from the fluid model, h = h_eos( p, T ). + * @param domain the domain + */ + void initializeEnthalpy( DomainPartition & domain ); private: @@ -220,6 +240,18 @@ class SinglePhaseMixedMFD : public SinglePhaseBase void computeGhostDofs( DomainPartition const & domain, DofManager const & dofManager ); + /** + * @brief Subtract h_K times the mass row from the energy row of every cell. + * @param domain the domain + * @param dofManager the dof manager + * @param localMatrix the system matrix + * @param localRhs the system right-hand side + */ + void removeEnthalpyReference( DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) const; + /// relative tolerance used in the mass matrix computations real64 m_areaRelTol; diff --git a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp index c1fa000c6fc..d071e913453 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/kernels/singlePhase/SinglePhaseMixedMFDKernels.hpp @@ -15,6 +15,16 @@ /** * @file SinglePhaseMixedMFDKernels.hpp + * + * Flow equations of the mixed mimetic single-phase solver. Unknowns: face mass flux m_f and cell pressure p_K. + * With sigma_f the orientation of the face f relative to the cell K, M the inner product weighted by K^{-1}, + * gamma = g . x the gravity coefficient and pi_f the pressure trace: + * + * Darcy law ( mu / rho )_K sum_j M_ij sigma_j m_j - p_K + rho_K ( gamma_K - gamma_f ) + pi_f = 0 + * mass balance ( phi rho V )_K - ( phi rho V )_K^n + dt sum_f sigma_f m_f = 0 + * + * M = chi M_mfd + ( 1 - chi ) M_tpfa, chi the classification of the cell. A face whose cells all have chi = 0 is + * condensed: its Darcy law reduces to the two-point relation A m_f = Phi_L - Phi_R, Phi = p - rho ( gamma_K - gamma_f ). */ #ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_SINGLEPHASE_MIXEDMFDKERNELS_HPP @@ -29,6 +39,7 @@ #include "linearAlgebra/interfaces/InterfaceTypes.hpp" #include "mesh/MeshLevel.hpp" #include "mixedMimetic/MixedMimeticDispatch.hpp" +#include "mixedMimetic/MixedMimeticBoundaryConditions.hpp" #include "mixedMimetic/MixedMimeticFields.hpp" #include "mixedMimetic/consistency/ConsistencyAdaptationKernels.hpp" #include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" @@ -45,14 +56,11 @@ namespace singlePhaseMixedMFDKernels /** * @class ElementBasedAssemblyKernel * @tparam NUM_FACE number of faces per element - * @tparam IP the type of MFD inner product used in the MFD-compatible cells - * @brief Assemble the cell-wise contributions of the mixed mimetic saddle-point system: - * the face-based constitutive (Darcy closure) rows and the cell-based divergence terms. + * @tparam IP the consistent inner product M_mfd + * @brief Cell contributions: Darcy law on the non-condensed faces and mass balance. * - * On each cell, the local mass matrix is selected according to the adaptive stencil flag: - * TPFA-compatible cells use the diagonal TPFA mass matrix, MFD-compatible cells use IP. - * The face rows are assembled with respect to the fixed global orientation of the face normal, - * so that the (unknown) interface pressure cancels across interior interfaces. + * The Darcy law of a face is multiplied by sigma_f: the contributions of its two cells add up to the face + * equation, in which pi_f cancels. */ template< integer NUM_FACE, typename IP > class ElementBasedAssemblyKernel @@ -75,7 +83,6 @@ class ElementBasedAssemblyKernel CellElementSubRegion const & subRegion, constitutive::SingleFluidBase const & fluid, constitutive::PermeabilityBase const & permeability, - SortedArrayView< localIndex const > const & regionFilter, real64 const & dt, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) @@ -102,30 +109,23 @@ class ElementBasedAssemblyKernel m_elemPerm( permeability.permeability() ), m_elemPres( subRegion.getField< fields::flow::pressure >() ), m_faceFlux( faceManager.getField< fields::mixedMimetic::faceMassFlux >() ), - m_bcPres( faceManager.getField< fields::flow::bcPressure >() ), - m_isPresBcFace( faceManager.getField< fields::flow::isBoundaryFace >() ), + m_faceArea( faceManager.faceArea() ), + m_flowBc{ faceManager.getField< fields::mixedMimetic::flowBoundaryType >(), + faceManager.getField< fields::mixedMimetic::flowBoundaryValue >(), + faceManager.getField< fields::mixedMimetic::flowBoundaryCoefficient >() }, m_faceStencilLabel( faceManager.getField< fields::mixedMimetic::faceStencilLabel >() ), m_mfdFlag( subRegion.getField< fields::mixedMimetic::mfdFlag >() ), m_elemDens( fluid.density() ), m_dElemDens( fluid.dDensity() ), m_mob( subRegion.getField< fields::flow::mobility >() ), m_dMob( subRegion.getField< fields::flow::dMobility >() ), - m_regionFilter( regionFilter ), m_localMatrix( localMatrix ), m_localRhs( localRhs ) {} - /// true if the k-th cell adjacent to face kf exists and belongs to a target region - GEOS_HOST_DEVICE - bool isTargetCell( localIndex const kf, integer const k ) const - { - localIndex const er = m_elemRegionList[kf][k]; - return er >= 0 && m_elemSubRegionList[kf][k] >= 0 && m_elemList[kf][k] >= 0 && m_regionFilter.contains( er ); - } - /** * @struct StackVariables - * @brief Kernel variables (dof numbers, jacobian and residual) located on the stack + * @brief Stack variables of the flow equations */ struct StackVariables { @@ -134,28 +134,30 @@ class ElementBasedAssemblyKernel : massMatrix( NUM_FACE, NUM_FACE ) {} - /// adaptive local mass matrix: chi * M_mfd + (1 - chi) * M_tpfa + /// M = chi M_mfd + ( 1 - chi ) M_tpfa stackArray2d< real64, NUM_FACE *NUM_FACE > massMatrix; - /// sign relating the cell-outward normal to the fixed global face normal + /// sigma_i real64 orientation[NUM_FACE]{}; - /// 1 if the face is a domain-boundary face without a pressure boundary condition (no-flow) - integer isNoFlowFace[NUM_FACE]{}; - - /// 1 if the face is a domain-boundary face with a pressure boundary condition - integer isPresBcFace[NUM_FACE]{}; + /// 1 if m_f is prescribed (Neumann) + integer isEssentialFace[NUM_FACE]{}; - /// 1 if the face flux is condensed into a two-point expression (all-TPFA neighborhood): - /// its closure row and its divergence contribution are assembled by the face-based kernel + /// 1 if m_f is condensed into a two-point relation integer isCondensedFace[NUM_FACE]{}; - /// face constitutive residuals (local, outward-oriented) and their derivatives + /// sigma_i m_i, d( sigma_i m_i )/dm_i (0 if prescribed), ( M sigma m )_i and gamma_K - gamma_f + real64 localFlux[NUM_FACE]{}; + real64 dLocalFlux[NUM_FACE]{}; + real64 mDotFlux[NUM_FACE]{}; + real64 gravCoefDif[NUM_FACE]{}; + + /// Darcy residuals and their derivatives real64 faceResidual[NUM_FACE]{}; real64 dFaceResidual_dFlux[NUM_FACE][NUM_FACE]{}; real64 dFaceResidual_dPres[NUM_FACE]{}; - /// divergence of the face fluxes and its derivatives + /// dt sum_f sigma_f m_f and its derivatives real64 divFlux = 0.0; real64 dDivFlux_dFlux[NUM_FACE]{}; @@ -166,7 +168,7 @@ class ElementBasedAssemblyKernel }; /** - * @brief Performs the setup phase for the kernel. + * @brief Row and column indices of the cell. */ GEOS_HOST_DEVICE void setup( localIndex const ei, @@ -183,7 +185,7 @@ class ElementBasedAssemblyKernel } /** - * @brief Compute the local constitutive rows and the flux divergence in a given element. + * @brief Darcy residuals and flux divergence of the cell. */ GEOS_HOST_DEVICE void compute( localIndex const ei, @@ -191,7 +193,7 @@ class ElementBasedAssemblyKernel { real64 const perm[ 3 ] = { m_elemPerm[ei][0][0], m_elemPerm[ei][0][1], m_elemPerm[ei][0][2] }; - // step 2: cell mobility scaling of the inverse Darcy coefficient + // ( mu / rho )_K and its pressure derivative real64 const mob = m_mob[ei]; real64 const dMob_dPres = m_dMob[ei][DerivOffset::dP]; real64 const invMob = 1.0 / mob; @@ -200,35 +202,32 @@ class ElementBasedAssemblyKernel real64 const ccDens = m_elemDens[ei][0]; real64 const dCcDens_dPres = m_dElemDens[ei][0][DerivOffset::dP]; - // step 3: face orientations, localized fluxes and no-flow flags. - // The orientation must be identical on every MPI rank sharing the face (the face - // mass-flux unknown is shared across ranks): the face points out of the adjacent cell - // with the smallest global element index, stored per face by the face owner and - // synchronized, so that a ghost cell whose neighbour is absent on this rank still - // orients the face like its owner - real64 localFlux[NUM_FACE]{}; + // sigma_f = +1 for the cell of smallest global index of the face, -1 for the other. That cell is stored by + // the owner of the face and synchronized: sigma_f is the same on every rank + real64 ( &localFlux )[NUM_FACE] = stack.localFlux; globalIndex const myGlobalElem = m_myElemLocalToGlobal[ei]; for( integer i = 0; i < NUM_FACE; ++i ) { localIndex const kf = m_elemToFaces[ei][i]; stack.orientation[i] = ( myGlobalElem == m_faceOrientationCell[kf] ) ? 1.0 : -1.0; - localFlux[i] = stack.orientation[i] * m_faceFlux[kf]; + stack.gravCoefDif[i] = m_elemGravCoef[ei] - m_faceGravCoef[kf]; + + stack.isEssentialFace[i] = m_flowBc[kf].isEssential() ? 1 : 0; + stack.isCondensedFace[i] = ( m_faceStencilLabel[kf] == 0 && stack.isEssentialFace[i] == 0 ) ? 1 : 0; - // the boundary is that of the target regions: a non-target neighbour counts as absent - bool const onBoundary = !( isTargetCell( kf, 0 ) && isTargetCell( kf, 1 ) ); - stack.isNoFlowFace[i] = ( onBoundary && m_isPresBcFace[kf] == 0 ) ? 1 : 0; - stack.isPresBcFace[i] = ( onBoundary && m_isPresBcFace[kf] == 1 ) ? 1 : 0; - stack.isCondensedFace[i] = ( m_faceStencilLabel[kf] == 0 && stack.isNoFlowFace[i] == 0 ) ? 1 : 0; + // prescribed flux: sigma m_f = g |f|, d( sigma m_f )/dm_f = 0 + real64 const faceFlux = stack.isEssentialFace[i] == 1 ? m_flowBc[kf].essentialFlux( stack.orientation[i], m_faceArea[kf] ) + : m_faceFlux[kf]; + localFlux[i] = stack.orientation[i] * faceFlux; + stack.dLocalFlux[i] = stack.isEssentialFace[i] == 1 ? 0.0 : stack.orientation[i]; } - // step 1: adaptive inner product, M = chi * M_mfd + (1 - chi) * M_tpfa. - // Only needed if the cell has at least one live (non-condensed, non-no-flow) face: - // cells fully surrounded by TPFA-condensed faces skip the local operator entirely + // M, needed if one face of the cell is non-condensed bool anyLiveFace = false; for( integer i = 0; i < NUM_FACE; ++i ) { - anyLiveFace = anyLiveFace || ( stack.isNoFlowFace[i] == 0 && stack.isCondensedFace[i] == 0 ); + anyLiveFace = anyLiveFace || ( stack.isEssentialFace[i] == 0 && stack.isCondensedFace[i] == 0 ); } if( anyLiveFace ) { @@ -244,14 +243,11 @@ class ElementBasedAssemblyKernel stack.massMatrix ); } - // step 4: constitutive (Darcy closure) rows: - // invMob * sum_j M_ij (sigma_j m_j) - p_K + pi_f + rho * ( g_K - g_f ) = 0 - // the interface pressure pi_f is dropped on interior faces (it cancels upon global - // orientation-signed assembly) and replaced by the prescribed value on boundary faces; - // condensed faces are assembled by the face-based two-point kernel instead + // Darcy law on the non-condensed faces: ( mu / rho )_K sum_j M_ij sigma_j m_j - p_K + rho_K ( gamma_K - gamma_f ) + pi_f = 0. + // pi_f cancels between the two cells of an interior face; the boundary condition defines it on a boundary face for( integer i = 0; i < NUM_FACE; ++i ) { - if( stack.isNoFlowFace[i] == 1 || stack.isCondensedFace[i] == 1 ) + if( stack.isEssentialFace[i] == 1 || stack.isCondensedFace[i] == 1 ) { continue; } @@ -262,43 +258,45 @@ class ElementBasedAssemblyKernel for( integer j = 0; j < NUM_FACE; ++j ) { mDotFlux += stack.massMatrix( i, j ) * localFlux[j]; - stack.dFaceResidual_dFlux[i][j] = invMob * stack.massMatrix( i, j ) * stack.orientation[j]; + stack.dFaceResidual_dFlux[i][j] = invMob * stack.massMatrix( i, j ) * stack.dLocalFlux[j]; } + stack.mDotFlux[i] = mDotFlux; - real64 const gravCoefDif = m_elemGravCoef[ei] - m_faceGravCoef[kf]; + real64 const gravCoefDif = stack.gravCoefDif[i]; stack.faceResidual[i] = invMob * mDotFlux - m_elemPres[ei] + ccDens * gravCoefDif; stack.dFaceResidual_dPres[i] = dInvMob_dPres * mDotFlux - 1.0 + dCcDens_dPres * gravCoefDif; - if( stack.isPresBcFace[i] == 1 ) + mixedMimeticBoundary::FaceBoundaryCondition const bc = m_flowBc[kf]; + if( bc.type != mixedMimeticBoundary::BoundaryType::interior ) { - stack.faceResidual[i] += m_bcPres[kf]; + real64 dTrace_dFlux = 0.0; + stack.faceResidual[i] += bc.trace( stack.orientation[i], m_faceArea[kf], localFlux[i], dTrace_dFlux ); + stack.dFaceResidual_dFlux[i][i] += dTrace_dFlux * stack.orientation[i]; } } - // step 5: divergence of the face fluxes for the cell-centered mass conservation equation. - // No-flow faces carry m = 0 exactly and condensed faces are handled by the face-based - // kernel (two-point expression in the pressures), so only live faces contribute here + // dt sum_f sigma_f m_f over the non-condensed faces for( integer i = 0; i < NUM_FACE; ++i ) { - if( stack.isNoFlowFace[i] == 1 || stack.isCondensedFace[i] == 1 ) + if( stack.isCondensedFace[i] == 1 ) { stack.dDivFlux_dFlux[i] = 0.0; continue; } stack.divFlux += m_dt * localFlux[i]; - stack.dDivFlux_dFlux[i] = m_dt * stack.orientation[i]; + stack.dDivFlux_dFlux[i] = m_dt * stack.dLocalFlux[i]; } } /** - * @brief Assemble the local contributions into the global system. + * @brief Add the contributions of the cell to the system. */ GEOS_HOST_DEVICE void complete( localIndex const ei, StackVariables & stack ) const { - // step 1: cell-centered mass conservation contribution (single thread per row) + // mass balance of the cell if( m_elemGhostRank[ei] < 0 ) { m_localRhs[stack.cellCenteredEqnRowIndex] += stack.divFlux; @@ -309,8 +307,7 @@ class ElementBasedAssemblyKernel NUM_FACE ); } - // step 2: face-centered constitutive rows, signed by the global face orientation - // (condensed faces are assembled by the face-based two-point kernel) + // Darcy law, multiplied by sigma_f for( integer i = 0; i < NUM_FACE; ++i ) { localIndex const kf = m_elemToFaces[ei][i]; @@ -319,11 +316,12 @@ class ElementBasedAssemblyKernel continue; } - if( stack.isNoFlowFace[i] == 1 ) + if( stack.isEssentialFace[i] == 1 ) { - // essential no-flow condition: m_f = 0 (row owned by the unique adjacent cell) + // Neumann condition: m_f - sigma g |f| = 0 real64 const one = 1.0; - RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[stack.faceCenteredEqnRowIndex[i]], m_faceFlux[kf] ); + real64 const target = m_flowBc[kf].essentialFlux( stack.orientation[i], m_faceArea[kf] ); + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[stack.faceCenteredEqnRowIndex[i]], m_faceFlux[kf] - target ); m_localMatrix.addToRow< parallelDeviceAtomic >( stack.faceCenteredEqnRowIndex[i], &stack.faceDofColIndices[i], &one, @@ -354,7 +352,7 @@ class ElementBasedAssemblyKernel } /** - * @brief Performs the kernel launch + * @brief Launch the kernel over the cells. */ template< typename POLICY, typename KERNEL_TYPE > static void @@ -375,7 +373,7 @@ class ElementBasedAssemblyKernel protected: - /// offset for my MPI rank + /// first global row of the rank globalIndex const m_rankOffset; /// length tolerance @@ -401,7 +399,7 @@ class ElementBasedAssemblyKernel arrayView2d< localIndex const > const m_elemSubRegionList; arrayView2d< localIndex const > const m_elemList; arrayView1d< globalIndex const > const m_myElemLocalToGlobal; - /// global index of the cell each face points out of (rank-invariant orientation) + /// global index of the cell with sigma_f = +1 arrayView1d< globalIndex const > const m_faceOrientationCell; arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const m_nodePosition; @@ -411,11 +409,11 @@ class ElementBasedAssemblyKernel /// primary variables and boundary data arrayView1d< real64 const > const m_elemPres; arrayView1d< real64 const > const m_faceFlux; - arrayView1d< real64 const > const m_bcPres; - arrayView1d< integer const > const m_isPresBcFace; + arrayView1d< real64 const > const m_faceArea; + mixedMimeticBoundary::FaceBoundaryView const m_flowBc; arrayView1d< integer const > const m_faceStencilLabel; - /// adaptive stencil flag + /// chi arrayView1d< integer const > const m_mfdFlag; /// fluid data @@ -424,9 +422,6 @@ class ElementBasedAssemblyKernel arrayView1d< real64 const > const m_mob; arrayView2d< real64 const, constitutive::singlefluid::USD_FLUID > const m_dMob; - /// target regions of the solver - SortedArrayView< localIndex const > const m_regionFilter; - /// View on the local CRS matrix and RHS CRSMatrixView< real64, globalIndex const > const m_localMatrix; arrayView1d< real64 > const m_localRhs; @@ -452,7 +447,6 @@ class ElementBasedAssemblyKernelFactory mimeticInnerProduct::MimeticInnerProductBase const & mimeticInnerProductBase, constitutive::SingleFluidBase const & fluid, constitutive::PermeabilityBase const & permeability, - SortedArrayView< localIndex const > const & regionFilter, real64 const & dt, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) @@ -466,7 +460,7 @@ class ElementBasedAssemblyKernelFactory { ElementBasedAssemblyKernel< NUM_FACES, IP > kernel( rankOffset, lengthTolerance, elemDofKey, faceDofKey, nodeManager, faceManager, - subRegion, fluid, permeability, regionFilter, dt, localMatrix, localRhs ); + subRegion, fluid, permeability, dt, localMatrix, localRhs ); ElementBasedAssemblyKernel< NUM_FACES, IP >::template launch< POLICY >( subRegion.size(), kernel ); } ); } ); @@ -478,20 +472,11 @@ class ElementBasedAssemblyKernelFactory /** * @class TpfaCondensedFluxKernel - * @brief Face-based assembly of the condensed (label-0) fluxes: on faces whose adjacent - * cells are all TPFA-compatible, the closure row is exactly diagonal and the flux - * can be substituted into the mass conservation rows as the standard two-point - * expression m_f = pot / A with A = sum_K invMob_K / t_K. - * - * The kernel writes: - * - the TPFA two-point contribution dt * m_f (+full Jacobian wrt the two cell pressures) - * into both adjacent cell rows (the pressure block gains standard TPFA stencil entries); - * - the closure row A * m_f - pot = 0 as a one-way *definition* of the face flux - * (no other equation references m_f, so the flux dof decouples from the Krylov - * iteration and reduces the live system to cells + MFD faces). - * - * The scheme is algebraically identical to the fully-coupled assembly: the closure row is - * unchanged, only its exact substitution into the divergence is performed ahead of time. + * @brief Contributions of the condensed faces, with L and R the cells of the face. M_tpfa is diagonal, so the + * Darcy laws of L and R give m_f = ( Phi_L - Phi_R ) / A, A = sum_K ( mu / rho )_K / t_K, t_K the one-sided + * transmissibility. The kernel adds +/- dt m_f( p_L, p_R ) to the mass balances of L and R and the face + * equation A m_f = Phi_L - Phi_R. No other equation depends on m_f: the system is equivalent to the + * non-condensed one, and m_f is decoupled from the cell unknowns. */ class TpfaCondensedFluxKernel { @@ -546,8 +531,10 @@ class TpfaCondensedFluxKernel m_faceToNodes( faceManager.nodeList().toViewConst() ), m_faceGravCoef( faceManager.getField< fields::flow::gravityCoefficient >() ), m_faceFlux( faceManager.getField< fields::mixedMimetic::faceMassFlux >() ), - m_bcPres( faceManager.getField< fields::flow::bcPressure >() ), - m_isPresBcFace( faceManager.getField< fields::flow::isBoundaryFace >() ), + m_faceArea( faceManager.faceArea() ), + m_flowBc{ faceManager.getField< fields::mixedMimetic::flowBoundaryType >(), + faceManager.getField< fields::mixedMimetic::flowBoundaryValue >(), + faceManager.getField< fields::mixedMimetic::flowBoundaryCoefficient >() }, m_faceStencilLabel( faceManager.getField< fields::mixedMimetic::faceStencilLabel >() ), m_elemRegionList( faceManager.elementRegionList() ), m_elemSubRegionList( faceManager.elementSubRegionList() ), @@ -569,16 +556,16 @@ class TpfaCondensedFluxKernel m_localRhs( localRhs ) {} + /** + * @brief Cells of a face in the target regions, the cell with sigma_f = +1 first. + * @return the number of cells + */ GEOS_HOST_DEVICE - void compute( localIndex const kf ) const + integer gatherCells( localIndex const kf, + localIndex ( & er )[2], + localIndex ( & esr )[2], + localIndex ( & ei )[2] ) const { - if( m_faceStencilLabel[kf] != 0 ) - { - return; - } - - // gather the (at most two) adjacent target cells - localIndex er[2]{}, esr[2]{}, ei[2]{}; integer numElems = 0; for( integer k = 0; k < m_elemRegionList.size( 1 ); ++k ) { @@ -591,20 +578,7 @@ class TpfaCondensedFluxKernel numElems++; } } - if( numElems == 0 ) - { - return; - } - - bool const onBoundary = ( numElems == 1 ); - if( onBoundary && m_isPresBcFace[kf] == 0 ) - { - // no-flow face: the identity row m_f = 0 is assembled by the cell-based kernel - return; - } - - // global orientation: the face points out of the adjacent cell with the smallest - // global element index (must match the cell-based kernel and be rank-invariant) + // L is the cell of smallest global index: sigma_f = +1, as in the cell kernel if( numElems == 2 && m_elemLocalToGlobal[er[1]][esr[1]][ei[1]] < m_elemLocalToGlobal[er[0]][esr[0]][ei[0]] ) { @@ -613,54 +587,117 @@ class TpfaCondensedFluxKernel tmp = esr[0]; esr[0] = esr[1]; esr[1] = tmp; tmp = ei[0]; ei[0] = ei[1]; ei[1] = tmp; } + return numElems; + } - // the same one-sided conductance and floor as the diagonal entries of TPFAInnerProduct::computeM + /// A, pot = Phi_L - Phi_R, m_f = pot / A and their derivatives with respect to the cell unknowns + template< integer NUM_DERIV > + struct TwoPointFlux + { + real64 A = 0.0; + real64 pot = 0.0; + real64 flux = 0.0; + real64 dA[2][NUM_DERIV]{}; + real64 dPot[2][NUM_DERIV]{}; + real64 dFlux[2][NUM_DERIV]{}; + }; + + /** + * @brief One-sided transmissibility t_K of a cell through a face for a diagonal coefficient, as in TPFAInnerProduct::computeM. + */ + GEOS_HOST_DEVICE + real64 oneSidedTrans( localIndex const kf, + localIndex const er, localIndex const esr, localIndex const ei, + real64 const (&coef)[3] ) const + { real64 const areaTolerance = m_lengthTolerance * m_lengthTolerance; real64 const weightTolerance = mimeticInnerProduct::TPFAInnerProduct::weightToleranceFactor * m_lengthTolerance; + return LvArray::math::max( mimeticInnerProduct::TPFAInnerProduct::computeOneSidedTrans( m_nodePosition, + m_faceToNodes, + kf, + m_elemCenter[er][esr][ei], + coef, + areaTolerance ), + weightTolerance ); + } - // per-side one-sided coefficients: a_K = invMob_K / t_K and gravity terms - real64 a[2]{}, dA_dp[2]{}, grav[2]{}, dGrav_dp[2]{}; + /** + * @brief Two-point mass flux m_f = ( Phi_L - Phi_R ) / A, A = sum_K ( mu / rho )_K / t_K. On a boundary face + * Phi_R is the boundary value and A includes 1 / ( alpha |f| ). + * @tparam NUM_DERIV 1 (pressure) or 2 (pressure and temperature) derivatives of the cell unknowns + */ + template< integer NUM_DERIV > + GEOS_HOST_DEVICE + void computeTwoPointMassFlux( localIndex const kf, + localIndex const (&er)[2], localIndex const (&esr)[2], localIndex const (&ei)[2], + integer const numElems, + TwoPointFlux< NUM_DERIV > & tp ) const + { + real64 grav[2]{}, dGrav[2][NUM_DERIV]{}; for( integer k = 0; k < numElems; ++k ) { real64 const perm[3] = { m_elemPerm[er[k]][esr[k]][ei[k]][0][0], m_elemPerm[er[k]][esr[k]][ei[k]][0][1], m_elemPerm[er[k]][esr[k]][ei[k]][0][2] }; - - real64 const t = LvArray::math::max( mimeticInnerProduct::TPFAInnerProduct::computeOneSidedTrans( m_nodePosition, - m_faceToNodes, - kf, - m_elemCenter[er[k]][esr[k]][ei[k]], - perm, - areaTolerance ), - weightTolerance ); - - real64 const mob = m_mob[er[k]][esr[k]][ei[k]]; - real64 const invMob = 1.0 / mob; - real64 const dInvMob = -m_dMob[er[k]][esr[k]][ei[k]][DerivOffset::dP] * invMob * invMob; - - a[k] = invMob / t; - dA_dp[k] = dInvMob / t; - + real64 const t = oneSidedTrans( kf, er[k], esr[k], ei[k], perm ); + real64 const invMob = 1.0 / m_mob[er[k]][esr[k]][ei[k]]; real64 const gravCoefDif = m_elemGravCoef[er[k]][esr[k]][ei[k]] - m_faceGravCoef[kf]; + tp.A += invMob / t; grav[k] = m_dens[er[k]][esr[k]][ei[k]][0] * gravCoefDif; - dGrav_dp[k] = m_dDens[er[k]][esr[k]][ei[k]][0][DerivOffset::dP] * gravCoefDif; + for( integer d = 0; d < NUM_DERIV; ++d ) + { + tp.dA[k][d] = -m_dMob[er[k]][esr[k]][ei[k]][d] * invMob * invMob / t; + dGrav[k][d] = m_dDens[er[k]][esr[k]][ei[k]][0][d] * gravCoefDif; + } + } + if( numElems == 1 ) + { + tp.A += m_flowBc[kf].resistance( m_faceArea[kf] ); } - - // assembled closure: A * m_f = pot, - // interior: pot = (p_L - p_R) - (grav_L - grav_R) - // boundary: pot = p_L - pi_bc - grav_L - real64 const A = ( numElems == 2 ) ? a[0] + a[1] : a[0]; real64 const pL = m_pres[er[0]][esr[0]][ei[0]]; - real64 const pR = ( numElems == 2 ) ? m_pres[er[1]][esr[1]][ei[1]] : m_bcPres[kf]; - real64 const pot = ( pL - pR ) - grav[0] + ( ( numElems == 2 ) ? grav[1] : 0.0 ); - - real64 const mFlux = pot / A; - real64 const dPot_dp[2] = { 1.0 - dGrav_dp[0], -1.0 + dGrav_dp[1] }; - real64 dFlux_dp[2]; + real64 const pR = ( numElems == 2 ) ? m_pres[er[1]][esr[1]][ei[1]] : m_flowBc[kf].value; + tp.pot = ( pL - pR ) - grav[0] + ( ( numElems == 2 ) ? grav[1] : 0.0 ); + tp.flux = tp.pot / tp.A; for( integer k = 0; k < numElems; ++k ) { - dFlux_dp[k] = ( dPot_dp[k] - mFlux * dA_dp[k] ) / A; + real64 const sign = ( k == 0 ) ? 1.0 : -1.0; + for( integer d = 0; d < NUM_DERIV; ++d ) + { + tp.dPot[k][d] = ( d == 0 ? sign : 0.0 ) - sign * dGrav[k][d]; + tp.dFlux[k][d] = ( tp.dPot[k][d] - tp.flux * tp.dA[k][d] ) / tp.A; + } } + } + + GEOS_HOST_DEVICE + void compute( localIndex const kf ) const + { + if( m_faceStencilLabel[kf] != 0 ) + { + return; + } + + localIndex er[2]{}, esr[2]{}, ei[2]{}; + integer const numElems = gatherCells( kf, er, esr, ei ); + if( numElems == 0 ) + { + return; + } + + if( m_flowBc[kf].isEssential() ) + { + // prescribed flux: assembled by the cell kernel + return; + } + + TwoPointFlux< 1 > tp; + computeTwoPointMassFlux< 1 >( kf, er, esr, ei, numElems, tp ); + real64 const A = tp.A; + real64 const pot = tp.pot; + real64 const mFlux = tp.flux; + real64 const dA_dp[2] = { tp.dA[0][0], tp.dA[1][0] }; + real64 const dPot_dp[2] = { tp.dPot[0][0], tp.dPot[1][0] }; + real64 const dFlux_dp[2] = { tp.dFlux[0][0], tp.dFlux[1][0] }; globalIndex elemDofCols[2]; for( integer k = 0; k < numElems; ++k ) @@ -668,7 +705,7 @@ class TpfaCondensedFluxKernel elemDofCols[k] = m_elemDofNumber[er[k]][esr[k]][ei[k]]; } - // 1) two-point contributions to the mass conservation rows: +dt*m to L, -dt*m to R + // mass balances of L and R: +/- dt m_f for( integer k = 0; k < numElems; ++k ) { if( m_elemGhostRank[er[k]][esr[k]][ei[k]] >= 0 ) @@ -691,7 +728,7 @@ class TpfaCondensedFluxKernel numElems ); } - // 2) one-way closure row defining the (decoupled) face flux: A * m_f - pot = 0 + // face equation: A m_f - ( Phi_L - Phi_R ) = 0 if( m_faceGhostRank[kf] < 0 ) { localIndex const row = m_faceDofNumber[kf] - m_rankOffset; @@ -739,8 +776,8 @@ class TpfaCondensedFluxKernel ArrayOfArraysView< localIndex const > const m_faceToNodes; arrayView1d< real64 const > const m_faceGravCoef; arrayView1d< real64 const > const m_faceFlux; - arrayView1d< real64 const > const m_bcPres; - arrayView1d< integer const > const m_isPresBcFace; + arrayView1d< real64 const > const m_faceArea; + mixedMimeticBoundary::FaceBoundaryView const m_flowBc; arrayView1d< integer const > const m_faceStencilLabel; arrayView2d< localIndex const > const m_elemRegionList; arrayView2d< localIndex const > const m_elemSubRegionList; @@ -748,7 +785,7 @@ class TpfaCondensedFluxKernel arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const m_nodePosition; SortedArrayView< localIndex const > const m_regionFilter; - /// element data (cross-region accessors) + /// cell data ElementViewConst< arrayView1d< globalIndex const > > const m_elemDofNumber; ElementViewConst< arrayView1d< globalIndex const > > const m_elemLocalToGlobal; ElementViewConst< arrayView1d< integer const > > const m_elemGhostRank; From 76149199396348a54995a69f03c5aca973666ac0 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Wed, 4 Nov 2026 13:30:38 -0800 Subject: [PATCH 61/64] wip: drop the local cmake patches --- src/cmake/GeosOptions.cmake | 2 +- src/cmake/thirdparty/SetupGeosxThirdParty.cmake | 6 ------ .../fluidFlowTests/testSinglePhaseMixedMFD.cpp | 2 ++ 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/cmake/GeosOptions.cmake b/src/cmake/GeosOptions.cmake index b5752d8c30c..2804dce0c02 100644 --- a/src/cmake/GeosOptions.cmake +++ b/src/cmake/GeosOptions.cmake @@ -152,7 +152,7 @@ message( "CMAKE_CXX_COMPILER_ID = ${CMAKE_CXX_COMPILER_ID}" ) blt_append_custom_compiler_flag( FLAGS_VAR CMAKE_CXX_FLAGS DEFAULT "${OpenMP_CXX_FLAGS}" ) blt_append_custom_compiler_flag( FLAGS_VAR CMAKE_CXX_FLAGS GNU "-Wpedantic -pedantic-errors -Wshadow -Wfloat-equal -Wcast-align -Wcast-qual" - CLANG "-Wpedantic -pedantic-errors -Wshadow -Wfloat-equal -Wno-cast-align -Wcast-qual -Wno-shorten-64-to-32" + CLANG "-Wpedantic -pedantic-errors -Wshadow -Wfloat-equal -Wno-cast-align -Wcast-qual" ) if( ENABLE_HIP ) diff --git a/src/cmake/thirdparty/SetupGeosxThirdParty.cmake b/src/cmake/thirdparty/SetupGeosxThirdParty.cmake index c64d80fddae..ca626fa132f 100644 --- a/src/cmake/thirdparty/SetupGeosxThirdParty.cmake +++ b/src/cmake/thirdparty/SetupGeosxThirdParty.cmake @@ -798,12 +798,6 @@ if( ENABLE_HYPREDRV ) list( PREPEND CMAKE_PREFIX_PATH "${HYPRE_DIR}" ) endif() - # expose the imported TPL hypre as HYPRE::HYPRE so HYPREDRVConfig does not search for another hypre - if( TARGET hypre AND NOT TARGET HYPRE::HYPRE ) - add_library( HYPRE::HYPRE INTERFACE IMPORTED ) - target_link_libraries( HYPRE::HYPRE INTERFACE hypre ) - endif() - find_package( HYPREDRV CONFIG QUIET PATHS ${HYPREDRV_DIR} ${HYPREDRV_DIR}/lib/cmake/HYPREDRV diff --git a/src/coreComponents/integrationTests/fluidFlowTests/testSinglePhaseMixedMFD.cpp b/src/coreComponents/integrationTests/fluidFlowTests/testSinglePhaseMixedMFD.cpp index c56c6f84193..21902716d4a 100644 --- a/src/coreComponents/integrationTests/fluidFlowTests/testSinglePhaseMixedMFD.cpp +++ b/src/coreComponents/integrationTests/fluidFlowTests/testSinglePhaseMixedMFD.cpp @@ -449,6 +449,8 @@ TEST( SinglePhaseMixedMFDBoundary, descriptors ) using namespace geos::mixedMimeticBoundary; MixedMFDProblem problem( { true, "0.1", 350.0, true } ); + // the descriptors are evaluated at assembly time from the specifications active at that time + problem.solver().applyFaceBoundaryValues( 0.0, problem.domain() ); FaceManager & faces = problem.faceManager(); arrayView2d< real64 const > const center = faces.faceCenter(); arrayView2d< localIndex const > const elemList = faces.elementList(); From 8538e8fac144a6e73cf4834c4781f31a20e13acd Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Wed, 4 Nov 2026 13:50:43 -0800 Subject: [PATCH 62/64] wip: Lapwood decks --- .../polyhedral/lapwoodConvectionFVM_Ra30.xml | 64 +++++ .../polyhedral/lapwoodConvectionFVM_Ra50.xml | 64 +++++ .../polyhedral/lapwoodConvectionFVM_base.xml | 208 ++++++++++++++ .../polyhedral/lapwoodConvection_Ra30.xml | 2 +- .../polyhedral/lapwoodConvection_Ra50.xml | 2 +- .../lapwoodConvection_Ra50_smoke.xml | 204 ++++++++++++- .../polyhedral/lapwoodConvection_base.xml | 40 +-- .../lapwoodConvection_simplex_Ra50_smoke.xml | 268 ++++++++++++++++++ .../lapwoodConvection_tables/conduction.geos | 1 + .../temperature3D.geos | 1 + .../lapwoodConvection_tables/x3D.geos | 1 + .../lapwoodConvection_tables/y3D.geos | 1 + .../lapwoodConvection_tables/z3D.geos | 1 + .../polyhedral/thermalMixedMFD.ats | 10 +- 14 files changed, 841 insertions(+), 26 deletions(-) create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvectionFVM_Ra30.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvectionFVM_Ra50.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvectionFVM_base.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_simplex_Ra50_smoke.xml create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/conduction.geos create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/temperature3D.geos create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/x3D.geos create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/y3D.geos create mode 100644 inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/z3D.geos diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvectionFVM_Ra30.xml b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvectionFVM_Ra30.xml new file mode 100644 index 00000000000..c70ee114a29 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvectionFVM_Ra30.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvectionFVM_Ra50.xml b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvectionFVM_Ra50.xml new file mode 100644 index 00000000000..6ae47577b6c --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvectionFVM_Ra50.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvectionFVM_base.xml b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvectionFVM_base.xml new file mode 100644 index 00000000000..47067006f0e --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvectionFVM_base.xml @@ -0,0 +1,208 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra30.xml b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra30.xml index 0dcb83ded22..efe2b586974 100644 --- a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra30.xml +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra30.xml @@ -1,6 +1,6 @@ - + diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra50.xml b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra50.xml index 49e29da073f..588ee2d5c3e 100644 --- a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra50.xml +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra50.xml @@ -1,6 +1,6 @@ - + diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra50_smoke.xml b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra50_smoke.xml index 8da9594843c..073b9e97097 100644 --- a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra50_smoke.xml +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_Ra50_smoke.xml @@ -1,14 +1,184 @@ - + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -67,4 +260,5 @@ targetExactTimestep="0" target="/Outputs/restartOutput"/> + diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_base.xml b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_base.xml index 6c4d8f757a2..d139ece7b95 100644 --- a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_base.xml +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_base.xml @@ -3,12 +3,16 @@ @@ -43,7 +47,7 @@ name="mixedMFD" innerProductType="RT" adaptiveConsistency="1" - consistencyTolerance="1.e-12" + consistencyTolerance="1.e+12" nominalGradient="{ 1.0, 1.0, 1.0 }"/> @@ -53,13 +57,13 @@ @@ -67,18 +71,18 @@ + xMax="{ 200.01, 200.01, 0.01 }"/> + xMax="{ 200.01, 200.01, 100.01 }"/> - + + xMax="{ 3.34, 6.67, 53.34 }"/> @@ -137,7 +141,7 @@ referenceTemperature="310.0"/> - + - + + coordinateFiles="{ lapwoodConvection_tables/x3D.geos, lapwoodConvection_tables/y3D.geos, lapwoodConvection_tables/z3D.geos }" + voxelFile="lapwoodConvection_tables/temperature3D.geos"/> diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_simplex_Ra50_smoke.xml b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_simplex_Ra50_smoke.xml new file mode 100644 index 00000000000..180974d3b27 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_simplex_Ra50_smoke.xml @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/conduction.geos b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/conduction.geos new file mode 100644 index 00000000000..f2783d1ca91 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/conduction.geos @@ -0,0 +1 @@ +320 319.8 319.6 319.4 319.2 319 318.8 318.6 318.4 318.2 318 317.8 317.6 317.4 317.2 317 316.8 316.6 316.4 316.2 316 315.8 315.6 315.4 315.2 315 314.8 314.6 314.4 314.2 314 313.8 313.6 313.4 313.2 313 312.8 312.6 312.4 312.2 312 311.8 311.6 311.4 311.2 311 310.8 310.6 310.4 310.2 310 309.8 309.6 309.4 309.2 309 308.8 308.6 308.4 308.2 308 307.8 307.6 307.4 307.2 307 306.8 306.6 306.4 306.2 306 305.8 305.6 305.4 305.2 305 304.8 304.6 304.4 304.2 304 303.8 303.6 303.4 303.2 303 302.8 302.6 302.4 302.2 302 301.8 301.6 301.4 301.2 301 300.8 300.6 300.4 300.2 300 diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/temperature3D.geos b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/temperature3D.geos new file mode 100644 index 00000000000..7a41282ccdb --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/temperature3D.geos @@ -0,0 +1 @@ +320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 320 319.0015643 319.0015451 319.0014878 319.0013938 319.0012656 319.0011062 319.0009195 319.0007102 319.0004834 319.0002447 319 318.9997553 318.9995166 318.9992898 318.9990805 318.9988938 318.9987344 318.9986062 318.9985122 318.9984549 318.9984357 318.9984549 318.9985122 318.9986062 318.9987344 318.9988938 318.9990805 318.9992898 318.9995166 318.9997553 319 319.0002447 319.0004834 319.0007102 319.0009195 319.0011062 319.0012656 319.0013938 319.0014878 319.0015451 319.0015643 319.0015451 319.0015261 319.0014695 319.0013767 319.00125 319.0010925 319.0009082 319.0007015 319.0004775 319.0002417 319 318.9997583 318.9995225 318.9992985 318.9990918 318.9989075 318.99875 318.9986233 318.9985305 318.9984739 318.9984549 318.9984739 318.9985305 318.9986233 318.99875 318.9989075 318.9990918 318.9992985 318.9995225 318.9997583 319 319.0002417 319.0004775 319.0007015 319.0009082 319.0010925 319.00125 319.0013767 319.0014695 319.0015261 319.0015451 319.0014878 319.0014695 319.001415 319.0013256 319.0012036 319.001052 319.0008745 319.0006754 319.0004597 319.0002327 319 318.9997673 318.9995403 318.9993246 318.9991255 318.998948 318.9987964 318.9986744 318.998585 318.9985305 318.9985122 318.9985305 318.998585 318.9986744 318.9987964 318.998948 318.9991255 318.9993246 318.9995403 318.9997673 319 319.0002327 319.0004597 319.0006754 319.0008745 319.001052 319.0012036 319.0013256 319.001415 319.0014695 319.0014878 319.0013938 319.0013767 319.0013256 319.0012419 319.0011276 319.0009856 319.0008193 319.0006328 319.0004307 319.000218 319 318.999782 318.9995693 318.9993672 318.9991807 318.9990144 318.9988724 318.9987581 318.9986744 318.9986233 318.9986062 318.9986233 318.9986744 318.9987581 318.9988724 318.9990144 318.9991807 318.9993672 318.9995693 318.999782 319 319.000218 319.0004307 319.0006328 319.0008193 319.0009856 319.0011276 319.0012419 319.0013256 319.0013767 319.0013938 319.0012656 319.00125 319.0012036 319.0011276 319.0010239 319.0008949 319.0007439 319.0005746 319.0003911 319.000198 319 318.999802 318.9996089 318.9994254 318.9992561 318.9991051 318.9989761 318.9988724 318.9987964 318.99875 318.9987344 318.99875 318.9987964 318.9988724 318.9989761 318.9991051 318.9992561 318.9994254 318.9996089 318.999802 319 319.000198 319.0003911 319.0005746 319.0007439 319.0008949 319.0010239 319.0011276 319.0012036 319.00125 319.0012656 319.0011062 319.0010925 319.001052 319.0009856 319.0008949 319.0007822 319.0006502 319.0005022 319.0003418 319.000173 319 318.999827 318.9996582 318.9994978 318.9993498 318.9992178 318.9991051 318.9990144 318.998948 318.9989075 318.9988938 318.9989075 318.998948 318.9990144 318.9991051 318.9992178 318.9993498 318.9994978 318.9996582 318.999827 319 319.000173 319.0003418 319.0005022 319.0006502 319.0007822 319.0008949 319.0009856 319.001052 319.0010925 319.0011062 319.0009195 319.0009082 319.0008745 319.0008193 319.0007439 319.0006502 319.0005405 319.0004174 319.0002841 319.0001438 319 318.9998562 318.9997159 318.9995826 318.9994595 318.9993498 318.9992561 318.9991807 318.9991255 318.9990918 318.9990805 318.9990918 318.9991255 318.9991807 318.9992561 318.9993498 318.9994595 318.9995826 318.9997159 318.9998562 319 319.0001438 319.0002841 319.0004174 319.0005405 319.0006502 319.0007439 319.0008193 319.0008745 319.0009082 319.0009195 319.0007102 319.0007015 319.0006754 319.0006328 319.0005746 319.0005022 319.0004174 319.0003224 319.0002195 319.0001111 319 318.9998889 318.9997805 318.9996776 318.9995826 318.9994978 318.9994254 318.9993672 318.9993246 318.9992985 318.9992898 318.9992985 318.9993246 318.9993672 318.9994254 318.9994978 318.9995826 318.9996776 318.9997805 318.9998889 319 319.0001111 319.0002195 319.0003224 319.0004174 319.0005022 319.0005746 319.0006328 319.0006754 319.0007015 319.0007102 319.0004834 319.0004775 319.0004597 319.0004307 319.0003911 319.0003418 319.0002841 319.0002195 319.0001494 319.0000756 319 318.9999244 318.9998506 318.9997805 318.9997159 318.9996582 318.9996089 318.9995693 318.9995403 318.9995225 318.9995166 318.9995225 318.9995403 318.9995693 318.9996089 318.9996582 318.9997159 318.9997805 318.9998506 318.9999244 319 319.0000756 319.0001494 319.0002195 319.0002841 319.0003418 319.0003911 319.0004307 319.0004597 319.0004775 319.0004834 319.0002447 319.0002417 319.0002327 319.000218 319.000198 319.000173 319.0001438 319.0001111 319.0000756 319.0000383 319 318.9999617 318.9999244 318.9998889 318.9998562 318.999827 318.999802 318.999782 318.9997673 318.9997583 318.9997553 318.9997583 318.9997673 318.999782 318.999802 318.999827 318.9998562 318.9998889 318.9999244 318.9999617 319 319.0000383 319.0000756 319.0001111 319.0001438 319.000173 319.000198 319.000218 319.0002327 319.0002417 319.0002447 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 318.9997553 318.9997583 318.9997673 318.999782 318.999802 318.999827 318.9998562 318.9998889 318.9999244 318.9999617 319 319.0000383 319.0000756 319.0001111 319.0001438 319.000173 319.000198 319.000218 319.0002327 319.0002417 319.0002447 319.0002417 319.0002327 319.000218 319.000198 319.000173 319.0001438 319.0001111 319.0000756 319.0000383 319 318.9999617 318.9999244 318.9998889 318.9998562 318.999827 318.999802 318.999782 318.9997673 318.9997583 318.9997553 318.9995166 318.9995225 318.9995403 318.9995693 318.9996089 318.9996582 318.9997159 318.9997805 318.9998506 318.9999244 319 319.0000756 319.0001494 319.0002195 319.0002841 319.0003418 319.0003911 319.0004307 319.0004597 319.0004775 319.0004834 319.0004775 319.0004597 319.0004307 319.0003911 319.0003418 319.0002841 319.0002195 319.0001494 319.0000756 319 318.9999244 318.9998506 318.9997805 318.9997159 318.9996582 318.9996089 318.9995693 318.9995403 318.9995225 318.9995166 318.9992898 318.9992985 318.9993246 318.9993672 318.9994254 318.9994978 318.9995826 318.9996776 318.9997805 318.9998889 319 319.0001111 319.0002195 319.0003224 319.0004174 319.0005022 319.0005746 319.0006328 319.0006754 319.0007015 319.0007102 319.0007015 319.0006754 319.0006328 319.0005746 319.0005022 319.0004174 319.0003224 319.0002195 319.0001111 319 318.9998889 318.9997805 318.9996776 318.9995826 318.9994978 318.9994254 318.9993672 318.9993246 318.9992985 318.9992898 318.9990805 318.9990918 318.9991255 318.9991807 318.9992561 318.9993498 318.9994595 318.9995826 318.9997159 318.9998562 319 319.0001438 319.0002841 319.0004174 319.0005405 319.0006502 319.0007439 319.0008193 319.0008745 319.0009082 319.0009195 319.0009082 319.0008745 319.0008193 319.0007439 319.0006502 319.0005405 319.0004174 319.0002841 319.0001438 319 318.9998562 318.9997159 318.9995826 318.9994595 318.9993498 318.9992561 318.9991807 318.9991255 318.9990918 318.9990805 318.9988938 318.9989075 318.998948 318.9990144 318.9991051 318.9992178 318.9993498 318.9994978 318.9996582 318.999827 319 319.000173 319.0003418 319.0005022 319.0006502 319.0007822 319.0008949 319.0009856 319.001052 319.0010925 319.0011062 319.0010925 319.001052 319.0009856 319.0008949 319.0007822 319.0006502 319.0005022 319.0003418 319.000173 319 318.999827 318.9996582 318.9994978 318.9993498 318.9992178 318.9991051 318.9990144 318.998948 318.9989075 318.9988938 318.9987344 318.99875 318.9987964 318.9988724 318.9989761 318.9991051 318.9992561 318.9994254 318.9996089 318.999802 319 319.000198 319.0003911 319.0005746 319.0007439 319.0008949 319.0010239 319.0011276 319.0012036 319.00125 319.0012656 319.00125 319.0012036 319.0011276 319.0010239 319.0008949 319.0007439 319.0005746 319.0003911 319.000198 319 318.999802 318.9996089 318.9994254 318.9992561 318.9991051 318.9989761 318.9988724 318.9987964 318.99875 318.9987344 318.9986062 318.9986233 318.9986744 318.9987581 318.9988724 318.9990144 318.9991807 318.9993672 318.9995693 318.999782 319 319.000218 319.0004307 319.0006328 319.0008193 319.0009856 319.0011276 319.0012419 319.0013256 319.0013767 319.0013938 319.0013767 319.0013256 319.0012419 319.0011276 319.0009856 319.0008193 319.0006328 319.0004307 319.000218 319 318.999782 318.9995693 318.9993672 318.9991807 318.9990144 318.9988724 318.9987581 318.9986744 318.9986233 318.9986062 318.9985122 318.9985305 318.998585 318.9986744 318.9987964 318.998948 318.9991255 318.9993246 318.9995403 318.9997673 319 319.0002327 319.0004597 319.0006754 319.0008745 319.001052 319.0012036 319.0013256 319.001415 319.0014695 319.0014878 319.0014695 319.001415 319.0013256 319.0012036 319.001052 319.0008745 319.0006754 319.0004597 319.0002327 319 318.9997673 318.9995403 318.9993246 318.9991255 318.998948 318.9987964 318.9986744 318.998585 318.9985305 318.9985122 318.9984549 318.9984739 318.9985305 318.9986233 318.99875 318.9989075 318.9990918 318.9992985 318.9995225 318.9997583 319 319.0002417 319.0004775 319.0007015 319.0009082 319.0010925 319.00125 319.0013767 319.0014695 319.0015261 319.0015451 319.0015261 319.0014695 319.0013767 319.00125 319.0010925 319.0009082 319.0007015 319.0004775 319.0002417 319 318.9997583 318.9995225 318.9992985 318.9990918 318.9989075 318.99875 318.9986233 318.9985305 318.9984739 318.9984549 318.9984357 318.9984549 318.9985122 318.9986062 318.9987344 318.9988938 318.9990805 318.9992898 318.9995166 318.9997553 319 319.0002447 319.0004834 319.0007102 319.0009195 319.0011062 319.0012656 319.0013938 319.0014878 319.0015451 319.0015643 319.0015451 319.0014878 319.0013938 319.0012656 319.0011062 319.0009195 319.0007102 319.0004834 319.0002447 319 318.9997553 318.9995166 318.9992898 318.9990805 318.9988938 318.9987344 318.9986062 318.9985122 318.9984549 318.9984357 318.9984549 318.9984739 318.9985305 318.9986233 318.99875 318.9989075 318.9990918 318.9992985 318.9995225 318.9997583 319 319.0002417 319.0004775 319.0007015 319.0009082 319.0010925 319.00125 319.0013767 319.0014695 319.0015261 319.0015451 319.0015261 319.0014695 319.0013767 319.00125 319.0010925 319.0009082 319.0007015 319.0004775 319.0002417 319 318.9997583 318.9995225 318.9992985 318.9990918 318.9989075 318.99875 318.9986233 318.9985305 318.9984739 318.9984549 318.9985122 318.9985305 318.998585 318.9986744 318.9987964 318.998948 318.9991255 318.9993246 318.9995403 318.9997673 319 319.0002327 319.0004597 319.0006754 319.0008745 319.001052 319.0012036 319.0013256 319.001415 319.0014695 319.0014878 319.0014695 319.001415 319.0013256 319.0012036 319.001052 319.0008745 319.0006754 319.0004597 319.0002327 319 318.9997673 318.9995403 318.9993246 318.9991255 318.998948 318.9987964 318.9986744 318.998585 318.9985305 318.9985122 318.9986062 318.9986233 318.9986744 318.9987581 318.9988724 318.9990144 318.9991807 318.9993672 318.9995693 318.999782 319 319.000218 319.0004307 319.0006328 319.0008193 319.0009856 319.0011276 319.0012419 319.0013256 319.0013767 319.0013938 319.0013767 319.0013256 319.0012419 319.0011276 319.0009856 319.0008193 319.0006328 319.0004307 319.000218 319 318.999782 318.9995693 318.9993672 318.9991807 318.9990144 318.9988724 318.9987581 318.9986744 318.9986233 318.9986062 318.9987344 318.99875 318.9987964 318.9988724 318.9989761 318.9991051 318.9992561 318.9994254 318.9996089 318.999802 319 319.000198 319.0003911 319.0005746 319.0007439 319.0008949 319.0010239 319.0011276 319.0012036 319.00125 319.0012656 319.00125 319.0012036 319.0011276 319.0010239 319.0008949 319.0007439 319.0005746 319.0003911 319.000198 319 318.999802 318.9996089 318.9994254 318.9992561 318.9991051 318.9989761 318.9988724 318.9987964 318.99875 318.9987344 318.9988938 318.9989075 318.998948 318.9990144 318.9991051 318.9992178 318.9993498 318.9994978 318.9996582 318.999827 319 319.000173 319.0003418 319.0005022 319.0006502 319.0007822 319.0008949 319.0009856 319.001052 319.0010925 319.0011062 319.0010925 319.001052 319.0009856 319.0008949 319.0007822 319.0006502 319.0005022 319.0003418 319.000173 319 318.999827 318.9996582 318.9994978 318.9993498 318.9992178 318.9991051 318.9990144 318.998948 318.9989075 318.9988938 318.9990805 318.9990918 318.9991255 318.9991807 318.9992561 318.9993498 318.9994595 318.9995826 318.9997159 318.9998562 319 319.0001438 319.0002841 319.0004174 319.0005405 319.0006502 319.0007439 319.0008193 319.0008745 319.0009082 319.0009195 319.0009082 319.0008745 319.0008193 319.0007439 319.0006502 319.0005405 319.0004174 319.0002841 319.0001438 319 318.9998562 318.9997159 318.9995826 318.9994595 318.9993498 318.9992561 318.9991807 318.9991255 318.9990918 318.9990805 318.9992898 318.9992985 318.9993246 318.9993672 318.9994254 318.9994978 318.9995826 318.9996776 318.9997805 318.9998889 319 319.0001111 319.0002195 319.0003224 319.0004174 319.0005022 319.0005746 319.0006328 319.0006754 319.0007015 319.0007102 319.0007015 319.0006754 319.0006328 319.0005746 319.0005022 319.0004174 319.0003224 319.0002195 319.0001111 319 318.9998889 318.9997805 318.9996776 318.9995826 318.9994978 318.9994254 318.9993672 318.9993246 318.9992985 318.9992898 318.9995166 318.9995225 318.9995403 318.9995693 318.9996089 318.9996582 318.9997159 318.9997805 318.9998506 318.9999244 319 319.0000756 319.0001494 319.0002195 319.0002841 319.0003418 319.0003911 319.0004307 319.0004597 319.0004775 319.0004834 319.0004775 319.0004597 319.0004307 319.0003911 319.0003418 319.0002841 319.0002195 319.0001494 319.0000756 319 318.9999244 318.9998506 318.9997805 318.9997159 318.9996582 318.9996089 318.9995693 318.9995403 318.9995225 318.9995166 318.9997553 318.9997583 318.9997673 318.999782 318.999802 318.999827 318.9998562 318.9998889 318.9999244 318.9999617 319 319.0000383 319.0000756 319.0001111 319.0001438 319.000173 319.000198 319.000218 319.0002327 319.0002417 319.0002447 319.0002417 319.0002327 319.000218 319.000198 319.000173 319.0001438 319.0001111 319.0000756 319.0000383 319 318.9999617 318.9999244 318.9998889 318.9998562 318.999827 318.999802 318.999782 318.9997673 318.9997583 318.9997553 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319 319.0002447 319.0002417 319.0002327 319.000218 319.000198 319.000173 319.0001438 319.0001111 319.0000756 319.0000383 319 318.9999617 318.9999244 318.9998889 318.9998562 318.999827 318.999802 318.999782 318.9997673 318.9997583 318.9997553 318.9997583 318.9997673 318.999782 318.999802 318.999827 318.9998562 318.9998889 318.9999244 318.9999617 319 319.0000383 319.0000756 319.0001111 319.0001438 319.000173 319.000198 319.000218 319.0002327 319.0002417 319.0002447 319.0004834 319.0004775 319.0004597 319.0004307 319.0003911 319.0003418 319.0002841 319.0002195 319.0001494 319.0000756 319 318.9999244 318.9998506 318.9997805 318.9997159 318.9996582 318.9996089 318.9995693 318.9995403 318.9995225 318.9995166 318.9995225 318.9995403 318.9995693 318.9996089 318.9996582 318.9997159 318.9997805 318.9998506 318.9999244 319 319.0000756 319.0001494 319.0002195 319.0002841 319.0003418 319.0003911 319.0004307 319.0004597 319.0004775 319.0004834 319.0007102 319.0007015 319.0006754 319.0006328 319.0005746 319.0005022 319.0004174 319.0003224 319.0002195 319.0001111 319 318.9998889 318.9997805 318.9996776 318.9995826 318.9994978 318.9994254 318.9993672 318.9993246 318.9992985 318.9992898 318.9992985 318.9993246 318.9993672 318.9994254 318.9994978 318.9995826 318.9996776 318.9997805 318.9998889 319 319.0001111 319.0002195 319.0003224 319.0004174 319.0005022 319.0005746 319.0006328 319.0006754 319.0007015 319.0007102 319.0009195 319.0009082 319.0008745 319.0008193 319.0007439 319.0006502 319.0005405 319.0004174 319.0002841 319.0001438 319 318.9998562 318.9997159 318.9995826 318.9994595 318.9993498 318.9992561 318.9991807 318.9991255 318.9990918 318.9990805 318.9990918 318.9991255 318.9991807 318.9992561 318.9993498 318.9994595 318.9995826 318.9997159 318.9998562 319 319.0001438 319.0002841 319.0004174 319.0005405 319.0006502 319.0007439 319.0008193 319.0008745 319.0009082 319.0009195 319.0011062 319.0010925 319.001052 319.0009856 319.0008949 319.0007822 319.0006502 319.0005022 319.0003418 319.000173 319 318.999827 318.9996582 318.9994978 318.9993498 318.9992178 318.9991051 318.9990144 318.998948 318.9989075 318.9988938 318.9989075 318.998948 318.9990144 318.9991051 318.9992178 318.9993498 318.9994978 318.9996582 318.999827 319 319.000173 319.0003418 319.0005022 319.0006502 319.0007822 319.0008949 319.0009856 319.001052 319.0010925 319.0011062 319.0012656 319.00125 319.0012036 319.0011276 319.0010239 319.0008949 319.0007439 319.0005746 319.0003911 319.000198 319 318.999802 318.9996089 318.9994254 318.9992561 318.9991051 318.9989761 318.9988724 318.9987964 318.99875 318.9987344 318.99875 318.9987964 318.9988724 318.9989761 318.9991051 318.9992561 318.9994254 318.9996089 318.999802 319 319.000198 319.0003911 319.0005746 319.0007439 319.0008949 319.0010239 319.0011276 319.0012036 319.00125 319.0012656 319.0013938 319.0013767 319.0013256 319.0012419 319.0011276 319.0009856 319.0008193 319.0006328 319.0004307 319.000218 319 318.999782 318.9995693 318.9993672 318.9991807 318.9990144 318.9988724 318.9987581 318.9986744 318.9986233 318.9986062 318.9986233 318.9986744 318.9987581 318.9988724 318.9990144 318.9991807 318.9993672 318.9995693 318.999782 319 319.000218 319.0004307 319.0006328 319.0008193 319.0009856 319.0011276 319.0012419 319.0013256 319.0013767 319.0013938 319.0014878 319.0014695 319.001415 319.0013256 319.0012036 319.001052 319.0008745 319.0006754 319.0004597 319.0002327 319 318.9997673 318.9995403 318.9993246 318.9991255 318.998948 318.9987964 318.9986744 318.998585 318.9985305 318.9985122 318.9985305 318.998585 318.9986744 318.9987964 318.998948 318.9991255 318.9993246 318.9995403 318.9997673 319 319.0002327 319.0004597 319.0006754 319.0008745 319.001052 319.0012036 319.0013256 319.001415 319.0014695 319.0014878 319.0015451 319.0015261 319.0014695 319.0013767 319.00125 319.0010925 319.0009082 319.0007015 319.0004775 319.0002417 319 318.9997583 318.9995225 318.9992985 318.9990918 318.9989075 318.99875 318.9986233 318.9985305 318.9984739 318.9984549 318.9984739 318.9985305 318.9986233 318.99875 318.9989075 318.9990918 318.9992985 318.9995225 318.9997583 319 319.0002417 319.0004775 319.0007015 319.0009082 319.0010925 319.00125 319.0013767 319.0014695 319.0015261 319.0015451 319.0015643 319.0015451 319.0014878 319.0013938 319.0012656 319.0011062 319.0009195 319.0007102 319.0004834 319.0002447 319 318.9997553 318.9995166 318.9992898 318.9990805 318.9988938 318.9987344 318.9986062 318.9985122 318.9984549 318.9984357 318.9984549 318.9985122 318.9986062 318.9987344 318.9988938 318.9990805 318.9992898 318.9995166 318.9997553 319 319.0002447 319.0004834 319.0007102 319.0009195 319.0011062 319.0012656 319.0013938 319.0014878 319.0015451 319.0015643 318.0030902 318.0030521 318.0029389 318.0027534 318.0025 318.0021851 318.0018164 318.0014029 318.0009549 318.0004834 318 317.9995166 317.9990451 317.9985971 317.9981836 317.9978149 317.9975 317.9972466 317.9970611 317.9969479 317.9969098 317.9969479 317.9970611 317.9972466 317.9975 317.9978149 317.9981836 317.9985971 317.9990451 317.9995166 318 318.0004834 318.0009549 318.0014029 318.0018164 318.0021851 318.0025 318.0027534 318.0029389 318.0030521 318.0030902 318.0030521 318.0030145 318.0029027 318.0027195 318.0024692 318.0021582 318.001794 318.0013856 318.0009432 318.0004775 318 317.9995225 317.9990568 317.9986144 317.998206 317.9978418 317.9975308 317.9972805 317.9970973 317.9969855 317.9969479 317.9969855 317.9970973 317.9972805 317.9975308 317.9978418 317.998206 317.9986144 317.9990568 317.9995225 318 318.0004775 318.0009432 318.0013856 318.001794 318.0021582 318.0024692 318.0027195 318.0029027 318.0030145 318.0030521 318.0029389 318.0029027 318.0027951 318.0026186 318.0023776 318.0020781 318.0017275 318.0013342 318.0009082 318.0004597 318 317.9995403 317.9990918 317.9986658 317.9982725 317.9979219 317.9976224 317.9973814 317.9972049 317.9970973 317.9970611 317.9970973 317.9972049 317.9973814 317.9976224 317.9979219 317.9982725 317.9986658 317.9990918 317.9995403 318 318.0004597 318.0009082 318.0013342 318.0017275 318.0020781 318.0023776 318.0026186 318.0027951 318.0029027 318.0029389 318.0027534 318.0027195 318.0026186 318.0024533 318.0022275 318.0019469 318.0016184 318.00125 318.0008508 318.0004307 318 317.9995693 317.9991492 317.99875 317.9983816 317.9980531 317.9977725 317.9975467 317.9973814 317.9972805 317.9972466 317.9972805 317.9973814 317.9975467 317.9977725 317.9980531 317.9983816 317.99875 317.9991492 317.9995693 318 318.0004307 318.0008508 318.00125 318.0016184 318.0019469 318.0022275 318.0024533 318.0026186 318.0027195 318.0027534 318.0025 318.0024692 318.0023776 318.0022275 318.0020225 318.0017678 318.0014695 318.001135 318.0007725 318.0003911 318 317.9996089 317.9992275 317.998865 317.9985305 317.9982322 317.9979775 317.9977725 317.9976224 317.9975308 317.9975 317.9975308 317.9976224 317.9977725 317.9979775 317.9982322 317.9985305 317.998865 317.9992275 317.9996089 318 318.0003911 318.0007725 318.001135 318.0014695 318.0017678 318.0020225 318.0022275 318.0023776 318.0024692 318.0025 318.0021851 318.0021582 318.0020781 318.0019469 318.0017678 318.0015451 318.0012844 318.000992 318.0006752 318.0003418 318 317.9996582 317.9993248 317.999008 317.9987156 317.9984549 317.9982322 317.9980531 317.9979219 317.9978418 317.9978149 317.9978418 317.9979219 317.9980531 317.9982322 317.9984549 317.9987156 317.999008 317.9993248 317.9996582 318 318.0003418 318.0006752 318.000992 318.0012844 318.0015451 318.0017678 318.0019469 318.0020781 318.0021582 318.0021851 318.0018164 318.001794 318.0017275 318.0016184 318.0014695 318.0012844 318.0010676 318.0008246 318.0005613 318.0002841 318 317.9997159 317.9994387 317.9991754 317.9989324 317.9987156 317.9985305 317.9983816 317.9982725 317.998206 317.9981836 317.998206 317.9982725 317.9983816 317.9985305 317.9987156 317.9989324 317.9991754 317.9994387 317.9997159 318 318.0002841 318.0005613 318.0008246 318.0010676 318.0012844 318.0014695 318.0016184 318.0017275 318.001794 318.0018164 318.0014029 318.0013856 318.0013342 318.00125 318.001135 318.000992 318.0008246 318.0006369 318.0004335 318.0002195 318 317.9997805 317.9995665 317.9993631 317.9991754 317.999008 317.998865 317.99875 317.9986658 317.9986144 317.9985971 317.9986144 317.9986658 317.99875 317.998865 317.999008 317.9991754 317.9993631 317.9995665 317.9997805 318 318.0002195 318.0004335 318.0006369 318.0008246 318.000992 318.001135 318.00125 318.0013342 318.0013856 318.0014029 318.0009549 318.0009432 318.0009082 318.0008508 318.0007725 318.0006752 318.0005613 318.0004335 318.0002951 318.0001494 318 317.9998506 317.9997049 317.9995665 317.9994387 317.9993248 317.9992275 317.9991492 317.9990918 317.9990568 317.9990451 317.9990568 317.9990918 317.9991492 317.9992275 317.9993248 317.9994387 317.9995665 317.9997049 317.9998506 318 318.0001494 318.0002951 318.0004335 318.0005613 318.0006752 318.0007725 318.0008508 318.0009082 318.0009432 318.0009549 318.0004834 318.0004775 318.0004597 318.0004307 318.0003911 318.0003418 318.0002841 318.0002195 318.0001494 318.0000756 318 317.9999244 317.9998506 317.9997805 317.9997159 317.9996582 317.9996089 317.9995693 317.9995403 317.9995225 317.9995166 317.9995225 317.9995403 317.9995693 317.9996089 317.9996582 317.9997159 317.9997805 317.9998506 317.9999244 318 318.0000756 318.0001494 318.0002195 318.0002841 318.0003418 318.0003911 318.0004307 318.0004597 318.0004775 318.0004834 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 317.9995166 317.9995225 317.9995403 317.9995693 317.9996089 317.9996582 317.9997159 317.9997805 317.9998506 317.9999244 318 318.0000756 318.0001494 318.0002195 318.0002841 318.0003418 318.0003911 318.0004307 318.0004597 318.0004775 318.0004834 318.0004775 318.0004597 318.0004307 318.0003911 318.0003418 318.0002841 318.0002195 318.0001494 318.0000756 318 317.9999244 317.9998506 317.9997805 317.9997159 317.9996582 317.9996089 317.9995693 317.9995403 317.9995225 317.9995166 317.9990451 317.9990568 317.9990918 317.9991492 317.9992275 317.9993248 317.9994387 317.9995665 317.9997049 317.9998506 318 318.0001494 318.0002951 318.0004335 318.0005613 318.0006752 318.0007725 318.0008508 318.0009082 318.0009432 318.0009549 318.0009432 318.0009082 318.0008508 318.0007725 318.0006752 318.0005613 318.0004335 318.0002951 318.0001494 318 317.9998506 317.9997049 317.9995665 317.9994387 317.9993248 317.9992275 317.9991492 317.9990918 317.9990568 317.9990451 317.9985971 317.9986144 317.9986658 317.99875 317.998865 317.999008 317.9991754 317.9993631 317.9995665 317.9997805 318 318.0002195 318.0004335 318.0006369 318.0008246 318.000992 318.001135 318.00125 318.0013342 318.0013856 318.0014029 318.0013856 318.0013342 318.00125 318.001135 318.000992 318.0008246 318.0006369 318.0004335 318.0002195 318 317.9997805 317.9995665 317.9993631 317.9991754 317.999008 317.998865 317.99875 317.9986658 317.9986144 317.9985971 317.9981836 317.998206 317.9982725 317.9983816 317.9985305 317.9987156 317.9989324 317.9991754 317.9994387 317.9997159 318 318.0002841 318.0005613 318.0008246 318.0010676 318.0012844 318.0014695 318.0016184 318.0017275 318.001794 318.0018164 318.001794 318.0017275 318.0016184 318.0014695 318.0012844 318.0010676 318.0008246 318.0005613 318.0002841 318 317.9997159 317.9994387 317.9991754 317.9989324 317.9987156 317.9985305 317.9983816 317.9982725 317.998206 317.9981836 317.9978149 317.9978418 317.9979219 317.9980531 317.9982322 317.9984549 317.9987156 317.999008 317.9993248 317.9996582 318 318.0003418 318.0006752 318.000992 318.0012844 318.0015451 318.0017678 318.0019469 318.0020781 318.0021582 318.0021851 318.0021582 318.0020781 318.0019469 318.0017678 318.0015451 318.0012844 318.000992 318.0006752 318.0003418 318 317.9996582 317.9993248 317.999008 317.9987156 317.9984549 317.9982322 317.9980531 317.9979219 317.9978418 317.9978149 317.9975 317.9975308 317.9976224 317.9977725 317.9979775 317.9982322 317.9985305 317.998865 317.9992275 317.9996089 318 318.0003911 318.0007725 318.001135 318.0014695 318.0017678 318.0020225 318.0022275 318.0023776 318.0024692 318.0025 318.0024692 318.0023776 318.0022275 318.0020225 318.0017678 318.0014695 318.001135 318.0007725 318.0003911 318 317.9996089 317.9992275 317.998865 317.9985305 317.9982322 317.9979775 317.9977725 317.9976224 317.9975308 317.9975 317.9972466 317.9972805 317.9973814 317.9975467 317.9977725 317.9980531 317.9983816 317.99875 317.9991492 317.9995693 318 318.0004307 318.0008508 318.00125 318.0016184 318.0019469 318.0022275 318.0024533 318.0026186 318.0027195 318.0027534 318.0027195 318.0026186 318.0024533 318.0022275 318.0019469 318.0016184 318.00125 318.0008508 318.0004307 318 317.9995693 317.9991492 317.99875 317.9983816 317.9980531 317.9977725 317.9975467 317.9973814 317.9972805 317.9972466 317.9970611 317.9970973 317.9972049 317.9973814 317.9976224 317.9979219 317.9982725 317.9986658 317.9990918 317.9995403 318 318.0004597 318.0009082 318.0013342 318.0017275 318.0020781 318.0023776 318.0026186 318.0027951 318.0029027 318.0029389 318.0029027 318.0027951 318.0026186 318.0023776 318.0020781 318.0017275 318.0013342 318.0009082 318.0004597 318 317.9995403 317.9990918 317.9986658 317.9982725 317.9979219 317.9976224 317.9973814 317.9972049 317.9970973 317.9970611 317.9969479 317.9969855 317.9970973 317.9972805 317.9975308 317.9978418 317.998206 317.9986144 317.9990568 317.9995225 318 318.0004775 318.0009432 318.0013856 318.001794 318.0021582 318.0024692 318.0027195 318.0029027 318.0030145 318.0030521 318.0030145 318.0029027 318.0027195 318.0024692 318.0021582 318.001794 318.0013856 318.0009432 318.0004775 318 317.9995225 317.9990568 317.9986144 317.998206 317.9978418 317.9975308 317.9972805 317.9970973 317.9969855 317.9969479 317.9969098 317.9969479 317.9970611 317.9972466 317.9975 317.9978149 317.9981836 317.9985971 317.9990451 317.9995166 318 318.0004834 318.0009549 318.0014029 318.0018164 318.0021851 318.0025 318.0027534 318.0029389 318.0030521 318.0030902 318.0030521 318.0029389 318.0027534 318.0025 318.0021851 318.0018164 318.0014029 318.0009549 318.0004834 318 317.9995166 317.9990451 317.9985971 317.9981836 317.9978149 317.9975 317.9972466 317.9970611 317.9969479 317.9969098 317.9969479 317.9969855 317.9970973 317.9972805 317.9975308 317.9978418 317.998206 317.9986144 317.9990568 317.9995225 318 318.0004775 318.0009432 318.0013856 318.001794 318.0021582 318.0024692 318.0027195 318.0029027 318.0030145 318.0030521 318.0030145 318.0029027 318.0027195 318.0024692 318.0021582 318.001794 318.0013856 318.0009432 318.0004775 318 317.9995225 317.9990568 317.9986144 317.998206 317.9978418 317.9975308 317.9972805 317.9970973 317.9969855 317.9969479 317.9970611 317.9970973 317.9972049 317.9973814 317.9976224 317.9979219 317.9982725 317.9986658 317.9990918 317.9995403 318 318.0004597 318.0009082 318.0013342 318.0017275 318.0020781 318.0023776 318.0026186 318.0027951 318.0029027 318.0029389 318.0029027 318.0027951 318.0026186 318.0023776 318.0020781 318.0017275 318.0013342 318.0009082 318.0004597 318 317.9995403 317.9990918 317.9986658 317.9982725 317.9979219 317.9976224 317.9973814 317.9972049 317.9970973 317.9970611 317.9972466 317.9972805 317.9973814 317.9975467 317.9977725 317.9980531 317.9983816 317.99875 317.9991492 317.9995693 318 318.0004307 318.0008508 318.00125 318.0016184 318.0019469 318.0022275 318.0024533 318.0026186 318.0027195 318.0027534 318.0027195 318.0026186 318.0024533 318.0022275 318.0019469 318.0016184 318.00125 318.0008508 318.0004307 318 317.9995693 317.9991492 317.99875 317.9983816 317.9980531 317.9977725 317.9975467 317.9973814 317.9972805 317.9972466 317.9975 317.9975308 317.9976224 317.9977725 317.9979775 317.9982322 317.9985305 317.998865 317.9992275 317.9996089 318 318.0003911 318.0007725 318.001135 318.0014695 318.0017678 318.0020225 318.0022275 318.0023776 318.0024692 318.0025 318.0024692 318.0023776 318.0022275 318.0020225 318.0017678 318.0014695 318.001135 318.0007725 318.0003911 318 317.9996089 317.9992275 317.998865 317.9985305 317.9982322 317.9979775 317.9977725 317.9976224 317.9975308 317.9975 317.9978149 317.9978418 317.9979219 317.9980531 317.9982322 317.9984549 317.9987156 317.999008 317.9993248 317.9996582 318 318.0003418 318.0006752 318.000992 318.0012844 318.0015451 318.0017678 318.0019469 318.0020781 318.0021582 318.0021851 318.0021582 318.0020781 318.0019469 318.0017678 318.0015451 318.0012844 318.000992 318.0006752 318.0003418 318 317.9996582 317.9993248 317.999008 317.9987156 317.9984549 317.9982322 317.9980531 317.9979219 317.9978418 317.9978149 317.9981836 317.998206 317.9982725 317.9983816 317.9985305 317.9987156 317.9989324 317.9991754 317.9994387 317.9997159 318 318.0002841 318.0005613 318.0008246 318.0010676 318.0012844 318.0014695 318.0016184 318.0017275 318.001794 318.0018164 318.001794 318.0017275 318.0016184 318.0014695 318.0012844 318.0010676 318.0008246 318.0005613 318.0002841 318 317.9997159 317.9994387 317.9991754 317.9989324 317.9987156 317.9985305 317.9983816 317.9982725 317.998206 317.9981836 317.9985971 317.9986144 317.9986658 317.99875 317.998865 317.999008 317.9991754 317.9993631 317.9995665 317.9997805 318 318.0002195 318.0004335 318.0006369 318.0008246 318.000992 318.001135 318.00125 318.0013342 318.0013856 318.0014029 318.0013856 318.0013342 318.00125 318.001135 318.000992 318.0008246 318.0006369 318.0004335 318.0002195 318 317.9997805 317.9995665 317.9993631 317.9991754 317.999008 317.998865 317.99875 317.9986658 317.9986144 317.9985971 317.9990451 317.9990568 317.9990918 317.9991492 317.9992275 317.9993248 317.9994387 317.9995665 317.9997049 317.9998506 318 318.0001494 318.0002951 318.0004335 318.0005613 318.0006752 318.0007725 318.0008508 318.0009082 318.0009432 318.0009549 318.0009432 318.0009082 318.0008508 318.0007725 318.0006752 318.0005613 318.0004335 318.0002951 318.0001494 318 317.9998506 317.9997049 317.9995665 317.9994387 317.9993248 317.9992275 317.9991492 317.9990918 317.9990568 317.9990451 317.9995166 317.9995225 317.9995403 317.9995693 317.9996089 317.9996582 317.9997159 317.9997805 317.9998506 317.9999244 318 318.0000756 318.0001494 318.0002195 318.0002841 318.0003418 318.0003911 318.0004307 318.0004597 318.0004775 318.0004834 318.0004775 318.0004597 318.0004307 318.0003911 318.0003418 318.0002841 318.0002195 318.0001494 318.0000756 318 317.9999244 317.9998506 317.9997805 317.9997159 317.9996582 317.9996089 317.9995693 317.9995403 317.9995225 317.9995166 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318 318.0004834 318.0004775 318.0004597 318.0004307 318.0003911 318.0003418 318.0002841 318.0002195 318.0001494 318.0000756 318 317.9999244 317.9998506 317.9997805 317.9997159 317.9996582 317.9996089 317.9995693 317.9995403 317.9995225 317.9995166 317.9995225 317.9995403 317.9995693 317.9996089 317.9996582 317.9997159 317.9997805 317.9998506 317.9999244 318 318.0000756 318.0001494 318.0002195 318.0002841 318.0003418 318.0003911 318.0004307 318.0004597 318.0004775 318.0004834 318.0009549 318.0009432 318.0009082 318.0008508 318.0007725 318.0006752 318.0005613 318.0004335 318.0002951 318.0001494 318 317.9998506 317.9997049 317.9995665 317.9994387 317.9993248 317.9992275 317.9991492 317.9990918 317.9990568 317.9990451 317.9990568 317.9990918 317.9991492 317.9992275 317.9993248 317.9994387 317.9995665 317.9997049 317.9998506 318 318.0001494 318.0002951 318.0004335 318.0005613 318.0006752 318.0007725 318.0008508 318.0009082 318.0009432 318.0009549 318.0014029 318.0013856 318.0013342 318.00125 318.001135 318.000992 318.0008246 318.0006369 318.0004335 318.0002195 318 317.9997805 317.9995665 317.9993631 317.9991754 317.999008 317.998865 317.99875 317.9986658 317.9986144 317.9985971 317.9986144 317.9986658 317.99875 317.998865 317.999008 317.9991754 317.9993631 317.9995665 317.9997805 318 318.0002195 318.0004335 318.0006369 318.0008246 318.000992 318.001135 318.00125 318.0013342 318.0013856 318.0014029 318.0018164 318.001794 318.0017275 318.0016184 318.0014695 318.0012844 318.0010676 318.0008246 318.0005613 318.0002841 318 317.9997159 317.9994387 317.9991754 317.9989324 317.9987156 317.9985305 317.9983816 317.9982725 317.998206 317.9981836 317.998206 317.9982725 317.9983816 317.9985305 317.9987156 317.9989324 317.9991754 317.9994387 317.9997159 318 318.0002841 318.0005613 318.0008246 318.0010676 318.0012844 318.0014695 318.0016184 318.0017275 318.001794 318.0018164 318.0021851 318.0021582 318.0020781 318.0019469 318.0017678 318.0015451 318.0012844 318.000992 318.0006752 318.0003418 318 317.9996582 317.9993248 317.999008 317.9987156 317.9984549 317.9982322 317.9980531 317.9979219 317.9978418 317.9978149 317.9978418 317.9979219 317.9980531 317.9982322 317.9984549 317.9987156 317.999008 317.9993248 317.9996582 318 318.0003418 318.0006752 318.000992 318.0012844 318.0015451 318.0017678 318.0019469 318.0020781 318.0021582 318.0021851 318.0025 318.0024692 318.0023776 318.0022275 318.0020225 318.0017678 318.0014695 318.001135 318.0007725 318.0003911 318 317.9996089 317.9992275 317.998865 317.9985305 317.9982322 317.9979775 317.9977725 317.9976224 317.9975308 317.9975 317.9975308 317.9976224 317.9977725 317.9979775 317.9982322 317.9985305 317.998865 317.9992275 317.9996089 318 318.0003911 318.0007725 318.001135 318.0014695 318.0017678 318.0020225 318.0022275 318.0023776 318.0024692 318.0025 318.0027534 318.0027195 318.0026186 318.0024533 318.0022275 318.0019469 318.0016184 318.00125 318.0008508 318.0004307 318 317.9995693 317.9991492 317.99875 317.9983816 317.9980531 317.9977725 317.9975467 317.9973814 317.9972805 317.9972466 317.9972805 317.9973814 317.9975467 317.9977725 317.9980531 317.9983816 317.99875 317.9991492 317.9995693 318 318.0004307 318.0008508 318.00125 318.0016184 318.0019469 318.0022275 318.0024533 318.0026186 318.0027195 318.0027534 318.0029389 318.0029027 318.0027951 318.0026186 318.0023776 318.0020781 318.0017275 318.0013342 318.0009082 318.0004597 318 317.9995403 317.9990918 317.9986658 317.9982725 317.9979219 317.9976224 317.9973814 317.9972049 317.9970973 317.9970611 317.9970973 317.9972049 317.9973814 317.9976224 317.9979219 317.9982725 317.9986658 317.9990918 317.9995403 318 318.0004597 318.0009082 318.0013342 318.0017275 318.0020781 318.0023776 318.0026186 318.0027951 318.0029027 318.0029389 318.0030521 318.0030145 318.0029027 318.0027195 318.0024692 318.0021582 318.001794 318.0013856 318.0009432 318.0004775 318 317.9995225 317.9990568 317.9986144 317.998206 317.9978418 317.9975308 317.9972805 317.9970973 317.9969855 317.9969479 317.9969855 317.9970973 317.9972805 317.9975308 317.9978418 317.998206 317.9986144 317.9990568 317.9995225 318 318.0004775 318.0009432 318.0013856 318.001794 318.0021582 318.0024692 318.0027195 318.0029027 318.0030145 318.0030521 318.0030902 318.0030521 318.0029389 318.0027534 318.0025 318.0021851 318.0018164 318.0014029 318.0009549 318.0004834 318 317.9995166 317.9990451 317.9985971 317.9981836 317.9978149 317.9975 317.9972466 317.9970611 317.9969479 317.9969098 317.9969479 317.9970611 317.9972466 317.9975 317.9978149 317.9981836 317.9985971 317.9990451 317.9995166 318 318.0004834 318.0009549 318.0014029 318.0018164 318.0021851 318.0025 318.0027534 318.0029389 318.0030521 318.0030902 317.0045399 317.004484 317.0043177 317.0040451 317.0036729 317.0032102 317.0026685 317.0020611 317.0014029 317.0007102 317 316.9992898 316.9985971 316.9979389 316.9973315 316.9967898 316.9963271 316.9959549 316.9956823 316.995516 316.9954601 316.995516 316.9956823 316.9959549 316.9963271 316.9967898 316.9973315 316.9979389 316.9985971 316.9992898 317 317.0007102 317.0014029 317.0020611 317.0026685 317.0032102 317.0036729 317.0040451 317.0043177 317.004484 317.0045399 317.004484 317.0044288 317.0042645 317.0039953 317.0036276 317.0031707 317.0026356 317.0020357 317.0013856 317.0007015 317 316.9992985 316.9986144 316.9979643 316.9973644 316.9968293 316.9963724 316.9960047 316.9957355 316.9955712 316.995516 316.9955712 316.9957355 316.9960047 316.9963724 316.9968293 316.9973644 316.9979643 316.9986144 316.9992985 317 317.0007015 317.0013856 317.0020357 317.0026356 317.0031707 317.0036276 317.0039953 317.0042645 317.0044288 317.004484 317.0043177 317.0042645 317.0041064 317.0038471 317.0034931 317.0030531 317.0025379 317.0019602 317.0013342 317.0006754 317 316.9993246 316.9986658 316.9980398 316.9974621 316.9969469 316.9965069 316.9961529 316.9958936 316.9957355 316.9956823 316.9957355 316.9958936 316.9961529 316.9965069 316.9969469 316.9974621 316.9980398 316.9986658 316.9993246 317 317.0006754 317.0013342 317.0019602 317.0025379 317.0030531 317.0034931 317.0038471 317.0041064 317.0042645 317.0043177 317.0040451 317.0039953 317.0038471 317.0036042 317.0032725 317.0028603 317.0023776 317.0018364 317.00125 317.0006328 317 316.9993672 316.99875 316.9981636 316.9976224 316.9971397 316.9967275 316.9963958 316.9961529 316.9960047 316.9959549 316.9960047 316.9961529 316.9963958 316.9967275 316.9971397 316.9976224 316.9981636 316.99875 316.9993672 317 317.0006328 317.00125 317.0018364 317.0023776 317.0028603 317.0032725 317.0036042 317.0038471 317.0039953 317.0040451 317.0036729 317.0036276 317.0034931 317.0032725 317.0029714 317.0025971 317.0021589 317.0016674 317.001135 317.0005746 317 316.9994254 316.998865 316.9983326 316.9978411 316.9974029 316.9970286 316.9967275 316.9965069 316.9963724 316.9963271 316.9963724 316.9965069 316.9967275 316.9970286 316.9974029 316.9978411 316.9983326 316.998865 316.9994254 317 317.0005746 317.001135 317.0016674 317.0021589 317.0025971 317.0029714 317.0032725 317.0034931 317.0036276 317.0036729 317.0032102 317.0031707 317.0030531 317.0028603 317.0025971 317.00227 317.0018869 317.0014574 317.000992 317.0005022 317 316.9994978 316.999008 316.9985426 316.9981131 316.99773 316.9974029 316.9971397 316.9969469 316.9968293 316.9967898 316.9968293 316.9969469 316.9971397 316.9974029 316.99773 316.9981131 316.9985426 316.999008 316.9994978 317 317.0005022 317.000992 317.0014574 317.0018869 317.00227 317.0025971 317.0028603 317.0030531 317.0031707 317.0032102 317.0026685 317.0026356 317.0025379 317.0023776 317.0021589 317.0018869 317.0015685 317.0012115 317.0008246 317.0004174 317 316.9995826 316.9991754 316.9987885 316.9984315 316.9981131 316.9978411 316.9976224 316.9974621 316.9973644 316.9973315 316.9973644 316.9974621 316.9976224 316.9978411 316.9981131 316.9984315 316.9987885 316.9991754 316.9995826 317 317.0004174 317.0008246 317.0012115 317.0015685 317.0018869 317.0021589 317.0023776 317.0025379 317.0026356 317.0026685 317.0020611 317.0020357 317.0019602 317.0018364 317.0016674 317.0014574 317.0012115 317.0009357 317.0006369 317.0003224 317 316.9996776 316.9993631 316.9990643 316.9987885 316.9985426 316.9983326 316.9981636 316.9980398 316.9979643 316.9979389 316.9979643 316.9980398 316.9981636 316.9983326 316.9985426 316.9987885 316.9990643 316.9993631 316.9996776 317 317.0003224 317.0006369 317.0009357 317.0012115 317.0014574 317.0016674 317.0018364 317.0019602 317.0020357 317.0020611 317.0014029 317.0013856 317.0013342 317.00125 317.001135 317.000992 317.0008246 317.0006369 317.0004335 317.0002195 317 316.9997805 316.9995665 316.9993631 316.9991754 316.999008 316.998865 316.99875 316.9986658 316.9986144 316.9985971 316.9986144 316.9986658 316.99875 316.998865 316.999008 316.9991754 316.9993631 316.9995665 316.9997805 317 317.0002195 317.0004335 317.0006369 317.0008246 317.000992 317.001135 317.00125 317.0013342 317.0013856 317.0014029 317.0007102 317.0007015 317.0006754 317.0006328 317.0005746 317.0005022 317.0004174 317.0003224 317.0002195 317.0001111 317 316.9998889 316.9997805 316.9996776 316.9995826 316.9994978 316.9994254 316.9993672 316.9993246 316.9992985 316.9992898 316.9992985 316.9993246 316.9993672 316.9994254 316.9994978 316.9995826 316.9996776 316.9997805 316.9998889 317 317.0001111 317.0002195 317.0003224 317.0004174 317.0005022 317.0005746 317.0006328 317.0006754 317.0007015 317.0007102 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 316.9992898 316.9992985 316.9993246 316.9993672 316.9994254 316.9994978 316.9995826 316.9996776 316.9997805 316.9998889 317 317.0001111 317.0002195 317.0003224 317.0004174 317.0005022 317.0005746 317.0006328 317.0006754 317.0007015 317.0007102 317.0007015 317.0006754 317.0006328 317.0005746 317.0005022 317.0004174 317.0003224 317.0002195 317.0001111 317 316.9998889 316.9997805 316.9996776 316.9995826 316.9994978 316.9994254 316.9993672 316.9993246 316.9992985 316.9992898 316.9985971 316.9986144 316.9986658 316.99875 316.998865 316.999008 316.9991754 316.9993631 316.9995665 316.9997805 317 317.0002195 317.0004335 317.0006369 317.0008246 317.000992 317.001135 317.00125 317.0013342 317.0013856 317.0014029 317.0013856 317.0013342 317.00125 317.001135 317.000992 317.0008246 317.0006369 317.0004335 317.0002195 317 316.9997805 316.9995665 316.9993631 316.9991754 316.999008 316.998865 316.99875 316.9986658 316.9986144 316.9985971 316.9979389 316.9979643 316.9980398 316.9981636 316.9983326 316.9985426 316.9987885 316.9990643 316.9993631 316.9996776 317 317.0003224 317.0006369 317.0009357 317.0012115 317.0014574 317.0016674 317.0018364 317.0019602 317.0020357 317.0020611 317.0020357 317.0019602 317.0018364 317.0016674 317.0014574 317.0012115 317.0009357 317.0006369 317.0003224 317 316.9996776 316.9993631 316.9990643 316.9987885 316.9985426 316.9983326 316.9981636 316.9980398 316.9979643 316.9979389 316.9973315 316.9973644 316.9974621 316.9976224 316.9978411 316.9981131 316.9984315 316.9987885 316.9991754 316.9995826 317 317.0004174 317.0008246 317.0012115 317.0015685 317.0018869 317.0021589 317.0023776 317.0025379 317.0026356 317.0026685 317.0026356 317.0025379 317.0023776 317.0021589 317.0018869 317.0015685 317.0012115 317.0008246 317.0004174 317 316.9995826 316.9991754 316.9987885 316.9984315 316.9981131 316.9978411 316.9976224 316.9974621 316.9973644 316.9973315 316.9967898 316.9968293 316.9969469 316.9971397 316.9974029 316.99773 316.9981131 316.9985426 316.999008 316.9994978 317 317.0005022 317.000992 317.0014574 317.0018869 317.00227 317.0025971 317.0028603 317.0030531 317.0031707 317.0032102 317.0031707 317.0030531 317.0028603 317.0025971 317.00227 317.0018869 317.0014574 317.000992 317.0005022 317 316.9994978 316.999008 316.9985426 316.9981131 316.99773 316.9974029 316.9971397 316.9969469 316.9968293 316.9967898 316.9963271 316.9963724 316.9965069 316.9967275 316.9970286 316.9974029 316.9978411 316.9983326 316.998865 316.9994254 317 317.0005746 317.001135 317.0016674 317.0021589 317.0025971 317.0029714 317.0032725 317.0034931 317.0036276 317.0036729 317.0036276 317.0034931 317.0032725 317.0029714 317.0025971 317.0021589 317.0016674 317.001135 317.0005746 317 316.9994254 316.998865 316.9983326 316.9978411 316.9974029 316.9970286 316.9967275 316.9965069 316.9963724 316.9963271 316.9959549 316.9960047 316.9961529 316.9963958 316.9967275 316.9971397 316.9976224 316.9981636 316.99875 316.9993672 317 317.0006328 317.00125 317.0018364 317.0023776 317.0028603 317.0032725 317.0036042 317.0038471 317.0039953 317.0040451 317.0039953 317.0038471 317.0036042 317.0032725 317.0028603 317.0023776 317.0018364 317.00125 317.0006328 317 316.9993672 316.99875 316.9981636 316.9976224 316.9971397 316.9967275 316.9963958 316.9961529 316.9960047 316.9959549 316.9956823 316.9957355 316.9958936 316.9961529 316.9965069 316.9969469 316.9974621 316.9980398 316.9986658 316.9993246 317 317.0006754 317.0013342 317.0019602 317.0025379 317.0030531 317.0034931 317.0038471 317.0041064 317.0042645 317.0043177 317.0042645 317.0041064 317.0038471 317.0034931 317.0030531 317.0025379 317.0019602 317.0013342 317.0006754 317 316.9993246 316.9986658 316.9980398 316.9974621 316.9969469 316.9965069 316.9961529 316.9958936 316.9957355 316.9956823 316.995516 316.9955712 316.9957355 316.9960047 316.9963724 316.9968293 316.9973644 316.9979643 316.9986144 316.9992985 317 317.0007015 317.0013856 317.0020357 317.0026356 317.0031707 317.0036276 317.0039953 317.0042645 317.0044288 317.004484 317.0044288 317.0042645 317.0039953 317.0036276 317.0031707 317.0026356 317.0020357 317.0013856 317.0007015 317 316.9992985 316.9986144 316.9979643 316.9973644 316.9968293 316.9963724 316.9960047 316.9957355 316.9955712 316.995516 316.9954601 316.995516 316.9956823 316.9959549 316.9963271 316.9967898 316.9973315 316.9979389 316.9985971 316.9992898 317 317.0007102 317.0014029 317.0020611 317.0026685 317.0032102 317.0036729 317.0040451 317.0043177 317.004484 317.0045399 317.004484 317.0043177 317.0040451 317.0036729 317.0032102 317.0026685 317.0020611 317.0014029 317.0007102 317 316.9992898 316.9985971 316.9979389 316.9973315 316.9967898 316.9963271 316.9959549 316.9956823 316.995516 316.9954601 316.995516 316.9955712 316.9957355 316.9960047 316.9963724 316.9968293 316.9973644 316.9979643 316.9986144 316.9992985 317 317.0007015 317.0013856 317.0020357 317.0026356 317.0031707 317.0036276 317.0039953 317.0042645 317.0044288 317.004484 317.0044288 317.0042645 317.0039953 317.0036276 317.0031707 317.0026356 317.0020357 317.0013856 317.0007015 317 316.9992985 316.9986144 316.9979643 316.9973644 316.9968293 316.9963724 316.9960047 316.9957355 316.9955712 316.995516 316.9956823 316.9957355 316.9958936 316.9961529 316.9965069 316.9969469 316.9974621 316.9980398 316.9986658 316.9993246 317 317.0006754 317.0013342 317.0019602 317.0025379 317.0030531 317.0034931 317.0038471 317.0041064 317.0042645 317.0043177 317.0042645 317.0041064 317.0038471 317.0034931 317.0030531 317.0025379 317.0019602 317.0013342 317.0006754 317 316.9993246 316.9986658 316.9980398 316.9974621 316.9969469 316.9965069 316.9961529 316.9958936 316.9957355 316.9956823 316.9959549 316.9960047 316.9961529 316.9963958 316.9967275 316.9971397 316.9976224 316.9981636 316.99875 316.9993672 317 317.0006328 317.00125 317.0018364 317.0023776 317.0028603 317.0032725 317.0036042 317.0038471 317.0039953 317.0040451 317.0039953 317.0038471 317.0036042 317.0032725 317.0028603 317.0023776 317.0018364 317.00125 317.0006328 317 316.9993672 316.99875 316.9981636 316.9976224 316.9971397 316.9967275 316.9963958 316.9961529 316.9960047 316.9959549 316.9963271 316.9963724 316.9965069 316.9967275 316.9970286 316.9974029 316.9978411 316.9983326 316.998865 316.9994254 317 317.0005746 317.001135 317.0016674 317.0021589 317.0025971 317.0029714 317.0032725 317.0034931 317.0036276 317.0036729 317.0036276 317.0034931 317.0032725 317.0029714 317.0025971 317.0021589 317.0016674 317.001135 317.0005746 317 316.9994254 316.998865 316.9983326 316.9978411 316.9974029 316.9970286 316.9967275 316.9965069 316.9963724 316.9963271 316.9967898 316.9968293 316.9969469 316.9971397 316.9974029 316.99773 316.9981131 316.9985426 316.999008 316.9994978 317 317.0005022 317.000992 317.0014574 317.0018869 317.00227 317.0025971 317.0028603 317.0030531 317.0031707 317.0032102 317.0031707 317.0030531 317.0028603 317.0025971 317.00227 317.0018869 317.0014574 317.000992 317.0005022 317 316.9994978 316.999008 316.9985426 316.9981131 316.99773 316.9974029 316.9971397 316.9969469 316.9968293 316.9967898 316.9973315 316.9973644 316.9974621 316.9976224 316.9978411 316.9981131 316.9984315 316.9987885 316.9991754 316.9995826 317 317.0004174 317.0008246 317.0012115 317.0015685 317.0018869 317.0021589 317.0023776 317.0025379 317.0026356 317.0026685 317.0026356 317.0025379 317.0023776 317.0021589 317.0018869 317.0015685 317.0012115 317.0008246 317.0004174 317 316.9995826 316.9991754 316.9987885 316.9984315 316.9981131 316.9978411 316.9976224 316.9974621 316.9973644 316.9973315 316.9979389 316.9979643 316.9980398 316.9981636 316.9983326 316.9985426 316.9987885 316.9990643 316.9993631 316.9996776 317 317.0003224 317.0006369 317.0009357 317.0012115 317.0014574 317.0016674 317.0018364 317.0019602 317.0020357 317.0020611 317.0020357 317.0019602 317.0018364 317.0016674 317.0014574 317.0012115 317.0009357 317.0006369 317.0003224 317 316.9996776 316.9993631 316.9990643 316.9987885 316.9985426 316.9983326 316.9981636 316.9980398 316.9979643 316.9979389 316.9985971 316.9986144 316.9986658 316.99875 316.998865 316.999008 316.9991754 316.9993631 316.9995665 316.9997805 317 317.0002195 317.0004335 317.0006369 317.0008246 317.000992 317.001135 317.00125 317.0013342 317.0013856 317.0014029 317.0013856 317.0013342 317.00125 317.001135 317.000992 317.0008246 317.0006369 317.0004335 317.0002195 317 316.9997805 316.9995665 316.9993631 316.9991754 316.999008 316.998865 316.99875 316.9986658 316.9986144 316.9985971 316.9992898 316.9992985 316.9993246 316.9993672 316.9994254 316.9994978 316.9995826 316.9996776 316.9997805 316.9998889 317 317.0001111 317.0002195 317.0003224 317.0004174 317.0005022 317.0005746 317.0006328 317.0006754 317.0007015 317.0007102 317.0007015 317.0006754 317.0006328 317.0005746 317.0005022 317.0004174 317.0003224 317.0002195 317.0001111 317 316.9998889 316.9997805 316.9996776 316.9995826 316.9994978 316.9994254 316.9993672 316.9993246 316.9992985 316.9992898 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317 317.0007102 317.0007015 317.0006754 317.0006328 317.0005746 317.0005022 317.0004174 317.0003224 317.0002195 317.0001111 317 316.9998889 316.9997805 316.9996776 316.9995826 316.9994978 316.9994254 316.9993672 316.9993246 316.9992985 316.9992898 316.9992985 316.9993246 316.9993672 316.9994254 316.9994978 316.9995826 316.9996776 316.9997805 316.9998889 317 317.0001111 317.0002195 317.0003224 317.0004174 317.0005022 317.0005746 317.0006328 317.0006754 317.0007015 317.0007102 317.0014029 317.0013856 317.0013342 317.00125 317.001135 317.000992 317.0008246 317.0006369 317.0004335 317.0002195 317 316.9997805 316.9995665 316.9993631 316.9991754 316.999008 316.998865 316.99875 316.9986658 316.9986144 316.9985971 316.9986144 316.9986658 316.99875 316.998865 316.999008 316.9991754 316.9993631 316.9995665 316.9997805 317 317.0002195 317.0004335 317.0006369 317.0008246 317.000992 317.001135 317.00125 317.0013342 317.0013856 317.0014029 317.0020611 317.0020357 317.0019602 317.0018364 317.0016674 317.0014574 317.0012115 317.0009357 317.0006369 317.0003224 317 316.9996776 316.9993631 316.9990643 316.9987885 316.9985426 316.9983326 316.9981636 316.9980398 316.9979643 316.9979389 316.9979643 316.9980398 316.9981636 316.9983326 316.9985426 316.9987885 316.9990643 316.9993631 316.9996776 317 317.0003224 317.0006369 317.0009357 317.0012115 317.0014574 317.0016674 317.0018364 317.0019602 317.0020357 317.0020611 317.0026685 317.0026356 317.0025379 317.0023776 317.0021589 317.0018869 317.0015685 317.0012115 317.0008246 317.0004174 317 316.9995826 316.9991754 316.9987885 316.9984315 316.9981131 316.9978411 316.9976224 316.9974621 316.9973644 316.9973315 316.9973644 316.9974621 316.9976224 316.9978411 316.9981131 316.9984315 316.9987885 316.9991754 316.9995826 317 317.0004174 317.0008246 317.0012115 317.0015685 317.0018869 317.0021589 317.0023776 317.0025379 317.0026356 317.0026685 317.0032102 317.0031707 317.0030531 317.0028603 317.0025971 317.00227 317.0018869 317.0014574 317.000992 317.0005022 317 316.9994978 316.999008 316.9985426 316.9981131 316.99773 316.9974029 316.9971397 316.9969469 316.9968293 316.9967898 316.9968293 316.9969469 316.9971397 316.9974029 316.99773 316.9981131 316.9985426 316.999008 316.9994978 317 317.0005022 317.000992 317.0014574 317.0018869 317.00227 317.0025971 317.0028603 317.0030531 317.0031707 317.0032102 317.0036729 317.0036276 317.0034931 317.0032725 317.0029714 317.0025971 317.0021589 317.0016674 317.001135 317.0005746 317 316.9994254 316.998865 316.9983326 316.9978411 316.9974029 316.9970286 316.9967275 316.9965069 316.9963724 316.9963271 316.9963724 316.9965069 316.9967275 316.9970286 316.9974029 316.9978411 316.9983326 316.998865 316.9994254 317 317.0005746 317.001135 317.0016674 317.0021589 317.0025971 317.0029714 317.0032725 317.0034931 317.0036276 317.0036729 317.0040451 317.0039953 317.0038471 317.0036042 317.0032725 317.0028603 317.0023776 317.0018364 317.00125 317.0006328 317 316.9993672 316.99875 316.9981636 316.9976224 316.9971397 316.9967275 316.9963958 316.9961529 316.9960047 316.9959549 316.9960047 316.9961529 316.9963958 316.9967275 316.9971397 316.9976224 316.9981636 316.99875 316.9993672 317 317.0006328 317.00125 317.0018364 317.0023776 317.0028603 317.0032725 317.0036042 317.0038471 317.0039953 317.0040451 317.0043177 317.0042645 317.0041064 317.0038471 317.0034931 317.0030531 317.0025379 317.0019602 317.0013342 317.0006754 317 316.9993246 316.9986658 316.9980398 316.9974621 316.9969469 316.9965069 316.9961529 316.9958936 316.9957355 316.9956823 316.9957355 316.9958936 316.9961529 316.9965069 316.9969469 316.9974621 316.9980398 316.9986658 316.9993246 317 317.0006754 317.0013342 317.0019602 317.0025379 317.0030531 317.0034931 317.0038471 317.0041064 317.0042645 317.0043177 317.004484 317.0044288 317.0042645 317.0039953 317.0036276 317.0031707 317.0026356 317.0020357 317.0013856 317.0007015 317 316.9992985 316.9986144 316.9979643 316.9973644 316.9968293 316.9963724 316.9960047 316.9957355 316.9955712 316.995516 316.9955712 316.9957355 316.9960047 316.9963724 316.9968293 316.9973644 316.9979643 316.9986144 316.9992985 317 317.0007015 317.0013856 317.0020357 317.0026356 317.0031707 317.0036276 317.0039953 317.0042645 317.0044288 317.004484 317.0045399 317.004484 317.0043177 317.0040451 317.0036729 317.0032102 317.0026685 317.0020611 317.0014029 317.0007102 317 316.9992898 316.9985971 316.9979389 316.9973315 316.9967898 316.9963271 316.9959549 316.9956823 316.995516 316.9954601 316.995516 316.9956823 316.9959549 316.9963271 316.9967898 316.9973315 316.9979389 316.9985971 316.9992898 317 317.0007102 317.0014029 317.0020611 317.0026685 317.0032102 317.0036729 317.0040451 317.0043177 317.004484 317.0045399 316.0058779 316.0058055 316.0055902 316.0052372 316.0047553 316.0041563 316.0034549 316.0026685 316.0018164 316.0009195 316 315.9990805 315.9981836 315.9973315 315.9965451 315.9958437 315.9952447 315.9947628 315.9944098 315.9941945 315.9941221 315.9941945 315.9944098 315.9947628 315.9952447 315.9958437 315.9965451 315.9973315 315.9981836 315.9990805 316 316.0009195 316.0018164 316.0026685 316.0034549 316.0041563 316.0047553 316.0052372 316.0055902 316.0058055 316.0058779 316.0058055 316.005734 316.0055213 316.0051727 316.0046967 316.0041051 316.0034124 316.0026356 316.001794 316.0009082 316 315.9990918 315.998206 315.9973644 315.9965876 315.9958949 315.9953033 315.9948273 315.9944787 315.994266 315.9941945 315.994266 315.9944787 315.9948273 315.9953033 315.9958949 315.9965876 315.9973644 315.998206 315.9990918 316 316.0009082 316.001794 316.0026356 316.0034124 316.0041051 316.0046967 316.0051727 316.0055213 316.005734 316.0058055 316.0055902 316.0055213 316.0053166 316.0049809 316.0045225 316.0039528 316.0032858 316.0025379 316.0017275 316.0008745 316 315.9991255 315.9982725 315.9974621 315.9967142 315.9960472 315.9954775 315.9950191 315.9946834 315.9944787 315.9944098 315.9944787 315.9946834 315.9950191 315.9954775 315.9960472 315.9967142 315.9974621 315.9982725 315.9991255 316 316.0008745 316.0017275 316.0025379 316.0032858 316.0039528 316.0045225 316.0049809 316.0053166 316.0055213 316.0055902 316.0052372 316.0051727 316.0049809 316.0046664 316.004237 316.0037033 316.0030784 316.0023776 316.0016184 316.0008193 316 315.9991807 315.9983816 315.9976224 315.9969216 315.9962967 315.995763 315.9953336 315.9950191 315.9948273 315.9947628 315.9948273 315.9950191 315.9953336 315.995763 315.9962967 315.9969216 315.9976224 315.9983816 315.9991807 316 316.0008193 316.0016184 316.0023776 316.0030784 316.0037033 316.004237 316.0046664 316.0049809 316.0051727 316.0052372 316.0047553 316.0046967 316.0045225 316.004237 316.0038471 316.0033625 316.0027951 316.0021589 316.0014695 316.0007439 316 315.9992561 315.9985305 315.9978411 315.9972049 315.9966375 315.9961529 315.995763 315.9954775 315.9953033 315.9952447 315.9953033 315.9954775 315.995763 315.9961529 315.9966375 315.9972049 315.9978411 315.9985305 315.9992561 316 316.0007439 316.0014695 316.0021589 316.0027951 316.0033625 316.0038471 316.004237 316.0045225 316.0046967 316.0047553 316.0041563 316.0041051 316.0039528 316.0037033 316.0033625 316.0029389 316.002443 316.0018869 316.0012844 316.0006502 316 315.9993498 315.9987156 315.9981131 315.997557 315.9970611 315.9966375 315.9962967 315.9960472 315.9958949 315.9958437 315.9958949 315.9960472 315.9962967 315.9966375 315.9970611 315.997557 315.9981131 315.9987156 315.9993498 316 316.0006502 316.0012844 316.0018869 316.002443 316.0029389 316.0033625 316.0037033 316.0039528 316.0041051 316.0041563 316.0034549 316.0034124 316.0032858 316.0030784 316.0027951 316.002443 316.0020307 316.0015685 316.0010676 316.0005405 316 315.9994595 315.9989324 315.9984315 315.9979693 315.997557 315.9972049 315.9969216 315.9967142 315.9965876 315.9965451 315.9965876 315.9967142 315.9969216 315.9972049 315.997557 315.9979693 315.9984315 315.9989324 315.9994595 316 316.0005405 316.0010676 316.0015685 316.0020307 316.002443 316.0027951 316.0030784 316.0032858 316.0034124 316.0034549 316.0026685 316.0026356 316.0025379 316.0023776 316.0021589 316.0018869 316.0015685 316.0012115 316.0008246 316.0004174 316 315.9995826 315.9991754 315.9987885 315.9984315 315.9981131 315.9978411 315.9976224 315.9974621 315.9973644 315.9973315 315.9973644 315.9974621 315.9976224 315.9978411 315.9981131 315.9984315 315.9987885 315.9991754 315.9995826 316 316.0004174 316.0008246 316.0012115 316.0015685 316.0018869 316.0021589 316.0023776 316.0025379 316.0026356 316.0026685 316.0018164 316.001794 316.0017275 316.0016184 316.0014695 316.0012844 316.0010676 316.0008246 316.0005613 316.0002841 316 315.9997159 315.9994387 315.9991754 315.9989324 315.9987156 315.9985305 315.9983816 315.9982725 315.998206 315.9981836 315.998206 315.9982725 315.9983816 315.9985305 315.9987156 315.9989324 315.9991754 315.9994387 315.9997159 316 316.0002841 316.0005613 316.0008246 316.0010676 316.0012844 316.0014695 316.0016184 316.0017275 316.001794 316.0018164 316.0009195 316.0009082 316.0008745 316.0008193 316.0007439 316.0006502 316.0005405 316.0004174 316.0002841 316.0001438 316 315.9998562 315.9997159 315.9995826 315.9994595 315.9993498 315.9992561 315.9991807 315.9991255 315.9990918 315.9990805 315.9990918 315.9991255 315.9991807 315.9992561 315.9993498 315.9994595 315.9995826 315.9997159 315.9998562 316 316.0001438 316.0002841 316.0004174 316.0005405 316.0006502 316.0007439 316.0008193 316.0008745 316.0009082 316.0009195 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 315.9990805 315.9990918 315.9991255 315.9991807 315.9992561 315.9993498 315.9994595 315.9995826 315.9997159 315.9998562 316 316.0001438 316.0002841 316.0004174 316.0005405 316.0006502 316.0007439 316.0008193 316.0008745 316.0009082 316.0009195 316.0009082 316.0008745 316.0008193 316.0007439 316.0006502 316.0005405 316.0004174 316.0002841 316.0001438 316 315.9998562 315.9997159 315.9995826 315.9994595 315.9993498 315.9992561 315.9991807 315.9991255 315.9990918 315.9990805 315.9981836 315.998206 315.9982725 315.9983816 315.9985305 315.9987156 315.9989324 315.9991754 315.9994387 315.9997159 316 316.0002841 316.0005613 316.0008246 316.0010676 316.0012844 316.0014695 316.0016184 316.0017275 316.001794 316.0018164 316.001794 316.0017275 316.0016184 316.0014695 316.0012844 316.0010676 316.0008246 316.0005613 316.0002841 316 315.9997159 315.9994387 315.9991754 315.9989324 315.9987156 315.9985305 315.9983816 315.9982725 315.998206 315.9981836 315.9973315 315.9973644 315.9974621 315.9976224 315.9978411 315.9981131 315.9984315 315.9987885 315.9991754 315.9995826 316 316.0004174 316.0008246 316.0012115 316.0015685 316.0018869 316.0021589 316.0023776 316.0025379 316.0026356 316.0026685 316.0026356 316.0025379 316.0023776 316.0021589 316.0018869 316.0015685 316.0012115 316.0008246 316.0004174 316 315.9995826 315.9991754 315.9987885 315.9984315 315.9981131 315.9978411 315.9976224 315.9974621 315.9973644 315.9973315 315.9965451 315.9965876 315.9967142 315.9969216 315.9972049 315.997557 315.9979693 315.9984315 315.9989324 315.9994595 316 316.0005405 316.0010676 316.0015685 316.0020307 316.002443 316.0027951 316.0030784 316.0032858 316.0034124 316.0034549 316.0034124 316.0032858 316.0030784 316.0027951 316.002443 316.0020307 316.0015685 316.0010676 316.0005405 316 315.9994595 315.9989324 315.9984315 315.9979693 315.997557 315.9972049 315.9969216 315.9967142 315.9965876 315.9965451 315.9958437 315.9958949 315.9960472 315.9962967 315.9966375 315.9970611 315.997557 315.9981131 315.9987156 315.9993498 316 316.0006502 316.0012844 316.0018869 316.002443 316.0029389 316.0033625 316.0037033 316.0039528 316.0041051 316.0041563 316.0041051 316.0039528 316.0037033 316.0033625 316.0029389 316.002443 316.0018869 316.0012844 316.0006502 316 315.9993498 315.9987156 315.9981131 315.997557 315.9970611 315.9966375 315.9962967 315.9960472 315.9958949 315.9958437 315.9952447 315.9953033 315.9954775 315.995763 315.9961529 315.9966375 315.9972049 315.9978411 315.9985305 315.9992561 316 316.0007439 316.0014695 316.0021589 316.0027951 316.0033625 316.0038471 316.004237 316.0045225 316.0046967 316.0047553 316.0046967 316.0045225 316.004237 316.0038471 316.0033625 316.0027951 316.0021589 316.0014695 316.0007439 316 315.9992561 315.9985305 315.9978411 315.9972049 315.9966375 315.9961529 315.995763 315.9954775 315.9953033 315.9952447 315.9947628 315.9948273 315.9950191 315.9953336 315.995763 315.9962967 315.9969216 315.9976224 315.9983816 315.9991807 316 316.0008193 316.0016184 316.0023776 316.0030784 316.0037033 316.004237 316.0046664 316.0049809 316.0051727 316.0052372 316.0051727 316.0049809 316.0046664 316.004237 316.0037033 316.0030784 316.0023776 316.0016184 316.0008193 316 315.9991807 315.9983816 315.9976224 315.9969216 315.9962967 315.995763 315.9953336 315.9950191 315.9948273 315.9947628 315.9944098 315.9944787 315.9946834 315.9950191 315.9954775 315.9960472 315.9967142 315.9974621 315.9982725 315.9991255 316 316.0008745 316.0017275 316.0025379 316.0032858 316.0039528 316.0045225 316.0049809 316.0053166 316.0055213 316.0055902 316.0055213 316.0053166 316.0049809 316.0045225 316.0039528 316.0032858 316.0025379 316.0017275 316.0008745 316 315.9991255 315.9982725 315.9974621 315.9967142 315.9960472 315.9954775 315.9950191 315.9946834 315.9944787 315.9944098 315.9941945 315.994266 315.9944787 315.9948273 315.9953033 315.9958949 315.9965876 315.9973644 315.998206 315.9990918 316 316.0009082 316.001794 316.0026356 316.0034124 316.0041051 316.0046967 316.0051727 316.0055213 316.005734 316.0058055 316.005734 316.0055213 316.0051727 316.0046967 316.0041051 316.0034124 316.0026356 316.001794 316.0009082 316 315.9990918 315.998206 315.9973644 315.9965876 315.9958949 315.9953033 315.9948273 315.9944787 315.994266 315.9941945 315.9941221 315.9941945 315.9944098 315.9947628 315.9952447 315.9958437 315.9965451 315.9973315 315.9981836 315.9990805 316 316.0009195 316.0018164 316.0026685 316.0034549 316.0041563 316.0047553 316.0052372 316.0055902 316.0058055 316.0058779 316.0058055 316.0055902 316.0052372 316.0047553 316.0041563 316.0034549 316.0026685 316.0018164 316.0009195 316 315.9990805 315.9981836 315.9973315 315.9965451 315.9958437 315.9952447 315.9947628 315.9944098 315.9941945 315.9941221 315.9941945 315.994266 315.9944787 315.9948273 315.9953033 315.9958949 315.9965876 315.9973644 315.998206 315.9990918 316 316.0009082 316.001794 316.0026356 316.0034124 316.0041051 316.0046967 316.0051727 316.0055213 316.005734 316.0058055 316.005734 316.0055213 316.0051727 316.0046967 316.0041051 316.0034124 316.0026356 316.001794 316.0009082 316 315.9990918 315.998206 315.9973644 315.9965876 315.9958949 315.9953033 315.9948273 315.9944787 315.994266 315.9941945 315.9944098 315.9944787 315.9946834 315.9950191 315.9954775 315.9960472 315.9967142 315.9974621 315.9982725 315.9991255 316 316.0008745 316.0017275 316.0025379 316.0032858 316.0039528 316.0045225 316.0049809 316.0053166 316.0055213 316.0055902 316.0055213 316.0053166 316.0049809 316.0045225 316.0039528 316.0032858 316.0025379 316.0017275 316.0008745 316 315.9991255 315.9982725 315.9974621 315.9967142 315.9960472 315.9954775 315.9950191 315.9946834 315.9944787 315.9944098 315.9947628 315.9948273 315.9950191 315.9953336 315.995763 315.9962967 315.9969216 315.9976224 315.9983816 315.9991807 316 316.0008193 316.0016184 316.0023776 316.0030784 316.0037033 316.004237 316.0046664 316.0049809 316.0051727 316.0052372 316.0051727 316.0049809 316.0046664 316.004237 316.0037033 316.0030784 316.0023776 316.0016184 316.0008193 316 315.9991807 315.9983816 315.9976224 315.9969216 315.9962967 315.995763 315.9953336 315.9950191 315.9948273 315.9947628 315.9952447 315.9953033 315.9954775 315.995763 315.9961529 315.9966375 315.9972049 315.9978411 315.9985305 315.9992561 316 316.0007439 316.0014695 316.0021589 316.0027951 316.0033625 316.0038471 316.004237 316.0045225 316.0046967 316.0047553 316.0046967 316.0045225 316.004237 316.0038471 316.0033625 316.0027951 316.0021589 316.0014695 316.0007439 316 315.9992561 315.9985305 315.9978411 315.9972049 315.9966375 315.9961529 315.995763 315.9954775 315.9953033 315.9952447 315.9958437 315.9958949 315.9960472 315.9962967 315.9966375 315.9970611 315.997557 315.9981131 315.9987156 315.9993498 316 316.0006502 316.0012844 316.0018869 316.002443 316.0029389 316.0033625 316.0037033 316.0039528 316.0041051 316.0041563 316.0041051 316.0039528 316.0037033 316.0033625 316.0029389 316.002443 316.0018869 316.0012844 316.0006502 316 315.9993498 315.9987156 315.9981131 315.997557 315.9970611 315.9966375 315.9962967 315.9960472 315.9958949 315.9958437 315.9965451 315.9965876 315.9967142 315.9969216 315.9972049 315.997557 315.9979693 315.9984315 315.9989324 315.9994595 316 316.0005405 316.0010676 316.0015685 316.0020307 316.002443 316.0027951 316.0030784 316.0032858 316.0034124 316.0034549 316.0034124 316.0032858 316.0030784 316.0027951 316.002443 316.0020307 316.0015685 316.0010676 316.0005405 316 315.9994595 315.9989324 315.9984315 315.9979693 315.997557 315.9972049 315.9969216 315.9967142 315.9965876 315.9965451 315.9973315 315.9973644 315.9974621 315.9976224 315.9978411 315.9981131 315.9984315 315.9987885 315.9991754 315.9995826 316 316.0004174 316.0008246 316.0012115 316.0015685 316.0018869 316.0021589 316.0023776 316.0025379 316.0026356 316.0026685 316.0026356 316.0025379 316.0023776 316.0021589 316.0018869 316.0015685 316.0012115 316.0008246 316.0004174 316 315.9995826 315.9991754 315.9987885 315.9984315 315.9981131 315.9978411 315.9976224 315.9974621 315.9973644 315.9973315 315.9981836 315.998206 315.9982725 315.9983816 315.9985305 315.9987156 315.9989324 315.9991754 315.9994387 315.9997159 316 316.0002841 316.0005613 316.0008246 316.0010676 316.0012844 316.0014695 316.0016184 316.0017275 316.001794 316.0018164 316.001794 316.0017275 316.0016184 316.0014695 316.0012844 316.0010676 316.0008246 316.0005613 316.0002841 316 315.9997159 315.9994387 315.9991754 315.9989324 315.9987156 315.9985305 315.9983816 315.9982725 315.998206 315.9981836 315.9990805 315.9990918 315.9991255 315.9991807 315.9992561 315.9993498 315.9994595 315.9995826 315.9997159 315.9998562 316 316.0001438 316.0002841 316.0004174 316.0005405 316.0006502 316.0007439 316.0008193 316.0008745 316.0009082 316.0009195 316.0009082 316.0008745 316.0008193 316.0007439 316.0006502 316.0005405 316.0004174 316.0002841 316.0001438 316 315.9998562 315.9997159 315.9995826 315.9994595 315.9993498 315.9992561 315.9991807 315.9991255 315.9990918 315.9990805 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316 316.0009195 316.0009082 316.0008745 316.0008193 316.0007439 316.0006502 316.0005405 316.0004174 316.0002841 316.0001438 316 315.9998562 315.9997159 315.9995826 315.9994595 315.9993498 315.9992561 315.9991807 315.9991255 315.9990918 315.9990805 315.9990918 315.9991255 315.9991807 315.9992561 315.9993498 315.9994595 315.9995826 315.9997159 315.9998562 316 316.0001438 316.0002841 316.0004174 316.0005405 316.0006502 316.0007439 316.0008193 316.0008745 316.0009082 316.0009195 316.0018164 316.001794 316.0017275 316.0016184 316.0014695 316.0012844 316.0010676 316.0008246 316.0005613 316.0002841 316 315.9997159 315.9994387 315.9991754 315.9989324 315.9987156 315.9985305 315.9983816 315.9982725 315.998206 315.9981836 315.998206 315.9982725 315.9983816 315.9985305 315.9987156 315.9989324 315.9991754 315.9994387 315.9997159 316 316.0002841 316.0005613 316.0008246 316.0010676 316.0012844 316.0014695 316.0016184 316.0017275 316.001794 316.0018164 316.0026685 316.0026356 316.0025379 316.0023776 316.0021589 316.0018869 316.0015685 316.0012115 316.0008246 316.0004174 316 315.9995826 315.9991754 315.9987885 315.9984315 315.9981131 315.9978411 315.9976224 315.9974621 315.9973644 315.9973315 315.9973644 315.9974621 315.9976224 315.9978411 315.9981131 315.9984315 315.9987885 315.9991754 315.9995826 316 316.0004174 316.0008246 316.0012115 316.0015685 316.0018869 316.0021589 316.0023776 316.0025379 316.0026356 316.0026685 316.0034549 316.0034124 316.0032858 316.0030784 316.0027951 316.002443 316.0020307 316.0015685 316.0010676 316.0005405 316 315.9994595 315.9989324 315.9984315 315.9979693 315.997557 315.9972049 315.9969216 315.9967142 315.9965876 315.9965451 315.9965876 315.9967142 315.9969216 315.9972049 315.997557 315.9979693 315.9984315 315.9989324 315.9994595 316 316.0005405 316.0010676 316.0015685 316.0020307 316.002443 316.0027951 316.0030784 316.0032858 316.0034124 316.0034549 316.0041563 316.0041051 316.0039528 316.0037033 316.0033625 316.0029389 316.002443 316.0018869 316.0012844 316.0006502 316 315.9993498 315.9987156 315.9981131 315.997557 315.9970611 315.9966375 315.9962967 315.9960472 315.9958949 315.9958437 315.9958949 315.9960472 315.9962967 315.9966375 315.9970611 315.997557 315.9981131 315.9987156 315.9993498 316 316.0006502 316.0012844 316.0018869 316.002443 316.0029389 316.0033625 316.0037033 316.0039528 316.0041051 316.0041563 316.0047553 316.0046967 316.0045225 316.004237 316.0038471 316.0033625 316.0027951 316.0021589 316.0014695 316.0007439 316 315.9992561 315.9985305 315.9978411 315.9972049 315.9966375 315.9961529 315.995763 315.9954775 315.9953033 315.9952447 315.9953033 315.9954775 315.995763 315.9961529 315.9966375 315.9972049 315.9978411 315.9985305 315.9992561 316 316.0007439 316.0014695 316.0021589 316.0027951 316.0033625 316.0038471 316.004237 316.0045225 316.0046967 316.0047553 316.0052372 316.0051727 316.0049809 316.0046664 316.004237 316.0037033 316.0030784 316.0023776 316.0016184 316.0008193 316 315.9991807 315.9983816 315.9976224 315.9969216 315.9962967 315.995763 315.9953336 315.9950191 315.9948273 315.9947628 315.9948273 315.9950191 315.9953336 315.995763 315.9962967 315.9969216 315.9976224 315.9983816 315.9991807 316 316.0008193 316.0016184 316.0023776 316.0030784 316.0037033 316.004237 316.0046664 316.0049809 316.0051727 316.0052372 316.0055902 316.0055213 316.0053166 316.0049809 316.0045225 316.0039528 316.0032858 316.0025379 316.0017275 316.0008745 316 315.9991255 315.9982725 315.9974621 315.9967142 315.9960472 315.9954775 315.9950191 315.9946834 315.9944787 315.9944098 315.9944787 315.9946834 315.9950191 315.9954775 315.9960472 315.9967142 315.9974621 315.9982725 315.9991255 316 316.0008745 316.0017275 316.0025379 316.0032858 316.0039528 316.0045225 316.0049809 316.0053166 316.0055213 316.0055902 316.0058055 316.005734 316.0055213 316.0051727 316.0046967 316.0041051 316.0034124 316.0026356 316.001794 316.0009082 316 315.9990918 315.998206 315.9973644 315.9965876 315.9958949 315.9953033 315.9948273 315.9944787 315.994266 315.9941945 315.994266 315.9944787 315.9948273 315.9953033 315.9958949 315.9965876 315.9973644 315.998206 315.9990918 316 316.0009082 316.001794 316.0026356 316.0034124 316.0041051 316.0046967 316.0051727 316.0055213 316.005734 316.0058055 316.0058779 316.0058055 316.0055902 316.0052372 316.0047553 316.0041563 316.0034549 316.0026685 316.0018164 316.0009195 316 315.9990805 315.9981836 315.9973315 315.9965451 315.9958437 315.9952447 315.9947628 315.9944098 315.9941945 315.9941221 315.9941945 315.9944098 315.9947628 315.9952447 315.9958437 315.9965451 315.9973315 315.9981836 315.9990805 316 316.0009195 316.0018164 316.0026685 316.0034549 316.0041563 316.0047553 316.0052372 316.0055902 316.0058055 316.0058779 315.0070711 315.006984 315.006725 315.0063004 315.0057206 315.005 315.0041563 315.0032102 315.0021851 315.0011062 315 314.9988938 314.9978149 314.9967898 314.9958437 314.995 314.9942794 314.9936996 314.993275 314.993016 314.9929289 314.993016 314.993275 314.9936996 314.9942794 314.995 314.9958437 314.9967898 314.9978149 314.9988938 315 315.0011062 315.0021851 315.0032102 315.0041563 315.005 315.0057206 315.0063004 315.006725 315.006984 315.0070711 315.006984 315.006898 315.0066422 315.0062228 315.0056502 315.0049384 315.0041051 315.0031707 315.0021582 315.0010925 315 314.9989075 314.9978418 314.9968293 314.9958949 314.9950616 314.9943498 314.9937772 314.9933578 314.993102 314.993016 314.993102 314.9933578 314.9937772 314.9943498 314.9950616 314.9958949 314.9968293 314.9978418 314.9989075 315 315.0010925 315.0021582 315.0031707 315.0041051 315.0049384 315.0056502 315.0062228 315.0066422 315.006898 315.006984 315.006725 315.0066422 315.0063958 315.005992 315.0054406 315.0047553 315.0039528 315.0030531 315.0020781 315.001052 315 314.998948 314.9979219 314.9969469 314.9960472 314.9952447 314.9945594 314.994008 314.9936042 314.9933578 314.993275 314.9933578 314.9936042 314.994008 314.9945594 314.9952447 314.9960472 314.9969469 314.9979219 314.998948 315 315.001052 315.0020781 315.0030531 315.0039528 315.0047553 315.0054406 315.005992 315.0063958 315.0066422 315.006725 315.0063004 315.0062228 315.005992 315.0056137 315.0050971 315.004455 315.0037033 315.0028603 315.0019469 315.0009856 315 314.9990144 314.9980531 314.9971397 314.9962967 314.995545 314.9949029 314.9943863 314.994008 314.9937772 314.9936996 314.9937772 314.994008 314.9943863 314.9949029 314.995545 314.9962967 314.9971397 314.9980531 314.9990144 315 315.0009856 315.0019469 315.0028603 315.0037033 315.004455 315.0050971 315.0056137 315.005992 315.0062228 315.0063004 315.0057206 315.0056502 315.0054406 315.0050971 315.0046281 315.0040451 315.0033625 315.0025971 315.0017678 315.0008949 315 314.9991051 314.9982322 314.9974029 314.9966375 314.9959549 314.9953719 314.9949029 314.9945594 314.9943498 314.9942794 314.9943498 314.9945594 314.9949029 314.9953719 314.9959549 314.9966375 314.9974029 314.9982322 314.9991051 315 315.0008949 315.0017678 315.0025971 315.0033625 315.0040451 315.0046281 315.0050971 315.0054406 315.0056502 315.0057206 315.005 315.0049384 315.0047553 315.004455 315.0040451 315.0035355 315.0029389 315.00227 315.0015451 315.0007822 315 314.9992178 314.9984549 314.99773 314.9970611 314.9964645 314.9959549 314.995545 314.9952447 314.9950616 314.995 314.9950616 314.9952447 314.995545 314.9959549 314.9964645 314.9970611 314.99773 314.9984549 314.9992178 315 315.0007822 315.0015451 315.00227 315.0029389 315.0035355 315.0040451 315.004455 315.0047553 315.0049384 315.005 315.0041563 315.0041051 315.0039528 315.0037033 315.0033625 315.0029389 315.002443 315.0018869 315.0012844 315.0006502 315 314.9993498 314.9987156 314.9981131 314.997557 314.9970611 314.9966375 314.9962967 314.9960472 314.9958949 314.9958437 314.9958949 314.9960472 314.9962967 314.9966375 314.9970611 314.997557 314.9981131 314.9987156 314.9993498 315 315.0006502 315.0012844 315.0018869 315.002443 315.0029389 315.0033625 315.0037033 315.0039528 315.0041051 315.0041563 315.0032102 315.0031707 315.0030531 315.0028603 315.0025971 315.00227 315.0018869 315.0014574 315.000992 315.0005022 315 314.9994978 314.999008 314.9985426 314.9981131 314.99773 314.9974029 314.9971397 314.9969469 314.9968293 314.9967898 314.9968293 314.9969469 314.9971397 314.9974029 314.99773 314.9981131 314.9985426 314.999008 314.9994978 315 315.0005022 315.000992 315.0014574 315.0018869 315.00227 315.0025971 315.0028603 315.0030531 315.0031707 315.0032102 315.0021851 315.0021582 315.0020781 315.0019469 315.0017678 315.0015451 315.0012844 315.000992 315.0006752 315.0003418 315 314.9996582 314.9993248 314.999008 314.9987156 314.9984549 314.9982322 314.9980531 314.9979219 314.9978418 314.9978149 314.9978418 314.9979219 314.9980531 314.9982322 314.9984549 314.9987156 314.999008 314.9993248 314.9996582 315 315.0003418 315.0006752 315.000992 315.0012844 315.0015451 315.0017678 315.0019469 315.0020781 315.0021582 315.0021851 315.0011062 315.0010925 315.001052 315.0009856 315.0008949 315.0007822 315.0006502 315.0005022 315.0003418 315.000173 315 314.999827 314.9996582 314.9994978 314.9993498 314.9992178 314.9991051 314.9990144 314.998948 314.9989075 314.9988938 314.9989075 314.998948 314.9990144 314.9991051 314.9992178 314.9993498 314.9994978 314.9996582 314.999827 315 315.000173 315.0003418 315.0005022 315.0006502 315.0007822 315.0008949 315.0009856 315.001052 315.0010925 315.0011062 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 314.9988938 314.9989075 314.998948 314.9990144 314.9991051 314.9992178 314.9993498 314.9994978 314.9996582 314.999827 315 315.000173 315.0003418 315.0005022 315.0006502 315.0007822 315.0008949 315.0009856 315.001052 315.0010925 315.0011062 315.0010925 315.001052 315.0009856 315.0008949 315.0007822 315.0006502 315.0005022 315.0003418 315.000173 315 314.999827 314.9996582 314.9994978 314.9993498 314.9992178 314.9991051 314.9990144 314.998948 314.9989075 314.9988938 314.9978149 314.9978418 314.9979219 314.9980531 314.9982322 314.9984549 314.9987156 314.999008 314.9993248 314.9996582 315 315.0003418 315.0006752 315.000992 315.0012844 315.0015451 315.0017678 315.0019469 315.0020781 315.0021582 315.0021851 315.0021582 315.0020781 315.0019469 315.0017678 315.0015451 315.0012844 315.000992 315.0006752 315.0003418 315 314.9996582 314.9993248 314.999008 314.9987156 314.9984549 314.9982322 314.9980531 314.9979219 314.9978418 314.9978149 314.9967898 314.9968293 314.9969469 314.9971397 314.9974029 314.99773 314.9981131 314.9985426 314.999008 314.9994978 315 315.0005022 315.000992 315.0014574 315.0018869 315.00227 315.0025971 315.0028603 315.0030531 315.0031707 315.0032102 315.0031707 315.0030531 315.0028603 315.0025971 315.00227 315.0018869 315.0014574 315.000992 315.0005022 315 314.9994978 314.999008 314.9985426 314.9981131 314.99773 314.9974029 314.9971397 314.9969469 314.9968293 314.9967898 314.9958437 314.9958949 314.9960472 314.9962967 314.9966375 314.9970611 314.997557 314.9981131 314.9987156 314.9993498 315 315.0006502 315.0012844 315.0018869 315.002443 315.0029389 315.0033625 315.0037033 315.0039528 315.0041051 315.0041563 315.0041051 315.0039528 315.0037033 315.0033625 315.0029389 315.002443 315.0018869 315.0012844 315.0006502 315 314.9993498 314.9987156 314.9981131 314.997557 314.9970611 314.9966375 314.9962967 314.9960472 314.9958949 314.9958437 314.995 314.9950616 314.9952447 314.995545 314.9959549 314.9964645 314.9970611 314.99773 314.9984549 314.9992178 315 315.0007822 315.0015451 315.00227 315.0029389 315.0035355 315.0040451 315.004455 315.0047553 315.0049384 315.005 315.0049384 315.0047553 315.004455 315.0040451 315.0035355 315.0029389 315.00227 315.0015451 315.0007822 315 314.9992178 314.9984549 314.99773 314.9970611 314.9964645 314.9959549 314.995545 314.9952447 314.9950616 314.995 314.9942794 314.9943498 314.9945594 314.9949029 314.9953719 314.9959549 314.9966375 314.9974029 314.9982322 314.9991051 315 315.0008949 315.0017678 315.0025971 315.0033625 315.0040451 315.0046281 315.0050971 315.0054406 315.0056502 315.0057206 315.0056502 315.0054406 315.0050971 315.0046281 315.0040451 315.0033625 315.0025971 315.0017678 315.0008949 315 314.9991051 314.9982322 314.9974029 314.9966375 314.9959549 314.9953719 314.9949029 314.9945594 314.9943498 314.9942794 314.9936996 314.9937772 314.994008 314.9943863 314.9949029 314.995545 314.9962967 314.9971397 314.9980531 314.9990144 315 315.0009856 315.0019469 315.0028603 315.0037033 315.004455 315.0050971 315.0056137 315.005992 315.0062228 315.0063004 315.0062228 315.005992 315.0056137 315.0050971 315.004455 315.0037033 315.0028603 315.0019469 315.0009856 315 314.9990144 314.9980531 314.9971397 314.9962967 314.995545 314.9949029 314.9943863 314.994008 314.9937772 314.9936996 314.993275 314.9933578 314.9936042 314.994008 314.9945594 314.9952447 314.9960472 314.9969469 314.9979219 314.998948 315 315.001052 315.0020781 315.0030531 315.0039528 315.0047553 315.0054406 315.005992 315.0063958 315.0066422 315.006725 315.0066422 315.0063958 315.005992 315.0054406 315.0047553 315.0039528 315.0030531 315.0020781 315.001052 315 314.998948 314.9979219 314.9969469 314.9960472 314.9952447 314.9945594 314.994008 314.9936042 314.9933578 314.993275 314.993016 314.993102 314.9933578 314.9937772 314.9943498 314.9950616 314.9958949 314.9968293 314.9978418 314.9989075 315 315.0010925 315.0021582 315.0031707 315.0041051 315.0049384 315.0056502 315.0062228 315.0066422 315.006898 315.006984 315.006898 315.0066422 315.0062228 315.0056502 315.0049384 315.0041051 315.0031707 315.0021582 315.0010925 315 314.9989075 314.9978418 314.9968293 314.9958949 314.9950616 314.9943498 314.9937772 314.9933578 314.993102 314.993016 314.9929289 314.993016 314.993275 314.9936996 314.9942794 314.995 314.9958437 314.9967898 314.9978149 314.9988938 315 315.0011062 315.0021851 315.0032102 315.0041563 315.005 315.0057206 315.0063004 315.006725 315.006984 315.0070711 315.006984 315.006725 315.0063004 315.0057206 315.005 315.0041563 315.0032102 315.0021851 315.0011062 315 314.9988938 314.9978149 314.9967898 314.9958437 314.995 314.9942794 314.9936996 314.993275 314.993016 314.9929289 314.993016 314.993102 314.9933578 314.9937772 314.9943498 314.9950616 314.9958949 314.9968293 314.9978418 314.9989075 315 315.0010925 315.0021582 315.0031707 315.0041051 315.0049384 315.0056502 315.0062228 315.0066422 315.006898 315.006984 315.006898 315.0066422 315.0062228 315.0056502 315.0049384 315.0041051 315.0031707 315.0021582 315.0010925 315 314.9989075 314.9978418 314.9968293 314.9958949 314.9950616 314.9943498 314.9937772 314.9933578 314.993102 314.993016 314.993275 314.9933578 314.9936042 314.994008 314.9945594 314.9952447 314.9960472 314.9969469 314.9979219 314.998948 315 315.001052 315.0020781 315.0030531 315.0039528 315.0047553 315.0054406 315.005992 315.0063958 315.0066422 315.006725 315.0066422 315.0063958 315.005992 315.0054406 315.0047553 315.0039528 315.0030531 315.0020781 315.001052 315 314.998948 314.9979219 314.9969469 314.9960472 314.9952447 314.9945594 314.994008 314.9936042 314.9933578 314.993275 314.9936996 314.9937772 314.994008 314.9943863 314.9949029 314.995545 314.9962967 314.9971397 314.9980531 314.9990144 315 315.0009856 315.0019469 315.0028603 315.0037033 315.004455 315.0050971 315.0056137 315.005992 315.0062228 315.0063004 315.0062228 315.005992 315.0056137 315.0050971 315.004455 315.0037033 315.0028603 315.0019469 315.0009856 315 314.9990144 314.9980531 314.9971397 314.9962967 314.995545 314.9949029 314.9943863 314.994008 314.9937772 314.9936996 314.9942794 314.9943498 314.9945594 314.9949029 314.9953719 314.9959549 314.9966375 314.9974029 314.9982322 314.9991051 315 315.0008949 315.0017678 315.0025971 315.0033625 315.0040451 315.0046281 315.0050971 315.0054406 315.0056502 315.0057206 315.0056502 315.0054406 315.0050971 315.0046281 315.0040451 315.0033625 315.0025971 315.0017678 315.0008949 315 314.9991051 314.9982322 314.9974029 314.9966375 314.9959549 314.9953719 314.9949029 314.9945594 314.9943498 314.9942794 314.995 314.9950616 314.9952447 314.995545 314.9959549 314.9964645 314.9970611 314.99773 314.9984549 314.9992178 315 315.0007822 315.0015451 315.00227 315.0029389 315.0035355 315.0040451 315.004455 315.0047553 315.0049384 315.005 315.0049384 315.0047553 315.004455 315.0040451 315.0035355 315.0029389 315.00227 315.0015451 315.0007822 315 314.9992178 314.9984549 314.99773 314.9970611 314.9964645 314.9959549 314.995545 314.9952447 314.9950616 314.995 314.9958437 314.9958949 314.9960472 314.9962967 314.9966375 314.9970611 314.997557 314.9981131 314.9987156 314.9993498 315 315.0006502 315.0012844 315.0018869 315.002443 315.0029389 315.0033625 315.0037033 315.0039528 315.0041051 315.0041563 315.0041051 315.0039528 315.0037033 315.0033625 315.0029389 315.002443 315.0018869 315.0012844 315.0006502 315 314.9993498 314.9987156 314.9981131 314.997557 314.9970611 314.9966375 314.9962967 314.9960472 314.9958949 314.9958437 314.9967898 314.9968293 314.9969469 314.9971397 314.9974029 314.99773 314.9981131 314.9985426 314.999008 314.9994978 315 315.0005022 315.000992 315.0014574 315.0018869 315.00227 315.0025971 315.0028603 315.0030531 315.0031707 315.0032102 315.0031707 315.0030531 315.0028603 315.0025971 315.00227 315.0018869 315.0014574 315.000992 315.0005022 315 314.9994978 314.999008 314.9985426 314.9981131 314.99773 314.9974029 314.9971397 314.9969469 314.9968293 314.9967898 314.9978149 314.9978418 314.9979219 314.9980531 314.9982322 314.9984549 314.9987156 314.999008 314.9993248 314.9996582 315 315.0003418 315.0006752 315.000992 315.0012844 315.0015451 315.0017678 315.0019469 315.0020781 315.0021582 315.0021851 315.0021582 315.0020781 315.0019469 315.0017678 315.0015451 315.0012844 315.000992 315.0006752 315.0003418 315 314.9996582 314.9993248 314.999008 314.9987156 314.9984549 314.9982322 314.9980531 314.9979219 314.9978418 314.9978149 314.9988938 314.9989075 314.998948 314.9990144 314.9991051 314.9992178 314.9993498 314.9994978 314.9996582 314.999827 315 315.000173 315.0003418 315.0005022 315.0006502 315.0007822 315.0008949 315.0009856 315.001052 315.0010925 315.0011062 315.0010925 315.001052 315.0009856 315.0008949 315.0007822 315.0006502 315.0005022 315.0003418 315.000173 315 314.999827 314.9996582 314.9994978 314.9993498 314.9992178 314.9991051 314.9990144 314.998948 314.9989075 314.9988938 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315 315.0011062 315.0010925 315.001052 315.0009856 315.0008949 315.0007822 315.0006502 315.0005022 315.0003418 315.000173 315 314.999827 314.9996582 314.9994978 314.9993498 314.9992178 314.9991051 314.9990144 314.998948 314.9989075 314.9988938 314.9989075 314.998948 314.9990144 314.9991051 314.9992178 314.9993498 314.9994978 314.9996582 314.999827 315 315.000173 315.0003418 315.0005022 315.0006502 315.0007822 315.0008949 315.0009856 315.001052 315.0010925 315.0011062 315.0021851 315.0021582 315.0020781 315.0019469 315.0017678 315.0015451 315.0012844 315.000992 315.0006752 315.0003418 315 314.9996582 314.9993248 314.999008 314.9987156 314.9984549 314.9982322 314.9980531 314.9979219 314.9978418 314.9978149 314.9978418 314.9979219 314.9980531 314.9982322 314.9984549 314.9987156 314.999008 314.9993248 314.9996582 315 315.0003418 315.0006752 315.000992 315.0012844 315.0015451 315.0017678 315.0019469 315.0020781 315.0021582 315.0021851 315.0032102 315.0031707 315.0030531 315.0028603 315.0025971 315.00227 315.0018869 315.0014574 315.000992 315.0005022 315 314.9994978 314.999008 314.9985426 314.9981131 314.99773 314.9974029 314.9971397 314.9969469 314.9968293 314.9967898 314.9968293 314.9969469 314.9971397 314.9974029 314.99773 314.9981131 314.9985426 314.999008 314.9994978 315 315.0005022 315.000992 315.0014574 315.0018869 315.00227 315.0025971 315.0028603 315.0030531 315.0031707 315.0032102 315.0041563 315.0041051 315.0039528 315.0037033 315.0033625 315.0029389 315.002443 315.0018869 315.0012844 315.0006502 315 314.9993498 314.9987156 314.9981131 314.997557 314.9970611 314.9966375 314.9962967 314.9960472 314.9958949 314.9958437 314.9958949 314.9960472 314.9962967 314.9966375 314.9970611 314.997557 314.9981131 314.9987156 314.9993498 315 315.0006502 315.0012844 315.0018869 315.002443 315.0029389 315.0033625 315.0037033 315.0039528 315.0041051 315.0041563 315.005 315.0049384 315.0047553 315.004455 315.0040451 315.0035355 315.0029389 315.00227 315.0015451 315.0007822 315 314.9992178 314.9984549 314.99773 314.9970611 314.9964645 314.9959549 314.995545 314.9952447 314.9950616 314.995 314.9950616 314.9952447 314.995545 314.9959549 314.9964645 314.9970611 314.99773 314.9984549 314.9992178 315 315.0007822 315.0015451 315.00227 315.0029389 315.0035355 315.0040451 315.004455 315.0047553 315.0049384 315.005 315.0057206 315.0056502 315.0054406 315.0050971 315.0046281 315.0040451 315.0033625 315.0025971 315.0017678 315.0008949 315 314.9991051 314.9982322 314.9974029 314.9966375 314.9959549 314.9953719 314.9949029 314.9945594 314.9943498 314.9942794 314.9943498 314.9945594 314.9949029 314.9953719 314.9959549 314.9966375 314.9974029 314.9982322 314.9991051 315 315.0008949 315.0017678 315.0025971 315.0033625 315.0040451 315.0046281 315.0050971 315.0054406 315.0056502 315.0057206 315.0063004 315.0062228 315.005992 315.0056137 315.0050971 315.004455 315.0037033 315.0028603 315.0019469 315.0009856 315 314.9990144 314.9980531 314.9971397 314.9962967 314.995545 314.9949029 314.9943863 314.994008 314.9937772 314.9936996 314.9937772 314.994008 314.9943863 314.9949029 314.995545 314.9962967 314.9971397 314.9980531 314.9990144 315 315.0009856 315.0019469 315.0028603 315.0037033 315.004455 315.0050971 315.0056137 315.005992 315.0062228 315.0063004 315.006725 315.0066422 315.0063958 315.005992 315.0054406 315.0047553 315.0039528 315.0030531 315.0020781 315.001052 315 314.998948 314.9979219 314.9969469 314.9960472 314.9952447 314.9945594 314.994008 314.9936042 314.9933578 314.993275 314.9933578 314.9936042 314.994008 314.9945594 314.9952447 314.9960472 314.9969469 314.9979219 314.998948 315 315.001052 315.0020781 315.0030531 315.0039528 315.0047553 315.0054406 315.005992 315.0063958 315.0066422 315.006725 315.006984 315.006898 315.0066422 315.0062228 315.0056502 315.0049384 315.0041051 315.0031707 315.0021582 315.0010925 315 314.9989075 314.9978418 314.9968293 314.9958949 314.9950616 314.9943498 314.9937772 314.9933578 314.993102 314.993016 314.993102 314.9933578 314.9937772 314.9943498 314.9950616 314.9958949 314.9968293 314.9978418 314.9989075 315 315.0010925 315.0021582 315.0031707 315.0041051 315.0049384 315.0056502 315.0062228 315.0066422 315.006898 315.006984 315.0070711 315.006984 315.006725 315.0063004 315.0057206 315.005 315.0041563 315.0032102 315.0021851 315.0011062 315 314.9988938 314.9978149 314.9967898 314.9958437 314.995 314.9942794 314.9936996 314.993275 314.993016 314.9929289 314.993016 314.993275 314.9936996 314.9942794 314.995 314.9958437 314.9967898 314.9978149 314.9988938 315 315.0011062 315.0021851 315.0032102 315.0041563 315.005 315.0057206 315.0063004 315.006725 315.006984 315.0070711 314.0080902 314.0079906 314.0076942 314.0072084 314.0065451 314.0057206 314.0047553 314.0036729 314.0025 314.0012656 314 313.9987344 313.9975 313.9963271 313.9952447 313.9942794 313.9934549 313.9927916 313.9923058 313.9920094 313.9919098 313.9920094 313.9923058 313.9927916 313.9934549 313.9942794 313.9952447 313.9963271 313.9975 313.9987344 314 314.0012656 314.0025 314.0036729 314.0047553 314.0057206 314.0065451 314.0072084 314.0076942 314.0079906 314.0080902 314.0079906 314.0078922 314.0075995 314.0071196 314.0064645 314.0056502 314.0046967 314.0036276 314.0024692 314.00125 314 313.99875 313.9975308 313.9963724 313.9953033 313.9943498 313.9935355 313.9928804 313.9924005 313.9921078 313.9920094 313.9921078 313.9924005 313.9928804 313.9935355 313.9943498 313.9953033 313.9963724 313.9975308 313.99875 314 314.00125 314.0024692 314.0036276 314.0046967 314.0056502 314.0064645 314.0071196 314.0075995 314.0078922 314.0079906 314.0076942 314.0075995 314.0073176 314.0068556 314.0062247 314.0054406 314.0045225 314.0034931 314.0023776 314.0012036 314 313.9987964 313.9976224 313.9965069 313.9954775 313.9945594 313.9937753 313.9931444 313.9926824 313.9924005 313.9923058 313.9924005 313.9926824 313.9931444 313.9937753 313.9945594 313.9954775 313.9965069 313.9976224 313.9987964 314 314.0012036 314.0023776 314.0034931 314.0045225 314.0054406 314.0062247 314.0068556 314.0073176 314.0075995 314.0076942 314.0072084 314.0071196 314.0068556 314.0064227 314.0058317 314.0050971 314.004237 314.0032725 314.0022275 314.0011276 314 313.9988724 313.9977725 313.9967275 313.995763 313.9949029 313.9941683 313.9935773 313.9931444 313.9928804 313.9927916 313.9928804 313.9931444 313.9935773 313.9941683 313.9949029 313.995763 313.9967275 313.9977725 313.9988724 314 314.0011276 314.0022275 314.0032725 314.004237 314.0050971 314.0058317 314.0064227 314.0068556 314.0071196 314.0072084 314.0065451 314.0064645 314.0062247 314.0058317 314.0052951 314.0046281 314.0038471 314.0029714 314.0020225 314.0010239 314 313.9989761 313.9979775 313.9970286 313.9961529 313.9953719 313.9947049 313.9941683 313.9937753 313.9935355 313.9934549 313.9935355 313.9937753 313.9941683 313.9947049 313.9953719 313.9961529 313.9970286 313.9979775 313.9989761 314 314.0010239 314.0020225 314.0029714 314.0038471 314.0046281 314.0052951 314.0058317 314.0062247 314.0064645 314.0065451 314.0057206 314.0056502 314.0054406 314.0050971 314.0046281 314.0040451 314.0033625 314.0025971 314.0017678 314.0008949 314 313.9991051 313.9982322 313.9974029 313.9966375 313.9959549 313.9953719 313.9949029 313.9945594 313.9943498 313.9942794 313.9943498 313.9945594 313.9949029 313.9953719 313.9959549 313.9966375 313.9974029 313.9982322 313.9991051 314 314.0008949 314.0017678 314.0025971 314.0033625 314.0040451 314.0046281 314.0050971 314.0054406 314.0056502 314.0057206 314.0047553 314.0046967 314.0045225 314.004237 314.0038471 314.0033625 314.0027951 314.0021589 314.0014695 314.0007439 314 313.9992561 313.9985305 313.9978411 313.9972049 313.9966375 313.9961529 313.995763 313.9954775 313.9953033 313.9952447 313.9953033 313.9954775 313.995763 313.9961529 313.9966375 313.9972049 313.9978411 313.9985305 313.9992561 314 314.0007439 314.0014695 314.0021589 314.0027951 314.0033625 314.0038471 314.004237 314.0045225 314.0046967 314.0047553 314.0036729 314.0036276 314.0034931 314.0032725 314.0029714 314.0025971 314.0021589 314.0016674 314.001135 314.0005746 314 313.9994254 313.998865 313.9983326 313.9978411 313.9974029 313.9970286 313.9967275 313.9965069 313.9963724 313.9963271 313.9963724 313.9965069 313.9967275 313.9970286 313.9974029 313.9978411 313.9983326 313.998865 313.9994254 314 314.0005746 314.001135 314.0016674 314.0021589 314.0025971 314.0029714 314.0032725 314.0034931 314.0036276 314.0036729 314.0025 314.0024692 314.0023776 314.0022275 314.0020225 314.0017678 314.0014695 314.001135 314.0007725 314.0003911 314 313.9996089 313.9992275 313.998865 313.9985305 313.9982322 313.9979775 313.9977725 313.9976224 313.9975308 313.9975 313.9975308 313.9976224 313.9977725 313.9979775 313.9982322 313.9985305 313.998865 313.9992275 313.9996089 314 314.0003911 314.0007725 314.001135 314.0014695 314.0017678 314.0020225 314.0022275 314.0023776 314.0024692 314.0025 314.0012656 314.00125 314.0012036 314.0011276 314.0010239 314.0008949 314.0007439 314.0005746 314.0003911 314.000198 314 313.999802 313.9996089 313.9994254 313.9992561 313.9991051 313.9989761 313.9988724 313.9987964 313.99875 313.9987344 313.99875 313.9987964 313.9988724 313.9989761 313.9991051 313.9992561 313.9994254 313.9996089 313.999802 314 314.000198 314.0003911 314.0005746 314.0007439 314.0008949 314.0010239 314.0011276 314.0012036 314.00125 314.0012656 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 313.9987344 313.99875 313.9987964 313.9988724 313.9989761 313.9991051 313.9992561 313.9994254 313.9996089 313.999802 314 314.000198 314.0003911 314.0005746 314.0007439 314.0008949 314.0010239 314.0011276 314.0012036 314.00125 314.0012656 314.00125 314.0012036 314.0011276 314.0010239 314.0008949 314.0007439 314.0005746 314.0003911 314.000198 314 313.999802 313.9996089 313.9994254 313.9992561 313.9991051 313.9989761 313.9988724 313.9987964 313.99875 313.9987344 313.9975 313.9975308 313.9976224 313.9977725 313.9979775 313.9982322 313.9985305 313.998865 313.9992275 313.9996089 314 314.0003911 314.0007725 314.001135 314.0014695 314.0017678 314.0020225 314.0022275 314.0023776 314.0024692 314.0025 314.0024692 314.0023776 314.0022275 314.0020225 314.0017678 314.0014695 314.001135 314.0007725 314.0003911 314 313.9996089 313.9992275 313.998865 313.9985305 313.9982322 313.9979775 313.9977725 313.9976224 313.9975308 313.9975 313.9963271 313.9963724 313.9965069 313.9967275 313.9970286 313.9974029 313.9978411 313.9983326 313.998865 313.9994254 314 314.0005746 314.001135 314.0016674 314.0021589 314.0025971 314.0029714 314.0032725 314.0034931 314.0036276 314.0036729 314.0036276 314.0034931 314.0032725 314.0029714 314.0025971 314.0021589 314.0016674 314.001135 314.0005746 314 313.9994254 313.998865 313.9983326 313.9978411 313.9974029 313.9970286 313.9967275 313.9965069 313.9963724 313.9963271 313.9952447 313.9953033 313.9954775 313.995763 313.9961529 313.9966375 313.9972049 313.9978411 313.9985305 313.9992561 314 314.0007439 314.0014695 314.0021589 314.0027951 314.0033625 314.0038471 314.004237 314.0045225 314.0046967 314.0047553 314.0046967 314.0045225 314.004237 314.0038471 314.0033625 314.0027951 314.0021589 314.0014695 314.0007439 314 313.9992561 313.9985305 313.9978411 313.9972049 313.9966375 313.9961529 313.995763 313.9954775 313.9953033 313.9952447 313.9942794 313.9943498 313.9945594 313.9949029 313.9953719 313.9959549 313.9966375 313.9974029 313.9982322 313.9991051 314 314.0008949 314.0017678 314.0025971 314.0033625 314.0040451 314.0046281 314.0050971 314.0054406 314.0056502 314.0057206 314.0056502 314.0054406 314.0050971 314.0046281 314.0040451 314.0033625 314.0025971 314.0017678 314.0008949 314 313.9991051 313.9982322 313.9974029 313.9966375 313.9959549 313.9953719 313.9949029 313.9945594 313.9943498 313.9942794 313.9934549 313.9935355 313.9937753 313.9941683 313.9947049 313.9953719 313.9961529 313.9970286 313.9979775 313.9989761 314 314.0010239 314.0020225 314.0029714 314.0038471 314.0046281 314.0052951 314.0058317 314.0062247 314.0064645 314.0065451 314.0064645 314.0062247 314.0058317 314.0052951 314.0046281 314.0038471 314.0029714 314.0020225 314.0010239 314 313.9989761 313.9979775 313.9970286 313.9961529 313.9953719 313.9947049 313.9941683 313.9937753 313.9935355 313.9934549 313.9927916 313.9928804 313.9931444 313.9935773 313.9941683 313.9949029 313.995763 313.9967275 313.9977725 313.9988724 314 314.0011276 314.0022275 314.0032725 314.004237 314.0050971 314.0058317 314.0064227 314.0068556 314.0071196 314.0072084 314.0071196 314.0068556 314.0064227 314.0058317 314.0050971 314.004237 314.0032725 314.0022275 314.0011276 314 313.9988724 313.9977725 313.9967275 313.995763 313.9949029 313.9941683 313.9935773 313.9931444 313.9928804 313.9927916 313.9923058 313.9924005 313.9926824 313.9931444 313.9937753 313.9945594 313.9954775 313.9965069 313.9976224 313.9987964 314 314.0012036 314.0023776 314.0034931 314.0045225 314.0054406 314.0062247 314.0068556 314.0073176 314.0075995 314.0076942 314.0075995 314.0073176 314.0068556 314.0062247 314.0054406 314.0045225 314.0034931 314.0023776 314.0012036 314 313.9987964 313.9976224 313.9965069 313.9954775 313.9945594 313.9937753 313.9931444 313.9926824 313.9924005 313.9923058 313.9920094 313.9921078 313.9924005 313.9928804 313.9935355 313.9943498 313.9953033 313.9963724 313.9975308 313.99875 314 314.00125 314.0024692 314.0036276 314.0046967 314.0056502 314.0064645 314.0071196 314.0075995 314.0078922 314.0079906 314.0078922 314.0075995 314.0071196 314.0064645 314.0056502 314.0046967 314.0036276 314.0024692 314.00125 314 313.99875 313.9975308 313.9963724 313.9953033 313.9943498 313.9935355 313.9928804 313.9924005 313.9921078 313.9920094 313.9919098 313.9920094 313.9923058 313.9927916 313.9934549 313.9942794 313.9952447 313.9963271 313.9975 313.9987344 314 314.0012656 314.0025 314.0036729 314.0047553 314.0057206 314.0065451 314.0072084 314.0076942 314.0079906 314.0080902 314.0079906 314.0076942 314.0072084 314.0065451 314.0057206 314.0047553 314.0036729 314.0025 314.0012656 314 313.9987344 313.9975 313.9963271 313.9952447 313.9942794 313.9934549 313.9927916 313.9923058 313.9920094 313.9919098 313.9920094 313.9921078 313.9924005 313.9928804 313.9935355 313.9943498 313.9953033 313.9963724 313.9975308 313.99875 314 314.00125 314.0024692 314.0036276 314.0046967 314.0056502 314.0064645 314.0071196 314.0075995 314.0078922 314.0079906 314.0078922 314.0075995 314.0071196 314.0064645 314.0056502 314.0046967 314.0036276 314.0024692 314.00125 314 313.99875 313.9975308 313.9963724 313.9953033 313.9943498 313.9935355 313.9928804 313.9924005 313.9921078 313.9920094 313.9923058 313.9924005 313.9926824 313.9931444 313.9937753 313.9945594 313.9954775 313.9965069 313.9976224 313.9987964 314 314.0012036 314.0023776 314.0034931 314.0045225 314.0054406 314.0062247 314.0068556 314.0073176 314.0075995 314.0076942 314.0075995 314.0073176 314.0068556 314.0062247 314.0054406 314.0045225 314.0034931 314.0023776 314.0012036 314 313.9987964 313.9976224 313.9965069 313.9954775 313.9945594 313.9937753 313.9931444 313.9926824 313.9924005 313.9923058 313.9927916 313.9928804 313.9931444 313.9935773 313.9941683 313.9949029 313.995763 313.9967275 313.9977725 313.9988724 314 314.0011276 314.0022275 314.0032725 314.004237 314.0050971 314.0058317 314.0064227 314.0068556 314.0071196 314.0072084 314.0071196 314.0068556 314.0064227 314.0058317 314.0050971 314.004237 314.0032725 314.0022275 314.0011276 314 313.9988724 313.9977725 313.9967275 313.995763 313.9949029 313.9941683 313.9935773 313.9931444 313.9928804 313.9927916 313.9934549 313.9935355 313.9937753 313.9941683 313.9947049 313.9953719 313.9961529 313.9970286 313.9979775 313.9989761 314 314.0010239 314.0020225 314.0029714 314.0038471 314.0046281 314.0052951 314.0058317 314.0062247 314.0064645 314.0065451 314.0064645 314.0062247 314.0058317 314.0052951 314.0046281 314.0038471 314.0029714 314.0020225 314.0010239 314 313.9989761 313.9979775 313.9970286 313.9961529 313.9953719 313.9947049 313.9941683 313.9937753 313.9935355 313.9934549 313.9942794 313.9943498 313.9945594 313.9949029 313.9953719 313.9959549 313.9966375 313.9974029 313.9982322 313.9991051 314 314.0008949 314.0017678 314.0025971 314.0033625 314.0040451 314.0046281 314.0050971 314.0054406 314.0056502 314.0057206 314.0056502 314.0054406 314.0050971 314.0046281 314.0040451 314.0033625 314.0025971 314.0017678 314.0008949 314 313.9991051 313.9982322 313.9974029 313.9966375 313.9959549 313.9953719 313.9949029 313.9945594 313.9943498 313.9942794 313.9952447 313.9953033 313.9954775 313.995763 313.9961529 313.9966375 313.9972049 313.9978411 313.9985305 313.9992561 314 314.0007439 314.0014695 314.0021589 314.0027951 314.0033625 314.0038471 314.004237 314.0045225 314.0046967 314.0047553 314.0046967 314.0045225 314.004237 314.0038471 314.0033625 314.0027951 314.0021589 314.0014695 314.0007439 314 313.9992561 313.9985305 313.9978411 313.9972049 313.9966375 313.9961529 313.995763 313.9954775 313.9953033 313.9952447 313.9963271 313.9963724 313.9965069 313.9967275 313.9970286 313.9974029 313.9978411 313.9983326 313.998865 313.9994254 314 314.0005746 314.001135 314.0016674 314.0021589 314.0025971 314.0029714 314.0032725 314.0034931 314.0036276 314.0036729 314.0036276 314.0034931 314.0032725 314.0029714 314.0025971 314.0021589 314.0016674 314.001135 314.0005746 314 313.9994254 313.998865 313.9983326 313.9978411 313.9974029 313.9970286 313.9967275 313.9965069 313.9963724 313.9963271 313.9975 313.9975308 313.9976224 313.9977725 313.9979775 313.9982322 313.9985305 313.998865 313.9992275 313.9996089 314 314.0003911 314.0007725 314.001135 314.0014695 314.0017678 314.0020225 314.0022275 314.0023776 314.0024692 314.0025 314.0024692 314.0023776 314.0022275 314.0020225 314.0017678 314.0014695 314.001135 314.0007725 314.0003911 314 313.9996089 313.9992275 313.998865 313.9985305 313.9982322 313.9979775 313.9977725 313.9976224 313.9975308 313.9975 313.9987344 313.99875 313.9987964 313.9988724 313.9989761 313.9991051 313.9992561 313.9994254 313.9996089 313.999802 314 314.000198 314.0003911 314.0005746 314.0007439 314.0008949 314.0010239 314.0011276 314.0012036 314.00125 314.0012656 314.00125 314.0012036 314.0011276 314.0010239 314.0008949 314.0007439 314.0005746 314.0003911 314.000198 314 313.999802 313.9996089 313.9994254 313.9992561 313.9991051 313.9989761 313.9988724 313.9987964 313.99875 313.9987344 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314 314.0012656 314.00125 314.0012036 314.0011276 314.0010239 314.0008949 314.0007439 314.0005746 314.0003911 314.000198 314 313.999802 313.9996089 313.9994254 313.9992561 313.9991051 313.9989761 313.9988724 313.9987964 313.99875 313.9987344 313.99875 313.9987964 313.9988724 313.9989761 313.9991051 313.9992561 313.9994254 313.9996089 313.999802 314 314.000198 314.0003911 314.0005746 314.0007439 314.0008949 314.0010239 314.0011276 314.0012036 314.00125 314.0012656 314.0025 314.0024692 314.0023776 314.0022275 314.0020225 314.0017678 314.0014695 314.001135 314.0007725 314.0003911 314 313.9996089 313.9992275 313.998865 313.9985305 313.9982322 313.9979775 313.9977725 313.9976224 313.9975308 313.9975 313.9975308 313.9976224 313.9977725 313.9979775 313.9982322 313.9985305 313.998865 313.9992275 313.9996089 314 314.0003911 314.0007725 314.001135 314.0014695 314.0017678 314.0020225 314.0022275 314.0023776 314.0024692 314.0025 314.0036729 314.0036276 314.0034931 314.0032725 314.0029714 314.0025971 314.0021589 314.0016674 314.001135 314.0005746 314 313.9994254 313.998865 313.9983326 313.9978411 313.9974029 313.9970286 313.9967275 313.9965069 313.9963724 313.9963271 313.9963724 313.9965069 313.9967275 313.9970286 313.9974029 313.9978411 313.9983326 313.998865 313.9994254 314 314.0005746 314.001135 314.0016674 314.0021589 314.0025971 314.0029714 314.0032725 314.0034931 314.0036276 314.0036729 314.0047553 314.0046967 314.0045225 314.004237 314.0038471 314.0033625 314.0027951 314.0021589 314.0014695 314.0007439 314 313.9992561 313.9985305 313.9978411 313.9972049 313.9966375 313.9961529 313.995763 313.9954775 313.9953033 313.9952447 313.9953033 313.9954775 313.995763 313.9961529 313.9966375 313.9972049 313.9978411 313.9985305 313.9992561 314 314.0007439 314.0014695 314.0021589 314.0027951 314.0033625 314.0038471 314.004237 314.0045225 314.0046967 314.0047553 314.0057206 314.0056502 314.0054406 314.0050971 314.0046281 314.0040451 314.0033625 314.0025971 314.0017678 314.0008949 314 313.9991051 313.9982322 313.9974029 313.9966375 313.9959549 313.9953719 313.9949029 313.9945594 313.9943498 313.9942794 313.9943498 313.9945594 313.9949029 313.9953719 313.9959549 313.9966375 313.9974029 313.9982322 313.9991051 314 314.0008949 314.0017678 314.0025971 314.0033625 314.0040451 314.0046281 314.0050971 314.0054406 314.0056502 314.0057206 314.0065451 314.0064645 314.0062247 314.0058317 314.0052951 314.0046281 314.0038471 314.0029714 314.0020225 314.0010239 314 313.9989761 313.9979775 313.9970286 313.9961529 313.9953719 313.9947049 313.9941683 313.9937753 313.9935355 313.9934549 313.9935355 313.9937753 313.9941683 313.9947049 313.9953719 313.9961529 313.9970286 313.9979775 313.9989761 314 314.0010239 314.0020225 314.0029714 314.0038471 314.0046281 314.0052951 314.0058317 314.0062247 314.0064645 314.0065451 314.0072084 314.0071196 314.0068556 314.0064227 314.0058317 314.0050971 314.004237 314.0032725 314.0022275 314.0011276 314 313.9988724 313.9977725 313.9967275 313.995763 313.9949029 313.9941683 313.9935773 313.9931444 313.9928804 313.9927916 313.9928804 313.9931444 313.9935773 313.9941683 313.9949029 313.995763 313.9967275 313.9977725 313.9988724 314 314.0011276 314.0022275 314.0032725 314.004237 314.0050971 314.0058317 314.0064227 314.0068556 314.0071196 314.0072084 314.0076942 314.0075995 314.0073176 314.0068556 314.0062247 314.0054406 314.0045225 314.0034931 314.0023776 314.0012036 314 313.9987964 313.9976224 313.9965069 313.9954775 313.9945594 313.9937753 313.9931444 313.9926824 313.9924005 313.9923058 313.9924005 313.9926824 313.9931444 313.9937753 313.9945594 313.9954775 313.9965069 313.9976224 313.9987964 314 314.0012036 314.0023776 314.0034931 314.0045225 314.0054406 314.0062247 314.0068556 314.0073176 314.0075995 314.0076942 314.0079906 314.0078922 314.0075995 314.0071196 314.0064645 314.0056502 314.0046967 314.0036276 314.0024692 314.00125 314 313.99875 313.9975308 313.9963724 313.9953033 313.9943498 313.9935355 313.9928804 313.9924005 313.9921078 313.9920094 313.9921078 313.9924005 313.9928804 313.9935355 313.9943498 313.9953033 313.9963724 313.9975308 313.99875 314 314.00125 314.0024692 314.0036276 314.0046967 314.0056502 314.0064645 314.0071196 314.0075995 314.0078922 314.0079906 314.0080902 314.0079906 314.0076942 314.0072084 314.0065451 314.0057206 314.0047553 314.0036729 314.0025 314.0012656 314 313.9987344 313.9975 313.9963271 313.9952447 313.9942794 313.9934549 313.9927916 313.9923058 313.9920094 313.9919098 313.9920094 313.9923058 313.9927916 313.9934549 313.9942794 313.9952447 313.9963271 313.9975 313.9987344 314 314.0012656 314.0025 314.0036729 314.0047553 314.0057206 314.0065451 314.0072084 314.0076942 314.0079906 314.0080902 313.0089101 313.0088004 313.008474 313.0079389 313.0072084 313.0063004 313.0052372 313.0040451 313.0027534 313.0013938 313 312.9986062 312.9972466 312.9959549 312.9947628 312.9936996 312.9927916 312.9920611 312.991526 312.9911996 312.9910899 312.9911996 312.991526 312.9920611 312.9927916 312.9936996 312.9947628 312.9959549 312.9972466 312.9986062 313 313.0013938 313.0027534 313.0040451 313.0052372 313.0063004 313.0072084 313.0079389 313.008474 313.0088004 313.0089101 313.0088004 313.008692 313.0083696 313.0078412 313.0071196 313.0062228 313.0051727 313.0039953 313.0027195 313.0013767 313 312.9986233 312.9972805 312.9960047 312.9948273 312.9937772 312.9928804 312.9921588 312.9916304 312.991308 312.9911996 312.991308 312.9916304 312.9921588 312.9928804 312.9937772 312.9948273 312.9960047 312.9972805 312.9986233 313 313.0013767 313.0027195 313.0039953 313.0051727 313.0062228 313.0071196 313.0078412 313.0083696 313.008692 313.0088004 313.008474 313.0083696 313.0080592 313.0075504 313.0068556 313.005992 313.0049809 313.0038471 313.0026186 313.0013256 313 312.9986744 312.9973814 312.9961529 312.9950191 312.994008 312.9931444 312.9924496 312.9919408 312.9916304 312.991526 312.9916304 312.9919408 312.9924496 312.9931444 312.994008 312.9950191 312.9961529 312.9973814 312.9986744 313 313.0013256 313.0026186 313.0038471 313.0049809 313.005992 313.0068556 313.0075504 313.0080592 313.0083696 313.008474 313.0079389 313.0078412 313.0075504 313.0070736 313.0064227 313.0056137 313.0046664 313.0036042 313.0024533 313.0012419 313 312.9987581 312.9975467 312.9963958 312.9953336 312.9943863 312.9935773 312.9929264 312.9924496 312.9921588 312.9920611 312.9921588 312.9924496 312.9929264 312.9935773 312.9943863 312.9953336 312.9963958 312.9975467 312.9987581 313 313.0012419 313.0024533 313.0036042 313.0046664 313.0056137 313.0064227 313.0070736 313.0075504 313.0078412 313.0079389 313.0072084 313.0071196 313.0068556 313.0064227 313.0058317 313.0050971 313.004237 313.0032725 313.0022275 313.0011276 313 312.9988724 312.9977725 312.9967275 312.995763 312.9949029 312.9941683 312.9935773 312.9931444 312.9928804 312.9927916 312.9928804 312.9931444 312.9935773 312.9941683 312.9949029 312.995763 312.9967275 312.9977725 312.9988724 313 313.0011276 313.0022275 313.0032725 313.004237 313.0050971 313.0058317 313.0064227 313.0068556 313.0071196 313.0072084 313.0063004 313.0062228 313.005992 313.0056137 313.0050971 313.004455 313.0037033 313.0028603 313.0019469 313.0009856 313 312.9990144 312.9980531 312.9971397 312.9962967 312.995545 312.9949029 312.9943863 312.994008 312.9937772 312.9936996 312.9937772 312.994008 312.9943863 312.9949029 312.995545 312.9962967 312.9971397 312.9980531 312.9990144 313 313.0009856 313.0019469 313.0028603 313.0037033 313.004455 313.0050971 313.0056137 313.005992 313.0062228 313.0063004 313.0052372 313.0051727 313.0049809 313.0046664 313.004237 313.0037033 313.0030784 313.0023776 313.0016184 313.0008193 313 312.9991807 312.9983816 312.9976224 312.9969216 312.9962967 312.995763 312.9953336 312.9950191 312.9948273 312.9947628 312.9948273 312.9950191 312.9953336 312.995763 312.9962967 312.9969216 312.9976224 312.9983816 312.9991807 313 313.0008193 313.0016184 313.0023776 313.0030784 313.0037033 313.004237 313.0046664 313.0049809 313.0051727 313.0052372 313.0040451 313.0039953 313.0038471 313.0036042 313.0032725 313.0028603 313.0023776 313.0018364 313.00125 313.0006328 313 312.9993672 312.99875 312.9981636 312.9976224 312.9971397 312.9967275 312.9963958 312.9961529 312.9960047 312.9959549 312.9960047 312.9961529 312.9963958 312.9967275 312.9971397 312.9976224 312.9981636 312.99875 312.9993672 313 313.0006328 313.00125 313.0018364 313.0023776 313.0028603 313.0032725 313.0036042 313.0038471 313.0039953 313.0040451 313.0027534 313.0027195 313.0026186 313.0024533 313.0022275 313.0019469 313.0016184 313.00125 313.0008508 313.0004307 313 312.9995693 312.9991492 312.99875 312.9983816 312.9980531 312.9977725 312.9975467 312.9973814 312.9972805 312.9972466 312.9972805 312.9973814 312.9975467 312.9977725 312.9980531 312.9983816 312.99875 312.9991492 312.9995693 313 313.0004307 313.0008508 313.00125 313.0016184 313.0019469 313.0022275 313.0024533 313.0026186 313.0027195 313.0027534 313.0013938 313.0013767 313.0013256 313.0012419 313.0011276 313.0009856 313.0008193 313.0006328 313.0004307 313.000218 313 312.999782 312.9995693 312.9993672 312.9991807 312.9990144 312.9988724 312.9987581 312.9986744 312.9986233 312.9986062 312.9986233 312.9986744 312.9987581 312.9988724 312.9990144 312.9991807 312.9993672 312.9995693 312.999782 313 313.000218 313.0004307 313.0006328 313.0008193 313.0009856 313.0011276 313.0012419 313.0013256 313.0013767 313.0013938 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 312.9986062 312.9986233 312.9986744 312.9987581 312.9988724 312.9990144 312.9991807 312.9993672 312.9995693 312.999782 313 313.000218 313.0004307 313.0006328 313.0008193 313.0009856 313.0011276 313.0012419 313.0013256 313.0013767 313.0013938 313.0013767 313.0013256 313.0012419 313.0011276 313.0009856 313.0008193 313.0006328 313.0004307 313.000218 313 312.999782 312.9995693 312.9993672 312.9991807 312.9990144 312.9988724 312.9987581 312.9986744 312.9986233 312.9986062 312.9972466 312.9972805 312.9973814 312.9975467 312.9977725 312.9980531 312.9983816 312.99875 312.9991492 312.9995693 313 313.0004307 313.0008508 313.00125 313.0016184 313.0019469 313.0022275 313.0024533 313.0026186 313.0027195 313.0027534 313.0027195 313.0026186 313.0024533 313.0022275 313.0019469 313.0016184 313.00125 313.0008508 313.0004307 313 312.9995693 312.9991492 312.99875 312.9983816 312.9980531 312.9977725 312.9975467 312.9973814 312.9972805 312.9972466 312.9959549 312.9960047 312.9961529 312.9963958 312.9967275 312.9971397 312.9976224 312.9981636 312.99875 312.9993672 313 313.0006328 313.00125 313.0018364 313.0023776 313.0028603 313.0032725 313.0036042 313.0038471 313.0039953 313.0040451 313.0039953 313.0038471 313.0036042 313.0032725 313.0028603 313.0023776 313.0018364 313.00125 313.0006328 313 312.9993672 312.99875 312.9981636 312.9976224 312.9971397 312.9967275 312.9963958 312.9961529 312.9960047 312.9959549 312.9947628 312.9948273 312.9950191 312.9953336 312.995763 312.9962967 312.9969216 312.9976224 312.9983816 312.9991807 313 313.0008193 313.0016184 313.0023776 313.0030784 313.0037033 313.004237 313.0046664 313.0049809 313.0051727 313.0052372 313.0051727 313.0049809 313.0046664 313.004237 313.0037033 313.0030784 313.0023776 313.0016184 313.0008193 313 312.9991807 312.9983816 312.9976224 312.9969216 312.9962967 312.995763 312.9953336 312.9950191 312.9948273 312.9947628 312.9936996 312.9937772 312.994008 312.9943863 312.9949029 312.995545 312.9962967 312.9971397 312.9980531 312.9990144 313 313.0009856 313.0019469 313.0028603 313.0037033 313.004455 313.0050971 313.0056137 313.005992 313.0062228 313.0063004 313.0062228 313.005992 313.0056137 313.0050971 313.004455 313.0037033 313.0028603 313.0019469 313.0009856 313 312.9990144 312.9980531 312.9971397 312.9962967 312.995545 312.9949029 312.9943863 312.994008 312.9937772 312.9936996 312.9927916 312.9928804 312.9931444 312.9935773 312.9941683 312.9949029 312.995763 312.9967275 312.9977725 312.9988724 313 313.0011276 313.0022275 313.0032725 313.004237 313.0050971 313.0058317 313.0064227 313.0068556 313.0071196 313.0072084 313.0071196 313.0068556 313.0064227 313.0058317 313.0050971 313.004237 313.0032725 313.0022275 313.0011276 313 312.9988724 312.9977725 312.9967275 312.995763 312.9949029 312.9941683 312.9935773 312.9931444 312.9928804 312.9927916 312.9920611 312.9921588 312.9924496 312.9929264 312.9935773 312.9943863 312.9953336 312.9963958 312.9975467 312.9987581 313 313.0012419 313.0024533 313.0036042 313.0046664 313.0056137 313.0064227 313.0070736 313.0075504 313.0078412 313.0079389 313.0078412 313.0075504 313.0070736 313.0064227 313.0056137 313.0046664 313.0036042 313.0024533 313.0012419 313 312.9987581 312.9975467 312.9963958 312.9953336 312.9943863 312.9935773 312.9929264 312.9924496 312.9921588 312.9920611 312.991526 312.9916304 312.9919408 312.9924496 312.9931444 312.994008 312.9950191 312.9961529 312.9973814 312.9986744 313 313.0013256 313.0026186 313.0038471 313.0049809 313.005992 313.0068556 313.0075504 313.0080592 313.0083696 313.008474 313.0083696 313.0080592 313.0075504 313.0068556 313.005992 313.0049809 313.0038471 313.0026186 313.0013256 313 312.9986744 312.9973814 312.9961529 312.9950191 312.994008 312.9931444 312.9924496 312.9919408 312.9916304 312.991526 312.9911996 312.991308 312.9916304 312.9921588 312.9928804 312.9937772 312.9948273 312.9960047 312.9972805 312.9986233 313 313.0013767 313.0027195 313.0039953 313.0051727 313.0062228 313.0071196 313.0078412 313.0083696 313.008692 313.0088004 313.008692 313.0083696 313.0078412 313.0071196 313.0062228 313.0051727 313.0039953 313.0027195 313.0013767 313 312.9986233 312.9972805 312.9960047 312.9948273 312.9937772 312.9928804 312.9921588 312.9916304 312.991308 312.9911996 312.9910899 312.9911996 312.991526 312.9920611 312.9927916 312.9936996 312.9947628 312.9959549 312.9972466 312.9986062 313 313.0013938 313.0027534 313.0040451 313.0052372 313.0063004 313.0072084 313.0079389 313.008474 313.0088004 313.0089101 313.0088004 313.008474 313.0079389 313.0072084 313.0063004 313.0052372 313.0040451 313.0027534 313.0013938 313 312.9986062 312.9972466 312.9959549 312.9947628 312.9936996 312.9927916 312.9920611 312.991526 312.9911996 312.9910899 312.9911996 312.991308 312.9916304 312.9921588 312.9928804 312.9937772 312.9948273 312.9960047 312.9972805 312.9986233 313 313.0013767 313.0027195 313.0039953 313.0051727 313.0062228 313.0071196 313.0078412 313.0083696 313.008692 313.0088004 313.008692 313.0083696 313.0078412 313.0071196 313.0062228 313.0051727 313.0039953 313.0027195 313.0013767 313 312.9986233 312.9972805 312.9960047 312.9948273 312.9937772 312.9928804 312.9921588 312.9916304 312.991308 312.9911996 312.991526 312.9916304 312.9919408 312.9924496 312.9931444 312.994008 312.9950191 312.9961529 312.9973814 312.9986744 313 313.0013256 313.0026186 313.0038471 313.0049809 313.005992 313.0068556 313.0075504 313.0080592 313.0083696 313.008474 313.0083696 313.0080592 313.0075504 313.0068556 313.005992 313.0049809 313.0038471 313.0026186 313.0013256 313 312.9986744 312.9973814 312.9961529 312.9950191 312.994008 312.9931444 312.9924496 312.9919408 312.9916304 312.991526 312.9920611 312.9921588 312.9924496 312.9929264 312.9935773 312.9943863 312.9953336 312.9963958 312.9975467 312.9987581 313 313.0012419 313.0024533 313.0036042 313.0046664 313.0056137 313.0064227 313.0070736 313.0075504 313.0078412 313.0079389 313.0078412 313.0075504 313.0070736 313.0064227 313.0056137 313.0046664 313.0036042 313.0024533 313.0012419 313 312.9987581 312.9975467 312.9963958 312.9953336 312.9943863 312.9935773 312.9929264 312.9924496 312.9921588 312.9920611 312.9927916 312.9928804 312.9931444 312.9935773 312.9941683 312.9949029 312.995763 312.9967275 312.9977725 312.9988724 313 313.0011276 313.0022275 313.0032725 313.004237 313.0050971 313.0058317 313.0064227 313.0068556 313.0071196 313.0072084 313.0071196 313.0068556 313.0064227 313.0058317 313.0050971 313.004237 313.0032725 313.0022275 313.0011276 313 312.9988724 312.9977725 312.9967275 312.995763 312.9949029 312.9941683 312.9935773 312.9931444 312.9928804 312.9927916 312.9936996 312.9937772 312.994008 312.9943863 312.9949029 312.995545 312.9962967 312.9971397 312.9980531 312.9990144 313 313.0009856 313.0019469 313.0028603 313.0037033 313.004455 313.0050971 313.0056137 313.005992 313.0062228 313.0063004 313.0062228 313.005992 313.0056137 313.0050971 313.004455 313.0037033 313.0028603 313.0019469 313.0009856 313 312.9990144 312.9980531 312.9971397 312.9962967 312.995545 312.9949029 312.9943863 312.994008 312.9937772 312.9936996 312.9947628 312.9948273 312.9950191 312.9953336 312.995763 312.9962967 312.9969216 312.9976224 312.9983816 312.9991807 313 313.0008193 313.0016184 313.0023776 313.0030784 313.0037033 313.004237 313.0046664 313.0049809 313.0051727 313.0052372 313.0051727 313.0049809 313.0046664 313.004237 313.0037033 313.0030784 313.0023776 313.0016184 313.0008193 313 312.9991807 312.9983816 312.9976224 312.9969216 312.9962967 312.995763 312.9953336 312.9950191 312.9948273 312.9947628 312.9959549 312.9960047 312.9961529 312.9963958 312.9967275 312.9971397 312.9976224 312.9981636 312.99875 312.9993672 313 313.0006328 313.00125 313.0018364 313.0023776 313.0028603 313.0032725 313.0036042 313.0038471 313.0039953 313.0040451 313.0039953 313.0038471 313.0036042 313.0032725 313.0028603 313.0023776 313.0018364 313.00125 313.0006328 313 312.9993672 312.99875 312.9981636 312.9976224 312.9971397 312.9967275 312.9963958 312.9961529 312.9960047 312.9959549 312.9972466 312.9972805 312.9973814 312.9975467 312.9977725 312.9980531 312.9983816 312.99875 312.9991492 312.9995693 313 313.0004307 313.0008508 313.00125 313.0016184 313.0019469 313.0022275 313.0024533 313.0026186 313.0027195 313.0027534 313.0027195 313.0026186 313.0024533 313.0022275 313.0019469 313.0016184 313.00125 313.0008508 313.0004307 313 312.9995693 312.9991492 312.99875 312.9983816 312.9980531 312.9977725 312.9975467 312.9973814 312.9972805 312.9972466 312.9986062 312.9986233 312.9986744 312.9987581 312.9988724 312.9990144 312.9991807 312.9993672 312.9995693 312.999782 313 313.000218 313.0004307 313.0006328 313.0008193 313.0009856 313.0011276 313.0012419 313.0013256 313.0013767 313.0013938 313.0013767 313.0013256 313.0012419 313.0011276 313.0009856 313.0008193 313.0006328 313.0004307 313.000218 313 312.999782 312.9995693 312.9993672 312.9991807 312.9990144 312.9988724 312.9987581 312.9986744 312.9986233 312.9986062 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313 313.0013938 313.0013767 313.0013256 313.0012419 313.0011276 313.0009856 313.0008193 313.0006328 313.0004307 313.000218 313 312.999782 312.9995693 312.9993672 312.9991807 312.9990144 312.9988724 312.9987581 312.9986744 312.9986233 312.9986062 312.9986233 312.9986744 312.9987581 312.9988724 312.9990144 312.9991807 312.9993672 312.9995693 312.999782 313 313.000218 313.0004307 313.0006328 313.0008193 313.0009856 313.0011276 313.0012419 313.0013256 313.0013767 313.0013938 313.0027534 313.0027195 313.0026186 313.0024533 313.0022275 313.0019469 313.0016184 313.00125 313.0008508 313.0004307 313 312.9995693 312.9991492 312.99875 312.9983816 312.9980531 312.9977725 312.9975467 312.9973814 312.9972805 312.9972466 312.9972805 312.9973814 312.9975467 312.9977725 312.9980531 312.9983816 312.99875 312.9991492 312.9995693 313 313.0004307 313.0008508 313.00125 313.0016184 313.0019469 313.0022275 313.0024533 313.0026186 313.0027195 313.0027534 313.0040451 313.0039953 313.0038471 313.0036042 313.0032725 313.0028603 313.0023776 313.0018364 313.00125 313.0006328 313 312.9993672 312.99875 312.9981636 312.9976224 312.9971397 312.9967275 312.9963958 312.9961529 312.9960047 312.9959549 312.9960047 312.9961529 312.9963958 312.9967275 312.9971397 312.9976224 312.9981636 312.99875 312.9993672 313 313.0006328 313.00125 313.0018364 313.0023776 313.0028603 313.0032725 313.0036042 313.0038471 313.0039953 313.0040451 313.0052372 313.0051727 313.0049809 313.0046664 313.004237 313.0037033 313.0030784 313.0023776 313.0016184 313.0008193 313 312.9991807 312.9983816 312.9976224 312.9969216 312.9962967 312.995763 312.9953336 312.9950191 312.9948273 312.9947628 312.9948273 312.9950191 312.9953336 312.995763 312.9962967 312.9969216 312.9976224 312.9983816 312.9991807 313 313.0008193 313.0016184 313.0023776 313.0030784 313.0037033 313.004237 313.0046664 313.0049809 313.0051727 313.0052372 313.0063004 313.0062228 313.005992 313.0056137 313.0050971 313.004455 313.0037033 313.0028603 313.0019469 313.0009856 313 312.9990144 312.9980531 312.9971397 312.9962967 312.995545 312.9949029 312.9943863 312.994008 312.9937772 312.9936996 312.9937772 312.994008 312.9943863 312.9949029 312.995545 312.9962967 312.9971397 312.9980531 312.9990144 313 313.0009856 313.0019469 313.0028603 313.0037033 313.004455 313.0050971 313.0056137 313.005992 313.0062228 313.0063004 313.0072084 313.0071196 313.0068556 313.0064227 313.0058317 313.0050971 313.004237 313.0032725 313.0022275 313.0011276 313 312.9988724 312.9977725 312.9967275 312.995763 312.9949029 312.9941683 312.9935773 312.9931444 312.9928804 312.9927916 312.9928804 312.9931444 312.9935773 312.9941683 312.9949029 312.995763 312.9967275 312.9977725 312.9988724 313 313.0011276 313.0022275 313.0032725 313.004237 313.0050971 313.0058317 313.0064227 313.0068556 313.0071196 313.0072084 313.0079389 313.0078412 313.0075504 313.0070736 313.0064227 313.0056137 313.0046664 313.0036042 313.0024533 313.0012419 313 312.9987581 312.9975467 312.9963958 312.9953336 312.9943863 312.9935773 312.9929264 312.9924496 312.9921588 312.9920611 312.9921588 312.9924496 312.9929264 312.9935773 312.9943863 312.9953336 312.9963958 312.9975467 312.9987581 313 313.0012419 313.0024533 313.0036042 313.0046664 313.0056137 313.0064227 313.0070736 313.0075504 313.0078412 313.0079389 313.008474 313.0083696 313.0080592 313.0075504 313.0068556 313.005992 313.0049809 313.0038471 313.0026186 313.0013256 313 312.9986744 312.9973814 312.9961529 312.9950191 312.994008 312.9931444 312.9924496 312.9919408 312.9916304 312.991526 312.9916304 312.9919408 312.9924496 312.9931444 312.994008 312.9950191 312.9961529 312.9973814 312.9986744 313 313.0013256 313.0026186 313.0038471 313.0049809 313.005992 313.0068556 313.0075504 313.0080592 313.0083696 313.008474 313.0088004 313.008692 313.0083696 313.0078412 313.0071196 313.0062228 313.0051727 313.0039953 313.0027195 313.0013767 313 312.9986233 312.9972805 312.9960047 312.9948273 312.9937772 312.9928804 312.9921588 312.9916304 312.991308 312.9911996 312.991308 312.9916304 312.9921588 312.9928804 312.9937772 312.9948273 312.9960047 312.9972805 312.9986233 313 313.0013767 313.0027195 313.0039953 313.0051727 313.0062228 313.0071196 313.0078412 313.0083696 313.008692 313.0088004 313.0089101 313.0088004 313.008474 313.0079389 313.0072084 313.0063004 313.0052372 313.0040451 313.0027534 313.0013938 313 312.9986062 312.9972466 312.9959549 312.9947628 312.9936996 312.9927916 312.9920611 312.991526 312.9911996 312.9910899 312.9911996 312.991526 312.9920611 312.9927916 312.9936996 312.9947628 312.9959549 312.9972466 312.9986062 313 313.0013938 313.0027534 313.0040451 313.0052372 313.0063004 313.0072084 313.0079389 313.008474 313.0088004 313.0089101 312.0095106 312.0093935 312.0090451 312.008474 312.0076942 312.006725 312.0055902 312.0043177 312.0029389 312.0014878 312 311.9985122 311.9970611 311.9956823 311.9944098 311.993275 311.9923058 311.991526 311.9909549 311.9906065 311.9904894 311.9906065 311.9909549 311.991526 311.9923058 311.993275 311.9944098 311.9956823 311.9970611 311.9985122 312 312.0014878 312.0029389 312.0043177 312.0055902 312.006725 312.0076942 312.008474 312.0090451 312.0093935 312.0095106 312.0093935 312.0092778 312.0089337 312.0083696 312.0075995 312.0066422 312.0055213 312.0042645 312.0029027 312.0014695 312 311.9985305 311.9970973 311.9957355 311.9944787 311.9933578 311.9924005 311.9916304 311.9910663 311.9907222 311.9906065 311.9907222 311.9910663 311.9916304 311.9924005 311.9933578 311.9944787 311.9957355 311.9970973 311.9985305 312 312.0014695 312.0029027 312.0042645 312.0055213 312.0066422 312.0075995 312.0083696 312.0089337 312.0092778 312.0093935 312.0090451 312.0089337 312.0086024 312.0080592 312.0073176 312.0063958 312.0053166 312.0041064 312.0027951 312.001415 312 311.998585 311.9972049 311.9958936 311.9946834 311.9936042 311.9926824 311.9919408 311.9913976 311.9910663 311.9909549 311.9910663 311.9913976 311.9919408 311.9926824 311.9936042 311.9946834 311.9958936 311.9972049 311.998585 312 312.001415 312.0027951 312.0041064 312.0053166 312.0063958 312.0073176 312.0080592 312.0086024 312.0089337 312.0090451 312.008474 312.0083696 312.0080592 312.0075504 312.0068556 312.005992 312.0049809 312.0038471 312.0026186 312.0013256 312 311.9986744 311.9973814 311.9961529 311.9950191 311.994008 311.9931444 311.9924496 311.9919408 311.9916304 311.991526 311.9916304 311.9919408 311.9924496 311.9931444 311.994008 311.9950191 311.9961529 311.9973814 311.9986744 312 312.0013256 312.0026186 312.0038471 312.0049809 312.005992 312.0068556 312.0075504 312.0080592 312.0083696 312.008474 312.0076942 312.0075995 312.0073176 312.0068556 312.0062247 312.0054406 312.0045225 312.0034931 312.0023776 312.0012036 312 311.9987964 311.9976224 311.9965069 311.9954775 311.9945594 311.9937753 311.9931444 311.9926824 311.9924005 311.9923058 311.9924005 311.9926824 311.9931444 311.9937753 311.9945594 311.9954775 311.9965069 311.9976224 311.9987964 312 312.0012036 312.0023776 312.0034931 312.0045225 312.0054406 312.0062247 312.0068556 312.0073176 312.0075995 312.0076942 312.006725 312.0066422 312.0063958 312.005992 312.0054406 312.0047553 312.0039528 312.0030531 312.0020781 312.001052 312 311.998948 311.9979219 311.9969469 311.9960472 311.9952447 311.9945594 311.994008 311.9936042 311.9933578 311.993275 311.9933578 311.9936042 311.994008 311.9945594 311.9952447 311.9960472 311.9969469 311.9979219 311.998948 312 312.001052 312.0020781 312.0030531 312.0039528 312.0047553 312.0054406 312.005992 312.0063958 312.0066422 312.006725 312.0055902 312.0055213 312.0053166 312.0049809 312.0045225 312.0039528 312.0032858 312.0025379 312.0017275 312.0008745 312 311.9991255 311.9982725 311.9974621 311.9967142 311.9960472 311.9954775 311.9950191 311.9946834 311.9944787 311.9944098 311.9944787 311.9946834 311.9950191 311.9954775 311.9960472 311.9967142 311.9974621 311.9982725 311.9991255 312 312.0008745 312.0017275 312.0025379 312.0032858 312.0039528 312.0045225 312.0049809 312.0053166 312.0055213 312.0055902 312.0043177 312.0042645 312.0041064 312.0038471 312.0034931 312.0030531 312.0025379 312.0019602 312.0013342 312.0006754 312 311.9993246 311.9986658 311.9980398 311.9974621 311.9969469 311.9965069 311.9961529 311.9958936 311.9957355 311.9956823 311.9957355 311.9958936 311.9961529 311.9965069 311.9969469 311.9974621 311.9980398 311.9986658 311.9993246 312 312.0006754 312.0013342 312.0019602 312.0025379 312.0030531 312.0034931 312.0038471 312.0041064 312.0042645 312.0043177 312.0029389 312.0029027 312.0027951 312.0026186 312.0023776 312.0020781 312.0017275 312.0013342 312.0009082 312.0004597 312 311.9995403 311.9990918 311.9986658 311.9982725 311.9979219 311.9976224 311.9973814 311.9972049 311.9970973 311.9970611 311.9970973 311.9972049 311.9973814 311.9976224 311.9979219 311.9982725 311.9986658 311.9990918 311.9995403 312 312.0004597 312.0009082 312.0013342 312.0017275 312.0020781 312.0023776 312.0026186 312.0027951 312.0029027 312.0029389 312.0014878 312.0014695 312.001415 312.0013256 312.0012036 312.001052 312.0008745 312.0006754 312.0004597 312.0002327 312 311.9997673 311.9995403 311.9993246 311.9991255 311.998948 311.9987964 311.9986744 311.998585 311.9985305 311.9985122 311.9985305 311.998585 311.9986744 311.9987964 311.998948 311.9991255 311.9993246 311.9995403 311.9997673 312 312.0002327 312.0004597 312.0006754 312.0008745 312.001052 312.0012036 312.0013256 312.001415 312.0014695 312.0014878 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 311.9985122 311.9985305 311.998585 311.9986744 311.9987964 311.998948 311.9991255 311.9993246 311.9995403 311.9997673 312 312.0002327 312.0004597 312.0006754 312.0008745 312.001052 312.0012036 312.0013256 312.001415 312.0014695 312.0014878 312.0014695 312.001415 312.0013256 312.0012036 312.001052 312.0008745 312.0006754 312.0004597 312.0002327 312 311.9997673 311.9995403 311.9993246 311.9991255 311.998948 311.9987964 311.9986744 311.998585 311.9985305 311.9985122 311.9970611 311.9970973 311.9972049 311.9973814 311.9976224 311.9979219 311.9982725 311.9986658 311.9990918 311.9995403 312 312.0004597 312.0009082 312.0013342 312.0017275 312.0020781 312.0023776 312.0026186 312.0027951 312.0029027 312.0029389 312.0029027 312.0027951 312.0026186 312.0023776 312.0020781 312.0017275 312.0013342 312.0009082 312.0004597 312 311.9995403 311.9990918 311.9986658 311.9982725 311.9979219 311.9976224 311.9973814 311.9972049 311.9970973 311.9970611 311.9956823 311.9957355 311.9958936 311.9961529 311.9965069 311.9969469 311.9974621 311.9980398 311.9986658 311.9993246 312 312.0006754 312.0013342 312.0019602 312.0025379 312.0030531 312.0034931 312.0038471 312.0041064 312.0042645 312.0043177 312.0042645 312.0041064 312.0038471 312.0034931 312.0030531 312.0025379 312.0019602 312.0013342 312.0006754 312 311.9993246 311.9986658 311.9980398 311.9974621 311.9969469 311.9965069 311.9961529 311.9958936 311.9957355 311.9956823 311.9944098 311.9944787 311.9946834 311.9950191 311.9954775 311.9960472 311.9967142 311.9974621 311.9982725 311.9991255 312 312.0008745 312.0017275 312.0025379 312.0032858 312.0039528 312.0045225 312.0049809 312.0053166 312.0055213 312.0055902 312.0055213 312.0053166 312.0049809 312.0045225 312.0039528 312.0032858 312.0025379 312.0017275 312.0008745 312 311.9991255 311.9982725 311.9974621 311.9967142 311.9960472 311.9954775 311.9950191 311.9946834 311.9944787 311.9944098 311.993275 311.9933578 311.9936042 311.994008 311.9945594 311.9952447 311.9960472 311.9969469 311.9979219 311.998948 312 312.001052 312.0020781 312.0030531 312.0039528 312.0047553 312.0054406 312.005992 312.0063958 312.0066422 312.006725 312.0066422 312.0063958 312.005992 312.0054406 312.0047553 312.0039528 312.0030531 312.0020781 312.001052 312 311.998948 311.9979219 311.9969469 311.9960472 311.9952447 311.9945594 311.994008 311.9936042 311.9933578 311.993275 311.9923058 311.9924005 311.9926824 311.9931444 311.9937753 311.9945594 311.9954775 311.9965069 311.9976224 311.9987964 312 312.0012036 312.0023776 312.0034931 312.0045225 312.0054406 312.0062247 312.0068556 312.0073176 312.0075995 312.0076942 312.0075995 312.0073176 312.0068556 312.0062247 312.0054406 312.0045225 312.0034931 312.0023776 312.0012036 312 311.9987964 311.9976224 311.9965069 311.9954775 311.9945594 311.9937753 311.9931444 311.9926824 311.9924005 311.9923058 311.991526 311.9916304 311.9919408 311.9924496 311.9931444 311.994008 311.9950191 311.9961529 311.9973814 311.9986744 312 312.0013256 312.0026186 312.0038471 312.0049809 312.005992 312.0068556 312.0075504 312.0080592 312.0083696 312.008474 312.0083696 312.0080592 312.0075504 312.0068556 312.005992 312.0049809 312.0038471 312.0026186 312.0013256 312 311.9986744 311.9973814 311.9961529 311.9950191 311.994008 311.9931444 311.9924496 311.9919408 311.9916304 311.991526 311.9909549 311.9910663 311.9913976 311.9919408 311.9926824 311.9936042 311.9946834 311.9958936 311.9972049 311.998585 312 312.001415 312.0027951 312.0041064 312.0053166 312.0063958 312.0073176 312.0080592 312.0086024 312.0089337 312.0090451 312.0089337 312.0086024 312.0080592 312.0073176 312.0063958 312.0053166 312.0041064 312.0027951 312.001415 312 311.998585 311.9972049 311.9958936 311.9946834 311.9936042 311.9926824 311.9919408 311.9913976 311.9910663 311.9909549 311.9906065 311.9907222 311.9910663 311.9916304 311.9924005 311.9933578 311.9944787 311.9957355 311.9970973 311.9985305 312 312.0014695 312.0029027 312.0042645 312.0055213 312.0066422 312.0075995 312.0083696 312.0089337 312.0092778 312.0093935 312.0092778 312.0089337 312.0083696 312.0075995 312.0066422 312.0055213 312.0042645 312.0029027 312.0014695 312 311.9985305 311.9970973 311.9957355 311.9944787 311.9933578 311.9924005 311.9916304 311.9910663 311.9907222 311.9906065 311.9904894 311.9906065 311.9909549 311.991526 311.9923058 311.993275 311.9944098 311.9956823 311.9970611 311.9985122 312 312.0014878 312.0029389 312.0043177 312.0055902 312.006725 312.0076942 312.008474 312.0090451 312.0093935 312.0095106 312.0093935 312.0090451 312.008474 312.0076942 312.006725 312.0055902 312.0043177 312.0029389 312.0014878 312 311.9985122 311.9970611 311.9956823 311.9944098 311.993275 311.9923058 311.991526 311.9909549 311.9906065 311.9904894 311.9906065 311.9907222 311.9910663 311.9916304 311.9924005 311.9933578 311.9944787 311.9957355 311.9970973 311.9985305 312 312.0014695 312.0029027 312.0042645 312.0055213 312.0066422 312.0075995 312.0083696 312.0089337 312.0092778 312.0093935 312.0092778 312.0089337 312.0083696 312.0075995 312.0066422 312.0055213 312.0042645 312.0029027 312.0014695 312 311.9985305 311.9970973 311.9957355 311.9944787 311.9933578 311.9924005 311.9916304 311.9910663 311.9907222 311.9906065 311.9909549 311.9910663 311.9913976 311.9919408 311.9926824 311.9936042 311.9946834 311.9958936 311.9972049 311.998585 312 312.001415 312.0027951 312.0041064 312.0053166 312.0063958 312.0073176 312.0080592 312.0086024 312.0089337 312.0090451 312.0089337 312.0086024 312.0080592 312.0073176 312.0063958 312.0053166 312.0041064 312.0027951 312.001415 312 311.998585 311.9972049 311.9958936 311.9946834 311.9936042 311.9926824 311.9919408 311.9913976 311.9910663 311.9909549 311.991526 311.9916304 311.9919408 311.9924496 311.9931444 311.994008 311.9950191 311.9961529 311.9973814 311.9986744 312 312.0013256 312.0026186 312.0038471 312.0049809 312.005992 312.0068556 312.0075504 312.0080592 312.0083696 312.008474 312.0083696 312.0080592 312.0075504 312.0068556 312.005992 312.0049809 312.0038471 312.0026186 312.0013256 312 311.9986744 311.9973814 311.9961529 311.9950191 311.994008 311.9931444 311.9924496 311.9919408 311.9916304 311.991526 311.9923058 311.9924005 311.9926824 311.9931444 311.9937753 311.9945594 311.9954775 311.9965069 311.9976224 311.9987964 312 312.0012036 312.0023776 312.0034931 312.0045225 312.0054406 312.0062247 312.0068556 312.0073176 312.0075995 312.0076942 312.0075995 312.0073176 312.0068556 312.0062247 312.0054406 312.0045225 312.0034931 312.0023776 312.0012036 312 311.9987964 311.9976224 311.9965069 311.9954775 311.9945594 311.9937753 311.9931444 311.9926824 311.9924005 311.9923058 311.993275 311.9933578 311.9936042 311.994008 311.9945594 311.9952447 311.9960472 311.9969469 311.9979219 311.998948 312 312.001052 312.0020781 312.0030531 312.0039528 312.0047553 312.0054406 312.005992 312.0063958 312.0066422 312.006725 312.0066422 312.0063958 312.005992 312.0054406 312.0047553 312.0039528 312.0030531 312.0020781 312.001052 312 311.998948 311.9979219 311.9969469 311.9960472 311.9952447 311.9945594 311.994008 311.9936042 311.9933578 311.993275 311.9944098 311.9944787 311.9946834 311.9950191 311.9954775 311.9960472 311.9967142 311.9974621 311.9982725 311.9991255 312 312.0008745 312.0017275 312.0025379 312.0032858 312.0039528 312.0045225 312.0049809 312.0053166 312.0055213 312.0055902 312.0055213 312.0053166 312.0049809 312.0045225 312.0039528 312.0032858 312.0025379 312.0017275 312.0008745 312 311.9991255 311.9982725 311.9974621 311.9967142 311.9960472 311.9954775 311.9950191 311.9946834 311.9944787 311.9944098 311.9956823 311.9957355 311.9958936 311.9961529 311.9965069 311.9969469 311.9974621 311.9980398 311.9986658 311.9993246 312 312.0006754 312.0013342 312.0019602 312.0025379 312.0030531 312.0034931 312.0038471 312.0041064 312.0042645 312.0043177 312.0042645 312.0041064 312.0038471 312.0034931 312.0030531 312.0025379 312.0019602 312.0013342 312.0006754 312 311.9993246 311.9986658 311.9980398 311.9974621 311.9969469 311.9965069 311.9961529 311.9958936 311.9957355 311.9956823 311.9970611 311.9970973 311.9972049 311.9973814 311.9976224 311.9979219 311.9982725 311.9986658 311.9990918 311.9995403 312 312.0004597 312.0009082 312.0013342 312.0017275 312.0020781 312.0023776 312.0026186 312.0027951 312.0029027 312.0029389 312.0029027 312.0027951 312.0026186 312.0023776 312.0020781 312.0017275 312.0013342 312.0009082 312.0004597 312 311.9995403 311.9990918 311.9986658 311.9982725 311.9979219 311.9976224 311.9973814 311.9972049 311.9970973 311.9970611 311.9985122 311.9985305 311.998585 311.9986744 311.9987964 311.998948 311.9991255 311.9993246 311.9995403 311.9997673 312 312.0002327 312.0004597 312.0006754 312.0008745 312.001052 312.0012036 312.0013256 312.001415 312.0014695 312.0014878 312.0014695 312.001415 312.0013256 312.0012036 312.001052 312.0008745 312.0006754 312.0004597 312.0002327 312 311.9997673 311.9995403 311.9993246 311.9991255 311.998948 311.9987964 311.9986744 311.998585 311.9985305 311.9985122 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312 312.0014878 312.0014695 312.001415 312.0013256 312.0012036 312.001052 312.0008745 312.0006754 312.0004597 312.0002327 312 311.9997673 311.9995403 311.9993246 311.9991255 311.998948 311.9987964 311.9986744 311.998585 311.9985305 311.9985122 311.9985305 311.998585 311.9986744 311.9987964 311.998948 311.9991255 311.9993246 311.9995403 311.9997673 312 312.0002327 312.0004597 312.0006754 312.0008745 312.001052 312.0012036 312.0013256 312.001415 312.0014695 312.0014878 312.0029389 312.0029027 312.0027951 312.0026186 312.0023776 312.0020781 312.0017275 312.0013342 312.0009082 312.0004597 312 311.9995403 311.9990918 311.9986658 311.9982725 311.9979219 311.9976224 311.9973814 311.9972049 311.9970973 311.9970611 311.9970973 311.9972049 311.9973814 311.9976224 311.9979219 311.9982725 311.9986658 311.9990918 311.9995403 312 312.0004597 312.0009082 312.0013342 312.0017275 312.0020781 312.0023776 312.0026186 312.0027951 312.0029027 312.0029389 312.0043177 312.0042645 312.0041064 312.0038471 312.0034931 312.0030531 312.0025379 312.0019602 312.0013342 312.0006754 312 311.9993246 311.9986658 311.9980398 311.9974621 311.9969469 311.9965069 311.9961529 311.9958936 311.9957355 311.9956823 311.9957355 311.9958936 311.9961529 311.9965069 311.9969469 311.9974621 311.9980398 311.9986658 311.9993246 312 312.0006754 312.0013342 312.0019602 312.0025379 312.0030531 312.0034931 312.0038471 312.0041064 312.0042645 312.0043177 312.0055902 312.0055213 312.0053166 312.0049809 312.0045225 312.0039528 312.0032858 312.0025379 312.0017275 312.0008745 312 311.9991255 311.9982725 311.9974621 311.9967142 311.9960472 311.9954775 311.9950191 311.9946834 311.9944787 311.9944098 311.9944787 311.9946834 311.9950191 311.9954775 311.9960472 311.9967142 311.9974621 311.9982725 311.9991255 312 312.0008745 312.0017275 312.0025379 312.0032858 312.0039528 312.0045225 312.0049809 312.0053166 312.0055213 312.0055902 312.006725 312.0066422 312.0063958 312.005992 312.0054406 312.0047553 312.0039528 312.0030531 312.0020781 312.001052 312 311.998948 311.9979219 311.9969469 311.9960472 311.9952447 311.9945594 311.994008 311.9936042 311.9933578 311.993275 311.9933578 311.9936042 311.994008 311.9945594 311.9952447 311.9960472 311.9969469 311.9979219 311.998948 312 312.001052 312.0020781 312.0030531 312.0039528 312.0047553 312.0054406 312.005992 312.0063958 312.0066422 312.006725 312.0076942 312.0075995 312.0073176 312.0068556 312.0062247 312.0054406 312.0045225 312.0034931 312.0023776 312.0012036 312 311.9987964 311.9976224 311.9965069 311.9954775 311.9945594 311.9937753 311.9931444 311.9926824 311.9924005 311.9923058 311.9924005 311.9926824 311.9931444 311.9937753 311.9945594 311.9954775 311.9965069 311.9976224 311.9987964 312 312.0012036 312.0023776 312.0034931 312.0045225 312.0054406 312.0062247 312.0068556 312.0073176 312.0075995 312.0076942 312.008474 312.0083696 312.0080592 312.0075504 312.0068556 312.005992 312.0049809 312.0038471 312.0026186 312.0013256 312 311.9986744 311.9973814 311.9961529 311.9950191 311.994008 311.9931444 311.9924496 311.9919408 311.9916304 311.991526 311.9916304 311.9919408 311.9924496 311.9931444 311.994008 311.9950191 311.9961529 311.9973814 311.9986744 312 312.0013256 312.0026186 312.0038471 312.0049809 312.005992 312.0068556 312.0075504 312.0080592 312.0083696 312.008474 312.0090451 312.0089337 312.0086024 312.0080592 312.0073176 312.0063958 312.0053166 312.0041064 312.0027951 312.001415 312 311.998585 311.9972049 311.9958936 311.9946834 311.9936042 311.9926824 311.9919408 311.9913976 311.9910663 311.9909549 311.9910663 311.9913976 311.9919408 311.9926824 311.9936042 311.9946834 311.9958936 311.9972049 311.998585 312 312.001415 312.0027951 312.0041064 312.0053166 312.0063958 312.0073176 312.0080592 312.0086024 312.0089337 312.0090451 312.0093935 312.0092778 312.0089337 312.0083696 312.0075995 312.0066422 312.0055213 312.0042645 312.0029027 312.0014695 312 311.9985305 311.9970973 311.9957355 311.9944787 311.9933578 311.9924005 311.9916304 311.9910663 311.9907222 311.9906065 311.9907222 311.9910663 311.9916304 311.9924005 311.9933578 311.9944787 311.9957355 311.9970973 311.9985305 312 312.0014695 312.0029027 312.0042645 312.0055213 312.0066422 312.0075995 312.0083696 312.0089337 312.0092778 312.0093935 312.0095106 312.0093935 312.0090451 312.008474 312.0076942 312.006725 312.0055902 312.0043177 312.0029389 312.0014878 312 311.9985122 311.9970611 311.9956823 311.9944098 311.993275 311.9923058 311.991526 311.9909549 311.9906065 311.9904894 311.9906065 311.9909549 311.991526 311.9923058 311.993275 311.9944098 311.9956823 311.9970611 311.9985122 312 312.0014878 312.0029389 312.0043177 312.0055902 312.006725 312.0076942 312.008474 312.0090451 312.0093935 312.0095106 311.0098769 311.0097553 311.0093935 311.0088004 311.0079906 311.006984 311.0058055 311.004484 311.0030521 311.0015451 311 310.9984549 310.9969479 310.995516 310.9941945 310.993016 310.9920094 310.9911996 310.9906065 310.9902447 310.9901231 310.9902447 310.9906065 310.9911996 310.9920094 310.993016 310.9941945 310.995516 310.9969479 310.9984549 311 311.0015451 311.0030521 311.004484 311.0058055 311.006984 311.0079906 311.0088004 311.0093935 311.0097553 311.0098769 311.0097553 311.0096352 311.0092778 311.008692 311.0078922 311.006898 311.005734 311.0044288 311.0030145 311.0015261 311 310.9984739 310.9969855 310.9955712 310.994266 310.993102 310.9921078 310.991308 310.9907222 310.9903648 310.9902447 310.9903648 310.9907222 310.991308 310.9921078 310.993102 310.994266 310.9955712 310.9969855 310.9984739 311 311.0015261 311.0030145 311.0044288 311.005734 311.006898 311.0078922 311.008692 311.0092778 311.0096352 311.0097553 311.0093935 311.0092778 311.0089337 311.0083696 311.0075995 311.0066422 311.0055213 311.0042645 311.0029027 311.0014695 311 310.9985305 310.9970973 310.9957355 310.9944787 310.9933578 310.9924005 310.9916304 310.9910663 310.9907222 310.9906065 310.9907222 310.9910663 310.9916304 310.9924005 310.9933578 310.9944787 310.9957355 310.9970973 310.9985305 311 311.0014695 311.0029027 311.0042645 311.0055213 311.0066422 311.0075995 311.0083696 311.0089337 311.0092778 311.0093935 311.0088004 311.008692 311.0083696 311.0078412 311.0071196 311.0062228 311.0051727 311.0039953 311.0027195 311.0013767 311 310.9986233 310.9972805 310.9960047 310.9948273 310.9937772 310.9928804 310.9921588 310.9916304 310.991308 310.9911996 310.991308 310.9916304 310.9921588 310.9928804 310.9937772 310.9948273 310.9960047 310.9972805 310.9986233 311 311.0013767 311.0027195 311.0039953 311.0051727 311.0062228 311.0071196 311.0078412 311.0083696 311.008692 311.0088004 311.0079906 311.0078922 311.0075995 311.0071196 311.0064645 311.0056502 311.0046967 311.0036276 311.0024692 311.00125 311 310.99875 310.9975308 310.9963724 310.9953033 310.9943498 310.9935355 310.9928804 310.9924005 310.9921078 310.9920094 310.9921078 310.9924005 310.9928804 310.9935355 310.9943498 310.9953033 310.9963724 310.9975308 310.99875 311 311.00125 311.0024692 311.0036276 311.0046967 311.0056502 311.0064645 311.0071196 311.0075995 311.0078922 311.0079906 311.006984 311.006898 311.0066422 311.0062228 311.0056502 311.0049384 311.0041051 311.0031707 311.0021582 311.0010925 311 310.9989075 310.9978418 310.9968293 310.9958949 310.9950616 310.9943498 310.9937772 310.9933578 310.993102 310.993016 310.993102 310.9933578 310.9937772 310.9943498 310.9950616 310.9958949 310.9968293 310.9978418 310.9989075 311 311.0010925 311.0021582 311.0031707 311.0041051 311.0049384 311.0056502 311.0062228 311.0066422 311.006898 311.006984 311.0058055 311.005734 311.0055213 311.0051727 311.0046967 311.0041051 311.0034124 311.0026356 311.001794 311.0009082 311 310.9990918 310.998206 310.9973644 310.9965876 310.9958949 310.9953033 310.9948273 310.9944787 310.994266 310.9941945 310.994266 310.9944787 310.9948273 310.9953033 310.9958949 310.9965876 310.9973644 310.998206 310.9990918 311 311.0009082 311.001794 311.0026356 311.0034124 311.0041051 311.0046967 311.0051727 311.0055213 311.005734 311.0058055 311.004484 311.0044288 311.0042645 311.0039953 311.0036276 311.0031707 311.0026356 311.0020357 311.0013856 311.0007015 311 310.9992985 310.9986144 310.9979643 310.9973644 310.9968293 310.9963724 310.9960047 310.9957355 310.9955712 310.995516 310.9955712 310.9957355 310.9960047 310.9963724 310.9968293 310.9973644 310.9979643 310.9986144 310.9992985 311 311.0007015 311.0013856 311.0020357 311.0026356 311.0031707 311.0036276 311.0039953 311.0042645 311.0044288 311.004484 311.0030521 311.0030145 311.0029027 311.0027195 311.0024692 311.0021582 311.001794 311.0013856 311.0009432 311.0004775 311 310.9995225 310.9990568 310.9986144 310.998206 310.9978418 310.9975308 310.9972805 310.9970973 310.9969855 310.9969479 310.9969855 310.9970973 310.9972805 310.9975308 310.9978418 310.998206 310.9986144 310.9990568 310.9995225 311 311.0004775 311.0009432 311.0013856 311.001794 311.0021582 311.0024692 311.0027195 311.0029027 311.0030145 311.0030521 311.0015451 311.0015261 311.0014695 311.0013767 311.00125 311.0010925 311.0009082 311.0007015 311.0004775 311.0002417 311 310.9997583 310.9995225 310.9992985 310.9990918 310.9989075 310.99875 310.9986233 310.9985305 310.9984739 310.9984549 310.9984739 310.9985305 310.9986233 310.99875 310.9989075 310.9990918 310.9992985 310.9995225 310.9997583 311 311.0002417 311.0004775 311.0007015 311.0009082 311.0010925 311.00125 311.0013767 311.0014695 311.0015261 311.0015451 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 310.9984549 310.9984739 310.9985305 310.9986233 310.99875 310.9989075 310.9990918 310.9992985 310.9995225 310.9997583 311 311.0002417 311.0004775 311.0007015 311.0009082 311.0010925 311.00125 311.0013767 311.0014695 311.0015261 311.0015451 311.0015261 311.0014695 311.0013767 311.00125 311.0010925 311.0009082 311.0007015 311.0004775 311.0002417 311 310.9997583 310.9995225 310.9992985 310.9990918 310.9989075 310.99875 310.9986233 310.9985305 310.9984739 310.9984549 310.9969479 310.9969855 310.9970973 310.9972805 310.9975308 310.9978418 310.998206 310.9986144 310.9990568 310.9995225 311 311.0004775 311.0009432 311.0013856 311.001794 311.0021582 311.0024692 311.0027195 311.0029027 311.0030145 311.0030521 311.0030145 311.0029027 311.0027195 311.0024692 311.0021582 311.001794 311.0013856 311.0009432 311.0004775 311 310.9995225 310.9990568 310.9986144 310.998206 310.9978418 310.9975308 310.9972805 310.9970973 310.9969855 310.9969479 310.995516 310.9955712 310.9957355 310.9960047 310.9963724 310.9968293 310.9973644 310.9979643 310.9986144 310.9992985 311 311.0007015 311.0013856 311.0020357 311.0026356 311.0031707 311.0036276 311.0039953 311.0042645 311.0044288 311.004484 311.0044288 311.0042645 311.0039953 311.0036276 311.0031707 311.0026356 311.0020357 311.0013856 311.0007015 311 310.9992985 310.9986144 310.9979643 310.9973644 310.9968293 310.9963724 310.9960047 310.9957355 310.9955712 310.995516 310.9941945 310.994266 310.9944787 310.9948273 310.9953033 310.9958949 310.9965876 310.9973644 310.998206 310.9990918 311 311.0009082 311.001794 311.0026356 311.0034124 311.0041051 311.0046967 311.0051727 311.0055213 311.005734 311.0058055 311.005734 311.0055213 311.0051727 311.0046967 311.0041051 311.0034124 311.0026356 311.001794 311.0009082 311 310.9990918 310.998206 310.9973644 310.9965876 310.9958949 310.9953033 310.9948273 310.9944787 310.994266 310.9941945 310.993016 310.993102 310.9933578 310.9937772 310.9943498 310.9950616 310.9958949 310.9968293 310.9978418 310.9989075 311 311.0010925 311.0021582 311.0031707 311.0041051 311.0049384 311.0056502 311.0062228 311.0066422 311.006898 311.006984 311.006898 311.0066422 311.0062228 311.0056502 311.0049384 311.0041051 311.0031707 311.0021582 311.0010925 311 310.9989075 310.9978418 310.9968293 310.9958949 310.9950616 310.9943498 310.9937772 310.9933578 310.993102 310.993016 310.9920094 310.9921078 310.9924005 310.9928804 310.9935355 310.9943498 310.9953033 310.9963724 310.9975308 310.99875 311 311.00125 311.0024692 311.0036276 311.0046967 311.0056502 311.0064645 311.0071196 311.0075995 311.0078922 311.0079906 311.0078922 311.0075995 311.0071196 311.0064645 311.0056502 311.0046967 311.0036276 311.0024692 311.00125 311 310.99875 310.9975308 310.9963724 310.9953033 310.9943498 310.9935355 310.9928804 310.9924005 310.9921078 310.9920094 310.9911996 310.991308 310.9916304 310.9921588 310.9928804 310.9937772 310.9948273 310.9960047 310.9972805 310.9986233 311 311.0013767 311.0027195 311.0039953 311.0051727 311.0062228 311.0071196 311.0078412 311.0083696 311.008692 311.0088004 311.008692 311.0083696 311.0078412 311.0071196 311.0062228 311.0051727 311.0039953 311.0027195 311.0013767 311 310.9986233 310.9972805 310.9960047 310.9948273 310.9937772 310.9928804 310.9921588 310.9916304 310.991308 310.9911996 310.9906065 310.9907222 310.9910663 310.9916304 310.9924005 310.9933578 310.9944787 310.9957355 310.9970973 310.9985305 311 311.0014695 311.0029027 311.0042645 311.0055213 311.0066422 311.0075995 311.0083696 311.0089337 311.0092778 311.0093935 311.0092778 311.0089337 311.0083696 311.0075995 311.0066422 311.0055213 311.0042645 311.0029027 311.0014695 311 310.9985305 310.9970973 310.9957355 310.9944787 310.9933578 310.9924005 310.9916304 310.9910663 310.9907222 310.9906065 310.9902447 310.9903648 310.9907222 310.991308 310.9921078 310.993102 310.994266 310.9955712 310.9969855 310.9984739 311 311.0015261 311.0030145 311.0044288 311.005734 311.006898 311.0078922 311.008692 311.0092778 311.0096352 311.0097553 311.0096352 311.0092778 311.008692 311.0078922 311.006898 311.005734 311.0044288 311.0030145 311.0015261 311 310.9984739 310.9969855 310.9955712 310.994266 310.993102 310.9921078 310.991308 310.9907222 310.9903648 310.9902447 310.9901231 310.9902447 310.9906065 310.9911996 310.9920094 310.993016 310.9941945 310.995516 310.9969479 310.9984549 311 311.0015451 311.0030521 311.004484 311.0058055 311.006984 311.0079906 311.0088004 311.0093935 311.0097553 311.0098769 311.0097553 311.0093935 311.0088004 311.0079906 311.006984 311.0058055 311.004484 311.0030521 311.0015451 311 310.9984549 310.9969479 310.995516 310.9941945 310.993016 310.9920094 310.9911996 310.9906065 310.9902447 310.9901231 310.9902447 310.9903648 310.9907222 310.991308 310.9921078 310.993102 310.994266 310.9955712 310.9969855 310.9984739 311 311.0015261 311.0030145 311.0044288 311.005734 311.006898 311.0078922 311.008692 311.0092778 311.0096352 311.0097553 311.0096352 311.0092778 311.008692 311.0078922 311.006898 311.005734 311.0044288 311.0030145 311.0015261 311 310.9984739 310.9969855 310.9955712 310.994266 310.993102 310.9921078 310.991308 310.9907222 310.9903648 310.9902447 310.9906065 310.9907222 310.9910663 310.9916304 310.9924005 310.9933578 310.9944787 310.9957355 310.9970973 310.9985305 311 311.0014695 311.0029027 311.0042645 311.0055213 311.0066422 311.0075995 311.0083696 311.0089337 311.0092778 311.0093935 311.0092778 311.0089337 311.0083696 311.0075995 311.0066422 311.0055213 311.0042645 311.0029027 311.0014695 311 310.9985305 310.9970973 310.9957355 310.9944787 310.9933578 310.9924005 310.9916304 310.9910663 310.9907222 310.9906065 310.9911996 310.991308 310.9916304 310.9921588 310.9928804 310.9937772 310.9948273 310.9960047 310.9972805 310.9986233 311 311.0013767 311.0027195 311.0039953 311.0051727 311.0062228 311.0071196 311.0078412 311.0083696 311.008692 311.0088004 311.008692 311.0083696 311.0078412 311.0071196 311.0062228 311.0051727 311.0039953 311.0027195 311.0013767 311 310.9986233 310.9972805 310.9960047 310.9948273 310.9937772 310.9928804 310.9921588 310.9916304 310.991308 310.9911996 310.9920094 310.9921078 310.9924005 310.9928804 310.9935355 310.9943498 310.9953033 310.9963724 310.9975308 310.99875 311 311.00125 311.0024692 311.0036276 311.0046967 311.0056502 311.0064645 311.0071196 311.0075995 311.0078922 311.0079906 311.0078922 311.0075995 311.0071196 311.0064645 311.0056502 311.0046967 311.0036276 311.0024692 311.00125 311 310.99875 310.9975308 310.9963724 310.9953033 310.9943498 310.9935355 310.9928804 310.9924005 310.9921078 310.9920094 310.993016 310.993102 310.9933578 310.9937772 310.9943498 310.9950616 310.9958949 310.9968293 310.9978418 310.9989075 311 311.0010925 311.0021582 311.0031707 311.0041051 311.0049384 311.0056502 311.0062228 311.0066422 311.006898 311.006984 311.006898 311.0066422 311.0062228 311.0056502 311.0049384 311.0041051 311.0031707 311.0021582 311.0010925 311 310.9989075 310.9978418 310.9968293 310.9958949 310.9950616 310.9943498 310.9937772 310.9933578 310.993102 310.993016 310.9941945 310.994266 310.9944787 310.9948273 310.9953033 310.9958949 310.9965876 310.9973644 310.998206 310.9990918 311 311.0009082 311.001794 311.0026356 311.0034124 311.0041051 311.0046967 311.0051727 311.0055213 311.005734 311.0058055 311.005734 311.0055213 311.0051727 311.0046967 311.0041051 311.0034124 311.0026356 311.001794 311.0009082 311 310.9990918 310.998206 310.9973644 310.9965876 310.9958949 310.9953033 310.9948273 310.9944787 310.994266 310.9941945 310.995516 310.9955712 310.9957355 310.9960047 310.9963724 310.9968293 310.9973644 310.9979643 310.9986144 310.9992985 311 311.0007015 311.0013856 311.0020357 311.0026356 311.0031707 311.0036276 311.0039953 311.0042645 311.0044288 311.004484 311.0044288 311.0042645 311.0039953 311.0036276 311.0031707 311.0026356 311.0020357 311.0013856 311.0007015 311 310.9992985 310.9986144 310.9979643 310.9973644 310.9968293 310.9963724 310.9960047 310.9957355 310.9955712 310.995516 310.9969479 310.9969855 310.9970973 310.9972805 310.9975308 310.9978418 310.998206 310.9986144 310.9990568 310.9995225 311 311.0004775 311.0009432 311.0013856 311.001794 311.0021582 311.0024692 311.0027195 311.0029027 311.0030145 311.0030521 311.0030145 311.0029027 311.0027195 311.0024692 311.0021582 311.001794 311.0013856 311.0009432 311.0004775 311 310.9995225 310.9990568 310.9986144 310.998206 310.9978418 310.9975308 310.9972805 310.9970973 310.9969855 310.9969479 310.9984549 310.9984739 310.9985305 310.9986233 310.99875 310.9989075 310.9990918 310.9992985 310.9995225 310.9997583 311 311.0002417 311.0004775 311.0007015 311.0009082 311.0010925 311.00125 311.0013767 311.0014695 311.0015261 311.0015451 311.0015261 311.0014695 311.0013767 311.00125 311.0010925 311.0009082 311.0007015 311.0004775 311.0002417 311 310.9997583 310.9995225 310.9992985 310.9990918 310.9989075 310.99875 310.9986233 310.9985305 310.9984739 310.9984549 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311 311.0015451 311.0015261 311.0014695 311.0013767 311.00125 311.0010925 311.0009082 311.0007015 311.0004775 311.0002417 311 310.9997583 310.9995225 310.9992985 310.9990918 310.9989075 310.99875 310.9986233 310.9985305 310.9984739 310.9984549 310.9984739 310.9985305 310.9986233 310.99875 310.9989075 310.9990918 310.9992985 310.9995225 310.9997583 311 311.0002417 311.0004775 311.0007015 311.0009082 311.0010925 311.00125 311.0013767 311.0014695 311.0015261 311.0015451 311.0030521 311.0030145 311.0029027 311.0027195 311.0024692 311.0021582 311.001794 311.0013856 311.0009432 311.0004775 311 310.9995225 310.9990568 310.9986144 310.998206 310.9978418 310.9975308 310.9972805 310.9970973 310.9969855 310.9969479 310.9969855 310.9970973 310.9972805 310.9975308 310.9978418 310.998206 310.9986144 310.9990568 310.9995225 311 311.0004775 311.0009432 311.0013856 311.001794 311.0021582 311.0024692 311.0027195 311.0029027 311.0030145 311.0030521 311.004484 311.0044288 311.0042645 311.0039953 311.0036276 311.0031707 311.0026356 311.0020357 311.0013856 311.0007015 311 310.9992985 310.9986144 310.9979643 310.9973644 310.9968293 310.9963724 310.9960047 310.9957355 310.9955712 310.995516 310.9955712 310.9957355 310.9960047 310.9963724 310.9968293 310.9973644 310.9979643 310.9986144 310.9992985 311 311.0007015 311.0013856 311.0020357 311.0026356 311.0031707 311.0036276 311.0039953 311.0042645 311.0044288 311.004484 311.0058055 311.005734 311.0055213 311.0051727 311.0046967 311.0041051 311.0034124 311.0026356 311.001794 311.0009082 311 310.9990918 310.998206 310.9973644 310.9965876 310.9958949 310.9953033 310.9948273 310.9944787 310.994266 310.9941945 310.994266 310.9944787 310.9948273 310.9953033 310.9958949 310.9965876 310.9973644 310.998206 310.9990918 311 311.0009082 311.001794 311.0026356 311.0034124 311.0041051 311.0046967 311.0051727 311.0055213 311.005734 311.0058055 311.006984 311.006898 311.0066422 311.0062228 311.0056502 311.0049384 311.0041051 311.0031707 311.0021582 311.0010925 311 310.9989075 310.9978418 310.9968293 310.9958949 310.9950616 310.9943498 310.9937772 310.9933578 310.993102 310.993016 310.993102 310.9933578 310.9937772 310.9943498 310.9950616 310.9958949 310.9968293 310.9978418 310.9989075 311 311.0010925 311.0021582 311.0031707 311.0041051 311.0049384 311.0056502 311.0062228 311.0066422 311.006898 311.006984 311.0079906 311.0078922 311.0075995 311.0071196 311.0064645 311.0056502 311.0046967 311.0036276 311.0024692 311.00125 311 310.99875 310.9975308 310.9963724 310.9953033 310.9943498 310.9935355 310.9928804 310.9924005 310.9921078 310.9920094 310.9921078 310.9924005 310.9928804 310.9935355 310.9943498 310.9953033 310.9963724 310.9975308 310.99875 311 311.00125 311.0024692 311.0036276 311.0046967 311.0056502 311.0064645 311.0071196 311.0075995 311.0078922 311.0079906 311.0088004 311.008692 311.0083696 311.0078412 311.0071196 311.0062228 311.0051727 311.0039953 311.0027195 311.0013767 311 310.9986233 310.9972805 310.9960047 310.9948273 310.9937772 310.9928804 310.9921588 310.9916304 310.991308 310.9911996 310.991308 310.9916304 310.9921588 310.9928804 310.9937772 310.9948273 310.9960047 310.9972805 310.9986233 311 311.0013767 311.0027195 311.0039953 311.0051727 311.0062228 311.0071196 311.0078412 311.0083696 311.008692 311.0088004 311.0093935 311.0092778 311.0089337 311.0083696 311.0075995 311.0066422 311.0055213 311.0042645 311.0029027 311.0014695 311 310.9985305 310.9970973 310.9957355 310.9944787 310.9933578 310.9924005 310.9916304 310.9910663 310.9907222 310.9906065 310.9907222 310.9910663 310.9916304 310.9924005 310.9933578 310.9944787 310.9957355 310.9970973 310.9985305 311 311.0014695 311.0029027 311.0042645 311.0055213 311.0066422 311.0075995 311.0083696 311.0089337 311.0092778 311.0093935 311.0097553 311.0096352 311.0092778 311.008692 311.0078922 311.006898 311.005734 311.0044288 311.0030145 311.0015261 311 310.9984739 310.9969855 310.9955712 310.994266 310.993102 310.9921078 310.991308 310.9907222 310.9903648 310.9902447 310.9903648 310.9907222 310.991308 310.9921078 310.993102 310.994266 310.9955712 310.9969855 310.9984739 311 311.0015261 311.0030145 311.0044288 311.005734 311.006898 311.0078922 311.008692 311.0092778 311.0096352 311.0097553 311.0098769 311.0097553 311.0093935 311.0088004 311.0079906 311.006984 311.0058055 311.004484 311.0030521 311.0015451 311 310.9984549 310.9969479 310.995516 310.9941945 310.993016 310.9920094 310.9911996 310.9906065 310.9902447 310.9901231 310.9902447 310.9906065 310.9911996 310.9920094 310.993016 310.9941945 310.995516 310.9969479 310.9984549 311 311.0015451 311.0030521 311.004484 311.0058055 311.006984 311.0079906 311.0088004 311.0093935 311.0097553 311.0098769 310.01 310.0098769 310.0095106 310.0089101 310.0080902 310.0070711 310.0058779 310.0045399 310.0030902 310.0015643 310 309.9984357 309.9969098 309.9954601 309.9941221 309.9929289 309.9919098 309.9910899 309.9904894 309.9901231 309.99 309.9901231 309.9904894 309.9910899 309.9919098 309.9929289 309.9941221 309.9954601 309.9969098 309.9984357 310 310.0015643 310.0030902 310.0045399 310.0058779 310.0070711 310.0080902 310.0089101 310.0095106 310.0098769 310.01 310.0098769 310.0097553 310.0093935 310.0088004 310.0079906 310.006984 310.0058055 310.004484 310.0030521 310.0015451 310 309.9984549 309.9969479 309.995516 309.9941945 309.993016 309.9920094 309.9911996 309.9906065 309.9902447 309.9901231 309.9902447 309.9906065 309.9911996 309.9920094 309.993016 309.9941945 309.995516 309.9969479 309.9984549 310 310.0015451 310.0030521 310.004484 310.0058055 310.006984 310.0079906 310.0088004 310.0093935 310.0097553 310.0098769 310.0095106 310.0093935 310.0090451 310.008474 310.0076942 310.006725 310.0055902 310.0043177 310.0029389 310.0014878 310 309.9985122 309.9970611 309.9956823 309.9944098 309.993275 309.9923058 309.991526 309.9909549 309.9906065 309.9904894 309.9906065 309.9909549 309.991526 309.9923058 309.993275 309.9944098 309.9956823 309.9970611 309.9985122 310 310.0014878 310.0029389 310.0043177 310.0055902 310.006725 310.0076942 310.008474 310.0090451 310.0093935 310.0095106 310.0089101 310.0088004 310.008474 310.0079389 310.0072084 310.0063004 310.0052372 310.0040451 310.0027534 310.0013938 310 309.9986062 309.9972466 309.9959549 309.9947628 309.9936996 309.9927916 309.9920611 309.991526 309.9911996 309.9910899 309.9911996 309.991526 309.9920611 309.9927916 309.9936996 309.9947628 309.9959549 309.9972466 309.9986062 310 310.0013938 310.0027534 310.0040451 310.0052372 310.0063004 310.0072084 310.0079389 310.008474 310.0088004 310.0089101 310.0080902 310.0079906 310.0076942 310.0072084 310.0065451 310.0057206 310.0047553 310.0036729 310.0025 310.0012656 310 309.9987344 309.9975 309.9963271 309.9952447 309.9942794 309.9934549 309.9927916 309.9923058 309.9920094 309.9919098 309.9920094 309.9923058 309.9927916 309.9934549 309.9942794 309.9952447 309.9963271 309.9975 309.9987344 310 310.0012656 310.0025 310.0036729 310.0047553 310.0057206 310.0065451 310.0072084 310.0076942 310.0079906 310.0080902 310.0070711 310.006984 310.006725 310.0063004 310.0057206 310.005 310.0041563 310.0032102 310.0021851 310.0011062 310 309.9988938 309.9978149 309.9967898 309.9958437 309.995 309.9942794 309.9936996 309.993275 309.993016 309.9929289 309.993016 309.993275 309.9936996 309.9942794 309.995 309.9958437 309.9967898 309.9978149 309.9988938 310 310.0011062 310.0021851 310.0032102 310.0041563 310.005 310.0057206 310.0063004 310.006725 310.006984 310.0070711 310.0058779 310.0058055 310.0055902 310.0052372 310.0047553 310.0041563 310.0034549 310.0026685 310.0018164 310.0009195 310 309.9990805 309.9981836 309.9973315 309.9965451 309.9958437 309.9952447 309.9947628 309.9944098 309.9941945 309.9941221 309.9941945 309.9944098 309.9947628 309.9952447 309.9958437 309.9965451 309.9973315 309.9981836 309.9990805 310 310.0009195 310.0018164 310.0026685 310.0034549 310.0041563 310.0047553 310.0052372 310.0055902 310.0058055 310.0058779 310.0045399 310.004484 310.0043177 310.0040451 310.0036729 310.0032102 310.0026685 310.0020611 310.0014029 310.0007102 310 309.9992898 309.9985971 309.9979389 309.9973315 309.9967898 309.9963271 309.9959549 309.9956823 309.995516 309.9954601 309.995516 309.9956823 309.9959549 309.9963271 309.9967898 309.9973315 309.9979389 309.9985971 309.9992898 310 310.0007102 310.0014029 310.0020611 310.0026685 310.0032102 310.0036729 310.0040451 310.0043177 310.004484 310.0045399 310.0030902 310.0030521 310.0029389 310.0027534 310.0025 310.0021851 310.0018164 310.0014029 310.0009549 310.0004834 310 309.9995166 309.9990451 309.9985971 309.9981836 309.9978149 309.9975 309.9972466 309.9970611 309.9969479 309.9969098 309.9969479 309.9970611 309.9972466 309.9975 309.9978149 309.9981836 309.9985971 309.9990451 309.9995166 310 310.0004834 310.0009549 310.0014029 310.0018164 310.0021851 310.0025 310.0027534 310.0029389 310.0030521 310.0030902 310.0015643 310.0015451 310.0014878 310.0013938 310.0012656 310.0011062 310.0009195 310.0007102 310.0004834 310.0002447 310 309.9997553 309.9995166 309.9992898 309.9990805 309.9988938 309.9987344 309.9986062 309.9985122 309.9984549 309.9984357 309.9984549 309.9985122 309.9986062 309.9987344 309.9988938 309.9990805 309.9992898 309.9995166 309.9997553 310 310.0002447 310.0004834 310.0007102 310.0009195 310.0011062 310.0012656 310.0013938 310.0014878 310.0015451 310.0015643 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 309.9984357 309.9984549 309.9985122 309.9986062 309.9987344 309.9988938 309.9990805 309.9992898 309.9995166 309.9997553 310 310.0002447 310.0004834 310.0007102 310.0009195 310.0011062 310.0012656 310.0013938 310.0014878 310.0015451 310.0015643 310.0015451 310.0014878 310.0013938 310.0012656 310.0011062 310.0009195 310.0007102 310.0004834 310.0002447 310 309.9997553 309.9995166 309.9992898 309.9990805 309.9988938 309.9987344 309.9986062 309.9985122 309.9984549 309.9984357 309.9969098 309.9969479 309.9970611 309.9972466 309.9975 309.9978149 309.9981836 309.9985971 309.9990451 309.9995166 310 310.0004834 310.0009549 310.0014029 310.0018164 310.0021851 310.0025 310.0027534 310.0029389 310.0030521 310.0030902 310.0030521 310.0029389 310.0027534 310.0025 310.0021851 310.0018164 310.0014029 310.0009549 310.0004834 310 309.9995166 309.9990451 309.9985971 309.9981836 309.9978149 309.9975 309.9972466 309.9970611 309.9969479 309.9969098 309.9954601 309.995516 309.9956823 309.9959549 309.9963271 309.9967898 309.9973315 309.9979389 309.9985971 309.9992898 310 310.0007102 310.0014029 310.0020611 310.0026685 310.0032102 310.0036729 310.0040451 310.0043177 310.004484 310.0045399 310.004484 310.0043177 310.0040451 310.0036729 310.0032102 310.0026685 310.0020611 310.0014029 310.0007102 310 309.9992898 309.9985971 309.9979389 309.9973315 309.9967898 309.9963271 309.9959549 309.9956823 309.995516 309.9954601 309.9941221 309.9941945 309.9944098 309.9947628 309.9952447 309.9958437 309.9965451 309.9973315 309.9981836 309.9990805 310 310.0009195 310.0018164 310.0026685 310.0034549 310.0041563 310.0047553 310.0052372 310.0055902 310.0058055 310.0058779 310.0058055 310.0055902 310.0052372 310.0047553 310.0041563 310.0034549 310.0026685 310.0018164 310.0009195 310 309.9990805 309.9981836 309.9973315 309.9965451 309.9958437 309.9952447 309.9947628 309.9944098 309.9941945 309.9941221 309.9929289 309.993016 309.993275 309.9936996 309.9942794 309.995 309.9958437 309.9967898 309.9978149 309.9988938 310 310.0011062 310.0021851 310.0032102 310.0041563 310.005 310.0057206 310.0063004 310.006725 310.006984 310.0070711 310.006984 310.006725 310.0063004 310.0057206 310.005 310.0041563 310.0032102 310.0021851 310.0011062 310 309.9988938 309.9978149 309.9967898 309.9958437 309.995 309.9942794 309.9936996 309.993275 309.993016 309.9929289 309.9919098 309.9920094 309.9923058 309.9927916 309.9934549 309.9942794 309.9952447 309.9963271 309.9975 309.9987344 310 310.0012656 310.0025 310.0036729 310.0047553 310.0057206 310.0065451 310.0072084 310.0076942 310.0079906 310.0080902 310.0079906 310.0076942 310.0072084 310.0065451 310.0057206 310.0047553 310.0036729 310.0025 310.0012656 310 309.9987344 309.9975 309.9963271 309.9952447 309.9942794 309.9934549 309.9927916 309.9923058 309.9920094 309.9919098 309.9910899 309.9911996 309.991526 309.9920611 309.9927916 309.9936996 309.9947628 309.9959549 309.9972466 309.9986062 310 310.0013938 310.0027534 310.0040451 310.0052372 310.0063004 310.0072084 310.0079389 310.008474 310.0088004 310.0089101 310.0088004 310.008474 310.0079389 310.0072084 310.0063004 310.0052372 310.0040451 310.0027534 310.0013938 310 309.9986062 309.9972466 309.9959549 309.9947628 309.9936996 309.9927916 309.9920611 309.991526 309.9911996 309.9910899 309.9904894 309.9906065 309.9909549 309.991526 309.9923058 309.993275 309.9944098 309.9956823 309.9970611 309.9985122 310 310.0014878 310.0029389 310.0043177 310.0055902 310.006725 310.0076942 310.008474 310.0090451 310.0093935 310.0095106 310.0093935 310.0090451 310.008474 310.0076942 310.006725 310.0055902 310.0043177 310.0029389 310.0014878 310 309.9985122 309.9970611 309.9956823 309.9944098 309.993275 309.9923058 309.991526 309.9909549 309.9906065 309.9904894 309.9901231 309.9902447 309.9906065 309.9911996 309.9920094 309.993016 309.9941945 309.995516 309.9969479 309.9984549 310 310.0015451 310.0030521 310.004484 310.0058055 310.006984 310.0079906 310.0088004 310.0093935 310.0097553 310.0098769 310.0097553 310.0093935 310.0088004 310.0079906 310.006984 310.0058055 310.004484 310.0030521 310.0015451 310 309.9984549 309.9969479 309.995516 309.9941945 309.993016 309.9920094 309.9911996 309.9906065 309.9902447 309.9901231 309.99 309.9901231 309.9904894 309.9910899 309.9919098 309.9929289 309.9941221 309.9954601 309.9969098 309.9984357 310 310.0015643 310.0030902 310.0045399 310.0058779 310.0070711 310.0080902 310.0089101 310.0095106 310.0098769 310.01 310.0098769 310.0095106 310.0089101 310.0080902 310.0070711 310.0058779 310.0045399 310.0030902 310.0015643 310 309.9984357 309.9969098 309.9954601 309.9941221 309.9929289 309.9919098 309.9910899 309.9904894 309.9901231 309.99 309.9901231 309.9902447 309.9906065 309.9911996 309.9920094 309.993016 309.9941945 309.995516 309.9969479 309.9984549 310 310.0015451 310.0030521 310.004484 310.0058055 310.006984 310.0079906 310.0088004 310.0093935 310.0097553 310.0098769 310.0097553 310.0093935 310.0088004 310.0079906 310.006984 310.0058055 310.004484 310.0030521 310.0015451 310 309.9984549 309.9969479 309.995516 309.9941945 309.993016 309.9920094 309.9911996 309.9906065 309.9902447 309.9901231 309.9904894 309.9906065 309.9909549 309.991526 309.9923058 309.993275 309.9944098 309.9956823 309.9970611 309.9985122 310 310.0014878 310.0029389 310.0043177 310.0055902 310.006725 310.0076942 310.008474 310.0090451 310.0093935 310.0095106 310.0093935 310.0090451 310.008474 310.0076942 310.006725 310.0055902 310.0043177 310.0029389 310.0014878 310 309.9985122 309.9970611 309.9956823 309.9944098 309.993275 309.9923058 309.991526 309.9909549 309.9906065 309.9904894 309.9910899 309.9911996 309.991526 309.9920611 309.9927916 309.9936996 309.9947628 309.9959549 309.9972466 309.9986062 310 310.0013938 310.0027534 310.0040451 310.0052372 310.0063004 310.0072084 310.0079389 310.008474 310.0088004 310.0089101 310.0088004 310.008474 310.0079389 310.0072084 310.0063004 310.0052372 310.0040451 310.0027534 310.0013938 310 309.9986062 309.9972466 309.9959549 309.9947628 309.9936996 309.9927916 309.9920611 309.991526 309.9911996 309.9910899 309.9919098 309.9920094 309.9923058 309.9927916 309.9934549 309.9942794 309.9952447 309.9963271 309.9975 309.9987344 310 310.0012656 310.0025 310.0036729 310.0047553 310.0057206 310.0065451 310.0072084 310.0076942 310.0079906 310.0080902 310.0079906 310.0076942 310.0072084 310.0065451 310.0057206 310.0047553 310.0036729 310.0025 310.0012656 310 309.9987344 309.9975 309.9963271 309.9952447 309.9942794 309.9934549 309.9927916 309.9923058 309.9920094 309.9919098 309.9929289 309.993016 309.993275 309.9936996 309.9942794 309.995 309.9958437 309.9967898 309.9978149 309.9988938 310 310.0011062 310.0021851 310.0032102 310.0041563 310.005 310.0057206 310.0063004 310.006725 310.006984 310.0070711 310.006984 310.006725 310.0063004 310.0057206 310.005 310.0041563 310.0032102 310.0021851 310.0011062 310 309.9988938 309.9978149 309.9967898 309.9958437 309.995 309.9942794 309.9936996 309.993275 309.993016 309.9929289 309.9941221 309.9941945 309.9944098 309.9947628 309.9952447 309.9958437 309.9965451 309.9973315 309.9981836 309.9990805 310 310.0009195 310.0018164 310.0026685 310.0034549 310.0041563 310.0047553 310.0052372 310.0055902 310.0058055 310.0058779 310.0058055 310.0055902 310.0052372 310.0047553 310.0041563 310.0034549 310.0026685 310.0018164 310.0009195 310 309.9990805 309.9981836 309.9973315 309.9965451 309.9958437 309.9952447 309.9947628 309.9944098 309.9941945 309.9941221 309.9954601 309.995516 309.9956823 309.9959549 309.9963271 309.9967898 309.9973315 309.9979389 309.9985971 309.9992898 310 310.0007102 310.0014029 310.0020611 310.0026685 310.0032102 310.0036729 310.0040451 310.0043177 310.004484 310.0045399 310.004484 310.0043177 310.0040451 310.0036729 310.0032102 310.0026685 310.0020611 310.0014029 310.0007102 310 309.9992898 309.9985971 309.9979389 309.9973315 309.9967898 309.9963271 309.9959549 309.9956823 309.995516 309.9954601 309.9969098 309.9969479 309.9970611 309.9972466 309.9975 309.9978149 309.9981836 309.9985971 309.9990451 309.9995166 310 310.0004834 310.0009549 310.0014029 310.0018164 310.0021851 310.0025 310.0027534 310.0029389 310.0030521 310.0030902 310.0030521 310.0029389 310.0027534 310.0025 310.0021851 310.0018164 310.0014029 310.0009549 310.0004834 310 309.9995166 309.9990451 309.9985971 309.9981836 309.9978149 309.9975 309.9972466 309.9970611 309.9969479 309.9969098 309.9984357 309.9984549 309.9985122 309.9986062 309.9987344 309.9988938 309.9990805 309.9992898 309.9995166 309.9997553 310 310.0002447 310.0004834 310.0007102 310.0009195 310.0011062 310.0012656 310.0013938 310.0014878 310.0015451 310.0015643 310.0015451 310.0014878 310.0013938 310.0012656 310.0011062 310.0009195 310.0007102 310.0004834 310.0002447 310 309.9997553 309.9995166 309.9992898 309.9990805 309.9988938 309.9987344 309.9986062 309.9985122 309.9984549 309.9984357 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310 310.0015643 310.0015451 310.0014878 310.0013938 310.0012656 310.0011062 310.0009195 310.0007102 310.0004834 310.0002447 310 309.9997553 309.9995166 309.9992898 309.9990805 309.9988938 309.9987344 309.9986062 309.9985122 309.9984549 309.9984357 309.9984549 309.9985122 309.9986062 309.9987344 309.9988938 309.9990805 309.9992898 309.9995166 309.9997553 310 310.0002447 310.0004834 310.0007102 310.0009195 310.0011062 310.0012656 310.0013938 310.0014878 310.0015451 310.0015643 310.0030902 310.0030521 310.0029389 310.0027534 310.0025 310.0021851 310.0018164 310.0014029 310.0009549 310.0004834 310 309.9995166 309.9990451 309.9985971 309.9981836 309.9978149 309.9975 309.9972466 309.9970611 309.9969479 309.9969098 309.9969479 309.9970611 309.9972466 309.9975 309.9978149 309.9981836 309.9985971 309.9990451 309.9995166 310 310.0004834 310.0009549 310.0014029 310.0018164 310.0021851 310.0025 310.0027534 310.0029389 310.0030521 310.0030902 310.0045399 310.004484 310.0043177 310.0040451 310.0036729 310.0032102 310.0026685 310.0020611 310.0014029 310.0007102 310 309.9992898 309.9985971 309.9979389 309.9973315 309.9967898 309.9963271 309.9959549 309.9956823 309.995516 309.9954601 309.995516 309.9956823 309.9959549 309.9963271 309.9967898 309.9973315 309.9979389 309.9985971 309.9992898 310 310.0007102 310.0014029 310.0020611 310.0026685 310.0032102 310.0036729 310.0040451 310.0043177 310.004484 310.0045399 310.0058779 310.0058055 310.0055902 310.0052372 310.0047553 310.0041563 310.0034549 310.0026685 310.0018164 310.0009195 310 309.9990805 309.9981836 309.9973315 309.9965451 309.9958437 309.9952447 309.9947628 309.9944098 309.9941945 309.9941221 309.9941945 309.9944098 309.9947628 309.9952447 309.9958437 309.9965451 309.9973315 309.9981836 309.9990805 310 310.0009195 310.0018164 310.0026685 310.0034549 310.0041563 310.0047553 310.0052372 310.0055902 310.0058055 310.0058779 310.0070711 310.006984 310.006725 310.0063004 310.0057206 310.005 310.0041563 310.0032102 310.0021851 310.0011062 310 309.9988938 309.9978149 309.9967898 309.9958437 309.995 309.9942794 309.9936996 309.993275 309.993016 309.9929289 309.993016 309.993275 309.9936996 309.9942794 309.995 309.9958437 309.9967898 309.9978149 309.9988938 310 310.0011062 310.0021851 310.0032102 310.0041563 310.005 310.0057206 310.0063004 310.006725 310.006984 310.0070711 310.0080902 310.0079906 310.0076942 310.0072084 310.0065451 310.0057206 310.0047553 310.0036729 310.0025 310.0012656 310 309.9987344 309.9975 309.9963271 309.9952447 309.9942794 309.9934549 309.9927916 309.9923058 309.9920094 309.9919098 309.9920094 309.9923058 309.9927916 309.9934549 309.9942794 309.9952447 309.9963271 309.9975 309.9987344 310 310.0012656 310.0025 310.0036729 310.0047553 310.0057206 310.0065451 310.0072084 310.0076942 310.0079906 310.0080902 310.0089101 310.0088004 310.008474 310.0079389 310.0072084 310.0063004 310.0052372 310.0040451 310.0027534 310.0013938 310 309.9986062 309.9972466 309.9959549 309.9947628 309.9936996 309.9927916 309.9920611 309.991526 309.9911996 309.9910899 309.9911996 309.991526 309.9920611 309.9927916 309.9936996 309.9947628 309.9959549 309.9972466 309.9986062 310 310.0013938 310.0027534 310.0040451 310.0052372 310.0063004 310.0072084 310.0079389 310.008474 310.0088004 310.0089101 310.0095106 310.0093935 310.0090451 310.008474 310.0076942 310.006725 310.0055902 310.0043177 310.0029389 310.0014878 310 309.9985122 309.9970611 309.9956823 309.9944098 309.993275 309.9923058 309.991526 309.9909549 309.9906065 309.9904894 309.9906065 309.9909549 309.991526 309.9923058 309.993275 309.9944098 309.9956823 309.9970611 309.9985122 310 310.0014878 310.0029389 310.0043177 310.0055902 310.006725 310.0076942 310.008474 310.0090451 310.0093935 310.0095106 310.0098769 310.0097553 310.0093935 310.0088004 310.0079906 310.006984 310.0058055 310.004484 310.0030521 310.0015451 310 309.9984549 309.9969479 309.995516 309.9941945 309.993016 309.9920094 309.9911996 309.9906065 309.9902447 309.9901231 309.9902447 309.9906065 309.9911996 309.9920094 309.993016 309.9941945 309.995516 309.9969479 309.9984549 310 310.0015451 310.0030521 310.004484 310.0058055 310.006984 310.0079906 310.0088004 310.0093935 310.0097553 310.0098769 310.01 310.0098769 310.0095106 310.0089101 310.0080902 310.0070711 310.0058779 310.0045399 310.0030902 310.0015643 310 309.9984357 309.9969098 309.9954601 309.9941221 309.9929289 309.9919098 309.9910899 309.9904894 309.9901231 309.99 309.9901231 309.9904894 309.9910899 309.9919098 309.9929289 309.9941221 309.9954601 309.9969098 309.9984357 310 310.0015643 310.0030902 310.0045399 310.0058779 310.0070711 310.0080902 310.0089101 310.0095106 310.0098769 310.01 309.0098769 309.0097553 309.0093935 309.0088004 309.0079906 309.006984 309.0058055 309.004484 309.0030521 309.0015451 309 308.9984549 308.9969479 308.995516 308.9941945 308.993016 308.9920094 308.9911996 308.9906065 308.9902447 308.9901231 308.9902447 308.9906065 308.9911996 308.9920094 308.993016 308.9941945 308.995516 308.9969479 308.9984549 309 309.0015451 309.0030521 309.004484 309.0058055 309.006984 309.0079906 309.0088004 309.0093935 309.0097553 309.0098769 309.0097553 309.0096352 309.0092778 309.008692 309.0078922 309.006898 309.005734 309.0044288 309.0030145 309.0015261 309 308.9984739 308.9969855 308.9955712 308.994266 308.993102 308.9921078 308.991308 308.9907222 308.9903648 308.9902447 308.9903648 308.9907222 308.991308 308.9921078 308.993102 308.994266 308.9955712 308.9969855 308.9984739 309 309.0015261 309.0030145 309.0044288 309.005734 309.006898 309.0078922 309.008692 309.0092778 309.0096352 309.0097553 309.0093935 309.0092778 309.0089337 309.0083696 309.0075995 309.0066422 309.0055213 309.0042645 309.0029027 309.0014695 309 308.9985305 308.9970973 308.9957355 308.9944787 308.9933578 308.9924005 308.9916304 308.9910663 308.9907222 308.9906065 308.9907222 308.9910663 308.9916304 308.9924005 308.9933578 308.9944787 308.9957355 308.9970973 308.9985305 309 309.0014695 309.0029027 309.0042645 309.0055213 309.0066422 309.0075995 309.0083696 309.0089337 309.0092778 309.0093935 309.0088004 309.008692 309.0083696 309.0078412 309.0071196 309.0062228 309.0051727 309.0039953 309.0027195 309.0013767 309 308.9986233 308.9972805 308.9960047 308.9948273 308.9937772 308.9928804 308.9921588 308.9916304 308.991308 308.9911996 308.991308 308.9916304 308.9921588 308.9928804 308.9937772 308.9948273 308.9960047 308.9972805 308.9986233 309 309.0013767 309.0027195 309.0039953 309.0051727 309.0062228 309.0071196 309.0078412 309.0083696 309.008692 309.0088004 309.0079906 309.0078922 309.0075995 309.0071196 309.0064645 309.0056502 309.0046967 309.0036276 309.0024692 309.00125 309 308.99875 308.9975308 308.9963724 308.9953033 308.9943498 308.9935355 308.9928804 308.9924005 308.9921078 308.9920094 308.9921078 308.9924005 308.9928804 308.9935355 308.9943498 308.9953033 308.9963724 308.9975308 308.99875 309 309.00125 309.0024692 309.0036276 309.0046967 309.0056502 309.0064645 309.0071196 309.0075995 309.0078922 309.0079906 309.006984 309.006898 309.0066422 309.0062228 309.0056502 309.0049384 309.0041051 309.0031707 309.0021582 309.0010925 309 308.9989075 308.9978418 308.9968293 308.9958949 308.9950616 308.9943498 308.9937772 308.9933578 308.993102 308.993016 308.993102 308.9933578 308.9937772 308.9943498 308.9950616 308.9958949 308.9968293 308.9978418 308.9989075 309 309.0010925 309.0021582 309.0031707 309.0041051 309.0049384 309.0056502 309.0062228 309.0066422 309.006898 309.006984 309.0058055 309.005734 309.0055213 309.0051727 309.0046967 309.0041051 309.0034124 309.0026356 309.001794 309.0009082 309 308.9990918 308.998206 308.9973644 308.9965876 308.9958949 308.9953033 308.9948273 308.9944787 308.994266 308.9941945 308.994266 308.9944787 308.9948273 308.9953033 308.9958949 308.9965876 308.9973644 308.998206 308.9990918 309 309.0009082 309.001794 309.0026356 309.0034124 309.0041051 309.0046967 309.0051727 309.0055213 309.005734 309.0058055 309.004484 309.0044288 309.0042645 309.0039953 309.0036276 309.0031707 309.0026356 309.0020357 309.0013856 309.0007015 309 308.9992985 308.9986144 308.9979643 308.9973644 308.9968293 308.9963724 308.9960047 308.9957355 308.9955712 308.995516 308.9955712 308.9957355 308.9960047 308.9963724 308.9968293 308.9973644 308.9979643 308.9986144 308.9992985 309 309.0007015 309.0013856 309.0020357 309.0026356 309.0031707 309.0036276 309.0039953 309.0042645 309.0044288 309.004484 309.0030521 309.0030145 309.0029027 309.0027195 309.0024692 309.0021582 309.001794 309.0013856 309.0009432 309.0004775 309 308.9995225 308.9990568 308.9986144 308.998206 308.9978418 308.9975308 308.9972805 308.9970973 308.9969855 308.9969479 308.9969855 308.9970973 308.9972805 308.9975308 308.9978418 308.998206 308.9986144 308.9990568 308.9995225 309 309.0004775 309.0009432 309.0013856 309.001794 309.0021582 309.0024692 309.0027195 309.0029027 309.0030145 309.0030521 309.0015451 309.0015261 309.0014695 309.0013767 309.00125 309.0010925 309.0009082 309.0007015 309.0004775 309.0002417 309 308.9997583 308.9995225 308.9992985 308.9990918 308.9989075 308.99875 308.9986233 308.9985305 308.9984739 308.9984549 308.9984739 308.9985305 308.9986233 308.99875 308.9989075 308.9990918 308.9992985 308.9995225 308.9997583 309 309.0002417 309.0004775 309.0007015 309.0009082 309.0010925 309.00125 309.0013767 309.0014695 309.0015261 309.0015451 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 308.9984549 308.9984739 308.9985305 308.9986233 308.99875 308.9989075 308.9990918 308.9992985 308.9995225 308.9997583 309 309.0002417 309.0004775 309.0007015 309.0009082 309.0010925 309.00125 309.0013767 309.0014695 309.0015261 309.0015451 309.0015261 309.0014695 309.0013767 309.00125 309.0010925 309.0009082 309.0007015 309.0004775 309.0002417 309 308.9997583 308.9995225 308.9992985 308.9990918 308.9989075 308.99875 308.9986233 308.9985305 308.9984739 308.9984549 308.9969479 308.9969855 308.9970973 308.9972805 308.9975308 308.9978418 308.998206 308.9986144 308.9990568 308.9995225 309 309.0004775 309.0009432 309.0013856 309.001794 309.0021582 309.0024692 309.0027195 309.0029027 309.0030145 309.0030521 309.0030145 309.0029027 309.0027195 309.0024692 309.0021582 309.001794 309.0013856 309.0009432 309.0004775 309 308.9995225 308.9990568 308.9986144 308.998206 308.9978418 308.9975308 308.9972805 308.9970973 308.9969855 308.9969479 308.995516 308.9955712 308.9957355 308.9960047 308.9963724 308.9968293 308.9973644 308.9979643 308.9986144 308.9992985 309 309.0007015 309.0013856 309.0020357 309.0026356 309.0031707 309.0036276 309.0039953 309.0042645 309.0044288 309.004484 309.0044288 309.0042645 309.0039953 309.0036276 309.0031707 309.0026356 309.0020357 309.0013856 309.0007015 309 308.9992985 308.9986144 308.9979643 308.9973644 308.9968293 308.9963724 308.9960047 308.9957355 308.9955712 308.995516 308.9941945 308.994266 308.9944787 308.9948273 308.9953033 308.9958949 308.9965876 308.9973644 308.998206 308.9990918 309 309.0009082 309.001794 309.0026356 309.0034124 309.0041051 309.0046967 309.0051727 309.0055213 309.005734 309.0058055 309.005734 309.0055213 309.0051727 309.0046967 309.0041051 309.0034124 309.0026356 309.001794 309.0009082 309 308.9990918 308.998206 308.9973644 308.9965876 308.9958949 308.9953033 308.9948273 308.9944787 308.994266 308.9941945 308.993016 308.993102 308.9933578 308.9937772 308.9943498 308.9950616 308.9958949 308.9968293 308.9978418 308.9989075 309 309.0010925 309.0021582 309.0031707 309.0041051 309.0049384 309.0056502 309.0062228 309.0066422 309.006898 309.006984 309.006898 309.0066422 309.0062228 309.0056502 309.0049384 309.0041051 309.0031707 309.0021582 309.0010925 309 308.9989075 308.9978418 308.9968293 308.9958949 308.9950616 308.9943498 308.9937772 308.9933578 308.993102 308.993016 308.9920094 308.9921078 308.9924005 308.9928804 308.9935355 308.9943498 308.9953033 308.9963724 308.9975308 308.99875 309 309.00125 309.0024692 309.0036276 309.0046967 309.0056502 309.0064645 309.0071196 309.0075995 309.0078922 309.0079906 309.0078922 309.0075995 309.0071196 309.0064645 309.0056502 309.0046967 309.0036276 309.0024692 309.00125 309 308.99875 308.9975308 308.9963724 308.9953033 308.9943498 308.9935355 308.9928804 308.9924005 308.9921078 308.9920094 308.9911996 308.991308 308.9916304 308.9921588 308.9928804 308.9937772 308.9948273 308.9960047 308.9972805 308.9986233 309 309.0013767 309.0027195 309.0039953 309.0051727 309.0062228 309.0071196 309.0078412 309.0083696 309.008692 309.0088004 309.008692 309.0083696 309.0078412 309.0071196 309.0062228 309.0051727 309.0039953 309.0027195 309.0013767 309 308.9986233 308.9972805 308.9960047 308.9948273 308.9937772 308.9928804 308.9921588 308.9916304 308.991308 308.9911996 308.9906065 308.9907222 308.9910663 308.9916304 308.9924005 308.9933578 308.9944787 308.9957355 308.9970973 308.9985305 309 309.0014695 309.0029027 309.0042645 309.0055213 309.0066422 309.0075995 309.0083696 309.0089337 309.0092778 309.0093935 309.0092778 309.0089337 309.0083696 309.0075995 309.0066422 309.0055213 309.0042645 309.0029027 309.0014695 309 308.9985305 308.9970973 308.9957355 308.9944787 308.9933578 308.9924005 308.9916304 308.9910663 308.9907222 308.9906065 308.9902447 308.9903648 308.9907222 308.991308 308.9921078 308.993102 308.994266 308.9955712 308.9969855 308.9984739 309 309.0015261 309.0030145 309.0044288 309.005734 309.006898 309.0078922 309.008692 309.0092778 309.0096352 309.0097553 309.0096352 309.0092778 309.008692 309.0078922 309.006898 309.005734 309.0044288 309.0030145 309.0015261 309 308.9984739 308.9969855 308.9955712 308.994266 308.993102 308.9921078 308.991308 308.9907222 308.9903648 308.9902447 308.9901231 308.9902447 308.9906065 308.9911996 308.9920094 308.993016 308.9941945 308.995516 308.9969479 308.9984549 309 309.0015451 309.0030521 309.004484 309.0058055 309.006984 309.0079906 309.0088004 309.0093935 309.0097553 309.0098769 309.0097553 309.0093935 309.0088004 309.0079906 309.006984 309.0058055 309.004484 309.0030521 309.0015451 309 308.9984549 308.9969479 308.995516 308.9941945 308.993016 308.9920094 308.9911996 308.9906065 308.9902447 308.9901231 308.9902447 308.9903648 308.9907222 308.991308 308.9921078 308.993102 308.994266 308.9955712 308.9969855 308.9984739 309 309.0015261 309.0030145 309.0044288 309.005734 309.006898 309.0078922 309.008692 309.0092778 309.0096352 309.0097553 309.0096352 309.0092778 309.008692 309.0078922 309.006898 309.005734 309.0044288 309.0030145 309.0015261 309 308.9984739 308.9969855 308.9955712 308.994266 308.993102 308.9921078 308.991308 308.9907222 308.9903648 308.9902447 308.9906065 308.9907222 308.9910663 308.9916304 308.9924005 308.9933578 308.9944787 308.9957355 308.9970973 308.9985305 309 309.0014695 309.0029027 309.0042645 309.0055213 309.0066422 309.0075995 309.0083696 309.0089337 309.0092778 309.0093935 309.0092778 309.0089337 309.0083696 309.0075995 309.0066422 309.0055213 309.0042645 309.0029027 309.0014695 309 308.9985305 308.9970973 308.9957355 308.9944787 308.9933578 308.9924005 308.9916304 308.9910663 308.9907222 308.9906065 308.9911996 308.991308 308.9916304 308.9921588 308.9928804 308.9937772 308.9948273 308.9960047 308.9972805 308.9986233 309 309.0013767 309.0027195 309.0039953 309.0051727 309.0062228 309.0071196 309.0078412 309.0083696 309.008692 309.0088004 309.008692 309.0083696 309.0078412 309.0071196 309.0062228 309.0051727 309.0039953 309.0027195 309.0013767 309 308.9986233 308.9972805 308.9960047 308.9948273 308.9937772 308.9928804 308.9921588 308.9916304 308.991308 308.9911996 308.9920094 308.9921078 308.9924005 308.9928804 308.9935355 308.9943498 308.9953033 308.9963724 308.9975308 308.99875 309 309.00125 309.0024692 309.0036276 309.0046967 309.0056502 309.0064645 309.0071196 309.0075995 309.0078922 309.0079906 309.0078922 309.0075995 309.0071196 309.0064645 309.0056502 309.0046967 309.0036276 309.0024692 309.00125 309 308.99875 308.9975308 308.9963724 308.9953033 308.9943498 308.9935355 308.9928804 308.9924005 308.9921078 308.9920094 308.993016 308.993102 308.9933578 308.9937772 308.9943498 308.9950616 308.9958949 308.9968293 308.9978418 308.9989075 309 309.0010925 309.0021582 309.0031707 309.0041051 309.0049384 309.0056502 309.0062228 309.0066422 309.006898 309.006984 309.006898 309.0066422 309.0062228 309.0056502 309.0049384 309.0041051 309.0031707 309.0021582 309.0010925 309 308.9989075 308.9978418 308.9968293 308.9958949 308.9950616 308.9943498 308.9937772 308.9933578 308.993102 308.993016 308.9941945 308.994266 308.9944787 308.9948273 308.9953033 308.9958949 308.9965876 308.9973644 308.998206 308.9990918 309 309.0009082 309.001794 309.0026356 309.0034124 309.0041051 309.0046967 309.0051727 309.0055213 309.005734 309.0058055 309.005734 309.0055213 309.0051727 309.0046967 309.0041051 309.0034124 309.0026356 309.001794 309.0009082 309 308.9990918 308.998206 308.9973644 308.9965876 308.9958949 308.9953033 308.9948273 308.9944787 308.994266 308.9941945 308.995516 308.9955712 308.9957355 308.9960047 308.9963724 308.9968293 308.9973644 308.9979643 308.9986144 308.9992985 309 309.0007015 309.0013856 309.0020357 309.0026356 309.0031707 309.0036276 309.0039953 309.0042645 309.0044288 309.004484 309.0044288 309.0042645 309.0039953 309.0036276 309.0031707 309.0026356 309.0020357 309.0013856 309.0007015 309 308.9992985 308.9986144 308.9979643 308.9973644 308.9968293 308.9963724 308.9960047 308.9957355 308.9955712 308.995516 308.9969479 308.9969855 308.9970973 308.9972805 308.9975308 308.9978418 308.998206 308.9986144 308.9990568 308.9995225 309 309.0004775 309.0009432 309.0013856 309.001794 309.0021582 309.0024692 309.0027195 309.0029027 309.0030145 309.0030521 309.0030145 309.0029027 309.0027195 309.0024692 309.0021582 309.001794 309.0013856 309.0009432 309.0004775 309 308.9995225 308.9990568 308.9986144 308.998206 308.9978418 308.9975308 308.9972805 308.9970973 308.9969855 308.9969479 308.9984549 308.9984739 308.9985305 308.9986233 308.99875 308.9989075 308.9990918 308.9992985 308.9995225 308.9997583 309 309.0002417 309.0004775 309.0007015 309.0009082 309.0010925 309.00125 309.0013767 309.0014695 309.0015261 309.0015451 309.0015261 309.0014695 309.0013767 309.00125 309.0010925 309.0009082 309.0007015 309.0004775 309.0002417 309 308.9997583 308.9995225 308.9992985 308.9990918 308.9989075 308.99875 308.9986233 308.9985305 308.9984739 308.9984549 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309 309.0015451 309.0015261 309.0014695 309.0013767 309.00125 309.0010925 309.0009082 309.0007015 309.0004775 309.0002417 309 308.9997583 308.9995225 308.9992985 308.9990918 308.9989075 308.99875 308.9986233 308.9985305 308.9984739 308.9984549 308.9984739 308.9985305 308.9986233 308.99875 308.9989075 308.9990918 308.9992985 308.9995225 308.9997583 309 309.0002417 309.0004775 309.0007015 309.0009082 309.0010925 309.00125 309.0013767 309.0014695 309.0015261 309.0015451 309.0030521 309.0030145 309.0029027 309.0027195 309.0024692 309.0021582 309.001794 309.0013856 309.0009432 309.0004775 309 308.9995225 308.9990568 308.9986144 308.998206 308.9978418 308.9975308 308.9972805 308.9970973 308.9969855 308.9969479 308.9969855 308.9970973 308.9972805 308.9975308 308.9978418 308.998206 308.9986144 308.9990568 308.9995225 309 309.0004775 309.0009432 309.0013856 309.001794 309.0021582 309.0024692 309.0027195 309.0029027 309.0030145 309.0030521 309.004484 309.0044288 309.0042645 309.0039953 309.0036276 309.0031707 309.0026356 309.0020357 309.0013856 309.0007015 309 308.9992985 308.9986144 308.9979643 308.9973644 308.9968293 308.9963724 308.9960047 308.9957355 308.9955712 308.995516 308.9955712 308.9957355 308.9960047 308.9963724 308.9968293 308.9973644 308.9979643 308.9986144 308.9992985 309 309.0007015 309.0013856 309.0020357 309.0026356 309.0031707 309.0036276 309.0039953 309.0042645 309.0044288 309.004484 309.0058055 309.005734 309.0055213 309.0051727 309.0046967 309.0041051 309.0034124 309.0026356 309.001794 309.0009082 309 308.9990918 308.998206 308.9973644 308.9965876 308.9958949 308.9953033 308.9948273 308.9944787 308.994266 308.9941945 308.994266 308.9944787 308.9948273 308.9953033 308.9958949 308.9965876 308.9973644 308.998206 308.9990918 309 309.0009082 309.001794 309.0026356 309.0034124 309.0041051 309.0046967 309.0051727 309.0055213 309.005734 309.0058055 309.006984 309.006898 309.0066422 309.0062228 309.0056502 309.0049384 309.0041051 309.0031707 309.0021582 309.0010925 309 308.9989075 308.9978418 308.9968293 308.9958949 308.9950616 308.9943498 308.9937772 308.9933578 308.993102 308.993016 308.993102 308.9933578 308.9937772 308.9943498 308.9950616 308.9958949 308.9968293 308.9978418 308.9989075 309 309.0010925 309.0021582 309.0031707 309.0041051 309.0049384 309.0056502 309.0062228 309.0066422 309.006898 309.006984 309.0079906 309.0078922 309.0075995 309.0071196 309.0064645 309.0056502 309.0046967 309.0036276 309.0024692 309.00125 309 308.99875 308.9975308 308.9963724 308.9953033 308.9943498 308.9935355 308.9928804 308.9924005 308.9921078 308.9920094 308.9921078 308.9924005 308.9928804 308.9935355 308.9943498 308.9953033 308.9963724 308.9975308 308.99875 309 309.00125 309.0024692 309.0036276 309.0046967 309.0056502 309.0064645 309.0071196 309.0075995 309.0078922 309.0079906 309.0088004 309.008692 309.0083696 309.0078412 309.0071196 309.0062228 309.0051727 309.0039953 309.0027195 309.0013767 309 308.9986233 308.9972805 308.9960047 308.9948273 308.9937772 308.9928804 308.9921588 308.9916304 308.991308 308.9911996 308.991308 308.9916304 308.9921588 308.9928804 308.9937772 308.9948273 308.9960047 308.9972805 308.9986233 309 309.0013767 309.0027195 309.0039953 309.0051727 309.0062228 309.0071196 309.0078412 309.0083696 309.008692 309.0088004 309.0093935 309.0092778 309.0089337 309.0083696 309.0075995 309.0066422 309.0055213 309.0042645 309.0029027 309.0014695 309 308.9985305 308.9970973 308.9957355 308.9944787 308.9933578 308.9924005 308.9916304 308.9910663 308.9907222 308.9906065 308.9907222 308.9910663 308.9916304 308.9924005 308.9933578 308.9944787 308.9957355 308.9970973 308.9985305 309 309.0014695 309.0029027 309.0042645 309.0055213 309.0066422 309.0075995 309.0083696 309.0089337 309.0092778 309.0093935 309.0097553 309.0096352 309.0092778 309.008692 309.0078922 309.006898 309.005734 309.0044288 309.0030145 309.0015261 309 308.9984739 308.9969855 308.9955712 308.994266 308.993102 308.9921078 308.991308 308.9907222 308.9903648 308.9902447 308.9903648 308.9907222 308.991308 308.9921078 308.993102 308.994266 308.9955712 308.9969855 308.9984739 309 309.0015261 309.0030145 309.0044288 309.005734 309.006898 309.0078922 309.008692 309.0092778 309.0096352 309.0097553 309.0098769 309.0097553 309.0093935 309.0088004 309.0079906 309.006984 309.0058055 309.004484 309.0030521 309.0015451 309 308.9984549 308.9969479 308.995516 308.9941945 308.993016 308.9920094 308.9911996 308.9906065 308.9902447 308.9901231 308.9902447 308.9906065 308.9911996 308.9920094 308.993016 308.9941945 308.995516 308.9969479 308.9984549 309 309.0015451 309.0030521 309.004484 309.0058055 309.006984 309.0079906 309.0088004 309.0093935 309.0097553 309.0098769 308.0095106 308.0093935 308.0090451 308.008474 308.0076942 308.006725 308.0055902 308.0043177 308.0029389 308.0014878 308 307.9985122 307.9970611 307.9956823 307.9944098 307.993275 307.9923058 307.991526 307.9909549 307.9906065 307.9904894 307.9906065 307.9909549 307.991526 307.9923058 307.993275 307.9944098 307.9956823 307.9970611 307.9985122 308 308.0014878 308.0029389 308.0043177 308.0055902 308.006725 308.0076942 308.008474 308.0090451 308.0093935 308.0095106 308.0093935 308.0092778 308.0089337 308.0083696 308.0075995 308.0066422 308.0055213 308.0042645 308.0029027 308.0014695 308 307.9985305 307.9970973 307.9957355 307.9944787 307.9933578 307.9924005 307.9916304 307.9910663 307.9907222 307.9906065 307.9907222 307.9910663 307.9916304 307.9924005 307.9933578 307.9944787 307.9957355 307.9970973 307.9985305 308 308.0014695 308.0029027 308.0042645 308.0055213 308.0066422 308.0075995 308.0083696 308.0089337 308.0092778 308.0093935 308.0090451 308.0089337 308.0086024 308.0080592 308.0073176 308.0063958 308.0053166 308.0041064 308.0027951 308.001415 308 307.998585 307.9972049 307.9958936 307.9946834 307.9936042 307.9926824 307.9919408 307.9913976 307.9910663 307.9909549 307.9910663 307.9913976 307.9919408 307.9926824 307.9936042 307.9946834 307.9958936 307.9972049 307.998585 308 308.001415 308.0027951 308.0041064 308.0053166 308.0063958 308.0073176 308.0080592 308.0086024 308.0089337 308.0090451 308.008474 308.0083696 308.0080592 308.0075504 308.0068556 308.005992 308.0049809 308.0038471 308.0026186 308.0013256 308 307.9986744 307.9973814 307.9961529 307.9950191 307.994008 307.9931444 307.9924496 307.9919408 307.9916304 307.991526 307.9916304 307.9919408 307.9924496 307.9931444 307.994008 307.9950191 307.9961529 307.9973814 307.9986744 308 308.0013256 308.0026186 308.0038471 308.0049809 308.005992 308.0068556 308.0075504 308.0080592 308.0083696 308.008474 308.0076942 308.0075995 308.0073176 308.0068556 308.0062247 308.0054406 308.0045225 308.0034931 308.0023776 308.0012036 308 307.9987964 307.9976224 307.9965069 307.9954775 307.9945594 307.9937753 307.9931444 307.9926824 307.9924005 307.9923058 307.9924005 307.9926824 307.9931444 307.9937753 307.9945594 307.9954775 307.9965069 307.9976224 307.9987964 308 308.0012036 308.0023776 308.0034931 308.0045225 308.0054406 308.0062247 308.0068556 308.0073176 308.0075995 308.0076942 308.006725 308.0066422 308.0063958 308.005992 308.0054406 308.0047553 308.0039528 308.0030531 308.0020781 308.001052 308 307.998948 307.9979219 307.9969469 307.9960472 307.9952447 307.9945594 307.994008 307.9936042 307.9933578 307.993275 307.9933578 307.9936042 307.994008 307.9945594 307.9952447 307.9960472 307.9969469 307.9979219 307.998948 308 308.001052 308.0020781 308.0030531 308.0039528 308.0047553 308.0054406 308.005992 308.0063958 308.0066422 308.006725 308.0055902 308.0055213 308.0053166 308.0049809 308.0045225 308.0039528 308.0032858 308.0025379 308.0017275 308.0008745 308 307.9991255 307.9982725 307.9974621 307.9967142 307.9960472 307.9954775 307.9950191 307.9946834 307.9944787 307.9944098 307.9944787 307.9946834 307.9950191 307.9954775 307.9960472 307.9967142 307.9974621 307.9982725 307.9991255 308 308.0008745 308.0017275 308.0025379 308.0032858 308.0039528 308.0045225 308.0049809 308.0053166 308.0055213 308.0055902 308.0043177 308.0042645 308.0041064 308.0038471 308.0034931 308.0030531 308.0025379 308.0019602 308.0013342 308.0006754 308 307.9993246 307.9986658 307.9980398 307.9974621 307.9969469 307.9965069 307.9961529 307.9958936 307.9957355 307.9956823 307.9957355 307.9958936 307.9961529 307.9965069 307.9969469 307.9974621 307.9980398 307.9986658 307.9993246 308 308.0006754 308.0013342 308.0019602 308.0025379 308.0030531 308.0034931 308.0038471 308.0041064 308.0042645 308.0043177 308.0029389 308.0029027 308.0027951 308.0026186 308.0023776 308.0020781 308.0017275 308.0013342 308.0009082 308.0004597 308 307.9995403 307.9990918 307.9986658 307.9982725 307.9979219 307.9976224 307.9973814 307.9972049 307.9970973 307.9970611 307.9970973 307.9972049 307.9973814 307.9976224 307.9979219 307.9982725 307.9986658 307.9990918 307.9995403 308 308.0004597 308.0009082 308.0013342 308.0017275 308.0020781 308.0023776 308.0026186 308.0027951 308.0029027 308.0029389 308.0014878 308.0014695 308.001415 308.0013256 308.0012036 308.001052 308.0008745 308.0006754 308.0004597 308.0002327 308 307.9997673 307.9995403 307.9993246 307.9991255 307.998948 307.9987964 307.9986744 307.998585 307.9985305 307.9985122 307.9985305 307.998585 307.9986744 307.9987964 307.998948 307.9991255 307.9993246 307.9995403 307.9997673 308 308.0002327 308.0004597 308.0006754 308.0008745 308.001052 308.0012036 308.0013256 308.001415 308.0014695 308.0014878 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 307.9985122 307.9985305 307.998585 307.9986744 307.9987964 307.998948 307.9991255 307.9993246 307.9995403 307.9997673 308 308.0002327 308.0004597 308.0006754 308.0008745 308.001052 308.0012036 308.0013256 308.001415 308.0014695 308.0014878 308.0014695 308.001415 308.0013256 308.0012036 308.001052 308.0008745 308.0006754 308.0004597 308.0002327 308 307.9997673 307.9995403 307.9993246 307.9991255 307.998948 307.9987964 307.9986744 307.998585 307.9985305 307.9985122 307.9970611 307.9970973 307.9972049 307.9973814 307.9976224 307.9979219 307.9982725 307.9986658 307.9990918 307.9995403 308 308.0004597 308.0009082 308.0013342 308.0017275 308.0020781 308.0023776 308.0026186 308.0027951 308.0029027 308.0029389 308.0029027 308.0027951 308.0026186 308.0023776 308.0020781 308.0017275 308.0013342 308.0009082 308.0004597 308 307.9995403 307.9990918 307.9986658 307.9982725 307.9979219 307.9976224 307.9973814 307.9972049 307.9970973 307.9970611 307.9956823 307.9957355 307.9958936 307.9961529 307.9965069 307.9969469 307.9974621 307.9980398 307.9986658 307.9993246 308 308.0006754 308.0013342 308.0019602 308.0025379 308.0030531 308.0034931 308.0038471 308.0041064 308.0042645 308.0043177 308.0042645 308.0041064 308.0038471 308.0034931 308.0030531 308.0025379 308.0019602 308.0013342 308.0006754 308 307.9993246 307.9986658 307.9980398 307.9974621 307.9969469 307.9965069 307.9961529 307.9958936 307.9957355 307.9956823 307.9944098 307.9944787 307.9946834 307.9950191 307.9954775 307.9960472 307.9967142 307.9974621 307.9982725 307.9991255 308 308.0008745 308.0017275 308.0025379 308.0032858 308.0039528 308.0045225 308.0049809 308.0053166 308.0055213 308.0055902 308.0055213 308.0053166 308.0049809 308.0045225 308.0039528 308.0032858 308.0025379 308.0017275 308.0008745 308 307.9991255 307.9982725 307.9974621 307.9967142 307.9960472 307.9954775 307.9950191 307.9946834 307.9944787 307.9944098 307.993275 307.9933578 307.9936042 307.994008 307.9945594 307.9952447 307.9960472 307.9969469 307.9979219 307.998948 308 308.001052 308.0020781 308.0030531 308.0039528 308.0047553 308.0054406 308.005992 308.0063958 308.0066422 308.006725 308.0066422 308.0063958 308.005992 308.0054406 308.0047553 308.0039528 308.0030531 308.0020781 308.001052 308 307.998948 307.9979219 307.9969469 307.9960472 307.9952447 307.9945594 307.994008 307.9936042 307.9933578 307.993275 307.9923058 307.9924005 307.9926824 307.9931444 307.9937753 307.9945594 307.9954775 307.9965069 307.9976224 307.9987964 308 308.0012036 308.0023776 308.0034931 308.0045225 308.0054406 308.0062247 308.0068556 308.0073176 308.0075995 308.0076942 308.0075995 308.0073176 308.0068556 308.0062247 308.0054406 308.0045225 308.0034931 308.0023776 308.0012036 308 307.9987964 307.9976224 307.9965069 307.9954775 307.9945594 307.9937753 307.9931444 307.9926824 307.9924005 307.9923058 307.991526 307.9916304 307.9919408 307.9924496 307.9931444 307.994008 307.9950191 307.9961529 307.9973814 307.9986744 308 308.0013256 308.0026186 308.0038471 308.0049809 308.005992 308.0068556 308.0075504 308.0080592 308.0083696 308.008474 308.0083696 308.0080592 308.0075504 308.0068556 308.005992 308.0049809 308.0038471 308.0026186 308.0013256 308 307.9986744 307.9973814 307.9961529 307.9950191 307.994008 307.9931444 307.9924496 307.9919408 307.9916304 307.991526 307.9909549 307.9910663 307.9913976 307.9919408 307.9926824 307.9936042 307.9946834 307.9958936 307.9972049 307.998585 308 308.001415 308.0027951 308.0041064 308.0053166 308.0063958 308.0073176 308.0080592 308.0086024 308.0089337 308.0090451 308.0089337 308.0086024 308.0080592 308.0073176 308.0063958 308.0053166 308.0041064 308.0027951 308.001415 308 307.998585 307.9972049 307.9958936 307.9946834 307.9936042 307.9926824 307.9919408 307.9913976 307.9910663 307.9909549 307.9906065 307.9907222 307.9910663 307.9916304 307.9924005 307.9933578 307.9944787 307.9957355 307.9970973 307.9985305 308 308.0014695 308.0029027 308.0042645 308.0055213 308.0066422 308.0075995 308.0083696 308.0089337 308.0092778 308.0093935 308.0092778 308.0089337 308.0083696 308.0075995 308.0066422 308.0055213 308.0042645 308.0029027 308.0014695 308 307.9985305 307.9970973 307.9957355 307.9944787 307.9933578 307.9924005 307.9916304 307.9910663 307.9907222 307.9906065 307.9904894 307.9906065 307.9909549 307.991526 307.9923058 307.993275 307.9944098 307.9956823 307.9970611 307.9985122 308 308.0014878 308.0029389 308.0043177 308.0055902 308.006725 308.0076942 308.008474 308.0090451 308.0093935 308.0095106 308.0093935 308.0090451 308.008474 308.0076942 308.006725 308.0055902 308.0043177 308.0029389 308.0014878 308 307.9985122 307.9970611 307.9956823 307.9944098 307.993275 307.9923058 307.991526 307.9909549 307.9906065 307.9904894 307.9906065 307.9907222 307.9910663 307.9916304 307.9924005 307.9933578 307.9944787 307.9957355 307.9970973 307.9985305 308 308.0014695 308.0029027 308.0042645 308.0055213 308.0066422 308.0075995 308.0083696 308.0089337 308.0092778 308.0093935 308.0092778 308.0089337 308.0083696 308.0075995 308.0066422 308.0055213 308.0042645 308.0029027 308.0014695 308 307.9985305 307.9970973 307.9957355 307.9944787 307.9933578 307.9924005 307.9916304 307.9910663 307.9907222 307.9906065 307.9909549 307.9910663 307.9913976 307.9919408 307.9926824 307.9936042 307.9946834 307.9958936 307.9972049 307.998585 308 308.001415 308.0027951 308.0041064 308.0053166 308.0063958 308.0073176 308.0080592 308.0086024 308.0089337 308.0090451 308.0089337 308.0086024 308.0080592 308.0073176 308.0063958 308.0053166 308.0041064 308.0027951 308.001415 308 307.998585 307.9972049 307.9958936 307.9946834 307.9936042 307.9926824 307.9919408 307.9913976 307.9910663 307.9909549 307.991526 307.9916304 307.9919408 307.9924496 307.9931444 307.994008 307.9950191 307.9961529 307.9973814 307.9986744 308 308.0013256 308.0026186 308.0038471 308.0049809 308.005992 308.0068556 308.0075504 308.0080592 308.0083696 308.008474 308.0083696 308.0080592 308.0075504 308.0068556 308.005992 308.0049809 308.0038471 308.0026186 308.0013256 308 307.9986744 307.9973814 307.9961529 307.9950191 307.994008 307.9931444 307.9924496 307.9919408 307.9916304 307.991526 307.9923058 307.9924005 307.9926824 307.9931444 307.9937753 307.9945594 307.9954775 307.9965069 307.9976224 307.9987964 308 308.0012036 308.0023776 308.0034931 308.0045225 308.0054406 308.0062247 308.0068556 308.0073176 308.0075995 308.0076942 308.0075995 308.0073176 308.0068556 308.0062247 308.0054406 308.0045225 308.0034931 308.0023776 308.0012036 308 307.9987964 307.9976224 307.9965069 307.9954775 307.9945594 307.9937753 307.9931444 307.9926824 307.9924005 307.9923058 307.993275 307.9933578 307.9936042 307.994008 307.9945594 307.9952447 307.9960472 307.9969469 307.9979219 307.998948 308 308.001052 308.0020781 308.0030531 308.0039528 308.0047553 308.0054406 308.005992 308.0063958 308.0066422 308.006725 308.0066422 308.0063958 308.005992 308.0054406 308.0047553 308.0039528 308.0030531 308.0020781 308.001052 308 307.998948 307.9979219 307.9969469 307.9960472 307.9952447 307.9945594 307.994008 307.9936042 307.9933578 307.993275 307.9944098 307.9944787 307.9946834 307.9950191 307.9954775 307.9960472 307.9967142 307.9974621 307.9982725 307.9991255 308 308.0008745 308.0017275 308.0025379 308.0032858 308.0039528 308.0045225 308.0049809 308.0053166 308.0055213 308.0055902 308.0055213 308.0053166 308.0049809 308.0045225 308.0039528 308.0032858 308.0025379 308.0017275 308.0008745 308 307.9991255 307.9982725 307.9974621 307.9967142 307.9960472 307.9954775 307.9950191 307.9946834 307.9944787 307.9944098 307.9956823 307.9957355 307.9958936 307.9961529 307.9965069 307.9969469 307.9974621 307.9980398 307.9986658 307.9993246 308 308.0006754 308.0013342 308.0019602 308.0025379 308.0030531 308.0034931 308.0038471 308.0041064 308.0042645 308.0043177 308.0042645 308.0041064 308.0038471 308.0034931 308.0030531 308.0025379 308.0019602 308.0013342 308.0006754 308 307.9993246 307.9986658 307.9980398 307.9974621 307.9969469 307.9965069 307.9961529 307.9958936 307.9957355 307.9956823 307.9970611 307.9970973 307.9972049 307.9973814 307.9976224 307.9979219 307.9982725 307.9986658 307.9990918 307.9995403 308 308.0004597 308.0009082 308.0013342 308.0017275 308.0020781 308.0023776 308.0026186 308.0027951 308.0029027 308.0029389 308.0029027 308.0027951 308.0026186 308.0023776 308.0020781 308.0017275 308.0013342 308.0009082 308.0004597 308 307.9995403 307.9990918 307.9986658 307.9982725 307.9979219 307.9976224 307.9973814 307.9972049 307.9970973 307.9970611 307.9985122 307.9985305 307.998585 307.9986744 307.9987964 307.998948 307.9991255 307.9993246 307.9995403 307.9997673 308 308.0002327 308.0004597 308.0006754 308.0008745 308.001052 308.0012036 308.0013256 308.001415 308.0014695 308.0014878 308.0014695 308.001415 308.0013256 308.0012036 308.001052 308.0008745 308.0006754 308.0004597 308.0002327 308 307.9997673 307.9995403 307.9993246 307.9991255 307.998948 307.9987964 307.9986744 307.998585 307.9985305 307.9985122 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308 308.0014878 308.0014695 308.001415 308.0013256 308.0012036 308.001052 308.0008745 308.0006754 308.0004597 308.0002327 308 307.9997673 307.9995403 307.9993246 307.9991255 307.998948 307.9987964 307.9986744 307.998585 307.9985305 307.9985122 307.9985305 307.998585 307.9986744 307.9987964 307.998948 307.9991255 307.9993246 307.9995403 307.9997673 308 308.0002327 308.0004597 308.0006754 308.0008745 308.001052 308.0012036 308.0013256 308.001415 308.0014695 308.0014878 308.0029389 308.0029027 308.0027951 308.0026186 308.0023776 308.0020781 308.0017275 308.0013342 308.0009082 308.0004597 308 307.9995403 307.9990918 307.9986658 307.9982725 307.9979219 307.9976224 307.9973814 307.9972049 307.9970973 307.9970611 307.9970973 307.9972049 307.9973814 307.9976224 307.9979219 307.9982725 307.9986658 307.9990918 307.9995403 308 308.0004597 308.0009082 308.0013342 308.0017275 308.0020781 308.0023776 308.0026186 308.0027951 308.0029027 308.0029389 308.0043177 308.0042645 308.0041064 308.0038471 308.0034931 308.0030531 308.0025379 308.0019602 308.0013342 308.0006754 308 307.9993246 307.9986658 307.9980398 307.9974621 307.9969469 307.9965069 307.9961529 307.9958936 307.9957355 307.9956823 307.9957355 307.9958936 307.9961529 307.9965069 307.9969469 307.9974621 307.9980398 307.9986658 307.9993246 308 308.0006754 308.0013342 308.0019602 308.0025379 308.0030531 308.0034931 308.0038471 308.0041064 308.0042645 308.0043177 308.0055902 308.0055213 308.0053166 308.0049809 308.0045225 308.0039528 308.0032858 308.0025379 308.0017275 308.0008745 308 307.9991255 307.9982725 307.9974621 307.9967142 307.9960472 307.9954775 307.9950191 307.9946834 307.9944787 307.9944098 307.9944787 307.9946834 307.9950191 307.9954775 307.9960472 307.9967142 307.9974621 307.9982725 307.9991255 308 308.0008745 308.0017275 308.0025379 308.0032858 308.0039528 308.0045225 308.0049809 308.0053166 308.0055213 308.0055902 308.006725 308.0066422 308.0063958 308.005992 308.0054406 308.0047553 308.0039528 308.0030531 308.0020781 308.001052 308 307.998948 307.9979219 307.9969469 307.9960472 307.9952447 307.9945594 307.994008 307.9936042 307.9933578 307.993275 307.9933578 307.9936042 307.994008 307.9945594 307.9952447 307.9960472 307.9969469 307.9979219 307.998948 308 308.001052 308.0020781 308.0030531 308.0039528 308.0047553 308.0054406 308.005992 308.0063958 308.0066422 308.006725 308.0076942 308.0075995 308.0073176 308.0068556 308.0062247 308.0054406 308.0045225 308.0034931 308.0023776 308.0012036 308 307.9987964 307.9976224 307.9965069 307.9954775 307.9945594 307.9937753 307.9931444 307.9926824 307.9924005 307.9923058 307.9924005 307.9926824 307.9931444 307.9937753 307.9945594 307.9954775 307.9965069 307.9976224 307.9987964 308 308.0012036 308.0023776 308.0034931 308.0045225 308.0054406 308.0062247 308.0068556 308.0073176 308.0075995 308.0076942 308.008474 308.0083696 308.0080592 308.0075504 308.0068556 308.005992 308.0049809 308.0038471 308.0026186 308.0013256 308 307.9986744 307.9973814 307.9961529 307.9950191 307.994008 307.9931444 307.9924496 307.9919408 307.9916304 307.991526 307.9916304 307.9919408 307.9924496 307.9931444 307.994008 307.9950191 307.9961529 307.9973814 307.9986744 308 308.0013256 308.0026186 308.0038471 308.0049809 308.005992 308.0068556 308.0075504 308.0080592 308.0083696 308.008474 308.0090451 308.0089337 308.0086024 308.0080592 308.0073176 308.0063958 308.0053166 308.0041064 308.0027951 308.001415 308 307.998585 307.9972049 307.9958936 307.9946834 307.9936042 307.9926824 307.9919408 307.9913976 307.9910663 307.9909549 307.9910663 307.9913976 307.9919408 307.9926824 307.9936042 307.9946834 307.9958936 307.9972049 307.998585 308 308.001415 308.0027951 308.0041064 308.0053166 308.0063958 308.0073176 308.0080592 308.0086024 308.0089337 308.0090451 308.0093935 308.0092778 308.0089337 308.0083696 308.0075995 308.0066422 308.0055213 308.0042645 308.0029027 308.0014695 308 307.9985305 307.9970973 307.9957355 307.9944787 307.9933578 307.9924005 307.9916304 307.9910663 307.9907222 307.9906065 307.9907222 307.9910663 307.9916304 307.9924005 307.9933578 307.9944787 307.9957355 307.9970973 307.9985305 308 308.0014695 308.0029027 308.0042645 308.0055213 308.0066422 308.0075995 308.0083696 308.0089337 308.0092778 308.0093935 308.0095106 308.0093935 308.0090451 308.008474 308.0076942 308.006725 308.0055902 308.0043177 308.0029389 308.0014878 308 307.9985122 307.9970611 307.9956823 307.9944098 307.993275 307.9923058 307.991526 307.9909549 307.9906065 307.9904894 307.9906065 307.9909549 307.991526 307.9923058 307.993275 307.9944098 307.9956823 307.9970611 307.9985122 308 308.0014878 308.0029389 308.0043177 308.0055902 308.006725 308.0076942 308.008474 308.0090451 308.0093935 308.0095106 307.0089101 307.0088004 307.008474 307.0079389 307.0072084 307.0063004 307.0052372 307.0040451 307.0027534 307.0013938 307 306.9986062 306.9972466 306.9959549 306.9947628 306.9936996 306.9927916 306.9920611 306.991526 306.9911996 306.9910899 306.9911996 306.991526 306.9920611 306.9927916 306.9936996 306.9947628 306.9959549 306.9972466 306.9986062 307 307.0013938 307.0027534 307.0040451 307.0052372 307.0063004 307.0072084 307.0079389 307.008474 307.0088004 307.0089101 307.0088004 307.008692 307.0083696 307.0078412 307.0071196 307.0062228 307.0051727 307.0039953 307.0027195 307.0013767 307 306.9986233 306.9972805 306.9960047 306.9948273 306.9937772 306.9928804 306.9921588 306.9916304 306.991308 306.9911996 306.991308 306.9916304 306.9921588 306.9928804 306.9937772 306.9948273 306.9960047 306.9972805 306.9986233 307 307.0013767 307.0027195 307.0039953 307.0051727 307.0062228 307.0071196 307.0078412 307.0083696 307.008692 307.0088004 307.008474 307.0083696 307.0080592 307.0075504 307.0068556 307.005992 307.0049809 307.0038471 307.0026186 307.0013256 307 306.9986744 306.9973814 306.9961529 306.9950191 306.994008 306.9931444 306.9924496 306.9919408 306.9916304 306.991526 306.9916304 306.9919408 306.9924496 306.9931444 306.994008 306.9950191 306.9961529 306.9973814 306.9986744 307 307.0013256 307.0026186 307.0038471 307.0049809 307.005992 307.0068556 307.0075504 307.0080592 307.0083696 307.008474 307.0079389 307.0078412 307.0075504 307.0070736 307.0064227 307.0056137 307.0046664 307.0036042 307.0024533 307.0012419 307 306.9987581 306.9975467 306.9963958 306.9953336 306.9943863 306.9935773 306.9929264 306.9924496 306.9921588 306.9920611 306.9921588 306.9924496 306.9929264 306.9935773 306.9943863 306.9953336 306.9963958 306.9975467 306.9987581 307 307.0012419 307.0024533 307.0036042 307.0046664 307.0056137 307.0064227 307.0070736 307.0075504 307.0078412 307.0079389 307.0072084 307.0071196 307.0068556 307.0064227 307.0058317 307.0050971 307.004237 307.0032725 307.0022275 307.0011276 307 306.9988724 306.9977725 306.9967275 306.995763 306.9949029 306.9941683 306.9935773 306.9931444 306.9928804 306.9927916 306.9928804 306.9931444 306.9935773 306.9941683 306.9949029 306.995763 306.9967275 306.9977725 306.9988724 307 307.0011276 307.0022275 307.0032725 307.004237 307.0050971 307.0058317 307.0064227 307.0068556 307.0071196 307.0072084 307.0063004 307.0062228 307.005992 307.0056137 307.0050971 307.004455 307.0037033 307.0028603 307.0019469 307.0009856 307 306.9990144 306.9980531 306.9971397 306.9962967 306.995545 306.9949029 306.9943863 306.994008 306.9937772 306.9936996 306.9937772 306.994008 306.9943863 306.9949029 306.995545 306.9962967 306.9971397 306.9980531 306.9990144 307 307.0009856 307.0019469 307.0028603 307.0037033 307.004455 307.0050971 307.0056137 307.005992 307.0062228 307.0063004 307.0052372 307.0051727 307.0049809 307.0046664 307.004237 307.0037033 307.0030784 307.0023776 307.0016184 307.0008193 307 306.9991807 306.9983816 306.9976224 306.9969216 306.9962967 306.995763 306.9953336 306.9950191 306.9948273 306.9947628 306.9948273 306.9950191 306.9953336 306.995763 306.9962967 306.9969216 306.9976224 306.9983816 306.9991807 307 307.0008193 307.0016184 307.0023776 307.0030784 307.0037033 307.004237 307.0046664 307.0049809 307.0051727 307.0052372 307.0040451 307.0039953 307.0038471 307.0036042 307.0032725 307.0028603 307.0023776 307.0018364 307.00125 307.0006328 307 306.9993672 306.99875 306.9981636 306.9976224 306.9971397 306.9967275 306.9963958 306.9961529 306.9960047 306.9959549 306.9960047 306.9961529 306.9963958 306.9967275 306.9971397 306.9976224 306.9981636 306.99875 306.9993672 307 307.0006328 307.00125 307.0018364 307.0023776 307.0028603 307.0032725 307.0036042 307.0038471 307.0039953 307.0040451 307.0027534 307.0027195 307.0026186 307.0024533 307.0022275 307.0019469 307.0016184 307.00125 307.0008508 307.0004307 307 306.9995693 306.9991492 306.99875 306.9983816 306.9980531 306.9977725 306.9975467 306.9973814 306.9972805 306.9972466 306.9972805 306.9973814 306.9975467 306.9977725 306.9980531 306.9983816 306.99875 306.9991492 306.9995693 307 307.0004307 307.0008508 307.00125 307.0016184 307.0019469 307.0022275 307.0024533 307.0026186 307.0027195 307.0027534 307.0013938 307.0013767 307.0013256 307.0012419 307.0011276 307.0009856 307.0008193 307.0006328 307.0004307 307.000218 307 306.999782 306.9995693 306.9993672 306.9991807 306.9990144 306.9988724 306.9987581 306.9986744 306.9986233 306.9986062 306.9986233 306.9986744 306.9987581 306.9988724 306.9990144 306.9991807 306.9993672 306.9995693 306.999782 307 307.000218 307.0004307 307.0006328 307.0008193 307.0009856 307.0011276 307.0012419 307.0013256 307.0013767 307.0013938 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 306.9986062 306.9986233 306.9986744 306.9987581 306.9988724 306.9990144 306.9991807 306.9993672 306.9995693 306.999782 307 307.000218 307.0004307 307.0006328 307.0008193 307.0009856 307.0011276 307.0012419 307.0013256 307.0013767 307.0013938 307.0013767 307.0013256 307.0012419 307.0011276 307.0009856 307.0008193 307.0006328 307.0004307 307.000218 307 306.999782 306.9995693 306.9993672 306.9991807 306.9990144 306.9988724 306.9987581 306.9986744 306.9986233 306.9986062 306.9972466 306.9972805 306.9973814 306.9975467 306.9977725 306.9980531 306.9983816 306.99875 306.9991492 306.9995693 307 307.0004307 307.0008508 307.00125 307.0016184 307.0019469 307.0022275 307.0024533 307.0026186 307.0027195 307.0027534 307.0027195 307.0026186 307.0024533 307.0022275 307.0019469 307.0016184 307.00125 307.0008508 307.0004307 307 306.9995693 306.9991492 306.99875 306.9983816 306.9980531 306.9977725 306.9975467 306.9973814 306.9972805 306.9972466 306.9959549 306.9960047 306.9961529 306.9963958 306.9967275 306.9971397 306.9976224 306.9981636 306.99875 306.9993672 307 307.0006328 307.00125 307.0018364 307.0023776 307.0028603 307.0032725 307.0036042 307.0038471 307.0039953 307.0040451 307.0039953 307.0038471 307.0036042 307.0032725 307.0028603 307.0023776 307.0018364 307.00125 307.0006328 307 306.9993672 306.99875 306.9981636 306.9976224 306.9971397 306.9967275 306.9963958 306.9961529 306.9960047 306.9959549 306.9947628 306.9948273 306.9950191 306.9953336 306.995763 306.9962967 306.9969216 306.9976224 306.9983816 306.9991807 307 307.0008193 307.0016184 307.0023776 307.0030784 307.0037033 307.004237 307.0046664 307.0049809 307.0051727 307.0052372 307.0051727 307.0049809 307.0046664 307.004237 307.0037033 307.0030784 307.0023776 307.0016184 307.0008193 307 306.9991807 306.9983816 306.9976224 306.9969216 306.9962967 306.995763 306.9953336 306.9950191 306.9948273 306.9947628 306.9936996 306.9937772 306.994008 306.9943863 306.9949029 306.995545 306.9962967 306.9971397 306.9980531 306.9990144 307 307.0009856 307.0019469 307.0028603 307.0037033 307.004455 307.0050971 307.0056137 307.005992 307.0062228 307.0063004 307.0062228 307.005992 307.0056137 307.0050971 307.004455 307.0037033 307.0028603 307.0019469 307.0009856 307 306.9990144 306.9980531 306.9971397 306.9962967 306.995545 306.9949029 306.9943863 306.994008 306.9937772 306.9936996 306.9927916 306.9928804 306.9931444 306.9935773 306.9941683 306.9949029 306.995763 306.9967275 306.9977725 306.9988724 307 307.0011276 307.0022275 307.0032725 307.004237 307.0050971 307.0058317 307.0064227 307.0068556 307.0071196 307.0072084 307.0071196 307.0068556 307.0064227 307.0058317 307.0050971 307.004237 307.0032725 307.0022275 307.0011276 307 306.9988724 306.9977725 306.9967275 306.995763 306.9949029 306.9941683 306.9935773 306.9931444 306.9928804 306.9927916 306.9920611 306.9921588 306.9924496 306.9929264 306.9935773 306.9943863 306.9953336 306.9963958 306.9975467 306.9987581 307 307.0012419 307.0024533 307.0036042 307.0046664 307.0056137 307.0064227 307.0070736 307.0075504 307.0078412 307.0079389 307.0078412 307.0075504 307.0070736 307.0064227 307.0056137 307.0046664 307.0036042 307.0024533 307.0012419 307 306.9987581 306.9975467 306.9963958 306.9953336 306.9943863 306.9935773 306.9929264 306.9924496 306.9921588 306.9920611 306.991526 306.9916304 306.9919408 306.9924496 306.9931444 306.994008 306.9950191 306.9961529 306.9973814 306.9986744 307 307.0013256 307.0026186 307.0038471 307.0049809 307.005992 307.0068556 307.0075504 307.0080592 307.0083696 307.008474 307.0083696 307.0080592 307.0075504 307.0068556 307.005992 307.0049809 307.0038471 307.0026186 307.0013256 307 306.9986744 306.9973814 306.9961529 306.9950191 306.994008 306.9931444 306.9924496 306.9919408 306.9916304 306.991526 306.9911996 306.991308 306.9916304 306.9921588 306.9928804 306.9937772 306.9948273 306.9960047 306.9972805 306.9986233 307 307.0013767 307.0027195 307.0039953 307.0051727 307.0062228 307.0071196 307.0078412 307.0083696 307.008692 307.0088004 307.008692 307.0083696 307.0078412 307.0071196 307.0062228 307.0051727 307.0039953 307.0027195 307.0013767 307 306.9986233 306.9972805 306.9960047 306.9948273 306.9937772 306.9928804 306.9921588 306.9916304 306.991308 306.9911996 306.9910899 306.9911996 306.991526 306.9920611 306.9927916 306.9936996 306.9947628 306.9959549 306.9972466 306.9986062 307 307.0013938 307.0027534 307.0040451 307.0052372 307.0063004 307.0072084 307.0079389 307.008474 307.0088004 307.0089101 307.0088004 307.008474 307.0079389 307.0072084 307.0063004 307.0052372 307.0040451 307.0027534 307.0013938 307 306.9986062 306.9972466 306.9959549 306.9947628 306.9936996 306.9927916 306.9920611 306.991526 306.9911996 306.9910899 306.9911996 306.991308 306.9916304 306.9921588 306.9928804 306.9937772 306.9948273 306.9960047 306.9972805 306.9986233 307 307.0013767 307.0027195 307.0039953 307.0051727 307.0062228 307.0071196 307.0078412 307.0083696 307.008692 307.0088004 307.008692 307.0083696 307.0078412 307.0071196 307.0062228 307.0051727 307.0039953 307.0027195 307.0013767 307 306.9986233 306.9972805 306.9960047 306.9948273 306.9937772 306.9928804 306.9921588 306.9916304 306.991308 306.9911996 306.991526 306.9916304 306.9919408 306.9924496 306.9931444 306.994008 306.9950191 306.9961529 306.9973814 306.9986744 307 307.0013256 307.0026186 307.0038471 307.0049809 307.005992 307.0068556 307.0075504 307.0080592 307.0083696 307.008474 307.0083696 307.0080592 307.0075504 307.0068556 307.005992 307.0049809 307.0038471 307.0026186 307.0013256 307 306.9986744 306.9973814 306.9961529 306.9950191 306.994008 306.9931444 306.9924496 306.9919408 306.9916304 306.991526 306.9920611 306.9921588 306.9924496 306.9929264 306.9935773 306.9943863 306.9953336 306.9963958 306.9975467 306.9987581 307 307.0012419 307.0024533 307.0036042 307.0046664 307.0056137 307.0064227 307.0070736 307.0075504 307.0078412 307.0079389 307.0078412 307.0075504 307.0070736 307.0064227 307.0056137 307.0046664 307.0036042 307.0024533 307.0012419 307 306.9987581 306.9975467 306.9963958 306.9953336 306.9943863 306.9935773 306.9929264 306.9924496 306.9921588 306.9920611 306.9927916 306.9928804 306.9931444 306.9935773 306.9941683 306.9949029 306.995763 306.9967275 306.9977725 306.9988724 307 307.0011276 307.0022275 307.0032725 307.004237 307.0050971 307.0058317 307.0064227 307.0068556 307.0071196 307.0072084 307.0071196 307.0068556 307.0064227 307.0058317 307.0050971 307.004237 307.0032725 307.0022275 307.0011276 307 306.9988724 306.9977725 306.9967275 306.995763 306.9949029 306.9941683 306.9935773 306.9931444 306.9928804 306.9927916 306.9936996 306.9937772 306.994008 306.9943863 306.9949029 306.995545 306.9962967 306.9971397 306.9980531 306.9990144 307 307.0009856 307.0019469 307.0028603 307.0037033 307.004455 307.0050971 307.0056137 307.005992 307.0062228 307.0063004 307.0062228 307.005992 307.0056137 307.0050971 307.004455 307.0037033 307.0028603 307.0019469 307.0009856 307 306.9990144 306.9980531 306.9971397 306.9962967 306.995545 306.9949029 306.9943863 306.994008 306.9937772 306.9936996 306.9947628 306.9948273 306.9950191 306.9953336 306.995763 306.9962967 306.9969216 306.9976224 306.9983816 306.9991807 307 307.0008193 307.0016184 307.0023776 307.0030784 307.0037033 307.004237 307.0046664 307.0049809 307.0051727 307.0052372 307.0051727 307.0049809 307.0046664 307.004237 307.0037033 307.0030784 307.0023776 307.0016184 307.0008193 307 306.9991807 306.9983816 306.9976224 306.9969216 306.9962967 306.995763 306.9953336 306.9950191 306.9948273 306.9947628 306.9959549 306.9960047 306.9961529 306.9963958 306.9967275 306.9971397 306.9976224 306.9981636 306.99875 306.9993672 307 307.0006328 307.00125 307.0018364 307.0023776 307.0028603 307.0032725 307.0036042 307.0038471 307.0039953 307.0040451 307.0039953 307.0038471 307.0036042 307.0032725 307.0028603 307.0023776 307.0018364 307.00125 307.0006328 307 306.9993672 306.99875 306.9981636 306.9976224 306.9971397 306.9967275 306.9963958 306.9961529 306.9960047 306.9959549 306.9972466 306.9972805 306.9973814 306.9975467 306.9977725 306.9980531 306.9983816 306.99875 306.9991492 306.9995693 307 307.0004307 307.0008508 307.00125 307.0016184 307.0019469 307.0022275 307.0024533 307.0026186 307.0027195 307.0027534 307.0027195 307.0026186 307.0024533 307.0022275 307.0019469 307.0016184 307.00125 307.0008508 307.0004307 307 306.9995693 306.9991492 306.99875 306.9983816 306.9980531 306.9977725 306.9975467 306.9973814 306.9972805 306.9972466 306.9986062 306.9986233 306.9986744 306.9987581 306.9988724 306.9990144 306.9991807 306.9993672 306.9995693 306.999782 307 307.000218 307.0004307 307.0006328 307.0008193 307.0009856 307.0011276 307.0012419 307.0013256 307.0013767 307.0013938 307.0013767 307.0013256 307.0012419 307.0011276 307.0009856 307.0008193 307.0006328 307.0004307 307.000218 307 306.999782 306.9995693 306.9993672 306.9991807 306.9990144 306.9988724 306.9987581 306.9986744 306.9986233 306.9986062 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307 307.0013938 307.0013767 307.0013256 307.0012419 307.0011276 307.0009856 307.0008193 307.0006328 307.0004307 307.000218 307 306.999782 306.9995693 306.9993672 306.9991807 306.9990144 306.9988724 306.9987581 306.9986744 306.9986233 306.9986062 306.9986233 306.9986744 306.9987581 306.9988724 306.9990144 306.9991807 306.9993672 306.9995693 306.999782 307 307.000218 307.0004307 307.0006328 307.0008193 307.0009856 307.0011276 307.0012419 307.0013256 307.0013767 307.0013938 307.0027534 307.0027195 307.0026186 307.0024533 307.0022275 307.0019469 307.0016184 307.00125 307.0008508 307.0004307 307 306.9995693 306.9991492 306.99875 306.9983816 306.9980531 306.9977725 306.9975467 306.9973814 306.9972805 306.9972466 306.9972805 306.9973814 306.9975467 306.9977725 306.9980531 306.9983816 306.99875 306.9991492 306.9995693 307 307.0004307 307.0008508 307.00125 307.0016184 307.0019469 307.0022275 307.0024533 307.0026186 307.0027195 307.0027534 307.0040451 307.0039953 307.0038471 307.0036042 307.0032725 307.0028603 307.0023776 307.0018364 307.00125 307.0006328 307 306.9993672 306.99875 306.9981636 306.9976224 306.9971397 306.9967275 306.9963958 306.9961529 306.9960047 306.9959549 306.9960047 306.9961529 306.9963958 306.9967275 306.9971397 306.9976224 306.9981636 306.99875 306.9993672 307 307.0006328 307.00125 307.0018364 307.0023776 307.0028603 307.0032725 307.0036042 307.0038471 307.0039953 307.0040451 307.0052372 307.0051727 307.0049809 307.0046664 307.004237 307.0037033 307.0030784 307.0023776 307.0016184 307.0008193 307 306.9991807 306.9983816 306.9976224 306.9969216 306.9962967 306.995763 306.9953336 306.9950191 306.9948273 306.9947628 306.9948273 306.9950191 306.9953336 306.995763 306.9962967 306.9969216 306.9976224 306.9983816 306.9991807 307 307.0008193 307.0016184 307.0023776 307.0030784 307.0037033 307.004237 307.0046664 307.0049809 307.0051727 307.0052372 307.0063004 307.0062228 307.005992 307.0056137 307.0050971 307.004455 307.0037033 307.0028603 307.0019469 307.0009856 307 306.9990144 306.9980531 306.9971397 306.9962967 306.995545 306.9949029 306.9943863 306.994008 306.9937772 306.9936996 306.9937772 306.994008 306.9943863 306.9949029 306.995545 306.9962967 306.9971397 306.9980531 306.9990144 307 307.0009856 307.0019469 307.0028603 307.0037033 307.004455 307.0050971 307.0056137 307.005992 307.0062228 307.0063004 307.0072084 307.0071196 307.0068556 307.0064227 307.0058317 307.0050971 307.004237 307.0032725 307.0022275 307.0011276 307 306.9988724 306.9977725 306.9967275 306.995763 306.9949029 306.9941683 306.9935773 306.9931444 306.9928804 306.9927916 306.9928804 306.9931444 306.9935773 306.9941683 306.9949029 306.995763 306.9967275 306.9977725 306.9988724 307 307.0011276 307.0022275 307.0032725 307.004237 307.0050971 307.0058317 307.0064227 307.0068556 307.0071196 307.0072084 307.0079389 307.0078412 307.0075504 307.0070736 307.0064227 307.0056137 307.0046664 307.0036042 307.0024533 307.0012419 307 306.9987581 306.9975467 306.9963958 306.9953336 306.9943863 306.9935773 306.9929264 306.9924496 306.9921588 306.9920611 306.9921588 306.9924496 306.9929264 306.9935773 306.9943863 306.9953336 306.9963958 306.9975467 306.9987581 307 307.0012419 307.0024533 307.0036042 307.0046664 307.0056137 307.0064227 307.0070736 307.0075504 307.0078412 307.0079389 307.008474 307.0083696 307.0080592 307.0075504 307.0068556 307.005992 307.0049809 307.0038471 307.0026186 307.0013256 307 306.9986744 306.9973814 306.9961529 306.9950191 306.994008 306.9931444 306.9924496 306.9919408 306.9916304 306.991526 306.9916304 306.9919408 306.9924496 306.9931444 306.994008 306.9950191 306.9961529 306.9973814 306.9986744 307 307.0013256 307.0026186 307.0038471 307.0049809 307.005992 307.0068556 307.0075504 307.0080592 307.0083696 307.008474 307.0088004 307.008692 307.0083696 307.0078412 307.0071196 307.0062228 307.0051727 307.0039953 307.0027195 307.0013767 307 306.9986233 306.9972805 306.9960047 306.9948273 306.9937772 306.9928804 306.9921588 306.9916304 306.991308 306.9911996 306.991308 306.9916304 306.9921588 306.9928804 306.9937772 306.9948273 306.9960047 306.9972805 306.9986233 307 307.0013767 307.0027195 307.0039953 307.0051727 307.0062228 307.0071196 307.0078412 307.0083696 307.008692 307.0088004 307.0089101 307.0088004 307.008474 307.0079389 307.0072084 307.0063004 307.0052372 307.0040451 307.0027534 307.0013938 307 306.9986062 306.9972466 306.9959549 306.9947628 306.9936996 306.9927916 306.9920611 306.991526 306.9911996 306.9910899 306.9911996 306.991526 306.9920611 306.9927916 306.9936996 306.9947628 306.9959549 306.9972466 306.9986062 307 307.0013938 307.0027534 307.0040451 307.0052372 307.0063004 307.0072084 307.0079389 307.008474 307.0088004 307.0089101 306.0080902 306.0079906 306.0076942 306.0072084 306.0065451 306.0057206 306.0047553 306.0036729 306.0025 306.0012656 306 305.9987344 305.9975 305.9963271 305.9952447 305.9942794 305.9934549 305.9927916 305.9923058 305.9920094 305.9919098 305.9920094 305.9923058 305.9927916 305.9934549 305.9942794 305.9952447 305.9963271 305.9975 305.9987344 306 306.0012656 306.0025 306.0036729 306.0047553 306.0057206 306.0065451 306.0072084 306.0076942 306.0079906 306.0080902 306.0079906 306.0078922 306.0075995 306.0071196 306.0064645 306.0056502 306.0046967 306.0036276 306.0024692 306.00125 306 305.99875 305.9975308 305.9963724 305.9953033 305.9943498 305.9935355 305.9928804 305.9924005 305.9921078 305.9920094 305.9921078 305.9924005 305.9928804 305.9935355 305.9943498 305.9953033 305.9963724 305.9975308 305.99875 306 306.00125 306.0024692 306.0036276 306.0046967 306.0056502 306.0064645 306.0071196 306.0075995 306.0078922 306.0079906 306.0076942 306.0075995 306.0073176 306.0068556 306.0062247 306.0054406 306.0045225 306.0034931 306.0023776 306.0012036 306 305.9987964 305.9976224 305.9965069 305.9954775 305.9945594 305.9937753 305.9931444 305.9926824 305.9924005 305.9923058 305.9924005 305.9926824 305.9931444 305.9937753 305.9945594 305.9954775 305.9965069 305.9976224 305.9987964 306 306.0012036 306.0023776 306.0034931 306.0045225 306.0054406 306.0062247 306.0068556 306.0073176 306.0075995 306.0076942 306.0072084 306.0071196 306.0068556 306.0064227 306.0058317 306.0050971 306.004237 306.0032725 306.0022275 306.0011276 306 305.9988724 305.9977725 305.9967275 305.995763 305.9949029 305.9941683 305.9935773 305.9931444 305.9928804 305.9927916 305.9928804 305.9931444 305.9935773 305.9941683 305.9949029 305.995763 305.9967275 305.9977725 305.9988724 306 306.0011276 306.0022275 306.0032725 306.004237 306.0050971 306.0058317 306.0064227 306.0068556 306.0071196 306.0072084 306.0065451 306.0064645 306.0062247 306.0058317 306.0052951 306.0046281 306.0038471 306.0029714 306.0020225 306.0010239 306 305.9989761 305.9979775 305.9970286 305.9961529 305.9953719 305.9947049 305.9941683 305.9937753 305.9935355 305.9934549 305.9935355 305.9937753 305.9941683 305.9947049 305.9953719 305.9961529 305.9970286 305.9979775 305.9989761 306 306.0010239 306.0020225 306.0029714 306.0038471 306.0046281 306.0052951 306.0058317 306.0062247 306.0064645 306.0065451 306.0057206 306.0056502 306.0054406 306.0050971 306.0046281 306.0040451 306.0033625 306.0025971 306.0017678 306.0008949 306 305.9991051 305.9982322 305.9974029 305.9966375 305.9959549 305.9953719 305.9949029 305.9945594 305.9943498 305.9942794 305.9943498 305.9945594 305.9949029 305.9953719 305.9959549 305.9966375 305.9974029 305.9982322 305.9991051 306 306.0008949 306.0017678 306.0025971 306.0033625 306.0040451 306.0046281 306.0050971 306.0054406 306.0056502 306.0057206 306.0047553 306.0046967 306.0045225 306.004237 306.0038471 306.0033625 306.0027951 306.0021589 306.0014695 306.0007439 306 305.9992561 305.9985305 305.9978411 305.9972049 305.9966375 305.9961529 305.995763 305.9954775 305.9953033 305.9952447 305.9953033 305.9954775 305.995763 305.9961529 305.9966375 305.9972049 305.9978411 305.9985305 305.9992561 306 306.0007439 306.0014695 306.0021589 306.0027951 306.0033625 306.0038471 306.004237 306.0045225 306.0046967 306.0047553 306.0036729 306.0036276 306.0034931 306.0032725 306.0029714 306.0025971 306.0021589 306.0016674 306.001135 306.0005746 306 305.9994254 305.998865 305.9983326 305.9978411 305.9974029 305.9970286 305.9967275 305.9965069 305.9963724 305.9963271 305.9963724 305.9965069 305.9967275 305.9970286 305.9974029 305.9978411 305.9983326 305.998865 305.9994254 306 306.0005746 306.001135 306.0016674 306.0021589 306.0025971 306.0029714 306.0032725 306.0034931 306.0036276 306.0036729 306.0025 306.0024692 306.0023776 306.0022275 306.0020225 306.0017678 306.0014695 306.001135 306.0007725 306.0003911 306 305.9996089 305.9992275 305.998865 305.9985305 305.9982322 305.9979775 305.9977725 305.9976224 305.9975308 305.9975 305.9975308 305.9976224 305.9977725 305.9979775 305.9982322 305.9985305 305.998865 305.9992275 305.9996089 306 306.0003911 306.0007725 306.001135 306.0014695 306.0017678 306.0020225 306.0022275 306.0023776 306.0024692 306.0025 306.0012656 306.00125 306.0012036 306.0011276 306.0010239 306.0008949 306.0007439 306.0005746 306.0003911 306.000198 306 305.999802 305.9996089 305.9994254 305.9992561 305.9991051 305.9989761 305.9988724 305.9987964 305.99875 305.9987344 305.99875 305.9987964 305.9988724 305.9989761 305.9991051 305.9992561 305.9994254 305.9996089 305.999802 306 306.000198 306.0003911 306.0005746 306.0007439 306.0008949 306.0010239 306.0011276 306.0012036 306.00125 306.0012656 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 305.9987344 305.99875 305.9987964 305.9988724 305.9989761 305.9991051 305.9992561 305.9994254 305.9996089 305.999802 306 306.000198 306.0003911 306.0005746 306.0007439 306.0008949 306.0010239 306.0011276 306.0012036 306.00125 306.0012656 306.00125 306.0012036 306.0011276 306.0010239 306.0008949 306.0007439 306.0005746 306.0003911 306.000198 306 305.999802 305.9996089 305.9994254 305.9992561 305.9991051 305.9989761 305.9988724 305.9987964 305.99875 305.9987344 305.9975 305.9975308 305.9976224 305.9977725 305.9979775 305.9982322 305.9985305 305.998865 305.9992275 305.9996089 306 306.0003911 306.0007725 306.001135 306.0014695 306.0017678 306.0020225 306.0022275 306.0023776 306.0024692 306.0025 306.0024692 306.0023776 306.0022275 306.0020225 306.0017678 306.0014695 306.001135 306.0007725 306.0003911 306 305.9996089 305.9992275 305.998865 305.9985305 305.9982322 305.9979775 305.9977725 305.9976224 305.9975308 305.9975 305.9963271 305.9963724 305.9965069 305.9967275 305.9970286 305.9974029 305.9978411 305.9983326 305.998865 305.9994254 306 306.0005746 306.001135 306.0016674 306.0021589 306.0025971 306.0029714 306.0032725 306.0034931 306.0036276 306.0036729 306.0036276 306.0034931 306.0032725 306.0029714 306.0025971 306.0021589 306.0016674 306.001135 306.0005746 306 305.9994254 305.998865 305.9983326 305.9978411 305.9974029 305.9970286 305.9967275 305.9965069 305.9963724 305.9963271 305.9952447 305.9953033 305.9954775 305.995763 305.9961529 305.9966375 305.9972049 305.9978411 305.9985305 305.9992561 306 306.0007439 306.0014695 306.0021589 306.0027951 306.0033625 306.0038471 306.004237 306.0045225 306.0046967 306.0047553 306.0046967 306.0045225 306.004237 306.0038471 306.0033625 306.0027951 306.0021589 306.0014695 306.0007439 306 305.9992561 305.9985305 305.9978411 305.9972049 305.9966375 305.9961529 305.995763 305.9954775 305.9953033 305.9952447 305.9942794 305.9943498 305.9945594 305.9949029 305.9953719 305.9959549 305.9966375 305.9974029 305.9982322 305.9991051 306 306.0008949 306.0017678 306.0025971 306.0033625 306.0040451 306.0046281 306.0050971 306.0054406 306.0056502 306.0057206 306.0056502 306.0054406 306.0050971 306.0046281 306.0040451 306.0033625 306.0025971 306.0017678 306.0008949 306 305.9991051 305.9982322 305.9974029 305.9966375 305.9959549 305.9953719 305.9949029 305.9945594 305.9943498 305.9942794 305.9934549 305.9935355 305.9937753 305.9941683 305.9947049 305.9953719 305.9961529 305.9970286 305.9979775 305.9989761 306 306.0010239 306.0020225 306.0029714 306.0038471 306.0046281 306.0052951 306.0058317 306.0062247 306.0064645 306.0065451 306.0064645 306.0062247 306.0058317 306.0052951 306.0046281 306.0038471 306.0029714 306.0020225 306.0010239 306 305.9989761 305.9979775 305.9970286 305.9961529 305.9953719 305.9947049 305.9941683 305.9937753 305.9935355 305.9934549 305.9927916 305.9928804 305.9931444 305.9935773 305.9941683 305.9949029 305.995763 305.9967275 305.9977725 305.9988724 306 306.0011276 306.0022275 306.0032725 306.004237 306.0050971 306.0058317 306.0064227 306.0068556 306.0071196 306.0072084 306.0071196 306.0068556 306.0064227 306.0058317 306.0050971 306.004237 306.0032725 306.0022275 306.0011276 306 305.9988724 305.9977725 305.9967275 305.995763 305.9949029 305.9941683 305.9935773 305.9931444 305.9928804 305.9927916 305.9923058 305.9924005 305.9926824 305.9931444 305.9937753 305.9945594 305.9954775 305.9965069 305.9976224 305.9987964 306 306.0012036 306.0023776 306.0034931 306.0045225 306.0054406 306.0062247 306.0068556 306.0073176 306.0075995 306.0076942 306.0075995 306.0073176 306.0068556 306.0062247 306.0054406 306.0045225 306.0034931 306.0023776 306.0012036 306 305.9987964 305.9976224 305.9965069 305.9954775 305.9945594 305.9937753 305.9931444 305.9926824 305.9924005 305.9923058 305.9920094 305.9921078 305.9924005 305.9928804 305.9935355 305.9943498 305.9953033 305.9963724 305.9975308 305.99875 306 306.00125 306.0024692 306.0036276 306.0046967 306.0056502 306.0064645 306.0071196 306.0075995 306.0078922 306.0079906 306.0078922 306.0075995 306.0071196 306.0064645 306.0056502 306.0046967 306.0036276 306.0024692 306.00125 306 305.99875 305.9975308 305.9963724 305.9953033 305.9943498 305.9935355 305.9928804 305.9924005 305.9921078 305.9920094 305.9919098 305.9920094 305.9923058 305.9927916 305.9934549 305.9942794 305.9952447 305.9963271 305.9975 305.9987344 306 306.0012656 306.0025 306.0036729 306.0047553 306.0057206 306.0065451 306.0072084 306.0076942 306.0079906 306.0080902 306.0079906 306.0076942 306.0072084 306.0065451 306.0057206 306.0047553 306.0036729 306.0025 306.0012656 306 305.9987344 305.9975 305.9963271 305.9952447 305.9942794 305.9934549 305.9927916 305.9923058 305.9920094 305.9919098 305.9920094 305.9921078 305.9924005 305.9928804 305.9935355 305.9943498 305.9953033 305.9963724 305.9975308 305.99875 306 306.00125 306.0024692 306.0036276 306.0046967 306.0056502 306.0064645 306.0071196 306.0075995 306.0078922 306.0079906 306.0078922 306.0075995 306.0071196 306.0064645 306.0056502 306.0046967 306.0036276 306.0024692 306.00125 306 305.99875 305.9975308 305.9963724 305.9953033 305.9943498 305.9935355 305.9928804 305.9924005 305.9921078 305.9920094 305.9923058 305.9924005 305.9926824 305.9931444 305.9937753 305.9945594 305.9954775 305.9965069 305.9976224 305.9987964 306 306.0012036 306.0023776 306.0034931 306.0045225 306.0054406 306.0062247 306.0068556 306.0073176 306.0075995 306.0076942 306.0075995 306.0073176 306.0068556 306.0062247 306.0054406 306.0045225 306.0034931 306.0023776 306.0012036 306 305.9987964 305.9976224 305.9965069 305.9954775 305.9945594 305.9937753 305.9931444 305.9926824 305.9924005 305.9923058 305.9927916 305.9928804 305.9931444 305.9935773 305.9941683 305.9949029 305.995763 305.9967275 305.9977725 305.9988724 306 306.0011276 306.0022275 306.0032725 306.004237 306.0050971 306.0058317 306.0064227 306.0068556 306.0071196 306.0072084 306.0071196 306.0068556 306.0064227 306.0058317 306.0050971 306.004237 306.0032725 306.0022275 306.0011276 306 305.9988724 305.9977725 305.9967275 305.995763 305.9949029 305.9941683 305.9935773 305.9931444 305.9928804 305.9927916 305.9934549 305.9935355 305.9937753 305.9941683 305.9947049 305.9953719 305.9961529 305.9970286 305.9979775 305.9989761 306 306.0010239 306.0020225 306.0029714 306.0038471 306.0046281 306.0052951 306.0058317 306.0062247 306.0064645 306.0065451 306.0064645 306.0062247 306.0058317 306.0052951 306.0046281 306.0038471 306.0029714 306.0020225 306.0010239 306 305.9989761 305.9979775 305.9970286 305.9961529 305.9953719 305.9947049 305.9941683 305.9937753 305.9935355 305.9934549 305.9942794 305.9943498 305.9945594 305.9949029 305.9953719 305.9959549 305.9966375 305.9974029 305.9982322 305.9991051 306 306.0008949 306.0017678 306.0025971 306.0033625 306.0040451 306.0046281 306.0050971 306.0054406 306.0056502 306.0057206 306.0056502 306.0054406 306.0050971 306.0046281 306.0040451 306.0033625 306.0025971 306.0017678 306.0008949 306 305.9991051 305.9982322 305.9974029 305.9966375 305.9959549 305.9953719 305.9949029 305.9945594 305.9943498 305.9942794 305.9952447 305.9953033 305.9954775 305.995763 305.9961529 305.9966375 305.9972049 305.9978411 305.9985305 305.9992561 306 306.0007439 306.0014695 306.0021589 306.0027951 306.0033625 306.0038471 306.004237 306.0045225 306.0046967 306.0047553 306.0046967 306.0045225 306.004237 306.0038471 306.0033625 306.0027951 306.0021589 306.0014695 306.0007439 306 305.9992561 305.9985305 305.9978411 305.9972049 305.9966375 305.9961529 305.995763 305.9954775 305.9953033 305.9952447 305.9963271 305.9963724 305.9965069 305.9967275 305.9970286 305.9974029 305.9978411 305.9983326 305.998865 305.9994254 306 306.0005746 306.001135 306.0016674 306.0021589 306.0025971 306.0029714 306.0032725 306.0034931 306.0036276 306.0036729 306.0036276 306.0034931 306.0032725 306.0029714 306.0025971 306.0021589 306.0016674 306.001135 306.0005746 306 305.9994254 305.998865 305.9983326 305.9978411 305.9974029 305.9970286 305.9967275 305.9965069 305.9963724 305.9963271 305.9975 305.9975308 305.9976224 305.9977725 305.9979775 305.9982322 305.9985305 305.998865 305.9992275 305.9996089 306 306.0003911 306.0007725 306.001135 306.0014695 306.0017678 306.0020225 306.0022275 306.0023776 306.0024692 306.0025 306.0024692 306.0023776 306.0022275 306.0020225 306.0017678 306.0014695 306.001135 306.0007725 306.0003911 306 305.9996089 305.9992275 305.998865 305.9985305 305.9982322 305.9979775 305.9977725 305.9976224 305.9975308 305.9975 305.9987344 305.99875 305.9987964 305.9988724 305.9989761 305.9991051 305.9992561 305.9994254 305.9996089 305.999802 306 306.000198 306.0003911 306.0005746 306.0007439 306.0008949 306.0010239 306.0011276 306.0012036 306.00125 306.0012656 306.00125 306.0012036 306.0011276 306.0010239 306.0008949 306.0007439 306.0005746 306.0003911 306.000198 306 305.999802 305.9996089 305.9994254 305.9992561 305.9991051 305.9989761 305.9988724 305.9987964 305.99875 305.9987344 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306 306.0012656 306.00125 306.0012036 306.0011276 306.0010239 306.0008949 306.0007439 306.0005746 306.0003911 306.000198 306 305.999802 305.9996089 305.9994254 305.9992561 305.9991051 305.9989761 305.9988724 305.9987964 305.99875 305.9987344 305.99875 305.9987964 305.9988724 305.9989761 305.9991051 305.9992561 305.9994254 305.9996089 305.999802 306 306.000198 306.0003911 306.0005746 306.0007439 306.0008949 306.0010239 306.0011276 306.0012036 306.00125 306.0012656 306.0025 306.0024692 306.0023776 306.0022275 306.0020225 306.0017678 306.0014695 306.001135 306.0007725 306.0003911 306 305.9996089 305.9992275 305.998865 305.9985305 305.9982322 305.9979775 305.9977725 305.9976224 305.9975308 305.9975 305.9975308 305.9976224 305.9977725 305.9979775 305.9982322 305.9985305 305.998865 305.9992275 305.9996089 306 306.0003911 306.0007725 306.001135 306.0014695 306.0017678 306.0020225 306.0022275 306.0023776 306.0024692 306.0025 306.0036729 306.0036276 306.0034931 306.0032725 306.0029714 306.0025971 306.0021589 306.0016674 306.001135 306.0005746 306 305.9994254 305.998865 305.9983326 305.9978411 305.9974029 305.9970286 305.9967275 305.9965069 305.9963724 305.9963271 305.9963724 305.9965069 305.9967275 305.9970286 305.9974029 305.9978411 305.9983326 305.998865 305.9994254 306 306.0005746 306.001135 306.0016674 306.0021589 306.0025971 306.0029714 306.0032725 306.0034931 306.0036276 306.0036729 306.0047553 306.0046967 306.0045225 306.004237 306.0038471 306.0033625 306.0027951 306.0021589 306.0014695 306.0007439 306 305.9992561 305.9985305 305.9978411 305.9972049 305.9966375 305.9961529 305.995763 305.9954775 305.9953033 305.9952447 305.9953033 305.9954775 305.995763 305.9961529 305.9966375 305.9972049 305.9978411 305.9985305 305.9992561 306 306.0007439 306.0014695 306.0021589 306.0027951 306.0033625 306.0038471 306.004237 306.0045225 306.0046967 306.0047553 306.0057206 306.0056502 306.0054406 306.0050971 306.0046281 306.0040451 306.0033625 306.0025971 306.0017678 306.0008949 306 305.9991051 305.9982322 305.9974029 305.9966375 305.9959549 305.9953719 305.9949029 305.9945594 305.9943498 305.9942794 305.9943498 305.9945594 305.9949029 305.9953719 305.9959549 305.9966375 305.9974029 305.9982322 305.9991051 306 306.0008949 306.0017678 306.0025971 306.0033625 306.0040451 306.0046281 306.0050971 306.0054406 306.0056502 306.0057206 306.0065451 306.0064645 306.0062247 306.0058317 306.0052951 306.0046281 306.0038471 306.0029714 306.0020225 306.0010239 306 305.9989761 305.9979775 305.9970286 305.9961529 305.9953719 305.9947049 305.9941683 305.9937753 305.9935355 305.9934549 305.9935355 305.9937753 305.9941683 305.9947049 305.9953719 305.9961529 305.9970286 305.9979775 305.9989761 306 306.0010239 306.0020225 306.0029714 306.0038471 306.0046281 306.0052951 306.0058317 306.0062247 306.0064645 306.0065451 306.0072084 306.0071196 306.0068556 306.0064227 306.0058317 306.0050971 306.004237 306.0032725 306.0022275 306.0011276 306 305.9988724 305.9977725 305.9967275 305.995763 305.9949029 305.9941683 305.9935773 305.9931444 305.9928804 305.9927916 305.9928804 305.9931444 305.9935773 305.9941683 305.9949029 305.995763 305.9967275 305.9977725 305.9988724 306 306.0011276 306.0022275 306.0032725 306.004237 306.0050971 306.0058317 306.0064227 306.0068556 306.0071196 306.0072084 306.0076942 306.0075995 306.0073176 306.0068556 306.0062247 306.0054406 306.0045225 306.0034931 306.0023776 306.0012036 306 305.9987964 305.9976224 305.9965069 305.9954775 305.9945594 305.9937753 305.9931444 305.9926824 305.9924005 305.9923058 305.9924005 305.9926824 305.9931444 305.9937753 305.9945594 305.9954775 305.9965069 305.9976224 305.9987964 306 306.0012036 306.0023776 306.0034931 306.0045225 306.0054406 306.0062247 306.0068556 306.0073176 306.0075995 306.0076942 306.0079906 306.0078922 306.0075995 306.0071196 306.0064645 306.0056502 306.0046967 306.0036276 306.0024692 306.00125 306 305.99875 305.9975308 305.9963724 305.9953033 305.9943498 305.9935355 305.9928804 305.9924005 305.9921078 305.9920094 305.9921078 305.9924005 305.9928804 305.9935355 305.9943498 305.9953033 305.9963724 305.9975308 305.99875 306 306.00125 306.0024692 306.0036276 306.0046967 306.0056502 306.0064645 306.0071196 306.0075995 306.0078922 306.0079906 306.0080902 306.0079906 306.0076942 306.0072084 306.0065451 306.0057206 306.0047553 306.0036729 306.0025 306.0012656 306 305.9987344 305.9975 305.9963271 305.9952447 305.9942794 305.9934549 305.9927916 305.9923058 305.9920094 305.9919098 305.9920094 305.9923058 305.9927916 305.9934549 305.9942794 305.9952447 305.9963271 305.9975 305.9987344 306 306.0012656 306.0025 306.0036729 306.0047553 306.0057206 306.0065451 306.0072084 306.0076942 306.0079906 306.0080902 305.0070711 305.006984 305.006725 305.0063004 305.0057206 305.005 305.0041563 305.0032102 305.0021851 305.0011062 305 304.9988938 304.9978149 304.9967898 304.9958437 304.995 304.9942794 304.9936996 304.993275 304.993016 304.9929289 304.993016 304.993275 304.9936996 304.9942794 304.995 304.9958437 304.9967898 304.9978149 304.9988938 305 305.0011062 305.0021851 305.0032102 305.0041563 305.005 305.0057206 305.0063004 305.006725 305.006984 305.0070711 305.006984 305.006898 305.0066422 305.0062228 305.0056502 305.0049384 305.0041051 305.0031707 305.0021582 305.0010925 305 304.9989075 304.9978418 304.9968293 304.9958949 304.9950616 304.9943498 304.9937772 304.9933578 304.993102 304.993016 304.993102 304.9933578 304.9937772 304.9943498 304.9950616 304.9958949 304.9968293 304.9978418 304.9989075 305 305.0010925 305.0021582 305.0031707 305.0041051 305.0049384 305.0056502 305.0062228 305.0066422 305.006898 305.006984 305.006725 305.0066422 305.0063958 305.005992 305.0054406 305.0047553 305.0039528 305.0030531 305.0020781 305.001052 305 304.998948 304.9979219 304.9969469 304.9960472 304.9952447 304.9945594 304.994008 304.9936042 304.9933578 304.993275 304.9933578 304.9936042 304.994008 304.9945594 304.9952447 304.9960472 304.9969469 304.9979219 304.998948 305 305.001052 305.0020781 305.0030531 305.0039528 305.0047553 305.0054406 305.005992 305.0063958 305.0066422 305.006725 305.0063004 305.0062228 305.005992 305.0056137 305.0050971 305.004455 305.0037033 305.0028603 305.0019469 305.0009856 305 304.9990144 304.9980531 304.9971397 304.9962967 304.995545 304.9949029 304.9943863 304.994008 304.9937772 304.9936996 304.9937772 304.994008 304.9943863 304.9949029 304.995545 304.9962967 304.9971397 304.9980531 304.9990144 305 305.0009856 305.0019469 305.0028603 305.0037033 305.004455 305.0050971 305.0056137 305.005992 305.0062228 305.0063004 305.0057206 305.0056502 305.0054406 305.0050971 305.0046281 305.0040451 305.0033625 305.0025971 305.0017678 305.0008949 305 304.9991051 304.9982322 304.9974029 304.9966375 304.9959549 304.9953719 304.9949029 304.9945594 304.9943498 304.9942794 304.9943498 304.9945594 304.9949029 304.9953719 304.9959549 304.9966375 304.9974029 304.9982322 304.9991051 305 305.0008949 305.0017678 305.0025971 305.0033625 305.0040451 305.0046281 305.0050971 305.0054406 305.0056502 305.0057206 305.005 305.0049384 305.0047553 305.004455 305.0040451 305.0035355 305.0029389 305.00227 305.0015451 305.0007822 305 304.9992178 304.9984549 304.99773 304.9970611 304.9964645 304.9959549 304.995545 304.9952447 304.9950616 304.995 304.9950616 304.9952447 304.995545 304.9959549 304.9964645 304.9970611 304.99773 304.9984549 304.9992178 305 305.0007822 305.0015451 305.00227 305.0029389 305.0035355 305.0040451 305.004455 305.0047553 305.0049384 305.005 305.0041563 305.0041051 305.0039528 305.0037033 305.0033625 305.0029389 305.002443 305.0018869 305.0012844 305.0006502 305 304.9993498 304.9987156 304.9981131 304.997557 304.9970611 304.9966375 304.9962967 304.9960472 304.9958949 304.9958437 304.9958949 304.9960472 304.9962967 304.9966375 304.9970611 304.997557 304.9981131 304.9987156 304.9993498 305 305.0006502 305.0012844 305.0018869 305.002443 305.0029389 305.0033625 305.0037033 305.0039528 305.0041051 305.0041563 305.0032102 305.0031707 305.0030531 305.0028603 305.0025971 305.00227 305.0018869 305.0014574 305.000992 305.0005022 305 304.9994978 304.999008 304.9985426 304.9981131 304.99773 304.9974029 304.9971397 304.9969469 304.9968293 304.9967898 304.9968293 304.9969469 304.9971397 304.9974029 304.99773 304.9981131 304.9985426 304.999008 304.9994978 305 305.0005022 305.000992 305.0014574 305.0018869 305.00227 305.0025971 305.0028603 305.0030531 305.0031707 305.0032102 305.0021851 305.0021582 305.0020781 305.0019469 305.0017678 305.0015451 305.0012844 305.000992 305.0006752 305.0003418 305 304.9996582 304.9993248 304.999008 304.9987156 304.9984549 304.9982322 304.9980531 304.9979219 304.9978418 304.9978149 304.9978418 304.9979219 304.9980531 304.9982322 304.9984549 304.9987156 304.999008 304.9993248 304.9996582 305 305.0003418 305.0006752 305.000992 305.0012844 305.0015451 305.0017678 305.0019469 305.0020781 305.0021582 305.0021851 305.0011062 305.0010925 305.001052 305.0009856 305.0008949 305.0007822 305.0006502 305.0005022 305.0003418 305.000173 305 304.999827 304.9996582 304.9994978 304.9993498 304.9992178 304.9991051 304.9990144 304.998948 304.9989075 304.9988938 304.9989075 304.998948 304.9990144 304.9991051 304.9992178 304.9993498 304.9994978 304.9996582 304.999827 305 305.000173 305.0003418 305.0005022 305.0006502 305.0007822 305.0008949 305.0009856 305.001052 305.0010925 305.0011062 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 304.9988938 304.9989075 304.998948 304.9990144 304.9991051 304.9992178 304.9993498 304.9994978 304.9996582 304.999827 305 305.000173 305.0003418 305.0005022 305.0006502 305.0007822 305.0008949 305.0009856 305.001052 305.0010925 305.0011062 305.0010925 305.001052 305.0009856 305.0008949 305.0007822 305.0006502 305.0005022 305.0003418 305.000173 305 304.999827 304.9996582 304.9994978 304.9993498 304.9992178 304.9991051 304.9990144 304.998948 304.9989075 304.9988938 304.9978149 304.9978418 304.9979219 304.9980531 304.9982322 304.9984549 304.9987156 304.999008 304.9993248 304.9996582 305 305.0003418 305.0006752 305.000992 305.0012844 305.0015451 305.0017678 305.0019469 305.0020781 305.0021582 305.0021851 305.0021582 305.0020781 305.0019469 305.0017678 305.0015451 305.0012844 305.000992 305.0006752 305.0003418 305 304.9996582 304.9993248 304.999008 304.9987156 304.9984549 304.9982322 304.9980531 304.9979219 304.9978418 304.9978149 304.9967898 304.9968293 304.9969469 304.9971397 304.9974029 304.99773 304.9981131 304.9985426 304.999008 304.9994978 305 305.0005022 305.000992 305.0014574 305.0018869 305.00227 305.0025971 305.0028603 305.0030531 305.0031707 305.0032102 305.0031707 305.0030531 305.0028603 305.0025971 305.00227 305.0018869 305.0014574 305.000992 305.0005022 305 304.9994978 304.999008 304.9985426 304.9981131 304.99773 304.9974029 304.9971397 304.9969469 304.9968293 304.9967898 304.9958437 304.9958949 304.9960472 304.9962967 304.9966375 304.9970611 304.997557 304.9981131 304.9987156 304.9993498 305 305.0006502 305.0012844 305.0018869 305.002443 305.0029389 305.0033625 305.0037033 305.0039528 305.0041051 305.0041563 305.0041051 305.0039528 305.0037033 305.0033625 305.0029389 305.002443 305.0018869 305.0012844 305.0006502 305 304.9993498 304.9987156 304.9981131 304.997557 304.9970611 304.9966375 304.9962967 304.9960472 304.9958949 304.9958437 304.995 304.9950616 304.9952447 304.995545 304.9959549 304.9964645 304.9970611 304.99773 304.9984549 304.9992178 305 305.0007822 305.0015451 305.00227 305.0029389 305.0035355 305.0040451 305.004455 305.0047553 305.0049384 305.005 305.0049384 305.0047553 305.004455 305.0040451 305.0035355 305.0029389 305.00227 305.0015451 305.0007822 305 304.9992178 304.9984549 304.99773 304.9970611 304.9964645 304.9959549 304.995545 304.9952447 304.9950616 304.995 304.9942794 304.9943498 304.9945594 304.9949029 304.9953719 304.9959549 304.9966375 304.9974029 304.9982322 304.9991051 305 305.0008949 305.0017678 305.0025971 305.0033625 305.0040451 305.0046281 305.0050971 305.0054406 305.0056502 305.0057206 305.0056502 305.0054406 305.0050971 305.0046281 305.0040451 305.0033625 305.0025971 305.0017678 305.0008949 305 304.9991051 304.9982322 304.9974029 304.9966375 304.9959549 304.9953719 304.9949029 304.9945594 304.9943498 304.9942794 304.9936996 304.9937772 304.994008 304.9943863 304.9949029 304.995545 304.9962967 304.9971397 304.9980531 304.9990144 305 305.0009856 305.0019469 305.0028603 305.0037033 305.004455 305.0050971 305.0056137 305.005992 305.0062228 305.0063004 305.0062228 305.005992 305.0056137 305.0050971 305.004455 305.0037033 305.0028603 305.0019469 305.0009856 305 304.9990144 304.9980531 304.9971397 304.9962967 304.995545 304.9949029 304.9943863 304.994008 304.9937772 304.9936996 304.993275 304.9933578 304.9936042 304.994008 304.9945594 304.9952447 304.9960472 304.9969469 304.9979219 304.998948 305 305.001052 305.0020781 305.0030531 305.0039528 305.0047553 305.0054406 305.005992 305.0063958 305.0066422 305.006725 305.0066422 305.0063958 305.005992 305.0054406 305.0047553 305.0039528 305.0030531 305.0020781 305.001052 305 304.998948 304.9979219 304.9969469 304.9960472 304.9952447 304.9945594 304.994008 304.9936042 304.9933578 304.993275 304.993016 304.993102 304.9933578 304.9937772 304.9943498 304.9950616 304.9958949 304.9968293 304.9978418 304.9989075 305 305.0010925 305.0021582 305.0031707 305.0041051 305.0049384 305.0056502 305.0062228 305.0066422 305.006898 305.006984 305.006898 305.0066422 305.0062228 305.0056502 305.0049384 305.0041051 305.0031707 305.0021582 305.0010925 305 304.9989075 304.9978418 304.9968293 304.9958949 304.9950616 304.9943498 304.9937772 304.9933578 304.993102 304.993016 304.9929289 304.993016 304.993275 304.9936996 304.9942794 304.995 304.9958437 304.9967898 304.9978149 304.9988938 305 305.0011062 305.0021851 305.0032102 305.0041563 305.005 305.0057206 305.0063004 305.006725 305.006984 305.0070711 305.006984 305.006725 305.0063004 305.0057206 305.005 305.0041563 305.0032102 305.0021851 305.0011062 305 304.9988938 304.9978149 304.9967898 304.9958437 304.995 304.9942794 304.9936996 304.993275 304.993016 304.9929289 304.993016 304.993102 304.9933578 304.9937772 304.9943498 304.9950616 304.9958949 304.9968293 304.9978418 304.9989075 305 305.0010925 305.0021582 305.0031707 305.0041051 305.0049384 305.0056502 305.0062228 305.0066422 305.006898 305.006984 305.006898 305.0066422 305.0062228 305.0056502 305.0049384 305.0041051 305.0031707 305.0021582 305.0010925 305 304.9989075 304.9978418 304.9968293 304.9958949 304.9950616 304.9943498 304.9937772 304.9933578 304.993102 304.993016 304.993275 304.9933578 304.9936042 304.994008 304.9945594 304.9952447 304.9960472 304.9969469 304.9979219 304.998948 305 305.001052 305.0020781 305.0030531 305.0039528 305.0047553 305.0054406 305.005992 305.0063958 305.0066422 305.006725 305.0066422 305.0063958 305.005992 305.0054406 305.0047553 305.0039528 305.0030531 305.0020781 305.001052 305 304.998948 304.9979219 304.9969469 304.9960472 304.9952447 304.9945594 304.994008 304.9936042 304.9933578 304.993275 304.9936996 304.9937772 304.994008 304.9943863 304.9949029 304.995545 304.9962967 304.9971397 304.9980531 304.9990144 305 305.0009856 305.0019469 305.0028603 305.0037033 305.004455 305.0050971 305.0056137 305.005992 305.0062228 305.0063004 305.0062228 305.005992 305.0056137 305.0050971 305.004455 305.0037033 305.0028603 305.0019469 305.0009856 305 304.9990144 304.9980531 304.9971397 304.9962967 304.995545 304.9949029 304.9943863 304.994008 304.9937772 304.9936996 304.9942794 304.9943498 304.9945594 304.9949029 304.9953719 304.9959549 304.9966375 304.9974029 304.9982322 304.9991051 305 305.0008949 305.0017678 305.0025971 305.0033625 305.0040451 305.0046281 305.0050971 305.0054406 305.0056502 305.0057206 305.0056502 305.0054406 305.0050971 305.0046281 305.0040451 305.0033625 305.0025971 305.0017678 305.0008949 305 304.9991051 304.9982322 304.9974029 304.9966375 304.9959549 304.9953719 304.9949029 304.9945594 304.9943498 304.9942794 304.995 304.9950616 304.9952447 304.995545 304.9959549 304.9964645 304.9970611 304.99773 304.9984549 304.9992178 305 305.0007822 305.0015451 305.00227 305.0029389 305.0035355 305.0040451 305.004455 305.0047553 305.0049384 305.005 305.0049384 305.0047553 305.004455 305.0040451 305.0035355 305.0029389 305.00227 305.0015451 305.0007822 305 304.9992178 304.9984549 304.99773 304.9970611 304.9964645 304.9959549 304.995545 304.9952447 304.9950616 304.995 304.9958437 304.9958949 304.9960472 304.9962967 304.9966375 304.9970611 304.997557 304.9981131 304.9987156 304.9993498 305 305.0006502 305.0012844 305.0018869 305.002443 305.0029389 305.0033625 305.0037033 305.0039528 305.0041051 305.0041563 305.0041051 305.0039528 305.0037033 305.0033625 305.0029389 305.002443 305.0018869 305.0012844 305.0006502 305 304.9993498 304.9987156 304.9981131 304.997557 304.9970611 304.9966375 304.9962967 304.9960472 304.9958949 304.9958437 304.9967898 304.9968293 304.9969469 304.9971397 304.9974029 304.99773 304.9981131 304.9985426 304.999008 304.9994978 305 305.0005022 305.000992 305.0014574 305.0018869 305.00227 305.0025971 305.0028603 305.0030531 305.0031707 305.0032102 305.0031707 305.0030531 305.0028603 305.0025971 305.00227 305.0018869 305.0014574 305.000992 305.0005022 305 304.9994978 304.999008 304.9985426 304.9981131 304.99773 304.9974029 304.9971397 304.9969469 304.9968293 304.9967898 304.9978149 304.9978418 304.9979219 304.9980531 304.9982322 304.9984549 304.9987156 304.999008 304.9993248 304.9996582 305 305.0003418 305.0006752 305.000992 305.0012844 305.0015451 305.0017678 305.0019469 305.0020781 305.0021582 305.0021851 305.0021582 305.0020781 305.0019469 305.0017678 305.0015451 305.0012844 305.000992 305.0006752 305.0003418 305 304.9996582 304.9993248 304.999008 304.9987156 304.9984549 304.9982322 304.9980531 304.9979219 304.9978418 304.9978149 304.9988938 304.9989075 304.998948 304.9990144 304.9991051 304.9992178 304.9993498 304.9994978 304.9996582 304.999827 305 305.000173 305.0003418 305.0005022 305.0006502 305.0007822 305.0008949 305.0009856 305.001052 305.0010925 305.0011062 305.0010925 305.001052 305.0009856 305.0008949 305.0007822 305.0006502 305.0005022 305.0003418 305.000173 305 304.999827 304.9996582 304.9994978 304.9993498 304.9992178 304.9991051 304.9990144 304.998948 304.9989075 304.9988938 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305 305.0011062 305.0010925 305.001052 305.0009856 305.0008949 305.0007822 305.0006502 305.0005022 305.0003418 305.000173 305 304.999827 304.9996582 304.9994978 304.9993498 304.9992178 304.9991051 304.9990144 304.998948 304.9989075 304.9988938 304.9989075 304.998948 304.9990144 304.9991051 304.9992178 304.9993498 304.9994978 304.9996582 304.999827 305 305.000173 305.0003418 305.0005022 305.0006502 305.0007822 305.0008949 305.0009856 305.001052 305.0010925 305.0011062 305.0021851 305.0021582 305.0020781 305.0019469 305.0017678 305.0015451 305.0012844 305.000992 305.0006752 305.0003418 305 304.9996582 304.9993248 304.999008 304.9987156 304.9984549 304.9982322 304.9980531 304.9979219 304.9978418 304.9978149 304.9978418 304.9979219 304.9980531 304.9982322 304.9984549 304.9987156 304.999008 304.9993248 304.9996582 305 305.0003418 305.0006752 305.000992 305.0012844 305.0015451 305.0017678 305.0019469 305.0020781 305.0021582 305.0021851 305.0032102 305.0031707 305.0030531 305.0028603 305.0025971 305.00227 305.0018869 305.0014574 305.000992 305.0005022 305 304.9994978 304.999008 304.9985426 304.9981131 304.99773 304.9974029 304.9971397 304.9969469 304.9968293 304.9967898 304.9968293 304.9969469 304.9971397 304.9974029 304.99773 304.9981131 304.9985426 304.999008 304.9994978 305 305.0005022 305.000992 305.0014574 305.0018869 305.00227 305.0025971 305.0028603 305.0030531 305.0031707 305.0032102 305.0041563 305.0041051 305.0039528 305.0037033 305.0033625 305.0029389 305.002443 305.0018869 305.0012844 305.0006502 305 304.9993498 304.9987156 304.9981131 304.997557 304.9970611 304.9966375 304.9962967 304.9960472 304.9958949 304.9958437 304.9958949 304.9960472 304.9962967 304.9966375 304.9970611 304.997557 304.9981131 304.9987156 304.9993498 305 305.0006502 305.0012844 305.0018869 305.002443 305.0029389 305.0033625 305.0037033 305.0039528 305.0041051 305.0041563 305.005 305.0049384 305.0047553 305.004455 305.0040451 305.0035355 305.0029389 305.00227 305.0015451 305.0007822 305 304.9992178 304.9984549 304.99773 304.9970611 304.9964645 304.9959549 304.995545 304.9952447 304.9950616 304.995 304.9950616 304.9952447 304.995545 304.9959549 304.9964645 304.9970611 304.99773 304.9984549 304.9992178 305 305.0007822 305.0015451 305.00227 305.0029389 305.0035355 305.0040451 305.004455 305.0047553 305.0049384 305.005 305.0057206 305.0056502 305.0054406 305.0050971 305.0046281 305.0040451 305.0033625 305.0025971 305.0017678 305.0008949 305 304.9991051 304.9982322 304.9974029 304.9966375 304.9959549 304.9953719 304.9949029 304.9945594 304.9943498 304.9942794 304.9943498 304.9945594 304.9949029 304.9953719 304.9959549 304.9966375 304.9974029 304.9982322 304.9991051 305 305.0008949 305.0017678 305.0025971 305.0033625 305.0040451 305.0046281 305.0050971 305.0054406 305.0056502 305.0057206 305.0063004 305.0062228 305.005992 305.0056137 305.0050971 305.004455 305.0037033 305.0028603 305.0019469 305.0009856 305 304.9990144 304.9980531 304.9971397 304.9962967 304.995545 304.9949029 304.9943863 304.994008 304.9937772 304.9936996 304.9937772 304.994008 304.9943863 304.9949029 304.995545 304.9962967 304.9971397 304.9980531 304.9990144 305 305.0009856 305.0019469 305.0028603 305.0037033 305.004455 305.0050971 305.0056137 305.005992 305.0062228 305.0063004 305.006725 305.0066422 305.0063958 305.005992 305.0054406 305.0047553 305.0039528 305.0030531 305.0020781 305.001052 305 304.998948 304.9979219 304.9969469 304.9960472 304.9952447 304.9945594 304.994008 304.9936042 304.9933578 304.993275 304.9933578 304.9936042 304.994008 304.9945594 304.9952447 304.9960472 304.9969469 304.9979219 304.998948 305 305.001052 305.0020781 305.0030531 305.0039528 305.0047553 305.0054406 305.005992 305.0063958 305.0066422 305.006725 305.006984 305.006898 305.0066422 305.0062228 305.0056502 305.0049384 305.0041051 305.0031707 305.0021582 305.0010925 305 304.9989075 304.9978418 304.9968293 304.9958949 304.9950616 304.9943498 304.9937772 304.9933578 304.993102 304.993016 304.993102 304.9933578 304.9937772 304.9943498 304.9950616 304.9958949 304.9968293 304.9978418 304.9989075 305 305.0010925 305.0021582 305.0031707 305.0041051 305.0049384 305.0056502 305.0062228 305.0066422 305.006898 305.006984 305.0070711 305.006984 305.006725 305.0063004 305.0057206 305.005 305.0041563 305.0032102 305.0021851 305.0011062 305 304.9988938 304.9978149 304.9967898 304.9958437 304.995 304.9942794 304.9936996 304.993275 304.993016 304.9929289 304.993016 304.993275 304.9936996 304.9942794 304.995 304.9958437 304.9967898 304.9978149 304.9988938 305 305.0011062 305.0021851 305.0032102 305.0041563 305.005 305.0057206 305.0063004 305.006725 305.006984 305.0070711 304.0058779 304.0058055 304.0055902 304.0052372 304.0047553 304.0041563 304.0034549 304.0026685 304.0018164 304.0009195 304 303.9990805 303.9981836 303.9973315 303.9965451 303.9958437 303.9952447 303.9947628 303.9944098 303.9941945 303.9941221 303.9941945 303.9944098 303.9947628 303.9952447 303.9958437 303.9965451 303.9973315 303.9981836 303.9990805 304 304.0009195 304.0018164 304.0026685 304.0034549 304.0041563 304.0047553 304.0052372 304.0055902 304.0058055 304.0058779 304.0058055 304.005734 304.0055213 304.0051727 304.0046967 304.0041051 304.0034124 304.0026356 304.001794 304.0009082 304 303.9990918 303.998206 303.9973644 303.9965876 303.9958949 303.9953033 303.9948273 303.9944787 303.994266 303.9941945 303.994266 303.9944787 303.9948273 303.9953033 303.9958949 303.9965876 303.9973644 303.998206 303.9990918 304 304.0009082 304.001794 304.0026356 304.0034124 304.0041051 304.0046967 304.0051727 304.0055213 304.005734 304.0058055 304.0055902 304.0055213 304.0053166 304.0049809 304.0045225 304.0039528 304.0032858 304.0025379 304.0017275 304.0008745 304 303.9991255 303.9982725 303.9974621 303.9967142 303.9960472 303.9954775 303.9950191 303.9946834 303.9944787 303.9944098 303.9944787 303.9946834 303.9950191 303.9954775 303.9960472 303.9967142 303.9974621 303.9982725 303.9991255 304 304.0008745 304.0017275 304.0025379 304.0032858 304.0039528 304.0045225 304.0049809 304.0053166 304.0055213 304.0055902 304.0052372 304.0051727 304.0049809 304.0046664 304.004237 304.0037033 304.0030784 304.0023776 304.0016184 304.0008193 304 303.9991807 303.9983816 303.9976224 303.9969216 303.9962967 303.995763 303.9953336 303.9950191 303.9948273 303.9947628 303.9948273 303.9950191 303.9953336 303.995763 303.9962967 303.9969216 303.9976224 303.9983816 303.9991807 304 304.0008193 304.0016184 304.0023776 304.0030784 304.0037033 304.004237 304.0046664 304.0049809 304.0051727 304.0052372 304.0047553 304.0046967 304.0045225 304.004237 304.0038471 304.0033625 304.0027951 304.0021589 304.0014695 304.0007439 304 303.9992561 303.9985305 303.9978411 303.9972049 303.9966375 303.9961529 303.995763 303.9954775 303.9953033 303.9952447 303.9953033 303.9954775 303.995763 303.9961529 303.9966375 303.9972049 303.9978411 303.9985305 303.9992561 304 304.0007439 304.0014695 304.0021589 304.0027951 304.0033625 304.0038471 304.004237 304.0045225 304.0046967 304.0047553 304.0041563 304.0041051 304.0039528 304.0037033 304.0033625 304.0029389 304.002443 304.0018869 304.0012844 304.0006502 304 303.9993498 303.9987156 303.9981131 303.997557 303.9970611 303.9966375 303.9962967 303.9960472 303.9958949 303.9958437 303.9958949 303.9960472 303.9962967 303.9966375 303.9970611 303.997557 303.9981131 303.9987156 303.9993498 304 304.0006502 304.0012844 304.0018869 304.002443 304.0029389 304.0033625 304.0037033 304.0039528 304.0041051 304.0041563 304.0034549 304.0034124 304.0032858 304.0030784 304.0027951 304.002443 304.0020307 304.0015685 304.0010676 304.0005405 304 303.9994595 303.9989324 303.9984315 303.9979693 303.997557 303.9972049 303.9969216 303.9967142 303.9965876 303.9965451 303.9965876 303.9967142 303.9969216 303.9972049 303.997557 303.9979693 303.9984315 303.9989324 303.9994595 304 304.0005405 304.0010676 304.0015685 304.0020307 304.002443 304.0027951 304.0030784 304.0032858 304.0034124 304.0034549 304.0026685 304.0026356 304.0025379 304.0023776 304.0021589 304.0018869 304.0015685 304.0012115 304.0008246 304.0004174 304 303.9995826 303.9991754 303.9987885 303.9984315 303.9981131 303.9978411 303.9976224 303.9974621 303.9973644 303.9973315 303.9973644 303.9974621 303.9976224 303.9978411 303.9981131 303.9984315 303.9987885 303.9991754 303.9995826 304 304.0004174 304.0008246 304.0012115 304.0015685 304.0018869 304.0021589 304.0023776 304.0025379 304.0026356 304.0026685 304.0018164 304.001794 304.0017275 304.0016184 304.0014695 304.0012844 304.0010676 304.0008246 304.0005613 304.0002841 304 303.9997159 303.9994387 303.9991754 303.9989324 303.9987156 303.9985305 303.9983816 303.9982725 303.998206 303.9981836 303.998206 303.9982725 303.9983816 303.9985305 303.9987156 303.9989324 303.9991754 303.9994387 303.9997159 304 304.0002841 304.0005613 304.0008246 304.0010676 304.0012844 304.0014695 304.0016184 304.0017275 304.001794 304.0018164 304.0009195 304.0009082 304.0008745 304.0008193 304.0007439 304.0006502 304.0005405 304.0004174 304.0002841 304.0001438 304 303.9998562 303.9997159 303.9995826 303.9994595 303.9993498 303.9992561 303.9991807 303.9991255 303.9990918 303.9990805 303.9990918 303.9991255 303.9991807 303.9992561 303.9993498 303.9994595 303.9995826 303.9997159 303.9998562 304 304.0001438 304.0002841 304.0004174 304.0005405 304.0006502 304.0007439 304.0008193 304.0008745 304.0009082 304.0009195 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 303.9990805 303.9990918 303.9991255 303.9991807 303.9992561 303.9993498 303.9994595 303.9995826 303.9997159 303.9998562 304 304.0001438 304.0002841 304.0004174 304.0005405 304.0006502 304.0007439 304.0008193 304.0008745 304.0009082 304.0009195 304.0009082 304.0008745 304.0008193 304.0007439 304.0006502 304.0005405 304.0004174 304.0002841 304.0001438 304 303.9998562 303.9997159 303.9995826 303.9994595 303.9993498 303.9992561 303.9991807 303.9991255 303.9990918 303.9990805 303.9981836 303.998206 303.9982725 303.9983816 303.9985305 303.9987156 303.9989324 303.9991754 303.9994387 303.9997159 304 304.0002841 304.0005613 304.0008246 304.0010676 304.0012844 304.0014695 304.0016184 304.0017275 304.001794 304.0018164 304.001794 304.0017275 304.0016184 304.0014695 304.0012844 304.0010676 304.0008246 304.0005613 304.0002841 304 303.9997159 303.9994387 303.9991754 303.9989324 303.9987156 303.9985305 303.9983816 303.9982725 303.998206 303.9981836 303.9973315 303.9973644 303.9974621 303.9976224 303.9978411 303.9981131 303.9984315 303.9987885 303.9991754 303.9995826 304 304.0004174 304.0008246 304.0012115 304.0015685 304.0018869 304.0021589 304.0023776 304.0025379 304.0026356 304.0026685 304.0026356 304.0025379 304.0023776 304.0021589 304.0018869 304.0015685 304.0012115 304.0008246 304.0004174 304 303.9995826 303.9991754 303.9987885 303.9984315 303.9981131 303.9978411 303.9976224 303.9974621 303.9973644 303.9973315 303.9965451 303.9965876 303.9967142 303.9969216 303.9972049 303.997557 303.9979693 303.9984315 303.9989324 303.9994595 304 304.0005405 304.0010676 304.0015685 304.0020307 304.002443 304.0027951 304.0030784 304.0032858 304.0034124 304.0034549 304.0034124 304.0032858 304.0030784 304.0027951 304.002443 304.0020307 304.0015685 304.0010676 304.0005405 304 303.9994595 303.9989324 303.9984315 303.9979693 303.997557 303.9972049 303.9969216 303.9967142 303.9965876 303.9965451 303.9958437 303.9958949 303.9960472 303.9962967 303.9966375 303.9970611 303.997557 303.9981131 303.9987156 303.9993498 304 304.0006502 304.0012844 304.0018869 304.002443 304.0029389 304.0033625 304.0037033 304.0039528 304.0041051 304.0041563 304.0041051 304.0039528 304.0037033 304.0033625 304.0029389 304.002443 304.0018869 304.0012844 304.0006502 304 303.9993498 303.9987156 303.9981131 303.997557 303.9970611 303.9966375 303.9962967 303.9960472 303.9958949 303.9958437 303.9952447 303.9953033 303.9954775 303.995763 303.9961529 303.9966375 303.9972049 303.9978411 303.9985305 303.9992561 304 304.0007439 304.0014695 304.0021589 304.0027951 304.0033625 304.0038471 304.004237 304.0045225 304.0046967 304.0047553 304.0046967 304.0045225 304.004237 304.0038471 304.0033625 304.0027951 304.0021589 304.0014695 304.0007439 304 303.9992561 303.9985305 303.9978411 303.9972049 303.9966375 303.9961529 303.995763 303.9954775 303.9953033 303.9952447 303.9947628 303.9948273 303.9950191 303.9953336 303.995763 303.9962967 303.9969216 303.9976224 303.9983816 303.9991807 304 304.0008193 304.0016184 304.0023776 304.0030784 304.0037033 304.004237 304.0046664 304.0049809 304.0051727 304.0052372 304.0051727 304.0049809 304.0046664 304.004237 304.0037033 304.0030784 304.0023776 304.0016184 304.0008193 304 303.9991807 303.9983816 303.9976224 303.9969216 303.9962967 303.995763 303.9953336 303.9950191 303.9948273 303.9947628 303.9944098 303.9944787 303.9946834 303.9950191 303.9954775 303.9960472 303.9967142 303.9974621 303.9982725 303.9991255 304 304.0008745 304.0017275 304.0025379 304.0032858 304.0039528 304.0045225 304.0049809 304.0053166 304.0055213 304.0055902 304.0055213 304.0053166 304.0049809 304.0045225 304.0039528 304.0032858 304.0025379 304.0017275 304.0008745 304 303.9991255 303.9982725 303.9974621 303.9967142 303.9960472 303.9954775 303.9950191 303.9946834 303.9944787 303.9944098 303.9941945 303.994266 303.9944787 303.9948273 303.9953033 303.9958949 303.9965876 303.9973644 303.998206 303.9990918 304 304.0009082 304.001794 304.0026356 304.0034124 304.0041051 304.0046967 304.0051727 304.0055213 304.005734 304.0058055 304.005734 304.0055213 304.0051727 304.0046967 304.0041051 304.0034124 304.0026356 304.001794 304.0009082 304 303.9990918 303.998206 303.9973644 303.9965876 303.9958949 303.9953033 303.9948273 303.9944787 303.994266 303.9941945 303.9941221 303.9941945 303.9944098 303.9947628 303.9952447 303.9958437 303.9965451 303.9973315 303.9981836 303.9990805 304 304.0009195 304.0018164 304.0026685 304.0034549 304.0041563 304.0047553 304.0052372 304.0055902 304.0058055 304.0058779 304.0058055 304.0055902 304.0052372 304.0047553 304.0041563 304.0034549 304.0026685 304.0018164 304.0009195 304 303.9990805 303.9981836 303.9973315 303.9965451 303.9958437 303.9952447 303.9947628 303.9944098 303.9941945 303.9941221 303.9941945 303.994266 303.9944787 303.9948273 303.9953033 303.9958949 303.9965876 303.9973644 303.998206 303.9990918 304 304.0009082 304.001794 304.0026356 304.0034124 304.0041051 304.0046967 304.0051727 304.0055213 304.005734 304.0058055 304.005734 304.0055213 304.0051727 304.0046967 304.0041051 304.0034124 304.0026356 304.001794 304.0009082 304 303.9990918 303.998206 303.9973644 303.9965876 303.9958949 303.9953033 303.9948273 303.9944787 303.994266 303.9941945 303.9944098 303.9944787 303.9946834 303.9950191 303.9954775 303.9960472 303.9967142 303.9974621 303.9982725 303.9991255 304 304.0008745 304.0017275 304.0025379 304.0032858 304.0039528 304.0045225 304.0049809 304.0053166 304.0055213 304.0055902 304.0055213 304.0053166 304.0049809 304.0045225 304.0039528 304.0032858 304.0025379 304.0017275 304.0008745 304 303.9991255 303.9982725 303.9974621 303.9967142 303.9960472 303.9954775 303.9950191 303.9946834 303.9944787 303.9944098 303.9947628 303.9948273 303.9950191 303.9953336 303.995763 303.9962967 303.9969216 303.9976224 303.9983816 303.9991807 304 304.0008193 304.0016184 304.0023776 304.0030784 304.0037033 304.004237 304.0046664 304.0049809 304.0051727 304.0052372 304.0051727 304.0049809 304.0046664 304.004237 304.0037033 304.0030784 304.0023776 304.0016184 304.0008193 304 303.9991807 303.9983816 303.9976224 303.9969216 303.9962967 303.995763 303.9953336 303.9950191 303.9948273 303.9947628 303.9952447 303.9953033 303.9954775 303.995763 303.9961529 303.9966375 303.9972049 303.9978411 303.9985305 303.9992561 304 304.0007439 304.0014695 304.0021589 304.0027951 304.0033625 304.0038471 304.004237 304.0045225 304.0046967 304.0047553 304.0046967 304.0045225 304.004237 304.0038471 304.0033625 304.0027951 304.0021589 304.0014695 304.0007439 304 303.9992561 303.9985305 303.9978411 303.9972049 303.9966375 303.9961529 303.995763 303.9954775 303.9953033 303.9952447 303.9958437 303.9958949 303.9960472 303.9962967 303.9966375 303.9970611 303.997557 303.9981131 303.9987156 303.9993498 304 304.0006502 304.0012844 304.0018869 304.002443 304.0029389 304.0033625 304.0037033 304.0039528 304.0041051 304.0041563 304.0041051 304.0039528 304.0037033 304.0033625 304.0029389 304.002443 304.0018869 304.0012844 304.0006502 304 303.9993498 303.9987156 303.9981131 303.997557 303.9970611 303.9966375 303.9962967 303.9960472 303.9958949 303.9958437 303.9965451 303.9965876 303.9967142 303.9969216 303.9972049 303.997557 303.9979693 303.9984315 303.9989324 303.9994595 304 304.0005405 304.0010676 304.0015685 304.0020307 304.002443 304.0027951 304.0030784 304.0032858 304.0034124 304.0034549 304.0034124 304.0032858 304.0030784 304.0027951 304.002443 304.0020307 304.0015685 304.0010676 304.0005405 304 303.9994595 303.9989324 303.9984315 303.9979693 303.997557 303.9972049 303.9969216 303.9967142 303.9965876 303.9965451 303.9973315 303.9973644 303.9974621 303.9976224 303.9978411 303.9981131 303.9984315 303.9987885 303.9991754 303.9995826 304 304.0004174 304.0008246 304.0012115 304.0015685 304.0018869 304.0021589 304.0023776 304.0025379 304.0026356 304.0026685 304.0026356 304.0025379 304.0023776 304.0021589 304.0018869 304.0015685 304.0012115 304.0008246 304.0004174 304 303.9995826 303.9991754 303.9987885 303.9984315 303.9981131 303.9978411 303.9976224 303.9974621 303.9973644 303.9973315 303.9981836 303.998206 303.9982725 303.9983816 303.9985305 303.9987156 303.9989324 303.9991754 303.9994387 303.9997159 304 304.0002841 304.0005613 304.0008246 304.0010676 304.0012844 304.0014695 304.0016184 304.0017275 304.001794 304.0018164 304.001794 304.0017275 304.0016184 304.0014695 304.0012844 304.0010676 304.0008246 304.0005613 304.0002841 304 303.9997159 303.9994387 303.9991754 303.9989324 303.9987156 303.9985305 303.9983816 303.9982725 303.998206 303.9981836 303.9990805 303.9990918 303.9991255 303.9991807 303.9992561 303.9993498 303.9994595 303.9995826 303.9997159 303.9998562 304 304.0001438 304.0002841 304.0004174 304.0005405 304.0006502 304.0007439 304.0008193 304.0008745 304.0009082 304.0009195 304.0009082 304.0008745 304.0008193 304.0007439 304.0006502 304.0005405 304.0004174 304.0002841 304.0001438 304 303.9998562 303.9997159 303.9995826 303.9994595 303.9993498 303.9992561 303.9991807 303.9991255 303.9990918 303.9990805 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304 304.0009195 304.0009082 304.0008745 304.0008193 304.0007439 304.0006502 304.0005405 304.0004174 304.0002841 304.0001438 304 303.9998562 303.9997159 303.9995826 303.9994595 303.9993498 303.9992561 303.9991807 303.9991255 303.9990918 303.9990805 303.9990918 303.9991255 303.9991807 303.9992561 303.9993498 303.9994595 303.9995826 303.9997159 303.9998562 304 304.0001438 304.0002841 304.0004174 304.0005405 304.0006502 304.0007439 304.0008193 304.0008745 304.0009082 304.0009195 304.0018164 304.001794 304.0017275 304.0016184 304.0014695 304.0012844 304.0010676 304.0008246 304.0005613 304.0002841 304 303.9997159 303.9994387 303.9991754 303.9989324 303.9987156 303.9985305 303.9983816 303.9982725 303.998206 303.9981836 303.998206 303.9982725 303.9983816 303.9985305 303.9987156 303.9989324 303.9991754 303.9994387 303.9997159 304 304.0002841 304.0005613 304.0008246 304.0010676 304.0012844 304.0014695 304.0016184 304.0017275 304.001794 304.0018164 304.0026685 304.0026356 304.0025379 304.0023776 304.0021589 304.0018869 304.0015685 304.0012115 304.0008246 304.0004174 304 303.9995826 303.9991754 303.9987885 303.9984315 303.9981131 303.9978411 303.9976224 303.9974621 303.9973644 303.9973315 303.9973644 303.9974621 303.9976224 303.9978411 303.9981131 303.9984315 303.9987885 303.9991754 303.9995826 304 304.0004174 304.0008246 304.0012115 304.0015685 304.0018869 304.0021589 304.0023776 304.0025379 304.0026356 304.0026685 304.0034549 304.0034124 304.0032858 304.0030784 304.0027951 304.002443 304.0020307 304.0015685 304.0010676 304.0005405 304 303.9994595 303.9989324 303.9984315 303.9979693 303.997557 303.9972049 303.9969216 303.9967142 303.9965876 303.9965451 303.9965876 303.9967142 303.9969216 303.9972049 303.997557 303.9979693 303.9984315 303.9989324 303.9994595 304 304.0005405 304.0010676 304.0015685 304.0020307 304.002443 304.0027951 304.0030784 304.0032858 304.0034124 304.0034549 304.0041563 304.0041051 304.0039528 304.0037033 304.0033625 304.0029389 304.002443 304.0018869 304.0012844 304.0006502 304 303.9993498 303.9987156 303.9981131 303.997557 303.9970611 303.9966375 303.9962967 303.9960472 303.9958949 303.9958437 303.9958949 303.9960472 303.9962967 303.9966375 303.9970611 303.997557 303.9981131 303.9987156 303.9993498 304 304.0006502 304.0012844 304.0018869 304.002443 304.0029389 304.0033625 304.0037033 304.0039528 304.0041051 304.0041563 304.0047553 304.0046967 304.0045225 304.004237 304.0038471 304.0033625 304.0027951 304.0021589 304.0014695 304.0007439 304 303.9992561 303.9985305 303.9978411 303.9972049 303.9966375 303.9961529 303.995763 303.9954775 303.9953033 303.9952447 303.9953033 303.9954775 303.995763 303.9961529 303.9966375 303.9972049 303.9978411 303.9985305 303.9992561 304 304.0007439 304.0014695 304.0021589 304.0027951 304.0033625 304.0038471 304.004237 304.0045225 304.0046967 304.0047553 304.0052372 304.0051727 304.0049809 304.0046664 304.004237 304.0037033 304.0030784 304.0023776 304.0016184 304.0008193 304 303.9991807 303.9983816 303.9976224 303.9969216 303.9962967 303.995763 303.9953336 303.9950191 303.9948273 303.9947628 303.9948273 303.9950191 303.9953336 303.995763 303.9962967 303.9969216 303.9976224 303.9983816 303.9991807 304 304.0008193 304.0016184 304.0023776 304.0030784 304.0037033 304.004237 304.0046664 304.0049809 304.0051727 304.0052372 304.0055902 304.0055213 304.0053166 304.0049809 304.0045225 304.0039528 304.0032858 304.0025379 304.0017275 304.0008745 304 303.9991255 303.9982725 303.9974621 303.9967142 303.9960472 303.9954775 303.9950191 303.9946834 303.9944787 303.9944098 303.9944787 303.9946834 303.9950191 303.9954775 303.9960472 303.9967142 303.9974621 303.9982725 303.9991255 304 304.0008745 304.0017275 304.0025379 304.0032858 304.0039528 304.0045225 304.0049809 304.0053166 304.0055213 304.0055902 304.0058055 304.005734 304.0055213 304.0051727 304.0046967 304.0041051 304.0034124 304.0026356 304.001794 304.0009082 304 303.9990918 303.998206 303.9973644 303.9965876 303.9958949 303.9953033 303.9948273 303.9944787 303.994266 303.9941945 303.994266 303.9944787 303.9948273 303.9953033 303.9958949 303.9965876 303.9973644 303.998206 303.9990918 304 304.0009082 304.001794 304.0026356 304.0034124 304.0041051 304.0046967 304.0051727 304.0055213 304.005734 304.0058055 304.0058779 304.0058055 304.0055902 304.0052372 304.0047553 304.0041563 304.0034549 304.0026685 304.0018164 304.0009195 304 303.9990805 303.9981836 303.9973315 303.9965451 303.9958437 303.9952447 303.9947628 303.9944098 303.9941945 303.9941221 303.9941945 303.9944098 303.9947628 303.9952447 303.9958437 303.9965451 303.9973315 303.9981836 303.9990805 304 304.0009195 304.0018164 304.0026685 304.0034549 304.0041563 304.0047553 304.0052372 304.0055902 304.0058055 304.0058779 303.0045399 303.004484 303.0043177 303.0040451 303.0036729 303.0032102 303.0026685 303.0020611 303.0014029 303.0007102 303 302.9992898 302.9985971 302.9979389 302.9973315 302.9967898 302.9963271 302.9959549 302.9956823 302.995516 302.9954601 302.995516 302.9956823 302.9959549 302.9963271 302.9967898 302.9973315 302.9979389 302.9985971 302.9992898 303 303.0007102 303.0014029 303.0020611 303.0026685 303.0032102 303.0036729 303.0040451 303.0043177 303.004484 303.0045399 303.004484 303.0044288 303.0042645 303.0039953 303.0036276 303.0031707 303.0026356 303.0020357 303.0013856 303.0007015 303 302.9992985 302.9986144 302.9979643 302.9973644 302.9968293 302.9963724 302.9960047 302.9957355 302.9955712 302.995516 302.9955712 302.9957355 302.9960047 302.9963724 302.9968293 302.9973644 302.9979643 302.9986144 302.9992985 303 303.0007015 303.0013856 303.0020357 303.0026356 303.0031707 303.0036276 303.0039953 303.0042645 303.0044288 303.004484 303.0043177 303.0042645 303.0041064 303.0038471 303.0034931 303.0030531 303.0025379 303.0019602 303.0013342 303.0006754 303 302.9993246 302.9986658 302.9980398 302.9974621 302.9969469 302.9965069 302.9961529 302.9958936 302.9957355 302.9956823 302.9957355 302.9958936 302.9961529 302.9965069 302.9969469 302.9974621 302.9980398 302.9986658 302.9993246 303 303.0006754 303.0013342 303.0019602 303.0025379 303.0030531 303.0034931 303.0038471 303.0041064 303.0042645 303.0043177 303.0040451 303.0039953 303.0038471 303.0036042 303.0032725 303.0028603 303.0023776 303.0018364 303.00125 303.0006328 303 302.9993672 302.99875 302.9981636 302.9976224 302.9971397 302.9967275 302.9963958 302.9961529 302.9960047 302.9959549 302.9960047 302.9961529 302.9963958 302.9967275 302.9971397 302.9976224 302.9981636 302.99875 302.9993672 303 303.0006328 303.00125 303.0018364 303.0023776 303.0028603 303.0032725 303.0036042 303.0038471 303.0039953 303.0040451 303.0036729 303.0036276 303.0034931 303.0032725 303.0029714 303.0025971 303.0021589 303.0016674 303.001135 303.0005746 303 302.9994254 302.998865 302.9983326 302.9978411 302.9974029 302.9970286 302.9967275 302.9965069 302.9963724 302.9963271 302.9963724 302.9965069 302.9967275 302.9970286 302.9974029 302.9978411 302.9983326 302.998865 302.9994254 303 303.0005746 303.001135 303.0016674 303.0021589 303.0025971 303.0029714 303.0032725 303.0034931 303.0036276 303.0036729 303.0032102 303.0031707 303.0030531 303.0028603 303.0025971 303.00227 303.0018869 303.0014574 303.000992 303.0005022 303 302.9994978 302.999008 302.9985426 302.9981131 302.99773 302.9974029 302.9971397 302.9969469 302.9968293 302.9967898 302.9968293 302.9969469 302.9971397 302.9974029 302.99773 302.9981131 302.9985426 302.999008 302.9994978 303 303.0005022 303.000992 303.0014574 303.0018869 303.00227 303.0025971 303.0028603 303.0030531 303.0031707 303.0032102 303.0026685 303.0026356 303.0025379 303.0023776 303.0021589 303.0018869 303.0015685 303.0012115 303.0008246 303.0004174 303 302.9995826 302.9991754 302.9987885 302.9984315 302.9981131 302.9978411 302.9976224 302.9974621 302.9973644 302.9973315 302.9973644 302.9974621 302.9976224 302.9978411 302.9981131 302.9984315 302.9987885 302.9991754 302.9995826 303 303.0004174 303.0008246 303.0012115 303.0015685 303.0018869 303.0021589 303.0023776 303.0025379 303.0026356 303.0026685 303.0020611 303.0020357 303.0019602 303.0018364 303.0016674 303.0014574 303.0012115 303.0009357 303.0006369 303.0003224 303 302.9996776 302.9993631 302.9990643 302.9987885 302.9985426 302.9983326 302.9981636 302.9980398 302.9979643 302.9979389 302.9979643 302.9980398 302.9981636 302.9983326 302.9985426 302.9987885 302.9990643 302.9993631 302.9996776 303 303.0003224 303.0006369 303.0009357 303.0012115 303.0014574 303.0016674 303.0018364 303.0019602 303.0020357 303.0020611 303.0014029 303.0013856 303.0013342 303.00125 303.001135 303.000992 303.0008246 303.0006369 303.0004335 303.0002195 303 302.9997805 302.9995665 302.9993631 302.9991754 302.999008 302.998865 302.99875 302.9986658 302.9986144 302.9985971 302.9986144 302.9986658 302.99875 302.998865 302.999008 302.9991754 302.9993631 302.9995665 302.9997805 303 303.0002195 303.0004335 303.0006369 303.0008246 303.000992 303.001135 303.00125 303.0013342 303.0013856 303.0014029 303.0007102 303.0007015 303.0006754 303.0006328 303.0005746 303.0005022 303.0004174 303.0003224 303.0002195 303.0001111 303 302.9998889 302.9997805 302.9996776 302.9995826 302.9994978 302.9994254 302.9993672 302.9993246 302.9992985 302.9992898 302.9992985 302.9993246 302.9993672 302.9994254 302.9994978 302.9995826 302.9996776 302.9997805 302.9998889 303 303.0001111 303.0002195 303.0003224 303.0004174 303.0005022 303.0005746 303.0006328 303.0006754 303.0007015 303.0007102 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 302.9992898 302.9992985 302.9993246 302.9993672 302.9994254 302.9994978 302.9995826 302.9996776 302.9997805 302.9998889 303 303.0001111 303.0002195 303.0003224 303.0004174 303.0005022 303.0005746 303.0006328 303.0006754 303.0007015 303.0007102 303.0007015 303.0006754 303.0006328 303.0005746 303.0005022 303.0004174 303.0003224 303.0002195 303.0001111 303 302.9998889 302.9997805 302.9996776 302.9995826 302.9994978 302.9994254 302.9993672 302.9993246 302.9992985 302.9992898 302.9985971 302.9986144 302.9986658 302.99875 302.998865 302.999008 302.9991754 302.9993631 302.9995665 302.9997805 303 303.0002195 303.0004335 303.0006369 303.0008246 303.000992 303.001135 303.00125 303.0013342 303.0013856 303.0014029 303.0013856 303.0013342 303.00125 303.001135 303.000992 303.0008246 303.0006369 303.0004335 303.0002195 303 302.9997805 302.9995665 302.9993631 302.9991754 302.999008 302.998865 302.99875 302.9986658 302.9986144 302.9985971 302.9979389 302.9979643 302.9980398 302.9981636 302.9983326 302.9985426 302.9987885 302.9990643 302.9993631 302.9996776 303 303.0003224 303.0006369 303.0009357 303.0012115 303.0014574 303.0016674 303.0018364 303.0019602 303.0020357 303.0020611 303.0020357 303.0019602 303.0018364 303.0016674 303.0014574 303.0012115 303.0009357 303.0006369 303.0003224 303 302.9996776 302.9993631 302.9990643 302.9987885 302.9985426 302.9983326 302.9981636 302.9980398 302.9979643 302.9979389 302.9973315 302.9973644 302.9974621 302.9976224 302.9978411 302.9981131 302.9984315 302.9987885 302.9991754 302.9995826 303 303.0004174 303.0008246 303.0012115 303.0015685 303.0018869 303.0021589 303.0023776 303.0025379 303.0026356 303.0026685 303.0026356 303.0025379 303.0023776 303.0021589 303.0018869 303.0015685 303.0012115 303.0008246 303.0004174 303 302.9995826 302.9991754 302.9987885 302.9984315 302.9981131 302.9978411 302.9976224 302.9974621 302.9973644 302.9973315 302.9967898 302.9968293 302.9969469 302.9971397 302.9974029 302.99773 302.9981131 302.9985426 302.999008 302.9994978 303 303.0005022 303.000992 303.0014574 303.0018869 303.00227 303.0025971 303.0028603 303.0030531 303.0031707 303.0032102 303.0031707 303.0030531 303.0028603 303.0025971 303.00227 303.0018869 303.0014574 303.000992 303.0005022 303 302.9994978 302.999008 302.9985426 302.9981131 302.99773 302.9974029 302.9971397 302.9969469 302.9968293 302.9967898 302.9963271 302.9963724 302.9965069 302.9967275 302.9970286 302.9974029 302.9978411 302.9983326 302.998865 302.9994254 303 303.0005746 303.001135 303.0016674 303.0021589 303.0025971 303.0029714 303.0032725 303.0034931 303.0036276 303.0036729 303.0036276 303.0034931 303.0032725 303.0029714 303.0025971 303.0021589 303.0016674 303.001135 303.0005746 303 302.9994254 302.998865 302.9983326 302.9978411 302.9974029 302.9970286 302.9967275 302.9965069 302.9963724 302.9963271 302.9959549 302.9960047 302.9961529 302.9963958 302.9967275 302.9971397 302.9976224 302.9981636 302.99875 302.9993672 303 303.0006328 303.00125 303.0018364 303.0023776 303.0028603 303.0032725 303.0036042 303.0038471 303.0039953 303.0040451 303.0039953 303.0038471 303.0036042 303.0032725 303.0028603 303.0023776 303.0018364 303.00125 303.0006328 303 302.9993672 302.99875 302.9981636 302.9976224 302.9971397 302.9967275 302.9963958 302.9961529 302.9960047 302.9959549 302.9956823 302.9957355 302.9958936 302.9961529 302.9965069 302.9969469 302.9974621 302.9980398 302.9986658 302.9993246 303 303.0006754 303.0013342 303.0019602 303.0025379 303.0030531 303.0034931 303.0038471 303.0041064 303.0042645 303.0043177 303.0042645 303.0041064 303.0038471 303.0034931 303.0030531 303.0025379 303.0019602 303.0013342 303.0006754 303 302.9993246 302.9986658 302.9980398 302.9974621 302.9969469 302.9965069 302.9961529 302.9958936 302.9957355 302.9956823 302.995516 302.9955712 302.9957355 302.9960047 302.9963724 302.9968293 302.9973644 302.9979643 302.9986144 302.9992985 303 303.0007015 303.0013856 303.0020357 303.0026356 303.0031707 303.0036276 303.0039953 303.0042645 303.0044288 303.004484 303.0044288 303.0042645 303.0039953 303.0036276 303.0031707 303.0026356 303.0020357 303.0013856 303.0007015 303 302.9992985 302.9986144 302.9979643 302.9973644 302.9968293 302.9963724 302.9960047 302.9957355 302.9955712 302.995516 302.9954601 302.995516 302.9956823 302.9959549 302.9963271 302.9967898 302.9973315 302.9979389 302.9985971 302.9992898 303 303.0007102 303.0014029 303.0020611 303.0026685 303.0032102 303.0036729 303.0040451 303.0043177 303.004484 303.0045399 303.004484 303.0043177 303.0040451 303.0036729 303.0032102 303.0026685 303.0020611 303.0014029 303.0007102 303 302.9992898 302.9985971 302.9979389 302.9973315 302.9967898 302.9963271 302.9959549 302.9956823 302.995516 302.9954601 302.995516 302.9955712 302.9957355 302.9960047 302.9963724 302.9968293 302.9973644 302.9979643 302.9986144 302.9992985 303 303.0007015 303.0013856 303.0020357 303.0026356 303.0031707 303.0036276 303.0039953 303.0042645 303.0044288 303.004484 303.0044288 303.0042645 303.0039953 303.0036276 303.0031707 303.0026356 303.0020357 303.0013856 303.0007015 303 302.9992985 302.9986144 302.9979643 302.9973644 302.9968293 302.9963724 302.9960047 302.9957355 302.9955712 302.995516 302.9956823 302.9957355 302.9958936 302.9961529 302.9965069 302.9969469 302.9974621 302.9980398 302.9986658 302.9993246 303 303.0006754 303.0013342 303.0019602 303.0025379 303.0030531 303.0034931 303.0038471 303.0041064 303.0042645 303.0043177 303.0042645 303.0041064 303.0038471 303.0034931 303.0030531 303.0025379 303.0019602 303.0013342 303.0006754 303 302.9993246 302.9986658 302.9980398 302.9974621 302.9969469 302.9965069 302.9961529 302.9958936 302.9957355 302.9956823 302.9959549 302.9960047 302.9961529 302.9963958 302.9967275 302.9971397 302.9976224 302.9981636 302.99875 302.9993672 303 303.0006328 303.00125 303.0018364 303.0023776 303.0028603 303.0032725 303.0036042 303.0038471 303.0039953 303.0040451 303.0039953 303.0038471 303.0036042 303.0032725 303.0028603 303.0023776 303.0018364 303.00125 303.0006328 303 302.9993672 302.99875 302.9981636 302.9976224 302.9971397 302.9967275 302.9963958 302.9961529 302.9960047 302.9959549 302.9963271 302.9963724 302.9965069 302.9967275 302.9970286 302.9974029 302.9978411 302.9983326 302.998865 302.9994254 303 303.0005746 303.001135 303.0016674 303.0021589 303.0025971 303.0029714 303.0032725 303.0034931 303.0036276 303.0036729 303.0036276 303.0034931 303.0032725 303.0029714 303.0025971 303.0021589 303.0016674 303.001135 303.0005746 303 302.9994254 302.998865 302.9983326 302.9978411 302.9974029 302.9970286 302.9967275 302.9965069 302.9963724 302.9963271 302.9967898 302.9968293 302.9969469 302.9971397 302.9974029 302.99773 302.9981131 302.9985426 302.999008 302.9994978 303 303.0005022 303.000992 303.0014574 303.0018869 303.00227 303.0025971 303.0028603 303.0030531 303.0031707 303.0032102 303.0031707 303.0030531 303.0028603 303.0025971 303.00227 303.0018869 303.0014574 303.000992 303.0005022 303 302.9994978 302.999008 302.9985426 302.9981131 302.99773 302.9974029 302.9971397 302.9969469 302.9968293 302.9967898 302.9973315 302.9973644 302.9974621 302.9976224 302.9978411 302.9981131 302.9984315 302.9987885 302.9991754 302.9995826 303 303.0004174 303.0008246 303.0012115 303.0015685 303.0018869 303.0021589 303.0023776 303.0025379 303.0026356 303.0026685 303.0026356 303.0025379 303.0023776 303.0021589 303.0018869 303.0015685 303.0012115 303.0008246 303.0004174 303 302.9995826 302.9991754 302.9987885 302.9984315 302.9981131 302.9978411 302.9976224 302.9974621 302.9973644 302.9973315 302.9979389 302.9979643 302.9980398 302.9981636 302.9983326 302.9985426 302.9987885 302.9990643 302.9993631 302.9996776 303 303.0003224 303.0006369 303.0009357 303.0012115 303.0014574 303.0016674 303.0018364 303.0019602 303.0020357 303.0020611 303.0020357 303.0019602 303.0018364 303.0016674 303.0014574 303.0012115 303.0009357 303.0006369 303.0003224 303 302.9996776 302.9993631 302.9990643 302.9987885 302.9985426 302.9983326 302.9981636 302.9980398 302.9979643 302.9979389 302.9985971 302.9986144 302.9986658 302.99875 302.998865 302.999008 302.9991754 302.9993631 302.9995665 302.9997805 303 303.0002195 303.0004335 303.0006369 303.0008246 303.000992 303.001135 303.00125 303.0013342 303.0013856 303.0014029 303.0013856 303.0013342 303.00125 303.001135 303.000992 303.0008246 303.0006369 303.0004335 303.0002195 303 302.9997805 302.9995665 302.9993631 302.9991754 302.999008 302.998865 302.99875 302.9986658 302.9986144 302.9985971 302.9992898 302.9992985 302.9993246 302.9993672 302.9994254 302.9994978 302.9995826 302.9996776 302.9997805 302.9998889 303 303.0001111 303.0002195 303.0003224 303.0004174 303.0005022 303.0005746 303.0006328 303.0006754 303.0007015 303.0007102 303.0007015 303.0006754 303.0006328 303.0005746 303.0005022 303.0004174 303.0003224 303.0002195 303.0001111 303 302.9998889 302.9997805 302.9996776 302.9995826 302.9994978 302.9994254 302.9993672 302.9993246 302.9992985 302.9992898 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303 303.0007102 303.0007015 303.0006754 303.0006328 303.0005746 303.0005022 303.0004174 303.0003224 303.0002195 303.0001111 303 302.9998889 302.9997805 302.9996776 302.9995826 302.9994978 302.9994254 302.9993672 302.9993246 302.9992985 302.9992898 302.9992985 302.9993246 302.9993672 302.9994254 302.9994978 302.9995826 302.9996776 302.9997805 302.9998889 303 303.0001111 303.0002195 303.0003224 303.0004174 303.0005022 303.0005746 303.0006328 303.0006754 303.0007015 303.0007102 303.0014029 303.0013856 303.0013342 303.00125 303.001135 303.000992 303.0008246 303.0006369 303.0004335 303.0002195 303 302.9997805 302.9995665 302.9993631 302.9991754 302.999008 302.998865 302.99875 302.9986658 302.9986144 302.9985971 302.9986144 302.9986658 302.99875 302.998865 302.999008 302.9991754 302.9993631 302.9995665 302.9997805 303 303.0002195 303.0004335 303.0006369 303.0008246 303.000992 303.001135 303.00125 303.0013342 303.0013856 303.0014029 303.0020611 303.0020357 303.0019602 303.0018364 303.0016674 303.0014574 303.0012115 303.0009357 303.0006369 303.0003224 303 302.9996776 302.9993631 302.9990643 302.9987885 302.9985426 302.9983326 302.9981636 302.9980398 302.9979643 302.9979389 302.9979643 302.9980398 302.9981636 302.9983326 302.9985426 302.9987885 302.9990643 302.9993631 302.9996776 303 303.0003224 303.0006369 303.0009357 303.0012115 303.0014574 303.0016674 303.0018364 303.0019602 303.0020357 303.0020611 303.0026685 303.0026356 303.0025379 303.0023776 303.0021589 303.0018869 303.0015685 303.0012115 303.0008246 303.0004174 303 302.9995826 302.9991754 302.9987885 302.9984315 302.9981131 302.9978411 302.9976224 302.9974621 302.9973644 302.9973315 302.9973644 302.9974621 302.9976224 302.9978411 302.9981131 302.9984315 302.9987885 302.9991754 302.9995826 303 303.0004174 303.0008246 303.0012115 303.0015685 303.0018869 303.0021589 303.0023776 303.0025379 303.0026356 303.0026685 303.0032102 303.0031707 303.0030531 303.0028603 303.0025971 303.00227 303.0018869 303.0014574 303.000992 303.0005022 303 302.9994978 302.999008 302.9985426 302.9981131 302.99773 302.9974029 302.9971397 302.9969469 302.9968293 302.9967898 302.9968293 302.9969469 302.9971397 302.9974029 302.99773 302.9981131 302.9985426 302.999008 302.9994978 303 303.0005022 303.000992 303.0014574 303.0018869 303.00227 303.0025971 303.0028603 303.0030531 303.0031707 303.0032102 303.0036729 303.0036276 303.0034931 303.0032725 303.0029714 303.0025971 303.0021589 303.0016674 303.001135 303.0005746 303 302.9994254 302.998865 302.9983326 302.9978411 302.9974029 302.9970286 302.9967275 302.9965069 302.9963724 302.9963271 302.9963724 302.9965069 302.9967275 302.9970286 302.9974029 302.9978411 302.9983326 302.998865 302.9994254 303 303.0005746 303.001135 303.0016674 303.0021589 303.0025971 303.0029714 303.0032725 303.0034931 303.0036276 303.0036729 303.0040451 303.0039953 303.0038471 303.0036042 303.0032725 303.0028603 303.0023776 303.0018364 303.00125 303.0006328 303 302.9993672 302.99875 302.9981636 302.9976224 302.9971397 302.9967275 302.9963958 302.9961529 302.9960047 302.9959549 302.9960047 302.9961529 302.9963958 302.9967275 302.9971397 302.9976224 302.9981636 302.99875 302.9993672 303 303.0006328 303.00125 303.0018364 303.0023776 303.0028603 303.0032725 303.0036042 303.0038471 303.0039953 303.0040451 303.0043177 303.0042645 303.0041064 303.0038471 303.0034931 303.0030531 303.0025379 303.0019602 303.0013342 303.0006754 303 302.9993246 302.9986658 302.9980398 302.9974621 302.9969469 302.9965069 302.9961529 302.9958936 302.9957355 302.9956823 302.9957355 302.9958936 302.9961529 302.9965069 302.9969469 302.9974621 302.9980398 302.9986658 302.9993246 303 303.0006754 303.0013342 303.0019602 303.0025379 303.0030531 303.0034931 303.0038471 303.0041064 303.0042645 303.0043177 303.004484 303.0044288 303.0042645 303.0039953 303.0036276 303.0031707 303.0026356 303.0020357 303.0013856 303.0007015 303 302.9992985 302.9986144 302.9979643 302.9973644 302.9968293 302.9963724 302.9960047 302.9957355 302.9955712 302.995516 302.9955712 302.9957355 302.9960047 302.9963724 302.9968293 302.9973644 302.9979643 302.9986144 302.9992985 303 303.0007015 303.0013856 303.0020357 303.0026356 303.0031707 303.0036276 303.0039953 303.0042645 303.0044288 303.004484 303.0045399 303.004484 303.0043177 303.0040451 303.0036729 303.0032102 303.0026685 303.0020611 303.0014029 303.0007102 303 302.9992898 302.9985971 302.9979389 302.9973315 302.9967898 302.9963271 302.9959549 302.9956823 302.995516 302.9954601 302.995516 302.9956823 302.9959549 302.9963271 302.9967898 302.9973315 302.9979389 302.9985971 302.9992898 303 303.0007102 303.0014029 303.0020611 303.0026685 303.0032102 303.0036729 303.0040451 303.0043177 303.004484 303.0045399 302.0030902 302.0030521 302.0029389 302.0027534 302.0025 302.0021851 302.0018164 302.0014029 302.0009549 302.0004834 302 301.9995166 301.9990451 301.9985971 301.9981836 301.9978149 301.9975 301.9972466 301.9970611 301.9969479 301.9969098 301.9969479 301.9970611 301.9972466 301.9975 301.9978149 301.9981836 301.9985971 301.9990451 301.9995166 302 302.0004834 302.0009549 302.0014029 302.0018164 302.0021851 302.0025 302.0027534 302.0029389 302.0030521 302.0030902 302.0030521 302.0030145 302.0029027 302.0027195 302.0024692 302.0021582 302.001794 302.0013856 302.0009432 302.0004775 302 301.9995225 301.9990568 301.9986144 301.998206 301.9978418 301.9975308 301.9972805 301.9970973 301.9969855 301.9969479 301.9969855 301.9970973 301.9972805 301.9975308 301.9978418 301.998206 301.9986144 301.9990568 301.9995225 302 302.0004775 302.0009432 302.0013856 302.001794 302.0021582 302.0024692 302.0027195 302.0029027 302.0030145 302.0030521 302.0029389 302.0029027 302.0027951 302.0026186 302.0023776 302.0020781 302.0017275 302.0013342 302.0009082 302.0004597 302 301.9995403 301.9990918 301.9986658 301.9982725 301.9979219 301.9976224 301.9973814 301.9972049 301.9970973 301.9970611 301.9970973 301.9972049 301.9973814 301.9976224 301.9979219 301.9982725 301.9986658 301.9990918 301.9995403 302 302.0004597 302.0009082 302.0013342 302.0017275 302.0020781 302.0023776 302.0026186 302.0027951 302.0029027 302.0029389 302.0027534 302.0027195 302.0026186 302.0024533 302.0022275 302.0019469 302.0016184 302.00125 302.0008508 302.0004307 302 301.9995693 301.9991492 301.99875 301.9983816 301.9980531 301.9977725 301.9975467 301.9973814 301.9972805 301.9972466 301.9972805 301.9973814 301.9975467 301.9977725 301.9980531 301.9983816 301.99875 301.9991492 301.9995693 302 302.0004307 302.0008508 302.00125 302.0016184 302.0019469 302.0022275 302.0024533 302.0026186 302.0027195 302.0027534 302.0025 302.0024692 302.0023776 302.0022275 302.0020225 302.0017678 302.0014695 302.001135 302.0007725 302.0003911 302 301.9996089 301.9992275 301.998865 301.9985305 301.9982322 301.9979775 301.9977725 301.9976224 301.9975308 301.9975 301.9975308 301.9976224 301.9977725 301.9979775 301.9982322 301.9985305 301.998865 301.9992275 301.9996089 302 302.0003911 302.0007725 302.001135 302.0014695 302.0017678 302.0020225 302.0022275 302.0023776 302.0024692 302.0025 302.0021851 302.0021582 302.0020781 302.0019469 302.0017678 302.0015451 302.0012844 302.000992 302.0006752 302.0003418 302 301.9996582 301.9993248 301.999008 301.9987156 301.9984549 301.9982322 301.9980531 301.9979219 301.9978418 301.9978149 301.9978418 301.9979219 301.9980531 301.9982322 301.9984549 301.9987156 301.999008 301.9993248 301.9996582 302 302.0003418 302.0006752 302.000992 302.0012844 302.0015451 302.0017678 302.0019469 302.0020781 302.0021582 302.0021851 302.0018164 302.001794 302.0017275 302.0016184 302.0014695 302.0012844 302.0010676 302.0008246 302.0005613 302.0002841 302 301.9997159 301.9994387 301.9991754 301.9989324 301.9987156 301.9985305 301.9983816 301.9982725 301.998206 301.9981836 301.998206 301.9982725 301.9983816 301.9985305 301.9987156 301.9989324 301.9991754 301.9994387 301.9997159 302 302.0002841 302.0005613 302.0008246 302.0010676 302.0012844 302.0014695 302.0016184 302.0017275 302.001794 302.0018164 302.0014029 302.0013856 302.0013342 302.00125 302.001135 302.000992 302.0008246 302.0006369 302.0004335 302.0002195 302 301.9997805 301.9995665 301.9993631 301.9991754 301.999008 301.998865 301.99875 301.9986658 301.9986144 301.9985971 301.9986144 301.9986658 301.99875 301.998865 301.999008 301.9991754 301.9993631 301.9995665 301.9997805 302 302.0002195 302.0004335 302.0006369 302.0008246 302.000992 302.001135 302.00125 302.0013342 302.0013856 302.0014029 302.0009549 302.0009432 302.0009082 302.0008508 302.0007725 302.0006752 302.0005613 302.0004335 302.0002951 302.0001494 302 301.9998506 301.9997049 301.9995665 301.9994387 301.9993248 301.9992275 301.9991492 301.9990918 301.9990568 301.9990451 301.9990568 301.9990918 301.9991492 301.9992275 301.9993248 301.9994387 301.9995665 301.9997049 301.9998506 302 302.0001494 302.0002951 302.0004335 302.0005613 302.0006752 302.0007725 302.0008508 302.0009082 302.0009432 302.0009549 302.0004834 302.0004775 302.0004597 302.0004307 302.0003911 302.0003418 302.0002841 302.0002195 302.0001494 302.0000756 302 301.9999244 301.9998506 301.9997805 301.9997159 301.9996582 301.9996089 301.9995693 301.9995403 301.9995225 301.9995166 301.9995225 301.9995403 301.9995693 301.9996089 301.9996582 301.9997159 301.9997805 301.9998506 301.9999244 302 302.0000756 302.0001494 302.0002195 302.0002841 302.0003418 302.0003911 302.0004307 302.0004597 302.0004775 302.0004834 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 301.9995166 301.9995225 301.9995403 301.9995693 301.9996089 301.9996582 301.9997159 301.9997805 301.9998506 301.9999244 302 302.0000756 302.0001494 302.0002195 302.0002841 302.0003418 302.0003911 302.0004307 302.0004597 302.0004775 302.0004834 302.0004775 302.0004597 302.0004307 302.0003911 302.0003418 302.0002841 302.0002195 302.0001494 302.0000756 302 301.9999244 301.9998506 301.9997805 301.9997159 301.9996582 301.9996089 301.9995693 301.9995403 301.9995225 301.9995166 301.9990451 301.9990568 301.9990918 301.9991492 301.9992275 301.9993248 301.9994387 301.9995665 301.9997049 301.9998506 302 302.0001494 302.0002951 302.0004335 302.0005613 302.0006752 302.0007725 302.0008508 302.0009082 302.0009432 302.0009549 302.0009432 302.0009082 302.0008508 302.0007725 302.0006752 302.0005613 302.0004335 302.0002951 302.0001494 302 301.9998506 301.9997049 301.9995665 301.9994387 301.9993248 301.9992275 301.9991492 301.9990918 301.9990568 301.9990451 301.9985971 301.9986144 301.9986658 301.99875 301.998865 301.999008 301.9991754 301.9993631 301.9995665 301.9997805 302 302.0002195 302.0004335 302.0006369 302.0008246 302.000992 302.001135 302.00125 302.0013342 302.0013856 302.0014029 302.0013856 302.0013342 302.00125 302.001135 302.000992 302.0008246 302.0006369 302.0004335 302.0002195 302 301.9997805 301.9995665 301.9993631 301.9991754 301.999008 301.998865 301.99875 301.9986658 301.9986144 301.9985971 301.9981836 301.998206 301.9982725 301.9983816 301.9985305 301.9987156 301.9989324 301.9991754 301.9994387 301.9997159 302 302.0002841 302.0005613 302.0008246 302.0010676 302.0012844 302.0014695 302.0016184 302.0017275 302.001794 302.0018164 302.001794 302.0017275 302.0016184 302.0014695 302.0012844 302.0010676 302.0008246 302.0005613 302.0002841 302 301.9997159 301.9994387 301.9991754 301.9989324 301.9987156 301.9985305 301.9983816 301.9982725 301.998206 301.9981836 301.9978149 301.9978418 301.9979219 301.9980531 301.9982322 301.9984549 301.9987156 301.999008 301.9993248 301.9996582 302 302.0003418 302.0006752 302.000992 302.0012844 302.0015451 302.0017678 302.0019469 302.0020781 302.0021582 302.0021851 302.0021582 302.0020781 302.0019469 302.0017678 302.0015451 302.0012844 302.000992 302.0006752 302.0003418 302 301.9996582 301.9993248 301.999008 301.9987156 301.9984549 301.9982322 301.9980531 301.9979219 301.9978418 301.9978149 301.9975 301.9975308 301.9976224 301.9977725 301.9979775 301.9982322 301.9985305 301.998865 301.9992275 301.9996089 302 302.0003911 302.0007725 302.001135 302.0014695 302.0017678 302.0020225 302.0022275 302.0023776 302.0024692 302.0025 302.0024692 302.0023776 302.0022275 302.0020225 302.0017678 302.0014695 302.001135 302.0007725 302.0003911 302 301.9996089 301.9992275 301.998865 301.9985305 301.9982322 301.9979775 301.9977725 301.9976224 301.9975308 301.9975 301.9972466 301.9972805 301.9973814 301.9975467 301.9977725 301.9980531 301.9983816 301.99875 301.9991492 301.9995693 302 302.0004307 302.0008508 302.00125 302.0016184 302.0019469 302.0022275 302.0024533 302.0026186 302.0027195 302.0027534 302.0027195 302.0026186 302.0024533 302.0022275 302.0019469 302.0016184 302.00125 302.0008508 302.0004307 302 301.9995693 301.9991492 301.99875 301.9983816 301.9980531 301.9977725 301.9975467 301.9973814 301.9972805 301.9972466 301.9970611 301.9970973 301.9972049 301.9973814 301.9976224 301.9979219 301.9982725 301.9986658 301.9990918 301.9995403 302 302.0004597 302.0009082 302.0013342 302.0017275 302.0020781 302.0023776 302.0026186 302.0027951 302.0029027 302.0029389 302.0029027 302.0027951 302.0026186 302.0023776 302.0020781 302.0017275 302.0013342 302.0009082 302.0004597 302 301.9995403 301.9990918 301.9986658 301.9982725 301.9979219 301.9976224 301.9973814 301.9972049 301.9970973 301.9970611 301.9969479 301.9969855 301.9970973 301.9972805 301.9975308 301.9978418 301.998206 301.9986144 301.9990568 301.9995225 302 302.0004775 302.0009432 302.0013856 302.001794 302.0021582 302.0024692 302.0027195 302.0029027 302.0030145 302.0030521 302.0030145 302.0029027 302.0027195 302.0024692 302.0021582 302.001794 302.0013856 302.0009432 302.0004775 302 301.9995225 301.9990568 301.9986144 301.998206 301.9978418 301.9975308 301.9972805 301.9970973 301.9969855 301.9969479 301.9969098 301.9969479 301.9970611 301.9972466 301.9975 301.9978149 301.9981836 301.9985971 301.9990451 301.9995166 302 302.0004834 302.0009549 302.0014029 302.0018164 302.0021851 302.0025 302.0027534 302.0029389 302.0030521 302.0030902 302.0030521 302.0029389 302.0027534 302.0025 302.0021851 302.0018164 302.0014029 302.0009549 302.0004834 302 301.9995166 301.9990451 301.9985971 301.9981836 301.9978149 301.9975 301.9972466 301.9970611 301.9969479 301.9969098 301.9969479 301.9969855 301.9970973 301.9972805 301.9975308 301.9978418 301.998206 301.9986144 301.9990568 301.9995225 302 302.0004775 302.0009432 302.0013856 302.001794 302.0021582 302.0024692 302.0027195 302.0029027 302.0030145 302.0030521 302.0030145 302.0029027 302.0027195 302.0024692 302.0021582 302.001794 302.0013856 302.0009432 302.0004775 302 301.9995225 301.9990568 301.9986144 301.998206 301.9978418 301.9975308 301.9972805 301.9970973 301.9969855 301.9969479 301.9970611 301.9970973 301.9972049 301.9973814 301.9976224 301.9979219 301.9982725 301.9986658 301.9990918 301.9995403 302 302.0004597 302.0009082 302.0013342 302.0017275 302.0020781 302.0023776 302.0026186 302.0027951 302.0029027 302.0029389 302.0029027 302.0027951 302.0026186 302.0023776 302.0020781 302.0017275 302.0013342 302.0009082 302.0004597 302 301.9995403 301.9990918 301.9986658 301.9982725 301.9979219 301.9976224 301.9973814 301.9972049 301.9970973 301.9970611 301.9972466 301.9972805 301.9973814 301.9975467 301.9977725 301.9980531 301.9983816 301.99875 301.9991492 301.9995693 302 302.0004307 302.0008508 302.00125 302.0016184 302.0019469 302.0022275 302.0024533 302.0026186 302.0027195 302.0027534 302.0027195 302.0026186 302.0024533 302.0022275 302.0019469 302.0016184 302.00125 302.0008508 302.0004307 302 301.9995693 301.9991492 301.99875 301.9983816 301.9980531 301.9977725 301.9975467 301.9973814 301.9972805 301.9972466 301.9975 301.9975308 301.9976224 301.9977725 301.9979775 301.9982322 301.9985305 301.998865 301.9992275 301.9996089 302 302.0003911 302.0007725 302.001135 302.0014695 302.0017678 302.0020225 302.0022275 302.0023776 302.0024692 302.0025 302.0024692 302.0023776 302.0022275 302.0020225 302.0017678 302.0014695 302.001135 302.0007725 302.0003911 302 301.9996089 301.9992275 301.998865 301.9985305 301.9982322 301.9979775 301.9977725 301.9976224 301.9975308 301.9975 301.9978149 301.9978418 301.9979219 301.9980531 301.9982322 301.9984549 301.9987156 301.999008 301.9993248 301.9996582 302 302.0003418 302.0006752 302.000992 302.0012844 302.0015451 302.0017678 302.0019469 302.0020781 302.0021582 302.0021851 302.0021582 302.0020781 302.0019469 302.0017678 302.0015451 302.0012844 302.000992 302.0006752 302.0003418 302 301.9996582 301.9993248 301.999008 301.9987156 301.9984549 301.9982322 301.9980531 301.9979219 301.9978418 301.9978149 301.9981836 301.998206 301.9982725 301.9983816 301.9985305 301.9987156 301.9989324 301.9991754 301.9994387 301.9997159 302 302.0002841 302.0005613 302.0008246 302.0010676 302.0012844 302.0014695 302.0016184 302.0017275 302.001794 302.0018164 302.001794 302.0017275 302.0016184 302.0014695 302.0012844 302.0010676 302.0008246 302.0005613 302.0002841 302 301.9997159 301.9994387 301.9991754 301.9989324 301.9987156 301.9985305 301.9983816 301.9982725 301.998206 301.9981836 301.9985971 301.9986144 301.9986658 301.99875 301.998865 301.999008 301.9991754 301.9993631 301.9995665 301.9997805 302 302.0002195 302.0004335 302.0006369 302.0008246 302.000992 302.001135 302.00125 302.0013342 302.0013856 302.0014029 302.0013856 302.0013342 302.00125 302.001135 302.000992 302.0008246 302.0006369 302.0004335 302.0002195 302 301.9997805 301.9995665 301.9993631 301.9991754 301.999008 301.998865 301.99875 301.9986658 301.9986144 301.9985971 301.9990451 301.9990568 301.9990918 301.9991492 301.9992275 301.9993248 301.9994387 301.9995665 301.9997049 301.9998506 302 302.0001494 302.0002951 302.0004335 302.0005613 302.0006752 302.0007725 302.0008508 302.0009082 302.0009432 302.0009549 302.0009432 302.0009082 302.0008508 302.0007725 302.0006752 302.0005613 302.0004335 302.0002951 302.0001494 302 301.9998506 301.9997049 301.9995665 301.9994387 301.9993248 301.9992275 301.9991492 301.9990918 301.9990568 301.9990451 301.9995166 301.9995225 301.9995403 301.9995693 301.9996089 301.9996582 301.9997159 301.9997805 301.9998506 301.9999244 302 302.0000756 302.0001494 302.0002195 302.0002841 302.0003418 302.0003911 302.0004307 302.0004597 302.0004775 302.0004834 302.0004775 302.0004597 302.0004307 302.0003911 302.0003418 302.0002841 302.0002195 302.0001494 302.0000756 302 301.9999244 301.9998506 301.9997805 301.9997159 301.9996582 301.9996089 301.9995693 301.9995403 301.9995225 301.9995166 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302 302.0004834 302.0004775 302.0004597 302.0004307 302.0003911 302.0003418 302.0002841 302.0002195 302.0001494 302.0000756 302 301.9999244 301.9998506 301.9997805 301.9997159 301.9996582 301.9996089 301.9995693 301.9995403 301.9995225 301.9995166 301.9995225 301.9995403 301.9995693 301.9996089 301.9996582 301.9997159 301.9997805 301.9998506 301.9999244 302 302.0000756 302.0001494 302.0002195 302.0002841 302.0003418 302.0003911 302.0004307 302.0004597 302.0004775 302.0004834 302.0009549 302.0009432 302.0009082 302.0008508 302.0007725 302.0006752 302.0005613 302.0004335 302.0002951 302.0001494 302 301.9998506 301.9997049 301.9995665 301.9994387 301.9993248 301.9992275 301.9991492 301.9990918 301.9990568 301.9990451 301.9990568 301.9990918 301.9991492 301.9992275 301.9993248 301.9994387 301.9995665 301.9997049 301.9998506 302 302.0001494 302.0002951 302.0004335 302.0005613 302.0006752 302.0007725 302.0008508 302.0009082 302.0009432 302.0009549 302.0014029 302.0013856 302.0013342 302.00125 302.001135 302.000992 302.0008246 302.0006369 302.0004335 302.0002195 302 301.9997805 301.9995665 301.9993631 301.9991754 301.999008 301.998865 301.99875 301.9986658 301.9986144 301.9985971 301.9986144 301.9986658 301.99875 301.998865 301.999008 301.9991754 301.9993631 301.9995665 301.9997805 302 302.0002195 302.0004335 302.0006369 302.0008246 302.000992 302.001135 302.00125 302.0013342 302.0013856 302.0014029 302.0018164 302.001794 302.0017275 302.0016184 302.0014695 302.0012844 302.0010676 302.0008246 302.0005613 302.0002841 302 301.9997159 301.9994387 301.9991754 301.9989324 301.9987156 301.9985305 301.9983816 301.9982725 301.998206 301.9981836 301.998206 301.9982725 301.9983816 301.9985305 301.9987156 301.9989324 301.9991754 301.9994387 301.9997159 302 302.0002841 302.0005613 302.0008246 302.0010676 302.0012844 302.0014695 302.0016184 302.0017275 302.001794 302.0018164 302.0021851 302.0021582 302.0020781 302.0019469 302.0017678 302.0015451 302.0012844 302.000992 302.0006752 302.0003418 302 301.9996582 301.9993248 301.999008 301.9987156 301.9984549 301.9982322 301.9980531 301.9979219 301.9978418 301.9978149 301.9978418 301.9979219 301.9980531 301.9982322 301.9984549 301.9987156 301.999008 301.9993248 301.9996582 302 302.0003418 302.0006752 302.000992 302.0012844 302.0015451 302.0017678 302.0019469 302.0020781 302.0021582 302.0021851 302.0025 302.0024692 302.0023776 302.0022275 302.0020225 302.0017678 302.0014695 302.001135 302.0007725 302.0003911 302 301.9996089 301.9992275 301.998865 301.9985305 301.9982322 301.9979775 301.9977725 301.9976224 301.9975308 301.9975 301.9975308 301.9976224 301.9977725 301.9979775 301.9982322 301.9985305 301.998865 301.9992275 301.9996089 302 302.0003911 302.0007725 302.001135 302.0014695 302.0017678 302.0020225 302.0022275 302.0023776 302.0024692 302.0025 302.0027534 302.0027195 302.0026186 302.0024533 302.0022275 302.0019469 302.0016184 302.00125 302.0008508 302.0004307 302 301.9995693 301.9991492 301.99875 301.9983816 301.9980531 301.9977725 301.9975467 301.9973814 301.9972805 301.9972466 301.9972805 301.9973814 301.9975467 301.9977725 301.9980531 301.9983816 301.99875 301.9991492 301.9995693 302 302.0004307 302.0008508 302.00125 302.0016184 302.0019469 302.0022275 302.0024533 302.0026186 302.0027195 302.0027534 302.0029389 302.0029027 302.0027951 302.0026186 302.0023776 302.0020781 302.0017275 302.0013342 302.0009082 302.0004597 302 301.9995403 301.9990918 301.9986658 301.9982725 301.9979219 301.9976224 301.9973814 301.9972049 301.9970973 301.9970611 301.9970973 301.9972049 301.9973814 301.9976224 301.9979219 301.9982725 301.9986658 301.9990918 301.9995403 302 302.0004597 302.0009082 302.0013342 302.0017275 302.0020781 302.0023776 302.0026186 302.0027951 302.0029027 302.0029389 302.0030521 302.0030145 302.0029027 302.0027195 302.0024692 302.0021582 302.001794 302.0013856 302.0009432 302.0004775 302 301.9995225 301.9990568 301.9986144 301.998206 301.9978418 301.9975308 301.9972805 301.9970973 301.9969855 301.9969479 301.9969855 301.9970973 301.9972805 301.9975308 301.9978418 301.998206 301.9986144 301.9990568 301.9995225 302 302.0004775 302.0009432 302.0013856 302.001794 302.0021582 302.0024692 302.0027195 302.0029027 302.0030145 302.0030521 302.0030902 302.0030521 302.0029389 302.0027534 302.0025 302.0021851 302.0018164 302.0014029 302.0009549 302.0004834 302 301.9995166 301.9990451 301.9985971 301.9981836 301.9978149 301.9975 301.9972466 301.9970611 301.9969479 301.9969098 301.9969479 301.9970611 301.9972466 301.9975 301.9978149 301.9981836 301.9985971 301.9990451 301.9995166 302 302.0004834 302.0009549 302.0014029 302.0018164 302.0021851 302.0025 302.0027534 302.0029389 302.0030521 302.0030902 301.0015643 301.0015451 301.0014878 301.0013938 301.0012656 301.0011062 301.0009195 301.0007102 301.0004834 301.0002447 301 300.9997553 300.9995166 300.9992898 300.9990805 300.9988938 300.9987344 300.9986062 300.9985122 300.9984549 300.9984357 300.9984549 300.9985122 300.9986062 300.9987344 300.9988938 300.9990805 300.9992898 300.9995166 300.9997553 301 301.0002447 301.0004834 301.0007102 301.0009195 301.0011062 301.0012656 301.0013938 301.0014878 301.0015451 301.0015643 301.0015451 301.0015261 301.0014695 301.0013767 301.00125 301.0010925 301.0009082 301.0007015 301.0004775 301.0002417 301 300.9997583 300.9995225 300.9992985 300.9990918 300.9989075 300.99875 300.9986233 300.9985305 300.9984739 300.9984549 300.9984739 300.9985305 300.9986233 300.99875 300.9989075 300.9990918 300.9992985 300.9995225 300.9997583 301 301.0002417 301.0004775 301.0007015 301.0009082 301.0010925 301.00125 301.0013767 301.0014695 301.0015261 301.0015451 301.0014878 301.0014695 301.001415 301.0013256 301.0012036 301.001052 301.0008745 301.0006754 301.0004597 301.0002327 301 300.9997673 300.9995403 300.9993246 300.9991255 300.998948 300.9987964 300.9986744 300.998585 300.9985305 300.9985122 300.9985305 300.998585 300.9986744 300.9987964 300.998948 300.9991255 300.9993246 300.9995403 300.9997673 301 301.0002327 301.0004597 301.0006754 301.0008745 301.001052 301.0012036 301.0013256 301.001415 301.0014695 301.0014878 301.0013938 301.0013767 301.0013256 301.0012419 301.0011276 301.0009856 301.0008193 301.0006328 301.0004307 301.000218 301 300.999782 300.9995693 300.9993672 300.9991807 300.9990144 300.9988724 300.9987581 300.9986744 300.9986233 300.9986062 300.9986233 300.9986744 300.9987581 300.9988724 300.9990144 300.9991807 300.9993672 300.9995693 300.999782 301 301.000218 301.0004307 301.0006328 301.0008193 301.0009856 301.0011276 301.0012419 301.0013256 301.0013767 301.0013938 301.0012656 301.00125 301.0012036 301.0011276 301.0010239 301.0008949 301.0007439 301.0005746 301.0003911 301.000198 301 300.999802 300.9996089 300.9994254 300.9992561 300.9991051 300.9989761 300.9988724 300.9987964 300.99875 300.9987344 300.99875 300.9987964 300.9988724 300.9989761 300.9991051 300.9992561 300.9994254 300.9996089 300.999802 301 301.000198 301.0003911 301.0005746 301.0007439 301.0008949 301.0010239 301.0011276 301.0012036 301.00125 301.0012656 301.0011062 301.0010925 301.001052 301.0009856 301.0008949 301.0007822 301.0006502 301.0005022 301.0003418 301.000173 301 300.999827 300.9996582 300.9994978 300.9993498 300.9992178 300.9991051 300.9990144 300.998948 300.9989075 300.9988938 300.9989075 300.998948 300.9990144 300.9991051 300.9992178 300.9993498 300.9994978 300.9996582 300.999827 301 301.000173 301.0003418 301.0005022 301.0006502 301.0007822 301.0008949 301.0009856 301.001052 301.0010925 301.0011062 301.0009195 301.0009082 301.0008745 301.0008193 301.0007439 301.0006502 301.0005405 301.0004174 301.0002841 301.0001438 301 300.9998562 300.9997159 300.9995826 300.9994595 300.9993498 300.9992561 300.9991807 300.9991255 300.9990918 300.9990805 300.9990918 300.9991255 300.9991807 300.9992561 300.9993498 300.9994595 300.9995826 300.9997159 300.9998562 301 301.0001438 301.0002841 301.0004174 301.0005405 301.0006502 301.0007439 301.0008193 301.0008745 301.0009082 301.0009195 301.0007102 301.0007015 301.0006754 301.0006328 301.0005746 301.0005022 301.0004174 301.0003224 301.0002195 301.0001111 301 300.9998889 300.9997805 300.9996776 300.9995826 300.9994978 300.9994254 300.9993672 300.9993246 300.9992985 300.9992898 300.9992985 300.9993246 300.9993672 300.9994254 300.9994978 300.9995826 300.9996776 300.9997805 300.9998889 301 301.0001111 301.0002195 301.0003224 301.0004174 301.0005022 301.0005746 301.0006328 301.0006754 301.0007015 301.0007102 301.0004834 301.0004775 301.0004597 301.0004307 301.0003911 301.0003418 301.0002841 301.0002195 301.0001494 301.0000756 301 300.9999244 300.9998506 300.9997805 300.9997159 300.9996582 300.9996089 300.9995693 300.9995403 300.9995225 300.9995166 300.9995225 300.9995403 300.9995693 300.9996089 300.9996582 300.9997159 300.9997805 300.9998506 300.9999244 301 301.0000756 301.0001494 301.0002195 301.0002841 301.0003418 301.0003911 301.0004307 301.0004597 301.0004775 301.0004834 301.0002447 301.0002417 301.0002327 301.000218 301.000198 301.000173 301.0001438 301.0001111 301.0000756 301.0000383 301 300.9999617 300.9999244 300.9998889 300.9998562 300.999827 300.999802 300.999782 300.9997673 300.9997583 300.9997553 300.9997583 300.9997673 300.999782 300.999802 300.999827 300.9998562 300.9998889 300.9999244 300.9999617 301 301.0000383 301.0000756 301.0001111 301.0001438 301.000173 301.000198 301.000218 301.0002327 301.0002417 301.0002447 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 300.9997553 300.9997583 300.9997673 300.999782 300.999802 300.999827 300.9998562 300.9998889 300.9999244 300.9999617 301 301.0000383 301.0000756 301.0001111 301.0001438 301.000173 301.000198 301.000218 301.0002327 301.0002417 301.0002447 301.0002417 301.0002327 301.000218 301.000198 301.000173 301.0001438 301.0001111 301.0000756 301.0000383 301 300.9999617 300.9999244 300.9998889 300.9998562 300.999827 300.999802 300.999782 300.9997673 300.9997583 300.9997553 300.9995166 300.9995225 300.9995403 300.9995693 300.9996089 300.9996582 300.9997159 300.9997805 300.9998506 300.9999244 301 301.0000756 301.0001494 301.0002195 301.0002841 301.0003418 301.0003911 301.0004307 301.0004597 301.0004775 301.0004834 301.0004775 301.0004597 301.0004307 301.0003911 301.0003418 301.0002841 301.0002195 301.0001494 301.0000756 301 300.9999244 300.9998506 300.9997805 300.9997159 300.9996582 300.9996089 300.9995693 300.9995403 300.9995225 300.9995166 300.9992898 300.9992985 300.9993246 300.9993672 300.9994254 300.9994978 300.9995826 300.9996776 300.9997805 300.9998889 301 301.0001111 301.0002195 301.0003224 301.0004174 301.0005022 301.0005746 301.0006328 301.0006754 301.0007015 301.0007102 301.0007015 301.0006754 301.0006328 301.0005746 301.0005022 301.0004174 301.0003224 301.0002195 301.0001111 301 300.9998889 300.9997805 300.9996776 300.9995826 300.9994978 300.9994254 300.9993672 300.9993246 300.9992985 300.9992898 300.9990805 300.9990918 300.9991255 300.9991807 300.9992561 300.9993498 300.9994595 300.9995826 300.9997159 300.9998562 301 301.0001438 301.0002841 301.0004174 301.0005405 301.0006502 301.0007439 301.0008193 301.0008745 301.0009082 301.0009195 301.0009082 301.0008745 301.0008193 301.0007439 301.0006502 301.0005405 301.0004174 301.0002841 301.0001438 301 300.9998562 300.9997159 300.9995826 300.9994595 300.9993498 300.9992561 300.9991807 300.9991255 300.9990918 300.9990805 300.9988938 300.9989075 300.998948 300.9990144 300.9991051 300.9992178 300.9993498 300.9994978 300.9996582 300.999827 301 301.000173 301.0003418 301.0005022 301.0006502 301.0007822 301.0008949 301.0009856 301.001052 301.0010925 301.0011062 301.0010925 301.001052 301.0009856 301.0008949 301.0007822 301.0006502 301.0005022 301.0003418 301.000173 301 300.999827 300.9996582 300.9994978 300.9993498 300.9992178 300.9991051 300.9990144 300.998948 300.9989075 300.9988938 300.9987344 300.99875 300.9987964 300.9988724 300.9989761 300.9991051 300.9992561 300.9994254 300.9996089 300.999802 301 301.000198 301.0003911 301.0005746 301.0007439 301.0008949 301.0010239 301.0011276 301.0012036 301.00125 301.0012656 301.00125 301.0012036 301.0011276 301.0010239 301.0008949 301.0007439 301.0005746 301.0003911 301.000198 301 300.999802 300.9996089 300.9994254 300.9992561 300.9991051 300.9989761 300.9988724 300.9987964 300.99875 300.9987344 300.9986062 300.9986233 300.9986744 300.9987581 300.9988724 300.9990144 300.9991807 300.9993672 300.9995693 300.999782 301 301.000218 301.0004307 301.0006328 301.0008193 301.0009856 301.0011276 301.0012419 301.0013256 301.0013767 301.0013938 301.0013767 301.0013256 301.0012419 301.0011276 301.0009856 301.0008193 301.0006328 301.0004307 301.000218 301 300.999782 300.9995693 300.9993672 300.9991807 300.9990144 300.9988724 300.9987581 300.9986744 300.9986233 300.9986062 300.9985122 300.9985305 300.998585 300.9986744 300.9987964 300.998948 300.9991255 300.9993246 300.9995403 300.9997673 301 301.0002327 301.0004597 301.0006754 301.0008745 301.001052 301.0012036 301.0013256 301.001415 301.0014695 301.0014878 301.0014695 301.001415 301.0013256 301.0012036 301.001052 301.0008745 301.0006754 301.0004597 301.0002327 301 300.9997673 300.9995403 300.9993246 300.9991255 300.998948 300.9987964 300.9986744 300.998585 300.9985305 300.9985122 300.9984549 300.9984739 300.9985305 300.9986233 300.99875 300.9989075 300.9990918 300.9992985 300.9995225 300.9997583 301 301.0002417 301.0004775 301.0007015 301.0009082 301.0010925 301.00125 301.0013767 301.0014695 301.0015261 301.0015451 301.0015261 301.0014695 301.0013767 301.00125 301.0010925 301.0009082 301.0007015 301.0004775 301.0002417 301 300.9997583 300.9995225 300.9992985 300.9990918 300.9989075 300.99875 300.9986233 300.9985305 300.9984739 300.9984549 300.9984357 300.9984549 300.9985122 300.9986062 300.9987344 300.9988938 300.9990805 300.9992898 300.9995166 300.9997553 301 301.0002447 301.0004834 301.0007102 301.0009195 301.0011062 301.0012656 301.0013938 301.0014878 301.0015451 301.0015643 301.0015451 301.0014878 301.0013938 301.0012656 301.0011062 301.0009195 301.0007102 301.0004834 301.0002447 301 300.9997553 300.9995166 300.9992898 300.9990805 300.9988938 300.9987344 300.9986062 300.9985122 300.9984549 300.9984357 300.9984549 300.9984739 300.9985305 300.9986233 300.99875 300.9989075 300.9990918 300.9992985 300.9995225 300.9997583 301 301.0002417 301.0004775 301.0007015 301.0009082 301.0010925 301.00125 301.0013767 301.0014695 301.0015261 301.0015451 301.0015261 301.0014695 301.0013767 301.00125 301.0010925 301.0009082 301.0007015 301.0004775 301.0002417 301 300.9997583 300.9995225 300.9992985 300.9990918 300.9989075 300.99875 300.9986233 300.9985305 300.9984739 300.9984549 300.9985122 300.9985305 300.998585 300.9986744 300.9987964 300.998948 300.9991255 300.9993246 300.9995403 300.9997673 301 301.0002327 301.0004597 301.0006754 301.0008745 301.001052 301.0012036 301.0013256 301.001415 301.0014695 301.0014878 301.0014695 301.001415 301.0013256 301.0012036 301.001052 301.0008745 301.0006754 301.0004597 301.0002327 301 300.9997673 300.9995403 300.9993246 300.9991255 300.998948 300.9987964 300.9986744 300.998585 300.9985305 300.9985122 300.9986062 300.9986233 300.9986744 300.9987581 300.9988724 300.9990144 300.9991807 300.9993672 300.9995693 300.999782 301 301.000218 301.0004307 301.0006328 301.0008193 301.0009856 301.0011276 301.0012419 301.0013256 301.0013767 301.0013938 301.0013767 301.0013256 301.0012419 301.0011276 301.0009856 301.0008193 301.0006328 301.0004307 301.000218 301 300.999782 300.9995693 300.9993672 300.9991807 300.9990144 300.9988724 300.9987581 300.9986744 300.9986233 300.9986062 300.9987344 300.99875 300.9987964 300.9988724 300.9989761 300.9991051 300.9992561 300.9994254 300.9996089 300.999802 301 301.000198 301.0003911 301.0005746 301.0007439 301.0008949 301.0010239 301.0011276 301.0012036 301.00125 301.0012656 301.00125 301.0012036 301.0011276 301.0010239 301.0008949 301.0007439 301.0005746 301.0003911 301.000198 301 300.999802 300.9996089 300.9994254 300.9992561 300.9991051 300.9989761 300.9988724 300.9987964 300.99875 300.9987344 300.9988938 300.9989075 300.998948 300.9990144 300.9991051 300.9992178 300.9993498 300.9994978 300.9996582 300.999827 301 301.000173 301.0003418 301.0005022 301.0006502 301.0007822 301.0008949 301.0009856 301.001052 301.0010925 301.0011062 301.0010925 301.001052 301.0009856 301.0008949 301.0007822 301.0006502 301.0005022 301.0003418 301.000173 301 300.999827 300.9996582 300.9994978 300.9993498 300.9992178 300.9991051 300.9990144 300.998948 300.9989075 300.9988938 300.9990805 300.9990918 300.9991255 300.9991807 300.9992561 300.9993498 300.9994595 300.9995826 300.9997159 300.9998562 301 301.0001438 301.0002841 301.0004174 301.0005405 301.0006502 301.0007439 301.0008193 301.0008745 301.0009082 301.0009195 301.0009082 301.0008745 301.0008193 301.0007439 301.0006502 301.0005405 301.0004174 301.0002841 301.0001438 301 300.9998562 300.9997159 300.9995826 300.9994595 300.9993498 300.9992561 300.9991807 300.9991255 300.9990918 300.9990805 300.9992898 300.9992985 300.9993246 300.9993672 300.9994254 300.9994978 300.9995826 300.9996776 300.9997805 300.9998889 301 301.0001111 301.0002195 301.0003224 301.0004174 301.0005022 301.0005746 301.0006328 301.0006754 301.0007015 301.0007102 301.0007015 301.0006754 301.0006328 301.0005746 301.0005022 301.0004174 301.0003224 301.0002195 301.0001111 301 300.9998889 300.9997805 300.9996776 300.9995826 300.9994978 300.9994254 300.9993672 300.9993246 300.9992985 300.9992898 300.9995166 300.9995225 300.9995403 300.9995693 300.9996089 300.9996582 300.9997159 300.9997805 300.9998506 300.9999244 301 301.0000756 301.0001494 301.0002195 301.0002841 301.0003418 301.0003911 301.0004307 301.0004597 301.0004775 301.0004834 301.0004775 301.0004597 301.0004307 301.0003911 301.0003418 301.0002841 301.0002195 301.0001494 301.0000756 301 300.9999244 300.9998506 300.9997805 300.9997159 300.9996582 300.9996089 300.9995693 300.9995403 300.9995225 300.9995166 300.9997553 300.9997583 300.9997673 300.999782 300.999802 300.999827 300.9998562 300.9998889 300.9999244 300.9999617 301 301.0000383 301.0000756 301.0001111 301.0001438 301.000173 301.000198 301.000218 301.0002327 301.0002417 301.0002447 301.0002417 301.0002327 301.000218 301.000198 301.000173 301.0001438 301.0001111 301.0000756 301.0000383 301 300.9999617 300.9999244 300.9998889 300.9998562 300.999827 300.999802 300.999782 300.9997673 300.9997583 300.9997553 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301 301.0002447 301.0002417 301.0002327 301.000218 301.000198 301.000173 301.0001438 301.0001111 301.0000756 301.0000383 301 300.9999617 300.9999244 300.9998889 300.9998562 300.999827 300.999802 300.999782 300.9997673 300.9997583 300.9997553 300.9997583 300.9997673 300.999782 300.999802 300.999827 300.9998562 300.9998889 300.9999244 300.9999617 301 301.0000383 301.0000756 301.0001111 301.0001438 301.000173 301.000198 301.000218 301.0002327 301.0002417 301.0002447 301.0004834 301.0004775 301.0004597 301.0004307 301.0003911 301.0003418 301.0002841 301.0002195 301.0001494 301.0000756 301 300.9999244 300.9998506 300.9997805 300.9997159 300.9996582 300.9996089 300.9995693 300.9995403 300.9995225 300.9995166 300.9995225 300.9995403 300.9995693 300.9996089 300.9996582 300.9997159 300.9997805 300.9998506 300.9999244 301 301.0000756 301.0001494 301.0002195 301.0002841 301.0003418 301.0003911 301.0004307 301.0004597 301.0004775 301.0004834 301.0007102 301.0007015 301.0006754 301.0006328 301.0005746 301.0005022 301.0004174 301.0003224 301.0002195 301.0001111 301 300.9998889 300.9997805 300.9996776 300.9995826 300.9994978 300.9994254 300.9993672 300.9993246 300.9992985 300.9992898 300.9992985 300.9993246 300.9993672 300.9994254 300.9994978 300.9995826 300.9996776 300.9997805 300.9998889 301 301.0001111 301.0002195 301.0003224 301.0004174 301.0005022 301.0005746 301.0006328 301.0006754 301.0007015 301.0007102 301.0009195 301.0009082 301.0008745 301.0008193 301.0007439 301.0006502 301.0005405 301.0004174 301.0002841 301.0001438 301 300.9998562 300.9997159 300.9995826 300.9994595 300.9993498 300.9992561 300.9991807 300.9991255 300.9990918 300.9990805 300.9990918 300.9991255 300.9991807 300.9992561 300.9993498 300.9994595 300.9995826 300.9997159 300.9998562 301 301.0001438 301.0002841 301.0004174 301.0005405 301.0006502 301.0007439 301.0008193 301.0008745 301.0009082 301.0009195 301.0011062 301.0010925 301.001052 301.0009856 301.0008949 301.0007822 301.0006502 301.0005022 301.0003418 301.000173 301 300.999827 300.9996582 300.9994978 300.9993498 300.9992178 300.9991051 300.9990144 300.998948 300.9989075 300.9988938 300.9989075 300.998948 300.9990144 300.9991051 300.9992178 300.9993498 300.9994978 300.9996582 300.999827 301 301.000173 301.0003418 301.0005022 301.0006502 301.0007822 301.0008949 301.0009856 301.001052 301.0010925 301.0011062 301.0012656 301.00125 301.0012036 301.0011276 301.0010239 301.0008949 301.0007439 301.0005746 301.0003911 301.000198 301 300.999802 300.9996089 300.9994254 300.9992561 300.9991051 300.9989761 300.9988724 300.9987964 300.99875 300.9987344 300.99875 300.9987964 300.9988724 300.9989761 300.9991051 300.9992561 300.9994254 300.9996089 300.999802 301 301.000198 301.0003911 301.0005746 301.0007439 301.0008949 301.0010239 301.0011276 301.0012036 301.00125 301.0012656 301.0013938 301.0013767 301.0013256 301.0012419 301.0011276 301.0009856 301.0008193 301.0006328 301.0004307 301.000218 301 300.999782 300.9995693 300.9993672 300.9991807 300.9990144 300.9988724 300.9987581 300.9986744 300.9986233 300.9986062 300.9986233 300.9986744 300.9987581 300.9988724 300.9990144 300.9991807 300.9993672 300.9995693 300.999782 301 301.000218 301.0004307 301.0006328 301.0008193 301.0009856 301.0011276 301.0012419 301.0013256 301.0013767 301.0013938 301.0014878 301.0014695 301.001415 301.0013256 301.0012036 301.001052 301.0008745 301.0006754 301.0004597 301.0002327 301 300.9997673 300.9995403 300.9993246 300.9991255 300.998948 300.9987964 300.9986744 300.998585 300.9985305 300.9985122 300.9985305 300.998585 300.9986744 300.9987964 300.998948 300.9991255 300.9993246 300.9995403 300.9997673 301 301.0002327 301.0004597 301.0006754 301.0008745 301.001052 301.0012036 301.0013256 301.001415 301.0014695 301.0014878 301.0015451 301.0015261 301.0014695 301.0013767 301.00125 301.0010925 301.0009082 301.0007015 301.0004775 301.0002417 301 300.9997583 300.9995225 300.9992985 300.9990918 300.9989075 300.99875 300.9986233 300.9985305 300.9984739 300.9984549 300.9984739 300.9985305 300.9986233 300.99875 300.9989075 300.9990918 300.9992985 300.9995225 300.9997583 301 301.0002417 301.0004775 301.0007015 301.0009082 301.0010925 301.00125 301.0013767 301.0014695 301.0015261 301.0015451 301.0015643 301.0015451 301.0014878 301.0013938 301.0012656 301.0011062 301.0009195 301.0007102 301.0004834 301.0002447 301 300.9997553 300.9995166 300.9992898 300.9990805 300.9988938 300.9987344 300.9986062 300.9985122 300.9984549 300.9984357 300.9984549 300.9985122 300.9986062 300.9987344 300.9988938 300.9990805 300.9992898 300.9995166 300.9997553 301 301.0002447 301.0004834 301.0007102 301.0009195 301.0011062 301.0012656 301.0013938 301.0014878 301.0015451 301.0015643 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/x3D.geos b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/x3D.geos new file mode 100644 index 00000000000..c74ab2b459e --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/x3D.geos @@ -0,0 +1 @@ +0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 105 110 115 120 125 130 135 140 145 150 155 160 165 170 175 180 185 190 195 200 diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/y3D.geos b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/y3D.geos new file mode 100644 index 00000000000..c74ab2b459e --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/y3D.geos @@ -0,0 +1 @@ +0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 105 110 115 120 125 130 135 140 145 150 155 160 165 170 175 180 185 190 195 200 diff --git a/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/z3D.geos b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/z3D.geos new file mode 100644 index 00000000000..8183c473766 --- /dev/null +++ b/inputFiles/singlePhaseFlow/polyhedral/lapwoodConvection_tables/z3D.geos @@ -0,0 +1 @@ +0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 diff --git a/inputFiles/singlePhaseFlow/polyhedral/thermalMixedMFD.ats b/inputFiles/singlePhaseFlow/polyhedral/thermalMixedMFD.ats index f3913e89542..167a6294204 100644 --- a/inputFiles/singlePhaseFlow/polyhedral/thermalMixedMFD.ats +++ b/inputFiles/singlePhaseFlow/polyhedral/thermalMixedMFD.ats @@ -24,7 +24,15 @@ decks = [ TestDeck( name="lapwoodConvection_Ra50_smoke", description= - 'Thermal mixed MFD single phase flow test (onset of natural convection in a porous layer, tetrahedral mesh, consistent inner product, GMRES with the thermal MGR strategy)', + 'Thermal mixed MFD single phase flow test (onset of natural convection in a porous layer, 3D hexahedral mesh, two-point product selected in every cell, GMRES with the thermal MGR strategy)', + partitions=((1, 1, 1), (2, 1, 1), (2, 1, 2)), + restart_step=5, + check_step=10, + restartcheck_params=RestartcheckParameters(**restartcheck_params)), + TestDeck( + name="lapwoodConvection_simplex_Ra50_smoke", + description= + 'Thermal mixed MFD single phase flow test (onset of natural convection in a porous layer, tetrahedral mesh, consistent inner product in every cell, GMRES with the thermal MGR strategy)', partitions=((1, 1, 1), (2, 1, 1), (2, 1, 2)), restart_step=5, check_step=10, From 5c4e218b9dca4a0c3a00e24fb4bf26459f410b59 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Wed, 4 Nov 2026 13:54:00 -0800 Subject: [PATCH 63/64] Retrigger CI From 8a92d105f238ea03c164d535553a62ef2afe4a65 Mon Sep 17 00:00:00 2001 From: Omar Duran Date: Wed, 4 Nov 2026 14:04:00 -0800 Subject: [PATCH 64/64] wip: insert the thermal boundary keys instead of iterating over temporaries --- .../physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp index 8ee3df0f85b..fb4ef0f1437 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/SinglePhaseMixedMFD.cpp @@ -444,11 +444,8 @@ void SinglePhaseMixedMFD::applyFaceBoundaryValues( real64 const time, stdVector< string > keys = { flow::bcPressure::key(), mixedMimetic::bcMassFlux::key() }; if( m_isThermal ) { - for( string const & key : { flow::bcTemperature::key(), mixedMimetic::bcHeatFlux::key(), mixedMimetic::bcHeatTransferCoefficient::key(), - mixedMimetic::bcAmbientTemperature::key(), mixedMimetic::bcEnthalpy::key() } ) - { - keys.push_back( key ); - } + keys.insert( keys.end(), { flow::bcTemperature::key(), mixedMimetic::bcHeatFlux::key(), mixedMimetic::bcHeatTransferCoefficient::key(), + mixedMimetic::bcAmbientTemperature::key(), mixedMimetic::bcEnthalpy::key() } ); } this->forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &,