diff --git a/.integrated_tests.yaml b/.integrated_tests.yaml index 2d289765a28..1026f009fb0 100644 --- a/.integrated_tests.yaml +++ b/.integrated_tests.yaml @@ -1,6 +1,6 @@ baselines: bucket: geosx - baseline: integratedTests/baseline_integratedTests-pr2878-8188-ed2ded2 + baseline: integratedTests/baseline_integratedTests-pr3156-8366-aa71f2a allow_fail: all: '' diff --git a/BASELINE_NOTES.md b/BASELINE_NOTES.md index 65cd8d82bd9..51b738f22fa 100644 --- a/BASELINE_NOTES.md +++ b/BASELINE_NOTES.md @@ -6,6 +6,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 #3156 (2024-10-29) +==================== +Restart check errors due to 1) schema node added to enable thermal option in well model and 2) arrays removed/added for option. Max difference errors due treatment of shutin wells. Previously non-zero rate value reported for shutin well, new code will set rate arrays to zero. + PR #2878 (2024-10-17) ===================== Sorted region cellBlocks names alphabetically. Therefore affected ordering of: faceManager/elemSubRegionList, nodeManager/elemList, nodeManager/elemSubRegionList, SurfaceElementSubRegion::fractureElementsToCellSubRegions, field::perforation::reservoirElementSubregion. diff --git a/src/coreComponents/constitutive/fluid/multifluid/CO2Brine/CO2BrineFluid.hpp b/src/coreComponents/constitutive/fluid/multifluid/CO2Brine/CO2BrineFluid.hpp index 62b6c3a1a15..eb31780f4d4 100644 --- a/src/coreComponents/constitutive/fluid/multifluid/CO2Brine/CO2BrineFluid.hpp +++ b/src/coreComponents/constitutive/fluid/multifluid/CO2Brine/CO2BrineFluid.hpp @@ -386,7 +386,6 @@ CO2BrineFluid< PHASE1, PHASE2, FLASH >::KernelWrapper:: if( m_isThermal ) { - m_phase1.enthalpy.compute( pressure, temperatureInCelsius, phaseCompFraction.value[ip1].toSliceConst(), phaseCompFraction.derivs[ip1].toSliceConst(), diff --git a/src/coreComponents/constitutive/fluid/multifluid/Layouts.hpp b/src/coreComponents/constitutive/fluid/multifluid/Layouts.hpp index 302b8fd9eb0..626f988104e 100644 --- a/src/coreComponents/constitutive/fluid/multifluid/Layouts.hpp +++ b/src/coreComponents/constitutive/fluid/multifluid/Layouts.hpp @@ -30,10 +30,23 @@ namespace geos { namespace constitutive { + +namespace singlefluid +{ +struct DerivativeOffset +{ + /// index of derivative wrt pressure + static integer constexpr dP = 0; + /// index of derivative wrt temperature + static integer constexpr dT = 1; + +}; +} namespace multifluid { /// indices of pressure, temperature, and composition derivatives +// Fix me - if the order is changed the code crashes struct DerivativeOffset { /// index of derivative wrt pressure @@ -44,6 +57,33 @@ struct DerivativeOffset static integer constexpr dC = 2; }; +/// indices of pressure, temperature, and composition derivatives +template< integer NC, integer IS_THERMAL > +struct DerivativeOffsetC {}; + +template< integer NC > +struct DerivativeOffsetC< NC, 1 > +{ + /// index of derivative wrt pressure + static integer constexpr dP = 0; + /// index of derivative wrt temperature + static integer constexpr dT = dP + 1; + /// index of first derivative wrt compositions + static integer constexpr dC = dP+2; + /// number of derivatives + static integer constexpr nDer = NC + 2; +}; +template< integer NC > +struct DerivativeOffsetC< NC, 0 > +{ + /// index of derivative wrt pressure + static integer constexpr dP = 0; + /// index of first derivative wrt compositions + static integer constexpr dC = dP+1; + /// number of derivatives + static integer constexpr nDer = NC + 1; +}; + #if defined( GEOS_USE_DEVICE ) /// Constitutive model phase property array layout diff --git a/src/coreComponents/constitutive/fluid/singlefluid/SingleFluidBase.hpp b/src/coreComponents/constitutive/fluid/singlefluid/SingleFluidBase.hpp index 5b1c159f01f..d5f63c8dedb 100644 --- a/src/coreComponents/constitutive/fluid/singlefluid/SingleFluidBase.hpp +++ b/src/coreComponents/constitutive/fluid/singlefluid/SingleFluidBase.hpp @@ -276,6 +276,14 @@ class SingleFluidBase : public ConstitutiveBase virtual real64 defaultDensity() const = 0; virtual real64 defaultViscosity() const = 0; +/** + * @brief Get the thermal flag. + * @return boolean value indicating whether the model can be used to assemble the energy balance equation or not + * @detail if isThermal is true, the constitutive model compute the enthalpy and internal energy of the phase. + * This can be used to check the compatibility of the constitutive model with the solver + */ + virtual bool isThermal() const { return false; } + protected: virtual void postInputInitialization() override; diff --git a/src/coreComponents/dataRepository/Group.hpp b/src/coreComponents/dataRepository/Group.hpp index 3741580fbbb..1cbc00c739a 100644 --- a/src/coreComponents/dataRepository/Group.hpp +++ b/src/coreComponents/dataRepository/Group.hpp @@ -457,7 +457,6 @@ class Group { using T = std::conditional_t< std::is_const< CONTAINERTYPE >::value, CASTTYPE const, CASTTYPE >; T * const castedContainer = dynamic_cast< T * >( &container ); - if( castedContainer != nullptr ) { lambda( *castedContainer ); @@ -598,6 +597,7 @@ class Group void forSubGroups( LOOKUP_CONTAINER const & subGroupKeys, LAMBDA && lambda ) { localIndex counter = 0; + for( auto const & subgroup : subGroupKeys ) { applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( getGroup( subgroup ), [&]( auto & castedSubGroup ) diff --git a/src/coreComponents/linearAlgebra/CMakeLists.txt b/src/coreComponents/linearAlgebra/CMakeLists.txt index 095544ff4aa..81c3536dfdd 100644 --- a/src/coreComponents/linearAlgebra/CMakeLists.txt +++ b/src/coreComponents/linearAlgebra/CMakeLists.txt @@ -110,6 +110,7 @@ if( ENABLE_HYPRE ) interfaces/hypre/mgrStrategies/SinglePhaseReservoirHybridFVM.hpp interfaces/hypre/mgrStrategies/SolidMechanicsEmbeddedFractures.hpp interfaces/hypre/mgrStrategies/ThermalCompositionalMultiphaseFVM.hpp + interfaces/hypre/mgrStrategies/ThermalCompositionalMultiphaseReservoirFVM.hpp interfaces/hypre/mgrStrategies/ThermalMultiphasePoromechanics.hpp interfaces/hypre/mgrStrategies/ThermalSinglePhasePoromechanics.hpp ) list( APPEND linearAlgebra_sources diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp index a14f0ac6b5b..67de2ca96ae 100644 --- a/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/HypreMGR.cpp @@ -38,6 +38,7 @@ #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseReservoirFVM.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SinglePhaseReservoirHybridFVM.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/ThermalCompositionalMultiphaseFVM.hpp" +#include "linearAlgebra/interfaces/hypre/mgrStrategies/ThermalCompositionalMultiphaseReservoirFVM.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/ThermalSinglePhasePoromechanics.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/ThermalMultiphasePoromechanics.hpp" #include "linearAlgebra/interfaces/hypre/mgrStrategies/SolidMechanicsEmbeddedFractures.hpp" @@ -112,6 +113,11 @@ void hypre::mgr::createMGR( LinearSolverParameters const & params, setStrategy< ThermalCompositionalMultiphaseFVM >( params.mgr, numComponentsPerField, precond, mgrData ); break; } + case LinearSolverParameters::MGR::StrategyType::thermalCompositionalMultiphaseReservoirFVM: + { + setStrategy< ThermalCompositionalMultiphaseReservoirFVM >( params.mgr, numComponentsPerField, precond, mgrData ); + break; + } case LinearSolverParameters::MGR::StrategyType::hybridSinglePhasePoromechanics: { setStrategy< HybridSinglePhasePoromechanics >( params.mgr, numComponentsPerField, precond, mgrData ); diff --git a/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/ThermalCompositionalMultiphaseReservoirFVM.hpp b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/ThermalCompositionalMultiphaseReservoirFVM.hpp new file mode 100644 index 00000000000..81702aa2f8b --- /dev/null +++ b/src/coreComponents/linearAlgebra/interfaces/hypre/mgrStrategies/ThermalCompositionalMultiphaseReservoirFVM.hpp @@ -0,0 +1,131 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file ThermalCompositionalMultiphaseFVM.hpp + */ + +#ifndef GEOS_LINEARALGEBRA_INTERFACES_HYPREMGRTHERMALCOMPOSITIONALMULTIPHASERESERVOIRFVM_HPP_ +#define GEOS_LINEARALGEBRA_INTERFACES_HYPREMGRTHERMALCOMPOSITIONALMULTIPHASERESERVOIRFVM_HPP_ + +#include "linearAlgebra/interfaces/hypre/HypreMGR.hpp" + +namespace geos +{ + +namespace hypre +{ + +namespace mgr +{ + +/** + * @brief ThermalCompositionalMultiphaseReservoirFVM strategy. + * + * Labels description stored in point_marker_array + * 0 = reservoir pressure + * 1 = reservoir density (component 1 ) + * ... = reservoir densities (# components , NC) + * NC + 1 = reservoir temperature + * + * numResLabels - 1 = reservoir temperature + * numResLabels = well pressure + * numResLabels + 1 = well density + * ... = ... (well densities) + * numResLabels + numWellLabels - 3 = last well density + * numResLabels + numWellLabels - 2 = well rate + * numResLabels + numWellLabels - 1 = well temperature + * + * 3-level MGR reduction strategy + * - 1st level: eliminate the well block + * - 2nd level: eliminate the reservoir density associated with the volume constraint + * - 3rd level: eliminate the remaining the reservoir densities + * - The coarse grid (pressure and temperature system) is solved with BoomerAMG with numFunctions==2. + * + */ + +class ThermalCompositionalMultiphaseReservoirFVM : public MGRStrategyBase< 3 > +{ +public: + /** + * @brief Constructor. + * @param numComponentsPerField array with number of components for each field + */ + explicit ThermalCompositionalMultiphaseReservoirFVM( arrayView1d< int const > const & numComponentsPerField ) + : MGRStrategyBase( LvArray::integerConversion< HYPRE_Int >( numComponentsPerField[0] + numComponentsPerField[1] ) ) + { + HYPRE_Int const numResLabels = LvArray::integerConversion< HYPRE_Int >( numComponentsPerField[0] ); + + // Level 0: eliminate the well block + m_labels[0].resize( numResLabels ); + std::iota( m_labels[0].begin(), m_labels[0].end(), 0 ); + + // Level 1: eliminate last density which corresponds to the volume constraint equation + m_labels[1].resize( numResLabels - 2 ); + std::iota( m_labels[1].begin(), m_labels[1].end(), 0 ); + m_labels[1].push_back( numResLabels-1 ); // keep temperature + // Level 2: eliminate the other densities + m_labels[2].push_back( 0 ); // keep pressure + m_labels[2].push_back( numResLabels-1 ); // keep temperature + + setupLabels(); + + // level 0 + m_levelFRelaxType[0] = MGRFRelaxationType::gsElimWInverse; + m_levelFRelaxIters[0] = 1; + m_levelInterpType[0] = MGRInterpolationType::blockJacobi; + 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; // Diagonal scaling (Jacobi) + m_levelRestrictType[1] = MGRRestrictionType::injection; + m_levelCoarseGridMethod[1] = MGRCoarseGridMethod::galerkin; // Standard Galerkin + m_levelGlobalSmootherType[1] = MGRGlobalSmootherType::blockGaussSeidel; + m_levelGlobalSmootherIters[1] = 1; + + m_levelFRelaxType[2] = MGRFRelaxationType::jacobi; + m_levelFRelaxIters[2] = 1; + m_levelInterpType[2] = MGRInterpolationType::injection; // Injection + m_levelRestrictType[2] = MGRRestrictionType::injection; + m_levelCoarseGridMethod[2] = MGRCoarseGridMethod::cprLikeBlockDiag; // Non-Galerkin Quasi-IMPES CPR + m_levelGlobalSmootherType[2] = MGRGlobalSmootherType::ilu0; + m_levelGlobalSmootherIters[2] = 1; + } + + /** + * @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/temperature reduced system + setPressureTemperatureAMG( mgrData.coarseSolver ); + } +}; + +} // namespace mgr + +} // namespace hypre + +} // namespace geos + +#endif /*GEOS_LINEARALGEBRA_INTERFACES_HYPREMGRCOMPOSITIONALMULTIPHASERESERVOIRFVM_HPP_*/ diff --git a/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp b/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp index 90efd13f7ef..7483a1721a9 100644 --- a/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp +++ b/src/coreComponents/linearAlgebra/utilities/LinearSolverParameters.hpp @@ -271,28 +271,29 @@ struct LinearSolverParameters */ enum class StrategyType : integer { - invalid, ///< default value, to ensure solver sets something - singlePhaseReservoirFVM, ///< finite volume single-phase flow with wells - singlePhaseHybridFVM, ///< hybrid finite volume 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 - hybridSinglePhasePoromechanics, ///< single phase poromechanics with hybrid finite volume single phase flow - singlePhasePoromechanicsEmbeddedFractures, ///< single phase poromechanics with FV embedded fractures - singlePhasePoromechanicsConformingFractures, ///< single phase poromechanics with FV conforming fractures - singlePhasePoromechanicsReservoirFVM, ///< single phase poromechanics with finite volume single phase flow with wells - compositionalMultiphaseFVM, ///< finite volume compositional multiphase flow - compositionalMultiphaseHybridFVM, ///< hybrid finite volume compositional multiphase flow - compositionalMultiphaseReservoirFVM, ///< finite volume compositional multiphase flow with wells - compositionalMultiphaseReservoirHybridFVM, ///< hybrid finite volume compositional multiphase flow with wells - reactiveCompositionalMultiphaseOBL, ///< finite volume reactive compositional flow with OBL - thermalCompositionalMultiphaseFVM, ///< finite volume thermal compositional multiphase flow - multiphasePoromechanics, ///< multiphase poromechanics with finite volume compositional multiphase flow - multiphasePoromechanicsReservoirFVM, ///< multiphase poromechanics with finite volume compositional multiphase flow with wells - thermalMultiphasePoromechanics, ///< thermal multiphase poromechanics with finite volume compositional multiphase flow - hydrofracture, ///< hydrofracture - lagrangianContactMechanics, ///< Lagrangian contact mechanics - solidMechanicsEmbeddedFractures ///< Embedded fractures mechanics + invalid, ///< default value, to ensure solver sets something + singlePhaseReservoirFVM, ///< finite volume single-phase flow with wells + singlePhaseHybridFVM, ///< hybrid finite volume 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 + hybridSinglePhasePoromechanics, ///< single phase poromechanics with hybrid finite volume single phase flow + singlePhasePoromechanicsEmbeddedFractures, ///< single phase poromechanics with FV embedded fractures + singlePhasePoromechanicsConformingFractures, ///< single phase poromechanics with FV embedded fractures + singlePhasePoromechanicsReservoirFVM, ///< single phase poromechanics with finite volume single phase flow with wells + compositionalMultiphaseFVM, ///< finite volume compositional multiphase flow + compositionalMultiphaseHybridFVM, ///< hybrid finite volume compositional multiphase flow + compositionalMultiphaseReservoirFVM, ///< finite volume compositional multiphase flow with wells + compositionalMultiphaseReservoirHybridFVM, ///< hybrid finite volume compositional multiphase flow with wells + reactiveCompositionalMultiphaseOBL, ///< finite volume reactive compositional flow with OBL + thermalCompositionalMultiphaseFVM, ///< finite volume thermal compositional multiphase flow + thermalCompositionalMultiphaseReservoirFVM,///< finite volume thermal compositional multiphase flow + multiphasePoromechanics, ///< multiphase poromechanics with finite volume compositional multiphase flow + multiphasePoromechanicsReservoirFVM, ///< multiphase poromechanics with finite volume compositional multiphase flow with wells + thermalMultiphasePoromechanics, ///< thermal multiphase poromechanics with finite volume compositional multiphase flow + hydrofracture, ///< hydrofracture + lagrangianContactMechanics, ///< Lagrangian contact mechanics + solidMechanicsEmbeddedFractures ///< Embedded fractures mechanics }; StrategyType strategy = StrategyType::invalid; ///< Predefined MGR solution strategy (solver specific) @@ -379,6 +380,7 @@ ENUM_STRINGS( LinearSolverParameters::MGR::StrategyType, "compositionalMultiphaseReservoirHybridFVM", "reactiveCompositionalMultiphaseOBL", "thermalCompositionalMultiphaseFVM", + "thermalCompositionalMultiphaseReservoirFVM", "multiphasePoromechanics", "multiphasePoromechanicsReservoirFVM", "thermalMultiphasePoromechanics", diff --git a/src/coreComponents/mesh/PerforationData.cpp b/src/coreComponents/mesh/PerforationData.cpp index e91970382c4..536e03f51ad 100644 --- a/src/coreComponents/mesh/PerforationData.cpp +++ b/src/coreComponents/mesh/PerforationData.cpp @@ -39,6 +39,8 @@ PerforationData::PerforationData( string const & name, Group * const parent ) registerField( fields::perforation::reservoirElementRegion{}, &m_toMeshElements.m_toElementRegion ); registerField( fields::perforation::reservoirElementSubRegion{}, &m_toMeshElements.m_toElementSubRegion ); registerField( fields::perforation::reservoirElementIndex{}, &m_toMeshElements.m_toElementIndex ); + registerField( fields::perforation::reservoirElementGlobalIndex{}, &m_reservoirElementGlobalIndex ); + registerField( fields::perforation::wellElementIndex{}, &m_wellElementIndex ); registerField( fields::perforation::location{}, &m_location ); registerField( fields::perforation::wellTransmissibility{}, &m_wellTransmissibility ); diff --git a/src/coreComponents/mesh/PerforationData.hpp b/src/coreComponents/mesh/PerforationData.hpp index 1071d357d77..f8f2e801c40 100644 --- a/src/coreComponents/mesh/PerforationData.hpp +++ b/src/coreComponents/mesh/PerforationData.hpp @@ -150,6 +150,20 @@ class PerforationData : public ObjectManagerBase */ arrayView1d< localIndex const > getWellElements() const { return m_wellElementIndex; } + /** + * @brief Get perforation-to-reservoir-element connectivity. + * @return list of global reservoir element index connected to each perforation + */ + arrayView1d< globalIndex > getReservoirElementGlobalIndex() { return m_reservoirElementGlobalIndex; } + + + + /** + * @brief Provide an immutable accessor to a const perforation-to-reservoir-element connectivity. + * @return list of well element index connected to each perforation + */ + arrayView1d< globalIndex const > getReservoirElementGlobalIndex() const { return m_reservoirElementGlobalIndex; } + /** * @brief Get perforation locations. @@ -275,6 +289,9 @@ class PerforationData : public ObjectManagerBase /// Indices of the well elements to which perforations are attached array1d< localIndex > m_wellElementIndex; + /// Global indices of reservoir cell containing perforation + array1d< globalIndex > m_reservoirElementGlobalIndex; + /// Location of the perforations array2d< real64 > m_location; diff --git a/src/coreComponents/mesh/PerforationFields.hpp b/src/coreComponents/mesh/PerforationFields.hpp index 8d376689a30..263c7254078 100644 --- a/src/coreComponents/mesh/PerforationFields.hpp +++ b/src/coreComponents/mesh/PerforationFields.hpp @@ -58,6 +58,14 @@ DECLARE_FIELD( reservoirElementIndex, WRITE_AND_READ, "For each perforation, element index of the perforated element" ); +DECLARE_FIELD( reservoirElementGlobalIndex, + "reservoirElementGlobalIndex", + array1d< globalIndex >, + 0, + NOPLOT, + WRITE_AND_READ, + "For each perforation, global element index of the perforated element" ); + DECLARE_FIELD( wellElementIndex, "wellElementIndex", array1d< localIndex >, diff --git a/src/coreComponents/mesh/WellElementSubRegion.cpp b/src/coreComponents/mesh/WellElementSubRegion.cpp index 107dcfe80fc..daf53c67621 100644 --- a/src/coreComponents/mesh/WellElementSubRegion.cpp +++ b/src/coreComponents/mesh/WellElementSubRegion.cpp @@ -148,6 +148,7 @@ void collectElementNodes( CellElementSubRegion const & subRegion, * @param[inout] erMatched the region index of the reservoir element that contains "location", if any * @param[inout] esrMatched the subregion index of the reservoir element that contains "location", if any * @param[inout] eiMatched the element index of the reservoir element that contains "location", if any + * @param[inout] giMatched the element global index of the reservoir element that contains "location", if any */ bool visitNeighborElements( MeshLevel const & mesh, real64 const (&location)[3], @@ -155,7 +156,8 @@ bool visitNeighborElements( MeshLevel const & mesh, SortedArray< globalIndex > & elements, localIndex & erMatched, localIndex & esrMatched, - localIndex & eiMatched ) + localIndex & eiMatched, + globalIndex & giMatched ) { ElementRegionManager const & elemManager = mesh.getElemManager(); NodeManager const & nodeManager = mesh.getNodeManager(); @@ -164,7 +166,6 @@ bool visitNeighborElements( MeshLevel const & mesh, ArrayOfArraysView< localIndex const > const & toElementRegionList = nodeManager.elementRegionList(); ArrayOfArraysView< localIndex const > const & toElementSubRegionList = nodeManager.elementSubRegionList(); ArrayOfArraysView< localIndex const > const & toElementList = nodeManager.elementList(); - arrayView2d< real64 const, nodes::REFERENCE_POSITION_USD > const referencePosition = nodeManager.referencePosition().toViewConst(); @@ -183,7 +184,7 @@ bool visitNeighborElements( MeshLevel const & mesh, // that contains only the nodes that have already been visited // the newly added nodes will be added to "nodes" SortedArray< localIndex > currNodes = nodes; - + giMatched = -1; // for all the nodes already visited for( localIndex currNode : currNodes ) { @@ -193,7 +194,6 @@ bool visitNeighborElements( MeshLevel const & mesh, localIndex const er = toElementRegionList[currNode][b]; localIndex const esr = toElementSubRegionList[currNode][b]; localIndex const eiLocal = toElementList[currNode][b]; - CellElementRegion const & region = elemManager.getRegion< CellElementRegion >( er ); CellElementSubRegion const & subRegion = region.getSubRegion< CellElementSubRegion >( esr ); arrayView2d< localIndex const > const elemsToFaces = subRegion.faceList(); @@ -221,6 +221,7 @@ bool visitNeighborElements( MeshLevel const & mesh, erMatched = er; esrMatched = esr; eiMatched = eiLocal; + giMatched = eiGlobal; matched = true; break; } @@ -292,6 +293,7 @@ void initializeLocalSearch( MeshLevel const & mesh, * @param[inout] erMatched the region index of the reservoir element that contains "location", if any * @param[inout] esrMatched the subregion index of the reservoir element that contains "location", if any * @param[inout] eiMatched the element index of the reservoir element that contains "location", if any + * @param[inout] giMatched the element global index of the reservoir element that contains "location", if any */ bool searchLocalElements( MeshLevel const & mesh, real64 const (&location)[3], @@ -301,7 +303,8 @@ bool searchLocalElements( MeshLevel const & mesh, localIndex const & eiInit, localIndex & erMatched, localIndex & esrMatched, - localIndex & eiMatched ) + localIndex & eiMatched, + globalIndex & giMatched ) { // search locally, starting from the location of the previous perforation // the assumption here is that perforations have been entered in order of depth @@ -324,7 +327,6 @@ bool searchLocalElements( MeshLevel const & mesh, // collect the nodes of the current element // they will be used to access the neighbors and check if they contain the perforation collectElementNodes( subRegion, eiInit, nodes ); - // if no match is found, enlarge the neighborhood m_searchDepth'th times for( localIndex d = 0; d < searchDepth; ++d ) { @@ -334,7 +336,7 @@ bool searchLocalElements( MeshLevel const & mesh, // stop if a reservoir element containing the perforation is found // if not, enlarge the set "nodes" resElemFound = visitNeighborElements( mesh, location, nodes, elements, - erMatched, esrMatched, eiMatched ); + erMatched, esrMatched, eiMatched, giMatched ); if( resElemFound || nNodes == nodes.size()) { break; @@ -433,6 +435,14 @@ void WellElementSubRegion::generate( MeshLevel & mesh, // this assumes that the elemToNodes maps has been filled at Step 5) updateNodeManagerNodeToElementMap( mesh ); + // Store local to global index mapping + integer n_localElems = localElems.size(); + m_globalWellElementIndex.resize( n_localElems ); + for( integer i=0; i getGlobalWellElementIndex() const + { + return m_globalWellElementIndex; + } /** * @brief Set the name of the WellControls object of this well. * @param[in] name the name of the WellControls object @@ -394,6 +402,9 @@ class WellElementSubRegion : public ElementSubRegionBase /// Element-to-node relation is one to one relation. NodeMapType m_toNodesRelation; + /// Local indices of the next well element (used in solvers) + array1d< globalIndex > m_globalWellElementIndex; + /// Local indices of the next well element (used in solvers) array1d< localIndex > m_nextWellElementIndex; diff --git a/src/coreComponents/physicsSolvers/KernelLaunchSelectors.hpp b/src/coreComponents/physicsSolvers/KernelLaunchSelectors.hpp new file mode 100644 index 00000000000..6a07386bd79 --- /dev/null +++ b/src/coreComponents/physicsSolvers/KernelLaunchSelectors.hpp @@ -0,0 +1,174 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file KernelLaunchSelectors.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_KERNELLAUNCHSELECTORS_HPP +#define GEOS_PHYSICSSOLVERS_KERNELLAUNCHSELECTORS_HPP + +namespace geos +{ +namespace internal +{ + +template< typename S, typename T, typename LAMBDA > +void invokePhaseDispatchLambda ( S val, T numPhases, LAMBDA && lambda ) +{ + if( numPhases == 1 ) + { + lambda( val, std::integral_constant< T, 1 >()); + return; + } + else if( numPhases == 2 ) + { + lambda( val, std::integral_constant< T, 2 >()); + return; + } + else if( numPhases == 3 ) + { + lambda( val, std::integral_constant< T, 3 >()); + return; + } + else + { + GEOS_ERROR( "Unsupported state: " << numPhases ); + } +} + +template< typename S, typename T, typename LAMBDA > +void invokeThermalDispatchLambda ( S val, T isThermal, LAMBDA && lambda ) +{ + if( isThermal == 1 ) + { + lambda( val, std::integral_constant< T, 1 >()); + return; + } + else if( isThermal == 0 ) + { + lambda( val, std::integral_constant< T, 0 >()); + return; + } + else + { + GEOS_ERROR( "Unsupported state: " << isThermal ); + } +} + +template< typename T, typename LAMBDA > +void kernelLaunchSelectorThermalSwitch( T value, LAMBDA && lambda ) +{ + static_assert( std::is_integral< T >::value, "kernelLaunchSelectorThermalSwitch: type should be integral" ); + + switch( value ) + { + case 0: + { + lambda( std::integral_constant< T, 0 >() ); + return; + } + case 1: + { + lambda( std::integral_constant< T, 1 >() ); + return; + } + default: + { + GEOS_ERROR( "Unsupported thermal state: " << value ); + } + } +} + +template< typename T, typename LAMBDA > +void kernelLaunchSelectorCompThermSwitch( T value, bool const isThermal, LAMBDA && lambda ) +{ + static_assert( std::is_integral< T >::value, "kernelLaunchSelectorCompSwitch: value type should be integral" ); + + + switch( value ) + { + case 1: + { + invokeThermalDispatchLambda( std::integral_constant< T, 1 >(), isThermal, lambda ); return; + } + case 2: + { + invokeThermalDispatchLambda( std::integral_constant< T, 2 >(), isThermal, lambda ); + return; + } + case 3: + { + invokeThermalDispatchLambda( std::integral_constant< T, 3 >(), isThermal, lambda ); + return; + } + case 4: + { + invokeThermalDispatchLambda( std::integral_constant< T, 4 >(), isThermal, lambda ); + return; + } + case 5: + { + invokeThermalDispatchLambda( std::integral_constant< T, 5 >(), isThermal, lambda ); + return; + } + default: + { + GEOS_ERROR( "Unsupported number of components: " << value ); + } + } +} + +template< typename T, typename LAMBDA > +void kernelLaunchSelectorCompPhaseSwitch( T value, T n_phase, LAMBDA && lambda ) +{ + static_assert( std::is_integral< T >::value, "kernelLaunchSelectorCompSwitch: value type should be integral" ); + switch( value ) + { + case 1: + { + invokePhaseDispatchLambda( std::integral_constant< T, 1 >(), n_phase, lambda ); + return; + } + case 2: + { + invokePhaseDispatchLambda( std::integral_constant< T, 2 >(), n_phase, lambda ); + return; + } + case 3: + { + invokePhaseDispatchLambda( std::integral_constant< T, 3 >(), n_phase, lambda ); + return; + } + case 4: + { + invokePhaseDispatchLambda( std::integral_constant< T, 4 >(), n_phase, lambda ); + return; + } + case 5: + { + invokePhaseDispatchLambda( std::integral_constant< T, 5 >(), n_phase, lambda ); + return; + } + default: + { GEOS_ERROR( "Unsupported number of components: " << value ); } + } +} + + +} // end namspace internal +} // end namespace geos + + +#endif // GEOS_PHYSICSSOLVERS_KERNELLAUNCHSELECTORS_HPP diff --git a/src/coreComponents/physicsSolvers/SolverStatistics.cpp b/src/coreComponents/physicsSolvers/SolverStatistics.cpp index 4fce578ebc0..cf3eaf44f14 100644 --- a/src/coreComponents/physicsSolvers/SolverStatistics.cpp +++ b/src/coreComponents/physicsSolvers/SolverStatistics.cpp @@ -38,7 +38,6 @@ SolverStatistics::SolverStatistics( string const & name, Group * const parent ) setApplyDefaultValue( 0 ). setDescription( "Number of time step cuts" ); - registerWrapper( viewKeyStruct::numSuccessfulOuterLoopIterationsString(), &m_numSuccessfulOuterLoopIterations ). setApplyDefaultValue( 0 ). setDescription( "Cumulative number of successful outer loop iterations" ); diff --git a/src/coreComponents/physicsSolvers/SolverStatistics.hpp b/src/coreComponents/physicsSolvers/SolverStatistics.hpp index 64a173a0395..ce8293dbcc0 100644 --- a/src/coreComponents/physicsSolvers/SolverStatistics.hpp +++ b/src/coreComponents/physicsSolvers/SolverStatistics.hpp @@ -128,7 +128,6 @@ class SolverStatistics : public dataRepository::Group integer getNumDiscardedLinearIterations() const { return m_numDiscardedLinearIterations; } -private: /** * @brief Struct to serve as a container for variable strings and keys. @@ -156,6 +155,7 @@ class SolverStatistics : public dataRepository::Group static constexpr char const * numDiscardedLinearIterationsString() { return "numDiscardedLinearIterations"; } }; +private: /// Number of time steps integer m_numTimeSteps; diff --git a/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseBase.cpp b/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseBase.cpp index 24bfc0dbff0..d863e690cc4 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseBase.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseBase.cpp @@ -280,6 +280,7 @@ void CompositionalMultiphaseBase::registerDataOnMesh( Group & meshBodies ) MultiFluidBase const & referenceFluid = cm.getConstitutiveRelation< MultiFluidBase >( m_referenceFluidModelName ); m_numPhases = referenceFluid.numFluidPhases(); m_numComponents = referenceFluid.numFluidComponents(); + m_isThermal = referenceFluid.isThermal(); } // n_c components + one pressure ( + one temperature if needed ) @@ -2266,7 +2267,6 @@ void CompositionalMultiphaseBase::computeCFLNumbers( geos::DomainPartition & dom fluxApprox.forAllStencils( mesh, [&] ( auto & stencil ) { - typename TYPEOFREF( stencil ) ::KernelWrapper stencilWrapper = stencil.createKernelWrapper(); // While this kernel is waiting for a factory class, pass all the accessors here diff --git a/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseFVM.cpp b/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseFVM.cpp index d85079eb757..3f850dcd1a5 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseFVM.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseFVM.cpp @@ -13,6 +13,8 @@ * ------------------------------------------------------------------------------------------------------------ */ + + /** * @file CompositionalMultiphaseFVM.cpp */ @@ -486,6 +488,14 @@ real64 CompositionalMultiphaseFVM::scalingForSystemSolution( DomainPartition & d [&]( localIndex const, ElementSubRegionBase & subRegion ) { + arrayView1d< real64 const > const pressure = subRegion.getField< fields::flow::pressure >(); + arrayView1d< real64 const > const temperature = subRegion.getField< fields::flow::temperature >(); + arrayView2d< real64 const, compflow::USD_COMP > const compDens = subRegion.getField< fields::flow::globalCompDensity >(); + arrayView1d< real64 > pressureScalingFactor = subRegion.getField< fields::flow::pressureScalingFactor >(); + arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< fields::flow::temperatureScalingFactor >(); + arrayView1d< real64 > compDensScalingFactor = subRegion.getField< fields::flow::globalCompDensityScalingFactor >(); + + const integer temperatureOffset = m_numComponents+1; auto const subRegionData = m_isThermal ? thermalCompositionalMultiphaseBaseKernels:: @@ -495,17 +505,28 @@ real64 CompositionalMultiphaseFVM::scalingForSystemSolution( DomainPartition & d m_maxRelativeTempChange, m_maxCompFracChange, m_maxRelativeCompDensChange, + pressure, + temperature, + compDens, + pressureScalingFactor, + compDensScalingFactor, + temperatureScalingFactor, dofManager.rankOffset(), m_numComponents, dofKey, subRegion, - localSolution ) + localSolution, + temperatureOffset ) : isothermalCompositionalMultiphaseBaseKernels:: ScalingForSystemSolutionKernelFactory:: createAndLaunch< parallelDevicePolicy<> >( m_maxRelativePresChange, m_maxAbsolutePresChange, m_maxCompFracChange, m_maxRelativeCompDensChange, + pressure, + compDens, + pressureScalingFactor, + compDensScalingFactor, dofManager.rankOffset(), m_numComponents, dofKey, @@ -578,8 +599,18 @@ bool CompositionalMultiphaseFVM::checkSystemSolution( DomainPartition & domain, [&]( localIndex const, ElementSubRegionBase & subRegion ) { + arrayView1d< real64 const > const pressure = + subRegion.getField< fields::flow::pressure >(); + arrayView1d< real64 const > const temperature = + subRegion.getField< fields::flow::temperature >(); + arrayView2d< real64 const, compflow::USD_COMP > const compDens = + subRegion.getField< fields::flow::globalCompDensity >(); + arrayView1d< real64 > pressureScalingFactor = subRegion.getField< fields::flow::pressureScalingFactor >(); + arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< fields::flow::temperatureScalingFactor >(); + arrayView1d< real64 > compDensScalingFactor = subRegion.getField< fields::flow::globalCompDensityScalingFactor >(); // check that pressure and component densities are non-negative // for thermal, check that temperature is above 273.15 K + const integer temperatureOffset = m_numComponents+1; auto const subRegionData = m_isThermal ? thermalCompositionalMultiphaseBaseKernels:: @@ -588,17 +619,30 @@ bool CompositionalMultiphaseFVM::checkSystemSolution( DomainPartition & domain, m_allowNegativePressure, m_scalingType, scalingFactor, + pressure, + temperature, + compDens, + pressureScalingFactor, + temperatureScalingFactor, + compDensScalingFactor, dofManager.rankOffset(), m_numComponents, dofKey, subRegion, - localSolution ) + localSolution, + temperatureOffset ) : isothermalCompositionalMultiphaseBaseKernels:: SolutionCheckKernelFactory:: createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping, m_allowNegativePressure, m_scalingType, scalingFactor, + pressure, + + compDens, + pressureScalingFactor, + + compDensScalingFactor, dofManager.rankOffset(), m_numComponents, dofKey, diff --git a/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseHybridFVM.cpp b/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseHybridFVM.cpp index dc4b75cfa27..70f9ba9ae8b 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseHybridFVM.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/CompositionalMultiphaseHybridFVM.cpp @@ -446,6 +446,10 @@ real64 CompositionalMultiphaseHybridFVM::scalingForSystemSolution( DomainPartiti mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, [&]( localIndex const, ElementSubRegionBase & subRegion ) { + arrayView1d< real64 const > const pressure = subRegion.getField< fields::flow::pressure >(); + arrayView2d< real64 const, compflow::USD_COMP > const compDens = subRegion.getField< fields::flow::globalCompDensity >(); + arrayView1d< real64 > pressureScalingFactor = subRegion.getField< fields::flow::pressureScalingFactor >(); + arrayView1d< real64 > compDensScalingFactor = subRegion.getField< fields::flow::globalCompDensityScalingFactor >(); auto const subRegionData = isothermalCompositionalMultiphaseBaseKernels:: ScalingForSystemSolutionKernelFactory:: @@ -453,6 +457,10 @@ real64 CompositionalMultiphaseHybridFVM::scalingForSystemSolution( DomainPartiti m_maxAbsolutePresChange, m_maxCompFracChange, m_maxRelativeCompDensChange, + pressure, + compDens, + pressureScalingFactor, + compDensScalingFactor, dofManager.rankOffset(), m_numComponents, dofKey, @@ -519,6 +527,13 @@ bool CompositionalMultiphaseHybridFVM::checkSystemSolution( DomainPartition & do mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, [&]( localIndex const, ElementSubRegionBase & subRegion ) { + arrayView1d< real64 const > const pressure = + subRegion.getField< fields::flow::pressure >(); + arrayView2d< real64 const, compflow::USD_COMP > const compDens = + subRegion.getField< fields::flow::globalCompDensity >(); + arrayView1d< real64 > pressureScalingFactor = subRegion.getField< fields::flow::pressureScalingFactor >(); + arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< fields::flow::temperatureScalingFactor >(); + arrayView1d< real64 > compDensScalingFactor = subRegion.getField< fields::flow::globalCompDensityScalingFactor >(); // check that pressure and component densities are non-negative auto const subRegionData = isothermalCompositionalMultiphaseBaseKernels:: @@ -527,6 +542,10 @@ bool CompositionalMultiphaseHybridFVM::checkSystemSolution( DomainPartition & do m_allowNegativePressure, CompositionalMultiphaseFVM::ScalingType::Global, scalingFactor, + pressure, + compDens, + pressureScalingFactor, + compDensScalingFactor, dofManager.rankOffset(), m_numComponents, elemDofKey, diff --git a/src/coreComponents/physicsSolvers/fluidFlow/IsothermalCompositionalMultiphaseBaseKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/IsothermalCompositionalMultiphaseBaseKernels.hpp index f2edf8c99ed..a7541e66e4f 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/IsothermalCompositionalMultiphaseBaseKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/IsothermalCompositionalMultiphaseBaseKernels.hpp @@ -34,10 +34,12 @@ #include "physicsSolvers/fluidFlow/CompositionalMultiphaseUtilities.hpp" #include "physicsSolvers/SolverBaseKernels.hpp" #include "physicsSolvers/fluidFlow/CompositionalMultiphaseFVM.hpp" +#include "physicsSolvers/KernelLaunchSelectors.hpp" namespace geos { + namespace isothermalCompositionalMultiphaseBaseKernels { @@ -113,6 +115,8 @@ class PropertyKernelBase namespace internal { + + template< typename T, typename LAMBDA > void kernelLaunchSelectorCompSwitch( T value, LAMBDA && lambda ) { @@ -1461,20 +1465,17 @@ class ScalingForSystemSolutionKernelFactory real64 const maxAbsolutePresChange, real64 const maxCompFracChange, real64 const maxRelativeCompDensChange, + arrayView1d< real64 const > const pressure, + arrayView2d< real64 const, compflow::USD_COMP > const compDens, + arrayView1d< real64 > pressureScalingFactor, + arrayView1d< real64 > compDensScalingFactor, globalIndex const rankOffset, integer const numComp, string const dofKey, ElementSubRegionBase & subRegion, arrayView1d< real64 const > const localSolution ) { - arrayView1d< real64 const > const pressure = - subRegion.getField< fields::flow::pressure >(); - arrayView2d< real64 const, compflow::USD_COMP > const compDens = - subRegion.getField< fields::flow::globalCompDensity >(); - arrayView1d< real64 > pressureScalingFactor = - subRegion.getField< fields::flow::pressureScalingFactor >(); - arrayView1d< real64 > compDensScalingFactor = - subRegion.getField< fields::flow::globalCompDensityScalingFactor >(); + ScalingForSystemSolutionKernel kernel( maxRelativePresChange, maxAbsolutePresChange, maxCompFracChange, maxRelativeCompDensChange, rankOffset, numComp, dofKey, subRegion, localSolution, pressure, compDens, pressureScalingFactor, compDensScalingFactor ); return ScalingForSystemSolutionKernel::launch< POLICY >( subRegion.size(), kernel ); @@ -1516,15 +1517,15 @@ class SolutionCheckKernel : public ScalingAndCheckingSystemSolutionKernelBase< i integer const allowNegativePressure, CompositionalMultiphaseFVM::ScalingType const scalingType, real64 const scalingFactor, + arrayView1d< real64 const > const pressure, + arrayView2d< real64 const, compflow::USD_COMP > const compDens, + arrayView1d< real64 > pressureScalingFactor, + arrayView1d< real64 > compDensScalingFactor, globalIndex const rankOffset, integer const numComp, string const dofKey, ElementSubRegionBase const & subRegion, - arrayView1d< real64 const > const localSolution, - arrayView1d< real64 const > const pressure, - arrayView2d< real64 const, compflow::USD_COMP > const compDens, - arrayView1d< real64 > pressureScalingFactor, - arrayView1d< real64 > compDensScalingFactor ) + arrayView1d< real64 const > const localSolution ) : Base( rankOffset, numComp, dofKey, @@ -1761,22 +1762,20 @@ class SolutionCheckKernelFactory integer const allowNegativePressure, CompositionalMultiphaseFVM::ScalingType const scalingType, real64 const scalingFactor, + arrayView1d< real64 const > const pressure, + arrayView2d< real64 const, compflow::USD_COMP > const compDens, + arrayView1d< real64 > pressureScalingFactor, + arrayView1d< real64 > compDensScalingFactor, globalIndex const rankOffset, integer const numComp, string const dofKey, ElementSubRegionBase & subRegion, arrayView1d< real64 const > const localSolution ) { - arrayView1d< real64 const > const pressure = - subRegion.getField< fields::flow::pressure >(); - arrayView2d< real64 const, compflow::USD_COMP > const compDens = - subRegion.getField< fields::flow::globalCompDensity >(); - arrayView1d< real64 > pressureScalingFactor = - subRegion.getField< fields::flow::pressureScalingFactor >(); - arrayView1d< real64 > compDensScalingFactor = - subRegion.getField< fields::flow::globalCompDensityScalingFactor >(); - SolutionCheckKernel kernel( allowCompDensChopping, allowNegativePressure, scalingType, scalingFactor, rankOffset, - numComp, dofKey, subRegion, localSolution, pressure, compDens, pressureScalingFactor, compDensScalingFactor ); + + SolutionCheckKernel kernel( allowCompDensChopping, allowNegativePressure, scalingType, scalingFactor, + pressure, compDens, pressureScalingFactor, compDensScalingFactor, rankOffset, + numComp, dofKey, subRegion, localSolution ); return SolutionCheckKernel::launch< POLICY >( subRegion.size(), kernel ); } @@ -2410,6 +2409,16 @@ struct HydrostaticPressureKernel /******************************** Kernel launch machinery ********************************/ + +template< typename KERNELWRAPPER, typename ... ARGS > +void KernelLaunchSelectorCompTherm( integer const numComp, bool const isThermal, ARGS && ... args ) +{ + geos::internal::kernelLaunchSelectorCompThermSwitch( numComp, isThermal, [&] ( auto NC, auto ISTHERMAL ) + { + KERNELWRAPPER::template launch< NC(), ISTHERMAL() >( std::forward< ARGS >( args )... ); + } ); +} + template< typename KERNELWRAPPER, typename ... ARGS > void KernelLaunchSelector1( integer const numComp, ARGS && ... args ) { @@ -2443,6 +2452,54 @@ void KernelLaunchSelector2( integer const numComp, integer const numPhase, ARGS } } +template< typename KERNELWRAPPER, typename ... ARGS > +void KernelLaunchSelector_NC_NP_THERM( integer const numComp, integer const numPhase, integer const isThermal, ARGS && ... args ) +{ + // Ideally this would be inside the dispatch, but it breaks on Summit with GCC 9.1.0 and CUDA 11.0.3. + if( isThermal ) + { + if( numPhase == 2 ) + { + internal::kernelLaunchSelectorCompSwitch( numComp, [&] ( auto NC ) + { + KERNELWRAPPER::template launch< NC(), 2, 1 >( std::forward< ARGS >( args ) ... ); + } ); + } + else if( numPhase == 3 ) + { + internal::kernelLaunchSelectorCompSwitch( numComp, [&] ( auto NC ) + { + KERNELWRAPPER::template launch< NC(), 3, 1 >( std::forward< ARGS >( args ) ... ); + } ); + } + else + { + GEOS_ERROR( "Unsupported number of phases: " << numPhase ); + } + } + else + { + if( numPhase == 2 ) + { + internal::kernelLaunchSelectorCompSwitch( numComp, [&] ( auto NC ) + { + KERNELWRAPPER::template launch< NC(), 2, 0 >( std::forward< ARGS >( args ) ... ); + } ); + } + else if( numPhase == 3 ) + { + internal::kernelLaunchSelectorCompSwitch( numComp, [&] ( auto NC ) + { + KERNELWRAPPER::template launch< NC(), 3, 0 >( std::forward< ARGS >( args ) ... ); + } ); + } + else + { + GEOS_ERROR( "Unsupported number of phases: " << numPhase ); + } + } +} + } // namespace isothermalCompositionalMultiphaseBaseKernels } // namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/ThermalCompositionalMultiphaseBaseKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/ThermalCompositionalMultiphaseBaseKernels.hpp index d39b7f51ddc..f4f5efc15a6 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/ThermalCompositionalMultiphaseBaseKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/ThermalCompositionalMultiphaseBaseKernels.hpp @@ -552,7 +552,8 @@ class ScalingForSystemSolutionKernel : public isothermalCompositionalMultiphaseB arrayView2d< real64 const, compflow::USD_COMP > const compDens, arrayView1d< real64 > pressureScalingFactor, arrayView1d< real64 > compDensScalingFactor, - arrayView1d< real64 > temperatureScalingFactor ) + arrayView1d< real64 > temperatureScalingFactor, + integer const temperatureOffset ) : Base( maxRelativePresChange, maxAbsolutePresChange, maxCompFracChange, @@ -568,7 +569,8 @@ class ScalingForSystemSolutionKernel : public isothermalCompositionalMultiphaseB compDensScalingFactor ), m_maxRelativeTempChange( maxRelativeTempChange ), m_temperature( temperature ), - m_temperatureScalingFactor( temperatureScalingFactor ) + m_temperatureScalingFactor( temperatureScalingFactor ), + m_temperatureOffset( temperatureOffset ) {} /** @@ -597,7 +599,7 @@ class ScalingForSystemSolutionKernel : public isothermalCompositionalMultiphaseB { // compute the change in temperature real64 const temp = m_temperature[ei]; - real64 const absTempChange = LvArray::math::abs( m_localSolution[stack.localRow + m_numComp + 1] ); + real64 const absTempChange = LvArray::math::abs( m_localSolution[stack.localRow + m_temperatureOffset] ); if( stack.localMaxDeltaTemp < absTempChange ) { stack.localMaxDeltaTemp = absTempChange; @@ -636,11 +638,15 @@ class ScalingForSystemSolutionKernel : public isothermalCompositionalMultiphaseB /// View on the scaling factor arrayView1d< real64 > const m_temperatureScalingFactor; + /// Temperature offset in solution array + integer const m_temperatureOffset; + }; /** * @class ScalingForSystemSolutionKernelFactory */ + class ScalingForSystemSolutionKernelFactory { public: @@ -666,23 +672,27 @@ class ScalingForSystemSolutionKernelFactory real64 const maxRelativeTempChange, real64 const maxCompFracChange, real64 const maxRelativeCompDensChange, + arrayView1d< real64 const > const pressure, + arrayView1d< real64 const > const temperature, + arrayView2d< real64 const, compflow::USD_COMP > const compDens, + arrayView1d< real64 > pressureScalingFactor, + arrayView1d< real64 > compDensScalingFactor, + arrayView1d< real64 > temperatureScalingFactor, + + globalIndex const rankOffset, integer const numComp, string const dofKey, ElementSubRegionBase & subRegion, - arrayView1d< real64 const > const localSolution ) + arrayView1d< real64 const > const localSolution, + integer const temperatureOffset ) { - arrayView1d< real64 const > const pressure = subRegion.getField< fields::flow::pressure >(); - arrayView1d< real64 const > const temperature = subRegion.getField< fields::flow::temperature >(); - arrayView2d< real64 const, compflow::USD_COMP > const compDens = subRegion.getField< fields::flow::globalCompDensity >(); - arrayView1d< real64 > pressureScalingFactor = subRegion.getField< fields::flow::pressureScalingFactor >(); - arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< fields::flow::temperatureScalingFactor >(); - arrayView1d< real64 > compDensScalingFactor = subRegion.getField< fields::flow::globalCompDensityScalingFactor >(); + ScalingForSystemSolutionKernel kernel( maxRelativePresChange, maxAbsolutePresChange, maxRelativeTempChange, maxCompFracChange, maxRelativeCompDensChange, rankOffset, numComp, dofKey, subRegion, localSolution, pressure, temperature, compDens, pressureScalingFactor, - temperatureScalingFactor, compDensScalingFactor ); + compDensScalingFactor, temperatureScalingFactor, temperatureOffset ); return thermalCompositionalMultiphaseBaseKernels:: ScalingForSystemSolutionKernel::launch< POLICY >( subRegion.size(), kernel ); } @@ -723,32 +733,36 @@ class SolutionCheckKernel : public isothermalCompositionalMultiphaseBaseKernels: integer const allowNegativePressure, CompositionalMultiphaseFVM::ScalingType const scalingType, real64 const scalingFactor, - globalIndex const rankOffset, - integer const numComp, - string const dofKey, - ElementSubRegionBase const & subRegion, - arrayView1d< real64 const > const localSolution, arrayView1d< real64 const > const pressure, arrayView1d< real64 const > const temperature, arrayView2d< real64 const, compflow::USD_COMP > const compDens, arrayView1d< real64 > pressureScalingFactor, arrayView1d< real64 > compDensScalingFactor, - arrayView1d< real64 > temperatureScalingFactor ) + arrayView1d< real64 > temperatureScalingFactor, + globalIndex const rankOffset, + integer const numComp, + string const dofKey, + ElementSubRegionBase const & subRegion, + arrayView1d< real64 const > const localSolution, + + integer const temperatureOffset ) : Base( allowCompDensChopping, allowNegativePressure, scalingType, scalingFactor, + + pressure, + compDens, + pressureScalingFactor, + compDensScalingFactor, rankOffset, numComp, dofKey, subRegion, - localSolution, - pressure, - compDens, - pressureScalingFactor, - compDensScalingFactor ), + localSolution ), m_temperature( temperature ), - m_temperatureScalingFactor( temperatureScalingFactor ) + m_temperatureScalingFactor( temperatureScalingFactor ), + m_temperatureOffset( temperatureOffset ) {} /** @@ -764,7 +778,7 @@ class SolutionCheckKernel : public isothermalCompositionalMultiphaseBaseKernels: { bool const localScaling = m_scalingType == CompositionalMultiphaseFVM::ScalingType::Local; // compute the change in temperature - real64 const newTemp = m_temperature[ei] + (localScaling ? m_temperatureScalingFactor[ei] : m_scalingFactor * m_localSolution[stack.localRow + m_numComp + 1]); + real64 const newTemp = m_temperature[ei] + (localScaling ? m_temperatureScalingFactor[ei] : m_scalingFactor * m_localSolution[stack.localRow + m_temperatureOffset]); if( newTemp < minTemperature ) { stack.localMinVal = 0; @@ -780,6 +794,9 @@ class SolutionCheckKernel : public isothermalCompositionalMultiphaseBaseKernels: /// View on the scaling factor arrayView1d< real64 const > const m_temperatureScalingFactor; + /// Offset to temperature variable + integer m_temperatureOffset; + }; /** @@ -807,24 +824,27 @@ class SolutionCheckKernelFactory integer const allowNegativePressure, CompositionalMultiphaseFVM::ScalingType const scalingType, real64 const scalingFactor, + arrayView1d< real64 const > const pressure, + arrayView1d< real64 const > const temperature, + arrayView2d< real64 const, compflow::USD_COMP > const compDens, + arrayView1d< real64 > pressureScalingFactor, + arrayView1d< real64 > temperatureScalingFactor, + arrayView1d< real64 > compDensScalingFactor, + + + globalIndex const rankOffset, integer const numComp, string const dofKey, ElementSubRegionBase & subRegion, - arrayView1d< real64 const > const localSolution ) + arrayView1d< real64 const > const localSolution, + integer temperatureOffset ) { - arrayView1d< real64 const > const pressure = - subRegion.getField< fields::flow::pressure >(); - arrayView1d< real64 const > const temperature = - subRegion.getField< fields::flow::temperature >(); - arrayView2d< real64 const, compflow::USD_COMP > const compDens = - subRegion.getField< fields::flow::globalCompDensity >(); - arrayView1d< real64 > pressureScalingFactor = subRegion.getField< fields::flow::pressureScalingFactor >(); - arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< fields::flow::temperatureScalingFactor >(); - arrayView1d< real64 > compDensScalingFactor = subRegion.getField< fields::flow::globalCompDensityScalingFactor >(); + SolutionCheckKernel kernel( allowCompDensChopping, allowNegativePressure, scalingType, scalingFactor, + pressure, temperature, compDens, pressureScalingFactor, compDensScalingFactor, temperatureScalingFactor, rankOffset, numComp, dofKey, subRegion, localSolution, - pressure, temperature, compDens, pressureScalingFactor, temperatureScalingFactor, compDensScalingFactor ); + temperatureOffset ); return SolutionCheckKernel::launch< POLICY >( subRegion.size(), kernel ); } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp index ffbef731fcd..08815b83e92 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp @@ -35,15 +35,20 @@ #include "mesh/PerforationFields.hpp" #include "mesh/WellElementSubRegion.hpp" #include "mesh/mpiCommunications/CommunicationTools.hpp" +#include "physicsSolvers/fluidFlow/wells/PerforationFluxKernels.hpp" #include "physicsSolvers/fluidFlow/IsothermalCompositionalMultiphaseBaseKernels.hpp" #include "physicsSolvers/fluidFlow/ThermalCompositionalMultiphaseBaseKernels.hpp" #include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp" #include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellKernels.hpp" + +#include "physicsSolvers/fluidFlow/wells/ThermalCompositionalMultiphaseWellKernels.hpp" #include "physicsSolvers/fluidFlow/wells/SinglePhaseWellKernels.hpp" #include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp" #include "physicsSolvers/fluidFlow/wells/WellControls.hpp" #include "physicsSolvers/fluidFlow/wells/LogLevelsInfo.hpp" +#include "physicsSolvers/fluidFlow/wells/WellFields.hpp" + #if defined( __INTEL_COMPILER ) #pragma GCC optimize "O0" #endif @@ -59,8 +64,6 @@ CompositionalMultiphaseWell::CompositionalMultiphaseWell( const string & name, Group * const parent ) : WellSolverBase( name, parent ), - m_numPhases( 0 ), - m_numComponents( 0 ), m_useMass( false ), m_useTotalMassEquation( 1 ), m_maxCompFracChange( 1.0 ), @@ -104,6 +107,12 @@ CompositionalMultiphaseWell::CompositionalMultiphaseWell( const string & name, setApplyDefaultValue( -1.0 ). // disabled by default setDescription( "Maximum (absolute) pressure change in a Newton iteration" ); + this->registerWrapper( viewKeyStruct::maxRelativeTempChangeString(), &m_maxRelativeTempChange ). + setSizedFromParent( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setApplyDefaultValue( 1.0 ). + setDescription( "Maximum (relative) change in temperature between two Newton iterations " ); + this->registerWrapper( viewKeyStruct::allowLocalCompDensChoppingString(), &m_allowCompDensChopping ). setSizedFromParent( 0 ). setInputFlag( InputFlags::OPTIONAL ). @@ -157,8 +166,9 @@ void CompositionalMultiphaseWell::registerDataOnMesh( Group & meshBodies ) m_numPhases = fluid0.numFluidPhases(); m_numComponents = fluid0.numFluidComponents(); } - m_numDofPerWellElement = m_numComponents + 2; // 1 pressure + NC compositions + 1 connectionRate - m_numDofPerResElement = m_numComponents + 1; // 1 pressure + NC compositions + m_numDofPerWellElement = isThermal() ? m_numComponents + 3 : m_numComponents + 2; // 1 pressure + NC compositions + 1 connectionRate + + // temp if thermal + m_numDofPerResElement = isThermal() ? m_numComponents + 2 : m_numComponents + 1; // 1 pressure + NC compositions + temp if thermal // loop over the wells forDiscretizationOnMeshTargets( meshBodies, [&] ( string const &, @@ -202,9 +212,8 @@ void CompositionalMultiphaseWell::registerDataOnMesh( Group & meshBodies ) reference().resizeDimension< 1, 2 >( m_numPhases, m_numComponents + 2 ); // dP, dT, dC subRegion.registerField< fields::well::totalMassDensity >( getName() ); - subRegion.registerField< fields::well::dTotalMassDensity_dPressure >( getName() ); - subRegion.registerField< fields::well::dTotalMassDensity_dGlobalCompDensity >( getName() ). - reference().resizeDimension< 1 >( m_numComponents ); + subRegion.registerField< fields::well::dTotalMassDensity >( getName() ). + reference().resizeDimension< 1 >( m_numComponents +2 ); // dP, dT, dC subRegion.registerField< fields::well::phaseVolumeFraction_n >( getName() ). reference().resizeDimension< 1 >( m_numPhases ); @@ -216,46 +225,39 @@ void CompositionalMultiphaseWell::registerDataOnMesh( Group & meshBodies ) PerforationData & perforationData = *subRegion.getPerforationData(); perforationData.registerField< fields::well::compPerforationRate >( getName() ). reference().resizeDimension< 1 >( m_numComponents ); - perforationData.registerField< fields::well::dCompPerforationRate_dPres >( getName() ). - reference().resizeDimension< 1, 2 >( 2, m_numComponents ); - perforationData.registerField< fields::well::dCompPerforationRate_dComp >( getName() ). - reference().resizeDimension< 1, 2, 3 >( 2, m_numComponents, m_numComponents ); + + perforationData.registerField< fields::well::dCompPerforationRate >( getName() ).reference().resizeDimension< 1, 2, 3 >( 2, m_numComponents, m_numComponents+ 2 ); + if( fluid.isThermal() ) + { + perforationData.registerField< fields::well::energyPerforationFlux >( getName() ); + perforationData.registerField< fields::well::dEnergyPerforationFlux >( getName() ). + reference().resizeDimension< 1, 2 >( 2, m_numComponents+2 ); + } WellControls & wellControls = getWellControls( subRegion ); wellControls.registerWrapper< real64 >( viewKeyStruct::currentBHPString() ); - wellControls.registerWrapper< real64 >( viewKeyStruct::dCurrentBHP_dPresString() ). - setRestartFlags( RestartFlags::NO_WRITE ); - wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentBHP_dCompDensString() ). - setRestartFlags( RestartFlags::NO_WRITE ). + + wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentBHPString() ). setSizedFromParent( 0 ). - reference().resizeDimension< 0 >( m_numComponents ); + reference().resizeDimension< 0 >( m_numComponents + 2 ); // dP, dT, dC + wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::currentPhaseVolRateString() ). setSizedFromParent( 0 ). reference().resizeDimension< 0 >( m_numPhases ); - wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentPhaseVolRate_dPresString() ). - setRestartFlags( RestartFlags::NO_WRITE ). - setSizedFromParent( 0 ). - reference().resizeDimension< 0 >( m_numPhases ); - wellControls.registerWrapper< array2d< real64 > >( viewKeyStruct::dCurrentPhaseVolRate_dCompDensString() ). - setRestartFlags( RestartFlags::NO_WRITE ). - setSizedFromParent( 0 ). - reference().resizeDimension< 0, 1 >( m_numPhases, m_numComponents ); - wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentPhaseVolRate_dRateString() ). - setRestartFlags( RestartFlags::NO_WRITE ). + + wellControls.registerWrapper< array2d< real64 > >( viewKeyStruct::dCurrentPhaseVolRateString() ). setSizedFromParent( 0 ). - reference().resizeDimension< 0 >( m_numPhases ); + reference().resizeDimension< 0, 1 >( m_numPhases, m_numComponents + 3 ); // dP, dT, dC, dQ - wellControls.registerWrapper< real64 >( viewKeyStruct::currentTotalVolRateString() ); wellControls.registerWrapper< real64 >( viewKeyStruct::massDensityString() ); - wellControls.registerWrapper< real64 >( viewKeyStruct::dCurrentTotalVolRate_dPresString() ). - setRestartFlags( RestartFlags::NO_WRITE ); - wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentTotalVolRate_dCompDensString() ). - setRestartFlags( RestartFlags::NO_WRITE ). + + wellControls.registerWrapper< real64 >( viewKeyStruct::currentTotalVolRateString() ); + wellControls.registerWrapper< array1d< real64 > >( viewKeyStruct::dCurrentTotalVolRateString() ). setSizedFromParent( 0 ). - reference().resizeDimension< 0 >( m_numComponents ); - wellControls.registerWrapper< real64 >( viewKeyStruct::dCurrentTotalVolRate_dRateString() ). - setRestartFlags( RestartFlags::NO_WRITE ); + reference().resizeDimension< 0 >( m_numComponents + 3 ); // dP, dT, dC dQ + + wellControls.registerWrapper< real64 >( viewKeyStruct::massDensityString() ); // write rates output header // the rank that owns the reference well element is responsible @@ -560,17 +562,21 @@ void CompositionalMultiphaseWell::updateBHPForConstraint( WellElementSubRegion & { return; } + using Deriv = multifluid::DerivativeOffset; integer const numComp = m_numComponents; localIndex const iwelemRef = subRegion.getTopWellElementIndex(); + string & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + fluidName = getConstitutiveName< MultiFluidBase >( subRegion ); + MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); + integer isThermal = fluid.isThermal(); // subRegion data arrayView1d< real64 const > const & pres = subRegion.getField< fields::well::pressure >(); arrayView1d< real64 > const & totalMassDens = subRegion.getField< fields::well::totalMassDensity >(); - arrayView1d< real64 > const & dTotalMassDens_dPres = subRegion.getField< fields::well::dTotalMassDensity_dPressure >(); - arrayView2d< real64, compflow::USD_FLUID_DC > const & dTotalMassDens_dCompDens = subRegion.getField< fields::well::dTotalMassDensity_dGlobalCompDensity >(); + arrayView2d< real64, compflow::USD_FLUID_DC > const & dTotalMassDens = subRegion.getField< fields::well::dTotalMassDensity >(); arrayView1d< real64 const > const wellElemGravCoef = subRegion.getField< fields::well::gravityCoefficient >(); @@ -583,31 +589,36 @@ void CompositionalMultiphaseWell::updateBHPForConstraint( WellElementSubRegion & real64 & currentBHP = wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ); - real64 & dCurrentBHP_dPres = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentBHP_dPresString() ); - arrayView1d< real64 > const & dCurrentBHP_dCompDens = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentBHP_dCompDensString() ); - - // bring everything back to host, capture the scalars by reference - forAll< serialPolicy >( 1, [&numComp, - pres, - totalMassDens, - dTotalMassDens_dPres, - dTotalMassDens_dCompDens, - wellElemGravCoef, - ¤tBHP, - &dCurrentBHP_dPres, - dCurrentBHP_dCompDens, - &iwelemRef, - &refGravCoef] ( localIndex const ) + arrayView1d< real64 > const & dCurrentBHP = + wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentBHPString() ); + + geos::internal::kernelLaunchSelectorCompThermSwitch( numComp, isThermal, [&] ( auto NC, auto ISTHERMAL ) { - real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef]; - currentBHP = pres[iwelemRef] + totalMassDens[iwelemRef] * diffGravCoef; - dCurrentBHP_dPres = 1 + dTotalMassDens_dPres[iwelemRef] * diffGravCoef; - for( integer ic = 0; ic < numComp; ++ic ) + integer constexpr IS_THERMAL = ISTHERMAL(); + GEOS_UNUSED_VAR( NC ); + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [&numComp, + pres, + totalMassDens, + dTotalMassDens, + wellElemGravCoef, + ¤tBHP, + &dCurrentBHP, + &iwelemRef, + &refGravCoef] ( localIndex const ) { - dCurrentBHP_dCompDens[ic] = dTotalMassDens_dCompDens[iwelemRef][ic] * diffGravCoef; - } + real64 const diffGravCoef = refGravCoef - wellElemGravCoef[iwelemRef]; + currentBHP = pres[iwelemRef] + totalMassDens[iwelemRef] * diffGravCoef; + dCurrentBHP[Deriv::dP] = 1 + dTotalMassDens[iwelemRef][Deriv::dP] * diffGravCoef; + for( integer ic = 0; ic < numComp; ++ic ) + { + dCurrentBHP[Deriv::dC+ic] = dTotalMassDens[iwelemRef][Deriv::dC+ic] * diffGravCoef; + } + if constexpr ( IS_THERMAL ) + { + dCurrentBHP[Deriv::dT] = dTotalMassDens[iwelemRef][Deriv::dT] * diffGravCoef; + } + } ); } ); if( logLevel >= 2 ) @@ -646,7 +657,7 @@ void CompositionalMultiphaseWell::updateVolRatesForConstraint( WellElementSubReg string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); MultiFluidBase & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); - + integer isThermal = fluid.isThermal(); arrayView3d< real64 const, multifluid::USD_PHASE > const & phaseFrac = fluid.phaseFraction(); arrayView4d< real64 const, multifluid::USD_PHASE_DC > const & dPhaseFrac = fluid.dPhaseFraction(); @@ -669,164 +680,170 @@ void CompositionalMultiphaseWell::updateVolRatesForConstraint( WellElementSubReg arrayView1d< real64 > const & currentPhaseVolRate = wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); - arrayView1d< real64 > const & dCurrentPhaseVolRate_dPres = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentPhaseVolRate_dPresString() ); - arrayView2d< real64 > const & dCurrentPhaseVolRate_dCompDens = - wellControls.getReference< array2d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentPhaseVolRate_dCompDensString() ); - arrayView1d< real64 > const & dCurrentPhaseVolRate_dRate = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentPhaseVolRate_dRateString() ); + arrayView2d< real64 > const & dCurrentPhaseVolRate = + wellControls.getReference< array2d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentPhaseVolRateString() ); real64 & currentTotalVolRate = wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); - real64 & dCurrentTotalVolRate_dPres = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentTotalVolRate_dPresString() ); - arrayView1d< real64 > const & dCurrentTotalVolRate_dCompDens = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentTotalVolRate_dCompDensString() ); - real64 & dCurrentTotalVolRate_dRate = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentTotalVolRate_dRateString() ); + arrayView1d< real64 > const & dCurrentTotalVolRate = + wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentTotalVolRateString() ); + real64 & massDensity = wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() ); constitutive::constitutiveUpdatePassThru( fluid, [&] ( auto & castedFluid ) { typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); - - // bring everything back to host, capture the scalars by reference - forAll< serialPolicy >( 1, [&numComp, - &numPhase, - fluidWrapper, - pres, - temp, - compFrac, - dCompFrac_dCompDens, - connRate, - totalDens, - dTotalDens, - phaseDens, - dPhaseDens, - phaseFrac, - dPhaseFrac, - &useSurfaceConditions, - &surfacePres, - &surfaceTemp, - ¤tTotalVolRate, - &dCurrentTotalVolRate_dPres, - dCurrentTotalVolRate_dCompDens, - &dCurrentTotalVolRate_dRate, - currentPhaseVolRate, - dCurrentPhaseVolRate_dPres, - dCurrentPhaseVolRate_dCompDens, - dCurrentPhaseVolRate_dRate, - &iwelemRef, - &logLevel, - &wellControlsName, - &massUnit, - &massDensity] ( localIndex const ) + geos::internal::kernelLaunchSelectorCompThermSwitch( numComp, isThermal, [&] ( auto NC, auto ISTHERMAL ) { - GEOS_UNUSED_VAR( massUnit ); - using Deriv = multifluid::DerivativeOffset; - - stackArray1d< real64, maxNumComp > work( numComp ); - - // Step 1: evaluate the phase and total density in the reference element - - // We need to evaluate the density as follows: - // - Surface conditions: using the surface pressure provided by the user - // - Reservoir conditions: using the pressure in the top element - if( useSurfaceConditions ) + integer constexpr NUM_COMP = NC(); + integer constexpr IS_THERMAL = ISTHERMAL(); + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NUM_COMP, IS_THERMAL >; + // bring everything back to host, capture the scalars by reference + forAll< serialPolicy >( 1, [&numComp, + &numPhase, + fluidWrapper, + pres, + temp, + compFrac, + dCompFrac_dCompDens, + connRate, + totalDens, + dTotalDens, + phaseDens, + dPhaseDens, + phaseFrac, + dPhaseFrac, + &useSurfaceConditions, + &surfacePres, + &surfaceTemp, + ¤tTotalVolRate, + dCurrentTotalVolRate, + currentPhaseVolRate, + dCurrentPhaseVolRate, + &iwelemRef, + &logLevel, + &wellControlsName, + &massUnit, + &massDensity] ( localIndex const ) { - // we need to compute the surface density - fluidWrapper.update( iwelemRef, 0, surfacePres, surfaceTemp, compFrac[iwelemRef] ); - if( logLevel >= 2 ) + GEOS_UNUSED_VAR( massUnit ); + using Deriv = multifluid::DerivativeOffset; + stackArray1d< real64, maxNumComp > work( numComp ); + // Step 1: evaluate the phase and total density in the reference element + + // We need to evaluate the density as follows: + // - Surface conditions: using the surface pressure provided by the user + // - Reservoir conditions: using the pressure in the top element + if( useSurfaceConditions ) { - GEOS_LOG_RANK( GEOS_FMT( "{}: surface density computed with P_surface = {} Pa and T_surface = {} K", - wellControlsName, surfacePres, surfaceTemp ) ); + // we need to compute the surface density + fluidWrapper.update( iwelemRef, 0, surfacePres, surfaceTemp, compFrac[iwelemRef] ); + if( logLevel >= 2 ) + { + GEOS_LOG_RANK( GEOS_FMT( "{}: surface density computed with P_surface = {} Pa and T_surface = {} K", + wellControlsName, surfacePres, surfaceTemp ) ); #ifdef GEOS_USE_HIP - GEOS_UNUSED_VAR( wellControlsName ); + GEOS_UNUSED_VAR( wellControlsName ); #endif + } + } + else + { + real64 const refPres = pres[iwelemRef]; + fluidWrapper.update( iwelemRef, 0, refPres, temp[iwelemRef], compFrac[iwelemRef] ); } - } - else - { - real64 const refPres = pres[iwelemRef]; - fluidWrapper.update( iwelemRef, 0, refPres, temp[iwelemRef], compFrac[iwelemRef] ); - } - - // Step 2: update the total volume rate - - real64 const currentTotalRate = connRate[iwelemRef]; - - // Step 2.1: compute the inverse of the total density and derivatives - massDensity = totalDens[iwelemRef][0]; // need to verify this is surface dens - real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0]; - real64 const dTotalDensInv_dPres = -dTotalDens[iwelemRef][0][Deriv::dP] * totalDensInv * totalDensInv; - stackArray1d< real64, maxNumComp > dTotalDensInv_dCompDens( numComp ); - for( integer ic = 0; ic < numComp; ++ic ) - { - dTotalDensInv_dCompDens[ic] = -dTotalDens[iwelemRef][0][Deriv::dC+ic] * totalDensInv * totalDensInv; - } - applyChainRuleInPlace( numComp, dCompFrac_dCompDens[iwelemRef], dTotalDensInv_dCompDens, work.data() ); - - // Step 2.2: divide the total mass/molar rate by the total density to get the total volumetric rate - currentTotalVolRate = currentTotalRate * totalDensInv; - dCurrentTotalVolRate_dPres = ( useSurfaceConditions == 0 ) * currentTotalRate * dTotalDensInv_dPres; - dCurrentTotalVolRate_dRate = totalDensInv; - for( integer ic = 0; ic < numComp; ++ic ) - { - dCurrentTotalVolRate_dCompDens[ic] = currentTotalRate * dTotalDensInv_dCompDens[ic]; - } - if( logLevel >= 2 && useSurfaceConditions ) - { - GEOS_LOG_RANK( GEOS_FMT( "{}: total fluid density at surface conditions = {} {}/sm3, total rate = {} {}/s, total surface volumetric rate = {} sm3/s", - wellControlsName, totalDens[iwelemRef][0], massUnit, currentTotalRate, massUnit, currentTotalVolRate ) ); - } + // Step 2: update the total volume rate - // Step 3: update the phase volume rate - for( integer ip = 0; ip < numPhase; ++ip ) - { + real64 const currentTotalRate = connRate[iwelemRef]; - // Step 3.1: compute the inverse of the (phase density * phase fraction) and derivatives + // Step 2.1: compute the inverse of the total density and derivatives + massDensity = totalDens[iwelemRef][0]; + real64 const totalDensInv = 1.0 / totalDens[iwelemRef][0]; - // skip the rest of this function if phase ip is absent - bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0); - if( !phaseExists ) + stackArray1d< real64, maxNumComp > dTotalDensInv_dCompDens( numComp ); + for( integer ic = 0; ic < numComp; ++ic ) { - continue; + dTotalDensInv_dCompDens[ic] = -dTotalDens[iwelemRef][0][Deriv::dC+ic] * totalDensInv * totalDensInv; + } + applyChainRuleInPlace( numComp, dCompFrac_dCompDens[iwelemRef], dTotalDensInv_dCompDens, work.data() ); + + // Step 2.2: divide the total mass/molar rate by the total density to get the total volumetric rate + currentTotalVolRate = currentTotalRate * totalDensInv; + // Compute derivatives dP dT + real64 const dTotalDensInv_dPres = -dTotalDens[iwelemRef][0][Deriv::dP] * totalDensInv * totalDensInv; + dCurrentTotalVolRate[COFFSET_WJ::dP] = ( useSurfaceConditions == 0 ) * currentTotalRate * dTotalDensInv_dPres; + if constexpr ( IS_THERMAL ) + { + dCurrentTotalVolRate[COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * currentTotalRate * -dTotalDens[iwelemRef][0][Deriv::dT] * totalDensInv * totalDensInv; } - real64 const phaseDensInv = 1.0 / phaseDens[iwelemRef][0][ip]; - real64 const phaseFracTimesPhaseDensInv = phaseFrac[iwelemRef][0][ip] * phaseDensInv; - real64 const dPhaseFracTimesPhaseDensInv_dPres = dPhaseFrac[iwelemRef][0][ip][Deriv::dP] * phaseDensInv - - dPhaseDens[iwelemRef][0][ip][Deriv::dP] * phaseFracTimesPhaseDensInv * phaseDensInv; - - - // Step 3.2: divide the total mass/molar rate by the (phase density * phase fraction) to get the phase volumetric rate - currentPhaseVolRate[ip] = currentTotalRate * phaseFracTimesPhaseDensInv; - dCurrentPhaseVolRate_dPres[ip] = ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dPres; - dCurrentPhaseVolRate_dRate[ip] = phaseFracTimesPhaseDensInv; + dCurrentTotalVolRate[COFFSET_WJ::dQ] = totalDensInv; for( integer ic = 0; ic < numComp; ++ic ) { - dCurrentPhaseVolRate_dCompDens[ip][ic] = -phaseFracTimesPhaseDensInv * dPhaseDens[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; - dCurrentPhaseVolRate_dCompDens[ip][ic] += dPhaseFrac[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; - dCurrentPhaseVolRate_dCompDens[ip][ic] *= currentTotalRate; + dCurrentTotalVolRate[COFFSET_WJ::dC+ic] = currentTotalRate * dTotalDensInv_dCompDens[ic]; } - applyChainRuleInPlace( numComp, dCompFrac_dCompDens[iwelemRef], dCurrentPhaseVolRate_dCompDens[ip], work.data() ); if( logLevel >= 2 && useSurfaceConditions ) { - GEOS_LOG_RANK( GEOS_FMT( "{}: density of phase {} at surface conditions = {} {}/sm3, phase surface volumetric rate = {} sm3/s", - wellControlsName, ip, phaseDens[iwelemRef][0][ip], massUnit, currentPhaseVolRate[ip] ) ); + GEOS_LOG_RANK( GEOS_FMT( "{}: total fluid density at surface conditions = {} {}/sm3, total rate = {} {}/s, total surface volumetric rate = {} sm3/s", + wellControlsName, totalDens[iwelemRef][0], massUnit, currentTotalRate, massUnit, currentTotalVolRate ) ); } - } + + // Step 3: update the phase volume rate + for( integer ip = 0; ip < numPhase; ++ip ) + { + + // Step 3.1: compute the inverse of the (phase density * phase fraction) and derivatives + + // skip the rest of this function if phase ip is absent + bool const phaseExists = (phaseFrac[iwelemRef][0][ip] > 0); + if( !phaseExists ) + { + continue; + } + + real64 const phaseDensInv = 1.0 / phaseDens[iwelemRef][0][ip]; + real64 const phaseFracTimesPhaseDensInv = phaseFrac[iwelemRef][0][ip] * phaseDensInv; + real64 const dPhaseFracTimesPhaseDensInv_dPres = dPhaseFrac[iwelemRef][0][ip][Deriv::dP] * phaseDensInv + - dPhaseDens[iwelemRef][0][ip][Deriv::dP] * phaseFracTimesPhaseDensInv * phaseDensInv; + + + // Step 3.2: divide the total mass/molar rate by the (phase density * phase fraction) to get the phase volumetric rate + currentPhaseVolRate[ip] = currentTotalRate * phaseFracTimesPhaseDensInv; + dCurrentPhaseVolRate[ip][COFFSET_WJ::dP] = ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dPres; + dCurrentPhaseVolRate[ip][COFFSET_WJ::dQ] = phaseFracTimesPhaseDensInv; + if constexpr (IS_THERMAL ) + { + real64 const dPhaseFracTimesPhaseDensInv_dTemp = dPhaseFrac[iwelemRef][0][ip][Deriv::dT] * phaseDensInv + - dPhaseDens[iwelemRef][0][ip][Deriv::dT] * phaseFracTimesPhaseDensInv * phaseDensInv; + dCurrentPhaseVolRate[ip][COFFSET_WJ::dT] = ( useSurfaceConditions == 0 ) * currentTotalRate * dPhaseFracTimesPhaseDensInv_dTemp; + } + + for( integer ic = 0; ic < numComp; ++ic ) + { + dCurrentPhaseVolRate[ip][COFFSET_WJ::dC+ic] = -phaseFracTimesPhaseDensInv * dPhaseDens[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; + dCurrentPhaseVolRate[ip][COFFSET_WJ::dC+ic] += dPhaseFrac[iwelemRef][0][ip][Deriv::dC+ic] * phaseDensInv; + dCurrentPhaseVolRate[ip][COFFSET_WJ::dC+ic] *= currentTotalRate; + } + applyChainRuleInPlace( numComp, dCompFrac_dCompDens[iwelemRef], &dCurrentPhaseVolRate[ip][COFFSET_WJ::dC], work.data() ); + + if( logLevel >= 2 && useSurfaceConditions ) + { + GEOS_LOG_RANK( GEOS_FMT( "{}: density of phase {} at surface conditions = {} {}/sm3, phase surface volumetric rate = {} sm3/s", + wellControlsName, ip, phaseDens[iwelemRef][0][ip], massUnit, currentPhaseVolRate[ip] ) ); + } + } + } ); } ); } ); } + void CompositionalMultiphaseWell::updateFluidModel( WellElementSubRegion & subRegion ) { GEOS_MARK_FUNCTION; - arrayView1d< real64 const > const & pres = subRegion.getField< fields::well::pressure >(); arrayView1d< real64 const > const & temp = subRegion.getField< fields::well::temperature >(); arrayView2d< real64 const, compflow::USD_COMP > const & compFrac = subRegion.getField< fields::well::globalCompFraction >(); @@ -839,7 +856,6 @@ void CompositionalMultiphaseWell::updateFluidModel( WellElementSubRegion & subRe using FluidType = TYPEOFREF( castedFluid ); using ExecPolicy = typename FluidType::exec_policy; typename FluidType::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); - thermalCompositionalMultiphaseBaseKernels:: FluidUpdateKernel:: launch< ExecPolicy >( subRegion.size(), @@ -848,21 +864,32 @@ void CompositionalMultiphaseWell::updateFluidModel( WellElementSubRegion & subRe temp, compFrac ); } ); + } -void CompositionalMultiphaseWell::updatePhaseVolumeFraction( WellElementSubRegion & subRegion ) const +real64 CompositionalMultiphaseWell::updatePhaseVolumeFraction( WellElementSubRegion & subRegion ) const { GEOS_MARK_FUNCTION; string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); MultiFluidBase & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); - isothermalCompositionalMultiphaseBaseKernels:: - PhaseVolumeFractionKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_numComponents, - m_numPhases, - subRegion, - fluid ); + real64 maxDeltaPhaseVolFrac = + m_isThermal ? + thermalCompositionalMultiphaseBaseKernels:: + PhaseVolumeFractionKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + m_numPhases, + subRegion, + fluid ) +: isothermalCompositionalMultiphaseBaseKernels:: + PhaseVolumeFractionKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + m_numPhases, + subRegion, + fluid ); + + return maxDeltaPhaseVolFrac; } void CompositionalMultiphaseWell::updateTotalMassDensity( WellElementSubRegion & subRegion ) const @@ -871,15 +898,45 @@ void CompositionalMultiphaseWell::updateTotalMassDensity( WellElementSubRegion & string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); MultiFluidBase & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); - + fluid.isThermal() ? + thermalCompositionalMultiphaseWellKernels:: + TotalMassDensityKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + m_numPhases, + subRegion, + fluid ) + : TotalMassDensityKernelFactory:: createAndLaunch< parallelDevicePolicy<> >( m_numComponents, m_numPhases, subRegion, fluid ); + } -void CompositionalMultiphaseWell::updateSubRegionState( WellElementSubRegion & subRegion ) +void CompositionalMultiphaseWell::updateState( DomainPartition & domain ) +{ + GEOS_MARK_FUNCTION; + + real64 maxPhaseVolFrac = 0.0; + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + real64 const maxRegionPhaseVolFrac = updateSubRegionState( subRegion ); + maxPhaseVolFrac = LvArray::math::max( maxRegionPhaseVolFrac, maxPhaseVolFrac ); + } ); + } ); + maxPhaseVolFrac = MpiWrapper::max( maxPhaseVolFrac ); + + GEOS_LOG_LEVEL_RANK_0( 1, GEOS_FMT( " {}: Max well phase volume fraction change = {}", getName(), fmt::format( "{:.{}f}", maxPhaseVolFrac, 4 ) ) ); + +} + +real64 CompositionalMultiphaseWell::updateSubRegionState( WellElementSubRegion & subRegion ) { // update properties updateGlobalComponentFraction( subRegion ); @@ -889,17 +946,16 @@ void CompositionalMultiphaseWell::updateSubRegionState( WellElementSubRegion & s updateVolRatesForConstraint( subRegion ); // update densities, phase fractions, phase volume fractions - updateFluidModel( subRegion ); - updatePhaseVolumeFraction( subRegion ); - updateTotalMassDensity( subRegion ); + updateFluidModel( subRegion ); // Calculate fluid properties; + real64 maxPhaseVolChange = updatePhaseVolumeFraction( subRegion ); + updateTotalMassDensity( subRegion ); // update the current BHP pressure updateBHPForConstraint( subRegion ); - - // note: the perforation rates are updated separately + return maxPhaseVolChange; } -void CompositionalMultiphaseWell::initializeWells( DomainPartition & domain ) +void CompositionalMultiphaseWell::initializeWells( DomainPartition & domain, real64 const & time_n, real64 const & dt ) { GEOS_MARK_FUNCTION; @@ -924,270 +980,253 @@ void CompositionalMultiphaseWell::initializeWells( DomainPartition & domain ) WellElementSubRegion & subRegion ) { WellControls const & wellControls = getWellControls( subRegion ); - PerforationData const & perforationData = *subRegion.getPerforationData(); - - // get well primary variables on well elements - arrayView1d< real64 > const & wellElemPressure = subRegion.getField< fields::well::pressure >(); - arrayView1d< real64 > const & wellElemTemp = subRegion.getField< fields::well::temperature >(); - arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = subRegion.getField< fields::well::globalCompDensity >(); - arrayView1d< real64 > const & connRate = subRegion.getField< fields::well::mixtureConnectionRate >(); - - // get the info stored on well elements - arrayView2d< real64, compflow::USD_COMP > const & wellElemCompFrac = subRegion.getField< fields::well::globalCompFraction >(); - arrayView1d< real64 const > const & wellElemGravCoef = subRegion.getField< fields::well::gravityCoefficient >(); - - // get the element region, subregion, index - arrayView1d< localIndex const > const resElementRegion = perforationData.getField< fields::perforation::reservoirElementRegion >(); - arrayView1d< localIndex const > const resElementSubRegion = perforationData.getField< fields::perforation::reservoirElementSubRegion >(); - arrayView1d< localIndex const > const resElementIndex = perforationData.getField< fields::perforation::reservoirElementIndex >(); - - arrayView1d< real64 const > const & perfGravCoef = perforationData.getField< fields::well::gravityCoefficient >(); - - // 1) Loop over all perforations to compute an average mixture density and component fraction - // 2) Initialize the reference pressure - // 3) Estimate the pressures in the well elements using the average density - PresTempCompFracInitializationKernel:: - launch( perforationData.size(), - subRegion.size(), - numComp, - numPhase, - perforationData.getNumPerforationsGlobal(), - wellControls, - 0.0, // initialization done at t = 0 - resCompFlowAccessors.get( fields::flow::pressure{} ), - resCompFlowAccessors.get( fields::flow::temperature{} ), - resCompFlowAccessors.get( fields::flow::globalCompDensity{} ), - resCompFlowAccessors.get( fields::flow::phaseVolumeFraction{} ), - resMultiFluidAccessors.get( fields::multifluid::phaseMassDensity{} ), - resElementRegion, - resElementSubRegion, - resElementIndex, - perfGravCoef, - wellElemGravCoef, - wellElemPressure, - wellElemTemp, - wellElemCompFrac ); - - // get well secondary variables on well elements - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - MultiFluidBase & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); - arrayView3d< real64 const, multifluid::USD_PHASE > const & wellElemPhaseDens = fluid.phaseDensity(); - arrayView2d< real64 const, multifluid::USD_FLUID > const & wellElemTotalDens = fluid.totalDensity(); - // 4) Back calculate component densities - constitutive::constitutiveUpdatePassThru( fluid, [&] ( auto & castedFluid ) + if( time_n <= 0.0 || + ( !wellControls.isWellOpen( time_n ) && wellControls.isWellOpen( time_n + dt ) ) ) { - typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); - - thermalCompositionalMultiphaseBaseKernels:: - FluidUpdateKernel:: - launch< serialPolicy >( subRegion.size(), - fluidWrapper, - wellElemPressure, - wellElemTemp, - wellElemCompFrac ); - } ); - - CompDensInitializationKernel::launch( subRegion.size(), - numComp, - wellElemCompFrac, - wellElemTotalDens, - wellElemCompDens ); - // 5) Recompute the pressure-dependent properties - updateSubRegionState( subRegion ); - - // 6) Estimate the well rates - // TODO: initialize rates using perforation rates - compositionalMultiphaseWellKernels:: - RateInitializationKernel:: - launch( subRegion.size(), - m_targetPhaseIndex, - wellControls, - 0.0, // initialization done at t = 0 - wellElemPhaseDens, - wellElemTotalDens, - connRate ); + PerforationData const & perforationData = *subRegion.getPerforationData(); + + // get well primary variables on well elements + arrayView1d< real64 > const & wellElemPressure = subRegion.getField< fields::well::pressure >(); + arrayView1d< real64 > const & wellElemTemp = subRegion.getField< fields::well::temperature >(); + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = subRegion.getField< fields::well::globalCompDensity >(); + arrayView1d< real64 > const & connRate = subRegion.getField< fields::well::mixtureConnectionRate >(); + + // get the info stored on well elements + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompFrac = subRegion.getField< fields::well::globalCompFraction >(); + arrayView1d< real64 const > const & wellElemGravCoef = subRegion.getField< fields::well::gravityCoefficient >(); + + // get the element region, subregion, index + arrayView1d< localIndex const > const resElementRegion = perforationData.getField< fields::perforation::reservoirElementRegion >(); + arrayView1d< localIndex const > const resElementSubRegion = perforationData.getField< fields::perforation::reservoirElementSubRegion >(); + arrayView1d< localIndex const > const resElementIndex = perforationData.getField< fields::perforation::reservoirElementIndex >(); + + arrayView1d< real64 const > const & perfGravCoef = perforationData.getField< fields::well::gravityCoefficient >(); + + // 1) Loop over all perforations to compute an average mixture density and component fraction + // 2) Initialize the reference pressure + // 3) Estimate the pressures in the well elements using the average density + PresTempCompFracInitializationKernel:: + launch( perforationData.size(), + subRegion.size(), + numComp, + numPhase, + perforationData.getNumPerforationsGlobal(), + wellControls, + 0.0, // initialization done at t = 0 + resCompFlowAccessors.get( fields::flow::pressure{} ), + resCompFlowAccessors.get( fields::flow::temperature{} ), + resCompFlowAccessors.get( fields::flow::globalCompDensity{} ), + resCompFlowAccessors.get( fields::flow::phaseVolumeFraction{} ), + resMultiFluidAccessors.get( fields::multifluid::phaseMassDensity{} ), + resElementRegion, + resElementSubRegion, + resElementIndex, + perfGravCoef, + wellElemGravCoef, + wellElemPressure, + wellElemTemp, + wellElemCompFrac ); + + // get well secondary variables on well elements + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + arrayView3d< real64 const, multifluid::USD_PHASE > const & wellElemPhaseDens = fluid.phaseDensity(); + arrayView2d< real64 const, multifluid::USD_FLUID > const & wellElemTotalDens = fluid.totalDensity(); + + // 4) Back calculate component densities + constitutive::constitutiveUpdatePassThru( fluid, [&] ( auto & castedFluid ) + { + typename TYPEOFREF( castedFluid ) ::KernelWrapper fluidWrapper = castedFluid.createKernelWrapper(); + + thermalCompositionalMultiphaseBaseKernels:: + FluidUpdateKernel:: + launch< serialPolicy >( subRegion.size(), + fluidWrapper, + wellElemPressure, + wellElemTemp, + wellElemCompFrac ); + } ); + + CompDensInitializationKernel::launch( subRegion.size(), + numComp, + wellElemCompFrac, + wellElemTotalDens, + wellElemCompDens ); + + // 5) Recompute the pressure-dependent properties + updateSubRegionState( subRegion ); + + // 6) Estimate the well rates + // TODO: initialize rates using perforation rates + compositionalMultiphaseWellKernels:: + RateInitializationKernel:: + launch( subRegion.size(), + m_targetPhaseIndex, + wellControls, + 0.0, // initialization done at t = 0 + wellElemPhaseDens, + wellElemTotalDens, + connRate ); + } } ); } ); } -void CompositionalMultiphaseWell::assembleFluxTerms( real64 const dt, - DomainPartition const & domain, +void CompositionalMultiphaseWell::assembleFluxTerms( real64 const & time, + real64 const & dt, + DomainPartition & domain, DofManager const & dofManager, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { GEOS_MARK_FUNCTION; - // loop over the wells - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel const & mesh, - arrayView1d< string const > const & regionNames ) - { - ElementRegionManager const & elemManager = mesh.getElemManager(); - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion const & subRegion ) + string const wellDofKey = dofManager.getKey( wellElementDofName()); + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) { - WellControls const & wellControls = getWellControls( subRegion ); - - // get a reference to the degree-of-freedom numbers - string const wellDofKey = dofManager.getKey( wellElementDofName() ); - arrayView1d< globalIndex const > const & wellElemDofNumber = - subRegion.getReference< array1d< globalIndex > >( wellDofKey ); - arrayView1d< localIndex const > const & nextWellElemIndex = - subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString() ); + WellControls const & well_controls = getWellControls( subRegion ); + if( well_controls.isWellOpen( time+ dt ) ) + { + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString()); + MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + int numComponents = fluid.numFluidComponents(); - // get a reference to the primary variables on well elements - arrayView1d< real64 const > const & connRate = subRegion.getField< fields::well::mixtureConnectionRate >(); - - // get the info stored on well elements - arrayView2d< real64 const, compflow::USD_COMP > const & wellElemCompFrac = subRegion.getField< fields::well::globalCompFraction >(); - arrayView3d< real64 const, compflow::USD_COMP_DC > const & dWellElemCompFrac_dCompDens = subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >(); - - isothermalCompositionalMultiphaseBaseKernels:: - KernelLaunchSelector1< FluxKernel >( numFluidComponents(), - subRegion.size(), - dofManager.rankOffset(), - m_useTotalMassEquation, - wellControls, - wellElemDofNumber, - nextWellElemIndex, - connRate, - wellElemCompFrac, - dWellElemCompFrac_dCompDens, - dt, - localMatrix, - localRhs ); + if( isThermal() ) + { + thermalCompositionalMultiphaseWellKernels:: + FaceBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( numComponents, + dt, + dofManager.rankOffset(), + m_useTotalMassEquation, + wellDofKey, + well_controls, + subRegion, + fluid, + localMatrix, + localRhs ); + } + else + { + compositionalMultiphaseWellKernels:: + FaceBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( numComponents, + dt, + dofManager.rankOffset(), + m_useTotalMassEquation, + wellDofKey, + well_controls, + subRegion, + localMatrix, + localRhs ); + } + } } ); } ); + } -void CompositionalMultiphaseWell::assembleAccumulationTerms( DomainPartition const & domain, +void CompositionalMultiphaseWell::assembleAccumulationTerms( real64 const & time, + real64 const & dt, + DomainPartition & domain, DofManager const & dofManager, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { GEOS_MARK_FUNCTION; - + GEOS_UNUSED_VAR( time ); + GEOS_UNUSED_VAR( dt ); string const wellDofKey = dofManager.getKey( wellElementDofName() ); - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel const & mesh, - arrayView1d< string const > const & regionNames ) + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) { - - ElementRegionManager const & elemManager = mesh.getElemManager(); - - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion const & subRegion ) + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) { + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString()); + MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + int numPhases = fluid.numFluidPhases(); + int numComponents = fluid.numFluidComponents(); + WellControls const & wellControls = getWellControls( subRegion ); + if( wellControls.isWellOpen( time+ dt ) ) + { + if( isThermal() ) + { - // get the degrees of freedom and ghosting info - arrayView1d< globalIndex const > const & wellElemDofNumber = - subRegion.getReference< array1d< globalIndex > >( wellDofKey ); - arrayView1d< integer const > const & wellElemGhostRank = subRegion.ghostRank(); - - // get the properties on the well element - arrayView2d< real64 const, compflow::USD_PHASE > const & wellElemPhaseVolFrac = - subRegion.getField< fields::well::phaseVolumeFraction >(); - arrayView3d< real64 const, compflow::USD_PHASE_DC > const & dWellElemPhaseVolFrac = - subRegion.getField< fields::well::dPhaseVolumeFraction >(); - - arrayView3d< real64 const, compflow::USD_COMP_DC > const & dWellElemCompFrac_dCompDens = - subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >(); - - arrayView2d< real64 const, compflow::USD_PHASE > const & wellElemPhaseVolFrac_n = - subRegion.getField< fields::well::phaseVolumeFraction_n >(); - - arrayView1d< real64 const > const & wellElemVolume = subRegion.getElementVolume(); + thermalCompositionalMultiphaseWellKernels:: + ElementBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( numComponents, + numPhases, + wellControls.isProducer(), + dofManager.rankOffset(), + m_useTotalMassEquation, + wellDofKey, + subRegion, + fluid, + localMatrix, + localRhs ); + } + else + { + compositionalMultiphaseWellKernels:: + ElementBasedAssemblyKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( numComponents, + numPhases, + wellControls.isProducer(), + dofManager.rankOffset(), + m_useTotalMassEquation, + wellDofKey, + subRegion, + fluid, + localMatrix, + localRhs ); + } + } + else + { + //wellControls.setWellOpen(false); + // get the degrees of freedom and ghosting info + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< integer const > const & wellElemGhostRank = subRegion.ghostRank(); + localIndex rank_offset = dofManager.rankOffset(); + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + if( wellElemGhostRank[ei] < 0 ) + { + globalIndex const dofIndex = wellElemDofNumber[ei]; + localIndex const localRow = dofIndex - rank_offset; - string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); - MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); - arrayView3d< real64 const, multifluid::USD_PHASE > const & wellElemPhaseDens = fluid.phaseDensity(); - arrayView4d< real64 const, multifluid::USD_PHASE_DC > const & dWellElemPhaseDens = fluid.dPhaseDensity(); - arrayView4d< real64 const, multifluid::USD_PHASE_COMP > const & wellElemPhaseCompFrac = fluid.phaseCompFraction(); - arrayView5d< real64 const, multifluid::USD_PHASE_COMP_DC > const & dWellElemPhaseCompFrac = fluid.dPhaseCompFraction(); - arrayView3d< real64 const, multifluid::USD_PHASE > const & wellElemPhaseDens_n = fluid.phaseDensity_n(); - arrayView4d< real64 const, multifluid::USD_PHASE_COMP > const & wellElemPhaseCompFrac_n = fluid.phaseCompFraction_n(); - - isothermalCompositionalMultiphaseBaseKernels:: - KernelLaunchSelector1< AccumulationKernel >( numFluidComponents(), - subRegion.size(), - numFluidPhases(), - dofManager.rankOffset(), - m_useTotalMassEquation, - wellElemDofNumber, - wellElemGhostRank, - wellElemVolume, - wellElemPhaseVolFrac, - dWellElemPhaseVolFrac, - dWellElemCompFrac_dCompDens, - wellElemPhaseDens, - dWellElemPhaseDens, - wellElemPhaseCompFrac, - dWellElemPhaseCompFrac, - wellElemPhaseVolFrac_n, - wellElemPhaseDens_n, - wellElemPhaseCompFrac_n, - localMatrix, - localRhs ); + real64 unity = 1.0; + for( integer i=0; i < m_numDofPerWellElement; i++ ) + { + globalIndex const rindex = localRow+i; + globalIndex const cindex =dofIndex + i; + localMatrix.template addToRow< serialAtomic >( rindex, + &cindex, + &unity, + 1 ); + localRhs[rindex] = 0.0; + } + } + } ); + } } ); } ); -} - - -void CompositionalMultiphaseWell::assembleVolumeBalanceTerms( DomainPartition const & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) -{ - GEOS_MARK_FUNCTION; - string const wellDofKey = dofManager.getKey( wellElementDofName() ); - - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel const & mesh, - arrayView1d< string const > const & regionNames ) - { - ElementRegionManager const & elemManager = mesh.getElemManager(); - - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion const & subRegion ) - { - // get the degrees of freedom and ghosting info - arrayView1d< globalIndex const > const & wellElemDofNumber = subRegion.getReference< array1d< globalIndex > >( wellDofKey ); - arrayView1d< integer const > const & wellElemGhostRank = subRegion.ghostRank(); - - // get the properties on the well element - arrayView2d< real64 const, compflow::USD_PHASE > const & wellElemPhaseVolFrac = - subRegion.getField< fields::well::phaseVolumeFraction >(); - arrayView3d< real64 const, compflow::USD_PHASE_DC > const & dWellElemPhaseVolFrac = - subRegion.getField< fields::well::dPhaseVolumeFraction >(); - - arrayView1d< real64 const > const & wellElemVolume = - subRegion.getReference< array1d< real64 > >( ElementSubRegionBase::viewKeyStruct::elementVolumeString() ); - - isothermalCompositionalMultiphaseBaseKernels:: - KernelLaunchSelector1< VolumeBalanceKernel >( numFluidComponents(), - subRegion.size(), - numFluidPhases(), - dofManager.rankOffset(), - wellElemDofNumber, - wellElemGhostRank, - wellElemPhaseVolFrac, - dWellElemPhaseVolFrac, - wellElemVolume, - localMatrix, - localRhs ); - } ); - } ); } @@ -1200,7 +1239,17 @@ CompositionalMultiphaseWell::calculateResidualNorm( real64 const & time_n, { GEOS_MARK_FUNCTION; - real64 localResidualNorm = 0.0; + integer numNorm = 1; // mass balance + array1d< real64 > localResidualNorm; + array1d< real64 > localResidualNormalizer; + + if( isThermal() ) + { + numNorm = 2; // mass balance and energy balance + } + localResidualNorm.resize( numNorm ); + localResidualNormalizer.resize( numNorm ); + globalIndex const rankOffset = dofManager.rankOffset(); string const wellDofKey = dofManager.getKey( wellElementDofName() ); @@ -1217,7 +1266,7 @@ CompositionalMultiphaseWell::calculateResidualNorm( real64 const & time_n, [&]( localIndex const, WellElementSubRegion const & subRegion ) { - real64 subRegionResidualNorm[1]{}; + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); @@ -1226,40 +1275,87 @@ CompositionalMultiphaseWell::calculateResidualNorm( real64 const & time_n, // step 1: compute the norm in the subRegion - ResidualNormKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_numComponents, - numDofPerWellElement(), - m_targetPhaseIndex, - rankOffset, - wellDofKey, - localRhs, - subRegion, - fluid, - wellControls, - time_n + dt, - dt, - m_nonlinearSolverParameters.m_minNormalizer, - subRegionResidualNorm ); - - // step 2: reduction across meshBodies/regions/subRegions - - if( subRegionResidualNorm[0] > localResidualNorm ) + if( isThermal() ) { - localResidualNorm = subRegionResidualNorm[0]; + real64 subRegionResidualNorm[2]{}; + + thermalCompositionalMultiphaseWellKernels::ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + m_targetPhaseIndex, + rankOffset, + wellDofKey, + localRhs, + subRegion, + fluid, + wellControls, + time_n + dt, + dt, + m_nonlinearSolverParameters.m_minNormalizer, + subRegionResidualNorm ); + // step 2: reduction across meshBodies/regions/subRegions + + for( integer i=0; i localResidualNorm[i] ) + { + localResidualNorm[i] = subRegionResidualNorm[i]; + } + } + } + else + { + real64 subRegionResidualNorm[1]{}; + ResidualNormKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + numDofPerWellElement(), + m_targetPhaseIndex, + rankOffset, + wellDofKey, + localRhs, + subRegion, + fluid, + wellControls, + time_n + dt, + dt, + m_nonlinearSolverParameters.m_minNormalizer, + subRegionResidualNorm ); + + + // step 2: reduction across meshBodies/regions/subRegions + + if( subRegionResidualNorm[0] > localResidualNorm[0] ) + { + localResidualNorm[0] = subRegionResidualNorm[0]; + } + } } ); } ); // step 3: second reduction across MPI ranks - - real64 const residualNorm = MpiWrapper::max( localResidualNorm ); - - if( getLogLevel() >= 1 && logger::internal::rank == 0 ) + real64 resNorm=localResidualNorm[0]; + if( isThermal() ) + { + real64 globalResidualNorm[2]{}; + globalResidualNorm[0] = MpiWrapper::max( localResidualNorm[0] ); + globalResidualNorm[1] = MpiWrapper::max( localResidualNorm[1] ); + resNorm=sqrt( globalResidualNorm[0] * globalResidualNorm[0] + globalResidualNorm[1] * globalResidualNorm[1] ); + if( getLogLevel() >= 1 && logger::internal::rank == 0 ) + { + std::cout << GEOS_FMT( " ( R{} ) = ( {:4.2e} ) ( Renergy ) = ( {:4.2e} )", + coupledSolverAttributePrefix(), globalResidualNorm[0], globalResidualNorm[1] ); + } + } + else { - std::cout << GEOS_FMT( " ( R{} ) = ( {:4.2e} )", coupledSolverAttributePrefix(), residualNorm ); + resNorm= MpiWrapper::max( resNorm ); + if( getLogLevel() >= 1 && logger::internal::rank == 0 ) + { + std::cout << GEOS_FMT( " ( R{} ) = ( {:4.2e} )", coupledSolverAttributePrefix(), resNorm ); + } } - return residualNorm; + return resNorm; } real64 @@ -1272,34 +1368,104 @@ CompositionalMultiphaseWell::scalingForSystemSolution( DomainPartition & domain, string const wellDofKey = dofManager.getKey( wellElementDofName() ); real64 scalingFactor = 1.0; - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - arrayView1d< string const > const & regionNames ) + real64 maxDeltaPres = 0.0, maxDeltaCompDens = 0.0, maxDeltaTemp = 0.0; + real64 minPresScalingFactor = 1.0, minCompDensScalingFactor = 1.0, minTempScalingFactor = 1.0; + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) { - mesh.getElemManager().forElementSubRegions< ElementSubRegionBase >( regionNames, - [&]( localIndex const, - ElementSubRegionBase & subRegion ) + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) { - // check that pressure and component densities are non-negative + arrayView1d< real64 const > const pressure = subRegion.getField< fields::well::pressure >(); + arrayView1d< real64 const > const temperature = subRegion.getField< fields::well::temperature >(); + arrayView2d< real64 const, compflow::USD_COMP > const compDens = subRegion.getField< fields::well::globalCompDensity >(); + arrayView1d< real64 > pressureScalingFactor = subRegion.getField< fields::well::pressureScalingFactor >(); + arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< fields::well::temperatureScalingFactor >(); + arrayView1d< real64 > compDensScalingFactor = subRegion.getField< fields::well::globalCompDensityScalingFactor >(); + const integer temperatureOffset = m_numComponents+2; auto const subRegionData = - compositionalMultiphaseWellKernels:: + m_isThermal + ? thermalCompositionalMultiphaseBaseKernels:: ScalingForSystemSolutionKernelFactory:: createAndLaunch< parallelDevicePolicy<> >( m_maxRelativePresChange, m_maxAbsolutePresChange, + m_maxRelativeTempChange, m_maxCompFracChange, m_maxRelativeCompDensChange, + pressure, + temperature, + compDens, + pressureScalingFactor, + compDensScalingFactor, + temperatureScalingFactor, + dofManager.rankOffset(), + m_numComponents, + wellDofKey, + subRegion, + localSolution, + temperatureOffset ) + : isothermalCompositionalMultiphaseBaseKernels:: + ScalingForSystemSolutionKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_maxRelativePresChange, + m_maxAbsolutePresChange, + m_maxCompFracChange, + m_maxRelativeCompDensChange, + pressure, + compDens, + pressureScalingFactor, + compDensScalingFactor, dofManager.rankOffset(), m_numComponents, wellDofKey, subRegion, localSolution ); + scalingFactor = std::min( subRegionData.localMinVal, scalingFactor ); - } ); + maxDeltaPres = std::max( maxDeltaPres, subRegionData.localMaxDeltaPres ); + maxDeltaCompDens = std::max( maxDeltaCompDens, subRegionData.localMaxDeltaCompDens ); + maxDeltaTemp = std::max( maxDeltaTemp, subRegionData.localMaxDeltaTemp ); + minPresScalingFactor = std::min( minPresScalingFactor, subRegionData.localMinPresScalingFactor ); + minCompDensScalingFactor = std::min( minCompDensScalingFactor, subRegionData.localMinCompDensScalingFactor ); + minTempScalingFactor = std::min( minTempScalingFactor, subRegionData.localMinTempScalingFactor ); + } ); } ); - return LvArray::math::max( MpiWrapper::min( scalingFactor ), m_minScalingFactor ); + scalingFactor = MpiWrapper::min( scalingFactor ); + maxDeltaPres = MpiWrapper::max( maxDeltaPres ); + maxDeltaCompDens = MpiWrapper::max( maxDeltaCompDens ); + minPresScalingFactor = MpiWrapper::min( minPresScalingFactor ); + minCompDensScalingFactor = MpiWrapper::min( minCompDensScalingFactor ); + + string const massUnit = m_useMass ? "kg/m3" : "mol/m3"; + GEOS_LOG_LEVEL_RANK_0( 1, GEOS_FMT( " {}: Max well pressure change: {} Pa (before scaling)", + getName(), GEOS_FMT( "{:.{}f}", maxDeltaPres, 3 ) ) ); + GEOS_LOG_LEVEL_RANK_0( 1, GEOS_FMT( " {}: Max well component density change: {} {} (before scaling)", + getName(), GEOS_FMT( "{:.{}f}", maxDeltaCompDens, 3 ), massUnit ) ); + + if( m_isThermal ) + { + maxDeltaTemp = MpiWrapper::max( maxDeltaTemp ); + minTempScalingFactor = MpiWrapper::min( minTempScalingFactor ); + GEOS_LOG_LEVEL_RANK_0( 1, GEOS_FMT( " {}: Max well temperature change: {} K (before scaling)", + getName(), GEOS_FMT( "{:.{}f}", maxDeltaTemp, 3 ) ) ); + } + + + GEOS_LOG_LEVEL_RANK_0( 1, GEOS_FMT( " {}: Min well pressure scaling factor: {}", getName(), minPresScalingFactor ) ); + GEOS_LOG_LEVEL_RANK_0( 1, GEOS_FMT( " {}: Min well component density scaling factor: {}", getName(), minCompDensScalingFactor ) ); + if( m_isThermal ) + { + GEOS_LOG_LEVEL_RANK_0( 1, GEOS_FMT( " {}: Min well temperature scaling factor: {}", getName(), minTempScalingFactor ) ); + } + + + return LvArray::math::max( scalingFactor, m_minScalingFactor ); + } bool @@ -1312,143 +1478,217 @@ CompositionalMultiphaseWell::checkSystemSolution( DomainPartition & domain, string const wellDofKey = dofManager.getKey( wellElementDofName() ); integer localCheck = 1; - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - arrayView1d< string const > const & regionNames ) + if( 0 ) { - mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, - [&]( localIndex const, - WellElementSubRegion & subRegion ) - { - auto const subRegionData = - compositionalMultiphaseWellKernels:: - SolutionCheckKernelFactory:: - createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping, - CompositionalMultiphaseFVM::ScalingType::Global, - scalingFactor, - dofManager.rankOffset(), - m_numComponents, - wellDofKey, - subRegion, - localSolution ); - if( !subRegionData.localMinVal ) + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) { - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Solution, - GEOS_FMT( "Solution is invalid in well {} (either a negative pressure or a negative component density was found)", subRegion.getName()) ); - } + arrayView1d< real64 const > const pressure = + subRegion.getField< fields::well::pressure >(); + arrayView2d< real64 const, compflow::USD_COMP > const compDens = + subRegion.getField< fields::well::globalCompDensity >(); + arrayView1d< real64 > pressureScalingFactor = subRegion.getField< fields::well::pressureScalingFactor >(); + arrayView1d< real64 > compDensScalingFactor = subRegion.getField< fields::well::globalCompDensityScalingFactor >(); + + auto const subRegionData = + compositionalMultiphaseWellKernels:: + SolutionCheckKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping, + CompositionalMultiphaseFVM::ScalingType::Global, + scalingFactor, + pressure, + compDens, + pressureScalingFactor, + compDensScalingFactor, + dofManager.rankOffset(), + m_numComponents, + wellDofKey, + subRegion, + localSolution ); + + if( !subRegionData.localMinVal ) + { + GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Solution, + GEOS_FMT( "Solution is invalid in well {} (either a negative pressure or a negative component density was found)", subRegion.getName()) ); + } - localCheck = std::min( localCheck, subRegionData.localMinVal ); + localCheck = std::min( localCheck, subRegionData.localMinVal ); + } ); } ); - } ); + } + else + { + + real64 minPres = 0.0, minDens = 0.0, minTotalDens = 0.0; + integer numNegPres = 0, numNegDens = 0, numNegTotalDens = 0; + + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + { + mesh.getElemManager().forElementSubRegions( regionNames, + [&]( localIndex const, + ElementSubRegionBase & subRegion ) + { + //integer const m_allowCompDensChopping(true); + integer const m_allowNegativePressure( false ); + CompositionalMultiphaseFVM::ScalingType const m_scalingType( CompositionalMultiphaseFVM::ScalingType::Global ); + arrayView1d< real64 const > const pressure = + subRegion.getField< fields::well::pressure >(); + arrayView1d< real64 const > const temperature = + subRegion.getField< fields::well::temperature >(); + arrayView2d< real64 const, compflow::USD_COMP > const compDens = + subRegion.getField< fields::well::globalCompDensity >(); + arrayView1d< real64 > pressureScalingFactor = subRegion.getField< fields::well::pressureScalingFactor >(); + arrayView1d< real64 > temperatureScalingFactor = subRegion.getField< fields::well::temperatureScalingFactor >(); + arrayView1d< real64 > compDensScalingFactor = subRegion.getField< fields::well::globalCompDensityScalingFactor >(); + + // check that pressure and component densities are non-negative + // for thermal, check that temperature is above 273.15 K + const integer temperatureOffset = m_numComponents+2; + auto const subRegionData = + m_isThermal + ? thermalCompositionalMultiphaseBaseKernels:: + SolutionCheckKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping, + m_allowNegativePressure, + m_scalingType, + scalingFactor, + pressure, + temperature, + compDens, + pressureScalingFactor, + temperatureScalingFactor, + compDensScalingFactor, + dofManager.rankOffset(), + m_numComponents, + wellDofKey, + subRegion, + localSolution, + temperatureOffset ) + : isothermalCompositionalMultiphaseBaseKernels:: + SolutionCheckKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_allowCompDensChopping, + m_allowNegativePressure, + m_scalingType, + scalingFactor, + pressure, + compDens, + pressureScalingFactor, + compDensScalingFactor, + dofManager.rankOffset(), + m_numComponents, + wellDofKey, + subRegion, + localSolution ); + + localCheck = std::min( localCheck, subRegionData.localMinVal ); + + minPres = std::min( minPres, subRegionData.localMinPres ); + minDens = std::min( minDens, subRegionData.localMinDens ); + minTotalDens = std::min( minTotalDens, subRegionData.localMinTotalDens ); + numNegPres += subRegionData.localNumNegPressures; + numNegDens += subRegionData.localNumNegDens; + numNegTotalDens += subRegionData.localNumNegTotalDens; + } ); + } ); + + minPres = MpiWrapper::min( minPres ); + minDens = MpiWrapper::min( minDens ); + minTotalDens = MpiWrapper::min( minTotalDens ); + numNegPres = MpiWrapper::sum( numNegPres ); + numNegDens = MpiWrapper::sum( numNegDens ); + numNegTotalDens = MpiWrapper::sum( numNegTotalDens ); + + if( numNegPres > 0 ) + GEOS_LOG_LEVEL_RANK_0( 1, GEOS_FMT( " {}: Number of negative well pressure values: {}, minimum value: {} Pa", + getName(), numNegPres, fmt::format( "{:.{}f}", minPres, 3 ) ) ); + string const massUnit = m_useMass ? "kg/m3" : "mol/m3"; + if( numNegDens > 0 ) + GEOS_LOG_LEVEL_RANK_0( 1, GEOS_FMT( " {}: Number of negative well component density values: {}, minimum value: {} {} ", + getName(), numNegDens, fmt::format( "{:.{}f}", minDens, 3 ), massUnit ) ); + if( minTotalDens > 0 ) + GEOS_LOG_LEVEL_RANK_0( 1, GEOS_FMT( " {}: Number of negative total well density values: {}, minimum value: {} {} ", + getName(), minTotalDens, fmt::format( "{:.{}f}", minDens, 3 ), massUnit ) ); + + } return MpiWrapper::min( localCheck ); } -void CompositionalMultiphaseWell::computePerforationRates( DomainPartition & domain ) +void CompositionalMultiphaseWell::computePerforationRates( real64 const & time_n, real64 const & dt, DomainPartition & domain ) { GEOS_MARK_FUNCTION; - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - arrayView1d< string const > const & regionNames ) + forDiscretizationOnMeshTargets ( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) { // TODO: change the way we access the flowSolver here CompositionalMultiphaseBase const & flowSolver = getParent().getGroup< CompositionalMultiphaseBase >( getFlowSolverName() ); - PerforationKernel::CompFlowAccessors resCompFlowAccessors( mesh.getElemManager(), flowSolver.getName() ); - PerforationKernel::MultiFluidAccessors resMultiFluidAccessors( mesh.getElemManager(), flowSolver.getName() ); - PerforationKernel::RelPermAccessors resRelPermAccessors( mesh.getElemManager(), flowSolver.getName() ); + ElementRegionManager & elemManager = mesh.getElemManager(); - mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, - WellElementSubRegion & subRegion ) + elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, + WellElementSubRegion & subRegion ) { - - WellControls const & wellControls = getWellControls( subRegion ); - bool const disableReservoirToWellFlow = wellControls.isInjector() and !wellControls.isCrossflowEnabled(); - PerforationData * const perforationData = subRegion.getPerforationData(); + WellControls const & wellControls = getWellControls( subRegion ); + if( wellControls.isWellOpen( time_n+ dt ) ) + { - // get depth - arrayView1d< real64 const > const & wellElemGravCoef = subRegion.getField< fields::well::gravityCoefficient >(); + bool const disableReservoirToWellFlow = wellControls.isInjector() and !wellControls.isCrossflowEnabled(); - // get well primary variables on well elements - arrayView1d< real64 const > const & wellElemPres = - subRegion.getField< fields::well::pressure >(); - arrayView2d< real64 const, compflow::USD_COMP > const & wellElemCompDens = - subRegion.getField< fields::well::globalCompDensity >(); - - arrayView1d< real64 const > const & wellElemTotalMassDens = - subRegion.getField< fields::well::totalMassDensity >(); - arrayView1d< real64 const > const & dWellElemTotalMassDens_dPres = - subRegion.getField< fields::well::dTotalMassDensity_dPressure >(); - arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens_dCompDens = - subRegion.getField< fields::well::dTotalMassDensity_dGlobalCompDensity >(); - - arrayView2d< real64 const, compflow::USD_COMP > const & wellElemCompFrac = - subRegion.getField< fields::well::globalCompFraction >(); - arrayView3d< real64 const, compflow::USD_COMP_DC > const & dWellElemCompFrac_dCompDens = - subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >(); - - // get well variables on perforations - arrayView1d< real64 const > const & perfGravCoef = - perforationData->getField< fields::well::gravityCoefficient >(); - arrayView1d< localIndex const > const perfWellElemIndex = - perforationData->getField< fields::perforation::wellElementIndex >(); - arrayView1d< real64 const > const perfTrans = - perforationData->getField< fields::perforation::wellTransmissibility >(); - - arrayView2d< real64 > const & compPerfRate = - perforationData->getField< fields::well::compPerforationRate >(); - arrayView3d< real64 > const & dCompPerfRate_dPres = - perforationData->getField< fields::well::dCompPerforationRate_dPres >(); - arrayView4d< real64 > const & dCompPerfRate_dComp = - perforationData->getField< fields::well::dCompPerforationRate_dComp >(); - - // get the element region, subregion, index - arrayView1d< localIndex const > const resElementRegion = - perforationData->getField< fields::perforation::reservoirElementRegion >(); - arrayView1d< localIndex const > const resElementSubRegion = - perforationData->getField< fields::perforation::reservoirElementSubRegion >(); - arrayView1d< localIndex const > const resElementIndex = - perforationData->getField< fields::perforation::reservoirElementIndex >(); - - isothermalCompositionalMultiphaseBaseKernels:: - KernelLaunchSelector2< PerforationKernel >( numFluidComponents(), - numFluidPhases(), - perforationData->size(), - disableReservoirToWellFlow, - resCompFlowAccessors.get( fields::flow::pressure{} ), - resCompFlowAccessors.get( fields::flow::phaseVolumeFraction{} ), - resCompFlowAccessors.get( fields::flow::dPhaseVolumeFraction{} ), - resCompFlowAccessors.get( fields::flow::dGlobalCompFraction_dGlobalCompDensity{} ), - resMultiFluidAccessors.get( fields::multifluid::phaseDensity{} ), - resMultiFluidAccessors.get( fields::multifluid::dPhaseDensity{} ), - resMultiFluidAccessors.get( fields::multifluid::phaseViscosity{} ), - resMultiFluidAccessors.get( fields::multifluid::dPhaseViscosity{} ), - resMultiFluidAccessors.get( fields::multifluid::phaseCompFraction{} ), - resMultiFluidAccessors.get( fields::multifluid::dPhaseCompFraction{} ), - resRelPermAccessors.get( fields::relperm::phaseRelPerm{} ), - resRelPermAccessors.get( fields::relperm::dPhaseRelPerm_dPhaseVolFraction{} ), - wellElemGravCoef, - wellElemPres, - wellElemCompDens, - wellElemTotalMassDens, - dWellElemTotalMassDens_dPres, - dWellElemTotalMassDens_dCompDens, - wellElemCompFrac, - dWellElemCompFrac_dCompDens, - perfGravCoef, - perfWellElemIndex, - perfTrans, - resElementRegion, - resElementSubRegion, - resElementIndex, - compPerfRate, - dCompPerfRate_dPres, - dCompPerfRate_dComp ); + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + bool isThermal = fluid.isThermal(); + if( isThermal ) + { + thermalPerforationFluxKernels:: + PerforationFluxKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + m_numPhases, + flowSolver.getName(), + perforationData, + subRegion, + fluid, + elemManager, + disableReservoirToWellFlow ); + } + else + { + isothermalPerforationFluxKernels:: + PerforationFluxKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( m_numComponents, + m_numPhases, + flowSolver.getName(), + perforationData, + subRegion, + elemManager, + disableReservoirToWellFlow ); + } + } + else + { + // Zero completion flow rate + arrayView2d< real64 > const compPerfRate = perforationData->getField< fields::well::compPerforationRate >(); + for( integer iperf=0; iperfsize(); iperf++ ) + { + for( integer ic = 0; ic < m_numComponents; ++ic ) + { + compPerfRate[iperf][ic] = 0.0; + } + } + } } ); + } ); } @@ -1461,26 +1701,42 @@ CompositionalMultiphaseWell::applySystemSolution( DofManager const & dofManager, real64 const dt, DomainPartition & domain ) { + + + DofManager::CompMask pressureMask( m_numDofPerWellElement, 0, 1 ); + DofManager::CompMask componentMask( m_numDofPerWellElement, 1, numFluidComponents()+1 ); + DofManager::CompMask connRateMask( m_numDofPerWellElement, numFluidComponents()+1, numFluidComponents()+2 ); GEOS_UNUSED_VAR( dt ); // update all the fields using the global damping coefficients dofManager.addVectorToField( localSolution, wellElementDofName(), fields::well::pressure::key(), scalingFactor, - { m_numDofPerWellElement, 0, 1 } ); + pressureMask ); dofManager.addVectorToField( localSolution, wellElementDofName(), fields::well::globalCompDensity::key(), scalingFactor, - { m_numDofPerWellElement, 1, m_numDofPerWellElement - 1 } ); + componentMask ); dofManager.addVectorToField( localSolution, wellElementDofName(), fields::well::mixtureConnectionRate::key(), scalingFactor, - { m_numDofPerWellElement, m_numDofPerWellElement - 1, m_numDofPerWellElement } ); + connRateMask ); + + if( isThermal() ) + { + DofManager::CompMask temperatureMask( m_numDofPerWellElement, numFluidComponents()+2, numFluidComponents()+3 ); + dofManager.addVectorToField( localSolution, + wellElementDofName(), + fields::well::temperature::key(), + scalingFactor, + temperatureMask ); + + } // if component density chopping is allowed, some component densities may be negative after the update // these negative component densities are set to zero in this function if( m_allowCompDensChopping ) @@ -1494,17 +1750,28 @@ CompositionalMultiphaseWell::applySystemSolution( DofManager const & dofManager, { // synchronize FieldIdentifiers fieldsToBeSync; - - fieldsToBeSync.addElementFields( { fields::well::pressure::key(), - fields::well::globalCompDensity::key(), - fields::well::mixtureConnectionRate::key() }, - regionNames ); - + if( isThermal() ) + { + fieldsToBeSync.addElementFields( { fields::well::pressure::key(), + fields::well::globalCompDensity::key(), + fields::well::mixtureConnectionRate::key(), + fields::well::temperature::key() }, + regionNames ); + } + else + { + fieldsToBeSync.addElementFields( { fields::well::pressure::key(), + fields::well::globalCompDensity::key(), + fields::well::mixtureConnectionRate::key() }, + regionNames ); + } CommunicationTools::getInstance().synchronizeFields( fieldsToBeSync, mesh, domain.getNeighbors(), true ); } ); + + } void CompositionalMultiphaseWell::chopNegativeDensities( DomainPartition & domain ) @@ -1568,6 +1835,15 @@ void CompositionalMultiphaseWell::resetStateToBeginningOfStep( DomainPartition & subRegion.getField< fields::well::pressure_n >(); wellElemPressure.setValues< parallelDevicePolicy<> >( wellElemPressure_n ); + if( isThermal() ) + { + // get a reference to the primary variables on well elements + arrayView1d< real64 > const & wellElemTemperature = + subRegion.getField< fields::well::temperature >(); + arrayView1d< real64 const > const & wellElemTemperature_n = + subRegion.getField< fields::well::temperature_n >(); + wellElemTemperature.setValues< parallelDevicePolicy<> >( wellElemTemperature_n ); + } arrayView2d< real64, compflow::USD_COMP > const & wellElemGlobalCompDensity = subRegion.getField< fields::well::globalCompDensity >(); arrayView2d< real64 const, compflow::USD_COMP > const & wellElemGlobalCompDensity_n = @@ -1608,81 +1884,84 @@ void CompositionalMultiphaseWell::assemblePressureRelations( real64 const & time WellControls & wellControls = getWellControls( subRegion ); - // get the degrees of freedom, depth info, next welem index - string const wellDofKey = dofManager.getKey( wellElementDofName() ); - arrayView1d< globalIndex const > const & wellElemDofNumber = - subRegion.getReference< array1d< globalIndex > >( wellDofKey ); - arrayView1d< real64 const > const & wellElemGravCoef = - subRegion.getField< fields::well::gravityCoefficient >(); - arrayView1d< localIndex const > const & nextWellElemIndex = - subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString() ); - - // get primary variables on well elements - arrayView1d< real64 const > const & wellElemPres = - subRegion.getField< fields::well::pressure >(); - - // get total mass density on well elements (for potential calculations) - arrayView1d< real64 const > const & wellElemTotalMassDens = - subRegion.getField< fields::well::totalMassDensity >(); - arrayView1d< real64 const > const & dWellElemTotalMassDens_dPres = - subRegion.getField< fields::well::dTotalMassDensity_dPressure >(); - arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens_dCompDens = - subRegion.getField< fields::well::dTotalMassDensity_dGlobalCompDensity >(); - - - bool controlHasSwitched = false; - isothermalCompositionalMultiphaseBaseKernels:: - KernelLaunchSelector1< PressureRelationKernel >( numFluidComponents(), - subRegion.size(), - dofManager.rankOffset(), - subRegion.isLocallyOwned(), - subRegion.getTopWellElementIndex(), - m_targetPhaseIndex, - wellControls, - time_n + dt, // controls evaluated with BHP/rate of the end of step - wellElemDofNumber, - wellElemGravCoef, - nextWellElemIndex, - wellElemPres, - wellElemTotalMassDens, - dWellElemTotalMassDens_dPres, - dWellElemTotalMassDens_dCompDens, - controlHasSwitched, - localMatrix, - localRhs ); - - if( controlHasSwitched ) + if( wellControls.isWellOpen( time_n+ dt ) ) { - // TODO: move the switch logic into wellControls - // TODO: implement a more general switch when more then two constraints per well type are allowed + string const & fluidName = subRegion.getReference< string >( viewKeyStruct::fluidNamesString() ); + MultiFluidBase const & fluid = getConstitutiveModel< MultiFluidBase >( subRegion, fluidName ); + bool isThermal = fluid.isThermal(); + // get the degrees of freedom, depth info, next welem index + string const wellDofKey = dofManager.getKey( wellElementDofName() ); + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + arrayView1d< real64 const > const & wellElemGravCoef = + subRegion.getField< fields::well::gravityCoefficient >(); + arrayView1d< localIndex const > const & nextWellElemIndex = + subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString() ); + + // get primary variables on well elements + arrayView1d< real64 const > const & wellElemPres = + subRegion.getField< fields::well::pressure >(); + + // get total mass density on well elements (for potential calculations) + arrayView1d< real64 const > const & wellElemTotalMassDens = + subRegion.getField< fields::well::totalMassDensity >(); + arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens = + subRegion.getField< fields::well::dTotalMassDensity >(); + + bool controlHasSwitched = false; + isothermalCompositionalMultiphaseBaseKernels:: + KernelLaunchSelectorCompTherm< PressureRelationKernel >( numFluidComponents(), + isThermal, + subRegion.size(), + dofManager.rankOffset(), + subRegion.isLocallyOwned(), + subRegion.getTopWellElementIndex(), + m_targetPhaseIndex, + wellControls, + time_n + dt, // controls evaluated with BHP/rate of the end of step + wellElemDofNumber, + wellElemGravCoef, + nextWellElemIndex, + wellElemPres, + wellElemTotalMassDens, + dWellElemTotalMassDens, + controlHasSwitched, + localMatrix, + localRhs ); + + if( controlHasSwitched ) + { + // TODO: move the switch logic into wellControls + // TODO: implement a more general switch when more then two constraints per well type are allowed - real64 const timeAtEndOfStep = time_n + dt; + real64 const timeAtEndOfStep = time_n + dt; - if( wellControls.getControl() == WellControls::Control::BHP ) - { - if( wellControls.isProducer() ) + if( wellControls.getControl() == WellControls::Control::BHP ) { - wellControls.switchToPhaseRateControl( wellControls.getTargetPhaseRate( timeAtEndOfStep ) ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::WellControl, - GEOS_FMT( "Control switch for well {} from BHP constraint to phase volumetric rate constraint", subRegion.getName() ) ); + if( wellControls.isProducer() ) + { + wellControls.switchToPhaseRateControl( wellControls.getTargetPhaseRate( timeAtEndOfStep ) ); + GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::WellControl, + GEOS_FMT( "Control switch for well {} from BHP constraint to phase volumetric rate constraint", subRegion.getName() ) ); + } + else + { + wellControls.switchToTotalRateControl( wellControls.getTargetTotalRate( timeAtEndOfStep ) ); + GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::WellControl, + GEOS_FMT( "Control switch for well {} from BHP constraint to total volumetric rate constraint", subRegion.getName()) ); + } } else { - wellControls.switchToTotalRateControl( wellControls.getTargetTotalRate( timeAtEndOfStep ) ); + wellControls.switchToBHPControl( wellControls.getTargetBHP( timeAtEndOfStep ) ); GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::WellControl, - GEOS_FMT( "Control switch for well {} from BHP constraint to total volumetric rate constraint", subRegion.getName()) ); + GEOS_FMT( "Control switch for well {} from rate constraint to BHP constraint", subRegion.getName() ) ); } } - else - { - wellControls.switchToBHPControl( wellControls.getTargetBHP( timeAtEndOfStep ) ); - GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::WellControl, - GEOS_FMT( "Control switch for well {} from rate constraint to BHP constraint", subRegion.getName() ) ); - } } + } ); - } - ); + } ); } void CompositionalMultiphaseWell::shutDownWell( real64 const time_n, @@ -1747,7 +2026,7 @@ void CompositionalMultiphaseWell::shutDownWell( real64 const time_n, rankOffset, localMatrix, rhsValue, - pres[ei], // freeze the current pressure value + pres[ei], // freeze the current pressure value pres[ei] ); localRhs[localRow] = rhsValue; @@ -1758,7 +2037,7 @@ void CompositionalMultiphaseWell::shutDownWell( real64 const time_n, rankOffset, localMatrix, rhsValue, - compDens[ei][ic], // freeze the current component density values + compDens[ei][ic], // freeze the current component density values compDens[ei][ic] ); localRhs[localRow + ic + 1] = rhsValue; } @@ -1768,7 +2047,7 @@ void CompositionalMultiphaseWell::shutDownWell( real64 const time_n, rankOffset, localMatrix, rhsValue, - connRate[ei], // freeze the current pressure value + connRate[ei], // freeze the current pressure value connRate[ei] ); localRhs[localRow + numComp + 1] = rhsValue; @@ -1794,15 +2073,27 @@ void CompositionalMultiphaseWell::implicitStepSetup( real64 const & time_n, [&]( localIndex const, WellElementSubRegion & subRegion ) { + // get a reference to the primary variables on well elements arrayView1d< real64 const > const & wellElemPressure = subRegion.getField< fields::well::pressure >(); + arrayView2d< real64 const, compflow::USD_COMP > const & wellElemGlobalCompDensity = + subRegion.getField< fields::well::globalCompDensity >(); + arrayView1d< real64 const > const & wellElemTemperature = + subRegion.getField< fields::well::temperature >(); + arrayView1d< real64 > const & wellElemPressure_n = subRegion.getField< fields::well::pressure_n >(); wellElemPressure_n.setValues< parallelDevicePolicy<> >( wellElemPressure ); - arrayView2d< real64 const, compflow::USD_COMP > const & wellElemGlobalCompDensity = - subRegion.getField< fields::well::globalCompDensity >(); + if( isThermal() ) + { + + arrayView1d< real64 > const & wellElemTemperature_n = + subRegion.getField< fields::well::temperature_n >(); + wellElemTemperature_n.setValues< parallelDevicePolicy<> >( wellElemTemperature ); + } + arrayView2d< real64, compflow::USD_COMP > const & wellElemGlobalCompDensity_n = subRegion.getField< fields::well::globalCompDensity_n >(); wellElemGlobalCompDensity_n.setValues< parallelDevicePolicy<> >( wellElemGlobalCompDensity ); diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp index c524575b860..c996896734c 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp @@ -162,7 +162,7 @@ class CompositionalMultiphaseWell : public WellSolverBase * @param subRegion the well subregion containing all the primary and dependent fields * @param targetIndex the targetIndex of the subRegion */ - void updatePhaseVolumeFraction( WellElementSubRegion & subRegion ) const; + real64 updatePhaseVolumeFraction( WellElementSubRegion & subRegion ) const; /** * @brief Recompute total mass densities from mass density and phase volume fractions @@ -174,13 +174,16 @@ class CompositionalMultiphaseWell : public WellSolverBase * @brief Recompute the perforation rates for all the wells * @param domain the domain containing the mesh and fields */ - virtual void computePerforationRates( DomainPartition & domain ) override; + virtual void computePerforationRates( real64 const & time_n, + real64 const & dt, DomainPartition & domain ) override; /** * @brief Recompute all dependent quantities from primary variables (including constitutive models) * @param subRegion the well subregion containing all the primary and dependent fields */ - virtual void updateSubRegionState( WellElementSubRegion & subRegion ) override; + virtual void updateState( DomainPartition & domain ) override; + + virtual real64 updateSubRegionState( WellElementSubRegion & subRegion ) override; virtual string wellElementDofName() const override { return viewKeyStruct::dofFieldString(); } @@ -190,6 +193,8 @@ class CompositionalMultiphaseWell : public WellSolverBase virtual localIndex numFluidPhases() const override { return m_numPhases; } + integer useTotalMassEquation() const { return m_useTotalMassEquation; } + /** * @brief assembles the flux terms for all connections between well elements * @param time_n previous time value @@ -199,12 +204,13 @@ class CompositionalMultiphaseWell : public WellSolverBase * @param matrix the system matrix * @param rhs the system right-hand side vector */ - virtual void assembleFluxTerms( real64 const dt, - DomainPartition const & domain, + + virtual void assembleFluxTerms( real64 const & time_n, + real64 const & dt, + DomainPartition & domain, DofManager const & dofManager, CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) override; - + arrayView1d< real64 > const & localRhs )override; /** * @brief assembles the accumulation term for all the well elements * @param domain the physical domain object @@ -212,23 +218,13 @@ class CompositionalMultiphaseWell : public WellSolverBase * @param matrix the system matrix * @param rhs the system right-hand side vector */ - virtual void assembleAccumulationTerms( DomainPartition const & domain, + virtual void assembleAccumulationTerms( real64 const & time_n, + real64 const & dt, + DomainPartition & domain, DofManager const & dofManager, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) override; - /** - * @brief assembles the volume balance terms for all well elements - * @param domain the physical domain object - * @param dofManager degree-of-freedom manager associated with the linear system - * @param matrix the system matrix - * @param rhs the system right-hand side vector - */ - virtual void assembleVolumeBalanceTerms( DomainPartition const & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) override; - /** * @brief assembles the pressure relations at all connections between well elements except at the well head * @param time_n time at the beginning of the time step @@ -289,18 +285,23 @@ class CompositionalMultiphaseWell : public WellSolverBase static constexpr char const * maxRelativeCompDensChangeString() { return "maxRelativeCompDensChange"; } + static constexpr char const * maxRelativeTempChangeString() { return "maxRelativeTemperatureChange"; } + static constexpr char const * allowLocalCompDensChoppingString() { return CompositionalMultiphaseBase::viewKeyStruct::allowLocalCompDensChoppingString(); } // control data (not registered on the mesh) static constexpr char const * massDensityString() { return "massDensity";} + static constexpr char const * currentBHPString() { return "currentBHP"; } + static constexpr char const * dCurrentBHPString() { return "dCurrentBHP"; } static constexpr char const * dCurrentBHP_dPresString() { return "dCurrentBHP_dPres"; } - static constexpr char const * dCurrentBHP_dCompDensString() { return "dCurrentBHP_dCompDens"; } static constexpr char const * currentPhaseVolRateString() { return "currentPhaseVolumetricRate"; } + static constexpr char const * dCurrentPhaseVolRateString() { return "dCurrentPhaseVolumetricRate"; } + static constexpr char const * dCurrentPhaseVolRate_dPresString() { return "dCurrentPhaseVolumetricRate_dPres"; } @@ -309,6 +310,7 @@ class CompositionalMultiphaseWell : public WellSolverBase static constexpr char const * dCurrentPhaseVolRate_dRateString() { return "dCurrentPhaseVolumetricRate_dRate"; } static constexpr char const * currentTotalVolRateString() { return "currentTotalVolumetricRate"; } + static constexpr char const * dCurrentTotalVolRateString() { return "dCurrentTotalVolumetricRate"; } static constexpr char const * dCurrentTotalVolRate_dPresString() { return "dCurrentTotalVolumetricRate_dPres"; } @@ -369,15 +371,11 @@ class CompositionalMultiphaseWell : public WellSolverBase * @brief Initialize all the primary and secondary variables in all the wells * @param domain the domain containing the well manager to access individual wells */ - void initializeWells( DomainPartition & domain ) override; + void initializeWells( DomainPartition & domain, real64 const & time_n, real64 const & dt ) override; virtual void setConstitutiveNames( ElementSubRegionBase & subRegion ) const override; - /// the max number of fluid phases - integer m_numPhases; - /// the number of fluid components - integer m_numComponents; /// flag indicating whether mass or molar formulation should be used integer m_useMass; @@ -400,6 +398,9 @@ class CompositionalMultiphaseWell : public WellSolverBase /// maximum (relative) change in component density between two Newton iterations real64 m_maxRelativeCompDensChange; + /// maximum (relative) change in temperature in a Newton iteration + real64 m_maxRelativeTempChange; + /// minimum value of the scaling factor obtained by enforcing maxCompFracChange real64 m_minScalingFactor; @@ -409,8 +410,7 @@ class CompositionalMultiphaseWell : public WellSolverBase /// index of the target phase, used to impose the phase rate constraint localIndex m_targetPhaseIndex; - /// name of the fluid constitutive model used as a reference for component/phase description - string m_referenceFluidModelName; + }; diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp index 88d502f7941..be0aa3e48ae 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp @@ -122,21 +122,14 @@ DECLARE_FIELD( totalMassDensity, WRITE_AND_READ, "Total mass density" ); -DECLARE_FIELD( dTotalMassDensity_dPressure, - "dTotalMassDensity_dPressure", - array1d< real64 >, - 0, - NOPLOT, - NO_WRITE, - "Derivative of total mass density with respect to pressure" ); - -DECLARE_FIELD( dTotalMassDensity_dGlobalCompDensity, - "dTotalMassDensity_dComp", // to avoid a rebaseline +DECLARE_FIELD( dTotalMassDensity, + "dTotalMassDensity", array2dLayoutFluid_dC, 0, NOPLOT, NO_WRITE, - "Derivative of total mass density with respect to global component density" ); + "Derivative of total mass density with respect to pressure, temperature, and global component density" ); + DECLARE_FIELD( compPerforationRate, "compPerforationRate", @@ -146,21 +139,15 @@ DECLARE_FIELD( compPerforationRate, WRITE_AND_READ, "Component perforation rate" ); -DECLARE_FIELD( dCompPerforationRate_dPres, - "dCompPerforationRate_dPres", - array3d< real64 >, - 0, - NOPLOT, - NO_WRITE, - "Derivative of component perforation rate with respect to pressure" ); - -DECLARE_FIELD( dCompPerforationRate_dComp, - "dCompPerforationRate_dComp", +DECLARE_FIELD( dCompPerforationRate, + "dCompPerforationRate", array4d< real64 >, 0, NOPLOT, NO_WRITE, - "Derivative of component perforation rate with respect to global component density" ); + "Derivative of component perforation rate with respect to pressure temperature and global component density" ); + + DECLARE_FIELD( globalCompDensityScalingFactor, "globalCompDensityScalingFactor", @@ -170,6 +157,7 @@ DECLARE_FIELD( globalCompDensityScalingFactor, NO_WRITE, "Scaling factors for global component densities" ); + } } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellKernels.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellKernels.cpp index 4e0d72499a4..7b592d049d5 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellKernels.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellKernels.cpp @@ -121,7 +121,7 @@ ControlEquationHelper:: } } -template< integer NC > +template< integer NC, integer IS_THERMAL > GEOS_HOST_DEVICE inline void @@ -134,35 +134,30 @@ ControlEquationHelper:: real64 const & targetTotalRate, real64 const & targetMassRate, real64 const & currentBHP, - real64 const & dCurrentBHP_dPres, - arrayView1d< real64 const > const & dCurrentBHP_dCompDens, + arrayView1d< real64 const > const & dCurrentBHP, arrayView1d< real64 const > const & currentPhaseVolRate, - arrayView1d< real64 const > const & dCurrentPhaseVolRate_dPres, - arrayView2d< real64 const > const & dCurrentPhaseVolRate_dCompDens, - arrayView1d< real64 const > const & dCurrentPhaseVolRate_dRate, + arrayView2d< real64 const > const & dCurrentPhaseVolRate, + real64 const & currentTotalVolRate, - real64 const & dCurrentTotalVolRate_dPres, - arrayView1d< real64 const > const & dCurrentTotalVolRate_dCompDens, - real64 const & dCurrentTotalVolRate_dRate, + arrayView1d< real64 const > const & dCurrentTotalVolRate, real64 const & massDensity, globalIndex const dofNumber, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { - localIndex const eqnRowIndex = dofNumber + ROFFSET::CONTROL - rankOffset; - globalIndex const presDofColIndex = dofNumber + COFFSET::DPRES; - globalIndex const rateDofColIndex = dofNumber + COFFSET::DCOMP + NC; - globalIndex compDofColIndices[NC]{}; - for( integer ic = 0; ic < NC; ++ic ) + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using Deriv = multifluid::DerivativeOffset; + + localIndex const eqnRowIndex = dofNumber + ROFFSET::CONTROL - rankOffset; + globalIndex dofColIndices[COFFSET_WJ::nDer]{}; + for( integer ic = 0; ic < COFFSET_WJ::nDer; ++ic ) { - compDofColIndices[ ic ] = presDofColIndex + ic + 1; + dofColIndices[ ic ] = dofNumber + ic; } real64 controlEqn = 0; - real64 dControlEqn_dPres = 0; - real64 dControlEqn_dRate = 0; - real64 dControlEqn_dComp[NC]{}; + real64 dControlEqn[NC+2+IS_THERMAL]{}; // Note: We assume in the computation of currentBHP that the reference elevation // is in the top well element. This is enforced by a check in the solver. @@ -175,392 +170,83 @@ ControlEquationHelper:: { // control equation is a difference between current BHP and target BHP controlEqn = currentBHP - targetBHP; - dControlEqn_dPres = dCurrentBHP_dPres; + dControlEqn[COFFSET_WJ::dP] = dCurrentBHP[Deriv::dP]; for( integer ic = 0; ic < NC; ++ic ) { - dControlEqn_dComp[ic] = dCurrentBHP_dCompDens[ic]; + dControlEqn[COFFSET_WJ::dC+ic] = dCurrentBHP[Deriv::dC+ic]; } + if constexpr ( IS_THERMAL ) + + dControlEqn[COFFSET_WJ::dT] = dCurrentBHP[Deriv::dT]; + } // Oil volumetric rate control else if( currentControl == WellControls::Control::PHASEVOLRATE ) { controlEqn = currentPhaseVolRate[targetPhaseIndex] - targetPhaseRate; - dControlEqn_dPres = dCurrentPhaseVolRate_dPres[targetPhaseIndex]; - dControlEqn_dRate = dCurrentPhaseVolRate_dRate[targetPhaseIndex]; + dControlEqn[COFFSET_WJ::dP] = dCurrentPhaseVolRate[targetPhaseIndex][COFFSET_WJ::dP]; + dControlEqn[COFFSET_WJ::dQ] = dCurrentPhaseVolRate[targetPhaseIndex][COFFSET_WJ::dQ]; for( integer ic = 0; ic < NC; ++ic ) { - dControlEqn_dComp[ic] = dCurrentPhaseVolRate_dCompDens[targetPhaseIndex][ic]; + dControlEqn[COFFSET_WJ::dC+ic] = dCurrentPhaseVolRate[targetPhaseIndex][COFFSET_WJ::dC+ic]; } + if constexpr ( IS_THERMAL ) + dControlEqn[COFFSET_WJ::dT] = dCurrentBHP[Deriv::dT]; } // Total volumetric rate control else if( currentControl == WellControls::Control::TOTALVOLRATE ) { controlEqn = currentTotalVolRate - targetTotalRate; - dControlEqn_dPres = dCurrentTotalVolRate_dPres; - dControlEqn_dRate = dCurrentTotalVolRate_dRate; + dControlEqn[COFFSET_WJ::dP] = dCurrentTotalVolRate[COFFSET_WJ::dP]; + dControlEqn[COFFSET_WJ::dQ] = dCurrentTotalVolRate[COFFSET_WJ::dQ]; for( integer ic = 0; ic < NC; ++ic ) { - dControlEqn_dComp[ic] = dCurrentTotalVolRate_dCompDens[ic]; + dControlEqn[COFFSET_WJ::dC+ic] = dCurrentTotalVolRate[COFFSET_WJ::dC+ic]; } + if constexpr ( IS_THERMAL ) + dControlEqn[COFFSET_WJ::dT] = dCurrentTotalVolRate[COFFSET_WJ::dT]; } // Total mass rate control else if( currentControl == WellControls::Control::MASSRATE ) { controlEqn = massDensity*currentTotalVolRate - targetMassRate; - dControlEqn_dPres = massDensity*dCurrentTotalVolRate_dPres; - dControlEqn_dRate = massDensity*dCurrentTotalVolRate_dRate; + dControlEqn[COFFSET_WJ::dP] = massDensity*dCurrentTotalVolRate[COFFSET_WJ::dP]; + dControlEqn[COFFSET_WJ::dQ] = massDensity*dCurrentTotalVolRate[COFFSET_WJ::dQ]; for( integer ic = 0; ic < NC; ++ic ) { - dControlEqn_dComp[ic] = massDensity*dCurrentTotalVolRate_dCompDens[ic]; + dControlEqn[COFFSET_WJ::dC+ic] = massDensity*dCurrentTotalVolRate[COFFSET_WJ::dC+ic]; } + if constexpr ( IS_THERMAL ) + dControlEqn[COFFSET_WJ::dT] = massDensity*dCurrentTotalVolRate[COFFSET_WJ::dT]; } - else - { - GEOS_ERROR( "This constraint is not supported in CompositionalMultiphaseWell" ); - } - localRhs[eqnRowIndex] += controlEqn; - localMatrix.addToRow< serialAtomic >( eqnRowIndex, - &presDofColIndex, - &dControlEqn_dPres, - 1 ); - localMatrix.addToRow< serialAtomic >( eqnRowIndex, - &rateDofColIndex, - &dControlEqn_dRate, - 1 ); - localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, - compDofColIndices, - dControlEqn_dComp, - NC ); -} - -/******************************** FluxKernel ********************************/ - -template< integer NC > -GEOS_HOST_DEVICE -void -FluxKernel:: - computeExit( real64 const & dt, - real64 const ( &compFlux )[NC], - real64 const ( &dCompFlux_dRate )[NC], - real64 const ( &dCompFlux_dPresUp )[NC], - real64 const ( &dCompFlux_dCompDensUp )[NC][NC], - real64 ( & oneSidedFlux )[NC], - real64 ( & oneSidedFluxJacobian_dRate )[NC][1], - real64 ( & oneSidedFluxJacobian_dPresCompUp )[NC][NC + 1] ) -{ - for( integer ic = 0; ic < NC; ++ic ) + // Total mass rate control + else if( currentControl == WellControls::Control::MASSRATE ) { - oneSidedFlux[ic] = -dt * compFlux[ic]; - - // derivative with respect to rate - oneSidedFluxJacobian_dRate[ic][0] = -dt * dCompFlux_dRate[ic]; - - // derivative with respect to upstream pressure - oneSidedFluxJacobian_dPresCompUp[ic][0] = -dt * dCompFlux_dPresUp[ic]; - - // derivatives with respect to upstream component densities - for( integer jdof = 0; jdof < NC; ++jdof ) + controlEqn = massDensity*currentTotalVolRate - targetMassRate; + dControlEqn[COFFSET_WJ::dP] = massDensity*dCurrentTotalVolRate[COFFSET_WJ::dP]; + dControlEqn[COFFSET_WJ::dQ] = massDensity*dCurrentTotalVolRate[COFFSET_WJ::dQ]; + for( integer ic = 0; ic < NC; ++ic ) { - oneSidedFluxJacobian_dPresCompUp[ic][jdof+1] = -dt * dCompFlux_dCompDensUp[ic][jdof]; + dControlEqn[COFFSET_WJ::dC+ic] = massDensity*dCurrentTotalVolRate[COFFSET_WJ::dC+ic]; } } -} - -template< integer NC > -GEOS_HOST_DEVICE -void -FluxKernel:: - compute( real64 const & dt, - real64 const ( &compFlux )[NC], - real64 const ( &dCompFlux_dRate )[NC], - real64 const ( &dCompFlux_dPresUp )[NC], - real64 const ( &dCompFlux_dCompDensUp )[NC][NC], - real64 ( & localFlux )[2*NC], - real64 ( & localFluxJacobian_dRate )[2*NC][1], - real64 ( & localFluxJacobian_dPresCompUp )[2*NC][NC + 1] ) -{ - // flux terms - for( integer ic = 0; ic < NC; ++ic ) + else { - localFlux[TAG::NEXT *NC+ic] = dt * compFlux[ic]; - localFlux[TAG::CURRENT *NC+ic] = -dt * compFlux[ic]; - - // derivative with respect to rate - localFluxJacobian_dRate[TAG::NEXT *NC+ic][0] = dt * dCompFlux_dRate[ic]; - localFluxJacobian_dRate[TAG::CURRENT *NC+ic][0] = -dt * dCompFlux_dRate[ic]; - - // derivative with respect to upstream pressure - localFluxJacobian_dPresCompUp[TAG::NEXT *NC+ic][0] = dt * dCompFlux_dPresUp[ic]; - localFluxJacobian_dPresCompUp[TAG::CURRENT *NC+ic][0] = -dt * dCompFlux_dPresUp[ic]; - - // derivatives with respect to upstream component densities - for( integer jdof = 0; jdof < NC; ++jdof ) - { - localFluxJacobian_dPresCompUp[TAG::NEXT *NC+ic][jdof+1] = dt * dCompFlux_dCompDensUp[ic][jdof]; - localFluxJacobian_dPresCompUp[TAG::CURRENT *NC+ic][jdof+1] = -dt * dCompFlux_dCompDensUp[ic][jdof]; - } + GEOS_ERROR( "This constraint is not supported in CompositionalMultiphaseWell" ); } -} - -template< integer NC > -void -FluxKernel:: - launch( localIndex const size, - globalIndex const rankOffset, - integer const useTotalMassEquation, - WellControls const & wellControls, - arrayView1d< globalIndex const > const & wellElemDofNumber, - arrayView1d< localIndex const > const & nextWellElemIndex, - arrayView1d< real64 const > const & connRate, - arrayView2d< real64 const, compflow::USD_COMP > const & wellElemCompFrac, - arrayView3d< real64 const, compflow::USD_COMP_DC > const & dWellElemCompFrac_dCompDens, - real64 const & dt, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) -{ - using namespace compositionalMultiphaseUtilities; - - bool const isProducer = wellControls.isProducer(); - arrayView1d< real64 const > const & injection = wellControls.getInjectionStream(); - - // loop over the well elements to compute the fluxes between elements - forAll< parallelDevicePolicy<> >( size, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) - { - // create local work arrays - real64 compFracUp[NC]{}; - real64 dCompFrac_dCompDensUp[NC][NC]{}; - - real64 compFlux[NC]{}; - real64 dCompFlux_dRate[NC]{}; - real64 dCompFlux_dPresUp[NC]{}; - real64 dCompFlux_dCompDensUp[NC][NC]{}; - - // Step 1) decide the upwind well element - - /* currentConnRate < 0 flow from iwelem to iwelemNext - * currentConnRate > 0 flow from iwelemNext to iwelem - * With this convention, currentConnRate < 0 at the last connection for a producer - * currentConnRate > 0 at the last connection for a injector - */ - - localIndex const iwelemNext = nextWellElemIndex[iwelem]; - real64 const currentConnRate = connRate[iwelem]; - localIndex iwelemUp = -1; - - if( iwelemNext < 0 && !isProducer ) // exit connection, injector - { - // we still need to define iwelemUp for Jacobian assembly - iwelemUp = iwelem; - - // just copy the injection stream into compFrac - for( integer ic = 0; ic < NC; ++ic ) - { - compFracUp[ic] = injection[ic]; - for( integer jc = 0; jc < NC; ++jc ) - { - dCompFrac_dCompDensUp[ic][jc] = 0.0; - } - } - } - else - { - // first set iwelemUp to the upstream cell - if( ( iwelemNext < 0 && isProducer ) // exit connection, producer - || currentConnRate < 0 ) // not an exit connection, iwelem is upstream - { - iwelemUp = iwelem; - } - else // not an exit connection, iwelemNext is upstream - { - iwelemUp = iwelemNext; - } - - // copy the vars of iwelemUp into compFrac - for( integer ic = 0; ic < NC; ++ic ) - { - compFracUp[ic] = wellElemCompFrac[iwelemUp][ic]; - for( integer jc = 0; jc < NC; ++jc ) - { - dCompFrac_dCompDensUp[ic][jc] = dWellElemCompFrac_dCompDens[iwelemUp][ic][jc]; - } - } - } - - // Step 2) compute upstream transport coefficient - - for( integer ic = 0; ic < NC; ++ic ) - { - compFlux[ic] = compFracUp[ic] * currentConnRate; - dCompFlux_dRate[ic] = compFracUp[ic]; - dCompFlux_dPresUp[ic] = 0.0; // none of these quantities depend on pressure - for( integer jc = 0; jc < NC; ++jc ) - { - dCompFlux_dCompDensUp[ic][jc] = dCompFrac_dCompDensUp[ic][jc] * currentConnRate; - } - } - - globalIndex const offsetUp = wellElemDofNumber[iwelemUp]; - globalIndex const offsetCurrent = wellElemDofNumber[iwelem]; - - if( iwelemNext < 0 ) // exit connection - { - // for this case, we only need NC mass conservation equations - // so we do not use the arrays initialized before the loop - real64 oneSidedFlux[NC]{}; - real64 oneSidedFluxJacobian_dRate[NC][1]{}; - real64 oneSidedFluxJacobian_dPresCompUp[NC][NC+1]{}; - - computeExit< NC >( dt, - compFlux, - dCompFlux_dRate, - dCompFlux_dPresUp, - dCompFlux_dCompDensUp, - oneSidedFlux, - oneSidedFluxJacobian_dRate, - oneSidedFluxJacobian_dPresCompUp ); - - - globalIndex oneSidedEqnRowIndices[NC]{}; - globalIndex oneSidedDofColIndices_dPresCompUp[NC+1]{}; - globalIndex oneSidedDofColIndices_dRate = 0; - - // jacobian indices - for( integer ic = 0; ic < NC; ++ic ) - { - // mass balance equations for all components - oneSidedEqnRowIndices[ic] = offsetUp + ROFFSET::MASSBAL + ic - rankOffset; - } - - // in the dof ordering used in this class, there are 1 pressure dofs - // and NC compDens dofs before the rate dof in this block - localIndex const dRateColOffset = COFFSET::DCOMP + NC; - oneSidedDofColIndices_dRate = offsetCurrent + dRateColOffset; - - for( integer jdof = 0; jdof < NC+1; ++jdof ) - { - // dofs are the **upstream** pressure and component densities - oneSidedDofColIndices_dPresCompUp[jdof] = offsetUp + COFFSET::DPRES + jdof; - } - - if( useTotalMassEquation > 0 ) - { - // Apply equation/variable change transformation(s) - real64 work[NC + 1]{}; - shiftRowsAheadByOneAndReplaceFirstRowWithColumnSum( NC, 1, oneSidedFluxJacobian_dRate, work ); - shiftRowsAheadByOneAndReplaceFirstRowWithColumnSum( NC, NC + 1, oneSidedFluxJacobian_dPresCompUp, work ); - shiftElementsAheadByOneAndReplaceFirstElementWithSum( NC, oneSidedFlux ); - } - - for( integer i = 0; i < NC; ++i ) - { - if( oneSidedEqnRowIndices[i] >= 0 && oneSidedEqnRowIndices[i] < localMatrix.numRows() ) - { - localMatrix.addToRow< parallelDeviceAtomic >( oneSidedEqnRowIndices[i], - &oneSidedDofColIndices_dRate, - oneSidedFluxJacobian_dRate[i], - 1 ); - localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic >( oneSidedEqnRowIndices[i], - oneSidedDofColIndices_dPresCompUp, - oneSidedFluxJacobian_dPresCompUp[i], - NC+1 ); - RAJA::atomicAdd( parallelDeviceAtomic{}, &localRhs[oneSidedEqnRowIndices[i]], oneSidedFlux[i] ); - } - } - } - else // not an exit connection - { - real64 localFlux[2*NC]{}; - real64 localFluxJacobian_dRate[2*NC][1]{}; - real64 localFluxJacobian_dPresCompUp[2*NC][NC+1]{}; - - compute< NC >( dt, - compFlux, - dCompFlux_dRate, - dCompFlux_dPresUp, - dCompFlux_dCompDensUp, - localFlux, - localFluxJacobian_dRate, - localFluxJacobian_dPresCompUp ); - - - globalIndex eqnRowIndices[2*NC]{}; - globalIndex dofColIndices_dPresCompUp[NC+1]{}; - globalIndex dofColIndices_dRate = 0; - - globalIndex const offsetNext = wellElemDofNumber[iwelemNext]; - - // jacobian indices - for( integer ic = 0; ic < NC; ++ic ) - { - // mass balance equations for all components - eqnRowIndices[TAG::NEXT *NC+ic] = offsetNext + ROFFSET::MASSBAL + ic - rankOffset; - eqnRowIndices[TAG::CURRENT *NC+ic] = offsetCurrent + ROFFSET::MASSBAL + ic - rankOffset; - } - - // in the dof ordering used in this class, there are 1 pressure dofs - // and NC compDens dofs before the rate dof in this block - localIndex const dRateColOffset = COFFSET::DCOMP + NC; - dofColIndices_dRate = offsetCurrent + dRateColOffset; + localRhs[eqnRowIndex] += controlEqn; - for( integer jdof = 0; jdof < NC+1; ++jdof ) - { - // dofs are the **upstream** pressure and component densities - dofColIndices_dPresCompUp[jdof] = offsetUp + COFFSET::DPRES + jdof; - } + localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( eqnRowIndex, + dofColIndices, + dControlEqn, + COFFSET_WJ::nDer ); - if( useTotalMassEquation > 0 ) - { - // Apply equation/variable change transformation(s) - real64 work[NC + 1]{}; - shiftBlockRowsAheadByOneAndReplaceFirstRowWithColumnSum( NC, NC, 1, 2, localFluxJacobian_dRate, work ); - shiftBlockRowsAheadByOneAndReplaceFirstRowWithColumnSum( NC, NC, NC + 1, 2, localFluxJacobian_dPresCompUp, work ); - shiftBlockElementsAheadByOneAndReplaceFirstElementWithSum( NC, NC, 2, localFlux ); - } - for( integer i = 0; i < 2*NC; ++i ) - { - if( eqnRowIndices[i] >= 0 && eqnRowIndices[i] < localMatrix.numRows() ) - { - localMatrix.addToRow< parallelDeviceAtomic >( eqnRowIndices[i], - &dofColIndices_dRate, - localFluxJacobian_dRate[i], - 1 ); - localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic >( eqnRowIndices[i], - dofColIndices_dPresCompUp, - localFluxJacobian_dPresCompUp[i], - NC+1 ); - RAJA::atomicAdd( parallelDeviceAtomic{}, &localRhs[eqnRowIndices[i]], localFlux[i] ); - } - } - } - } ); } -#define INST_FluxKernel( NC ) \ - template \ - void FluxKernel:: \ - launch< NC >( localIndex const size, \ - globalIndex const rankOffset, \ - integer const useTotalMassEquation, \ - WellControls const & wellControls, \ - arrayView1d< globalIndex const > const & wellElemDofNumber, \ - arrayView1d< localIndex const > const & nextWellElemIndex, \ - arrayView1d< real64 const > const & connRate, \ - arrayView2d< real64 const, compflow::USD_COMP > const & wellElemCompFrac, \ - arrayView3d< real64 const, compflow::USD_COMP_DC > const & dWellElemCompFrac_dCompDens, \ - real64 const & dt, \ - CRSMatrixView< real64, globalIndex const > const & localMatrix, \ - arrayView1d< real64 > const & localRhs ) - -INST_FluxKernel( 1 ); -INST_FluxKernel( 2 ); -INST_FluxKernel( 3 ); -INST_FluxKernel( 4 ); -INST_FluxKernel( 5 ); - /******************************** PressureRelationKernel ********************************/ -template< integer NC > +template< integer NC, integer IS_THERMAL > GEOS_HOST_DEVICE void PressureRelationKernel:: @@ -570,12 +256,10 @@ PressureRelationKernel:: real64 const & presNext, real64 const & totalMassDens, real64 const & totalMassDensNext, - real64 const & dTotalMassDens_dPres, - real64 const & dTotalMassDens_dPresNext, - arraySlice1d< real64 const, compflow::USD_FLUID_DC - 1 > const & dTotalMassDens_dCompDens, - arraySlice1d< real64 const, compflow::USD_FLUID_DC - 1 > const & dTotalMassDens_dCompDensNext, + arraySlice1d< real64 const, compflow::USD_FLUID_DC - 1 > const & dTotalMassDens, + arraySlice1d< real64 const, compflow::USD_FLUID_DC - 1 > const & dTotalMassDensNext, real64 & localPresRel, - real64 ( & localPresRelJacobian )[2*(NC+1)] ) + real64 ( & localPresRelJacobian )[2*(NC+1 + IS_THERMAL)] ) { // local working variables and arrays real64 dAvgMassDens_dCompCurrent[NC]{}; @@ -583,12 +267,12 @@ PressureRelationKernel:: // compute the average density at the interface between well elements real64 const avgMassDens = 0.5 * ( totalMassDensNext + totalMassDens ); - real64 const dAvgMassDens_dPresNext = 0.5 * dTotalMassDens_dPresNext; - real64 const dAvgMassDens_dPresCurrent = 0.5 * dTotalMassDens_dPres; + real64 const dAvgMassDens_dPresNext = 0.5 * dTotalMassDensNext[Deriv::dP]; + real64 const dAvgMassDens_dPresCurrent = 0.5 * dTotalMassDens[Deriv::dP]; for( integer ic = 0; ic < NC; ++ic ) { - dAvgMassDens_dCompNext[ic] = 0.5 * dTotalMassDens_dCompDensNext[ic]; - dAvgMassDens_dCompCurrent[ic] = 0.5 * dTotalMassDens_dCompDens[ic]; + dAvgMassDens_dCompNext[ic] = 0.5 * dTotalMassDensNext[Deriv::dC+ic]; + dAvgMassDens_dCompCurrent[ic] = 0.5 * dTotalMassDens[Deriv::dC+ic]; } // compute depth diff times acceleration @@ -597,17 +281,25 @@ PressureRelationKernel:: // TODO: add friction and acceleration terms localPresRel = ( presNext - pres - avgMassDens * gravD ); - localPresRelJacobian[TAG::NEXT *(NC+1)] = ( 1 - dAvgMassDens_dPresNext * gravD ); - localPresRelJacobian[TAG::CURRENT *(NC+1)] = ( -1 - dAvgMassDens_dPresCurrent * gravD ); + + // localPresRelJacbain contains dP, dC and potentially dT derivatives for neighboring well elements + // TAG::NEXT is 1, CURRENT is 0 , not sure why indexes are setup as below + localPresRelJacobian[TAG::NEXT *(NC+1+IS_THERMAL)] = ( 1 - dAvgMassDens_dPresNext * gravD ); + localPresRelJacobian[TAG::CURRENT *(NC+1+IS_THERMAL)] = ( -1 - dAvgMassDens_dPresCurrent * gravD ); for( integer ic = 0; ic < NC; ++ic ) { - localPresRelJacobian[TAG::NEXT *(NC+1) + ic+1] = -dAvgMassDens_dCompNext[ic] * gravD; - localPresRelJacobian[TAG::CURRENT *(NC+1) + ic+1] = -dAvgMassDens_dCompCurrent[ic] * gravD; + localPresRelJacobian[TAG::NEXT *(NC+1+IS_THERMAL) + ic+1] = -dAvgMassDens_dCompNext[ic] * gravD; + localPresRelJacobian[TAG::CURRENT *(NC+1+IS_THERMAL) + ic+1] = -dAvgMassDens_dCompCurrent[ic] * gravD; + } + if constexpr ( IS_THERMAL ) + { + localPresRelJacobian[TAG::NEXT *(NC+1+IS_THERMAL)+NC+1] = 0.5 * dTotalMassDensNext[Deriv::dT]; + localPresRelJacobian[TAG::CURRENT *(NC+1+IS_THERMAL)+NC+1] = 0.5 * dTotalMassDens[Deriv::dT]; } } -template< integer NC > +template< integer NC, integer IS_THERMAL > void PressureRelationKernel:: launch( localIndex const size, @@ -622,13 +314,12 @@ PressureRelationKernel:: arrayView1d< localIndex const > const & nextWellElemIndex, arrayView1d< real64 const > const & wellElemPressure, arrayView1d< real64 const > const & wellElemTotalMassDens, - arrayView1d< real64 const > const & dWellElemTotalMassDens_dPres, - arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens_dCompDens, + arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens, bool & controlHasSwitched, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { - + using COFFSET_WJ = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; // static well control data bool const isProducer = wellControls.isProducer(); WellControls::Control const currentControl = wellControls.getControl(); @@ -640,28 +331,19 @@ PressureRelationKernel:: // dynamic well control data real64 const & currentBHP = wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentBHPString() ); - real64 const & dCurrentBHP_dPres = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentBHP_dPresString() ); - arrayView1d< real64 const > const & dCurrentBHP_dCompDens = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentBHP_dCompDensString() ); + arrayView1d< real64 const > const & dCurrentBHP = + wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentBHPString() ); arrayView1d< real64 const > const & currentPhaseVolRate = wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::currentPhaseVolRateString() ); - arrayView1d< real64 const > const & dCurrentPhaseVolRate_dPres = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentPhaseVolRate_dPresString() ); - arrayView2d< real64 const > const & dCurrentPhaseVolRate_dCompDens = - wellControls.getReference< array2d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentPhaseVolRate_dCompDensString() ); - arrayView1d< real64 const > const & dCurrentPhaseVolRate_dRate = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentPhaseVolRate_dRateString() ); + arrayView2d< real64 const > const & dCurrentPhaseVolRate = + wellControls.getReference< array2d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentPhaseVolRateString() ); real64 const & currentTotalVolRate = wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::currentTotalVolRateString() ); - real64 const & dCurrentTotalVolRate_dPres = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentTotalVolRate_dPresString() ); - arrayView1d< real64 const > const & dCurrentTotalVolRate_dCompDens = - wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentTotalVolRate_dCompDensString() ); - real64 const & dCurrentTotalVolRate_dRate = - wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentTotalVolRate_dRateString() ); + arrayView1d< real64 const > const & dCurrentTotalVolRate = + wellControls.getReference< array1d< real64 > >( CompositionalMultiphaseWell::viewKeyStruct::dCurrentTotalVolRateString() ); + real64 const & massDensity = wellControls.getReference< real64 >( CompositionalMultiphaseWell::viewKeyStruct::massDensityString() ); @@ -690,30 +372,23 @@ PressureRelationKernel:: { switchControl.max( 1 ); } - - ControlEquationHelper::compute< NC >( rankOffset, - newControl, - targetPhaseIndex, - targetBHP, - targetPhaseRate, - targetTotalRate, - targetMassRate, - currentBHP, - dCurrentBHP_dPres, - dCurrentBHP_dCompDens, - currentPhaseVolRate, - dCurrentPhaseVolRate_dPres, - dCurrentPhaseVolRate_dCompDens, - dCurrentPhaseVolRate_dRate, - currentTotalVolRate, - dCurrentTotalVolRate_dPres, - dCurrentTotalVolRate_dCompDens, - dCurrentTotalVolRate_dRate, - massDensity, - wellElemDofNumber[iwelemControl], - localMatrix, - localRhs ); - + ControlEquationHelper::compute< NC, IS_THERMAL >( rankOffset, + newControl, + targetPhaseIndex, + targetBHP, + targetPhaseRate, + targetTotalRate, + targetMassRate, + currentBHP, + dCurrentBHP, + currentPhaseVolRate, + dCurrentPhaseVolRate, + currentTotalVolRate, + dCurrentTotalVolRate, + massDensity, + wellElemDofNumber[iwelemControl], + localMatrix, + localRhs ); // TODO: for consistency, we should assemble here, not in compute... } @@ -721,41 +396,44 @@ PressureRelationKernel:: { real64 localPresRel = 0; - real64 localPresRelJacobian[2*(NC+1)]{}; - - compute< NC >( wellElemGravCoef[iwelem], - wellElemGravCoef[iwelemNext], - wellElemPressure[iwelem], - wellElemPressure[iwelemNext], - wellElemTotalMassDens[iwelem], - wellElemTotalMassDens[iwelemNext], - dWellElemTotalMassDens_dPres[iwelem], - dWellElemTotalMassDens_dPres[iwelemNext], - dWellElemTotalMassDens_dCompDens[iwelem], - dWellElemTotalMassDens_dCompDens[iwelemNext], - localPresRel, - localPresRelJacobian ); + real64 localPresRelJacobian[2*(NC+1+IS_THERMAL)]{}; + + compute< NC, IS_THERMAL >( + wellElemGravCoef[iwelem], + wellElemGravCoef[iwelemNext], + wellElemPressure[iwelem], + wellElemPressure[iwelemNext], + wellElemTotalMassDens[iwelem], + wellElemTotalMassDens[iwelemNext], + dWellElemTotalMassDens[iwelem], + dWellElemTotalMassDens[iwelemNext], + localPresRel, + localPresRelJacobian ); // local working variables and arrays - globalIndex dofColIndices[2*(NC+1)]; + globalIndex dofColIndices[2*(NC+1+IS_THERMAL)]; globalIndex const eqnRowIndex = wellElemDofNumber[iwelem] + ROFFSET::CONTROL - rankOffset; - dofColIndices[TAG::NEXT *(NC+1)] = wellElemDofNumber[iwelemNext] + COFFSET::DPRES; - dofColIndices[TAG::CURRENT *(NC+1)] = wellElemDofNumber[iwelem] + COFFSET::DPRES; + dofColIndices[TAG::NEXT *(NC+1+IS_THERMAL)] = wellElemDofNumber[iwelemNext] + COFFSET_WJ::dP; + dofColIndices[TAG::CURRENT *(NC+1+IS_THERMAL)] = wellElemDofNumber[iwelem] + COFFSET_WJ::dP; for( integer ic = 0; ic < NC; ++ic ) { - dofColIndices[TAG::NEXT *(NC+1) + ic+1] = wellElemDofNumber[iwelemNext] + COFFSET::DCOMP + ic; - dofColIndices[TAG::CURRENT *(NC+1) + ic+1] = wellElemDofNumber[iwelem] + COFFSET::DCOMP + ic; + dofColIndices[TAG::NEXT *(NC+1+IS_THERMAL) + ic+1] = wellElemDofNumber[iwelemNext] + COFFSET_WJ::dC + ic; + dofColIndices[TAG::CURRENT *(NC+1+IS_THERMAL) + ic+1] = wellElemDofNumber[iwelem] + COFFSET_WJ::dC + ic; + } + if constexpr ( IS_THERMAL ) + { + dofColIndices[TAG::NEXT *(NC+1+IS_THERMAL)+NC+1] = wellElemDofNumber[iwelemNext] + COFFSET_WJ::dT; + dofColIndices[TAG::CURRENT *(NC+1+IS_THERMAL)+NC+1] = wellElemDofNumber[iwelem] + COFFSET_WJ::dT; } - if( eqnRowIndex >= 0 && eqnRowIndex < localMatrix.numRows() ) { localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic >( eqnRowIndex, dofColIndices, localPresRelJacobian, - 2 * (NC+1) ); + 2 * (NC+1+IS_THERMAL) ); RAJA::atomicAdd( parallelDeviceAtomic{}, &localRhs[eqnRowIndex], localPresRel ); } } @@ -763,783 +441,36 @@ PressureRelationKernel:: controlHasSwitched = ( switchControl.get() == 1 ); } -#define INST_PressureRelationKernel( NC ) \ +#define INST_PressureRelationKernel( NC, IS_THERMAL ) \ template \ void PressureRelationKernel:: \ - launch< NC >( localIndex const size, \ - globalIndex const rankOffset, \ - bool const isLocallyOwned, \ - localIndex const iwelemControl, \ - integer const targetPhaseIndex, \ - WellControls const & wellControls, \ - real64 const & timeAtEndOfStep, \ - arrayView1d< globalIndex const > const & wellElemDofNumber, \ - arrayView1d< real64 const > const & wellElemGravCoef, \ - arrayView1d< localIndex const > const & nextWellElemIndex, \ - arrayView1d< real64 const > const & wellElemPressure, \ - arrayView1d< real64 const > const & wellElemTotalMassDens, \ - arrayView1d< real64 const > const & dWellElemTotalMassDens_dPres, \ - arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens_dCompDens, \ - bool & controlHasSwitched, \ - CRSMatrixView< real64, globalIndex const > const & localMatrix, \ - arrayView1d< real64 > const & localRhs ) - -INST_PressureRelationKernel( 1 ); -INST_PressureRelationKernel( 2 ); -INST_PressureRelationKernel( 3 ); -INST_PressureRelationKernel( 4 ); -INST_PressureRelationKernel( 5 ); - - -/******************************** PerforationKernel ********************************/ - -template< integer NC, integer NP > -GEOS_HOST_DEVICE -void -PerforationKernel:: - compute( bool const & disableReservoirToWellFlow, - real64 const & resPres, - arraySlice1d< real64 const, compflow::USD_PHASE - 1 > const & resPhaseVolFrac, - arraySlice2d< real64 const, compflow::USD_PHASE_DC - 1 > const & dResPhaseVolFrac, - arraySlice2d< real64 const, compflow::USD_COMP_DC - 1 > const & dResCompFrac_dCompDens, - arraySlice1d< real64 const, multifluid::USD_PHASE - 2 > const & resPhaseDens, - arraySlice2d< real64 const, multifluid::USD_PHASE_DC - 2 > const & dResPhaseDens, - arraySlice1d< real64 const, multifluid::USD_PHASE - 2 > const & resPhaseVisc, - arraySlice2d< real64 const, multifluid::USD_PHASE_DC - 2 > const & dResPhaseVisc, - arraySlice2d< real64 const, multifluid::USD_PHASE_COMP - 2 > const & resPhaseCompFrac, - arraySlice3d< real64 const, multifluid::USD_PHASE_COMP_DC - 2 > const & dResPhaseCompFrac, - arraySlice1d< real64 const, relperm::USD_RELPERM - 2 > const & resPhaseRelPerm, - arraySlice2d< real64 const, relperm::USD_RELPERM_DS - 2 > const & dResPhaseRelPerm_dPhaseVolFrac, - real64 const & wellElemGravCoef, - real64 const & wellElemPres, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & wellElemCompDens, - real64 const & wellElemTotalMassDens, - real64 const & dWellElemTotalMassDens_dPres, - arraySlice1d< real64 const, compflow::USD_FLUID_DC - 1 > const & dWellElemTotalMassDens_dCompDens, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & wellElemCompFrac, - arraySlice2d< real64 const, compflow::USD_COMP_DC - 1 > const & dWellElemCompFrac_dCompDens, - real64 const & perfGravCoef, - real64 const & trans, - arraySlice1d< real64 > const & compPerfRate, - arraySlice2d< real64 > const & dCompPerfRate_dPres, - arraySlice3d< real64 > const & dCompPerfRate_dComp ) -{ - using Deriv = multifluid::DerivativeOffset; - - // local working variables and arrays - real64 pres[2]{}; - real64 dPres_dP[2]{}; - real64 dPres_dC[2][NC]{}; - real64 dFlux_dP[2]{}; - real64 dFlux_dC[2][NC]{}; - real64 dMult_dP[2]{}; - real64 dMult_dC[2][NC]{}; - real64 dPotDiff_dP[2]{}; - real64 dPotDiff_dC[2][NC]{}; - real64 multiplier[2]{}; - - real64 dResTotalMob_dC[NC]{}; - real64 dDens_dC[NC]{}; - real64 dVisc_dC[NC]{}; - real64 dRelPerm_dC[NC]{}; - real64 dMob_dC[NC]{}; - real64 dCompFrac_dCompDens[NC]{}; - - - // Step 1: reset the perforation rates - - for( integer ic = 0; ic < NC; ++ic ) - { - compPerfRate[ic] = 0.0; - for( integer ke = 0; ke < 2; ++ke ) - { - dCompPerfRate_dPres[ke][ic] = 0.0; - for( integer jc = 0; jc < NC; ++jc ) - { - dCompPerfRate_dComp[ke][ic][jc] = 0.0; - } - } - } - - - // Step 2: copy the variables from the reservoir and well element - - // a) get reservoir variables - - pres[TAG::RES] = resPres; - dPres_dP[TAG::RES] = 1.0; - multiplier[TAG::RES] = 1.0; - - // Here in the absence of a buoyancy term we assume that the reservoir cell is perforated at its center - // TODO: add a buoyancy term for the reservoir side here - - - // b) get well variables - - pres[TAG::WELL] = wellElemPres; - dPres_dP[TAG::WELL] = 1.0; - multiplier[TAG::WELL] = -1.0; - - real64 const gravD = ( perfGravCoef - wellElemGravCoef ); - - pres[TAG::WELL] += wellElemTotalMassDens * gravD; - dPres_dP[TAG::WELL] += dWellElemTotalMassDens_dPres * gravD; - for( integer ic = 0; ic < NC; ++ic ) - { - dPres_dC[TAG::WELL][ic] += dWellElemTotalMassDens_dCompDens[ic] * gravD; - } - - - // Step 3: compute potential difference - - real64 potDiff = 0.0; - for( integer i = 0; i < 2; ++i ) - { - potDiff += multiplier[i] * trans * pres[i]; - dPotDiff_dP[i] += multiplier[i] * trans * dPres_dP[i]; - - for( integer ic = 0; ic < NC; ++ic ) - { - dPotDiff_dC[i][ic] += multiplier[i] * trans * dPres_dC[i][ic]; - } - } - - - // Step 4: upwinding based on the flow direction - - real64 flux = 0.0; - if( potDiff >= 0 ) // ** reservoir cell is upstream ** - { - - // loop over phases, compute and upwind phase flux - // and sum contributions to each component's perforation rate - for( integer ip = 0; ip < NP; ++ip ) - { - - // skip the rest of the calculation if the phase is absent - // or if crossflow is disabled for injectors - bool const phaseExists = (resPhaseVolFrac[ip] > 0); - if( !phaseExists || disableReservoirToWellFlow ) - { - continue; - } - - // here, we have to recompute the reservoir phase mobility (not including density) - - // density - real64 const resDens = resPhaseDens[ip]; - real64 const dResDens_dP = dResPhaseDens[ip][Deriv::dP]; - applyChainRule( NC, dResCompFrac_dCompDens, - dResPhaseDens[ip], - dDens_dC, - Deriv::dC ); - - // viscosity - real64 const resVisc = resPhaseVisc[ip]; - real64 const dResVisc_dP = dResPhaseVisc[ip][Deriv::dP]; - applyChainRule( NC, dResCompFrac_dCompDens, - dResPhaseVisc[ip], - dVisc_dC, - Deriv::dC ); - - // relative permeability - real64 const resRelPerm = resPhaseRelPerm[ip]; - real64 dResRelPerm_dP = 0.0; - for( integer jc = 0; jc < NC; ++jc ) - { - dRelPerm_dC[jc] = 0; - } - - for( integer jp = 0; jp < NP; ++jp ) - { - real64 const dResRelPerm_dS = dResPhaseRelPerm_dPhaseVolFrac[ip][jp]; - dResRelPerm_dP += dResRelPerm_dS * dResPhaseVolFrac[jp][Deriv::dP]; - - for( integer jc = 0; jc < NC; ++jc ) - { - dRelPerm_dC[jc] += dResRelPerm_dS * dResPhaseVolFrac[jp][Deriv::dC+jc]; - } - } - - // compute the reservoir phase mobility, including phase density - real64 const resPhaseMob = resDens * resRelPerm / resVisc; - real64 const dResPhaseMob_dPres = dResRelPerm_dP * resDens / resVisc - + resPhaseMob * (dResDens_dP / resDens - dResVisc_dP / resVisc); - for( integer jc = 0; jc < NC; ++jc ) - { - dMob_dC[jc] = dRelPerm_dC[jc] * resDens / resVisc - + resPhaseMob * (dDens_dC[jc] / resDens - dVisc_dC[jc] / resVisc); - } - - // compute the phase flux and derivatives using upstream cell mobility - flux = resPhaseMob * potDiff; - dFlux_dP[TAG::RES] = dResPhaseMob_dPres * potDiff + resPhaseMob * dPotDiff_dP[TAG::RES]; - dFlux_dP[TAG::WELL] = resPhaseMob * dPotDiff_dP[TAG::WELL]; - - for( integer ic = 0; ic < NC; ++ic ) - { - dFlux_dC[TAG::RES][ic] = dMob_dC[ic] * potDiff + resPhaseMob * dPotDiff_dC[TAG::RES][ic]; - dFlux_dC[TAG::WELL][ic] = resPhaseMob * dPotDiff_dC[TAG::WELL][ic]; - } - - // increment component fluxes - for( integer ic = 0; ic < NC; ++ic ) - { - compPerfRate[ic] += flux * resPhaseCompFrac[ip][ic]; - - dCompPerfRate_dPres[TAG::RES][ic] += resPhaseCompFrac[ip][ic] * dFlux_dP[TAG::RES]; - dCompPerfRate_dPres[TAG::RES][ic] += dResPhaseCompFrac[ip][ic][Deriv::dP] * flux; - dCompPerfRate_dPres[TAG::WELL][ic] += resPhaseCompFrac[ip][ic] * dFlux_dP[TAG::WELL]; - - applyChainRule( NC, - dResCompFrac_dCompDens, - dResPhaseCompFrac[ip][ic], - dCompFrac_dCompDens, - Deriv::dC ); - - for( integer jc = 0; jc < NC; ++jc ) - { - dCompPerfRate_dComp[TAG::RES][ic][jc] += dFlux_dC[TAG::RES][jc] * resPhaseCompFrac[ip][ic]; - dCompPerfRate_dComp[TAG::RES][ic][jc] += flux * dCompFrac_dCompDens[jc]; - dCompPerfRate_dComp[TAG::WELL][ic][jc] += dFlux_dC[TAG::WELL][jc] * resPhaseCompFrac[ip][ic]; - } - } - } - } - else // ** well is upstream ** - { - - real64 resTotalMob = 0.0; - real64 dResTotalMob_dP = 0.0; - - // we re-compute here the total mass (when useMass == 1) or molar (when useMass == 0) density - real64 wellElemTotalDens = 0; - for( integer ic = 0; ic < NC; ++ic ) - { - wellElemTotalDens += wellElemCompDens[ic]; - } - - // first, compute the reservoir total mobility (excluding phase density) - for( integer ip = 0; ip < NP; ++ip ) - { - - // skip the rest of the calculation if the phase is absent - bool const phaseExists = (resPhaseVolFrac[ip] > 0); - if( !phaseExists ) - { - continue; - } - - // viscosity - real64 const resVisc = resPhaseVisc[ip]; - real64 const dResVisc_dP = dResPhaseVisc[ip][Deriv::dP]; - applyChainRule( NC, dResCompFrac_dCompDens, - dResPhaseVisc[ip], - dVisc_dC, - Deriv::dC ); - - // relative permeability - real64 const resRelPerm = resPhaseRelPerm[ip]; - real64 dResRelPerm_dP = 0.0; - for( integer jc = 0; jc < NC; ++jc ) - { - dRelPerm_dC[jc] = 0; - } - - for( integer jp = 0; jp < NP; ++jp ) - { - real64 const dResRelPerm_dS = dResPhaseRelPerm_dPhaseVolFrac[ip][jp]; - dResRelPerm_dP += dResRelPerm_dS * dResPhaseVolFrac[jp][Deriv::dP]; - - for( integer jc = 0; jc < NC; ++jc ) - { - dRelPerm_dC[jc] += dResRelPerm_dS * dResPhaseVolFrac[jp][Deriv::dC+jc]; - } - } - - // increment total mobility - resTotalMob += resRelPerm / resVisc; - dResTotalMob_dP += ( dResRelPerm_dP * resVisc - resRelPerm * dResVisc_dP ) - / ( resVisc * resVisc ); - for( integer ic = 0; ic < NC; ++ic ) - { - dResTotalMob_dC[ic] += ( dRelPerm_dC[ic] * resVisc - resRelPerm * dVisc_dC[ic] ) - / ( resVisc * resVisc ); - } - } - - // compute a potdiff multiplier = wellElemTotalDens * resTotalMob - // wellElemTotalDens is a mass density if useMass == 1 and a molar density otherwise - real64 const mult = wellElemTotalDens * resTotalMob; - dMult_dP[TAG::RES] = wellElemTotalDens * dResTotalMob_dP; - dMult_dP[TAG::WELL] = 0.0; // because totalDens does not depend on pressure - - for( integer ic = 0; ic < NC; ++ic ) - { - dMult_dC[TAG::RES][ic] = wellElemTotalDens * dResTotalMob_dC[ic]; - dMult_dC[TAG::WELL][ic] = resTotalMob; - } - - // compute the volumetric flux and derivatives using upstream cell mobility - flux = mult * potDiff; - dFlux_dP[TAG::RES] = dMult_dP[TAG::RES] * potDiff + mult * dPotDiff_dP[TAG::RES]; - dFlux_dP[TAG::WELL] = dMult_dP[TAG::WELL] * potDiff + mult * dPotDiff_dP[TAG::WELL]; - - for( integer ic = 0; ic < NC; ++ic ) - { - dFlux_dC[TAG::RES][ic] = dMult_dC[TAG::RES][ic] * potDiff + mult * dPotDiff_dC[TAG::RES][ic]; - dFlux_dC[TAG::WELL][ic] = dMult_dC[TAG::WELL][ic] * potDiff + mult * dPotDiff_dC[TAG::WELL][ic]; - } - - // compute component fluxes - for( integer ic = 0; ic < NC; ++ic ) - { - compPerfRate[ic] += wellElemCompFrac[ic] * flux; - dCompPerfRate_dPres[TAG::RES][ic] = wellElemCompFrac[ic] * dFlux_dP[TAG::RES]; - dCompPerfRate_dPres[TAG::WELL][ic] = wellElemCompFrac[ic] * dFlux_dP[TAG::WELL]; - - for( integer jc = 0; jc < NC; ++jc ) - { - dCompPerfRate_dComp[TAG::RES][ic][jc] += wellElemCompFrac[ic] * dFlux_dC[TAG::RES][jc]; - dCompPerfRate_dComp[TAG::WELL][ic][jc] += wellElemCompFrac[ic] * dFlux_dC[TAG::WELL][jc]; - dCompPerfRate_dComp[TAG::WELL][ic][jc] += dWellElemCompFrac_dCompDens[ic][jc] * flux; - } - } - } -} - -template< integer NC, integer NP > -void -PerforationKernel:: - launch( localIndex const size, - bool const disableReservoirToWellFlow, - ElementViewConst< arrayView1d< real64 const > > const & resPres, - ElementViewConst< arrayView2d< real64 const, compflow::USD_PHASE > > const & resPhaseVolFrac, - ElementViewConst< arrayView3d< real64 const, compflow::USD_PHASE_DC > > const & dResPhaseVolFrac, - ElementViewConst< arrayView3d< real64 const, compflow::USD_COMP_DC > > const & dResCompFrac_dCompDens, - ElementViewConst< arrayView3d< real64 const, multifluid::USD_PHASE > > const & resPhaseDens, - ElementViewConst< arrayView4d< real64 const, multifluid::USD_PHASE_DC > > const & dResPhaseDens, - ElementViewConst< arrayView3d< real64 const, multifluid::USD_PHASE > > const & resPhaseVisc, - ElementViewConst< arrayView4d< real64 const, multifluid::USD_PHASE_DC > > const & dResPhaseVisc, - ElementViewConst< arrayView4d< real64 const, multifluid::USD_PHASE_COMP > > const & resPhaseCompFrac, - ElementViewConst< arrayView5d< real64 const, multifluid::USD_PHASE_COMP_DC > > const & dResPhaseCompFrac, - ElementViewConst< arrayView3d< real64 const, relperm::USD_RELPERM > > const & resPhaseRelPerm, - ElementViewConst< arrayView4d< real64 const, relperm::USD_RELPERM_DS > > const & dResPhaseRelPerm_dPhaseVolFrac, - arrayView1d< real64 const > const & wellElemGravCoef, - arrayView1d< real64 const > const & wellElemPres, - arrayView2d< real64 const, compflow::USD_COMP > const & wellElemCompDens, - arrayView1d< real64 const > const & wellElemTotalMassDens, - arrayView1d< real64 const > const & dWellElemTotalMassDens_dPres, - arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens_dCompDens, - arrayView2d< real64 const, compflow::USD_COMP > const & wellElemCompFrac, - arrayView3d< real64 const, compflow::USD_COMP_DC > const & dWellElemCompFrac_dCompDens, - arrayView1d< real64 const > const & perfGravCoef, - arrayView1d< localIndex const > const & perfWellElemIndex, - arrayView1d< real64 const > const & perfTrans, - arrayView1d< localIndex const > const & resElementRegion, - arrayView1d< localIndex const > const & resElementSubRegion, - arrayView1d< localIndex const > const & resElementIndex, - arrayView2d< real64 > const & compPerfRate, - arrayView3d< real64 > const & dCompPerfRate_dPres, - arrayView4d< real64 > const & dCompPerfRate_dComp ) -{ - - // loop over the perforations to compute the perforation rates - forAll< parallelDevicePolicy<> >( size, [=] GEOS_HOST_DEVICE ( localIndex const iperf ) - { - - // get the index of the reservoir elem - localIndex const er = resElementRegion[iperf]; - localIndex const esr = resElementSubRegion[iperf]; - localIndex const ei = resElementIndex[iperf]; - - // get the index of the well elem - localIndex const iwelem = perfWellElemIndex[iperf]; - - compute< NC, NP >( disableReservoirToWellFlow, - resPres[er][esr][ei], - resPhaseVolFrac[er][esr][ei], - dResPhaseVolFrac[er][esr][ei], - dResCompFrac_dCompDens[er][esr][ei], - resPhaseDens[er][esr][ei][0], - dResPhaseDens[er][esr][ei][0], - resPhaseVisc[er][esr][ei][0], - dResPhaseVisc[er][esr][ei][0], - resPhaseCompFrac[er][esr][ei][0], - dResPhaseCompFrac[er][esr][ei][0], - resPhaseRelPerm[er][esr][ei][0], - dResPhaseRelPerm_dPhaseVolFrac[er][esr][ei][0], - wellElemGravCoef[iwelem], - wellElemPres[iwelem], - wellElemCompDens[iwelem], - wellElemTotalMassDens[iwelem], - dWellElemTotalMassDens_dPres[iwelem], - dWellElemTotalMassDens_dCompDens[iwelem], - wellElemCompFrac[iwelem], - dWellElemCompFrac_dCompDens[iwelem], - perfGravCoef[iperf], - perfTrans[iperf], - compPerfRate[iperf], - dCompPerfRate_dPres[iperf], - dCompPerfRate_dComp[iperf] ); - - } ); -} - -#define INST_PerforationKernel( NC, NP ) \ - template \ - void PerforationKernel:: \ - launch< NC, NP >( localIndex const size, \ - bool const disableReservoirToWellFlow, \ - ElementViewConst< arrayView1d< real64 const > > const & resPres, \ - ElementViewConst< arrayView2d< real64 const, compflow::USD_PHASE > > const & resPhaseVolFrac, \ - ElementViewConst< arrayView3d< real64 const, compflow::USD_PHASE_DC > > const & dResPhaseVolFrac, \ - ElementViewConst< arrayView3d< real64 const, compflow::USD_COMP_DC > > const & dResCompFrac_dCompDens, \ - ElementViewConst< arrayView3d< real64 const, multifluid::USD_PHASE > > const & resPhaseDens, \ - ElementViewConst< arrayView4d< real64 const, multifluid::USD_PHASE_DC > > const & dResPhaseDens, \ - ElementViewConst< arrayView3d< real64 const, multifluid::USD_PHASE > > const & resPhaseVisc, \ - ElementViewConst< arrayView4d< real64 const, multifluid::USD_PHASE_DC > > const & dResPhaseVisc, \ - ElementViewConst< arrayView4d< real64 const, multifluid::USD_PHASE_COMP > > const & resPhaseCompFrac, \ - ElementViewConst< arrayView5d< real64 const, multifluid::USD_PHASE_COMP_DC > > const & dResPhaseCompFrac, \ - ElementViewConst< arrayView3d< real64 const, relperm::USD_RELPERM > > const & resPhaseRelPerm, \ - ElementViewConst< arrayView4d< real64 const, relperm::USD_RELPERM_DS > > const & dResPhaseRelPerm_dPhaseVolFrac, \ - arrayView1d< real64 const > const & wellElemGravCoef, \ - arrayView1d< real64 const > const & wellElemPres, \ - arrayView2d< real64 const, compflow::USD_COMP > const & wellElemCompDens, \ - arrayView1d< real64 const > const & wellElemTotalMassDens, \ - arrayView1d< real64 const > const & dWellElemTotalMassDens_dPres, \ - arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens_dCompDens, \ - arrayView2d< real64 const, compflow::USD_COMP > const & wellElemCompFrac, \ - arrayView3d< real64 const, compflow::USD_COMP_DC > const & dWellElemCompFrac_dCompDens, \ - arrayView1d< real64 const > const & perfGravCoef, \ - arrayView1d< localIndex const > const & perfWellElemIndex, \ - arrayView1d< real64 const > const & perfTrans, \ - arrayView1d< localIndex const > const & resElementRegion, \ - arrayView1d< localIndex const > const & resElementSubRegion, \ - arrayView1d< localIndex const > const & resElementIndex, \ - arrayView2d< real64 > const & compPerfRate, \ - arrayView3d< real64 > const & dCompPerfRate_dPres, \ - arrayView4d< real64 > const & dCompPerfRate_dComp ) - -INST_PerforationKernel( 1, 2 ); -INST_PerforationKernel( 2, 2 ); -INST_PerforationKernel( 3, 2 ); -INST_PerforationKernel( 4, 2 ); -INST_PerforationKernel( 5, 2 ); -INST_PerforationKernel( 1, 3 ); -INST_PerforationKernel( 2, 3 ); -INST_PerforationKernel( 3, 3 ); -INST_PerforationKernel( 4, 3 ); -INST_PerforationKernel( 5, 3 ); - -/******************************** AccumulationKernel ********************************/ - -template< integer NC > -GEOS_HOST_DEVICE -void -AccumulationKernel:: - compute( integer const numPhases, - real64 const & volume, - arraySlice1d< real64 const, compflow::USD_PHASE - 1 > const & phaseVolFrac, - arraySlice2d< real64 const, compflow::USD_PHASE_DC - 1 > const & dPhaseVolFrac, - arraySlice2d< real64 const, compflow::USD_COMP_DC - 1 > const & dCompFrac_dCompDens, - arraySlice1d< real64 const, multifluid::USD_PHASE - 2 > const & phaseDens, - arraySlice2d< real64 const, multifluid::USD_PHASE_DC - 2 > const & dPhaseDens, - arraySlice2d< real64 const, multifluid::USD_PHASE_COMP - 2 > const & phaseCompFrac, - arraySlice3d< real64 const, multifluid::USD_PHASE_COMP_DC - 2 > const & dPhaseCompFrac, - arraySlice1d< real64 const, compflow::USD_PHASE - 1 > const & phaseVolFrac_n, - arraySlice1d< real64 const, multifluid::USD_PHASE - 2 > const & phaseDens_n, - arraySlice2d< real64 const, multifluid::USD_PHASE_COMP - 2 > const & phaseCompFrac_n, - real64 ( & localAccum )[NC], - real64 ( & localAccumJacobian )[NC][NC + 1] ) -{ - using Deriv = multifluid::DerivativeOffset; - - // temporary work arrays - real64 dPhaseAmount_dC[NC]{}; - real64 dPhaseCompFrac_dC[NC]{}; - - // reset the local values - for( integer i = 0; i < NC; ++i ) - { - localAccum[i] = 0.0; - for( integer j = 0; j < NC+1; ++j ) - { - localAccumJacobian[i][j] = 0.0; - } - } - - // sum contributions to component accumulation from each phase - for( integer ip = 0; ip < numPhases; ++ip ) - { - real64 const phaseAmountNew = volume * phaseVolFrac[ip] * phaseDens[ip]; - real64 const phaseAmount_n = volume * phaseVolFrac_n[ip] * phaseDens_n[ip]; - - real64 const dPhaseAmount_dP = volume * ( dPhaseVolFrac[ip][Deriv::dP] * phaseDens[ip] - + phaseVolFrac[ip] * dPhaseDens[ip][Deriv::dP] ); - - // assemble density dependence - applyChainRule( NC, dCompFrac_dCompDens, dPhaseDens[ip], dPhaseAmount_dC, Deriv::dC ); - for( integer jc = 0; jc < NC; ++jc ) - { - dPhaseAmount_dC[jc] = dPhaseAmount_dC[jc] * phaseVolFrac[ip] - + phaseDens[ip] * dPhaseVolFrac[ip][Deriv::dC+jc]; - dPhaseAmount_dC[jc] *= volume; - } - - // ic - index of component whose conservation equation is assembled - // (i.e. row number in local matrix) - for( integer ic = 0; ic < NC; ++ic ) - { - real64 const phaseCompAmountNew = phaseAmountNew * phaseCompFrac[ip][ic]; - real64 const phaseCompAmount_n = phaseAmount_n * phaseCompFrac_n[ip][ic]; - - real64 const dPhaseCompAmount_dP = dPhaseAmount_dP * phaseCompFrac[ip][ic] - + phaseAmountNew * dPhaseCompFrac[ip][ic][Deriv::dP]; - - localAccum[ic] += phaseCompAmountNew - phaseCompAmount_n; - localAccumJacobian[ic][0] += dPhaseCompAmount_dP; - - // jc - index of component w.r.t. whose compositional var the derivative is being taken - // (i.e. col number in local matrix) - - // assemble phase composition dependence - applyChainRule( NC, dCompFrac_dCompDens, dPhaseCompFrac[ip][ic], dPhaseCompFrac_dC, Deriv::dC ); - for( integer jc = 0; jc < NC; ++jc ) - { - real64 const dPhaseCompAmount_dC = dPhaseCompFrac_dC[jc] * phaseAmountNew - + phaseCompFrac[ip][ic] * dPhaseAmount_dC[jc]; - localAccumJacobian[ic][jc + 1] += dPhaseCompAmount_dC; - } - } - } -} - -template< integer NC > -void -AccumulationKernel:: - launch( localIndex const size, - integer const numPhases, - globalIndex const rankOffset, - integer const useTotalMassEquation, - arrayView1d< globalIndex const > const & wellElemDofNumber, - arrayView1d< integer const > const & wellElemGhostRank, - arrayView1d< real64 const > const & wellElemVolume, - arrayView2d< real64 const, compflow::USD_PHASE > const & wellElemPhaseVolFrac, - arrayView3d< real64 const, compflow::USD_PHASE_DC > const & dWellElemPhaseVolFrac, - arrayView3d< real64 const, compflow::USD_COMP_DC > const & dWellElemCompFrac_dCompDens, - arrayView3d< real64 const, multifluid::USD_PHASE > const & wellElemPhaseDens, - arrayView4d< real64 const, multifluid::USD_PHASE_DC > const & dWellElemPhaseDens, - arrayView4d< real64 const, multifluid::USD_PHASE_COMP > const & wellElemPhaseCompFrac, - arrayView5d< real64 const, multifluid::USD_PHASE_COMP_DC > const & dWellElemPhaseCompFrac, - arrayView2d< real64 const, compflow::USD_PHASE > const & wellElemPhaseVolFrac_n, - arrayView3d< real64 const, multifluid::USD_PHASE > const & wellElemPhaseDens_n, - arrayView4d< real64 const, multifluid::USD_PHASE_COMP > const & wellElemPhaseCompFrac_n, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) -{ - - using namespace compositionalMultiphaseUtilities; - - forAll< parallelDevicePolicy<> >( size, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) - { - - if( wellElemGhostRank[iwelem] >= 0 ) - { - return; - } - - real64 localAccum[NC]{}; - real64 localAccumJacobian[NC][NC+1]{}; - - compute< NC >( numPhases, - wellElemVolume[iwelem], - wellElemPhaseVolFrac[iwelem], - dWellElemPhaseVolFrac[iwelem], - dWellElemCompFrac_dCompDens[iwelem], - wellElemPhaseDens[iwelem][0], - dWellElemPhaseDens[iwelem][0], - wellElemPhaseCompFrac[iwelem][0], - dWellElemPhaseCompFrac[iwelem][0], - wellElemPhaseVolFrac_n[iwelem], - wellElemPhaseDens_n[iwelem][0], - wellElemPhaseCompFrac_n[iwelem][0], - localAccum, - localAccumJacobian ); - - // set the equation row indices to be the mass balance equations for all components - localIndex eqnRowIndices[NC]{}; - for( integer ic = 0; ic < NC; ++ic ) - { - eqnRowIndices[ic] = wellElemDofNumber[iwelem] + ROFFSET::MASSBAL + ic - rankOffset; - } - - // set DOF col indices for this block - globalIndex dofColIndices[NC+1]{}; - for( integer idof = 0; idof < NC+1; ++idof ) - { - dofColIndices[idof] = wellElemDofNumber[iwelem] + COFFSET::DPRES + idof; - } - - if( useTotalMassEquation > 0 ) - { - // Apply equation/variable change transformation(s) - real64 work[NC + 1]; - shiftRowsAheadByOneAndReplaceFirstRowWithColumnSum( NC, NC + 1, localAccumJacobian, work ); - shiftElementsAheadByOneAndReplaceFirstElementWithSum( NC, localAccum ); - } - - // add contribution to residual and jacobian - for( integer ic = 0; ic < NC; ++ic ) - { - localRhs[eqnRowIndices[ic]] += localAccum[ic]; - localMatrix.addToRow< serialAtomic >( eqnRowIndices[ic], - dofColIndices, - localAccumJacobian[ic], - NC+1 ); - } - } ); -} - -#define INST_AccumulationKernel( NC ) \ - template \ - void AccumulationKernel:: \ - launch< NC >( localIndex const size, \ - integer const numPhases, \ - globalIndex const rankOffset, \ - integer const useTotalMassEquation, \ - arrayView1d< globalIndex const > const & wellElemDofNumber, \ - arrayView1d< integer const > const & wellElemGhostRank, \ - arrayView1d< real64 const > const & wellElemVolume, \ - arrayView2d< real64 const, compflow::USD_PHASE > const & wellElemPhaseVolFrac, \ - arrayView3d< real64 const, compflow::USD_PHASE_DC > const & dWellElemPhaseVolFrac, \ - arrayView3d< real64 const, compflow::USD_COMP_DC > const & dWellElemCompFrac_dCompDens, \ - arrayView3d< real64 const, multifluid::USD_PHASE > const & wellElemPhaseDens, \ - arrayView4d< real64 const, multifluid::USD_PHASE_DC > const & dWellElemPhaseDens, \ - arrayView4d< real64 const, multifluid::USD_PHASE_COMP > const & wellElemPhaseCompFrac, \ - arrayView5d< real64 const, multifluid::USD_PHASE_COMP_DC > const & dWellElemPhaseCompFrac, \ - arrayView2d< real64 const, compflow::USD_PHASE > const & wellElemPhaseVolFrac_n, \ - arrayView3d< real64 const, multifluid::USD_PHASE > const & wellElemPhaseDens_n, \ - arrayView4d< real64 const, multifluid::USD_PHASE_COMP > const & wellElemPhaseCompFrac_n, \ - CRSMatrixView< real64, globalIndex const > const & localMatrix, \ - arrayView1d< real64 > const & localRhs ) - -INST_AccumulationKernel( 1 ); -INST_AccumulationKernel( 2 ); -INST_AccumulationKernel( 3 ); -INST_AccumulationKernel( 4 ); -INST_AccumulationKernel( 5 ); - -/******************************** VolumeBalanceKernel ********************************/ - -template< integer NC > -GEOS_HOST_DEVICE -void -VolumeBalanceKernel:: - compute( integer const numPhases, - real64 const & volume, - arraySlice1d< real64 const, compflow::USD_PHASE - 1 > const & phaseVolFrac, - arraySlice2d< real64 const, compflow::USD_PHASE_DC - 1 > const & dPhaseVolFrac, - real64 & localVolBalance, - real64 ( & localVolBalanceJacobian )[NC+1] ) -{ - using Deriv = multifluid::DerivativeOffset; - - localVolBalance = 1.0; - for( integer ic = 0; ic < NC+1; ++ic ) - { - localVolBalanceJacobian[ic] = 0.0; - } - - // sum contributions to component accumulation from each phase - for( integer ip = 0; ip < numPhases; ++ip ) - { - localVolBalance -= phaseVolFrac[ip]; - localVolBalanceJacobian[0] -= dPhaseVolFrac[ip][Deriv::dP]; - - for( integer jc = 0; jc < NC; ++jc ) - { - localVolBalanceJacobian[jc + 1] -= dPhaseVolFrac[ip][Deriv::dC+jc]; - } - } - - // scale saturation-based volume balance by pore volume (for better scaling w.r.t. other equations) - for( integer idof = 0; idof < NC+1; ++idof ) - { - localVolBalanceJacobian[idof] *= volume; - } - localVolBalance *= volume; -} - -template< integer NC > -void -VolumeBalanceKernel:: - launch( localIndex const size, - integer const numPhases, - globalIndex const rankOffset, - arrayView1d< globalIndex const > const & wellElemDofNumber, - arrayView1d< integer const > const & wellElemGhostRank, - arrayView2d< real64 const, compflow::USD_PHASE > const & wellElemPhaseVolFrac, - arrayView3d< real64 const, compflow::USD_PHASE_DC > const & dWellElemPhaseVolFrac, - arrayView1d< real64 const > const & wellElemVolume, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) -{ - forAll< parallelDevicePolicy<> >( size, [=] GEOS_HOST_DEVICE ( localIndex const iwelem ) - { - - if( wellElemGhostRank[iwelem] >= 0 ) - { - return; - } - - real64 localVolBalance = 1.0; - real64 localVolBalanceJacobian[NC+1]{}; - - compute< NC >( numPhases, - wellElemVolume[iwelem], - wellElemPhaseVolFrac[iwelem], - dWellElemPhaseVolFrac[iwelem], - localVolBalance, - localVolBalanceJacobian ); - - // get equation/dof indices - localIndex const localVolBalanceEqnIndex = wellElemDofNumber[iwelem] - rankOffset + ROFFSET::MASSBAL + NC; - globalIndex localVolBalanceDOF[NC+1]{}; - for( integer jdof = 0; jdof < NC+1; ++jdof ) - { - localVolBalanceDOF[jdof] = wellElemDofNumber[iwelem] + COFFSET::DPRES + jdof; - } - - localMatrix.addToRowBinarySearchUnsorted< serialAtomic >( localVolBalanceEqnIndex, - localVolBalanceDOF, - localVolBalanceJacobian, - NC+1 ); - localRhs[localVolBalanceEqnIndex] += localVolBalance; - } ); -} - -#define INST_VolumeBalanceKernel( NC ) \ - template \ - void VolumeBalanceKernel:: \ - launch< NC >( localIndex const size, \ - integer const numPhases, \ - globalIndex const rankOffset, \ - arrayView1d< globalIndex const > const & wellElemDofNumber, \ - arrayView1d< integer const > const & wellElemGhostRank, \ - arrayView2d< real64 const, compflow::USD_PHASE > const & wellElemPhaseVolFrac, \ - arrayView3d< real64 const, compflow::USD_PHASE_DC > const & dWellElemPhaseVolFrac, \ - arrayView1d< real64 const > const & wellElemVolume, \ - CRSMatrixView< real64, globalIndex const > const & localMatrix, \ - arrayView1d< real64 > const & localRhs ) - -INST_VolumeBalanceKernel( 1 ); -INST_VolumeBalanceKernel( 2 ); -INST_VolumeBalanceKernel( 3 ); -INST_VolumeBalanceKernel( 4 ); -INST_VolumeBalanceKernel( 5 ); - -/******************************** PresTempCompFracInitializationKernel ********************************/ + launch< NC, IS_THERMAL >( localIndex const size, \ + globalIndex const rankOffset, \ + bool const isLocallyOwned, \ + localIndex const iwelemControl, \ + integer const targetPhaseIndex, \ + WellControls const & wellControls, \ + real64 const & timeAtEndOfStep, \ + arrayView1d< globalIndex const > const & wellElemDofNumber, \ + arrayView1d< real64 const > const & wellElemGravCoef, \ + arrayView1d< localIndex const > const & nextWellElemIndex, \ + arrayView1d< real64 const > const & wellElemPressure, \ + arrayView1d< real64 const > const & wellElemTotalMassDens, \ + arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens, \ + bool & controlHasSwitched, \ + CRSMatrixView< real64, globalIndex const > const & localMatrix, \ + arrayView1d< real64 > const & localRhs ) + +INST_PressureRelationKernel( 1, 0 ); +INST_PressureRelationKernel( 1, 1 ); +INST_PressureRelationKernel( 2, 0 ); +INST_PressureRelationKernel( 2, 1 ); +INST_PressureRelationKernel( 3, 0 ); +INST_PressureRelationKernel( 3, 1 ); +INST_PressureRelationKernel( 4, 0 ); +INST_PressureRelationKernel( 4, 1 ); +INST_PressureRelationKernel( 5, 0 ); +INST_PressureRelationKernel( 5, 1 ); void PresTempCompFracInitializationKernel:: diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellKernels.hpp index f06b27ac202..220df6a5abc 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellKernels.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellKernels.hpp @@ -29,6 +29,7 @@ #include "constitutive/relativePermeability/RelativePermeabilityFields.hpp" #include "mesh/ElementRegionManager.hpp" #include "mesh/ObjectManagerBase.hpp" +#include "physicsSolvers/KernelLaunchSelectors.hpp" #include "physicsSolvers/fluidFlow/CompositionalMultiphaseBaseFields.hpp" #include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" #include "physicsSolvers/fluidFlow/IsothermalCompositionalMultiphaseBaseKernels.hpp" @@ -40,6 +41,7 @@ namespace geos { + namespace compositionalMultiphaseWellKernels { @@ -66,6 +68,30 @@ struct ColOffset static constexpr integer DCOMP = 1; }; +template< integer NC, integer IS_THERMAL > +struct ColOffset_WellJac; + +template< integer NC > +struct ColOffset_WellJac< NC, 0 > +{ + static constexpr integer dP = 0; + static constexpr integer dC = 1; + static constexpr integer dQ = dC + NC; + static integer constexpr nDer = dQ + 1; + +}; + +template< integer NC > +struct ColOffset_WellJac< NC, 1 > +{ + static constexpr integer dP = 0; + static constexpr integer dC = 1; + static constexpr integer dQ = dC + NC; + static constexpr integer dT = dQ+1; +/// number of derivatives + static integer constexpr nDer = dT + 1; +}; + // define the row offset of the residual equations struct RowOffset { @@ -73,11 +99,31 @@ struct RowOffset static constexpr integer MASSBAL = 1; }; -/******************************** ControlEquationHelper ********************************/ +template< integer NC, integer IS_THERMAL > +struct RowOffset_WellJac; -struct ControlEquationHelper +template< integer NC > +struct RowOffset_WellJac< NC, 0 > +{ + static constexpr integer CONTROL = 0; + static constexpr integer MASSBAL = 1; + static constexpr integer VOLBAL = MASSBAL + NC; + static constexpr integer nEqn = VOLBAL+1; +}; + +template< integer NC > +struct RowOffset_WellJac< NC, 1 > { + static constexpr integer CONTROL = 0; + static constexpr integer MASSBAL = 1; + static constexpr integer VOLBAL = MASSBAL + NC; + static constexpr integer ENERGYBAL = VOLBAL+1; + static constexpr integer nEqn = ENERGYBAL+1; +}; +/******************************** ControlEquationHelper ********************************/ +struct ControlEquationHelper +{ using ROFFSET = compositionalMultiphaseWellKernels::RowOffset; using COFFSET = compositionalMultiphaseWellKernels::ColOffset; @@ -97,7 +143,7 @@ struct ControlEquationHelper real64 const & currentTotalVolRate, WellControls::Control & newControl ); - template< integer NC > + template< integer NC, integer IS_THERMAL > GEOS_HOST_DEVICE inline static void @@ -109,16 +155,11 @@ struct ControlEquationHelper real64 const & targetTotalRate, real64 const & targetMassRate, real64 const & currentBHP, - real64 const & dCurrentBHP_dPres, - arrayView1d< real64 const > const & dCurrentBHP_dCompDens, + arrayView1d< real64 const > const & dCurrentBHP, arrayView1d< real64 const > const & currentPhaseVolRate, - arrayView1d< real64 const > const & dCurrentPhaseVolRate_dPres, - arrayView2d< real64 const > const & dCurrentPhaseVolRate_dCompDens, - arrayView1d< real64 const > const & dCurrentPhaseVolRate_dRate, + arrayView2d< real64 const > const & dCurrentPhaseVolRate, real64 const & currentTotalVolRate, - real64 const & dCurrentTotalVolRate_dPres, - arrayView1d< real64 const > const & dCurrentTotalVolRate_dCompDens, - real64 const & dCurrentTotalVolRate_dRate, + arrayView1d< real64 const > const & dCurrentTotalVolRate, real64 const & massDensity, globalIndex const dofNumber, CRSMatrixView< real64, globalIndex const > const & localMatrix, @@ -126,68 +167,16 @@ struct ControlEquationHelper }; -/******************************** FluxKernel ********************************/ - -struct FluxKernel -{ - - using TAG = compositionalMultiphaseWellKernels::ElemTag; - using ROFFSET = compositionalMultiphaseWellKernels::RowOffset; - using COFFSET = compositionalMultiphaseWellKernels::ColOffset; - - template< integer NC > - GEOS_HOST_DEVICE - inline - static void - computeExit( real64 const & dt, - real64 const ( &compFlux )[NC], - real64 const ( &dCompFlux_dRate )[NC], - real64 const ( &dCompFlux_dPresUp )[NC], - real64 const ( &dCompFlux_dCompDensUp )[NC][NC], - real64 ( &oneSidedFlux )[NC], - real64 ( &oneSidedFluxJacobian_dRate )[NC][1], - real64 ( &oneSidedFluxJacobian_dPresCompUp )[NC][NC + 1] ); - - template< integer NC > - GEOS_HOST_DEVICE - inline - static void - compute( real64 const & dt, - real64 const ( &compFlux )[NC], - real64 const ( &dCompFlux_dRate )[NC], - real64 const ( &dCompFlux_dPresUp )[NC], - real64 const ( &dCompFlux_dCompDensUp )[NC][NC], - real64 ( &localFlux )[2*NC], - real64 ( &localFluxJacobian_dRate )[2*NC][1], - real64 ( &localFluxJacobian_dPresCompUp )[2*NC][NC + 1] ); - - template< integer NC > - static void - launch( localIndex const size, - globalIndex const rankOffset, - integer const useTotalMassEquation, - WellControls const & wellControls, - arrayView1d< globalIndex const > const & wellElemDofNumber, - arrayView1d< localIndex const > const & nextWellElemIndex, - arrayView1d< real64 const > const & connRate, - arrayView2d< real64 const, compflow::USD_COMP > const & wellElemCompFrac, - arrayView3d< real64 const, compflow::USD_COMP_DC > const & dWellElemCompFrac_dCompDens, - real64 const & dt, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ); - -}; - /******************************** PressureRelationKernel ********************************/ struct PressureRelationKernel { - + using Deriv = constitutive::multifluid::DerivativeOffset; using TAG = compositionalMultiphaseWellKernels::ElemTag; using ROFFSET = compositionalMultiphaseWellKernels::RowOffset; using COFFSET = compositionalMultiphaseWellKernels::ColOffset; - template< integer NC > + template< integer NC, integer IS_THERMAL > GEOS_HOST_DEVICE inline static void @@ -197,14 +186,12 @@ struct PressureRelationKernel real64 const & presNext, real64 const & totalMassDens, real64 const & totalMassDensNext, - real64 const & dTotalMassDens_dPres, - real64 const & dTotalMassDens_dPresNext, - arraySlice1d< real64 const, compflow::USD_FLUID_DC - 1 > const & dTotalMassDens_dCompDens, - arraySlice1d< real64 const, compflow::USD_FLUID_DC - 1 > const & dTotalMassDens_dCompDensNext, + arraySlice1d< real64 const, compflow::USD_FLUID_DC - 1 > const & dTotalMassDens, + arraySlice1d< real64 const, compflow::USD_FLUID_DC - 1 > const & dTotalMassDensNext, real64 & localPresRel, - real64 ( &localPresRelJacobian )[2*(NC+1)] ); + real64 ( &localPresRelJacobian )[2*(NC+1+IS_THERMAL)] ); - template< integer NC > + template< integer NC, integer IS_THERMAL > static void launch( localIndex const size, globalIndex const rankOffset, @@ -218,171 +205,13 @@ struct PressureRelationKernel arrayView1d< localIndex const > const & nextWellElemIndex, arrayView1d< real64 const > const & wellElemPressure, arrayView1d< real64 const > const & wellElemTotalMassDens, - arrayView1d< real64 const > const & dWellElemTotalMassDens_dPres, - arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens_dCompDens, + arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens, bool & controlHasSwitched, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ); }; -/******************************** PerforationKernel ********************************/ - -struct PerforationKernel -{ - - using TAG = compositionalMultiphaseWellKernels::SubRegionTag; - - using CompFlowAccessors = - StencilAccessors< fields::flow::pressure, - fields::flow::phaseVolumeFraction, - fields::flow::dPhaseVolumeFraction, - fields::flow::dGlobalCompFraction_dGlobalCompDensity >; - - using MultiFluidAccessors = - StencilMaterialAccessors< constitutive::MultiFluidBase, - fields::multifluid::phaseDensity, - fields::multifluid::dPhaseDensity, - fields::multifluid::phaseViscosity, - fields::multifluid::dPhaseViscosity, - fields::multifluid::phaseCompFraction, - fields::multifluid::dPhaseCompFraction >; - - using RelPermAccessors = - StencilMaterialAccessors< constitutive::RelativePermeabilityBase, - fields::relperm::phaseRelPerm, - fields::relperm::dPhaseRelPerm_dPhaseVolFraction >; - - - /** - * @brief The type for element-based non-constitutive data parameters. - * Consists entirely of ArrayView's. - * - * Can be converted from ElementRegionManager::ElementViewAccessor - * by calling .toView() or .toViewConst() on an accessor instance - */ - template< typename VIEWTYPE > - using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; - - - template< integer NC, integer NP > - GEOS_HOST_DEVICE - inline - static void - compute( bool const & disableReservoirToWellFlow, - real64 const & resPres, - arraySlice1d< real64 const, compflow::USD_PHASE - 1 > const & resPhaseVolFrac, - arraySlice2d< real64 const, compflow::USD_PHASE_DC - 1 > const & dResPhaseVolFrac, - arraySlice2d< real64 const, compflow::USD_COMP_DC - 1 > const & dResCompFrac_dCompDens, - arraySlice1d< real64 const, constitutive::multifluid::USD_PHASE - 2 > const & resPhaseDens, - arraySlice2d< real64 const, constitutive::multifluid::USD_PHASE_DC - 2 > const & dResPhaseDens, - arraySlice1d< real64 const, constitutive::multifluid::USD_PHASE - 2 > const & resPhaseVisc, - arraySlice2d< real64 const, constitutive::multifluid::USD_PHASE_DC - 2 > const & dResPhaseVisc, - arraySlice2d< real64 const, constitutive::multifluid::USD_PHASE_COMP - 2 > const & resPhaseCompFrac, - arraySlice3d< real64 const, constitutive::multifluid::USD_PHASE_COMP_DC - 2 > const & dResPhaseCompFrac, - arraySlice1d< real64 const, constitutive::relperm::USD_RELPERM - 2 > const & resPhaseRelPerm, - arraySlice2d< real64 const, constitutive::relperm::USD_RELPERM_DS - 2 > const & dResPhaseRelPerm_dPhaseVolFrac, - real64 const & wellElemGravCoef, - real64 const & wellElemPres, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & wellElemCompDens, - real64 const & wellElemTotalMassDens, - real64 const & dWellElemTotalMassDens_dPres, - arraySlice1d< real64 const, compflow::USD_FLUID_DC - 1 > const & dWellElemTotalMassDens_dCompDens, - arraySlice1d< real64 const, compflow::USD_COMP - 1 > const & wellElemCompFrac, - arraySlice2d< real64 const, compflow::USD_COMP_DC - 1 > const & dWellElemCompFrac_dCompDens, - real64 const & perfGravCoef, - real64 const & trans, - arraySlice1d< real64 > const & compPerfRate, - arraySlice2d< real64 > const & dCompPerfRate_dPres, - arraySlice3d< real64 > const & dCompPerfRate_dComp ); - - template< integer NC, integer NP > - static void - launch( localIndex const size, - bool const disableReservoirToWellFlow, - ElementViewConst< arrayView1d< real64 const > > const & resPres, - ElementViewConst< arrayView2d< real64 const, compflow::USD_PHASE > > const & resPhaseVolFrac, - ElementViewConst< arrayView3d< real64 const, compflow::USD_PHASE_DC > > const & dResPhaseVolFrac_dComp, - ElementViewConst< arrayView3d< real64 const, compflow::USD_COMP_DC > > const & dResCompFrac_dCompDens, - ElementViewConst< arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > > const & resPhaseDens, - ElementViewConst< arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > > const & dResPhaseDens, - ElementViewConst< arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > > const & resPhaseVisc, - ElementViewConst< arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > > const & dResPhaseVisc, - ElementViewConst< arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_COMP > > const & resPhaseCompFrac, - ElementViewConst< arrayView5d< real64 const, constitutive::multifluid::USD_PHASE_COMP_DC > > const & dResPhaseCompFrac, - ElementViewConst< arrayView3d< real64 const, constitutive::relperm::USD_RELPERM > > const & resPhaseRelPerm, - ElementViewConst< arrayView4d< real64 const, constitutive::relperm::USD_RELPERM_DS > > const & dResPhaseRelPerm_dPhaseVolFrac, - arrayView1d< real64 const > const & wellElemGravCoef, - arrayView1d< real64 const > const & wellElemPres, - arrayView2d< real64 const, compflow::USD_COMP > const & wellElemCompDens, - arrayView1d< real64 const > const & wellElemTotalMassDens, - arrayView1d< real64 const > const & dWellElemTotalMassDens_dPres, - arrayView2d< real64 const, compflow::USD_FLUID_DC > const & dWellElemTotalMassDens_dCompDens, - arrayView2d< real64 const, compflow::USD_COMP > const & wellElemCompFrac, - arrayView3d< real64 const, compflow::USD_COMP_DC > const & dWellElemCompFrac_dCompDens, - arrayView1d< real64 const > const & perfGravCoef, - arrayView1d< localIndex const > const & perfWellElemIndex, - arrayView1d< real64 const > const & perfTrans, - arrayView1d< localIndex const > const & resElementRegion, - arrayView1d< localIndex const > const & resElementSubRegion, - arrayView1d< localIndex const > const & resElementIndex, - arrayView2d< real64 > const & compPerfRate, - arrayView3d< real64 > const & dCompPerfRate_dPres, - arrayView4d< real64 > const & dCompPerfRate_dComp ); - -}; - -/******************************** AccumulationKernel ********************************/ - -struct AccumulationKernel -{ - - using ROFFSET = compositionalMultiphaseWellKernels::RowOffset; - using COFFSET = compositionalMultiphaseWellKernels::ColOffset; - - template< integer NC > - GEOS_HOST_DEVICE - inline - static void - compute( integer const numPhases, - real64 const & volume, - arraySlice1d< real64 const, compflow::USD_PHASE - 1 > const & phaseVolFrac, - arraySlice2d< real64 const, compflow::USD_PHASE_DC - 1 > const & dPhaseVolFrac, - arraySlice2d< real64 const, compflow::USD_COMP_DC - 1 > const & dCompFrac_dCompDens, - arraySlice1d< real64 const, constitutive::multifluid::USD_PHASE - 2 > const & phaseDens, - arraySlice2d< real64 const, constitutive::multifluid::USD_PHASE_DC - 2 > const & dPhaseDens, - arraySlice2d< real64 const, constitutive::multifluid::USD_PHASE_COMP - 2 > const & phaseCompFrac, - arraySlice3d< real64 const, constitutive::multifluid::USD_PHASE_COMP_DC - 2 > const & dPhaseCompFrac, - arraySlice1d< real64 const, compflow::USD_PHASE - 1 > const & phaseVolFrac_n, - arraySlice1d< real64 const, constitutive::multifluid::USD_PHASE - 2 > const & phaseDens_n, - arraySlice2d< real64 const, constitutive::multifluid::USD_PHASE_COMP - 2 > const & phaseCompFrac_n, - real64 ( &localAccum )[NC], - real64 ( &localAccumJacobian )[NC][NC + 1] ); - - template< integer NC > - static void - launch( localIndex const size, - integer const numPhases, - globalIndex const rankOffset, - integer const useTotalMassEquation, - arrayView1d< globalIndex const > const & wellElemDofNumber, - arrayView1d< integer const > const & wellElemGhostRank, - arrayView1d< real64 const > const & wellElemVolume, - arrayView2d< real64 const, compflow::USD_PHASE > const & wellElemPhaseVolFrac, - arrayView3d< real64 const, compflow::USD_PHASE_DC > const & dWellElemPhaseVolFrac, - arrayView3d< real64 const, compflow::USD_COMP_DC > const & dWellElemCompFrac_dCompDens, - arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & wellElemPhaseDens, - arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const & dWellElemPhaseDens, - arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_COMP > const & wellElemPhaseCompFrac, - arrayView5d< real64 const, constitutive::multifluid::USD_PHASE_COMP_DC > const & dWellElemPhaseCompFrac, - arrayView2d< real64 const, compflow::USD_PHASE > const & wellElemPhaseVolFrac_n, - arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const & wellElemPhaseDens_n, - arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_COMP > const & wellElemPhaseCompFrac_n, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ); - -}; - /******************************** VolumeBalanceKernel ********************************/ struct VolumeBalanceKernel @@ -531,8 +360,7 @@ class TotalMassDensityKernel : public isothermalCompositionalMultiphaseBaseKerne m_phaseMassDens( fluid.phaseMassDensity() ), m_dPhaseMassDens( fluid.dPhaseMassDensity() ), m_totalMassDens( subRegion.getField< fields::well::totalMassDensity >() ), - m_dTotalMassDens_dPres( subRegion.getField< fields::well::dTotalMassDensity_dPressure >() ), - m_dTotalMassDens_dCompDens( subRegion.getField< fields::well::dTotalMassDensity_dGlobalCompDensity >() ) + m_dTotalMassDens( subRegion.getField< fields::well::dTotalMassDensity >() ) {} /** @@ -555,31 +383,31 @@ class TotalMassDensityKernel : public isothermalCompositionalMultiphaseBaseKerne arraySlice1d< real64 const, constitutive::multifluid::USD_PHASE - 2 > phaseMassDens = m_phaseMassDens[ei][0]; arraySlice2d< real64 const, constitutive::multifluid::USD_PHASE_DC - 2 > dPhaseMassDens = m_dPhaseMassDens[ei][0]; real64 & totalMassDens = m_totalMassDens[ei]; - real64 & dTotalMassDens_dPres = m_dTotalMassDens_dPres[ei]; - arraySlice1d< real64, compflow::USD_FLUID_DC - 1 > dTotalMassDens_dCompDens = m_dTotalMassDens_dCompDens[ei]; + arraySlice1d< real64, compflow::USD_FLUID_DC - 1 > dTotalMassDens = m_dTotalMassDens[ei]; real64 dMassDens_dC[numComp]{}; totalMassDens = 0.0; - dTotalMassDens_dPres = 0.0; + + dTotalMassDens[Deriv::dP]=0.0; for( integer ic = 0; ic < numComp; ++ic ) { - dTotalMassDens_dCompDens[ic] = 0.0; + dTotalMassDens[Deriv::dC+ic]=0.0; } for( integer ip = 0; ip < numPhase; ++ip ) { totalMassDens += phaseVolFrac[ip] * phaseMassDens[ip]; - dTotalMassDens_dPres += dPhaseVolFrac[ip][Deriv::dP] * phaseMassDens[ip] + phaseVolFrac[ip] * dPhaseMassDens[ip][Deriv::dP]; + dTotalMassDens[Deriv::dP] += dPhaseVolFrac[ip][Deriv::dP] * phaseMassDens[ip] + phaseVolFrac[ip] * dPhaseMassDens[ip][Deriv::dP]; applyChainRule( numComp, dCompFrac_dCompDens, dPhaseMassDens[ip], dMassDens_dC, Deriv::dC ); for( integer ic = 0; ic < numComp; ++ic ) { - dTotalMassDens_dCompDens[ic] += dPhaseVolFrac[ip][Deriv::dC+ic] * phaseMassDens[ip] + dTotalMassDens[Deriv::dC+ic] += dPhaseVolFrac[ip][Deriv::dC+ic] * phaseMassDens[ip] + phaseVolFrac[ip] * dMassDens_dC[ic]; } - totalMassDensityKernelOp( ip, totalMassDens, dTotalMassDens_dPres, dTotalMassDens_dCompDens ); + totalMassDensityKernelOp( ip ); //, phaseVolFrac, dTotalMassDens_dPres, dTotalMassDens_dCompDens ); } } @@ -601,8 +429,8 @@ class TotalMassDensityKernel : public isothermalCompositionalMultiphaseBaseKerne /// Views on total mass densities arrayView1d< real64 > m_totalMassDens; - arrayView1d< real64 > m_dTotalMassDens_dPres; - arrayView2d< real64, compflow::USD_FLUID_DC > m_dTotalMassDens_dCompDens; + arrayView2d< real64, compflow::USD_FLUID_DC > m_dTotalMassDens; + }; @@ -772,7 +600,7 @@ class ResidualNormKernel : public solverBaseKernels::ResidualNormKernelBase< 1 > normalizer = LvArray::math::max( normalizer, m_volume[iwelem] * m_totalDens_n[iwelem][0] ); } // Step 3: compute a normalizer for the volume balance equations - else + else if( idof == ROFFSET::MASSBAL + m_numComp ) { if( m_isProducer ) // only PHASEVOLRATE is supported for now { @@ -792,10 +620,10 @@ class ResidualNormKernel : public solverBaseKernels::ResidualNormKernelBase< 1 > } - // to make sure that everything still works well if the rate is zero, we add this check - normalizer = LvArray::math::max( normalizer, m_volume[iwelem] ); } - normalizer = LvArray::math::max( m_minNormalizer, normalizer ); + + // to make sure that everything still works well if the rate is zero, we add this check + normalizer = LvArray::math::max( normalizer, m_volume[iwelem] ); // Step 4: compute the contribution to the residual real64 const val = LvArray::math::abs( m_localResidual[stack.localRow + idof] ) / normalizer; @@ -980,19 +808,21 @@ class SolutionCheckKernelFactory createAndLaunch( integer const allowCompDensChopping, CompositionalMultiphaseFVM::ScalingType const scalingType, real64 const scalingFactor, + arrayView1d< real64 const > const pressure, + arrayView2d< real64 const, compflow::USD_COMP > const compDens, + arrayView1d< real64 > pressureScalingFactor, + arrayView1d< real64 > compDensScalingFactor, globalIndex const rankOffset, integer const numComp, string const dofKey, ElementSubRegionBase & subRegion, arrayView1d< real64 const > const localSolution ) { - arrayView1d< real64 const > const pressure = subRegion.getField< fields::well::pressure >(); - arrayView2d< real64 const, compflow::USD_COMP > const compDens = subRegion.getField< fields::well::globalCompDensity >(); - arrayView1d< real64 > pressureScalingFactor = subRegion.getField< fields::well::pressureScalingFactor >(); - arrayView1d< real64 > compDensScalingFactor = subRegion.getField< fields::well::globalCompDensityScalingFactor >(); + isothermalCompositionalMultiphaseBaseKernels:: - SolutionCheckKernel kernel( allowCompDensChopping, 0, scalingType, scalingFactor, rankOffset, // no negative pressure - numComp, dofKey, subRegion, localSolution, pressure, compDens, pressureScalingFactor, compDensScalingFactor ); + SolutionCheckKernel kernel( allowCompDensChopping, 0, scalingType, scalingFactor, + pressure, compDens, pressureScalingFactor, compDensScalingFactor, rankOffset, + numComp, dofKey, subRegion, localSolution ); return isothermalCompositionalMultiphaseBaseKernels:: SolutionCheckKernel:: launch< POLICY >( subRegion.size(), kernel ); @@ -1000,7 +830,1086 @@ class SolutionCheckKernelFactory }; +/******************************** ElementBasedAssemblyKernel ********************************/ + +/** + * @class ElementBasedAssemblyKernel + * @tparam NUM_COMP number of fluid components + * @tparam IS_THERMAL thermal switch + * @brief Define the interface for the assembly kernel in charge of accumulation and volume balance + */ +template< integer NUM_COMP, integer IS_THERMAL > +class ElementBasedAssemblyKernel +{ +public: + using COFFSET = compositionalMultiphaseWellKernels::ColOffset; + using ROFFSET = compositionalMultiphaseWellKernels::RowOffset; + + // Well jacobian column and row indicies + // tjb - change NUM_DOF to IS_THERMAL + using FLUID_PROP_COFFSET = constitutive::multifluid::DerivativeOffsetC< NUM_COMP, IS_THERMAL >; + using WJ_COFFSET = compositionalMultiphaseWellKernels::ColOffset_WellJac< NUM_COMP, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NUM_COMP, IS_THERMAL >; + /// Compile time value for the number of components + static constexpr integer numComp = NUM_COMP; + + /// Number of Dof's set in this kernal - no dQ in accum + static constexpr integer numDof = NUM_COMP + 1 + IS_THERMAL; + + /// Compute time value for the number of equations mass bal + vol bal + energy bal + static constexpr integer numEqn = NUM_COMP + 1 + IS_THERMAL; + + + /** + * @brief Constructor + * @param[in] numPhases the number of fluid phases + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey the string key to retrieve the degress of freedom numbers + * @param[in] subRegion the element subregion + * @param[in] fluid the fluid model + * @param[in] solid the solid model + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + ElementBasedAssemblyKernel( localIndex const numPhases, + integer const isProducer, + globalIndex const rankOffset, + string const dofKey, + WellElementSubRegion const & subRegion, + constitutive::MultiFluidBase const & fluid, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs, + BitFlags< isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags > const kernelFlags ) + : m_numPhases( numPhases ), + m_isProducer( isProducer ), + m_rankOffset( rankOffset ), + m_iwelemControl( subRegion.getTopWellElementIndex() ), + m_dofNumber( subRegion.getReference< array1d< globalIndex > >( dofKey ) ), + m_elemGhostRank( subRegion.ghostRank() ), + m_volume( subRegion.getElementVolume() ), + m_dCompFrac_dCompDens( subRegion.getField< fields::flow::dGlobalCompFraction_dGlobalCompDensity >() ), + m_phaseVolFrac_n( subRegion.getField< fields::flow::phaseVolumeFraction_n >() ), + m_phaseVolFrac( subRegion.getField< fields::flow::phaseVolumeFraction >() ), + m_dPhaseVolFrac( subRegion.getField< fields::flow::dPhaseVolumeFraction >() ), + m_phaseDens_n( fluid.phaseDensity_n() ), + m_phaseDens( fluid.phaseDensity() ), + m_dPhaseDens( fluid.dPhaseDensity() ), + m_phaseCompFrac_n( fluid.phaseCompFraction_n() ), + m_phaseCompFrac( fluid.phaseCompFraction() ), + m_dPhaseCompFrac( fluid.dPhaseCompFraction() ), + m_compDens( subRegion.getField< fields::flow::globalCompDensity >() ), + m_compDens_n( subRegion.getField< fields::flow::globalCompDensity_n >() ), + m_localMatrix( localMatrix ), + m_localRhs( localRhs ), + m_kernelFlags( kernelFlags ) + {} + + /** + * @struct StackVariables + * @brief Kernel variables (dof numbers, jacobian and residual) located on the stack + */ + struct StackVariables + { +public: + + // volume information (used by both accumulation and volume balance) + real64 volume = 0.0; + + // Residual information + + /// Index of the local row corresponding to this element + localIndex localRow = -1; + + /// Indices of the matrix rows/columns corresponding to the dofs in this element + globalIndex dofIndices[numDof]{}; // NC compdens + P + thermal + globalIndex eqnRowIndices[numDof]{}; + globalIndex dofColIndices[numDof]{}; + + /// C-array storage for the element local residual vector (all equations except constraint and momentum) + real64 localResidual[numEqn]{}; + + /// C-array storage for the element local Jacobian matrix (all equations except constraint and momentum) + real64 localJacobian[numEqn][numDof]{}; + + }; + + /** + * @brief Getter for the ghost rank of an element + * @param[in] ei the element index + * @return the ghost rank of the element + */ + GEOS_HOST_DEVICE + integer elemGhostRank( localIndex const ei ) const + { return m_elemGhostRank( ei ); } + + + /** + * @brief Performs the setup phase for the kernel. + * @param[in] ei the element index + * @param[in] stack the stack variables + */ + GEOS_HOST_DEVICE + void setup( localIndex const ei, + StackVariables & stack ) const + { + // initialize the volume + stack.volume = m_volume[ei]; + + // Note row/col indices needed to be consistent with layout of stack.localJacobian + // Setup row equation indices for this element ( mass + vol + thermal if valid) + + // 1) Mass Balance + for( integer ic = 0; ic < numComp; ++ic ) + { + stack.eqnRowIndices[ic] = m_dofNumber[ei] + WJ_ROFFSET::MASSBAL + ic - m_rankOffset; + } +// 2) Volume Balance + stack.eqnRowIndices[numComp] = m_dofNumber[ei] + WJ_ROFFSET::VOLBAL - m_rankOffset; + // 3) Energy Balance + if constexpr ( IS_THERMAL ) + { + stack.eqnRowIndices[numComp+1] = m_dofNumber[ei] + WJ_ROFFSET::ENERGYBAL - m_rankOffset; + } + // Setup equation column indices for this element ( P + COMPDENS + THERMAL if valid) + stack.dofColIndices[0] = m_dofNumber[ei] + WJ_COFFSET::dP; + for( integer ic = 0; ic < numComp; ++ic ) + { + stack.dofColIndices[ic+1] = m_dofNumber[ei] + WJ_COFFSET::dC+ic; + } + if constexpr ( IS_THERMAL ) + { + stack.dofColIndices[numComp+1] = m_dofNumber[ei] + WJ_COFFSET::dT; + } + if( 1 ) + for( integer jc = 0; jc < numEqn; ++jc ) + { + stack.localResidual[jc] = 0.0; + for( integer ic = 0; ic < numDof; ++ic ) + { + stack.localJacobian[jc][ic] = 0.0; + } + + } + + } + + /** + * @brief Compute the local accumulation contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the kernel + * @param[in] ei the element index + * @param[inout] stack the stack variables + * @param[in] phaseAmountKernelOp the function used to customize the kernel + */ + template< typename FUNC = NoOpFunc > + GEOS_HOST_DEVICE + void computeAccumulation( localIndex const ei, + StackVariables & stack, + FUNC && phaseAmountKernelOp = NoOpFunc{} ) const + { + + using Deriv = constitutive::multifluid::DerivativeOffset; + + // construct the slices for variables accessed multiple times + arraySlice2d< real64 const, compflow::USD_COMP_DC - 1 > dCompFrac_dCompDens = m_dCompFrac_dCompDens[ei]; + + arraySlice1d< real64 const, compflow::USD_PHASE - 1 > phaseVolFrac_n = m_phaseVolFrac_n[ei]; + arraySlice1d< real64 const, compflow::USD_PHASE - 1 > phaseVolFrac = m_phaseVolFrac[ei]; + arraySlice2d< real64 const, compflow::USD_PHASE_DC - 1 > dPhaseVolFrac = m_dPhaseVolFrac[ei]; + + arraySlice1d< real64 const, constitutive::multifluid::USD_PHASE - 2 > phaseDens_n = m_phaseDens_n[ei][0]; + arraySlice1d< real64 const, constitutive::multifluid::USD_PHASE - 2 > phaseDens = m_phaseDens[ei][0]; + arraySlice2d< real64 const, constitutive::multifluid::USD_PHASE_DC - 2 > dPhaseDens = m_dPhaseDens[ei][0]; + + arraySlice2d< real64 const, constitutive::multifluid::USD_PHASE_COMP - 2 > phaseCompFrac_n = m_phaseCompFrac_n[ei][0]; + arraySlice2d< real64 const, constitutive::multifluid::USD_PHASE_COMP - 2 > phaseCompFrac = m_phaseCompFrac[ei][0]; + arraySlice3d< real64 const, constitutive::multifluid::USD_PHASE_COMP_DC - 2 > dPhaseCompFrac = m_dPhaseCompFrac[ei][0]; + + // temporary work arrays + real64 dPhaseAmount[FLUID_PROP_COFFSET::nDer]{}; + real64 dPhaseAmount_dC[numComp]{}; + real64 dPhaseCompFrac_dC[numComp]{}; + + // sum contributions to component accumulation from each phase + for( integer ip = 0; ip < m_numPhases; ++ip ) + { + real64 const phaseAmount = stack.volume * phaseVolFrac[ip] * phaseDens[ip]; + real64 const phaseAmount_n = stack.volume * phaseVolFrac_n[ip] * phaseDens_n[ip]; + //remove tjb + real64 const dPhaseAmount_dP = stack.volume * ( dPhaseVolFrac[ip][Deriv::dP] * phaseDens[ip] + + phaseVolFrac[ip] * dPhaseDens[ip][Deriv::dP] ); + dPhaseAmount[FLUID_PROP_COFFSET::dP]=stack.volume * ( dPhaseVolFrac[ip][Deriv::dP] * phaseDens[ip] + + phaseVolFrac[ip] * dPhaseDens[ip][Deriv::dP] ); + + // assemble density dependence + applyChainRule( numComp, dCompFrac_dCompDens, dPhaseDens[ip], dPhaseAmount_dC, Deriv::dC ); + applyChainRule( numComp, dCompFrac_dCompDens, dPhaseDens[ip], &dPhaseAmount[FLUID_PROP_COFFSET::dC], Deriv::dC ); + for( integer jc = 0; jc < numComp; ++jc ) + { + dPhaseAmount_dC[jc] = dPhaseAmount_dC[jc] * phaseVolFrac[ip] + + phaseDens[ip] * dPhaseVolFrac[ip][Deriv::dC+jc]; + dPhaseAmount_dC[jc] *= stack.volume; + dPhaseAmount[FLUID_PROP_COFFSET::dC+jc] = dPhaseAmount[FLUID_PROP_COFFSET::dC+jc] * phaseVolFrac[ip] + + phaseDens[ip] * dPhaseVolFrac[ip][Deriv::dC+jc]; + dPhaseAmount[FLUID_PROP_COFFSET::dC+jc] *= stack.volume; + } +// tjb- remove when safe + for( integer ic = 0; ic < numComp; ic++ ) + { + assert( fabs( dPhaseAmount[FLUID_PROP_COFFSET::dC+ic] -dPhaseAmount_dC[ic] ) < FLT_EPSILON ); + + } + // ic - index of component whose conservation equation is assembled + // (i.e. row number in local matrix) + for( integer ic = 0; ic < numComp; ++ic ) + { + real64 const phaseCompAmount = phaseAmount * phaseCompFrac[ip][ic]; + real64 const phaseCompAmount_n = phaseAmount_n * phaseCompFrac_n[ip][ic]; + + real64 const dPhaseCompAmount_dP = dPhaseAmount_dP * phaseCompFrac[ip][ic] + + phaseAmount * dPhaseCompFrac[ip][ic][Deriv::dP]; + + stack.localResidual[ic] += phaseCompAmount - phaseCompAmount_n; + stack.localJacobian[ic][0] += dPhaseCompAmount_dP; + + // jc - index of component w.r.t. whose compositional var the derivative is being taken + // (i.e. col number in local matrix) + + // assemble phase composition dependence + applyChainRule( numComp, dCompFrac_dCompDens, dPhaseCompFrac[ip][ic], dPhaseCompFrac_dC, Deriv::dC ); + for( integer jc = 0; jc < numComp; ++jc ) + { + real64 const dPhaseCompAmount_dC = dPhaseCompFrac_dC[jc] * phaseAmount + + phaseCompFrac[ip][ic] * dPhaseAmount[FLUID_PROP_COFFSET::dC+jc]; + + stack.localJacobian[ic][jc + 1] += dPhaseCompAmount_dC; + } + } + if constexpr ( IS_THERMAL ) + { + dPhaseAmount[FLUID_PROP_COFFSET::dT] = stack.volume * (dPhaseVolFrac[ip][Deriv::dT] * phaseDens[ip] + phaseVolFrac[ip] * dPhaseDens[ip][Deriv::dT] ); + for( integer ic = 0; ic < numComp; ++ic ) + { + // assemble the derivatives of the component mass balance equations with respect to temperature + stack.localJacobian[ic][numComp+1] += dPhaseAmount[FLUID_PROP_COFFSET::dT] * phaseCompFrac[ip][ic] + + phaseAmount * dPhaseCompFrac[ip][ic][Deriv::dT]; + } + } + // call the lambda in the phase loop to allow the reuse of the phase amounts and their derivatives + // possible use: assemble accumulation term of the energy equation for this phase + phaseAmountKernelOp( ip, phaseAmount, phaseAmount_n, dPhaseAmount ); + + } + + // check zero diagonal (works only in debug) + /* + for( integer ic = 0; ic < numComp; ++ic ) + { + GEOS_ASSERT_MSG ( LvArray::math::abs( stack.localJacobian[ic][ic] ) > minDensForDivision, + GEOS_FMT( "Zero diagonal in Jacobian: equation {}, value = {}", ic, stack.localJacobian[ic][ic] ) ); + } + */ + } + + + /** + * @brief Compute the local volume balance contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the kernel + * @param[in] ei the element index + * @param[inout] stack the stack variables + * @param[in] phaseVolFractionSumKernelOp the function used to customize the kernel + */ + + GEOS_HOST_DEVICE + void computeVolumeBalance( localIndex const ei, + StackVariables & stack ) const + { + using Deriv = constitutive::multifluid::DerivativeOffset; + + arraySlice1d< real64 const, compflow::USD_PHASE - 1 > phaseVolFrac = m_phaseVolFrac[ei]; + arraySlice2d< real64 const, compflow::USD_PHASE_DC - 1 > dPhaseVolFrac = m_dPhaseVolFrac[ei]; + + real64 oneMinusPhaseVolFracSum = 1.0; + + // sum contributions to component accumulation from each phase +// Note localJacobian stores equation balances in order of component/vol/enerqy + // These are mapped to solver orderings with indicies setup in stack variables + for( integer ip = 0; ip < m_numPhases; ++ip ) + { + oneMinusPhaseVolFracSum -= phaseVolFrac[ip]; + stack.localJacobian[numComp][0] -= dPhaseVolFrac[ip][Deriv::dP]; + + for( integer jc = 0; jc < numComp; ++jc ) + { + stack.localJacobian[numComp][jc+1] -= dPhaseVolFrac[ip][Deriv::dC+jc]; + } + if constexpr ( IS_THERMAL) + { + stack.localJacobian[numComp][numComp+1] -= dPhaseVolFrac[ip][Deriv::dT]; + } + + } + // scale saturation-based volume balance by pore volume (for better scaling w.r.t. other equations) + stack.localResidual[numComp] = stack.volume * oneMinusPhaseVolFracSum; + for( integer idof = 0; idof < numComp+1+IS_THERMAL; ++idof ) + { + stack.localJacobian[numComp][idof] *= stack.volume; + } + + } + + /** + * @brief Performs the complete phase for the kernel. + * @param[in] ei the element index + * @param[inout] stack the stack variables + */ + GEOS_HOST_DEVICE + void complete( localIndex const ei, //GEOS_UNUSED_PARAM( ei ), + StackVariables & stack ) const + { + using namespace compositionalMultiphaseUtilities; + + integer const numRows = numComp+1+ IS_THERMAL; + + if constexpr ( IS_THERMAL) + { + if( ei == m_iwelemControl && !m_isProducer ) + { + // For top segment energy balance eqn replaced with T(n+1) - T = 0 + // No other energy balance derivatives + // Assumption is global index == 0 is top segment with fixed temp BC + + for( integer i=0; i < numComp+1+IS_THERMAL; i++ ) + { + stack.localJacobian[numRows-1][i] = 0.0; + } + // constant Temperature + for( integer i=0; i < numComp+1+IS_THERMAL; i++ ) + stack.localJacobian[i][numRows-1] = 0.0; + stack.localJacobian[numRows-1][numRows-1] = 1.0; + + stack.localResidual[numRows-1]=0.0; + } + } + + if( m_kernelFlags.isSet( isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags::TotalMassEquation ) ) + { + // apply equation/variable change transformation to the component mass balance equations + real64 work[numComp + 1 + IS_THERMAL]{}; + shiftRowsAheadByOneAndReplaceFirstRowWithColumnSum( numComp, numComp+1+ IS_THERMAL, stack.localJacobian, work ); + shiftElementsAheadByOneAndReplaceFirstElementWithSum( numComp, stack.localResidual ); + } + + // add contribution to residual and jacobian into: + // - the component mass balance equations (i = 0 to i = numComp-1) + // - the volume balance equations (i = numComp) + // note that numDof includes derivatives wrt temperature if this class is derived in ThermalKernels + + for( integer i = 0; i < numRows; ++i ) + { + m_localRhs[stack.eqnRowIndices[i]] += stack.localResidual[i]; + m_localMatrix.addToRow< serialAtomic >( stack.eqnRowIndices[i], + stack.dofColIndices, + stack.localJacobian[i], + numComp+1+ IS_THERMAL ); + } + + } + + /** + * @brief Performs the kernel launch + * @tparam POLICY the policy used in the RAJA kernels + * @tparam KERNEL_TYPE the kernel type + * @param[in] numElems the number of elements + * @param[inout] kernelComponent the kernel component providing access to setup/compute/complete functions and stack variables + */ + 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 ) + { + if( kernelComponent.elemGhostRank( ei ) >= 0 ) + { + return; + } + + typename KERNEL_TYPE::StackVariables stack; + + kernelComponent.setup( ei, stack ); + kernelComponent.computeAccumulation( ei, stack ); + kernelComponent.computeVolumeBalance( ei, stack ); + kernelComponent.complete( ei, stack ); + } ); + } + +protected: + + /// Number of fluid phases + integer const m_numPhases; + + /// Well type + integer const m_isProducer; + + /// Offset for my MPI rank + globalIndex const m_rankOffset; + + /// Index of the element where the control is enforced + localIndex const m_iwelemControl; + + /// View on the dof numbers + arrayView1d< globalIndex const > const m_dofNumber; + + /// View on the ghost ranks + arrayView1d< integer const > const m_elemGhostRank; + + /// View on the element volumes + arrayView1d< real64 const > const m_volume; + + /// Views on the porosity + arrayView2d< real64 const > const m_porosity_n; + arrayView2d< real64 const > const m_porosity; + arrayView2d< real64 const > const m_dPoro_dPres; + + /// Views on the derivatives of comp fractions wrt component density + arrayView3d< real64 const, compflow::USD_COMP_DC > const m_dCompFrac_dCompDens; + + /// Views on the phase volume fractions + arrayView2d< real64 const, compflow::USD_PHASE > const m_phaseVolFrac_n; + arrayView2d< real64 const, compflow::USD_PHASE > const m_phaseVolFrac; + arrayView3d< real64 const, compflow::USD_PHASE_DC > const m_dPhaseVolFrac; + + /// Views on the phase densities + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const m_phaseDens_n; + arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > const m_phaseDens; + arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > const m_dPhaseDens; + + /// Views on the phase component fraction + arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_COMP > const m_phaseCompFrac_n; + arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_COMP > const m_phaseCompFrac; + arrayView5d< real64 const, constitutive::multifluid::USD_PHASE_COMP_DC > const m_dPhaseCompFrac; + + // Views on component densities + arrayView2d< real64 const, compflow::USD_COMP > m_compDens; + arrayView2d< real64 const, compflow::USD_COMP > m_compDens_n; + + /// View on the local CRS matrix + CRSMatrixView< real64, globalIndex const > const m_localMatrix; + /// View on the local RHS + arrayView1d< real64 > const m_localRhs; + + BitFlags< isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags > const m_kernelFlags; +}; + + +/** + * @class ElementBasedAssemblyKernelFactory + */ +class ElementBasedAssemblyKernelFactory +{ +public: + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numComps the number of fluid components + * @param[in] numPhases the number of fluid phases + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey the string key to retrieve the degress of freedom numbers + * @param[in] subRegion the element subregion + * @param[in] fluid the fluid model + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY > + static void + createAndLaunch( localIndex const numComps, + localIndex const numPhases, + integer const isProducer, + globalIndex const rankOffset, + integer const useTotalMassEquation, + string const dofKey, + WellElementSubRegion const & subRegion, + constitutive::MultiFluidBase const & fluid, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + geos::internal::kernelLaunchSelectorCompThermSwitch( numComps, 0, [&]( auto NC, auto IS_THERMAL ) + { + localIndex constexpr NUM_COMP = NC(); + + integer constexpr istherm = IS_THERMAL(); + + BitFlags< isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags > kernelFlags; + if( useTotalMassEquation ) + kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags::TotalMassEquation ); + + ElementBasedAssemblyKernel< NUM_COMP, istherm > + kernel( numPhases, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ); + ElementBasedAssemblyKernel< NUM_COMP, istherm >::template + launch< POLICY, ElementBasedAssemblyKernel< NUM_COMP, istherm > >( subRegion.size(), kernel ); + } ); + } +}; +/** + * @class FaceBasedAssemblyKernel + * @tparam NUM_COMP number of fluid components + * @tparam NUM_DOF number of degrees of freedom + * @brief Define the interface for the assembly kernel in charge of flux terms + */ + + +template< integer NC, integer IS_THERMAL > +class FaceBasedAssemblyKernel +{ +public: + + using COFFSET = compositionalMultiphaseWellKernels::ColOffset; + using ROFFSET = compositionalMultiphaseWellKernels::RowOffset; + using TAG = compositionalMultiphaseWellKernels::ElemTag; + + using FLUID_PROP_COFFSET = constitutive::multifluid::DerivativeOffsetC< NC, IS_THERMAL >; + using WJ_COFFSET = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + + using CP_Deriv = constitutive::multifluid::DerivativeOffsetC< NC, IS_THERMAL >; + /// Compile time value for the number of components + static constexpr integer numComp = NC; + + /// Number of Dof's set in this kernal + static constexpr integer numDof = WJ_COFFSET::nDer; + + /// Compile time value for the number of equations except rate, momentum, energy + static constexpr integer numEqn = NC; + + static constexpr integer maxNumElems = 2; + static constexpr integer maxStencilSize = 2; + /** + * @brief Constructor for the kernel interface + * @param[in] rankOffset the offset of my MPI rank + * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] dofNumberAccessor + * @param[in] compFlowAccessors + * @param[in] multiFluidAccessors + * @param[in] capPressureAccessors + * @param[in] permeabilityAccessors + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + * @param[in] kernelFlags flags packed together + */ + FaceBasedAssemblyKernel( real64 const dt, + globalIndex const rankOffset, + string const wellDofKey, + WellControls const & wellControls, + WellElementSubRegion const & subRegion, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs, + BitFlags< isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags > kernelFlags ) + : + m_dt( dt ), + m_rankOffset( rankOffset ), + m_wellElemDofNumber ( subRegion.getReference< array1d< globalIndex > >( wellDofKey ) ), + m_nextWellElemIndex ( subRegion.getReference< array1d< localIndex > >( WellElementSubRegion::viewKeyStruct::nextWellElementIndexString()) ), + m_connRate ( subRegion.getField< fields::well::mixtureConnectionRate >() ), + m_wellElemCompFrac ( subRegion.getField< fields::well::globalCompFraction >() ), + m_dWellElemCompFrac_dCompDens ( subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >() ), + m_localMatrix( localMatrix ), + m_localRhs ( localRhs ), + m_useTotalMassEquation ( kernelFlags.isSet( isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags::TotalMassEquation ) ), + m_isProducer ( wellControls.isProducer() ), + m_injection ( wellControls.getInjectionStream() ) + {} + + struct StackVariables + { +public: + + /** + * @brief Constructor for the stack variables + * @param[in] size size of the stencil for this connection + * @param[in] numElems number of elements for this connection + */ + GEOS_HOST_DEVICE + StackVariables( localIndex const size ) + : stencilSize( size ), + numConnectedElems( 2 ), + dofColIndices( size * numDof ) + {} + + // Stencil information + localIndex const stencilSize; + /// Number of elements connected at a given connection + localIndex numConnectedElems; + + + // edge indexes + localIndex iwelemUp; + localIndex iwelemNext; + localIndex iwelemCurrent; + globalIndex offsetUp; + globalIndex offsetCurrent; + globalIndex offsetNext; + // Local degrees of freedom and local residual/jacobian + + /// Indices of the matrix rows/columns corresponding to the dofs in this face + stackArray1d< globalIndex, maxNumElems * numDof > dofColIndices; + + /// Storage for the face local residual vector (all mass bal equations) + stackArray1d< real64, maxNumElems * numEqn > localFlux; + /// Storage for the face local Jacobian matrix dC dP dT + stackArray2d< real64, maxNumElems * numEqn * maxStencilSize * CP_Deriv::nDer > localFluxJacobian; + /// Storage for the face local Jacobian matrix dQ only + stackArray2d< real64, maxNumElems * numEqn * maxStencilSize > localFluxJacobian_dQ; + }; + + /** + * @brief Performs the setup phase for the kernel. + * @param[in] iconn the connection index + * @param[in] stack the stack variables + */ + GEOS_HOST_DEVICE + inline + void setup( localIndex const iconn, + StackVariables & stack ) const + { + stack.numConnectedElems=2; + if( m_nextWellElemIndex[iconn] <0 ) + { + stack.numConnectedElems = 1; + } + stack.localFlux.resize( stack.numConnectedElems*numEqn ); + stack.localFluxJacobian.resize( stack.numConnectedElems * numEqn, stack.stencilSize * numDof ); + stack.localFluxJacobian_dQ.resize( stack.numConnectedElems * numEqn, 1 ); + + } + + /** + * @brief Performs the setup phase for the kernel. + * @param[in] iconn the connection index + * @param[in] stack the stack variables + */ + GEOS_HOST_DEVICE + inline + void complete( localIndex const iconn, + StackVariables & stack ) const + { + GEOS_UNUSED_VAR( iconn ); + using namespace compositionalMultiphaseUtilities; + if( stack.numConnectedElems ==1 ) + { + // Setup Jacobian global row indicies + // equations for COMPONENT + ENERGY balances + globalIndex oneSidedEqnRowIndices[numEqn]{}; + for( integer ic = 0; ic < NC; ++ic ) + { + oneSidedEqnRowIndices[ic] = stack.offsetUp + WJ_ROFFSET::MASSBAL + ic - m_rankOffset; + } + + // Setup Jacobian global col indicies ( Mapping from local jac order to well jac order) + globalIndex oneSidedDofColIndices_dPresCompTempUp[CP_Deriv::nDer]{}; + globalIndex oneSidedDofColIndices_dRate = stack.offsetCurrent + WJ_COFFSET::dQ; + // Note localFluxJacobian cols are stored using CP_Deriv order (dP dC or dP dT dC) + int ioff=0; + oneSidedDofColIndices_dPresCompTempUp[ioff++] = stack.offsetUp + WJ_COFFSET::dP; + + if constexpr ( IS_THERMAL ) + { + oneSidedDofColIndices_dPresCompTempUp[ioff++] = stack.offsetUp + WJ_COFFSET::dT; + } + for( integer jdof = 0; jdof < NC; ++jdof ) + { + oneSidedDofColIndices_dPresCompTempUp[ioff++] = stack.offsetUp + WJ_COFFSET::dC+ jdof; + } + if( m_useTotalMassEquation > 0 ) + { + // Apply equation/variable change transformation(s) + real64 work[CP_Deriv::nDer]{}; + shiftRowsAheadByOneAndReplaceFirstRowWithColumnSum( numEqn, 1, stack.localFluxJacobian_dQ, work ); + shiftRowsAheadByOneAndReplaceFirstRowWithColumnSum( numEqn, CP_Deriv::nDer, stack.localFluxJacobian, work ); + shiftElementsAheadByOneAndReplaceFirstElementWithSum( numEqn, stack.localFlux ); + } + for( integer i = 0; i < numEqn; ++i ) + { + if( oneSidedEqnRowIndices[i] >= 0 && oneSidedEqnRowIndices[i] < m_localMatrix.numRows() ) + { + m_localMatrix.addToRow< parallelDeviceAtomic >( oneSidedEqnRowIndices[i], + &oneSidedDofColIndices_dRate, + stack.localFluxJacobian_dQ[i], + 1 ); + m_localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic >( oneSidedEqnRowIndices[i], + oneSidedDofColIndices_dPresCompTempUp, + stack.localFluxJacobian[i], + CP_Deriv::nDer ); + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[oneSidedEqnRowIndices[i]], stack.localFlux[i] ); + } + } + } + else + { + // Setup Jacobian global row indicies + // equations for COMPONENT + ENERGY balances + globalIndex eqnRowIndices[2*numEqn]{}; + + for( integer ic = 0; ic < NC; ++ic ) + { + // mass balance equations for all components + eqnRowIndices[TAG::NEXT *numEqn+ic] = stack.offsetNext + WJ_ROFFSET::MASSBAL + ic - m_rankOffset; + eqnRowIndices[TAG::CURRENT *numEqn+ic] = stack.offsetCurrent + WJ_ROFFSET::MASSBAL + ic - m_rankOffset; + } + + // Setup Jacobian global col indicies ( Mapping from local jac order to well jac order) + globalIndex dofColIndices_dPresCompUp[CP_Deriv::nDer]{}; + globalIndex dofColIndices_dRate = stack.offsetCurrent + WJ_COFFSET::dQ; + + int ioff=0; + // Indice storage order reflects local jac col storage order CP::Deriv order P T DENS + dofColIndices_dPresCompUp[ioff++] = stack.offsetUp + WJ_COFFSET::dP; + + if constexpr ( IS_THERMAL ) + { + dofColIndices_dPresCompUp[ioff++] = stack.offsetUp + WJ_COFFSET::dT; + } + for( integer jdof = 0; jdof < NC; ++jdof ) + { + dofColIndices_dPresCompUp[ioff++] = stack.offsetUp + WJ_COFFSET::dC+ jdof; + } + + + if( m_useTotalMassEquation > 0 ) + { + // Apply equation/variable change transformation(s) + real64 work[CP_Deriv::nDer]{}; + shiftBlockRowsAheadByOneAndReplaceFirstRowWithColumnSum( numEqn, numEqn, 1, 2, stack.localFluxJacobian_dQ, work ); + shiftBlockRowsAheadByOneAndReplaceFirstRowWithColumnSum( numEqn, numEqn, CP_Deriv::nDer, 2, stack.localFluxJacobian, work ); + shiftBlockElementsAheadByOneAndReplaceFirstElementWithSum( numEqn, numEqn, 2, stack.localFlux ); + } + // Note this updates diag and offdiag + for( integer i = 0; i < 2*NC; ++i ) + { + if( eqnRowIndices[i] >= 0 && eqnRowIndices[i] < m_localMatrix.numRows() ) + { + m_localMatrix.addToRow< parallelDeviceAtomic >( eqnRowIndices[i], + &dofColIndices_dRate, + stack.localFluxJacobian_dQ[i], + 1 ); + m_localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic >( eqnRowIndices[i], + dofColIndices_dPresCompUp, + stack.localFluxJacobian[i], + CP_Deriv::nDer ); + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[eqnRowIndices[i]], stack.localFlux[i] ); + + } + } + } + } + + GEOS_HOST_DEVICE + inline + void + computeExit( real64 const & dt, + real64 const ( &compFlux )[NC ], + StackVariables & stack, + real64 ( & dCompFlux)[NC][numDof] ) const + { + for( integer ic = 0; ic < NC; ++ic ) + { + stack.localFlux[ic] = -dt * compFlux[ic]; + // derivative with respect to rate + stack.localFluxJacobian_dQ[ic][0] = -dt * dCompFlux[ic][WJ_COFFSET::dQ]; + // derivative with respect to upstream pressure + stack.localFluxJacobian[ic][CP_Deriv::dP] = -dt * dCompFlux[ic][WJ_COFFSET::dP]; + // derivatives with respect to upstream component densities + for( integer jdof = 0; jdof < NC; ++jdof ) + { + stack.localFluxJacobian[ic][CP_Deriv::dC+jdof] = -dt * dCompFlux[ic][WJ_COFFSET::dC+jdof]; + } + if constexpr ( IS_THERMAL ) + { + stack.localFluxJacobian[ic][CP_Deriv::dT] = -dt * dCompFlux[ic][WJ_COFFSET::dT]; + } + } + } + + GEOS_HOST_DEVICE + inline + void + compute( real64 const & dt, + real64 const ( &compFlux )[NC ], + StackVariables & stack, + real64 ( & dCompFlux)[(NC )][numDof] ) const + { + // flux terms + for( integer ic = 0; ic < NC; ++ic ) + { + stack.localFlux[TAG::NEXT * NC +ic] = dt * compFlux[ic]; + stack.localFlux[TAG::CURRENT * NC +ic] = -dt * compFlux[ic]; + // derivative with respect to rate + stack.localFluxJacobian_dQ[TAG::NEXT * NC+ ic][0] = dt * dCompFlux[ic][WJ_COFFSET::dQ]; + stack.localFluxJacobian_dQ[TAG::CURRENT * NC + +ic][0] = -dt * dCompFlux[ic][WJ_COFFSET::dQ]; + + // derivative with respect to upstream pressure + stack.localFluxJacobian[TAG::NEXT * NC +ic][CP_Deriv::dP] = dt * dCompFlux[ic][WJ_COFFSET::dP]; + stack.localFluxJacobian[TAG::CURRENT * NC+ ic][CP_Deriv::dP] = -dt * dCompFlux[ic][WJ_COFFSET::dP]; + + if constexpr ( IS_THERMAL ) + { + stack.localFluxJacobian[TAG::NEXT * NC +ic][CP_Deriv::dT] = dt * dCompFlux[ic][WJ_COFFSET::dT]; + stack.localFluxJacobian[TAG::CURRENT * NC +ic][CP_Deriv::dT] = -dt * dCompFlux[ic][WJ_COFFSET::dT]; + } + + // derivatives with respect to upstream component densities + for( integer jdof = 0; jdof < NC; ++jdof ) + { + stack.localFluxJacobian[TAG::NEXT * NC +ic][CP_Deriv::dC+jdof] = dt * dCompFlux[ic][WJ_COFFSET::dC+jdof]; + stack.localFluxJacobian[TAG::CURRENT * NC +ic][CP_Deriv::dC+jdof] = -dt * dCompFlux[ic][WJ_COFFSET::dC+jdof]; + } + } + } + + /** + * @brief Compute the local flux contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the computation of the phase fluxes + * @param[in] ie the element index + * @param[inout] stack the stack variables + * @param[in] compFluxKernelOp the function used to customize the computation of the component fluxes + */ + template< typename FUNC = NoOpFunc > + GEOS_HOST_DEVICE + inline + void computeFlux( localIndex const iwelem, + StackVariables & stack, + FUNC && compFluxKernelOp = NoOpFunc{} ) const + { + + using namespace compositionalMultiphaseUtilities; + + // create local work arrays + real64 compFracUp[NC]{}; + real64 compFlux[NC]{}; + real64 dComp[NC][NC]; + real64 dCompFlux[NC][numDof]{}; + for( integer ic = 0; ic < NC; ++ic ) + { + for( integer jc = 0; jc < NC; ++jc ) + { + dComp[ic][jc]=0.0; + } + } + // Step 1) decide the upwind well element + + /* currentConnRate < 0 flow from iwelem to iwelemNext + * currentConnRate > 0 flow from iwelemNext to iwelem + * With this convention, currentConnRate < 0 at the last connection for a producer + * currentConnRate > 0 at the last connection for a injector + */ + + localIndex const iwelemNext = m_nextWellElemIndex[iwelem]; + real64 const currentConnRate = m_connRate[iwelem]; + localIndex iwelemUp = -1; + + if( iwelemNext < 0 && !m_isProducer ) // exit connection, injector + { + // we still need to define iwelemUp for Jacobian assembly + iwelemUp = iwelem; + + // just copy the injection stream into compFrac + for( integer ic = 0; ic < NC; ++ic ) + { + compFracUp[ic] = m_injection[ic]; + for( integer jc = 0; jc < NC; ++jc ) + { + dComp[ic][jc] = m_dWellElemCompFrac_dCompDens[iwelemUp][ic][jc]; + } + for( integer jc = 0; jc < NC; ++jc ) + { + dCompFlux[ic][WJ_COFFSET::dC+jc] = 0.0; + } + } + } + else + { + // first set iwelemUp to the upstream cell + if( ( iwelemNext < 0 && m_isProducer ) // exit connection, producer + || currentConnRate < 0 ) // not an exit connection, iwelem is upstream + { + iwelemUp = iwelem; + } + else // not an exit connection, iwelemNext is upstream + { + iwelemUp = iwelemNext; + } + + // copy the vars of iwelemUp into compFrac + for( integer ic = 0; ic < NC; ++ic ) + { + compFracUp[ic] = m_wellElemCompFrac[iwelemUp][ic]; + for( integer jc = 0; jc < NC; ++jc ) + { + dCompFlux[ic][WJ_COFFSET::dC+jc] = m_dWellElemCompFrac_dCompDens[iwelemUp][ic][jc]; + dComp[ic][jc] = m_dWellElemCompFrac_dCompDens[iwelemUp][ic][jc]; + } + } + } + + // Step 2) compute upstream transport coefficient + + for( integer ic = 0; ic < NC; ++ic ) + { + compFlux[ic] = compFracUp[ic] * currentConnRate; + dCompFlux[ic][WJ_COFFSET::dQ] = compFracUp[ic]; + // none of these quantities depend on pressure + dCompFlux[ic][WJ_COFFSET::dP] = 0.0; + if constexpr ( IS_THERMAL ) + { + dCompFlux[ic][WJ_COFFSET::dT] = 0.0; + } + for( integer jc = 0; jc < NC; ++jc ) + { + dCompFlux[ic][WJ_COFFSET::dC+jc] = dCompFlux[ic][WJ_COFFSET::dC+jc] * currentConnRate; + } + } + + stack.offsetUp = m_wellElemDofNumber[iwelemUp]; + stack.iwelemUp = iwelemUp; + stack.offsetCurrent = m_wellElemDofNumber[iwelem]; + stack.iwelemCurrent= iwelem; + + + if( iwelemNext < 0 ) // exit connection + { + // for this case, we only need NC mass conservation equations + computeExit ( m_dt, + compFlux, + stack, + dCompFlux ); + + } + else // not an exit connection + { + compute( m_dt, + compFlux, + stack, + dCompFlux + ); + stack.offsetNext = m_wellElemDofNumber[iwelemNext]; + } + stack.iwelemNext = iwelemNext; + compFluxKernelOp( iwelemNext, iwelemUp, currentConnRate, dComp ); + } + + + /** + * @brief Performs the kernel launch + * @tparam POLICY the policy used in the RAJA kernels + * @tparam KERNEL_TYPE the kernel type + * @param[in] numElements the number of elements + * @param[inout] kernelComponent the kernel component providing access to setup/compute/complete functions and stack variables + */ + template< typename POLICY, typename KERNEL_TYPE > + static void + launch( localIndex const numElements, + KERNEL_TYPE const & kernelComponent ) + { + GEOS_MARK_FUNCTION; + forAll< POLICY >( numElements, [=] GEOS_HOST_DEVICE ( localIndex const ie ) + { + typename KERNEL_TYPE::StackVariables stack( 1 ); + + kernelComponent.setup( ie, stack ); + kernelComponent.computeFlux( ie, stack ); + kernelComponent.complete( ie, stack ); + } ); + } + +protected: + /// Time step size + real64 const m_dt; + /// Rank offset for calculating row/col Jacobian indices + integer const m_rankOffset; + + /// Reference to the degree-of-freedom numbers + arrayView1d< globalIndex const > const m_wellElemDofNumber; + /// Next element index, needed since iterating over element nodes, not edges + arrayView1d< localIndex const > const m_nextWellElemIndex; + + /// Connection rate + arrayView1d< real64 const > const m_connRate; + + + /// Element component fraction + arrayView2d< real64 const, compflow::USD_COMP > const m_wellElemCompFrac; + /// Element component fraction derivatives + arrayView3d< real64 const, compflow::USD_COMP_DC > const m_dWellElemCompFrac_dCompDens; + + /// View on the local CRS matrix + CRSMatrixView< real64, globalIndex const > const m_localMatrix; + /// View on the local RHS + arrayView1d< real64 > const m_localRhs; + + /// Kernel option flag + integer const m_useTotalMassEquation; + + /// Well type + bool const m_isProducer; + + /// Injection stream composition + arrayView1d< real64 const > const m_injection; + + +}; + +/** + * @class FaceBasedAssemblyKernelFactory + */ +class FaceBasedAssemblyKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numComps the number of fluid components + * @param[in] dt time step size + * @param[in] rankOffset the offset of my MPI rank + * @param[in] useTotalMassEquation flag specifying whether to replace one component bal eqn with total mass eqn + * @param[in] dofKey string to get the element degrees of freedom numbers + * @param[in] wellControls object holding well control/constraint information + * @param[in] subregion well subregion + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY > + static void + createAndLaunch( integer const numComps, + real64 const dt, + globalIndex const rankOffset, + integer const useTotalMassEquation, + string const dofKey, + WellControls const & wellControls, + WellElementSubRegion const & subRegion, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC ) + { + integer constexpr NUM_COMP = NC(); + + + BitFlags< isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags > kernelFlags; + if( useTotalMassEquation ) + kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags::TotalMassEquation ); + + using kernelType = FaceBasedAssemblyKernel< NUM_COMP, 0 >; + + + kernelType kernel( dt, rankOffset, dofKey, wellControls, subRegion, localMatrix, localRhs, kernelFlags ); + kernelType::template launch< POLICY >( subRegion.size(), kernel ); + } ); + } +}; } // end namespace compositionalMultiphaseWellKernels } // end namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/PerforationFluxKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/PerforationFluxKernels.hpp new file mode 100644 index 00000000000..6c042f18d34 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/PerforationFluxKernels.hpp @@ -0,0 +1,879 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file PerforationFluxKernels.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_PERFORATIONFLUXLKERNELS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_PERFORATIONFLUXLKERNELS_HPP + +#include "codingUtilities/Utilities.hpp" +#include "common/DataTypes.hpp" +#include "common/GEOS_RAJA_Interface.hpp" +#include "constitutive/fluid/multifluid/MultiFluidBase.hpp" +#include "constitutive/fluid/multifluid/MultiFluidFields.hpp" +#include "constitutive/relativePermeability/RelativePermeabilityBase.hpp" +#include "constitutive/relativePermeability/RelativePermeabilityFields.hpp" +#include "mesh/ElementRegionManager.hpp" +#include "mesh/ObjectManagerBase.hpp" +#include "physicsSolvers/KernelLaunchSelectors.hpp" +#include "physicsSolvers/fluidFlow/CompositionalMultiphaseBaseFields.hpp" +#include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" +#include "physicsSolvers/fluidFlow/StencilAccessors.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp" +#include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp" +#include "physicsSolvers/fluidFlow/wells/WellTags.hpp" +#include "physicsSolvers/fluidFlow/wells/WellFields.hpp" + + +namespace geos +{ + +struct NoOpStuct +{ + NoOpStuct(){} +}; + +namespace isothermalPerforationFluxKernels +{ + + + +/******************************** PerforationFluxKernel ********************************/ + +template< integer NC, integer NP, integer IS_THERMAL > +class PerforationFluxKernel +{ +public: + /// Compile time value for the number of components + static constexpr integer numComp = NC; + + /// Compile time value for the number of phases + static constexpr integer numPhase = NP; + + /// Compile time value for thermal option + static constexpr integer isThermal = IS_THERMAL; + + using TAG = wellTags::SubRegionTag; + + using CompFlowAccessors = + StencilAccessors< fields::flow::pressure, + fields::flow::phaseVolumeFraction, + fields::flow::dPhaseVolumeFraction, + fields::flow::dGlobalCompFraction_dGlobalCompDensity >; + + using MultiFluidAccessors = + StencilMaterialAccessors< constitutive::MultiFluidBase, + fields::multifluid::phaseDensity, + fields::multifluid::dPhaseDensity, + fields::multifluid::phaseViscosity, + fields::multifluid::dPhaseViscosity, + fields::multifluid::phaseCompFraction, + fields::multifluid::dPhaseCompFraction >; + + using RelPermAccessors = + StencilMaterialAccessors< constitutive::RelativePermeabilityBase, + fields::relperm::phaseRelPerm, + fields::relperm::dPhaseRelPerm_dPhaseVolFraction >; + + + /** + * @brief The type for element-based non-constitutive data parameters. + * Consists entirely of ArrayView's. + * + * Can be converted from ElementRegionManager::ElementViewAccessor + * by calling .toView() or .toViewConst() on an accessor instance + */ + template< typename VIEWTYPE > + using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; + + PerforationFluxKernel ( PerforationData * const perforationData, + ElementSubRegionBase const & subRegion, + CompFlowAccessors const & compFlowAccessors, + MultiFluidAccessors const & multiFluidAccessors, + RelPermAccessors const & relPermAccessors, + bool const disableReservoirToWellFlow ): + m_resPres( compFlowAccessors.get( fields::flow::pressure {} )), + m_resPhaseVolFrac( compFlowAccessors.get( fields::flow::phaseVolumeFraction {} )), + m_dResPhaseVolFrac( compFlowAccessors.get( fields::flow::dPhaseVolumeFraction {} )), + m_dResCompFrac_dCompDens( compFlowAccessors.get( fields::flow::dGlobalCompFraction_dGlobalCompDensity {} )), + m_resPhaseDens( multiFluidAccessors.get( fields::multifluid::phaseDensity {} )), + m_dResPhaseDens( multiFluidAccessors.get( fields::multifluid::dPhaseDensity {} )), + m_resPhaseVisc( multiFluidAccessors.get( fields::multifluid::phaseViscosity {} )), + m_dResPhaseVisc( multiFluidAccessors.get( fields::multifluid::dPhaseViscosity {} )), + m_resPhaseCompFrac( multiFluidAccessors.get( fields::multifluid::phaseCompFraction {} )), + m_dResPhaseCompFrac( multiFluidAccessors.get( fields::multifluid::dPhaseCompFraction {} )), + m_resPhaseRelPerm( relPermAccessors.get( fields::relperm::phaseRelPerm {} )), + m_dResPhaseRelPerm_dPhaseVolFrac( relPermAccessors.get( fields::relperm::dPhaseRelPerm_dPhaseVolFraction {} )), + m_wellElemGravCoef( subRegion.getField< fields::well::gravityCoefficient >()), + m_wellElemPres( subRegion.getField< fields::well::pressure >()), + m_wellElemCompDens( subRegion.getField< fields::well::globalCompDensity >()), + m_wellElemTotalMassDens( subRegion.getField< fields::well::totalMassDensity >()), + m_dWellElemTotalMassDens( subRegion.getField< fields::well::dTotalMassDensity >()), + m_wellElemCompFrac( subRegion.getField< fields::well::globalCompFraction >()), + m_dWellElemCompFrac_dCompDens( subRegion.getField< fields::well::dGlobalCompFraction_dGlobalCompDensity >()), + m_perfGravCoef( perforationData->getField< fields::well::gravityCoefficient >()), + m_perfWellElemIndex( perforationData->getField< fields::perforation::wellElementIndex >()), + m_perfTrans( perforationData->getField< fields::perforation::wellTransmissibility >()), + m_resElementRegion( perforationData->getField< fields::perforation::reservoirElementRegion >()), + m_resElementSubRegion( perforationData->getField< fields::perforation::reservoirElementSubRegion >()), + m_resElementIndex( perforationData->getField< fields::perforation::reservoirElementIndex >()), + m_compPerfRate( perforationData->getField< fields::well::compPerforationRate >()), + m_dCompPerfRate( perforationData->getField< fields::well::dCompPerforationRate >()), + m_disableReservoirToWellFlow( disableReservoirToWellFlow ) + {} + + struct StackVariables + { +public: + /** + * @brief Constructor for the stack variables + */ + + GEOS_HOST_DEVICE + StackVariables() {} + + }; + + template< typename FUNC = NoOpFunc > + GEOS_HOST_DEVICE + inline + void + computeFlux( localIndex const iperf, FUNC && fluxKernelOp= NoOpFunc {} ) const + { + // get the index of the reservoir elem + localIndex const er = m_resElementRegion[iperf]; + localIndex const esr = m_resElementSubRegion[iperf]; + localIndex const ei = m_resElementIndex[iperf]; + + // get the index of the well elem + localIndex const iwelem = m_perfWellElemIndex[iperf]; + + using Deriv = constitutive::multifluid::DerivativeOffset; + using CP_Deriv = constitutive::multifluid::DerivativeOffsetC< NC, IS_THERMAL >; + + // local working variables and arrays + real64 pres[2]{}; + real64 multiplier[2]{}; + + // local working variables - compact + // All derivative quantiites generated are stored in arrays using CP_Deriv offsets + // The input well/reservoir quantites use the Deriv offsets + // The arrays using the deriv offsets have extra column for dT in isothermal cases + + real64 dPres[2][CP_Deriv::nDer]{}; + real64 dFlux[2][CP_Deriv::nDer]{}; + real64 dMob[CP_Deriv::nDer]{}; + real64 dPotDiff[2][CP_Deriv::nDer]{}; + real64 dCompFrac[CP_Deriv::nDer]{}; + + // Step 1: reset the perforation rates + for( integer ic = 0; ic < NC; ++ic ) + { + m_compPerfRate[iperf][ic] = 0.0; + for( integer ke = 0; ke < 2; ++ke ) + { + for( integer jc = 0; jc < CP_Deriv::nDer; ++jc ) + { + m_dCompPerfRate[iperf][ke][ic][jc] = 0.0; + } + } + } + + // Step 2: copy the variables from the reservoir and well element + + // a) get reservoir variables + + pres[TAG::RES] = m_resPres[er][esr][ei]; + dPres[TAG::RES][CP_Deriv::dP] = 1.0; + multiplier[TAG::RES] = 1.0; + + // Here in the absence of a buoyancy term we assume that the reservoir cell is perforated at its center + // TODO: add a buoyancy term for the reservoir side here + + + // b) get well variables + + pres[TAG::WELL] = m_wellElemPres[iwelem]; + dPres[TAG::WELL][CP_Deriv::dP] = 1.0; + multiplier[TAG::WELL] = -1.0; + + real64 const gravD = ( m_perfGravCoef[iperf] - m_wellElemGravCoef[iwelem] ); + + pres[TAG::WELL] += m_wellElemTotalMassDens[iwelem] * gravD; + // Note LHS uses CP_Deriv while RHS uses Deriv !!! + dPres[TAG::WELL][CP_Deriv::dP] += m_dWellElemTotalMassDens[iwelem][Deriv::dP] * gravD; + if constexpr ( IS_THERMAL ) + { + dPres[TAG::WELL][CP_Deriv::dT] += m_dWellElemTotalMassDens[iwelem][Deriv::dT] * gravD; + } + for( integer ic = 0; ic < NC; ++ic ) + { + dPres[TAG::WELL][CP_Deriv::dC+ic] += m_dWellElemTotalMassDens[iwelem][Deriv::dC+ic] * gravD; + } + + // Step 3: compute potential difference + + real64 potDiff = 0.0; + for( integer i = 0; i < 2; ++i ) + { + potDiff += multiplier[i] * m_perfTrans[iperf] * pres[i]; + // LHS & RHS both use CP_Deriv + for( integer ic = 0; ic < CP_Deriv::nDer; ++ic ) + { + dPotDiff[i][ic] += multiplier[i] * m_perfTrans[iperf] * dPres[i][ic]; + } + } + // Step 4: upwinding based on the flow direction + + real64 flux = 0.0; + if( potDiff >= 0 ) // ** reservoir cell is upstream ** + { + + // loop over phases, compute and upwind phase flux + // and sum contributions to each component's perforation rate + for( integer ip = 0; ip < NP; ++ip ) + { + // skip the rest of the calculation if the phase is absent + // or if crossflow is disabled for injectors + bool const phaseExists = (m_resPhaseVolFrac[er][esr][ei][ip] > 0); + if( !phaseExists || m_disableReservoirToWellFlow ) + { + continue; + } + + // here, we have to recompute the reservoir phase mobility (not including density) + + // density + real64 const resDens = m_resPhaseDens[er][esr][ei][0][ip]; + real64 dDens[CP_Deriv::nDer]{}; + + dDens[CP_Deriv::dP] = m_dResPhaseDens[er][esr][ei][0][ip][Deriv::dP]; + if constexpr ( IS_THERMAL ) + { + dDens[CP_Deriv::dT] = m_dResPhaseDens[er][esr][ei][0][ip][Deriv::dT]; + } + applyChainRule( NC, m_dResCompFrac_dCompDens[er][esr][ei], + m_dResPhaseDens[er][esr][ei][0][ip], + &dDens[CP_Deriv::dC], + Deriv::dC ); + // viscosity + real64 const resVisc = m_resPhaseVisc[er][esr][ei][0][ip]; + real64 dVisc[CP_Deriv::nDer]{}; + dVisc[CP_Deriv::dP] = m_dResPhaseVisc[er][esr][ei][0][ip][Deriv::dP]; + if constexpr ( IS_THERMAL ) + { + dVisc[CP_Deriv::dT] = m_dResPhaseVisc[er][esr][ei][0][ip][Deriv::dT]; + } + + applyChainRule( NC, m_dResCompFrac_dCompDens[er][esr][ei], + m_dResPhaseVisc[er][esr][ei][0][ip], + &dVisc[CP_Deriv::dC], + Deriv::dC ); + + // relative permeability + real64 const resRelPerm = m_resPhaseRelPerm[er][esr][ei][0][ip]; + real64 dRelPerm[CP_Deriv::nDer]{}; + for( integer jc = 0; jc < CP_Deriv::nDer; ++jc ) + { + dRelPerm[jc]=0; + } + for( integer jp = 0; jp < NP; ++jp ) + { + real64 const dResRelPerm_dS = m_dResPhaseRelPerm_dPhaseVolFrac[er][esr][ei][0][ip][jp]; + dRelPerm[CP_Deriv::dP] += dResRelPerm_dS * m_dResPhaseVolFrac[er][esr][ei][jp][Deriv::dP]; + if constexpr ( IS_THERMAL ) + { + dRelPerm[CP_Deriv::dT] += dResRelPerm_dS * m_dResPhaseVolFrac[er][esr][ei][jp][Deriv::dT]; + } + for( integer jc = 0; jc < NC; ++jc ) + { + dRelPerm[CP_Deriv::dC+jc] += dResRelPerm_dS * m_dResPhaseVolFrac[er][esr][ei][jp][Deriv::dC+jc]; + } + } + + // compute the reservoir phase mobility, including phase density + real64 const resPhaseMob = resDens * resRelPerm / resVisc; + + // Handles all dependencies + for( integer jc = 0; jc < CP_Deriv::nDer; ++jc ) + { + dMob[jc] = dRelPerm[jc] * resDens / resVisc + + resPhaseMob * (dDens[jc] / resDens - dVisc[jc] / resVisc); + } + // compute the phase flux and derivatives using upstream cell mobility + flux = resPhaseMob * potDiff; + // Handles all dependencies + for( integer jc = 0; jc < CP_Deriv::nDer; ++jc ) + { + dFlux[TAG::RES][jc] = dMob[jc] * potDiff + resPhaseMob * dPotDiff[TAG::RES][jc]; + dFlux[TAG::WELL][jc] = resPhaseMob * dPotDiff[TAG::WELL][jc]; + } + + // increment component fluxes + for( integer ic = 0; ic < NC; ++ic ) + { + // Note this needs to be uncommented out + m_compPerfRate[iperf][ic] += flux * m_resPhaseCompFrac[er][esr][ei][0][ip][ic]; + dCompFrac[CP_Deriv::dP] = m_dResPhaseCompFrac[er][esr][ei][0][ip][ic][Deriv::dP]; + if constexpr (IS_THERMAL) + { + dCompFrac[CP_Deriv::dT] = m_dResPhaseCompFrac[er][esr][ei][0][ip][ic][Deriv::dT]; + } + + applyChainRule( NC, + m_dResCompFrac_dCompDens[er][esr][ei], + m_dResPhaseCompFrac[er][esr][ei][0][ip][ic], + &dCompFrac[CP_Deriv::dC], + Deriv::dC ); + + for( integer jc = 0; jc < CP_Deriv::nDer; ++jc ) + { + m_dCompPerfRate[iperf][TAG::RES][ic][jc] += dFlux[TAG::RES][jc] * m_resPhaseCompFrac[er][esr][ei][0][ip][ic]; + m_dCompPerfRate[iperf][TAG::RES][ic][jc] += flux * dCompFrac[jc]; + m_dCompPerfRate[iperf][TAG::WELL][ic][jc] += dFlux[TAG::WELL][jc] * m_resPhaseCompFrac[er][esr][ei][0][ip][ic]; + } + } + if constexpr ( IS_THERMAL ) + { + fluxKernelOp( iwelem, er, esr, ei, ip, potDiff, flux, dFlux ); + } + + } // end resevoir is upstream phase loop + + } + else // ** well is upstream ** + { + + real64 resTotalMob = 0.0; + + // we re-compute here the total mass (when useMass == 1) or molar (when useMass == 0) density + real64 wellElemTotalDens = 0; + for( integer ic = 0; ic < NC; ++ic ) + { + wellElemTotalDens += m_wellElemCompDens[iwelem][ic]; + } + + // first, compute the reservoir total mobility (excluding phase density) + for( integer ip = 0; ip < NP; ++ip ) + { + + // skip the rest of the calculation if the phase is absent + bool const phaseExists = (m_resPhaseVolFrac[er][esr][ei][ip] > 0); + if( !phaseExists ) + { + continue; + } + + // viscosity + real64 const resVisc = m_resPhaseVisc[er][esr][ei][0][ip]; + real64 dVisc[CP_Deriv::nDer]{}; + dVisc[CP_Deriv::dP] = m_dResPhaseVisc[er][esr][ei][0][ip][Deriv::dP]; + if constexpr ( IS_THERMAL ) + { + dVisc[CP_Deriv::dT] = m_dResPhaseVisc[er][esr][ei][0][ip][Deriv::dT]; + } + + applyChainRule( NC, m_dResCompFrac_dCompDens[er][esr][ei], + m_dResPhaseVisc[er][esr][ei][0][ip], + &dVisc[CP_Deriv::dC], + Deriv::dC ); + + + // relative permeability + real64 const resRelPerm = m_resPhaseRelPerm[er][esr][ei][0][ip]; + real64 dRelPerm[CP_Deriv::nDer]{}; + for( integer jc = 0; jc < CP_Deriv::nDer; ++jc ) + { + dRelPerm[jc]=0; + } + for( integer jp = 0; jp < NP; ++jp ) + { + real64 const dResRelPerm_dS = m_dResPhaseRelPerm_dPhaseVolFrac[er][esr][ei][0][ip][jp]; + dRelPerm[CP_Deriv::dP] += dResRelPerm_dS * m_dResPhaseVolFrac[er][esr][ei][jp][Deriv::dP]; + if constexpr ( IS_THERMAL ) + { + dRelPerm[CP_Deriv::dT] += dResRelPerm_dS * m_dResPhaseVolFrac[er][esr][ei][jp][Deriv::dT]; + } + for( integer jc = 0; jc < NC; ++jc ) + { + dRelPerm[CP_Deriv::dC+jc] += dResRelPerm_dS * m_dResPhaseVolFrac[er][esr][ei][jp][Deriv::dC+jc]; + } + } + // increment total mobility + resTotalMob += resRelPerm / resVisc; + // Handles all dependencies + for( integer jc = 0; jc < CP_Deriv::nDer; ++jc ) + { + dMob[jc] += (dRelPerm[jc] *resVisc - resRelPerm * dVisc[jc] ) + / ( resVisc * resVisc); + } + } // end well is upstream phase loop + + // compute a potdiff multiplier = wellElemTotalDens * resTotalMob + // wellElemTotalDens is a mass density if useMass == 1 and a molar density otherwise + real64 const mult = wellElemTotalDens * resTotalMob; + + real64 dMult[2][CP_Deriv::nDer]{}; + dMult[TAG::WELL][CP_Deriv::dP] = 0.0; + if constexpr ( IS_THERMAL ) + { + dMult[TAG::WELL][CP_Deriv::dT] = 0.0; + } + for( integer ic = 0; ic < NC; ++ic ) + { + dMult[TAG::WELL][CP_Deriv::dC+ic] = resTotalMob; + } + for( integer jc = 0; jc < CP_Deriv::nDer; ++jc ) + { + dMult[TAG::RES][jc] = wellElemTotalDens * dMob[jc]; + } + + + // compute the volumetric flux and derivatives using upstream cell mobility + flux = mult * potDiff; + + for( integer ic = 0; ic < CP_Deriv::nDer; ++ic ) + { + dFlux[TAG::RES][ic] = dMult[TAG::RES][ic] * potDiff + mult * dPotDiff[TAG::RES][ic]; + dFlux[TAG::WELL][ic] = dMult[TAG::WELL][ic] * potDiff + mult * dPotDiff[TAG::WELL][ic]; + } + // compute component fluxes + for( integer ic = 0; ic < NC; ++ic ) + { + m_compPerfRate[iperf][ic] += m_wellElemCompFrac[iwelem][ic] * flux; + for( integer jc = 0; jc < CP_Deriv::nDer; ++jc ) + { + m_dCompPerfRate[iperf][TAG::RES][ic][jc] = m_wellElemCompFrac[iwelem][ic] * dFlux[TAG::RES][jc]; + } + } + for( integer ic = 0; ic < NC; ++ic ) + { + m_dCompPerfRate[iperf][TAG::WELL][ic][CP_Deriv::dP] = m_wellElemCompFrac[iwelem][ic] * dFlux[TAG::WELL][CP_Deriv::dP]; + if constexpr ( IS_THERMAL ) + { + m_dCompPerfRate[iperf][TAG::WELL][ic][CP_Deriv::dT] = m_wellElemCompFrac[iwelem][ic] * dFlux[TAG::WELL][CP_Deriv::dT]; + } + for( integer jc = 0; jc < NC; ++jc ) + { + m_dCompPerfRate[iperf][TAG::WELL][ic][CP_Deriv::dC+jc] += m_wellElemCompFrac[iwelem][ic] * dFlux[TAG::WELL][CP_Deriv::dC+jc]; + m_dCompPerfRate[iperf][TAG::WELL][ic][CP_Deriv::dC+jc] += m_dWellElemCompFrac_dCompDens[iwelem][ic][jc] * flux; + } + } + if constexpr ( IS_THERMAL ) + { + fluxKernelOp( iwelem, er, esr, ei, -1, potDiff, flux, dFlux ); + } + } // end upstream + } + /** + * @brief Performs the kernel launch + * @tparam POLICY the policy used in the RAJA kernels + * @tparam KERNEL_TYPE the kernel type + * @param[in] numElements the number of elements + * @param[inout] kernelComponent the kernel component providing access to setup/compute/complete functions and stack variables + */ + template< typename POLICY, typename KERNEL_TYPE > + static void + launch( localIndex const numElements, + KERNEL_TYPE const & kernelComponent ) + { + GEOS_MARK_FUNCTION; + forAll< POLICY >( numElements, [=] GEOS_HOST_DEVICE ( localIndex const iperf ) + { + + kernelComponent.computeFlux( iperf ); + + } ); + } + + + StackVariables m_stackVariables; + +protected: + ElementViewConst< arrayView1d< real64 const > > const m_resPres; + ElementViewConst< arrayView2d< real64 const, compflow::USD_PHASE > > const m_resPhaseVolFrac; + ElementViewConst< arrayView3d< real64 const, compflow::USD_PHASE_DC > > const m_dResPhaseVolFrac; + ElementViewConst< arrayView3d< real64 const, compflow::USD_COMP_DC > > const m_dResCompFrac_dCompDens; + ElementViewConst< arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > > const m_resPhaseDens; + ElementViewConst< arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > > const m_dResPhaseDens; + ElementViewConst< arrayView3d< real64 const, constitutive::multifluid::USD_PHASE > > const m_resPhaseVisc; + ElementViewConst< arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_DC > > const m_dResPhaseVisc; + ElementViewConst< arrayView4d< real64 const, constitutive::multifluid::USD_PHASE_COMP > > const m_resPhaseCompFrac; + ElementViewConst< arrayView5d< real64 const, constitutive::multifluid::USD_PHASE_COMP_DC > > const m_dResPhaseCompFrac; + ElementViewConst< arrayView3d< real64 const, constitutive::relperm::USD_RELPERM > > const m_resPhaseRelPerm; + ElementViewConst< arrayView4d< real64 const, constitutive::relperm::USD_RELPERM_DS > > const m_dResPhaseRelPerm_dPhaseVolFrac; + arrayView1d< real64 const > const m_wellElemGravCoef; + arrayView1d< real64 const > const m_wellElemPres; + arrayView2d< real64 const, compflow::USD_COMP > const m_wellElemCompDens; + arrayView1d< real64 const > const m_wellElemTotalMassDens; + arrayView2d< real64 const, compflow::USD_FLUID_DC > const m_dWellElemTotalMassDens; + arrayView2d< real64 const, compflow::USD_COMP > const m_wellElemCompFrac; + arrayView3d< real64 const, compflow::USD_COMP_DC > const m_dWellElemCompFrac_dCompDens; + arrayView1d< real64 const > const m_perfGravCoef; + arrayView1d< localIndex const > const m_perfWellElemIndex; + arrayView1d< real64 const > const m_perfTrans; + arrayView1d< localIndex const > const m_resElementRegion; + arrayView1d< localIndex const > const m_resElementSubRegion; + arrayView1d< localIndex const > const m_resElementIndex; + arrayView2d< real64 > const m_compPerfRate; + arrayView4d< real64 > const m_dCompPerfRate; + arrayView3d< real64 > const m_dCompPerfRate_dPres; + arrayView4d< real64 > const m_dCompPerfRate_dComp; + + bool const m_disableReservoirToWellFlow; + + +}; + +/** + * @class PerforationKernelFactory + */ +class PerforationFluxKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numComps the number of fluid components + * @param[in] dt time step size + * @param[in] rankOffset the offset of my MPI rank + * @param[in] useTotalMassEquation flag specifying whether to replace one component bal eqn with total mass eqn + * @param[in] dofKey string to get the element degrees of freedom numbers + * @param[in] wellControls object holding well control/constraint information + * @param[in] subregion well subregion + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY > + static void + createAndLaunch( integer const numComp, + integer const numPhases, + string const flowSolverName, + PerforationData * const perforationData, + ElementSubRegionBase const & subRegion, + ElementRegionManager & elemManager, + integer const disableReservoirToWellFlow ) + { + geos::internal::kernelLaunchSelectorCompPhaseSwitch( numComp, numPhases, [&]( auto NC, auto NP ) + { + integer constexpr NUM_COMP = NC(); + integer constexpr NUM_PHASE = NP(); + integer constexpr IS_THERMAL = 0; + + using kernelType = PerforationFluxKernel< NUM_COMP, NUM_PHASE, IS_THERMAL >; + typename kernelType::CompFlowAccessors compFlowAccessors( elemManager, flowSolverName ); + typename kernelType::MultiFluidAccessors multiFluidAccessors( elemManager, flowSolverName ); + typename kernelType::RelPermAccessors relPermAccessors( elemManager, flowSolverName ); + + kernelType kernel( perforationData, subRegion, compFlowAccessors, multiFluidAccessors, relPermAccessors, disableReservoirToWellFlow ); + kernelType::template launch< POLICY >( perforationData->size(), kernel ); + } ); + } +}; + +} // end namespace isothermalPerforationFluxKernels + +namespace thermalPerforationFluxKernels +{ + +using namespace constitutive; + +/******************************** PerforationFluxKernel ********************************/ + +template< integer NC, integer NP, integer IS_THERMAL > +class PerforationFluxKernel : public isothermalPerforationFluxKernels::PerforationFluxKernel< NC, NP, IS_THERMAL > +{ +public: + + using Base = isothermalPerforationFluxKernels::PerforationFluxKernel< NC, NP, IS_THERMAL >; + //using AbstractBase::m_dPhaseVolFrac; + using Base::m_resPhaseCompFrac; + using Base::m_dResCompFrac_dCompDens; + using Base::m_dWellElemCompFrac_dCompDens; + //using AbstractBase::m_dPhaseCompFrac; + //using AbstractBase::m_dCompFrac_dCompDens; + /// Compile time value for the number of components + static constexpr integer numComp = NC; + + /// Compile time value for the number of phases + static constexpr integer numPhase = NP; + + /// Compile time value for thermal option + static constexpr integer isThermal = IS_THERMAL; + + using TAG = typename Base::TAG; + using CompFlowAccessors = typename Base::CompFlowAccessors; + using MultiFluidAccessors = typename Base::MultiFluidAccessors; + using RelPermAccessors = typename Base::RelPermAccessors; + + + using ThermalCompFlowAccessors = + StencilAccessors< fields::flow::temperature >; + + using ThermalMultiFluidAccessors = + StencilMaterialAccessors< MultiFluidBase, + fields::multifluid::phaseEnthalpy, + fields::multifluid::dPhaseEnthalpy >; + + //using ThermalConductivityAccessors = + // StencilMaterialAccessors< MultiPhaseThermalConductivityBase, + // fields::thermalconductivity::effectiveConductivity >; + + /** + * @brief The type for element-based non-constitutive data parameters. + * Consists entirely of ArrayView's. + * + * Can be converted from ElementRegionManager::ElementViewAccessor + * by calling .toView() or .toViewConst() on an accessor instance + */ + template< typename VIEWTYPE > + using ElementViewConst = ElementRegionManager::ElementViewConst< VIEWTYPE >; + + PerforationFluxKernel ( PerforationData * const perforationData, + ElementSubRegionBase const & subRegion, + MultiFluidBase const & fluid, + CompFlowAccessors const & compFlowAccessors, + MultiFluidAccessors const & multiFluidAccessors, + RelPermAccessors const & relPermAccessors, + bool const disableReservoirToWellFlow, + ThermalCompFlowAccessors const & thermalCompFlowAccessors, + ThermalMultiFluidAccessors const & thermalMultiFluidAccessors ) + : Base( perforationData, + subRegion, + compFlowAccessors, + multiFluidAccessors, + relPermAccessors, + disableReservoirToWellFlow ), + m_wellElemPhaseFrac( fluid.phaseFraction() ), + m_dPhaseFrac( fluid.dPhaseFraction() ), + m_wellElemPhaseEnthalpy( fluid.phaseEnthalpy()), + m_dWellElemPhaseEnthalpy( fluid.dPhaseEnthalpy()), + m_energyPerfFlux( perforationData->getField< fields::well::energyPerforationFlux >()), + m_dEnergyPerfFlux( perforationData->getField< fields::well::dEnergyPerforationFlux >()), + m_temp( thermalCompFlowAccessors.get( fields::flow::temperature {} ) ), + m_resPhaseEnthalpy( thermalMultiFluidAccessors.get( fields::multifluid::phaseEnthalpy {} ) ), + m_dResPhaseEnthalpy( thermalMultiFluidAccessors.get( fields::multifluid::dPhaseEnthalpy {} ) ) + + {} + + template< typename FUNC = NoOpFunc > + GEOS_HOST_DEVICE + inline + void + computeFlux( localIndex const iperf ) const + { + using Deriv = constitutive::multifluid::DerivativeOffset; + using CP_Deriv =constitutive::multifluid::DerivativeOffsetC< NC, IS_THERMAL >; + // initialize outputs + m_energyPerfFlux[iperf]=0; + for( integer ke = 0; ke < 2; ++ke ) + { + for( integer i = 0; i < CP_Deriv::nDer; ++i ) + { + m_dEnergyPerfFlux[iperf][ke][i]=0; + } + } + + Base::computeFlux ( iperf, [&]( localIndex const iwelem, localIndex const er, localIndex const esr, localIndex const ei, localIndex const ip, + real64 const potDiff, real64 const flux, real64 const (&dFlux)[2][CP_Deriv::nDer] ) + { + if( potDiff >= 0 ) // ** reservoir cell is upstream ** + { + + real64 const res_enthalpy = m_resPhaseEnthalpy[er][esr][ei][0][ip]; + + m_energyPerfFlux[iperf] += flux * res_enthalpy; + + // energy equation derivatives WRT res P & T + m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dP] += dFlux[TAG::RES][CP_Deriv::dP] * res_enthalpy + + flux * m_dResPhaseEnthalpy[er][esr][ei][0][ip][Deriv::dP]; + m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dT] += dFlux[TAG::RES][CP_Deriv::dT] * res_enthalpy + + flux * m_dResPhaseEnthalpy[er][esr][ei][0][ip][Deriv::dT]; + // energy equation derivatives WRT well P + m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dP] += dFlux[TAG::WELL][CP_Deriv::dP] * res_enthalpy; + m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dT] += dFlux[TAG::WELL][CP_Deriv::dT] * res_enthalpy; + + + // energy equation derivatives WRT reservoir dens + real64 dProp_dC[numComp]{}; + applyChainRule( NC, + m_dResCompFrac_dCompDens[er][esr][ei], + m_dResPhaseEnthalpy[er][esr][ei][0][ip], + dProp_dC, + Deriv::dC ); + + for( integer jc = 0; jc < NC; ++jc ) + { + m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dC+jc] += flux * dProp_dC[jc]; + } + } + else // ** reservoir cell is downstream + { + for( integer iphase = 0; iphase < NP; ++iphase ) + { + bool const phaseExists = m_wellElemPhaseFrac[iwelem][0][iphase] > 0.0; + if( !phaseExists ) + continue; + double pflux = m_wellElemPhaseFrac[iwelem][0][iphase]*flux; + real64 const wellelem_enthalpy = m_wellElemPhaseEnthalpy[iwelem][0][iphase]; + m_energyPerfFlux[iperf] += pflux * wellelem_enthalpy; + + // energy equation derivatives WRT res P & T + m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dP] += dFlux[TAG::RES][CP_Deriv::dP] * wellelem_enthalpy; + m_dEnergyPerfFlux[iperf][TAG::RES][CP_Deriv::dT] += dFlux[TAG::RES][CP_Deriv::dT] * wellelem_enthalpy; + + m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dP] += dFlux[TAG::WELL][CP_Deriv::dP] * wellelem_enthalpy + + pflux * m_dWellElemPhaseEnthalpy[iwelem][0][iphase][Deriv::dP] + + pflux * wellelem_enthalpy * m_dPhaseFrac[iwelem][0][iphase][Deriv::dP]; + m_dEnergyPerfFlux[iperf][TAG::WELL][CP_Deriv::dT] += dFlux[TAG::WELL][CP_Deriv::dT] * wellelem_enthalpy + + pflux * m_dWellElemPhaseEnthalpy[iwelem][0][iphase][Deriv::dT] + + pflux * wellelem_enthalpy * m_dPhaseFrac[iwelem][0][iphase][Deriv::dT]; + + //energy e + real64 dPVF_dC[numComp]{}; + applyChainRule( NC, + m_dWellElemCompFrac_dCompDens[iwelem], + m_dPhaseFrac[iwelem][0][iphase], + dPVF_dC, + Deriv::dC ); + for( integer ic=0; ic + static void + launch( localIndex const numElements, + KERNEL_TYPE const & kernelComponent ) + { + GEOS_MARK_FUNCTION; + forAll< POLICY >( numElements, [=] GEOS_HOST_DEVICE ( localIndex const iperf ) + { + kernelComponent.computeFlux( iperf ); + + } ); + } + +protected: + + /// Views on well element properties + /// Element phase fraction + arrayView3d< real64 const, multifluid::USD_PHASE > const m_wellElemPhaseFrac; + arrayView4d< real64 const, multifluid::USD_PHASE_DC > const m_dPhaseFrac; + arrayView3d< real64 const, multifluid::USD_PHASE > const m_wellElemPhaseEnthalpy; + arrayView4d< real64 const, multifluid::USD_PHASE_DC > const m_dWellElemPhaseEnthalpy; + + /// Views on energy flux + arrayView1d< real64 > const m_energyPerfFlux; + arrayView3d< real64 > const m_dEnergyPerfFlux; + + /// Views on temperature + ElementViewConst< arrayView1d< real64 const > > const m_temp; + + /// Views on phase enthalpies + ElementViewConst< arrayView3d< real64 const, multifluid::USD_PHASE > > const m_resPhaseEnthalpy; + ElementViewConst< arrayView4d< real64 const, multifluid::USD_PHASE_DC > > const m_dResPhaseEnthalpy; + + +}; + +/** + * @class PerforationKernelFactory + */ +class PerforationFluxKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numComps the number of fluid components + * @param[in] dt time step size + * @param[in] rankOffset the offset of my MPI rank + * @param[in] useTotalMassEquation flag specifying whether to replace one component bal eqn with total mass eqn + * @param[in] dofKey string to get the element degrees of freedom numbers + * @param[in] wellControls object holding well control/constraint information + * @param[in] subregion well subregion + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY > + static void + createAndLaunch( integer const numComp, + integer const numPhases, + string const flowSolverName, + PerforationData * const perforationData, + ElementSubRegionBase const & subRegion, + MultiFluidBase const & fluid, + ElementRegionManager & elemManager, + integer const disableReservoirToWellFlow ) + { + geos::internal::kernelLaunchSelectorCompPhaseSwitch( numComp, numPhases, [&]( auto NC, auto NP ) + { + integer constexpr NUM_COMP = NC(); + integer constexpr NUM_PHASE = NP(); + integer constexpr IS_THERMAL = 1; + + using kernelType = PerforationFluxKernel< NUM_COMP, NUM_PHASE, IS_THERMAL >; + typename kernelType::CompFlowAccessors compFlowAccessors( elemManager, flowSolverName ); + typename kernelType::MultiFluidAccessors multiFluidAccessors( elemManager, flowSolverName ); + typename kernelType::RelPermAccessors relPermAccessors( elemManager, flowSolverName ); + typename kernelType::ThermalCompFlowAccessors thermalCompFlowAccessors( elemManager, flowSolverName ); + typename kernelType::ThermalMultiFluidAccessors thermalMultiFluidAccessors( elemManager, flowSolverName ); + + kernelType kernel( perforationData, subRegion, fluid, compFlowAccessors, multiFluidAccessors, + relPermAccessors, disableReservoirToWellFlow, + thermalCompFlowAccessors, + thermalMultiFluidAccessors ); + kernelType::template launch< POLICY >( perforationData->size(), kernel ); + } ); + } +}; + +} // end namespace thermalPerforationFluxKernels + +} // end namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_PERFORATIONFLUXLKERNELS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp index e06f25cd3eb..0c8fac303ba 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp @@ -50,6 +50,8 @@ SinglePhaseWell::SinglePhaseWell( const string & name, { m_numDofPerWellElement = 2; m_numDofPerResElement = 1; + m_numPhases = 1; + m_numComponents = 1; this->registerWrapper( FlowSolverBase::viewKeyStruct::allowNegativePressureString(), &m_allowNegativePressure ). setApplyDefaultValue( 1 ). // negative pressure is allowed by default @@ -313,7 +315,7 @@ void SinglePhaseWell::updateFluidModel( WellElementSubRegion & subRegion ) const } ); } -void SinglePhaseWell::updateSubRegionState( WellElementSubRegion & subRegion ) +real64 SinglePhaseWell::updateSubRegionState( WellElementSubRegion & subRegion ) { // update volumetric rates for the well constraints // Warning! This must be called before updating the fluid model @@ -326,12 +328,21 @@ void SinglePhaseWell::updateSubRegionState( WellElementSubRegion & subRegion ) updateBHPForConstraint( subRegion ); // note: the perforation rates are updated separately + return 0.0; // change in phasevolume fraction doesnt apply } -void SinglePhaseWell::initializeWells( DomainPartition & domain ) +void SinglePhaseWell::initializeWells( DomainPartition & domain, real64 const & time_n, real64 const & dt ) { GEOS_MARK_FUNCTION; - + GEOS_UNUSED_VAR( time_n ); + GEOS_UNUSED_VAR( dt ); + // different functionality than for compositional + // compositional has better treatment of well initialization in cases where well starts later in sim + // logic will be incorporated into single phase in different push + if( time_n > 0 ) + { + return; + } // loop over the wells forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, MeshLevel & meshLevel, @@ -380,7 +391,7 @@ void SinglePhaseWell::initializeWells( DomainPartition & domain ) subRegion.size(), perforationData.getNumPerforationsGlobal(), wellControls, - 0.0, // initialization done at t = 0 + 0.0, // initialization done at t = 0 resSinglePhaseFlowAccessors.get( fields::flow::pressure{} ), resSingleFluidAccessors.get( fields::singlefluid::density{} ), resElementRegion, @@ -402,7 +413,7 @@ void SinglePhaseWell::initializeWells( DomainPartition & domain ) // 5) Estimate the well rates RateInitializationKernel::launch( subRegion.size(), wellControls, - 0.0, // initialization done at t = 0 + 0.0, // initialization done at t = 0 wellElemDens, connRate ); @@ -411,14 +422,16 @@ void SinglePhaseWell::initializeWells( DomainPartition & domain ) } ); } -void SinglePhaseWell::assembleFluxTerms( real64 const dt, - DomainPartition const & domain, +void SinglePhaseWell::assembleFluxTerms( 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; - + GEOS_UNUSED_VAR( time_n ); + GEOS_UNUSED_VAR( dt ); // loop over the wells forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, @@ -505,7 +518,7 @@ void SinglePhaseWell::assemblePressureRelations( real64 const & time_n, subRegion.isLocallyOwned(), subRegion.getTopWellElementIndex(), wellControls, - time_n + dt, // controls evaluated with BHP/rate of the end of the time interval + time_n + dt, // controls evaluated with BHP/rate of the end of the time interval wellElemDofNumber, wellElemGravCoef, nextWellElemIndex, @@ -538,13 +551,16 @@ void SinglePhaseWell::assemblePressureRelations( real64 const & time_n, } ); } -void SinglePhaseWell::assembleAccumulationTerms( DomainPartition const & domain, +void SinglePhaseWell::assembleAccumulationTerms( 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; - + GEOS_UNUSED_VAR( time_n ); + GEOS_UNUSED_VAR( dt ); forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, MeshLevel const & mesh, arrayView1d< string const > const & regionNames ) @@ -583,7 +599,8 @@ void SinglePhaseWell::assembleAccumulationTerms( DomainPartition const & domain, } ); } ); - + // then assemble the volume balance equations + assembleVolumeBalanceTerms( domain, dofManager, localMatrix, localRhs ); } void SinglePhaseWell::assembleVolumeBalanceTerms( DomainPartition const & GEOS_UNUSED_PARAM( domain ), @@ -671,10 +688,12 @@ void SinglePhaseWell::shutDownWell( real64 const time_n, } -void SinglePhaseWell::computePerforationRates( DomainPartition & domain ) +void SinglePhaseWell::computePerforationRates( real64 const & time_n, + real64 const & dt, DomainPartition & domain ) { GEOS_MARK_FUNCTION; - + GEOS_UNUSED_VAR( time_n ); + GEOS_UNUSED_VAR( dt ); forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, MeshLevel & mesh, arrayView1d< string const > const & regionNames ) diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp index 144a4de033f..bd95ac6d2d4 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.hpp @@ -160,13 +160,14 @@ class SinglePhaseWell : public WellSolverBase * @brief Recompute the perforation rates for all the wells * @param domain the domain containing the mesh and fields */ - virtual void computePerforationRates( DomainPartition & domain ) override; + virtual void computePerforationRates( real64 const & time_n, + real64 const & dt, DomainPartition & domain ) override; /** * @brief Recompute all dependent quantities from primary variables (including constitutive models) on the well * @param subRegion the well subRegion containing the well elements and their associated fields */ - virtual void updateSubRegionState( WellElementSubRegion & subRegion ) override; + virtual real64 updateSubRegionState( WellElementSubRegion & subRegion ) override; /** * @brief assembles the flux terms for all connections between well elements @@ -177,11 +178,12 @@ class SinglePhaseWell : public WellSolverBase * @param matrix the system matrix * @param rhs the system right-hand side vector */ - void assembleFluxTerms( real64 const dt, - DomainPartition const & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) override; + virtual void assembleFluxTerms( real64 const & time_n, + real64 const & dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; /** * @brief assembles the accumulation term for all the well elements @@ -190,10 +192,11 @@ class SinglePhaseWell : public WellSolverBase * @param matrix the system matrix * @param rhs the system right-hand side vector */ - void assembleAccumulationTerms( DomainPartition const & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) override; + virtual void assembleAccumulationTerms( real64 const & time_n, + real64 const & dt, DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) override; /** * @brief assembles the volume balance terms for all well elements @@ -202,10 +205,10 @@ class SinglePhaseWell : public WellSolverBase * @param matrix the system matrix * @param rhs the system right-hand side vector */ - virtual void assembleVolumeBalanceTerms( DomainPartition const & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) override; + void assembleVolumeBalanceTerms( DomainPartition const & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ); /** * @brief assembles the pressure relations at all connections between well elements except at the well head @@ -269,7 +272,7 @@ class SinglePhaseWell : public WellSolverBase * @brief Initialize all the primary and secondary variables in all the wells * @param domain the domain containing the well manager to access individual wells */ - void initializeWells( DomainPartition & domain ) override; + void initializeWells( DomainPartition & domain, real64 const & time_n, real64 const & dt ) override; /** * @brief Make sure that the well constraints are compatible diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/ThermalCompositionalMultiphaseWellKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/ThermalCompositionalMultiphaseWellKernels.hpp new file mode 100644 index 00000000000..280dc6c0959 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/ThermalCompositionalMultiphaseWellKernels.hpp @@ -0,0 +1,1121 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file ThermalCompositionalMultiphaseWellKernels.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_THERMALCOMPOSITIONALMULTIPHASEWELLKERNELS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_THERMALCOMPOSITIONALMULTIPHASEWELLKERNELS_HPP + +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellKernels.hpp" +#include "physicsSolvers/SolverBaseKernels.hpp" +namespace geos +{ + +namespace thermalCompositionalMultiphaseWellKernels +{ + +using namespace constitutive; + +/******************************** TotalMassDensityKernel ****************************/ + +/** + * @class TotalMassDensityKernel + * @tparam NUM_COMP number of fluid components + * @tparam NUM_PHASE number of fluid phases + * @brief Define the interface for the property kernel in charge of computing the total mass density + */ +template< integer NUM_COMP, integer NUM_PHASE > +class TotalMassDensityKernel : public compositionalMultiphaseWellKernels::TotalMassDensityKernel< NUM_COMP, NUM_PHASE > +{ +public: + using Base = compositionalMultiphaseWellKernels::TotalMassDensityKernel< NUM_COMP, NUM_PHASE >; + using Base::m_dCompFrac_dCompDens; + using Base::m_dPhaseMassDens; + using Base::m_dPhaseVolFrac; + using Base::m_dTotalMassDens; + using Base::m_phaseMassDens; + using Base::m_phaseVolFrac; + using Base::m_totalMassDens; + using Base::numComp; + using Base::numPhase; + + /** + * @brief Constructor + * @param[in] subRegion the element subregion + * @param[in] fluid the fluid model + */ + TotalMassDensityKernel( ObjectManagerBase & subRegion, + MultiFluidBase const & fluid ) + : Base( subRegion, fluid ) + {} + + /** + * @brief Compute the total mass density in an element + * @tparam FUNC the type of the function that can be used to customize the kernel + * @param[in] ei the element index + * @param[in] totalMassDensityKernelOp the function used to customize the kernel + */ + GEOS_HOST_DEVICE inline void compute( localIndex const ei ) const + { + using Deriv = multifluid::DerivativeOffset; + + arraySlice1d< real64 const, compflow::USD_PHASE - 1 > phaseVolFrac = m_phaseVolFrac[ei]; + arraySlice2d< real64 const, compflow::USD_PHASE_DC - 1 > dPhaseVolFrac = m_dPhaseVolFrac[ei]; + arraySlice1d< real64 const, multifluid::USD_PHASE - 2 > phaseMassDens = m_phaseMassDens[ei][0]; + arraySlice2d< real64 const, multifluid::USD_PHASE_DC - 2 > dPhaseMassDens = m_dPhaseMassDens[ei][0]; + + real64 & dTotalMassDens_dT = m_dTotalMassDens[ei][Deriv::dT]; + + // Call the base compute the compute the total mass density and derivatives + return Base::compute( ei, [&]( localIndex const ip ) + { + dTotalMassDens_dT += dPhaseVolFrac[ip][Deriv::dT] * phaseMassDens[ip] + phaseVolFrac[ip] * dPhaseMassDens[ip][Deriv::dT]; + } ); + } + +protected: + // outputs + arrayView1d< real64 > m_dTotalMassDens_dTemp; +}; + +/** + * @class TotalMassDensityKernelFactory + */ +class TotalMassDensityKernelFactory +{ +public: + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numComp the number of fluid components + * @param[in] numPhase the number of fluid phases + * @param[in] subRegion the element subregion + * @param[in] fluid the fluid model + */ + template< typename POLICY > + static void + createAndLaunch( integer const numComp, + integer const numPhase, + ObjectManagerBase & subRegion, + MultiFluidBase const & fluid ) + { + if( numPhase == 2 ) + { + isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComp, [&]( auto NC ) + { + integer constexpr NUM_COMP = NC(); + TotalMassDensityKernel< NUM_COMP, 2 > kernel( subRegion, fluid ); + TotalMassDensityKernel< NUM_COMP, 2 >::template launch< POLICY >( subRegion.size(), kernel ); + } ); + } + else if( numPhase == 3 ) + { + isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComp, [&]( auto NC ) + { + integer constexpr NUM_COMP = NC(); + TotalMassDensityKernel< NUM_COMP, 3 > kernel( subRegion, fluid ); + TotalMassDensityKernel< NUM_COMP, 3 >::template launch< POLICY >( subRegion.size(), kernel ); + } ); + } + } +}; + +/******************************** ResidualNormKernel ********************************/ + +/** + * @class ResidualNormKernel + */ +template< localIndex NUM_COMP > +class ResidualNormKernel : public solverBaseKernels::ResidualNormKernelBase< 2 > +{ +public: + + /// Compile time value for the number of components + static constexpr integer numComp = NUM_COMP; + + + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NUM_COMP, 1 >; + + using Base = solverBaseKernels::ResidualNormKernelBase< 2 >; + using Base::m_minNormalizer; + using Base::m_rankOffset; + using Base::m_localResidual; + using Base::m_dofNumber; + + ResidualNormKernel( globalIndex const rankOffset, + arrayView1d< real64 const > const & localResidual, + arrayView1d< globalIndex const > const & dofNumber, + arrayView1d< localIndex const > const & ghostRank, + integer const targetPhaseIndex, + WellElementSubRegion const & subRegion, + MultiFluidBase const & fluid, + WellControls const & wellControls, + real64 const timeAtEndOfStep, + real64 const dt, + real64 const minNormalizer ) + : Base( rankOffset, + localResidual, + dofNumber, + ghostRank, + minNormalizer ), + m_numPhases( fluid.numFluidPhases()), + m_targetPhaseIndex( targetPhaseIndex ), + m_dt( dt ), + m_isLocallyOwned( subRegion.isLocallyOwned() ), + m_iwelemControl( subRegion.getTopWellElementIndex() ), + m_isProducer( wellControls.isProducer() ), + m_currentControl( wellControls.getControl() ), + m_targetBHP( wellControls.getTargetBHP( timeAtEndOfStep ) ), + m_targetTotalRate( wellControls.getTargetTotalRate( timeAtEndOfStep ) ), + m_targetPhaseRate( wellControls.getTargetPhaseRate( timeAtEndOfStep ) ), + m_targetMassRate( wellControls.getTargetMassRate( timeAtEndOfStep ) ), + m_volume( subRegion.getElementVolume() ), + m_phaseDens_n( fluid.phaseDensity_n() ), + m_totalDens_n( fluid.totalDensity_n() ), + m_phaseVolFraction_n( subRegion.getField< fields::well::phaseVolumeFraction_n >()), + m_phaseInternalEnergy_n( fluid.phaseInternalEnergy_n() ) + {} + + + GEOS_HOST_DEVICE + void computeMassEnergyNormalizers( localIndex const iwelem, + real64 & massNormalizer, + real64 & energyNormalizer ) const + { + massNormalizer = LvArray::math::max( m_minNormalizer, m_totalDens_n[iwelem][0] * m_volume[iwelem] ); + + for( integer ip = 0; ip < m_numPhases; ++ip ) + { + energyNormalizer += m_phaseInternalEnergy_n[iwelem][0][ip] * m_phaseDens_n[iwelem][0][ip] * m_phaseVolFraction_n[iwelem][ip] * m_volume[iwelem]; + } + // warning: internal energy can be negative + energyNormalizer = LvArray::math::max( m_minNormalizer, LvArray::math::abs( energyNormalizer ) ); + } + + GEOS_HOST_DEVICE + virtual void computeLinf( localIndex const iwelem, + LinfStackVariables & stack ) const override + { + real64 normalizer = 0.0; + for( integer idof = 0; idof < WJ_ROFFSET::nEqn; ++idof ) + { + + // Step 1: compute a normalizer for the control or pressure equation + + // for the control equation, we distinguish two cases + if( idof == WJ_ROFFSET::CONTROL ) + { + + // for the top well element, normalize using the current control + if( m_isLocallyOwned && iwelem == m_iwelemControl ) + { + if( m_currentControl == WellControls::Control::BHP ) + { + // the residual entry is in pressure units + normalizer = m_targetBHP; + } + else if( m_currentControl == WellControls::Control::TOTALVOLRATE ) + { + // the residual entry is in volume / time units + normalizer = LvArray::math::max( LvArray::math::abs( m_targetTotalRate ), m_minNormalizer ); + } + else if( m_currentControl == WellControls::Control::PHASEVOLRATE ) + { + // the residual entry is in volume / time units + normalizer = LvArray::math::max( LvArray::math::abs( m_targetPhaseRate ), m_minNormalizer ); + } + else if( m_currentControl == WellControls::Control::MASSRATE ) + { + // the residual entry is in volume / time units + normalizer = LvArray::math::max( LvArray::math::abs( m_targetMassRate ), m_minNormalizer ); + } + } + // for the pressure difference equation, always normalize by the BHP + else + { + normalizer = m_targetBHP; + } + } + // Step 2: compute a normalizer for the mass balance equations + else if( idof >= WJ_ROFFSET::MASSBAL && idof < WJ_ROFFSET::MASSBAL + numComp ) + { + if( m_isProducer ) // only PHASEVOLRATE is supported for now + { + // the residual is in mass units + normalizer = m_dt * LvArray::math::abs( m_targetPhaseRate ) * m_phaseDens_n[iwelem][0][m_targetPhaseIndex]; + } + else // Type::INJECTOR, only TOTALVOLRATE is supported for now + { + if( m_currentControl == WellControls::Control::MASSRATE ) + { + normalizer = m_dt * LvArray::math::abs( m_targetMassRate ); + } + else + { + // the residual is in mass units + normalizer = m_dt * LvArray::math::abs( m_targetTotalRate ) * m_totalDens_n[iwelem][0]; + } + + } + + // to make sure that everything still works well if the rate is zero, we add this check + normalizer = LvArray::math::max( normalizer, m_volume[iwelem] * m_totalDens_n[iwelem][0] ); + } + // Step 3: compute a normalizer for the volume balance equations + else if( idof == WJ_ROFFSET::VOLBAL ) + { + if( m_isProducer ) // only PHASEVOLRATE is supported for now + { + // the residual is in volume units + normalizer = m_dt * LvArray::math::abs( m_targetPhaseRate ); + } + else // Type::INJECTOR, only TOTALVOLRATE is supported for now + { + if( m_currentControl == WellControls::Control::MASSRATE ) + { + normalizer = m_dt * LvArray::math::abs( m_targetMassRate/ m_totalDens_n[iwelem][0] ); + } + else + { + normalizer = m_dt * LvArray::math::abs( m_targetTotalRate ); + } + + } + // to make sure that everything still works well if the rate is zero, we add this check + normalizer = LvArray::math::max( normalizer, m_volume[iwelem] ); + } + // step 3: energy residual + if( idof == WJ_ROFFSET::ENERGYBAL ) + { + real64 massNormalizer = 0.0, energyNormalizer = 0.0; + computeMassEnergyNormalizers( iwelem, massNormalizer, energyNormalizer ); + real64 const valEnergy = LvArray::math::abs( m_localResidual[stack.localRow + WJ_ROFFSET::ENERGYBAL] ) / energyNormalizer; + if( valEnergy > stack.localValue[1] ) + { + stack.localValue[1] = valEnergy; + } + + } + else + { + normalizer = LvArray::math::max( m_minNormalizer, normalizer ); + // Step 4: compute the contribution to the residual + real64 const val = LvArray::math::abs( m_localResidual[stack.localRow + idof] ) / normalizer; + if( val > stack.localValue[0] ) + { + stack.localValue[0] = val; + } + } + } + } + + GEOS_HOST_DEVICE + virtual void computeL2( localIndex const iwelem, + L2StackVariables & stack ) const override + { + GEOS_UNUSED_VAR( iwelem, stack ); + GEOS_ERROR( "The L2 norm is not implemented for CompositionalMultiphaseWell" ); + } + + +protected: + + /// Number of fluid phases + integer const m_numPhases; + + /// Index of the target phase + integer const m_targetPhaseIndex; + + /// Time step size + real64 const m_dt; + + /// Flag indicating whether the well is locally owned or not + bool const m_isLocallyOwned; + + /// Index of the element where the control is enforced + localIndex const m_iwelemControl; + + /// Flag indicating whether the well is a producer or an injector + bool const m_isProducer; + + /// Controls + WellControls::Control const m_currentControl; + real64 const m_targetBHP; + real64 const m_targetTotalRate; + real64 const m_targetPhaseRate; + real64 const m_targetMassRate; + + /// View on the volume + arrayView1d< real64 const > const m_volume; + + /// View on phase/total density at the previous converged time step + arrayView3d< real64 const, multifluid::USD_PHASE > const m_phaseDens_n; + arrayView2d< real64 const, multifluid::USD_FLUID > const m_totalDens_n; + arrayView2d< real64 const, compflow::USD_PHASE > const m_phaseVolFraction_n; + arrayView3d< real64 const, multifluid::USD_PHASE > const m_phaseInternalEnergy_n; + +}; + +/* + *@class ResidualNormKernelFactory + */ +class ResidualNormKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numComp number of fluid components + * @param[in] numDof number of dofs per well element + * @param[in] targetPhaseIndex the index of the target phase (for phase volume control) + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey the string key to retrieve the degress of freedom numbers + * @param[in] localResidual the residual vector on my MPI rank + * @param[in] subRegion the well element subregion + * @param[in] fluid the fluid model + * @param[in] wellControls the controls + * @param[in] timeAtEndOfStep the time at the end of the step (time_n + dt) + * @param[in] dt the time step size + * @param[out] residualNorm the residual norm on the subRegion + */ + template< typename POLICY > + static void + createAndLaunch( integer const numComp, + integer const targetPhaseIndex, + globalIndex const rankOffset, + string const & dofKey, + arrayView1d< real64 const > const & localResidual, + WellElementSubRegion const & subRegion, + MultiFluidBase const & fluid, + WellControls const & wellControls, + real64 const timeAtEndOfStep, + real64 const dt, + real64 const minNormalizer, + real64 (& residualNorm)[2] ) + { + isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch ( numComp, [&]( auto NC ) + { + + integer constexpr NUM_COMP = NC(); + using kernelType = ResidualNormKernel< NUM_COMP >; + arrayView1d< globalIndex const > const dofNumber = subRegion.getReference< array1d< globalIndex > >( dofKey ); + arrayView1d< integer const > const ghostRank = subRegion.ghostRank(); + + kernelType kernel( rankOffset, localResidual, dofNumber, ghostRank, + targetPhaseIndex, subRegion, fluid, wellControls, timeAtEndOfStep, dt, minNormalizer ); + kernelType::template launchLinf< POLICY >( subRegion.size(), kernel, residualNorm ); + } ); + } + +}; + +/******************************** ElementBasedAssemblyKernel ********************************/ + +/** + * @class ElementBasedAssemblyKernel + * @tparam NUM_COMP number of fluid components + * @tparam IS_THERMAL thermal flag + * @brief Define the interface for the assembly kernel in charge of thermal accumulation and volume balance + */ +template< localIndex NUM_COMP > +class ElementBasedAssemblyKernel : public compositionalMultiphaseWellKernels::ElementBasedAssemblyKernel< NUM_COMP, 1 > +{ +public: + using Base = compositionalMultiphaseWellKernels::ElementBasedAssemblyKernel< NUM_COMP, 1 >; + using Base::m_dCompFrac_dCompDens; + using Base::m_dofNumber; + using Base::m_dPhaseCompFrac; + using Base::m_dPhaseDens; + using Base::m_dPhaseVolFrac; + using Base::m_dPoro_dPres; + using Base::m_elemGhostRank; + using Base::m_localMatrix; + using Base::m_localRhs; + using Base::m_numPhases; + using Base::m_phaseCompFrac; + using Base::m_phaseCompFrac_n; + using Base::m_phaseDens; + using Base::m_phaseDens_n; + using Base::m_phaseVolFrac; + using Base::m_phaseVolFrac_n; + using Base::m_porosity; + using Base::m_porosity_n; + using Base::m_rankOffset; + using Base::m_volume; + using Base::numComp; + using Base::numDof; + using Base::numEqn; + + using FLUID_PROP_COFFSET = multifluid::DerivativeOffsetC< NUM_COMP, 1 >; + + + /** + * @brief Constructor + * @param[in] numPhases the number of fluid phases + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey the string key to retrieve the degress of freedom numbers + * @param[in] subRegion the element subregion + * @param[in] fluid the fluid model + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + ElementBasedAssemblyKernel( localIndex const numPhases, + integer const isProducer, + globalIndex const rankOffset, + string const dofKey, + WellElementSubRegion const & subRegion, + MultiFluidBase const & fluid, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs, + BitFlags< isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags > const kernelFlags ) + : Base( numPhases, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ), + m_phaseInternalEnergy_n( fluid.phaseInternalEnergy_n()), + m_phaseInternalEnergy( fluid.phaseInternalEnergy()), + m_dPhaseInternalEnergy( fluid.dPhaseInternalEnergy()) + {} + + struct StackVariables : public Base::StackVariables + { +public: + GEOS_HOST_DEVICE + StackVariables() + : Base::StackVariables() + {} + using Base::StackVariables::eqnRowIndices; + using Base::StackVariables::dofColIndices; + using Base::StackVariables::localJacobian; + using Base::StackVariables::localResidual; + using Base::StackVariables::localRow; + using Base::StackVariables::volume; + + + + }; + /** + * @brief Performs the setup phase for the kernel. + * @param[in] ei the element index + * @param[in] stack the stack variables + */ + GEOS_HOST_DEVICE + void setup( localIndex const ei, + StackVariables & stack ) const + { + Base::setup( ei, stack ); + + + + } + + /** + * @brief Compute the local accumulation contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the kernel + * @param[in] ei the element index + * @param[inout] stack the stack variables + */ + GEOS_HOST_DEVICE + void computeAccumulation( localIndex const ei, + StackVariables & stack ) const + { + using Deriv = multifluid::DerivativeOffset; + + Base::computeAccumulation( ei, stack, [&]( integer const ip + , real64 const & phaseAmount + , real64 const & phaseAmount_n + , real64 const (&dPhaseAmount)[FLUID_PROP_COFFSET::nDer] ) + { + // We are in the loop over phases, ip provides the current phase index. + // We have to do two things: + // 1- Assemble the derivatives of the component mass balance equations with respect to temperature + // 2- Assemble the phase-dependent part of the accumulation term of the energy equation + + real64 dPhaseInternalEnergy_dC[numComp]{}; + + // construct the slices + arraySlice2d< real64 const, compflow::USD_COMP_DC - 1 > dCompFrac_dCompDens = m_dCompFrac_dCompDens[ei]; + arraySlice1d< real64 const, multifluid::USD_PHASE - 2 > phaseInternalEnergy_n = m_phaseInternalEnergy_n[ei][0]; + arraySlice1d< real64 const, multifluid::USD_PHASE - 2 > phaseInternalEnergy = m_phaseInternalEnergy[ei][0]; + arraySlice2d< real64 const, multifluid::USD_PHASE_DC - 2 > dPhaseInternalEnergy = m_dPhaseInternalEnergy[ei][0]; + + // Step 1: assemble the phase-dependent part of the accumulation term of the energy equation + + real64 const phaseEnergy = phaseAmount * phaseInternalEnergy[ip]; + real64 const phaseEnergy_n = phaseAmount_n * phaseInternalEnergy_n[ip]; + real64 const dPhaseEnergy_dP = dPhaseAmount[FLUID_PROP_COFFSET::dP] * phaseInternalEnergy[ip] + + phaseAmount * dPhaseInternalEnergy[ip][Deriv::dP]; + real64 const dPhaseEnergy_dT = dPhaseAmount[FLUID_PROP_COFFSET::dT] * phaseInternalEnergy[ip] + + phaseAmount * dPhaseInternalEnergy[ip][Deriv::dT]; + // local accumulation + stack.localResidual[numEqn-1] += phaseEnergy - phaseEnergy_n; + + // derivatives w.r.t. pressure and temperature + stack.localJacobian[numEqn-1][0] += dPhaseEnergy_dP; + stack.localJacobian[numEqn-1][numDof-1] += dPhaseEnergy_dT; + + // derivatives w.r.t. component densities + applyChainRule( numComp, dCompFrac_dCompDens, dPhaseInternalEnergy[ip], dPhaseInternalEnergy_dC, Deriv::dC ); + for( integer jc = 0; jc < numComp; ++jc ) + { + stack.localJacobian[numEqn-1][jc + 1] += phaseInternalEnergy[ip] * dPhaseAmount[FLUID_PROP_COFFSET::dC+jc] + + dPhaseInternalEnergy_dC[jc] * phaseAmount; + } + } ); + + + } + + /** + * @brief Compute the local volume balance contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the kernel + * @param[in] ei the element index + * @param[inout] stack the stack variables + */ + GEOS_HOST_DEVICE + void computeVolumeBalance( localIndex const ei, + StackVariables & stack ) const + { + Base::computeVolumeBalance( ei, stack ); + + } + + GEOS_HOST_DEVICE + void complete( localIndex const ei, + StackVariables & stack ) const + { + // Assemble the component mass balance equations and volume balance equations + // Energy balance equation updates to solver matrices included in Base class + Base::complete( ei, stack ); + + } + +protected: + /// View on derivative of porosity w.r.t temperature + arrayView2d< real64 const > const m_dPoro_dTemp; + + /// Views on phase internal energy + arrayView3d< real64 const, multifluid::USD_PHASE > m_phaseInternalEnergy_n; + arrayView3d< real64 const, multifluid::USD_PHASE > m_phaseInternalEnergy; + arrayView4d< real64 const, multifluid::USD_PHASE_DC > m_dPhaseInternalEnergy; + +}; + +/** + * @class ElementBasedAssemblyKernelFactory + */ +class ElementBasedAssemblyKernelFactory +{ +public: + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numComps the number of fluid components + * @param[in] numPhases the number of fluid phases + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey the string key to retrieve the degress of freedom numbers + * @param[in] subRegion the element subregion + * @param[in] fluid the fluid model + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY > + static void + createAndLaunch( localIndex const numComps, + localIndex const numPhases, + integer const isProducer, + globalIndex const rankOffset, + integer const useTotalMassEquation, + string const dofKey, + WellElementSubRegion const & subRegion, + MultiFluidBase const & fluid, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + isothermalCompositionalMultiphaseBaseKernels:: + internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC ) + { + localIndex constexpr NUM_COMP = NC(); + + + BitFlags< isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags > kernelFlags; + if( useTotalMassEquation ) + kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags::TotalMassEquation ); + + ElementBasedAssemblyKernel< NUM_COMP > + kernel( numPhases, isProducer, rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs, kernelFlags ); + ElementBasedAssemblyKernel< NUM_COMP >::template + launch< POLICY, ElementBasedAssemblyKernel< NUM_COMP > >( subRegion.size(), kernel ); + } ); + } +}; +/** + * @class FaceBasedAssemblyKernel + * @tparam NUM_COMP number of fluid components + * @brief Define the interface for the assembly kernel in charge of flux terms + */ +template< integer NC > +class FaceBasedAssemblyKernel : public compositionalMultiphaseWellKernels::FaceBasedAssemblyKernel< NC, 1 > +{ +public: + static constexpr integer IS_THERMAL = 1; + using Base = compositionalMultiphaseWellKernels::FaceBasedAssemblyKernel< NC, IS_THERMAL >; + + // Well jacobian column and row indicies + using WJ_COFFSET = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + + using CP_Deriv = multifluid::DerivativeOffsetC< NC, IS_THERMAL >; + + using TAG = compositionalMultiphaseWellKernels::ElemTag; + + + using Base::m_isProducer; + using Base::m_dt; + using Base::m_localRhs; + using Base::m_localMatrix; + using Base::m_rankOffset; + using Base::maxNumElems; + using Base::maxStencilSize; + using Base::m_useTotalMassEquation; + + /// Compile time value for the number of components + static constexpr integer numComp = NC; + + /// Compute time value for the number of degrees of freedom + static constexpr integer numDof = WJ_COFFSET::nDer; + +/// Compile time value for the number of equations except volume and momentum + static constexpr integer numEqn = WJ_ROFFSET::nEqn - 2; + + /** + * @brief Constructor for the kernel interface + * @param[in] rankOffset the offset of my MPI rank + * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] dofNumberAccessor + * @param[in] compFlowAccessors + * @param[in] multiFluidAccessors + * @param[in] capPressureAccessors + * @param[in] permeabilityAccessors + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + * @param[in] kernelFlags flags packed together + */ + FaceBasedAssemblyKernel( real64 const dt, + globalIndex const rankOffset, + string const wellDofKey, + WellControls const & wellControls, + WellElementSubRegion const & subRegion, + MultiFluidBase const & fluid, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs, + BitFlags< isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags > kernelFlags ) + : Base( dt + , rankOffset + , wellDofKey + , wellControls + , subRegion + , localMatrix + , localRhs + , kernelFlags ), + m_numPhases ( fluid.numFluidPhases()), + m_globalWellElementIndex( subRegion.getGlobalWellElementIndex() ), + m_phaseFraction( fluid.phaseFraction()), + m_dPhaseFraction( fluid.dPhaseFraction()), + m_phaseEnthalpy( fluid.phaseEnthalpy()), + m_dPhaseEnthalpy( fluid.dPhaseEnthalpy()) + { } + + struct StackVariables : public Base::StackVariables + { +public: + + GEOS_HOST_DEVICE + StackVariables( localIndex const size ) + : Base::StackVariables( size ) + {} + + /// Storage for the face local residual vector (energy equation) + stackArray1d< real64, maxNumElems > localEnergyFlux; + /// Storage for the face local energy Jacobian matrix dC dP dT + stackArray2d< real64, maxNumElems * maxStencilSize * CP_Deriv::nDer > localEnergyFluxJacobian; + /// Storage for the face local Jacobian matrix dQ only + stackArray2d< real64, maxNumElems * maxStencilSize > localEnergyFluxJacobian_dQ; + }; + + GEOS_HOST_DEVICE + inline + void setup( localIndex const iwelem, StackVariables & stack ) const + { + Base::setup ( iwelem, stack ); + stack.localEnergyFlux.resize( stack.numConnectedElems ); + stack.localEnergyFluxJacobian.resize( stack.numConnectedElems, stack.stencilSize * numDof ); + stack.localEnergyFluxJacobian_dQ.resize( stack.numConnectedElems, 1 ); + for( integer i=0; i= 0 && oneSidedEqnRowIndices < m_localMatrix.numRows() ) + { + + if( !m_isProducer && m_globalWellElementIndex[iwelem] == 0 ) + { + // For top segment energy balance eqn replaced with T(n+1) - T = 0 + // No other energy balance derivatives + // Assumption is global index == 0 is top segment with fixed temp BC + for( integer i=0; i< CP_Deriv::nDer; i++ ) + { + stack.localEnergyFluxJacobian[0][i] = 0.0; + } + stack.localEnergyFluxJacobian_dQ[0][0]=0; + stack.localEnergyFlux[0]=0; + } + + + // Setup Jacobian global col indicies ( Mapping from local jac order to well jac order) + globalIndex oneSidedDofColIndices_dRate = stack.offsetCurrent + WJ_COFFSET::dQ; + globalIndex oneSidedDofColIndices_dPresCompTempUp[CP_Deriv::nDer]{}; + + int ioff=0; + oneSidedDofColIndices_dPresCompTempUp[ioff++] = stack.offsetUp + WJ_COFFSET::dP; + oneSidedDofColIndices_dPresCompTempUp[ioff++] = stack.offsetUp + WJ_COFFSET::dT; + for( integer jdof = 0; jdof < NC; ++jdof ) + { + oneSidedDofColIndices_dPresCompTempUp[ioff++] = stack.offsetUp + WJ_COFFSET::dC+ jdof; + } + + m_localMatrix.template addToRow< parallelDeviceAtomic >( oneSidedEqnRowIndices, + &oneSidedDofColIndices_dRate, + stack.localEnergyFluxJacobian_dQ[0], + 1 ); + m_localMatrix.template addToRowBinarySearchUnsorted< parallelDeviceAtomic >( oneSidedEqnRowIndices, + oneSidedDofColIndices_dPresCompTempUp, + stack.localEnergyFluxJacobian[0], + CP_Deriv::nDer ); + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[oneSidedEqnRowIndices], stack.localEnergyFlux[0] ); + } + } + else // if ( stack.numConnectedElems == 2 ) + { + globalIndex row_current = stack.offsetCurrent + WJ_ROFFSET::ENERGYBAL - m_rankOffset; + globalIndex row_next = stack.offsetNext + WJ_ROFFSET::ENERGYBAL - m_rankOffset; + + if( !m_isProducer ) + { + if( row_next >= 0 && row_next < m_localMatrix.numRows() ) + { + if( m_globalWellElementIndex[stack.iwelemNext] == 0 ) + { + for( integer i=0; i= 0 && eqnRowIndices[i] < m_localMatrix.numRows() ) + { + m_localMatrix.template addToRow< parallelDeviceAtomic >( eqnRowIndices[i], + &dofColIndices_dRate, + stack.localEnergyFluxJacobian_dQ[i], + 1 ); + m_localMatrix.template addToRowBinarySearchUnsorted< parallelDeviceAtomic >( eqnRowIndices[i], + dofColIndices, + stack.localEnergyFluxJacobian[i], + CP_Deriv::nDer ); + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[eqnRowIndices[i]], stack.localEnergyFlux[i] ); + } + } + } + + } + + /** + * @brief Compute the local flux contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the computation of the phase fluxes + * @param[in] ie the element index + * @param[inout] stack the stack variables + * @param[in] compFluxKernelOp the function used to customize the computation of the component fluxes + */ + + GEOS_HOST_DEVICE + inline + void computeFlux( localIndex const iwelem, StackVariables & stack ) const + { + Base::computeFlux ( iwelem, stack, [&] ( localIndex const & iwelemNext + , localIndex const & iwelemUp + , real64 const & currentConnRate + , real64 const (&dCompFrac_dCompDens)[NC][NC] ) + { + + if( iwelemNext < 0 && !m_isProducer ) // exit connection, injector + { + real64 eflux=0; + real64 eflux_dq=0; + for( integer ip = 0; ip < m_numPhases; ++ip ) + { + eflux += m_phaseEnthalpy[iwelemUp][0][ip]* m_phaseFraction[iwelemUp][0][ip]; + eflux_dq += m_phaseEnthalpy[iwelemUp][0][ip] * m_phaseFraction[iwelemUp][0][ip]; + + stack.localEnergyFluxJacobian[0] [CP_Deriv::dP] += m_phaseEnthalpy[iwelemUp][0][ip]*m_dPhaseFraction[iwelemUp][0][ip][CP_Deriv::dP] + + m_dPhaseEnthalpy[iwelemUp][0][ip][CP_Deriv::dP]*m_phaseFraction[iwelemUp][0][ip]; + stack.localEnergyFluxJacobian[0] [CP_Deriv::dT] += m_phaseEnthalpy[iwelemUp][0][ip]*m_dPhaseFraction[iwelemUp][0][ip][CP_Deriv::dT] + + m_dPhaseEnthalpy[iwelemUp][0][ip][CP_Deriv::dT]*m_phaseFraction[iwelemUp][0][ip]; + + real64 dProp1_dC[numComp]{}; + applyChainRule( numComp, dCompFrac_dCompDens, m_dPhaseEnthalpy[iwelemUp][0][ip], dProp1_dC, CP_Deriv::dC ); + real64 dProp2_dC[numComp]{}; + applyChainRule( numComp, dCompFrac_dCompDens, m_dPhaseFraction[iwelemUp][0][ip], dProp2_dC, CP_Deriv::dC ); + for( integer dof=0; dof < numComp; dof++ ) + { + stack.localEnergyFluxJacobian[0] [CP_Deriv::dC+dof] += m_phaseEnthalpy[iwelemUp][0][ip]*dProp2_dC[dof] + + dProp1_dC[dof]*m_phaseFraction[iwelemUp][0][ip]; + } + } + for( integer dof=0; dof < CP_Deriv::nDer; dof++ ) + { + stack.localEnergyFluxJacobian[0] [dof] *= -m_dt*currentConnRate; + } + // Energy equation + stack.localEnergyFlux[0] = -m_dt * eflux * currentConnRate; + stack.localEnergyFluxJacobian_dQ[0][0] = -m_dt * eflux_dq; + } + else if( ( iwelemNext < 0 && m_isProducer ) || currentConnRate < 0 ) // exit connection, producer + { + real64 eflux=0; + real64 eflux_dq=0; + for( integer ip = 0; ip < m_numPhases; ++ip ) + { + eflux += m_phaseEnthalpy[iwelemUp][0][ip]* m_phaseFraction[iwelemUp][0][ip]; + eflux_dq += m_phaseEnthalpy[iwelemUp][0][ip] * m_phaseFraction[iwelemUp][0][ip]; + stack.localEnergyFluxJacobian[0] [CP_Deriv::dP] += m_phaseEnthalpy[iwelemUp][0][ip]*m_dPhaseFraction[iwelemUp][0][ip][CP_Deriv::dP] + + m_dPhaseEnthalpy[iwelemUp][0][ip][CP_Deriv::dP]*m_phaseFraction[iwelemUp][0][ip]; + stack.localEnergyFluxJacobian[0] [CP_Deriv::dT] += m_phaseEnthalpy[iwelemUp][0][ip]*m_dPhaseFraction[iwelemUp][0][ip][CP_Deriv::dT] + + m_dPhaseEnthalpy[iwelemUp][0][ip][CP_Deriv::dT]*m_phaseFraction[iwelemUp][0][ip]; + + real64 dProp1_dC[numComp]{}; + applyChainRule( numComp, dCompFrac_dCompDens, m_dPhaseEnthalpy[iwelemUp][0][ip], dProp1_dC, CP_Deriv::dC ); + real64 dProp2_dC[numComp]{}; + applyChainRule( numComp, dCompFrac_dCompDens, m_dPhaseFraction[iwelemUp][0][ip], dProp2_dC, CP_Deriv::dC ); + for( integer dof=0; dof < numComp; dof++ ) + { + stack.localEnergyFluxJacobian[0] [CP_Deriv::dC+dof] += m_phaseEnthalpy[iwelemUp][0][ip]*dProp2_dC[dof] + + dProp1_dC[dof]*m_phaseFraction[iwelemUp][0][ip]; + } + + } + + for( integer dof=0; dof < CP_Deriv::nDer; dof++ ) + { + stack.localEnergyFluxJacobian[0][dof] *= -m_dt*currentConnRate; + } + stack.localEnergyFlux[0] = -m_dt * eflux * currentConnRate; + stack.localEnergyFluxJacobian_dQ[0][0] = -m_dt*eflux_dq; + } + else + { + real64 eflux=0; + real64 eflux_dq=0; + for( integer ip = 0; ip < m_numPhases; ++ip ) + { + eflux += m_phaseEnthalpy[iwelemUp][0][ip]* m_phaseFraction[iwelemUp][0][ip]; + eflux_dq += m_phaseEnthalpy[iwelemUp][0][ip] * m_phaseFraction[iwelemUp][0][ip]; + + real64 dprop_dp = m_phaseEnthalpy[iwelemUp][0][ip]*m_dPhaseFraction[iwelemUp][0][ip][CP_Deriv::dP] + + m_dPhaseEnthalpy[iwelemUp][0][ip][CP_Deriv::dP]*m_phaseFraction[iwelemUp][0][ip]; + real64 dprop_dt = m_phaseEnthalpy[iwelemUp][0][ip]*m_dPhaseFraction[iwelemUp][0][ip][CP_Deriv::dT] + + m_dPhaseEnthalpy[iwelemUp][0][ip][CP_Deriv::dT]*m_phaseFraction[iwelemUp][0][ip]; + + stack.localEnergyFluxJacobian[TAG::NEXT ] [CP_Deriv::dP] += dprop_dp; + stack.localEnergyFluxJacobian[TAG::NEXT] [CP_Deriv::dT] += dprop_dt; + + stack.localEnergyFluxJacobian[TAG::CURRENT ] [CP_Deriv::dP] += dprop_dp; + stack.localEnergyFluxJacobian[TAG::CURRENT] [CP_Deriv::dT] += dprop_dt; + + real64 dPE_dC[numComp]{}; + applyChainRule( numComp, dCompFrac_dCompDens, m_dPhaseEnthalpy[iwelemUp][0][ip], dPE_dC, CP_Deriv::dC ); + real64 dPF_dC[numComp]{}; + applyChainRule( numComp, dCompFrac_dCompDens, m_dPhaseFraction[iwelemUp][0][ip], dPF_dC, CP_Deriv::dC ); + + for( integer dof=0; dof < numComp; dof++ ) + { + stack.localEnergyFluxJacobian[TAG::NEXT ][CP_Deriv::dC+dof] += m_phaseEnthalpy[iwelemUp][0][ip]*dPF_dC[dof] + +dPE_dC[dof]*m_phaseFraction[iwelemUp][0][ip]; + stack.localEnergyFluxJacobian[TAG::CURRENT ][CP_Deriv::dC+dof] += m_phaseEnthalpy[iwelemUp][0][ip]*dPF_dC[dof] + +dPE_dC[dof]*m_phaseFraction[iwelemUp][0][ip]; + } + } + stack.localEnergyFlux[TAG::NEXT ] = m_dt * eflux * currentConnRate; + stack.localEnergyFlux[TAG::CURRENT ] = -m_dt * eflux * currentConnRate; + stack.localEnergyFluxJacobian_dQ [TAG::NEXT ][0] = m_dt * eflux_dq; + stack.localEnergyFluxJacobian_dQ [TAG::CURRENT][0] = -m_dt * eflux_dq; + for( integer dof=0; dof < CP_Deriv::nDer; dof++ ) + { + stack.localEnergyFluxJacobian[TAG::NEXT ][dof] *= m_dt*currentConnRate; + stack.localEnergyFluxJacobian[TAG::CURRENT ][dof] *= -m_dt*currentConnRate; + } + } + + } ); + + } + + + /** + * @brief Performs the kernel launch + * @tparam POLICY the policy used in the RAJA kernels + * @tparam KERNEL_TYPE the kernel type + * @param[in] numElements the number of elements + * @param[inout] kernelComponent the kernel component providing access to setup/compute/complete functions and stack + * variables + */ + template< typename POLICY, typename KERNEL_TYPE > + static void + launch( localIndex const numElements, + KERNEL_TYPE const & kernelComponent ) + { + GEOS_MARK_FUNCTION; + forAll< POLICY >( numElements, [=] GEOS_HOST_DEVICE ( localIndex const ie ) + { + typename KERNEL_TYPE::StackVariables stack( 1 ); + + kernelComponent.setup( ie, stack ); + kernelComponent.computeFlux( ie, stack ); + kernelComponent.complete( ie, stack ); + } ); + } + +protected: + /// Number of phases + integer const m_numPhases; + + /// Global index of local element + arrayView1d< globalIndex const > m_globalWellElementIndex; + + /// Element phase fraction + arrayView3d< real64 const, multifluid::USD_PHASE > const m_phaseFraction; + arrayView4d< real64 const, multifluid::USD_PHASE_DC > const m_dPhaseFraction; + + /// Views on phase enthalpy + arrayView3d< real64 const, multifluid::USD_PHASE > m_phaseEnthalpy; + arrayView4d< real64 const, multifluid::USD_PHASE_DC > m_dPhaseEnthalpy; + + +}; + +/** + * @class FaceBasedAssemblyKernelFactory + */ +class FaceBasedAssemblyKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numComps the number of fluid components + * @param[in] dt time step size + * @param[in] rankOffset the offset of my MPI rank + * @param[in] useTotalMassEquation flag specifying whether to replace one component bal eqn with total mass eqn + * @param[in] dofKey string to get the element degrees of freedom numbers + * @param[in] wellControls object holding well control/constraint information + * @param[in] subregion well subregion + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY > + static void + createAndLaunch( integer const numComps, + real64 const dt, + globalIndex const rankOffset, + integer const useTotalMassEquation, + string const dofKey, + WellControls const & wellControls, + WellElementSubRegion const & subRegion, + MultiFluidBase const & fluid, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC ) + { + integer constexpr NUM_COMP = NC(); + + + BitFlags< isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags > kernelFlags; + if( useTotalMassEquation ) + kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags::TotalMassEquation ); + + + using kernelType = FaceBasedAssemblyKernel< NUM_COMP >; + + + kernelType kernel( dt, rankOffset, dofKey, wellControls, subRegion, fluid, localMatrix, localRhs, kernelFlags ); + kernelType::template launch< POLICY >( subRegion.size(), kernel ); + } ); + } +}; + +} // end namespace thermalCompositionalMultiphaseWellKernels + +} // end namespace geos + +#endif // GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_THERMALCOMPOSITIONALMULTIPHASEWELLKERNELS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/ThermalSinglePhaseWellKernels.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/ThermalSinglePhaseWellKernels.hpp new file mode 100644 index 00000000000..c55a1f2991e --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/ThermalSinglePhaseWellKernels.hpp @@ -0,0 +1,246 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file SinglePhaseWellKernels.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_THERMALSINGLEPHASEWELLKERNELS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_THERMALSINGLEPHASEWELLKERNELS_HPP + +#include "constitutive/fluid/singlefluid/SingleFluidFields.hpp" +#include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" +#include "common/DataTypes.hpp" +#include "common/GEOS_RAJA_Interface.hpp" +#include "mesh/ElementRegionManager.hpp" +#include "physicsSolvers/fluidFlow/FlowSolverBaseFields.hpp" +#include "physicsSolvers/fluidFlow/StencilAccessors.hpp" +#include "physicsSolvers/fluidFlow/wells/WellControls.hpp" +#include "physicsSolvers/SolverBaseKernels.hpp" + +namespace geos +{ + +namespace thermalSinglePhaseWellKernels +{ + + + +/******************************** ElementBasedAssemblyKernel ********************************/ + +/** + * @class ElementBasedAssemblyKernel + * @tparam NUM_DOF number of degrees of freedom + * @brief Define the interface for the assembly kernel in charge of accumulation and volume balance + */ +template< integer NUM_DOF > +class ElementBasedAssemblyKernel : public singlePhaseWellKernels::ElementBasedAssemblyKernel< NUM_DOF > +{ +public: + using Base = singlePhaseWellKernels::ElementBasedAssemblyKernel< NUM_DOF >; + using Base::m_rankOffset; + using Base::m_wellElemDofNumber; + using Base::m_elemGhostRank; + using Base::m_wellElemVolume; + using Base::m_wellElemDensity; + using Base::m_wellElemDensity_n; + using Base::m_dWellElemDensity_dPressure; + using Base::m_localMatrix; + using Base::m_localRhs; + using ROFFSET = singlePhaseWellKernels::RowOffset; + using COFFSET = singlePhaseWellKernels::ColOffset; + + /// Compute time value for the number of degrees of freedom + static constexpr integer numDof = NUM_DOF; + + /// Compute time value for the number of equations + static constexpr integer numEqn = NUM_DOF; + + /** + * @brief Constructor + * @param[in] numPhases the number of fluid phases + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey the string key to retrieve the degress of freedom numbers + * @param[in] subRegion the element subregion + * @param[in] fluid the fluid model + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + ElementBasedAssemblyKernel( globalIndex const rankOffset, + string const dofKey, + ElementSubRegionBase const & subRegion, + constitutive::SingleFluidBase const & fluid, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + : Base( rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs ), + m_dWellElemDensity_dTemperature( fluid.dDensity_dTemperature() ), + m_internalEnergy( fluid.internalEnergy() ), + m_internalEnergy_n( fluid.internalEnergy_n() ), + m_dInternalEnergy_dPres( fluid.dInternalEnergy_dPressure() ), + m_dInternalEnergy_dTemp( fluid.dInternalEnergy_dTemperature() ) + {} + + /** + * @struct StackVariables + * @brief Kernel variables (dof numbers, jacobian and residual) located on the stack + */ + struct StackVariables : public Base::StackVariables + { +public: + GEOS_HOST_DEVICE + StackVariables() + : Base::StackVariables() + {} + using Base::StackVariables::eqnRowIndices; + using Base::StackVariables::dofColIndices; + using Base::StackVariables::localJacobian; + using Base::StackVariables::localResidual; + using Base::StackVariables::localRow; + using Base::StackVariables::volume; + using Base::StackVariables::density; + using Base::StackVariables::density_n; + using Base::StackVariables::dDensity_dPres; + + }; + /** + * @brief Getter for the ghost rank of an element + * @param[in] ei the element index + * @return the ghost rank of the element + */ + GEOS_HOST_DEVICE + integer elemGhostRank( localIndex const ei ) const + { return m_elemGhostRank( ei ); } + + + + /** + * @brief Compute the local accumulation contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the kernel + * @param[in] ei the element index + * @param[inout] stack the stack variables + * @param[in] phaseAmountKernelOp the function used to customize the kernel + */ + template< typename FUNC = NoOpFunc > + GEOS_HOST_DEVICE + void computeAccumulation( localIndex const iwelem, + StackVariables & stack ) const + { + Base::computeAccumulation( iwelem, stack, [&]( ) + { + + // Step 1: assemble the derivatives of the mass balance equation w.r.t temperature + stack.localJacobian[0][numDof-1] = stack.volume * m_dWellElemDensity_dTemperature[iwelem][0]; + + // Step 2: assemble the fluid part of the accumulation term of the energy equation + real64 const fluidEnergy = stack.volume * stack.density * m_internalEnergy[iwelem][0]; + real64 const fluidEnergy_n = stack.volume * stack.density_n * m_internalEnergy_n[iwelem][0]; + + real64 const dFluidEnergy_dP = stack.volume * stack.dDensity_dPres * m_internalEnergy[iwelem][0] + + stack.volume * stack.density * m_dInternalEnergy_dPres[iwelem][0]; + + + real64 const dFluidEnergy_dT = stack.volume * m_dWellElemDensity_dTemperature[iwelem][0] * m_internalEnergy[iwelem][0] + + stack.volume * stack.density * m_dInternalEnergy_dTemp[iwelem][0]; + + // local accumulation + stack.localResidual[numEqn-1] = fluidEnergy - fluidEnergy_n; + + // derivatives w.r.t. pressure and temperature + stack.localJacobian[numEqn-1][0] = dFluidEnergy_dP; + stack.localJacobian[numEqn-1][numDof-1] = dFluidEnergy_dT; + } ); + } + + + + /** + * @brief Performs the kernel launch + * @tparam POLICY the policy used in the RAJA kernels + * @tparam KERNEL_TYPE the kernel type + * @param[in] numElems the number of elements + * @param[inout] kernelComponent the kernel component providing access to setup/compute/complete functions and stack variables + */ + 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 iwelem ) + { + if( kernelComponent.elemGhostRank( iwelem ) >= 0 ) + { + return; + } + typename KERNEL_TYPE::StackVariables stack; + kernelComponent.setup( iwelem, stack ); + kernelComponent.computeAccumulation( iwelem, stack ); + kernelComponent.complete( iwelem, stack ); + + } ); + } + +protected: + + /// View on derivative of fluid density w.r.t temperature + arrayView2d< real64 const > const m_dWellElemDensity_dTemperature; + + /// Views on fluid internal energy + arrayView2d< real64 const > const m_internalEnergy; + arrayView2d< real64 const > const m_internalEnergy_n; + arrayView2d< real64 const > const m_dInternalEnergy_dPres; + arrayView2d< real64 const > const m_dInternalEnergy_dTemp; + +}; + + +/** + * @class ElementBasedAssemblyKernelFactory + */ +class ElementBasedAssemblyKernelFactory +{ +public: + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] rankOffset the offset of my MPI rank + * @param[in] dofKey the string key to retrieve the degress of freedom numbers + * @param[in] subRegion the element subregion + * @param[in] fluid the fluid model + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY > + static void + createAndLaunch( globalIndex const rankOffset, + string const dofKey, + ElementSubRegionBase const & subRegion, + constitutive::SingleFluidBase const & fluid, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + integer constexpr NUM_DOF = 2; + ElementBasedAssemblyKernel< NUM_DOF > + kernel( rankOffset, dofKey, subRegion, fluid, localMatrix, localRhs ); + ElementBasedAssemblyKernel< NUM_DOF >::template + launch< POLICY, ElementBasedAssemblyKernel< NUM_DOF > >( subRegion.size(), kernel ); + + } +}; +} // end namespace singlePhaseWellKernels + +} // end namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_SINGLEPHASEWELLKERNELS_HPP diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellFields.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellFields.hpp new file mode 100644 index 00000000000..2c6549958b0 --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellFields.hpp @@ -0,0 +1,59 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file WellFields.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLFIELDS_HPP_ +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLFIELDS_HPP_ + +#include "common/DataLayouts.hpp" +#include "mesh/MeshFields.hpp" + +namespace geos +{ +/** + * A scope for field traits. + */ +namespace fields +{ + +namespace well +{ + +DECLARE_FIELD( energyPerforationFlux, + "energyPerforationFlux", + array1d< real64 >, + 0, + NOPLOT, + WRITE_AND_READ, + "Energy perforation flux" ); + +DECLARE_FIELD( dEnergyPerforationFlux, + "dEnergyPerforationFlux", + array3d< real64 >, + 0, + NOPLOT, + NO_WRITE, + "Derivative of energy perforation flux with respect to pressure temperature and global component density" ); + + +} + +} + +} + +#endif // GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLFIELDS_HPP_ diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp index 2b114b13f70..b87c37c7dd3 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.cpp @@ -28,6 +28,7 @@ #include "physicsSolvers/fluidFlow/wells/WellControls.hpp" #include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp" #include "physicsSolvers/fluidFlow/wells/LogLevelsInfo.hpp" +#include "physicsSolvers/fluidFlow/wells/ThermalCompositionalMultiphaseWellKernels.hpp" #include "fileIO/Outputs/OutputBase.hpp" namespace geos @@ -39,10 +40,18 @@ using namespace constitutive; WellSolverBase::WellSolverBase( string const & name, Group * const parent ) : SolverBase( name, parent ), + m_numPhases( 0 ), + m_numComponents( 0 ), m_numDofPerWellElement( 0 ), m_numDofPerResElement( 0 ), - m_ratesOutputDir( joinPath( OutputBase::getOutputDirectory(), name + "_rates" ) ) + m_isThermal( 0 ), + m_ratesOutputDir( joinPath( OutputBase::getOutputDirectory(), name + "_rates" )) { + registerWrapper( viewKeyStruct::isThermalString(), &m_isThermal ). + setApplyDefaultValue( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Flag indicating whether the problem is thermal or not." ); + this->getWrapper< string >( viewKeyStruct::discretizationString() ). setInputFlag( InputFlags::FALSE ); @@ -55,9 +64,9 @@ WellSolverBase::WellSolverBase( string const & name, addLogLevel< logInfo::Crossflow >(); } -Group * WellSolverBase::createChild( string const & childKey, string const & childName ) +Group *WellSolverBase::createChild( string const & childKey, string const & childName ) { - Group * rval = nullptr; + Group *rval = nullptr; if( childKey == keys::wellControls ) { @@ -75,13 +84,17 @@ void WellSolverBase::expandObjectCatalogs() createChild( keys::wellControls, keys::wellControls ); } - WellSolverBase::~WellSolverBase() = default; void WellSolverBase::postInputInitialization() { SolverBase::postInputInitialization(); + // 1. Set key dimensions of the problem + m_numDofPerWellElement = m_isThermal ? m_numComponents + 2 : m_numComponents + 1; // 1 pressure connectionRate + temp if thermal + m_numDofPerResElement = m_isThermal ? m_numComponents + 1: m_numComponents; // 1 pressure + temp if thermal + + // create dir for rates output if( m_writeCSV > 0 ) { @@ -113,7 +126,10 @@ void WellSolverBase::registerDataOnMesh( Group & meshBodies ) subRegion.registerField< fields::well::pressure_n >( getName() ); subRegion.registerField< fields::well::temperature >( getName() ); - subRegion.registerField< fields::well::temperature_n >( getName() ); + if( isThermal() ) + { + subRegion.registerField< fields::well::temperature_n >( getName() ); + } subRegion.registerField< fields::well::gravityCoefficient >( getName() ); @@ -148,19 +164,16 @@ void WellSolverBase::initializePostSubGroups() void WellSolverBase::setConstitutiveNamesCallSuper( ElementSubRegionBase & subRegion ) const { SolverBase::setConstitutiveNamesCallSuper( subRegion ); - subRegion.registerWrapper< string >( viewKeyStruct::fluidNamesString() ). - setPlotLevel( PlotLevel::NOPLOT ). - setRestartFlags( RestartFlags::NO_WRITE ). - setSizedFromParent( 0 ); + subRegion.registerWrapper< string >( viewKeyStruct::fluidNamesString()).setPlotLevel( PlotLevel::NOPLOT ).setRestartFlags( RestartFlags::NO_WRITE ).setSizedFromParent( 0 ); } void WellSolverBase::setupDofs( DomainPartition const & domain, DofManager & dofManager ) const { map< std::pair< string, string >, array1d< string > > meshTargets; - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const & meshBodyName, - MeshLevel const & meshLevel, - arrayView1d< string const > const & regionNames ) + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const & meshBodyName, + MeshLevel const & meshLevel, + arrayView1d< string const > const & regionNames ) { array1d< string > regions; ElementRegionManager const & elementRegionManager = meshLevel.getElemManager(); @@ -185,58 +198,114 @@ void WellSolverBase::setupDofs( DomainPartition const & domain, } void WellSolverBase::implicitStepSetup( real64 const & time_n, - real64 const & GEOS_UNUSED_PARAM( dt ), + real64 const & dt, DomainPartition & domain ) { // Initialize the primary and secondary variables for the first time step - if( time_n <= 0.0 ) - { - initializeWells( domain ); - } + + initializeWells( domain, time_n, dt ); } -void WellSolverBase::assembleSystem( real64 const time, - real64 const dt, - DomainPartition & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) + +void WellSolverBase::shutInWell( real64 const time_n, + real64 const dt, + DomainPartition const & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) { - // assemble the accumulation term in the mass balance equations - assembleAccumulationTerms( domain, dofManager, localMatrix, localRhs ); + GEOS_MARK_FUNCTION; + GEOS_UNUSED_VAR( time_n ); + GEOS_UNUSED_VAR( dt ); - // then assemble the flux terms in the mass balance equations - assembleFluxTerms( dt, domain, dofManager, localMatrix, localRhs ); + string const wellDofKey = dofManager.getKey( wellElementDofName() ); - // then assemble the volume balance equations - assembleVolumeBalanceTerms( domain, dofManager, localMatrix, localRhs ); + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel const & mesh, + arrayView1d< string const > const & regionNames ) + { - // then assemble the pressure relations between well elements - assemblePressureRelations( time, dt, domain, dofManager, localMatrix, localRhs ); + ElementRegionManager const & elemManager = mesh.getElemManager(); - // then compute the perforation rates (later assembled by the coupled solver) - computePerforationRates( domain ); + elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion const & subRegion ) + { + + globalIndex const rankOffset = dofManager.rankOffset(); + + arrayView1d< integer const > const ghostRank = + subRegion.getReference< array1d< integer > >( ObjectManagerBase::viewKeyStruct::ghostRankString() ); + arrayView1d< globalIndex const > const dofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + + forAll< parallelDevicePolicy<> >( subRegion.size(), [=] GEOS_HOST_DEVICE ( localIndex const ei ) + { + if( ghostRank[ei] >= 0 ) + { + return; + } + + globalIndex const dofIndex = dofNumber[ei]; + localIndex const localRow = dofIndex - rankOffset; + + + real64 unity = 1.0; + for( integer i=0; i < m_numDofPerWellElement; i++ ) + { + globalIndex const cindex = dofNumber[ei] + i; + globalIndex const rindex = localRow+i; + localMatrix.template addToRow< serialAtomic >( rindex, + &cindex, + &unity, + 1 ); + localRhs[cindex] = 0.0; + } - // then apply a special treatment to the wells that are shut - shutDownWell( time, dt, domain, dofManager, localMatrix, localRhs ); + } ); + } ); + } ); } + void WellSolverBase::updateState( DomainPartition & domain ) { GEOS_MARK_FUNCTION; - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - arrayView1d< string const > const & regionNames ) + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) { mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, WellElementSubRegion & subRegion ) - { - updateSubRegionState( subRegion ); - } ); + { updateSubRegionState( subRegion ); } ); } ); } +void WellSolverBase::assembleSystem( real64 const time, + real64 const dt, + DomainPartition & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) +{ + string const wellDofKey = dofManager.getKey( wellElementDofName()); + + // assemble the accumulation term in the mass balance equations + assembleAccumulationTerms( time, dt, domain, dofManager, localMatrix, localRhs ); + + // then assemble the pressure relations between well elements + assemblePressureRelations( time, dt, domain, dofManager, localMatrix, localRhs ); + // then compute the perforation rates (later assembled by the coupled solver) + computePerforationRates( time, dt, domain ); + + // then assemble the flux terms in the mass balance equations + // get a reference to the degree-of-freedom numbers + // then assemble the flux terms in the mass balance equations + assembleFluxTerms( time, dt, domain, dofManager, localMatrix, localRhs ); + +} + void WellSolverBase::initializePostInitialConditionsPreSubGroups() { SolverBase::initializePostInitialConditionsPreSubGroups(); @@ -244,15 +313,13 @@ void WellSolverBase::initializePostInitialConditionsPreSubGroups() DomainPartition & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); // make sure that nextWellElementIndex is up-to-date (will be used in well initialization and assembly) - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - arrayView1d< string const > const & regionNames ) + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) { mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, WellElementSubRegion & subRegion ) - { - subRegion.reconstructLocalConnectivity(); - } ); + { subRegion.reconstructLocalConnectivity(); } ); } ); // Precompute solver-specific constant data (e.g. gravity-coefficient) @@ -262,9 +329,9 @@ void WellSolverBase::initializePostInitialConditionsPreSubGroups() void WellSolverBase::precomputeData( DomainPartition & domain ) { R1Tensor const gravVector = gravityVector(); - forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, - MeshLevel & mesh, - arrayView1d< string const > const & regionNames ) + forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&]( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) { mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, WellElementSubRegion & subRegion ) @@ -292,16 +359,19 @@ void WellSolverBase::precomputeData( DomainPartition & domain ) } ); // set the reference well element where the BHP control is applied - wellControls.setReferenceGravityCoef( refElev * gravVector[ 2 ] ); - + wellControls.setReferenceGravityCoef( refElev * gravVector[2] ); } ); } ); } WellControls & WellSolverBase::getWellControls( WellElementSubRegion const & subRegion ) -{ return this->getGroup< WellControls >( subRegion.getWellControlsName() ); } +{ + return this->getGroup< WellControls >( subRegion.getWellControlsName()); +} WellControls const & WellSolverBase::getWellControls( WellElementSubRegion const & subRegion ) const -{ return this->getGroup< WellControls >( subRegion.getWellControlsName() ); } +{ + return this->getGroup< WellControls >( subRegion.getWellControlsName()); +} } // namespace geos diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp index 1397512189e..628db6681cf 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBase.hpp @@ -98,6 +98,12 @@ class WellSolverBase : public SolverBase */ localIndex numDofPerResElement() const { return m_numDofPerResElement; } + /** + * @brief getter for iso/thermal switch + * @return True if thermal + */ + integer isThermal() const { return m_isThermal; } + /** * @brief get the name of DOF defined on well elements * @return name of the DOF field used by derived solver type @@ -191,8 +197,9 @@ class WellSolverBase : public SolverBase * @param matrix the system matrix * @param rhs the system right-hand side vector */ - virtual void assembleFluxTerms( real64 const dt, - DomainPartition const & domain, + virtual void assembleFluxTerms( real64 const & time_n, + real64 const & dt, + DomainPartition & domain, DofManager const & dofManager, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) = 0; @@ -204,23 +211,13 @@ class WellSolverBase : public SolverBase * @param matrix the system matrix * @param rhs the system right-hand side vector */ - virtual void assembleAccumulationTerms( DomainPartition const & domain, + virtual void assembleAccumulationTerms( real64 const & time_n, + real64 const & dt, + DomainPartition & domain, DofManager const & dofManager, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) = 0; - /** - * @brief assembles the volume balance terms for all well elements - * @param domain the physical domain object - * @param dofManager degree-of-freedom manager associated with the linear system - * @param matrix the system matrix - * @param rhs the system right-hand side vector - */ - virtual void assembleVolumeBalanceTerms( DomainPartition const & domain, - DofManager const & dofManager, - CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) = 0; - /** * @brief assembles the pressure relations at all connections between well elements except at the well head * @param time_n time at the beginning of the time step @@ -237,6 +234,23 @@ class WellSolverBase : public SolverBase CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) = 0; + + /** + * @brief apply a special treatment to the wells that are shut (set Aww=I , Awr=Arw=0) + * @param time_n the time at the previous converged time step + * @param dt the time step size + * @param domain the physical domain object + * @param dofManager degree-of-freedom manager associated with the linear system + * @param matrix the system matrix + * @param rhs the system right-hand side vector + */ + void shutInWell( real64 const time_n, + real64 const dt, + DomainPartition const & domain, + DofManager const & dofManager, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ); + /** * @brief apply a special treatment to the wells that are shut * @param time_n the time at the previous converged time step @@ -263,17 +277,20 @@ class WellSolverBase : public SolverBase * @brief Recompute all dependent quantities from primary variables (including constitutive models) * @param subRegion the well subRegion containing the well elements and their associated fields */ - virtual void updateSubRegionState( WellElementSubRegion & subRegion ) = 0; + virtual real64 updateSubRegionState( WellElementSubRegion & subRegion ) = 0; /** * @brief Recompute the perforation rates for all the wells * @param domain the domain containing the mesh and fields */ - virtual void computePerforationRates( DomainPartition & domain ) = 0; + virtual void computePerforationRates( real64 const & time_n, + real64 const & dt, + DomainPartition & domain ) = 0; struct viewKeyStruct : SolverBase::viewKeyStruct { static constexpr char const * fluidNamesString() { return "fluidNames"; } + static constexpr char const * isThermalString() { return "isThermal"; } static constexpr char const * writeCSVFlagString() { return "writeCSV"; } }; @@ -300,7 +317,7 @@ class WellSolverBase : public SolverBase * @brief Initialize all the primary and secondary variables in all the wells * @param domain the domain containing the well manager to access individual wells */ - virtual void initializeWells( DomainPartition & domain ) = 0; + virtual void initializeWells( DomainPartition & domain, real64 const & time_n, real64 const & dt ) = 0; /** * @brief Make sure that the well constraints are compatible @@ -319,15 +336,26 @@ class WellSolverBase : public SolverBase /// name of the flow solver string m_flowSolverName; + /// the max number of fluid phases + integer m_numPhases; + + /// the number of fluid components + integer m_numComponents; + /// the number of Degrees of Freedom per well element integer m_numDofPerWellElement; /// the number of Degrees of Freedom per reservoir element integer m_numDofPerResElement; + /// flag indicating whether thermal formulation is used + integer m_isThermal; + integer m_writeCSV; string const m_ratesOutputDir; +/// name of the fluid constitutive model used as a reference for component/phase description + string m_referenceFluidModelName; }; } diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp index 244b24d4ec4..1c6f4d4a748 100644 --- a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp @@ -50,7 +50,7 @@ DECLARE_FIELD( pressure_n, "Pressure at the previous converged time step" ); DECLARE_FIELD( temperature, - "wellTemperature", + "temperature", array1d< real64 >, 0, LEVEL_0, @@ -58,7 +58,7 @@ DECLARE_FIELD( temperature, "Temperature" ); DECLARE_FIELD( temperature_n, - "wellTemperature_n", + "temperature_n", array1d< real64 >, 0, NOPLOT, diff --git a/src/coreComponents/physicsSolvers/fluidFlow/wells/WellTags.hpp b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellTags.hpp new file mode 100644 index 00000000000..dc75bcbdc9a --- /dev/null +++ b/src/coreComponents/physicsSolvers/fluidFlow/wells/WellTags.hpp @@ -0,0 +1,110 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file WellTags.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLTAGS_HPP +#define GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLTAGS_HPP + + +namespace geos +{ + +namespace wellTags +{ + +static constexpr real64 minDensForDivision = 1e-10; + +// tag to access well and reservoir elements in perforation rates computation +struct SubRegionTag +{ + static constexpr integer RES = 0; + static constexpr integer WELL = 1; +}; + +// tag to access the next and current well elements of a connection +struct ElemTag +{ + static constexpr integer CURRENT = 0; + static constexpr integer NEXT = 1; +}; + +// define the column offset of the derivatives +struct ColOffset +{ + static constexpr integer DPRES = 0; + static constexpr integer DCOMP = 1; +}; + +template< integer NC, integer IS_THERMAL > +struct ColOffset_WellJac; + +template< integer NC > +struct ColOffset_WellJac< NC, 0 > +{ + static constexpr integer dP = 0; + static constexpr integer dC = 1; + static constexpr integer dQ = dC + NC; + static integer constexpr nDer = dQ + 1; + +}; + +template< integer NC > +struct ColOffset_WellJac< NC, 1 > +{ + static constexpr integer dP = 0; + static constexpr integer dC = 1; + static constexpr integer dQ = dC + NC; + static constexpr integer dT = dQ+1; + /// number of derivatives + static integer constexpr nDer = dT + 1; +}; + +// define the row offset of the residual equations +struct RowOffset +{ + static constexpr integer CONTROL = 0; + static constexpr integer MASSBAL = 1; +}; + +template< integer NC, integer IS_THERMAL > +struct RowOffset_WellJac; + +template< integer NC > +struct RowOffset_WellJac< NC, 0 > +{ + static constexpr integer CONTROL = 0; + static constexpr integer MASSBAL = 1; + static constexpr integer VOLBAL = MASSBAL + NC; + static constexpr integer nEqn = VOLBAL+1; +}; + +template< integer NC > +struct RowOffset_WellJac< NC, 1 > +{ + static constexpr integer CONTROL = 0; + static constexpr integer MASSBAL = 1; + static constexpr integer VOLBAL = MASSBAL + NC; + static constexpr integer ENERGYBAL = VOLBAL+1; + static constexpr integer nEqn = ENERGYBAL+1; + +}; + +} // end namespace wellTags + +} // end namespace geos + +#endif //GEOS_PHYSICSSOLVERS_FLUIDFLOW_WELLS_WELLTAGS_HPP diff --git a/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.cpp b/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.cpp index 5853f513231..fcc7bdf2227 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.cpp +++ b/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.cpp @@ -24,6 +24,7 @@ #include "dataRepository/LogLevelsInfo.hpp" #include "constitutive/fluid/multifluid/MultiFluidBase.hpp" #include "mesh/PerforationFields.hpp" +#include "physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp" #include "physicsSolvers/fluidFlow/CompositionalMultiphaseFVM.hpp" #include "physicsSolvers/fluidFlow/CompositionalMultiphaseHybridFVM.hpp" #include "physicsSolvers/fluidFlow/CompositionalMultiphaseUtilities.hpp" @@ -34,6 +35,7 @@ #include "physicsSolvers/fluidFlow/wells/LogLevelsInfo.hpp" #include "physicsSolvers/multiphysics/MultiphasePoromechanics.hpp" + namespace geos { @@ -80,6 +82,11 @@ setMGRStrategy() // add Reservoir m_linearSolverParameters.get().mgr.strategy = LinearSolverParameters::MGR::StrategyType::compositionalMultiphaseReservoirHybridFVM; } + else if( isThermal() ) + { + m_linearSolverParameters.get().mgr.strategy = LinearSolverParameters::MGR::StrategyType::thermalCompositionalMultiphaseReservoirFVM; + + } else { // add Reservoir @@ -155,7 +162,7 @@ addCouplingSparsityPattern( DomainPartition const & domain, integer const wellNDOF = Base::wellSolver()->numDofPerWellElement(); integer constexpr maxNumComp = MultiFluidBase::MAX_NUM_COMPONENTS; - integer constexpr maxNumDof = maxNumComp + 1; + integer constexpr maxNumDof = maxNumComp + 2; string const wellDofKey = dofManager.getKey( Base::wellSolver()->wellElementDofName() ); string const resDofKey = dofManager.getKey( Base::wellSolver()->resElementDofName() ); @@ -251,10 +258,6 @@ assembleCouplingTerms( real64 const time_n, { using namespace compositionalMultiphaseUtilities; - using TAG = compositionalMultiphaseWellKernels::SubRegionTag; - using ROFFSET = compositionalMultiphaseWellKernels::RowOffset; - using COFFSET = compositionalMultiphaseWellKernels::ColOffset; - GEOS_THROW_IF( !Base::m_isWellTransmissibilityComputed, GEOS_FMT( "{} {}: The well transmissibility has not been computed yet", this->getCatalogName(), this->getName() ), @@ -268,11 +271,7 @@ assembleCouplingTerms( real64 const time_n, ElementRegionManager const & elemManager = mesh.getElemManager(); - integer constexpr MAX_NUM_COMP = MultiFluidBase::MAX_NUM_COMPONENTS; - integer constexpr MAX_NUM_DOF = MAX_NUM_COMP + 1; - integer const numComps = Base::wellSolver()->numFluidComponents(); - integer const resNumDofs = Base::wellSolver()->numDofPerResElement(); string const resDofKey = dofManager.getKey( Base::wellSolver()->resElementDofName() ); ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > const resDofNumberAccessor = @@ -284,6 +283,8 @@ assembleCouplingTerms( real64 const time_n, elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, WellElementSubRegion const & subRegion ) { + string const & fluidName = this->flowSolver()->template getConstitutiveName< MultiFluidBase >( subRegion ); + MultiFluidBase const & fluid = subRegion.getConstitutiveModel< MultiFluidBase >( fluidName ); // if the well is shut, we neglect reservoir-well flow that may occur despite the zero rate // therefore, we do not want to compute perforation rates and we simply assume they are zero @@ -297,123 +298,55 @@ assembleCouplingTerms( real64 const time_n, return; } - areWellsShut = 0; - PerforationData const * const perforationData = subRegion.getPerforationData(); // get the degrees of freedom string const wellDofKey = dofManager.getKey( Base::wellSolver()->wellElementDofName() ); - arrayView1d< globalIndex const > const & wellElemDofNumber = - subRegion.getReference< array1d< globalIndex > >( wellDofKey ); - - // get well variables on perforations - arrayView2d< real64 const > const & compPerfRate = - perforationData->getField< fields::well::compPerforationRate >(); - arrayView3d< real64 const > const & dCompPerfRate_dPres = - perforationData->getField< fields::well::dCompPerforationRate_dPres >(); - arrayView4d< real64 const > const & dCompPerfRate_dComp = - perforationData->getField< fields::well::dCompPerforationRate_dComp >(); - - arrayView1d< localIndex const > const & perfWellElemIndex = - perforationData->getField< fields::perforation::wellElementIndex >(); - - // get the element region, subregion, index - arrayView1d< localIndex const > const & resElementRegion = - perforationData->getField< fields::perforation::reservoirElementRegion >(); - arrayView1d< localIndex const > const & resElementSubRegion = - perforationData->getField< fields::perforation::reservoirElementSubRegion >(); - arrayView1d< localIndex const > const & resElementIndex = - perforationData->getField< fields::perforation::reservoirElementIndex >(); - - bool const useTotalMassEquation = this->flowSolver()->useTotalMassEquation() > 0; - - RAJA::ReduceSum< parallelDeviceReduce, integer > numCrossflowPerforations( 0 ); + areWellsShut = 0; - // loop over the perforations and add the rates to the residual and jacobian - forAll< parallelDevicePolicy<> >( perforationData->size(), [=] GEOS_HOST_DEVICE ( localIndex const iperf ) + integer useTotalMassEquation=Base::wellSolver()->useTotalMassEquation(); + integer numCrossflowPerforations=0; + if( isThermal ( ) ) { - // local working variables and arrays - stackArray1d< localIndex, 2 * MAX_NUM_COMP > eqnRowIndices( 2 * numComps ); - stackArray1d< globalIndex, 2 * MAX_NUM_DOF > dofColIndices( 2 * resNumDofs ); - - stackArray1d< real64, 2 * MAX_NUM_COMP > localPerf( 2 * numComps ); - stackArray2d< real64, 2 * MAX_NUM_COMP * 2 * MAX_NUM_DOF > localPerfJacobian( 2 * numComps, 2 * resNumDofs ); - - // get the reservoir (sub)region and element indices - localIndex const er = resElementRegion[iperf]; - localIndex const esr = resElementSubRegion[iperf]; - localIndex const ei = resElementIndex[iperf]; - - // get the well element index for this perforation - localIndex const iwelem = perfWellElemIndex[iperf]; - globalIndex const resOffset = resDofNumber[er][esr][ei]; - globalIndex const wellElemOffset = wellElemDofNumber[iwelem]; - - for( integer ic = 0; ic < numComps; ++ic ) - { - eqnRowIndices[TAG::RES * numComps + ic] = LvArray::integerConversion< localIndex >( resOffset - rankOffset ) + ic; - eqnRowIndices[TAG::WELL * numComps + ic] = LvArray::integerConversion< localIndex >( wellElemOffset - rankOffset ) + ROFFSET::MASSBAL + ic; - } - for( integer jdof = 0; jdof < resNumDofs; ++jdof ) - { - dofColIndices[TAG::RES * resNumDofs + jdof] = resOffset + jdof; - dofColIndices[TAG::WELL * resNumDofs + jdof] = wellElemOffset + COFFSET::DPRES + jdof; - } - - // populate local flux vector and derivatives - for( integer ic = 0; ic < numComps; ++ic ) - { - localPerf[TAG::RES * numComps + ic] = dt * compPerfRate[iperf][ic]; - localPerf[TAG::WELL * numComps + ic] = -dt * compPerfRate[iperf][ic]; - - if( detectCrossflow ) - { - if( compPerfRate[iperf][ic] > LvArray::NumericLimits< real64 >::epsilon ) - { - numCrossflowPerforations += 1; - } - } - - for( integer ke = 0; ke < 2; ++ke ) - { - localIndex const localDofIndexPres = ke * resNumDofs; - localPerfJacobian[TAG::RES * numComps + ic][localDofIndexPres] = dt * dCompPerfRate_dPres[iperf][ke][ic]; - localPerfJacobian[TAG::WELL * numComps + ic][localDofIndexPres] = -dt * dCompPerfRate_dPres[iperf][ke][ic]; - - for( integer jc = 0; jc < numComps; ++jc ) - { - localIndex const localDofIndexComp = localDofIndexPres + jc + 1; - localPerfJacobian[TAG::RES * numComps + ic][localDofIndexComp] = dt * dCompPerfRate_dComp[iperf][ke][ic][jc]; - localPerfJacobian[TAG::WELL * numComps + ic][localDofIndexComp] = -dt * dCompPerfRate_dComp[iperf][ke][ic][jc]; - } - } - } - - if( useTotalMassEquation ) - { - // Apply equation/variable change transformation(s) - stackArray1d< real64, 2 * MAX_NUM_DOF > work( 2 * resNumDofs ); - shiftBlockRowsAheadByOneAndReplaceFirstRowWithColumnSum( numComps, numComps, resNumDofs * 2, 2, localPerfJacobian, work ); - shiftBlockElementsAheadByOneAndReplaceFirstElementWithSum( numComps, numComps, 2, localPerf ); - } - - for( localIndex i = 0; i < localPerf.size(); ++i ) - { - if( eqnRowIndices[i] >= 0 && eqnRowIndices[i] < localMatrix.numRows() ) - { - localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic >( eqnRowIndices[i], - dofColIndices.data(), - localPerfJacobian[i].dataIfContiguous(), - 2 * resNumDofs ); - RAJA::atomicAdd( parallelDeviceAtomic{}, &localRhs[eqnRowIndices[i]], localPerf[i] ); - } - } - } ); - + coupledReservoirAndWellKernels:: + ThermalCompositionalMultiPhaseFluxKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( numComps, + wellControls.isProducer(), + dt, + rankOffset, + wellDofKey, + subRegion, + resDofNumber, + perforationData, + fluid, + useTotalMassEquation, + detectCrossflow, + numCrossflowPerforations, + localRhs, + localMatrix ); + } + else + { + coupledReservoirAndWellKernels:: + IsothermalCompositionalMultiPhaseFluxKernelFactory:: + createAndLaunch< parallelDevicePolicy<> >( numComps, + dt, + rankOffset, + wellDofKey, + subRegion, + resDofNumber, + perforationData, + fluid, + useTotalMassEquation, + detectCrossflow, + numCrossflowPerforations, + localRhs, + localMatrix ); + } - if( detectCrossflow ) // check to avoid communications if not needed + if( detectCrossflow ) // check to avoid communications if not needed { - globalIndex const totalNumCrossflowPerforations = MpiWrapper::sum( numCrossflowPerforations.get() ); + globalIndex const totalNumCrossflowPerforations = MpiWrapper::sum( numCrossflowPerforations ); if( totalNumCrossflowPerforations > 0 ) { GEOS_LOG_LEVEL_INFO_RANK_0( logInfo::Crossflow, GEOS_FMT( "CompositionalMultiphaseReservoir '{}': Warning! Crossflow detected at {} perforations in well {}" @@ -422,12 +355,12 @@ assembleCouplingTerms( real64 const time_n, WellControls::viewKeyStruct::enableCrossflowString(), wellControls.getName() )); } } - } ); - // update dynamically the MGR recipe to optimize the linear solve if all wells are shut - areWellsShut = MpiWrapper::min( areWellsShut ); - m_linearSolverParameters.get().mgr.areWellsShut = areWellsShut; + // update dynamically the MGR recipe to optimize the linear solve if all wells are shut + areWellsShut = MpiWrapper::min( areWellsShut ); + m_linearSolverParameters.get().mgr.areWellsShut = areWellsShut; + } ); } ); } diff --git a/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp b/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp index 4904148cef4..5aae361b31d 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp +++ b/src/coreComponents/physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp @@ -84,7 +84,7 @@ class CompositionalMultiphaseReservoirAndWells : public CoupledReservoirAndWells DofManager const & dofManager, CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) override; - + integer isThermal() { return flowSolver()->isThermal(); } integer useSimpleAccumulation() const { return flowSolver()->useSimpleAccumulation(); } integer useTotalMassEquation() const { return flowSolver()->useTotalMassEquation(); } integer numFluidPhases() { return flowSolver()->numFluidPhases(); } diff --git a/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp b/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp new file mode 100644 index 00000000000..91b568e6eab --- /dev/null +++ b/src/coreComponents/physicsSolvers/multiphysics/CoupledReservoirAndWellKernels.hpp @@ -0,0 +1,592 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file ThermalCompositionalMultiphaseWellKernels.hpp + */ + +#ifndef GEOS_PHYSICSSOLVERS_MULTIPHYSICS_COUPLEDRESERVOIRANDWELLS_HPP +#define GEOS_PHYSICSSOLVERS_MULTIPHYSICS_COUPLEDRESERVOIRANDWELLS_HPP + + +#include "common/DataTypes.hpp" +#include "common/GEOS_RAJA_Interface.hpp" +#include "constitutive/fluid/multifluid/Layouts.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellKernels.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp" +#include "physicsSolvers/fluidFlow/wells/WellTags.hpp" +#include "physicsSolvers/fluidFlow/wells/WellFields.hpp" +namespace geos +{ + +namespace coupledReservoirAndWellKernels +{ + +using namespace constitutive; + +/** + * @class FaceBasedAssemblyKernel + * @tparam NUM_COMP number of fluid components + * @brief Define the interface for the assembly kernel in charge of flux terms + */ +template< integer NC, integer IS_THERMAL > +class IsothermalCompositionalMultiPhaseFluxKernel +{ +public: + + /// Compile time value for the number of components + static constexpr integer numComp = NC; + static constexpr integer resNumDOF = NC+1+IS_THERMAL; + + // Well jacobian column and row indicies + using WJ_COFFSET = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + + using ROFFSET = compositionalMultiphaseWellKernels::RowOffset; + using COFFSET = compositionalMultiphaseWellKernels::ColOffset; + + using CP_Deriv = multifluid::DerivativeOffsetC< NC, IS_THERMAL >; + + using TAG = compositionalMultiphaseWellKernels::SubRegionTag; + + + + /// Compute time value for the number of degrees of freedom + static constexpr integer numDof = WJ_COFFSET::nDer; + + /// Compile time value for the number of equations except volume and momentum + static constexpr integer numEqn = WJ_ROFFSET::nEqn - 2; + + /** + * @brief Constructor for the kernel interface + * @param[in] rankOffset the offset of my MPI rank + * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] dofNumberAccessor + * @param[in] compFlowAccessors + * @param[in] multiFluidAccessors + * @param[in] capPressureAccessors + * @param[in] permeabilityAccessors + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + * @param[in] kernelFlags flags packed together + */ + IsothermalCompositionalMultiPhaseFluxKernel( real64 const dt, + globalIndex const rankOffset, + string const wellDofKey, + WellElementSubRegion const & subRegion, + ElementRegionManager::ElementViewConst< arrayView1d< globalIndex const > > const resDofNumber, + PerforationData const * const perforationData, + MultiFluidBase const & fluid, + + arrayView1d< real64 > const & localRhs, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + bool const & detectCrossflow, + integer & numCrossFlowPerforations, + BitFlags< isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags > kernelFlags ) + : + m_dt( dt ), + m_numPhases ( fluid.numFluidPhases()), + m_rankOffset( rankOffset ), + m_compPerfRate( perforationData->getField< fields::well::compPerforationRate >() ), + m_dCompPerfRate( perforationData->getField< fields::well::dCompPerforationRate >() ), + m_perfWellElemIndex( perforationData->getField< fields::perforation::wellElementIndex >() ), + m_wellElemDofNumber( subRegion.getReference< array1d< globalIndex > >( wellDofKey ) ), + m_resElemDofNumber( resDofNumber ), + m_resElementRegion( perforationData->getField< fields::perforation::reservoirElementRegion >() ), + m_resElementSubRegion( perforationData->getField< fields::perforation::reservoirElementSubRegion >() ), + m_resElementIndex( perforationData->getField< fields::perforation::reservoirElementIndex >() ), + m_localRhs( localRhs ), + m_localMatrix( localMatrix ), + m_detectCrossflow( detectCrossflow ), + m_numCrossFlowPerforations( numCrossFlowPerforations ), + m_useTotalMassEquation ( kernelFlags.isSet( isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags::TotalMassEquation ) ) + { } + + + /** + * @brief Compute the local flux contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the computation of the phase fluxes + * @param[in] ie the element index + * @param[inout] stack the stack variables + * @param[in] compFluxKernelOp the function used to customize the computation of the component fluxes + */ + + template< typename FUNC = NoOpFunc > + GEOS_HOST_DEVICE + inline + void computeFlux( localIndex const iperf, + FUNC && compFluxKernelOp = NoOpFunc{} ) const + { + + using namespace compositionalMultiphaseUtilities; + // local working variables and arrays + stackArray1d< localIndex, 2* numComp > eqnRowIndices( 2 * numComp ); + stackArray1d< globalIndex, 2*resNumDOF > dofColIndices( 2 * resNumDOF ); + + stackArray1d< real64, 2 * numComp > localPerf( 2 * numComp ); + stackArray2d< real64, 2 * resNumDOF * 2 * numComp > localPerfJacobian( 2 * numComp, 2 * resNumDOF ); + + // get the reservoir (sub)region and element indices + localIndex const er = m_resElementRegion[iperf]; + localIndex const esr = m_resElementSubRegion[iperf]; + localIndex const ei = m_resElementIndex[iperf]; + + // get the well element index for this perforation + localIndex const iwelem = m_perfWellElemIndex[iperf]; + globalIndex const resOffset = m_resElemDofNumber[er][esr][ei]; + globalIndex const wellElemOffset = m_wellElemDofNumber[iwelem]; + + for( integer ic = 0; ic < numComp; ++ic ) + { + eqnRowIndices[TAG::RES * numComp + ic] = LvArray::integerConversion< localIndex >( resOffset - m_rankOffset ) + ic; + eqnRowIndices[TAG::WELL * numComp + ic] = LvArray::integerConversion< localIndex >( wellElemOffset - m_rankOffset ) + WJ_ROFFSET::MASSBAL + ic; + } + // Note res and well have same col lineup for P and compdens + for( integer jdof = 0; jdof < NC+1; ++jdof ) + { + dofColIndices[TAG::RES * resNumDOF + jdof] = resOffset + jdof; + dofColIndices[TAG::WELL * resNumDOF + jdof] = wellElemOffset + WJ_COFFSET::dP + jdof; + } + // For temp its different + if constexpr ( IS_THERMAL ) + { + dofColIndices[TAG::RES * resNumDOF + NC+1 ] = resOffset + NC+1; + dofColIndices[TAG::WELL * resNumDOF + NC+1 ] = wellElemOffset + WJ_COFFSET::dT; + } + // populate local flux vector and derivatives + for( integer ic = 0; ic < numComp; ++ic ) + { + localPerf[TAG::RES * numComp + ic] = m_dt * m_compPerfRate[iperf][ic]; + localPerf[TAG::WELL * numComp + ic] = -m_dt * m_compPerfRate[iperf][ic]; + + if( m_detectCrossflow ) + { + if( m_compPerfRate[iperf][ic] > LvArray::NumericLimits< real64 >::epsilon ) + { + m_numCrossFlowPerforations += 1; + } + } + for( integer ke = 0; ke < 2; ++ke ) + { + localIndex localDofIndexPres = ke * resNumDOF; + + localPerfJacobian[TAG::RES * numComp + ic][localDofIndexPres] = m_dt * m_dCompPerfRate[iperf][ke][ic][CP_Deriv::dP]; + localPerfJacobian[TAG::WELL * numComp + ic][localDofIndexPres] = -m_dt * m_dCompPerfRate[iperf][ke][ic][CP_Deriv::dP]; + for( integer jc = 0; jc < numComp; ++jc ) + { + localIndex const localDofIndexComp = localDofIndexPres + jc + 1; + + localPerfJacobian[TAG::RES * numComp + ic][localDofIndexComp] = m_dt * m_dCompPerfRate[iperf][ke][ic][CP_Deriv::dC+jc]; + localPerfJacobian[TAG::WELL * numComp + ic][localDofIndexComp] = -m_dt * m_dCompPerfRate[iperf][ke][ic][CP_Deriv::dC+jc]; + } + if constexpr ( IS_THERMAL ) + { + localIndex localDofIndexTemp = localDofIndexPres + NC + 1; + localPerfJacobian[TAG::RES * numComp + ic][localDofIndexTemp] = m_dt * m_dCompPerfRate[iperf][ke][ic][CP_Deriv::dT]; + localPerfJacobian[TAG::WELL * numComp + ic][localDofIndexTemp] = -m_dt * m_dCompPerfRate[iperf][ke][ic][CP_Deriv::dT]; + } + } + } + + if( m_useTotalMassEquation ) + { + // Apply equation/variable change transformation(s) + stackArray1d< real64, 2 * resNumDOF > work( 2 * resNumDOF ); + shiftBlockRowsAheadByOneAndReplaceFirstRowWithColumnSum( numComp, numComp, resNumDOF * 2, 2, localPerfJacobian, work ); + shiftBlockElementsAheadByOneAndReplaceFirstElementWithSum( numComp, numComp, 2, localPerf ); + } + + for( localIndex i = 0; i < localPerf.size(); ++i ) + { + if( eqnRowIndices[i] >= 0 && eqnRowIndices[i] < m_localMatrix.numRows() ) + { + m_localMatrix.addToRowBinarySearchUnsorted< parallelDeviceAtomic >( eqnRowIndices[i], + dofColIndices.data(), + localPerfJacobian[i].dataIfContiguous(), + 2 * resNumDOF ); + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[eqnRowIndices[i]], localPerf[i] ); + } + } + compFluxKernelOp( resOffset, wellElemOffset, dofColIndices, iwelem ); + + } + + + /** + * @brief Performs the kernel launch + * @tparam POLICY the policy used in the RAJA kernels + * @tparam KERNEL_TYPE the kernel type + * @param[in] numElements the number of elements + * @param[inout] kernelComponent the kernel component providing access to setup/compute/complete functions and stack + * variables + */ + template< typename POLICY, typename KERNEL_TYPE > + static void + launch( localIndex const numElements, + KERNEL_TYPE const & kernelComponent ) + { + GEOS_MARK_FUNCTION; + forAll< POLICY >( numElements, [=] GEOS_HOST_DEVICE ( localIndex const ie ) + { + kernelComponent.computeFlux( ie ); + + } ); + } + +protected: + + /// Time step size + real64 const m_dt; + + /// Number of phases + integer const m_numPhases; + + globalIndex const m_rankOffset; + // Perfoation variables + arrayView2d< real64 const > const m_compPerfRate; + arrayView4d< real64 const > const m_dCompPerfRate; + arrayView1d< localIndex const > const m_perfWellElemIndex; + + // Element region, subregion, index + arrayView1d< globalIndex const > const m_wellElemDofNumber; + ElementRegionManager::ElementViewConst< arrayView1d< globalIndex const > > const m_resElemDofNumber; + arrayView1d< localIndex const > const m_resElementRegion; + arrayView1d< localIndex const > const m_resElementSubRegion; + arrayView1d< localIndex const > const m_resElementIndex; + + // RHS and Jacobian + arrayView1d< real64 > const m_localRhs; + CRSMatrixView< real64, globalIndex const > m_localMatrix; + + bool const m_detectCrossflow; + integer & m_numCrossFlowPerforations; + integer const m_useTotalMassEquation; +}; + +/** + * @class FaceBasedAssemblyKernelFactory + */ +class IsothermalCompositionalMultiPhaseFluxKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numComps the number of fluid components + * @param[in] dt time step size + * @param[in] rankOffset the offset of my MPI rank + * @param[in] useTotalMassEquation flag specifying whether to replace one component bal eqn with total mass eqn + * @param[in] dofKey string to get the element degrees of freedom numbers + * @param[in] wellControls object holding well control/constraint information + * @param[in] subregion well subregion + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY > + static void + createAndLaunch( integer const numComps, + real64 const dt, + globalIndex const rankOffset, + string const wellDofKey, + WellElementSubRegion const & subRegion, + ElementRegionManager::ElementViewConst< arrayView1d< globalIndex const > > const resDofNumber, + PerforationData const * const perforationData, + MultiFluidBase const & fluid, + integer const & useTotalMassEquation, + bool const & detectCrossflow, + integer & numCrossFlowPerforations, + arrayView1d< real64 > const & localRhs, + CRSMatrixView< real64, globalIndex const > const & localMatrix + ) + { + isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC ) + { + integer constexpr NUM_COMP = NC(); + + + BitFlags< isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags > kernelFlags; + if( useTotalMassEquation ) + kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags::TotalMassEquation ); + + + using kernelType = IsothermalCompositionalMultiPhaseFluxKernel< NUM_COMP, 0 >; + + + kernelType kernel( dt, rankOffset, wellDofKey, subRegion, resDofNumber, perforationData, fluid, localRhs, localMatrix, detectCrossflow, numCrossFlowPerforations, kernelFlags ); + kernelType::template launch< POLICY >( perforationData->size(), kernel ); + } ); + + } +}; + + +/** + * @class FaceBasedAssemblyKernel + * @tparam NUM_COMP number of fluid components + * @brief Define the interface for the assembly kernel in charge of flux terms + */ +template< integer NC, integer IS_THERMAL > +class ThermalCompositionalMultiPhaseFluxKernel : public IsothermalCompositionalMultiPhaseFluxKernel< NC, IS_THERMAL > +{ +public: + using Base = IsothermalCompositionalMultiPhaseFluxKernel< NC, IS_THERMAL >; + /// Compile time value for the number of components + static constexpr integer numComp = NC; + static constexpr integer resNumDOF = NC+1+IS_THERMAL; + + // Well jacobian column and row indicies + using WJ_COFFSET = compositionalMultiphaseWellKernels::ColOffset_WellJac< NC, IS_THERMAL >; + using WJ_ROFFSET = compositionalMultiphaseWellKernels::RowOffset_WellJac< NC, IS_THERMAL >; + + using ROFFSET = compositionalMultiphaseWellKernels::RowOffset; + using COFFSET = compositionalMultiphaseWellKernels::ColOffset; + + using CP_Deriv = multifluid::DerivativeOffsetC< NC, IS_THERMAL >; + + using TAG = compositionalMultiphaseWellKernels::SubRegionTag; + + using Base::m_dt; + using Base::m_localRhs; + using Base::m_localMatrix; + using Base::m_rankOffset; + + + + /// Compute time value for the number of degrees of freedom + static constexpr integer numDof = WJ_COFFSET::nDer; + + /// Compile time value for the number of equations except volume and momentum + static constexpr integer numEqn = WJ_ROFFSET::nEqn - 2; + + /** + * @brief Constructor for the kernel interface + * @param[in] rankOffset the offset of my MPI rank + * @param[in] stencilWrapper reference to the stencil wrapper + * @param[in] dofNumberAccessor + * @param[in] compFlowAccessors + * @param[in] multiFluidAccessors + * @param[in] capPressureAccessors + * @param[in] permeabilityAccessors + * @param[in] dt time step size + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + * @param[in] kernelFlags flags packed together + */ + ThermalCompositionalMultiPhaseFluxKernel( real64 const dt, + integer const isProducer, + globalIndex const rankOffset, + string const wellDofKey, + WellElementSubRegion const & subRegion, + ElementRegionManager::ElementViewConst< arrayView1d< globalIndex const > > const resDofNumber, + PerforationData const * const perforationData, + MultiFluidBase const & fluid, + arrayView1d< real64 > const & localRhs, + CRSMatrixView< real64, globalIndex const > const & localMatrix, + bool const & detectCrossflow, + integer & numCrossFlowPerforations, + BitFlags< isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags > kernelFlags ) + : Base( dt, + rankOffset, + wellDofKey, + subRegion, + resDofNumber, + perforationData, + fluid, + localRhs, + localMatrix, + detectCrossflow, + numCrossFlowPerforations, + kernelFlags ), + m_isProducer( isProducer ), + m_globalWellElementIndex( subRegion.getGlobalWellElementIndex() ), + m_energyPerfFlux( perforationData->getField< fields::well::energyPerforationFlux >()), + m_dEnergyPerfFlux( perforationData->getField< fields::well::dEnergyPerforationFlux >()) + + { } + + + /** + * @brief Compute the local flux contributions to the residual and Jacobian + * @tparam FUNC the type of the function that can be used to customize the computation of the phase fluxes + * @param[in] ie the element index + * @param[inout] stack the stack variables + * @param[in] compFluxKernelOp the function used to customize the computation of the component fluxes + */ + + GEOS_HOST_DEVICE + inline + void computeFlux( localIndex const iperf ) const + { + Base::computeFlux( iperf, [&] ( globalIndex const & resOffset, + globalIndex const & wellElemOffset, + stackArray1d< globalIndex, 2*resNumDOF > & dofColIndices, + localIndex const iwelem ) + { + // No energy equation if top element and Injector + // Top element defined by global index == 0 + // Assumption is global index == 0 is top segment with fixed temp BC + if( !m_isProducer ) + { + if( m_globalWellElementIndex[iwelem] == 0 ) + return; + } + // local working variables and arrays + stackArray1d< localIndex, 2* numComp > eqnRowIndices( 2 ); + + stackArray1d< real64, 2 * numComp > localPerf( 2 ); + stackArray2d< real64, 2 * resNumDOF * 2 * numComp > localPerfJacobian( 2, 2 * resNumDOF ); + + + // equantion offsets - note res and well have different equation lineups + eqnRowIndices[TAG::RES ] = LvArray::integerConversion< localIndex >( resOffset - m_rankOffset ) + NC + 1; + eqnRowIndices[TAG::WELL ] = LvArray::integerConversion< localIndex >( wellElemOffset - m_rankOffset ) + WJ_ROFFSET::ENERGYBAL; + + // populate local flux vector and derivatives + localPerf[TAG::RES ] = m_dt * m_energyPerfFlux[iperf]; + localPerf[TAG::WELL ] = -m_dt * m_energyPerfFlux[iperf]; + + for( integer ke = 0; ke < 2; ++ke ) + { + localIndex localDofIndexPres = ke * resNumDOF; + localPerfJacobian[TAG::RES ][localDofIndexPres] = m_dt * m_dEnergyPerfFlux[iperf][ke][CP_Deriv::dP]; + localPerfJacobian[TAG::WELL ][localDofIndexPres] = -m_dt * m_dEnergyPerfFlux[iperf][ke][CP_Deriv::dP]; + + // populate local flux vector and derivatives + for( integer ic = 0; ic < numComp; ++ic ) + { + localIndex const localDofIndexComp = localDofIndexPres + ic + 1; + localPerfJacobian[TAG::RES ][localDofIndexComp] = m_dt * m_dEnergyPerfFlux[iperf][ke][CP_Deriv::dC+ic]; + localPerfJacobian[TAG::WELL][localDofIndexComp] = -m_dt * m_dEnergyPerfFlux[iperf][ke][CP_Deriv::dC+ic]; + } + localPerfJacobian[TAG::RES ][localDofIndexPres+NC+1] = m_dt * m_dEnergyPerfFlux[iperf][ke][CP_Deriv::dT]; + localPerfJacobian[TAG::WELL][localDofIndexPres+NC+1] = -m_dt * m_dEnergyPerfFlux[iperf][ke][CP_Deriv::dT]; + } + + + for( localIndex i = 0; i < localPerf.size(); ++i ) + { + if( eqnRowIndices[i] >= 0 && eqnRowIndices[i] < m_localMatrix.numRows() ) + { + m_localMatrix.template addToRowBinarySearchUnsorted< parallelDeviceAtomic >( eqnRowIndices[i], + dofColIndices.data(), + localPerfJacobian[i].dataIfContiguous(), + 2 * resNumDOF ); + RAJA::atomicAdd( parallelDeviceAtomic{}, &m_localRhs[eqnRowIndices[i]], localPerf[i] ); + } + } + } ); + + + } + + + /** + * @brief Performs the kernel launch + * @tparam POLICY the policy used in the RAJA kernels + * @tparam KERNEL_TYPE the kernel type + * @param[in] numElements the number of elements + * @param[inout] kernelComponent the kernel component providing access to setup/compute/complete functions and stack + * variables + */ + template< typename POLICY, typename KERNEL_TYPE > + static void + launch( localIndex const numElements, + KERNEL_TYPE const & kernelComponent ) + { + GEOS_MARK_FUNCTION; + forAll< POLICY >( numElements, [=] GEOS_HOST_DEVICE ( localIndex const ie ) + { + kernelComponent.computeFlux( ie ); + + } ); + } + +protected: + + /// Well type + integer const m_isProducer; + + /// Global index of local element + arrayView1d< globalIndex const > m_globalWellElementIndex; + + /// Views on energy flux + arrayView1d< real64 const > const m_energyPerfFlux; + arrayView3d< real64 const > const m_dEnergyPerfFlux; +}; + +/** + * @class ThermalCompositionalMultiPhaseFluxKernelFactory + */ +class ThermalCompositionalMultiPhaseFluxKernelFactory +{ +public: + + /** + * @brief Create a new kernel and launch + * @tparam POLICY the policy used in the RAJA kernel + * @param[in] numComps the number of fluid components + * @param[in] dt time step size + * @param[in] rankOffset the offset of my MPI rank + * @param[in] useTotalMassEquation flag specifying whether to replace one component bal eqn with total mass eqn + * @param[in] dofKey string to get the element degrees of freedom numbers + * @param[in] wellControls object holding well control/constraint information + * @param[in] subregion well subregion + * @param[inout] localMatrix the local CRS matrix + * @param[inout] localRhs the local right-hand side vector + */ + template< typename POLICY > + static void + createAndLaunch( integer const numComps, + integer const isProducer, + real64 const dt, + globalIndex const rankOffset, + string const wellDofKey, + WellElementSubRegion const & subRegion, + ElementRegionManager::ElementViewConst< arrayView1d< globalIndex const > > const resDofNumber, + PerforationData const * const perforationData, + MultiFluidBase const & fluid, + integer const & useTotalMassEquation, + bool const & detectCrossflow, + integer & numCrossFlowPerforations, + arrayView1d< real64 > const & localRhs, + CRSMatrixView< real64, globalIndex const > const & localMatrix + ) + { + isothermalCompositionalMultiphaseBaseKernels::internal::kernelLaunchSelectorCompSwitch( numComps, [&]( auto NC ) + { + integer constexpr NUM_COMP = NC(); + + + BitFlags< isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags > kernelFlags; + if( useTotalMassEquation ) + kernelFlags.set( isothermalCompositionalMultiphaseBaseKernels::ElementBasedAssemblyKernelFlags::TotalMassEquation ); + + + using kernelType = ThermalCompositionalMultiPhaseFluxKernel< NUM_COMP, 1 >; + + + kernelType kernel( dt, isProducer, rankOffset, wellDofKey, subRegion, resDofNumber, perforationData, fluid, localRhs, localMatrix, detectCrossflow, numCrossFlowPerforations, kernelFlags ); + kernelType::template launch< POLICY >( perforationData->size(), kernel ); + } ); + + } +}; + +} // end namespace coupledReservoirAndWellKernels + +} // end namespace geos + +#endif // GEOS_PHYSICSSOLVERS_MULTIPHYSICS_COUPLEDRESERVOIRANDWELLS_HPP diff --git a/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.cpp b/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.cpp index 34932b61aba..4e3dacbd699 100644 --- a/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.cpp +++ b/src/coreComponents/physicsSolvers/multiphysics/SinglePhaseReservoirAndWells.cpp @@ -22,6 +22,7 @@ #include "common/TimingMacros.hpp" #include "mesh/PerforationFields.hpp" +#include "physicsSolvers/KernelLaunchSelectors.hpp" #include "physicsSolvers/fluidFlow/SinglePhaseFVM.hpp" #include "physicsSolvers/fluidFlow/wells/SinglePhaseWellFields.hpp" #include "physicsSolvers/fluidFlow/wells/SinglePhaseWellKernels.hpp" @@ -137,73 +138,75 @@ addCouplingSparsityPattern( DomainPartition const & domain, string const resDofKey = dofManager.getKey( Base::wellSolver()->resElementDofName() ); string const wellDofKey = dofManager.getKey( Base::wellSolver()->wellElementDofName() ); - + integer isThermal = Base::wellSolver()->isThermal(); integer const wellNDOF = Base::wellSolver()->numDofPerWellElement(); ElementRegionManager::ElementViewAccessor< arrayView1d< globalIndex const > > const & resDofNumber = elemManager.constructArrayViewAccessor< globalIndex, 1 >( resDofKey ); globalIndex const rankOffset = dofManager.rankOffset(); - - elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, - WellElementSubRegion const & subRegion ) - { - PerforationData const * const perforationData = subRegion.getPerforationData(); - - // get the well degrees of freedom and ghosting info - arrayView1d< globalIndex const > const & wellElemDofNumber = - subRegion.getReference< array1d< globalIndex > >( wellDofKey ); - - // get the well element indices corresponding to each perforation - arrayView1d< localIndex const > const & perfWellElemIndex = - perforationData->getField< fields::perforation::wellElementIndex >(); - - // get the element region, subregion, index - arrayView1d< localIndex const > const & resElementRegion = - perforationData->getField< fields::perforation::reservoirElementRegion >(); - arrayView1d< localIndex const > const & resElementSubRegion = - perforationData->getField< fields::perforation::reservoirElementSubRegion >(); - arrayView1d< localIndex const > const & resElementIndex = - perforationData->getField< fields::perforation::reservoirElementIndex >(); - - // Insert the entries corresponding to reservoir-well perforations - // This will fill J_WR, and J_RW - forAll< serialPolicy >( perforationData->size(), [=] ( localIndex const iperf ) + geos::internal::kernelLaunchSelectorThermalSwitch( isThermal, [&] ( auto ISTHERMAL ) { + integer constexpr IS_THERMAL = ISTHERMAL(); + elemManager.forElementSubRegions< WellElementSubRegion >( regionNames, [&]( localIndex const, + WellElementSubRegion const & subRegion ) { - // Get the reservoir (sub)region and element indices - localIndex const er = resElementRegion[iperf]; - localIndex const esr = resElementSubRegion[iperf]; - localIndex const ei = resElementIndex[iperf]; - localIndex const iwelem = perfWellElemIndex[iperf]; + PerforationData const * const perforationData = subRegion.getPerforationData(); + + // get the well degrees of freedom and ghosting info + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + + // get the well element indices corresponding to each perforation + arrayView1d< localIndex const > const & perfWellElemIndex = + perforationData->getField< fields::perforation::wellElementIndex >(); + + // get the element region, subregion, index + arrayView1d< localIndex const > const & resElementRegion = + perforationData->getField< fields::perforation::reservoirElementRegion >(); + arrayView1d< localIndex const > const & resElementSubRegion = + perforationData->getField< fields::perforation::reservoirElementSubRegion >(); + arrayView1d< localIndex const > const & resElementIndex = + perforationData->getField< fields::perforation::reservoirElementIndex >(); + + // Insert the entries corresponding to reservoir-well perforations + // This will fill J_WR, and J_RW + forAll< serialPolicy >( perforationData->size(), [=] ( localIndex const iperf ) + { + // Get the reservoir (sub)region and element indices + localIndex const er = resElementRegion[iperf]; + localIndex const esr = resElementSubRegion[iperf]; + localIndex const ei = resElementIndex[iperf]; + localIndex const iwelem = perfWellElemIndex[iperf]; - globalIndex const eqnRowIndexRes = resDofNumber[er][esr][ei] - rankOffset; - globalIndex const dofColIndexRes = resDofNumber[er][esr][ei]; + globalIndex const eqnRowIndexRes = resDofNumber[er][esr][ei] - rankOffset; + globalIndex const dofColIndexRes = resDofNumber[er][esr][ei]; - // working arrays - stackArray1d< globalIndex, 2 > eqnRowIndicesWell( wellNDOF ); - stackArray1d< globalIndex, 2 > dofColIndicesWell( wellNDOF ); + // working arrays - tjb previously dim was 2 + stackArray1d< globalIndex, 2+IS_THERMAL > eqnRowIndicesWell( wellNDOF ); + stackArray1d< globalIndex, 2+IS_THERMAL > dofColIndicesWell( wellNDOF ); - for( integer idof = 0; idof < wellNDOF; ++idof ) - { - eqnRowIndicesWell[idof] = wellElemDofNumber[iwelem] + idof - rankOffset; - dofColIndicesWell[idof] = wellElemDofNumber[iwelem] + idof; - } + for( integer idof = 0; idof < wellNDOF; ++idof ) + { + eqnRowIndicesWell[idof] = wellElemDofNumber[iwelem] + idof - rankOffset; + dofColIndicesWell[idof] = wellElemDofNumber[iwelem] + idof; + } - if( eqnRowIndexRes >= 0 && eqnRowIndexRes < pattern.numRows() ) - { - for( localIndex j = 0; j < dofColIndicesWell.size(); ++j ) + if( eqnRowIndexRes >= 0 && eqnRowIndexRes < pattern.numRows() ) { - pattern.insertNonZero( eqnRowIndexRes, dofColIndicesWell[j] ); + for( localIndex j = 0; j < dofColIndicesWell.size(); ++j ) + { + pattern.insertNonZero( eqnRowIndexRes, dofColIndicesWell[j] ); + } } - } - for( localIndex i = 0; i < eqnRowIndicesWell.size(); ++i ) - { - if( eqnRowIndicesWell[i] >= 0 && eqnRowIndicesWell[i] < pattern.numRows() ) + for( localIndex i = 0; i < eqnRowIndicesWell.size(); ++i ) { - pattern.insertNonZero( eqnRowIndicesWell[i], dofColIndexRes ); + if( eqnRowIndicesWell[i] >= 0 && eqnRowIndicesWell[i] < pattern.numRows() ) + { + pattern.insertNonZero( eqnRowIndicesWell[i], dofColIndexRes ); + } } - } + } ); } ); } ); } ); diff --git a/src/coreComponents/schema/schema.xsd b/src/coreComponents/schema/schema.xsd index 6c137c9f37f..f6d771d5a78 100644 --- a/src/coreComponents/schema/schema.xsd +++ b/src/coreComponents/schema/schema.xsd @@ -2758,6 +2758,8 @@ Local- Add jump stabilization on interior of macro elements--> + + + + @@ -3987,6 +3991,8 @@ Local- Add jump stabilization on interior of macro elements--> + + + + diff --git a/src/coreComponents/unitTests/fluidFlowTests/testCompFlowUtils.hpp b/src/coreComponents/unitTests/fluidFlowTests/testCompFlowUtils.hpp index a69995c1166..791e5fe8607 100644 --- a/src/coreComponents/unitTests/fluidFlowTests/testCompFlowUtils.hpp +++ b/src/coreComponents/unitTests/fluidFlowTests/testCompFlowUtils.hpp @@ -50,6 +50,19 @@ void fillNumericalJacobian( arrayView1d< real64 const > const & residual, } ); } +inline +void setNumericalJacobianValue( localIndex const rowIndex, + globalIndex const colIndex, + real64 const val, + CRSMatrixView< real64, const globalIndex > const jacobian ) +{ + forAll< parallelDevicePolicy<> >( 1, [=] GEOS_HOST_DEVICE ( localIndex const k ) + { + GEOS_UNUSED_VAR( k ); + jacobian.addToRow< parallelDeviceAtomic >( rowIndex, &colIndex, &val, 1 ); + } ); +} + inline void setupProblemFromXML( ProblemManager & problemManager, char const * const xmlInput ) { diff --git a/src/coreComponents/unitTests/wellsTests/CMakeLists.txt b/src/coreComponents/unitTests/wellsTests/CMakeLists.txt index 988582e06a6..1420b47ec78 100644 --- a/src/coreComponents/unitTests/wellsTests/CMakeLists.txt +++ b/src/coreComponents/unitTests/wellsTests/CMakeLists.txt @@ -9,7 +9,11 @@ set( dependencyList mainInterface ) if( ENABLE_PVTPackage ) list( APPEND gtest_geosx_tests - testReservoirCompositionalMultiphaseMSWells.cpp ) + testReservoirCompositionalMultiphaseMSWells.cpp + testIsothermalReservoirCompositionalMultiphaseMSWells.cpp + testIsothermalReservoirCompositionalMultiphaseSSWells.cpp + testThermalReservoirCompositionalMultiphaseSSWells.cpp + testThermalReservoirCompositionalMultiphaseMSWells.cpp ) endif() geos_decorate_link_dependencies( LIST decoratedDependencies diff --git a/src/coreComponents/unitTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp b/src/coreComponents/unitTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp new file mode 100644 index 00000000000..84362ec0698 --- /dev/null +++ b/src/coreComponents/unitTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseMSWells.cpp @@ -0,0 +1,677 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +#include "unitTests/fluidFlowTests/testCompFlowUtils.hpp" + +#include "common/DataTypes.hpp" +#include "mainInterface/initialization.hpp" +#include "constitutive/fluid/multifluid/MultiFluidBase.hpp" +#include "mainInterface/ProblemManager.hpp" +#include "mesh/DomainPartition.hpp" +#include "mainInterface/GeosxState.hpp" +#include "mesh/WellElementSubRegion.hpp" +#include "physicsSolvers/PhysicsSolverManager.hpp" +#include "physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp" +#include "physicsSolvers/fluidFlow/CompositionalMultiphaseFVM.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellKernels.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp" +#include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp" + +using namespace geos; +using namespace geos::dataRepository; +using namespace geos::constitutive; +using namespace geos::testing; + +CommandLineOptions g_commandLineOptions; +void writeTableToFile( string const & filename, char const * str ) +{ + std::ofstream os( filename ); + ASSERT_TRUE( os.is_open() ); + os << str; + os.close(); +} + +void removeFile( string const & filename ) +{ + int const ret = std::remove( filename.c_str() ); + ASSERT_TRUE( ret == 0 ); +} +char const * co2flash = "FlashModel CO2Solubility 1e6 7.5e7 5e5 299.15 369.15 10 0"; +char const * pvtLiquid = "DensityFun PhillipsBrineDensity 1e6 7.5e7 5e5 299.15 369.15 10 0\n" + "ViscosityFun PhillipsBrineViscosity 0\n" + "EnthalpyFun BrineEnthalpy 1e6 7.5e7 5e5 299.15 369.15 10 0\n"; + +char const * pvtGas = "DensityFun SpanWagnerCO2Density 1e6 7.5e7 5e5 299.15 369.15 10\n" + "ViscosityFun FenghourCO2Viscosity 1e6 7.5e7 5e5 299.15 369.15 10\n" + "EnthalpyFun CO2Enthalpy 1e6 7.5e7 5e5 299.15 369.15 10\n"; +char const * xmlInput = + R"xml( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )xml"; + +template< typename LAMBDA > +void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > & solver, + DomainPartition & domain, + real64 const perturbParameter, + real64 const relTol, + LAMBDA && assembleFunction ) +{ + CompositionalMultiphaseWell & wellSolver = *solver.wellSolver(); + CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() ); + + localIndex const NC = flowSolver.numFluidComponents(); + + CRSMatrix< real64, globalIndex > const & jacobian = solver.getLocalMatrix(); + array1d< real64 > residual( jacobian.numRows() ); + DofManager const & dofManager = solver.getDofManager(); + + // assemble the analytical residual + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + residual.move( hostMemorySpace, false ); + + // copy the analytical residual + array1d< real64 > residualOrig( residual ); + + // create the numerical jacobian + jacobian.move( hostMemorySpace ); + CRSMatrix< real64, globalIndex > jacobianFD( jacobian ); + jacobianFD.zero(); + + string const resDofKey = dofManager.getKey( wellSolver.resElementDofName() ); + string const wellDofKey = dofManager.getKey( wellSolver.wellElementDofName() ); + + // at this point we start assembling the finite-difference block by block + + //////////////////////////////////////////////// + // Step 1) Compute the terms in J_RR and J_WR // + //////////////////////////////////////////////// + domain.forMeshBodies( [&] ( MeshBody & meshBody ) + { + meshBody.forMeshLevels( [&] ( MeshLevel & mesh ) + { + ElementRegionManager & elemManager = mesh.getElemManager(); + for( localIndex er = 0; er < elemManager.numRegions(); ++er ) + { + ElementRegionBase & elemRegion = elemManager.getRegion( er ); + elemRegion.forElementSubRegionsIndex< CellElementSubRegion >( [&]( localIndex const, CellElementSubRegion & subRegion ) + { + // get the degrees of freedom and ghosting information + arrayView1d< globalIndex const > const & dofNumber = + subRegion.getReference< array1d< globalIndex > >( resDofKey ); + + // get the primary variables on the reservoir elements + arrayView1d< real64 > const & pres = + subRegion.getField< fields::well::pressure >(); + pres.move( hostMemorySpace, false ); + + arrayView2d< real64, compflow::USD_COMP > const & compDens = + subRegion.getField< fields::well::globalCompDensity >(); + compDens.move( hostMemorySpace, false ); + + // a) compute all the derivatives wrt to the pressure in RESERVOIR elem ei + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + real64 totalDensity = 0.0; + for( localIndex ic = 0; ic < NC; ++ic ) + { + totalDensity += compDens[ei][ic]; + } + + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the element + real64 const dP = perturbParameter * (pres[ei] + perturbParameter); + pres.move( hostMemorySpace, true ); + pres[ei] += dP; + + // after perturbing, update the pressure-dependent quantities in the reservoir + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + arrayView1d< string const > const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei], + dP, + jacobianFD.toViewConstSizes() ); + } + + for( localIndex jc = 0; jc < NC; ++jc ) + { + solver.resetStateToBeginningOfStep( domain ); + + real64 const dRho = perturbParameter * totalDensity; + compDens.move( hostMemorySpace, true ); + compDens[ei][jc] += dRho; + + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + arrayView1d< string const > const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei] + jc + 1, + dRho, + jacobianFD.toViewConstSizes() ); + } + } + } ); + } + } ); + } ); + + ///////////////////////////////////////////////// + // Step 2) Compute the terms in J_RW and J_WW // + ///////////////////////////////////////////////// + + // loop over the wells + wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + // get the degrees of freedom, ghosting info and next well elem index + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + + // get the primary variables on the well elements + arrayView1d< real64 > const & wellElemPressure = + subRegion.getField< fields::well::pressure >(); + wellElemPressure.move( hostMemorySpace, false ); + + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = + subRegion.getField< fields::well::globalCompDensity >(); + wellElemCompDens.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & connRate = + subRegion.getField< fields::well::mixtureConnectionRate >(); + connRate.move( hostMemorySpace, false ); + + // a) compute all the derivatives wrt to the pressure in WELL elem iwelem + for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem ) + { + real64 wellElemTotalDensity = 0.0; + for( localIndex ic = 0; ic < NC; ++ic ) + { + wellElemTotalDensity += wellElemCompDens[iwelem][ic]; + } + + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the well element + real64 const dP = perturbParameter * ( wellElemPressure[iwelem] + perturbParameter ); + wellElemPressure.move( hostMemorySpace, true ); + wellElemPressure[iwelem] += dP; + + // after perturbing, update the pressure-dependent quantities in the well + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DPRES, + dP, + jacobianFD.toViewConstSizes() ); + } + + for( localIndex jc = 0; jc < NC; ++jc ) + { + solver.resetStateToBeginningOfStep( domain ); + + real64 const dRho = perturbParameter * wellElemTotalDensity; + wellElemCompDens.move( hostMemorySpace, true ); + wellElemCompDens[iwelem][jc] += dRho; + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + jc, + dRho, + jacobianFD.toViewConstSizes() ); + } + } + + // b) compute all the derivatives wrt to the connection in WELL elem iwelem + for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem ) + { + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the rate of the well element + real64 const dRate = perturbParameter * ( connRate[iwelem] + perturbParameter ); + connRate.move( hostMemorySpace, true ); + connRate[iwelem] += dRate; + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC, + dRate, + jacobianFD.toViewConstSizes() ); + } + } + } ); + } ); + + // assemble the analytical jacobian + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + //printCompareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst()); + compareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), relTol ); +} + +class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test +{ +public: + + CompositionalMultiphaseReservoirSolverTest(): + state( std::make_unique< CommandLineOptions >( g_commandLineOptions ) ) + {} + +protected: + + void SetUp() override + { + setupProblemFromXML( state.getProblemManager(), xmlInput ); + solver = &state.getProblemManager().getPhysicsSolverManager().getGroup< CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > >( "reservoirSystem" ); + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + solver->setupSystem( domain, + solver->getDofManager(), + solver->getLocalMatrix(), + solver->getSystemRhs(), + solver->getSystemSolution() ); + + solver->implicitStepSetup( time, dt, domain ); + } + + static real64 constexpr time = 0.0; + static real64 constexpr dt = 1e4; + static real64 constexpr eps = std::numeric_limits< real64 >::epsilon(); + + GeosxState state; + CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > * solver; +}; + +real64 constexpr CompositionalMultiphaseReservoirSolverTest::time; +real64 constexpr CompositionalMultiphaseReservoirSolverTest::dt; +real64 constexpr CompositionalMultiphaseReservoirSolverTest::eps; + +#if 0 + +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Perforation ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = *state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->assembleCouplingTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} + +#endif + +#if 0 +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Flux ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->wellSolver()->assembleFluxTerms( dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} +#endif + +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Accum ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->wellSolver()->assembleAccumulationTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} + +#if 0 +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_PressureRel ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->wellSolver()->assemblePressureRelations( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} +#endif + +int main( int argc, char * * argv ) +{ + writeTableToFile( "co2flash.txt", co2flash ); + writeTableToFile( "pvtliquid.txt", pvtLiquid ); + writeTableToFile( "pvtgas.txt", pvtGas ); + ::testing::InitGoogleTest( &argc, argv ); + g_commandLineOptions = *geos::basicSetup( argc, argv ); + int const result = RUN_ALL_TESTS(); + geos::basicCleanup(); + removeFile( "co2flash.txt" ); + removeFile( "pvtliquid.txt" ); + removeFile( "pvtgas.txt" ); + return result; +} diff --git a/src/coreComponents/unitTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp b/src/coreComponents/unitTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp new file mode 100644 index 00000000000..b7b63b1033e --- /dev/null +++ b/src/coreComponents/unitTests/wellsTests/testIsothermalReservoirCompositionalMultiphaseSSWells.cpp @@ -0,0 +1,769 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +#include "unitTests/fluidFlowTests/testCompFlowUtils.hpp" + +#include "common/DataTypes.hpp" +#include "mainInterface/initialization.hpp" +#include "constitutive/fluid/multifluid/MultiFluidBase.hpp" +#include "mainInterface/ProblemManager.hpp" +#include "mesh/DomainPartition.hpp" +#include "mainInterface/GeosxState.hpp" +#include "mesh/WellElementSubRegion.hpp" +#include "physicsSolvers/PhysicsSolverManager.hpp" +#include "physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp" +#include "physicsSolvers/fluidFlow/CompositionalMultiphaseFVM.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellKernels.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp" +#include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp" + +using namespace geos; +using namespace geos::dataRepository; +using namespace geos::constitutive; +using namespace geos::testing; + +CommandLineOptions g_commandLineOptions; +void writeTableToFile( string const & filename, char const * str ) +{ + std::ofstream os( filename ); + ASSERT_TRUE( os.is_open() ); + os << str; + os.close(); +} + +void removeFile( string const & filename ) +{ + int const ret = std::remove( filename.c_str() ); + ASSERT_TRUE( ret == 0 ); +} +char const * co2flash = "FlashModel CO2Solubility 1e6 7.5e7 5e5 299.15 369.15 10 0"; +char const * pvtLiquid = "DensityFun PhillipsBrineDensity 1e6 7.5e7 5e5 299.15 369.15 10 0\n" + "ViscosityFun PhillipsBrineViscosity 0\n" + "EnthalpyFun BrineEnthalpy 1e6 7.5e7 5e5 299.15 369.15 10 0\n"; + +char const * pvtGas = "DensityFun SpanWagnerCO2Density 1e6 7.5e7 5e5 299.15 369.15 10\n" + "ViscosityFun FenghourCO2Viscosity 1e6 7.5e7 5e5 299.15 369.15 10\n" + "EnthalpyFun CO2Enthalpy 1e6 7.5e7 5e5 299.15 369.15 10\n"; +char const * xmlInput = + R"xml( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )xml"; + + + +template< typename LAMBDA > +void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > & solver, + DomainPartition & domain, + real64 const perturbParameter, + real64 const relTol, + LAMBDA && assembleFunction ) +{ + CompositionalMultiphaseWell & wellSolver = *solver.wellSolver(); + CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() ); + + localIndex const NC = flowSolver.numFluidComponents(); + + CRSMatrix< real64, globalIndex > const & jacobian = solver.getLocalMatrix(); + array1d< real64 > residual( jacobian.numRows() ); + DofManager const & dofManager = solver.getDofManager(); + + // assemble the analytical residual + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + residual.move( hostMemorySpace, false ); + + // copy the analytical residual + array1d< real64 > residualOrig( residual ); + + // create the numerical jacobian + jacobian.move( hostMemorySpace ); + CRSMatrix< real64, globalIndex > jacobianFD( jacobian ); + jacobianFD.zero(); + + string const resDofKey = dofManager.getKey( wellSolver.resElementDofName() ); + string const wellDofKey = dofManager.getKey( wellSolver.wellElementDofName() ); + + // at this point we start assembling the finite-difference block by block + + //////////////////////////////////////////////// + // Step 1) Compute the terms in J_RR and J_WR // + //////////////////////////////////////////////// + if( 1 ) + domain.forMeshBodies( [&] ( MeshBody & meshBody ) + { + meshBody.forMeshLevels( [&] ( MeshLevel & mesh ) + { + ElementRegionManager & elemManager = mesh.getElemManager(); + for( localIndex er = 0; er < elemManager.numRegions(); ++er ) + { + ElementRegionBase & elemRegion = elemManager.getRegion( er ); + elemRegion.forElementSubRegionsIndex< CellElementSubRegion >( [&]( localIndex const, CellElementSubRegion & subRegion ) + { + // get the degrees of freedom and ghosting information + arrayView1d< globalIndex const > const & dofNumber = + subRegion.getReference< array1d< globalIndex > >( resDofKey ); + + // get the primary variables on the reservoir elements + arrayView1d< real64 > const & pres = + subRegion.getField< fields::well::pressure >(); + pres.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & temp = + subRegion.getField< fields::well::temperature >(); + temp.move( hostMemorySpace, false ); + + arrayView2d< real64, compflow::USD_COMP > const & compDens = + subRegion.getField< fields::well::globalCompDensity >(); + compDens.move( hostMemorySpace, false ); + + // a) compute all the derivatives wrt to the pressure in RESERVOIR elem ei + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + real64 totalDensity = 0.0; + for( localIndex ic = 0; ic < NC; ++ic ) + { + totalDensity += compDens[ei][ic]; + } + + if( 1 ) + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the element + real64 const dP = perturbParameter * (pres[ei] + perturbParameter); + pres.move( hostMemorySpace, true ); + pres[ei] += dP; + + // after perturbing, update the pressure-dependent quantities in the reservoir + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + arrayView1d< string const > const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei], + dP, + jacobianFD.toViewConstSizes() ); + } + + if( 0 ) + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the temperature of the element + real64 const dT = perturbParameter * (temp[ei] + perturbParameter); + temp.move( hostMemorySpace, true ); + temp[ei] += dT; + + // after perturbing, update the pressure-dependent quantities in the reservoir + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + arrayView1d< string const > const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei], + dT, + jacobianFD.toViewConstSizes() ); + } + + + for( localIndex jc = 0; jc < NC; ++jc ) + { + solver.resetStateToBeginningOfStep( domain ); + + real64 const dRho = perturbParameter * totalDensity; + compDens.move( hostMemorySpace, true ); + compDens[ei][jc] += dRho; + + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + arrayView1d< string const > const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei] + jc + 1, + dRho, + jacobianFD.toViewConstSizes() ); + } + } + } ); + } + } ); + } ); + + ///////////////////////////////////////////////// + // Step 2) Compute the terms in J_RW and J_WW // + ///////////////////////////////////////////////// + + // loop over the wells + wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + // get the degrees of freedom, ghosting info and next well elem index + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + + // get the primary variables on the well elements + arrayView1d< real64 > const & wellElemPressure = + subRegion.getField< fields::well::pressure >(); + wellElemPressure.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & wellElemTemperature = + subRegion.getField< fields::well::temperature >(); + wellElemTemperature.move( hostMemorySpace, false ); + + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = + subRegion.getField< fields::well::globalCompDensity >(); + wellElemCompDens.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & connRate = + subRegion.getField< fields::well::mixtureConnectionRate >(); + connRate.move( hostMemorySpace, false ); + + // a) compute all the derivatives wrt to the pressure in WELL elem iwelem + for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem ) + { + + real64 wellElemTotalDensity = 0.0; + for( localIndex ic = 0; ic < NC; ++ic ) + { + wellElemTotalDensity += wellElemCompDens[iwelem][ic]; + } + + if( 1 ) + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the well element + real64 const dP = perturbParameter * ( wellElemPressure[iwelem] + perturbParameter ); + wellElemPressure.move( hostMemorySpace, true ); + wellElemPressure[iwelem] += dP; + + // after perturbing, update the pressure-dependent quantities in the well + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DPRES, + dP, + jacobianFD.toViewConstSizes() ); + } + + for( localIndex jc = 0; jc < NC; ++jc ) + { + solver.resetStateToBeginningOfStep( domain ); + + real64 const dRho = perturbParameter*wellElemTotalDensity; + wellElemCompDens.move( hostMemorySpace, true ); + wellElemCompDens[iwelem][jc] += dRho; + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + jc, + dRho, + jacobianFD.toViewConstSizes() ); + } + if( 0 ) + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the well element + real64 const dT = perturbParameter * ( wellElemTemperature[iwelem] + perturbParameter ); + wellElemTemperature.move( hostMemorySpace, true ); + wellElemTemperature[iwelem] += dT; + + // after perturbing, update the pressure-dependent quantities in the well + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC+1, + dT, + jacobianFD.toViewConstSizes() ); + } + } + + // b) compute all the derivatives wrt to the connection in WELL elem iwelem + if( 1 ) + for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem ) + { + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the rate of the well element + real64 const dRate = perturbParameter * ( connRate[iwelem] + perturbParameter ); + connRate.move( hostMemorySpace, true ); + connRate[iwelem] += dRate; + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC, + dRate, + jacobianFD.toViewConstSizes() ); + } + } + } ); + } ); + + // assemble the analytical jacobian + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + //printCompareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst()); + compareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), relTol ); +} + +class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test +{ +public: + + CompositionalMultiphaseReservoirSolverTest(): + state( std::make_unique< CommandLineOptions >( g_commandLineOptions ) ) + {} + +protected: + + void SetUp() override + { + setupProblemFromXML( state.getProblemManager(), xmlInput ); + solver = &state.getProblemManager().getPhysicsSolverManager().getGroup< CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > >( "reservoirSystem" ); + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + solver->setupSystem( domain, + solver->getDofManager(), + solver->getLocalMatrix(), + solver->getSystemRhs(), + solver->getSystemSolution() ); + + solver->implicitStepSetup( time, dt, domain ); + } + + static real64 constexpr time = 0.0; + static real64 constexpr dt = 1e4; + static real64 constexpr eps = std::numeric_limits< real64 >::epsilon(); + + GeosxState state; + CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > * solver; +}; + +real64 constexpr CompositionalMultiphaseReservoirSolverTest::time; +real64 constexpr CompositionalMultiphaseReservoirSolverTest::dt; +real64 constexpr CompositionalMultiphaseReservoirSolverTest::eps; + +#if 0 + +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Perforation ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + //solver->assembleCouplingTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + solver->assembleSystem( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + //solver->assembleCouplingTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} + +#endif + +#if 1 +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Accum ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->wellSolver()->assembleAccumulationTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} +#endif + +#if 0 +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Flux ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->wellSolver()->assembleFluxTerms( dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} + +#endif +#if 0 + +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_VolumeBalance ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->wellSolver()->assembleVolumeBalanceTerms( domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} +#endif +#if 0 +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_PressureRel ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->wellSolver()->assemblePressureRelations( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} +#endif +int main( int argc, char * * argv ) +{ + writeTableToFile( "co2flash.txt", co2flash ); + writeTableToFile( "pvtliquid.txt", pvtLiquid ); + writeTableToFile( "pvtgas.txt", pvtGas ); + ::testing::InitGoogleTest( &argc, argv ); + g_commandLineOptions = *geos::basicSetup( argc, argv ); + int const result = RUN_ALL_TESTS(); + geos::basicCleanup(); + removeFile( "co2flash.txt" ); + removeFile( "pvtliquid.txt" ); + removeFile( "pvtgas.txt" ); + return result; +} diff --git a/src/coreComponents/unitTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp b/src/coreComponents/unitTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp index 4d3b4e775ba..f69e0b5cb2c 100644 --- a/src/coreComponents/unitTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp +++ b/src/coreComponents/unitTests/wellsTests/testReservoirCompositionalMultiphaseMSWells.cpp @@ -528,25 +528,11 @@ TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Flux [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { - solver->wellSolver()->assembleFluxTerms( dt, domain, solver->getDofManager(), localMatrix, localRhs ); + solver->wellSolver()->assembleFluxTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); } ); } -TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_VolumeBalance ) -{ - real64 const perturb = std::sqrt( eps ); - real64 const tol = 1e-1; // 10% error margin - - DomainPartition & domain = state.getProblemManager().getDomainPartition(); - - testNumericalJacobian( *solver, domain, perturb, tol, - [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, - arrayView1d< real64 > const & localRhs ) - { - solver->wellSolver()->assembleVolumeBalanceTerms( domain, solver->getDofManager(), localMatrix, localRhs ); - } ); -} TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_PressureRel ) { diff --git a/src/coreComponents/unitTests/wellsTests/testReservoirSinglePhaseMSWells.cpp b/src/coreComponents/unitTests/wellsTests/testReservoirSinglePhaseMSWells.cpp index 0835f9c45c4..c54c1419c40 100644 --- a/src/coreComponents/unitTests/wellsTests/testReservoirSinglePhaseMSWells.cpp +++ b/src/coreComponents/unitTests/wellsTests/testReservoirSinglePhaseMSWells.cpp @@ -385,7 +385,7 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { - solver->wellSolver()->assembleFluxTerms( DT, domain, solver->getDofManager(), localMatrix, localRhs ); + solver->wellSolver()->assembleFluxTerms( TIME, DT, domain, solver->getDofManager(), localMatrix, localRhs ); } ); } @@ -415,7 +415,7 @@ class SinglePhaseReservoirSolverTest : public ::testing::Test [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { - solver->wellSolver()->assembleAccumulationTerms( domain, solver->getDofManager(), localMatrix, localRhs ); + solver->wellSolver()->assembleAccumulationTerms( TIME, DT, domain, solver->getDofManager(), localMatrix, localRhs ); } ); } @@ -497,7 +497,7 @@ TEST_F( SinglePhaseReservoirSolverInternalWellTest, jacobianNumericalCheck_Flux [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, arrayView1d< real64 > const & localRhs ) { - solver->wellSolver()->assembleFluxTerms( DT, domain, solver->getDofManager(), localMatrix, localRhs ); + solver->wellSolver()->assembleFluxTerms( TIME, DT, domain, solver->getDofManager(), localMatrix, localRhs ); } ); } diff --git a/src/coreComponents/unitTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp b/src/coreComponents/unitTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp new file mode 100644 index 00000000000..3e6114d9c18 --- /dev/null +++ b/src/coreComponents/unitTests/wellsTests/testThermalReservoirCompositionalMultiphaseMSWells.cpp @@ -0,0 +1,818 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +#include "unitTests/fluidFlowTests/testCompFlowUtils.hpp" + +#include "common/DataTypes.hpp" +#include "mainInterface/initialization.hpp" +#include "constitutive/fluid/multifluid/MultiFluidBase.hpp" +#include "mainInterface/ProblemManager.hpp" +#include "mesh/DomainPartition.hpp" +#include "mainInterface/GeosxState.hpp" +#include "mesh/WellElementSubRegion.hpp" +#include "physicsSolvers/PhysicsSolverManager.hpp" +#include "physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp" +#include "physicsSolvers/fluidFlow/CompositionalMultiphaseFVM.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellKernels.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp" +#include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp" + +using namespace geos; +using namespace geos::dataRepository; +using namespace geos::constitutive; +using namespace geos::testing; + +CommandLineOptions g_commandLineOptions; + +void writeTableToFile( string const & filename, char const * str ) +{ + std::ofstream os( filename ); + ASSERT_TRUE( os.is_open() ); + os << str; + os.close(); +} + +void removeFile( string const & filename ) +{ + int const ret = std::remove( filename.c_str() ); + ASSERT_TRUE( ret == 0 ); +} +char const * co2flash = "FlashModel CO2Solubility 1e6 7.5e7 5e5 299.15 369.15 10 0"; +char const * pvtLiquid = "DensityFun PhillipsBrineDensity 1e6 7.5e7 5e5 299.15 369.15 10 0\n" + "ViscosityFun PhillipsBrineViscosity 0\n" + "EnthalpyFun BrineEnthalpy 1e6 7.5e7 5e5 299.15 369.15 10 0\n"; + +char const * pvtGas = "DensityFun SpanWagnerCO2Density 1e6 7.5e7 5e5 299.15 369.15 10\n" + "ViscosityFun FenghourCO2Viscosity 1e6 7.5e7 5e5 299.15 369.15 10\n" + "EnthalpyFun CO2Enthalpy 1e6 7.5e7 5e5 299.15 369.15 10\n"; +char const * xmlInput = + R"xml( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )xml"; + + + +template< typename LAMBDA > +void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > & solver, + DomainPartition & domain, + real64 const perturbParameter, + real64 const relTol, bool diag_check, + LAMBDA && assembleFunction ) +{ + CompositionalMultiphaseWell & wellSolver = *solver.wellSolver(); + CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() ); + + localIndex const NC = flowSolver.numFluidComponents(); + + CRSMatrix< real64, globalIndex > const & jacobian = solver.getLocalMatrix(); + array1d< real64 > residual( jacobian.numRows() ); + DofManager const & dofManager = solver.getDofManager(); + + // assemble the analytical residual + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + residual.move( hostMemorySpace, false ); + + // copy the analytical residual + array1d< real64 > residualOrig( residual ); + + // create the numerical jacobian + jacobian.move( hostMemorySpace ); + CRSMatrix< real64, globalIndex > jacobianFD( jacobian ); + jacobianFD.zero(); + + string const resDofKey = dofManager.getKey( wellSolver.resElementDofName() ); + string const wellDofKey = dofManager.getKey( wellSolver.wellElementDofName() ); + + // at this point we start assembling the finite-difference block by block + + //////////////////////////////////////////////// + // Step 1) Compute the terms in J_RR and J_WR // + //////////////////////////////////////////////// + if( 1 ) + domain.forMeshBodies( [&] ( MeshBody & meshBody ) + { + meshBody.forMeshLevels( [&] ( MeshLevel & mesh ) + { + ElementRegionManager & elemManager = mesh.getElemManager(); + for( localIndex er = 0; er < elemManager.numRegions(); ++er ) + { + ElementRegionBase & elemRegion = elemManager.getRegion( er ); + elemRegion.forElementSubRegionsIndex< CellElementSubRegion >( [&]( localIndex const, CellElementSubRegion & subRegion ) + { + // get the degrees of freedom and ghosting information + arrayView1d< globalIndex const > const & dofNumber = + subRegion.getReference< array1d< globalIndex > >( resDofKey ); + + // get the primary variables on the reservoir elements + arrayView1d< real64 > const & pres = + subRegion.getField< fields::flow::pressure >(); + pres.move( hostMemorySpace, false ); + + arrayView2d< real64, compflow::USD_COMP > const & compDens = + subRegion.getField< fields::flow::globalCompDensity >(); + compDens.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & temp = + subRegion.getField< fields::flow::temperature >(); + temp.move( hostMemorySpace, false ); + + // a) compute all the derivatives wrt to the pressure in RESERVOIR elem ei + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + if( ei !=0 ) + break; + real64 totalDensity = 0.0; + for( localIndex ic = 0; ic < NC; ++ic ) + { + totalDensity += compDens[ei][ic]; + } + + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the element + real64 const dP = perturbParameter * (pres[ei] + perturbParameter); + pres.move( hostMemorySpace, true ); + pres[ei] += dP; + + // after perturbing, update the pressure-dependent quantities in the reservoir + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + arrayView1d< string const > const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei], + dP, + jacobianFD.toViewConstSizes() ); + } + + for( localIndex jc = 0; jc < NC; ++jc ) + { + solver.resetStateToBeginningOfStep( domain ); + + real64 const dRho = perturbParameter * totalDensity; + compDens.move( hostMemorySpace, true ); + compDens[ei][jc] += dRho; + + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + arrayView1d< string const > const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + wellSolver.updateState( domain ); + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei] + jc + 1, + dRho, + jacobianFD.toViewConstSizes() ); + } + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the element + real64 const dTemp = perturbParameter * (temp[ei] + perturbParameter); + temp.move( hostMemorySpace, true ); + temp[ei] += dTemp; + + // after perturbing, update the pressure-dependent quantities in the reservoir + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + arrayView1d< string const > const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei]+NC+1, + dTemp, + jacobianFD.toViewConstSizes() ); + } + } + } ); + } + } ); + return; + } ); + + ///////////////////////////////////////////////// + // Step 2) Compute the terms in J_RW and J_WW // + ///////////////////////////////////////////////// + + // loop over the wells + if( 1 ) + wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + // get the degrees of freedom, ghosting info and next well elem index + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + + // get the primary variables on the well elements + arrayView1d< real64 > const & wellElemPressure = + subRegion.getField< fields::well::pressure >(); + wellElemPressure.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & wellElemTemperature = + subRegion.getField< fields::well::temperature >(); + wellElemTemperature.move( hostMemorySpace, false ); + + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = + subRegion.getField< fields::well::globalCompDensity >(); + wellElemCompDens.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & connRate = + subRegion.getField< fields::well::mixtureConnectionRate >(); + connRate.move( hostMemorySpace, false ); + + // a) compute all the derivatives wrt to the pressure in WELL elem iwelem + for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem ) + { + + real64 wellElemTotalDensity = 0.0; + for( localIndex ic = 0; ic < NC; ++ic ) + { + wellElemTotalDensity += wellElemCompDens[iwelem][ic]; + } + + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the well element + real64 const dP = perturbParameter * ( wellElemPressure[iwelem] + perturbParameter ); + wellElemPressure.move( hostMemorySpace, true ); + wellElemPressure[iwelem] += dP; + + // after perturbing, update the pressure-dependent quantities in the well + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DPRES, + dP, + jacobianFD.toViewConstSizes() ); + } + + for( localIndex jc = 0; jc < NC; ++jc ) + { + solver.resetStateToBeginningOfStep( domain ); + + real64 const dRho = perturbParameter * wellElemTotalDensity; + wellElemCompDens.move( hostMemorySpace, true ); + wellElemCompDens[iwelem][jc] += dRho; + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + jc, + dRho, + jacobianFD.toViewConstSizes() ); + } + { + solver.resetStateToBeginningOfStep( domain ); + residual.zero(); + jacobian.zero(); + if( diag_check || iwelem > 0 ) + { + // here is the perturbation in the temperature of the well element + real64 const dT = perturbParameter * ( wellElemTemperature[iwelem] + perturbParameter ); + wellElemTemperature.move( hostMemorySpace, true ); + wellElemTemperature[iwelem] += dT; + + // after perturbing, update the pressure-dependent quantities in the well + wellSolver.updateState( domain ); + + + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC+1, + dT, + jacobianFD.toViewConstSizes() ); + if( iwelem == 1 ) + { + real64 dRdX = 0.0; + localIndex rowIndex = wellElemDofNumber[0] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC+1;; + for( integer ider=0; ider< 3; ider++ ) + { + globalIndex colIndex = wellElemDofNumber[0]+ ider; + setNumericalJacobianValue( rowIndex, colIndex, dRdX, jacobianFD.toViewConstSizes() ); + } + globalIndex colIndex = wellElemDofNumber[1]+3; + setNumericalJacobianValue( rowIndex, colIndex, dRdX, jacobianFD.toViewConstSizes() ); + } + } + else + { + localIndex rowIndex = wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC+1;; + globalIndex colIndex = wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC+1;; + setNumericalJacobianValue( rowIndex, colIndex, 1.0, jacobianFD.toViewConstSizes() ); + } + + } + } + + + // b) compute all the derivatives wrt to the connection in WELL elem + // iwelem + for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem ) + { + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the rate of the well element + real64 const dRate = perturbParameter * ( connRate[iwelem] + perturbParameter ); + connRate.move( hostMemorySpace, true ); + connRate[iwelem] += dRate; + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC, + dRate, + jacobianFD.toViewConstSizes() ); + } + } + } ); + } ); + + // assemble the analytical jacobian + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + //printCompareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst()); + compareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), relTol ); +} + +class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test +{ +public: + + CompositionalMultiphaseReservoirSolverTest(): + state( std::make_unique< CommandLineOptions >( g_commandLineOptions ) ) + {} + +protected: + + void SetUp() override + { + setupProblemFromXML( state.getProblemManager(), xmlInput ); + solver = &state.getProblemManager().getPhysicsSolverManager().getGroup< CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > >( "reservoirSystem" ); + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + solver->setupSystem( domain, + solver->getDofManager(), + solver->getLocalMatrix(), + solver->getSystemRhs(), + solver->getSystemSolution() ); + + solver->implicitStepSetup( time, dt, domain ); + } + + static real64 constexpr time = 0.0; + static real64 constexpr dt = 1e4; + static real64 constexpr eps = std::numeric_limits< real64 >::epsilon(); + + GeosxState state; + CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > * solver; +}; + +real64 constexpr CompositionalMultiphaseReservoirSolverTest::time; +real64 constexpr CompositionalMultiphaseReservoirSolverTest::dt; +real64 constexpr CompositionalMultiphaseReservoirSolverTest::eps; + +#if 0 + +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Perforation ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->assembleCouplingTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + //solver->assembleSystem( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} + +#endif + +#if 0 +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Flux ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, false, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->wellSolver()->assembleSystem( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + //solver->assembleSystem( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + //solver->assembleCouplingTerms( time,dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} + +#endif +#if 0 + +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_flux ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, true, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->wellSolver()->computePerforationRates( time, dt, domain ); + solver->wellSolver()->assembleFluxTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + solver->assembleCouplingTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} +#endif +#if 1 + +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Accum_Vol_Energy_Bal ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, false, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->wellSolver()->assembleAccumulationTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_PressureRel ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, true, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->wellSolver()->assemblePressureRelations( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} +#endif +int main( int argc, char * * argv ) +{ + writeTableToFile( "co2flash.txt", co2flash ); + writeTableToFile( "pvtliquid.txt", pvtLiquid ); + writeTableToFile( "pvtgas.txt", pvtGas ); + ::testing::InitGoogleTest( &argc, argv ); + g_commandLineOptions = *geos::basicSetup( argc, argv ); + int const result = RUN_ALL_TESTS(); + geos::basicCleanup(); + removeFile( "co2flash.txt" ); + removeFile( "pvtliquid.txt" ); + removeFile( "pvtgas.txt" ); + + return result; +} diff --git a/src/coreComponents/unitTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp b/src/coreComponents/unitTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp new file mode 100644 index 00000000000..47c0503a29c --- /dev/null +++ b/src/coreComponents/unitTests/wellsTests/testThermalReservoirCompositionalMultiphaseSSWells.cpp @@ -0,0 +1,807 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2018-2020 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2020 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2018-2020 TotalEnergies + * Copyright (c) 2019- GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +#include "unitTests/fluidFlowTests/testCompFlowUtils.hpp" + +#include "common/DataTypes.hpp" +#include "mainInterface/initialization.hpp" +#include "constitutive/fluid/multifluid/MultiFluidBase.hpp" +#include "mainInterface/ProblemManager.hpp" +#include "mesh/DomainPartition.hpp" +#include "mainInterface/GeosxState.hpp" +#include "mesh/WellElementSubRegion.hpp" +#include "physicsSolvers/PhysicsSolverManager.hpp" +#include "physicsSolvers/multiphysics/CompositionalMultiphaseReservoirAndWells.hpp" +#include "physicsSolvers/fluidFlow/CompositionalMultiphaseFVM.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellKernels.hpp" +#include "physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWellFields.hpp" +#include "physicsSolvers/fluidFlow/wells/WellSolverBaseFields.hpp" + +using namespace geos; +using namespace geos::dataRepository; +using namespace geos::constitutive; +using namespace geos::testing; + +CommandLineOptions g_commandLineOptions; + +void writeTableToFile( string const & filename, char const * str ) +{ + std::ofstream os( filename ); + ASSERT_TRUE( os.is_open() ); + os << str; + os.close(); +} + +void removeFile( string const & filename ) +{ + int const ret = std::remove( filename.c_str() ); + ASSERT_TRUE( ret == 0 ); +} +char const * co2flash = "FlashModel CO2Solubility 1e6 7.5e7 5e5 299.15 369.15 10 0"; +char const * pvtLiquid = "DensityFun PhillipsBrineDensity 1e6 7.5e7 5e5 299.15 369.15 10 0\n" + "ViscosityFun PhillipsBrineViscosity 0\n" + "EnthalpyFun BrineEnthalpy 1e6 7.5e7 5e5 299.15 369.15 10 0\n"; + +char const * pvtGas = "DensityFun SpanWagnerCO2Density 1e6 7.5e7 5e5 299.15 369.15 10\n" + "ViscosityFun FenghourCO2Viscosity 1e6 7.5e7 5e5 299.15 369.15 10\n" + "EnthalpyFun CO2Enthalpy 1e6 7.5e7 5e5 299.15 369.15 10\n"; + +char const * xmlInput = + R"xml( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )xml"; + + + +template< typename LAMBDA > +void testNumericalJacobian( CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > & solver, + DomainPartition & domain, + real64 const perturbParameter, + real64 const relTol, bool diag_check, + LAMBDA && assembleFunction ) +{ + CompositionalMultiphaseWell & wellSolver = *solver.wellSolver(); + CompositionalMultiphaseFVM & flowSolver = dynamicCast< CompositionalMultiphaseFVM & >( *solver.reservoirSolver() ); + + localIndex const NC = flowSolver.numFluidComponents(); + + CRSMatrix< real64, globalIndex > const & jacobian = solver.getLocalMatrix(); + array1d< real64 > residual( jacobian.numRows() ); + DofManager const & dofManager = solver.getDofManager(); + + // assemble the analytical residual + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + residual.move( hostMemorySpace, false ); + + // copy the analytical residual + array1d< real64 > residualOrig( residual ); + + // create the numerical jacobian + jacobian.move( hostMemorySpace ); + CRSMatrix< real64, globalIndex > jacobianFD( jacobian ); + jacobianFD.zero(); + + string const resDofKey = dofManager.getKey( wellSolver.resElementDofName() ); + string const wellDofKey = dofManager.getKey( wellSolver.wellElementDofName() ); + + // at this point we start assembling the finite-difference block by block + + //////////////////////////////////////////////// + // Step 1) Compute the terms in J_RR and J_WR // + //////////////////////////////////////////////// + if( 1 ) + domain.forMeshBodies( [&] ( MeshBody & meshBody ) + { + meshBody.forMeshLevels( [&] ( MeshLevel & mesh ) + { + ElementRegionManager & elemManager = mesh.getElemManager(); + for( localIndex er = 0; er < elemManager.numRegions(); ++er ) + { + ElementRegionBase & elemRegion = elemManager.getRegion( er ); + elemRegion.forElementSubRegionsIndex< CellElementSubRegion >( [&]( localIndex const, CellElementSubRegion & subRegion ) + { + // get the degrees of freedom and ghosting information + arrayView1d< globalIndex const > const & dofNumber = + subRegion.getReference< array1d< globalIndex > >( resDofKey ); + + // get the primary variables on the reservoir elements + arrayView1d< real64 > const & pres = + subRegion.getField< fields::flow::pressure >(); + pres.move( hostMemorySpace, false ); + + arrayView2d< real64, compflow::USD_COMP > const & compDens = + subRegion.getField< fields::flow::globalCompDensity >(); + compDens.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & temp = + subRegion.getField< fields::flow::temperature >(); + temp.move( hostMemorySpace, false ); + + // a) compute all the derivatives wrt to the pressure in RESERVOIR elem ei + for( localIndex ei = 0; ei < subRegion.size(); ++ei ) + { + if( ei ==0 ) + { + real64 totalDensity = 0.0; + for( localIndex ic = 0; ic < NC; ++ic ) + { + totalDensity += compDens[ei][ic]; + } + + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the element + real64 const dP = perturbParameter * (pres[ei] + perturbParameter); + pres.move( hostMemorySpace, true ); + pres[ei] += dP; + + // after perturbing, update the pressure-dependent quantities in the reservoir + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + arrayView1d< string const > const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei], + dP, + jacobianFD.toViewConstSizes() ); + } + + for( localIndex jc = 0; jc < NC; ++jc ) + { + solver.resetStateToBeginningOfStep( domain ); + + real64 const dRho = perturbParameter * totalDensity; + compDens.move( hostMemorySpace, true ); + compDens[ei][jc] += dRho; + + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + arrayView1d< string const > const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + wellSolver.updateState( domain ); + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei] + jc + 1, + dRho, + jacobianFD.toViewConstSizes() ); + } + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the element + real64 const dTemp = perturbParameter * (temp[ei] + perturbParameter); + temp.move( hostMemorySpace, true ); + temp[ei] += dTemp; + + // after perturbing, update the pressure-dependent quantities in the reservoir + flowSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh2, + arrayView1d< string const > const & regionNames2 ) + { + mesh2.getElemManager().forElementSubRegions( regionNames2, + [&]( localIndex const, + ElementSubRegionBase & subRegion2 ) + { + flowSolver.updateFluidState( subRegion2 ); + } ); + } ); + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + dofNumber[ei]+NC+1, + dTemp, + jacobianFD.toViewConstSizes() ); + } + } + } + } ); + } + } ); + return; + } ); + + ///////////////////////////////////////////////// + // Step 2) Compute the terms in J_RW and J_WW // + ///////////////////////////////////////////////// + + // loop over the wells + if( 1 ) + wellSolver.forDiscretizationOnMeshTargets( domain.getMeshBodies(), [&] ( string const &, + MeshLevel & mesh, + arrayView1d< string const > const & regionNames ) + { + mesh.getElemManager().forElementSubRegions< WellElementSubRegion >( regionNames, + [&]( localIndex const, + WellElementSubRegion & subRegion ) + { + // get the degrees of freedom, ghosting info and next well elem index + arrayView1d< globalIndex const > const & wellElemDofNumber = + subRegion.getReference< array1d< globalIndex > >( wellDofKey ); + + // get the primary variables on the well elements + arrayView1d< real64 > const & wellElemPressure = + subRegion.getField< fields::well::pressure >(); + wellElemPressure.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & wellElemTemperature = + subRegion.getField< fields::well::temperature >(); + wellElemTemperature.move( hostMemorySpace, false ); + + arrayView2d< real64, compflow::USD_COMP > const & wellElemCompDens = + subRegion.getField< fields::well::globalCompDensity >(); + wellElemCompDens.move( hostMemorySpace, false ); + + arrayView1d< real64 > const & connRate = + subRegion.getField< fields::well::mixtureConnectionRate >(); + connRate.move( hostMemorySpace, false ); + + // a) compute all the derivatives wrt to the pressure in WELL elem iwelem + for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem ) + { + + real64 wellElemTotalDensity = 0.0; + for( localIndex ic = 0; ic < NC; ++ic ) + { + wellElemTotalDensity += wellElemCompDens[iwelem][ic]; + } + + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the pressure of the well element + real64 const dP = perturbParameter * ( wellElemPressure[iwelem] + perturbParameter ); + wellElemPressure.move( hostMemorySpace, true ); + wellElemPressure[iwelem] += dP; + + // after perturbing, update the pressure-dependent quantities in the well + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DPRES, + dP, + jacobianFD.toViewConstSizes() ); + } + + for( localIndex jc = 0; jc < NC; ++jc ) + { + solver.resetStateToBeginningOfStep( domain ); + + real64 const dRho = perturbParameter * wellElemTotalDensity; + wellElemCompDens.move( hostMemorySpace, true ); + wellElemCompDens[iwelem][jc] += dRho; + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + jc, + dRho, + jacobianFD.toViewConstSizes() ); + } + { + solver.resetStateToBeginningOfStep( domain ); + residual.zero(); + jacobian.zero(); + if( diag_check || iwelem > 0 ) + { + // here is the perturbation in the temperature of the well element + real64 const dT = perturbParameter * ( wellElemTemperature[iwelem] + perturbParameter ); + wellElemTemperature.move( hostMemorySpace, true ); + wellElemTemperature[iwelem] += dT; + + // after perturbing, update the pressure-dependent quantities in the well + wellSolver.updateState( domain ); + + + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC+1, + dT, + jacobianFD.toViewConstSizes() ); + } + else + { + localIndex rowIndex = wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC+1;; + globalIndex colIndex = wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC+1;; + setNumericalJacobianValue( rowIndex, colIndex, 1.0, jacobianFD.toViewConstSizes() ); + } + } + } + + + // b) compute all the derivatives wrt to the connection in WELL elem + // iwelem + for( localIndex iwelem = 0; iwelem < subRegion.size(); ++iwelem ) + { + { + solver.resetStateToBeginningOfStep( domain ); + + // here is the perturbation in the rate of the well element + real64 const dRate = perturbParameter * ( connRate[iwelem] + perturbParameter ); + connRate.move( hostMemorySpace, true ); + connRate[iwelem] += dRate; + + wellSolver.updateState( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + + fillNumericalJacobian( residual.toViewConst(), + residualOrig.toViewConst(), + wellElemDofNumber[iwelem] + compositionalMultiphaseWellKernels::ColOffset::DCOMP + NC, + dRate, + jacobianFD.toViewConstSizes() ); + } + } + } ); + } ); + + // assemble the analytical jacobian + solver.resetStateToBeginningOfStep( domain ); + + residual.zero(); + jacobian.zero(); + assembleFunction( jacobian.toViewConstSizes(), residual.toView() ); + //printCompareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst()); + compareLocalMatrices( jacobian.toViewConst(), jacobianFD.toViewConst(), relTol ); +} + +class CompositionalMultiphaseReservoirSolverTest : public ::testing::Test +{ +public: + + CompositionalMultiphaseReservoirSolverTest(): + state( std::make_unique< CommandLineOptions >( g_commandLineOptions ) ) + {} + +protected: + + void SetUp() override + { + setupProblemFromXML( state.getProblemManager(), xmlInput ); + solver = &state.getProblemManager().getPhysicsSolverManager().getGroup< CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > >( "reservoirSystem" ); + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + solver->setupSystem( domain, + solver->getDofManager(), + solver->getLocalMatrix(), + solver->getSystemRhs(), + solver->getSystemSolution() ); + + solver->implicitStepSetup( time, dt, domain ); + } + + static real64 constexpr time = 0.0; + static real64 constexpr dt = 1e4; + static real64 constexpr eps = std::numeric_limits< real64 >::epsilon(); + + GeosxState state; + CompositionalMultiphaseReservoirAndWells< CompositionalMultiphaseBase > * solver; +}; + +real64 constexpr CompositionalMultiphaseReservoirSolverTest::time; +real64 constexpr CompositionalMultiphaseReservoirSolverTest::dt; +real64 constexpr CompositionalMultiphaseReservoirSolverTest::eps; + +#if 0 + +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Perforation ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->assembleCouplingTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + //solver->assembleSystem( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} + +#endif + +#if 1 +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Flux ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, false, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->wellSolver()->assembleSystem( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + //solver->assembleSystem( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + //solver->assembleCouplingTerms( time,dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} + +#endif +#if 0 + +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_flux ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, true, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->wellSolver()->computePerforationRates( time, dt, domain ); + solver->wellSolver()->assembleFluxTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + solver->assembleCouplingTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} +#endif +#if 1 + +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_Accum_Vol_Energy_Bal ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, false, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->wellSolver()->assembleAccumulationTerms( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} +TEST_F( CompositionalMultiphaseReservoirSolverTest, jacobianNumericalCheck_PressureRel ) +{ + real64 const perturb = std::sqrt( eps ); + real64 const tol = 1e-1; // 10% error margin + + DomainPartition & domain = state.getProblemManager().getDomainPartition(); + + testNumericalJacobian( *solver, domain, perturb, tol, true, + [&] ( CRSMatrixView< real64, globalIndex const > const & localMatrix, + arrayView1d< real64 > const & localRhs ) + { + solver->wellSolver()->assemblePressureRelations( time, dt, domain, solver->getDofManager(), localMatrix, localRhs ); + } ); +} +#endif +int main( int argc, char * * argv ) +{ + writeTableToFile( "co2flash.txt", co2flash ); + writeTableToFile( "pvtliquid.txt", pvtLiquid ); + writeTableToFile( "pvtgas.txt", pvtGas ); + ::testing::InitGoogleTest( &argc, argv ); + g_commandLineOptions = *geos::basicSetup( argc, argv ); + int const result = RUN_ALL_TESTS(); + geos::basicCleanup(); + removeFile( "co2flash.txt" ); + removeFile( "pvtliquid.txt" ); + removeFile( "pvtgas.txt" ); + + return result; +}