diff --git a/src/coreComponents/constitutive/CMakeLists.txt b/src/coreComponents/constitutive/CMakeLists.txt index 6af439fe23f..98c22c7cdbd 100644 --- a/src/coreComponents/constitutive/CMakeLists.txt +++ b/src/coreComponents/constitutive/CMakeLists.txt @@ -86,6 +86,7 @@ set( constitutive_headers fluid/multifluid/CO2Brine/functions/PVTFunctionHelpers.hpp fluid/multifluid/CO2Brine/functions/SpanWagnerCO2Density.hpp fluid/multifluid/CO2Brine/functions/WaterDensity.hpp + fluid/multifluid/compositional/functions/CompositionalMultiphaseFluidUtilities.hpp fluid/multifluid/compositional/functions/CompositionalProperties_impl.hpp fluid/multifluid/compositional/functions/CompositionalProperties.hpp fluid/multifluid/compositional/functions/CubicEOSPhaseModel_impl.hpp @@ -102,6 +103,7 @@ set( constitutive_headers fluid/multifluid/compositional/functions/StabilityTest.hpp fluid/multifluid/compositional/functions/Utilities.hpp fluid/multifluid/compositional/models/CompositionalDensity.hpp + fluid/multifluid/compositional/models/CompositionalEnthalpy.hpp fluid/multifluid/compositional/models/ConstantViscosity.hpp fluid/multifluid/compositional/models/FunctionBase.hpp fluid/multifluid/compositional/models/ImmiscibleWaterDensity.hpp @@ -267,6 +269,7 @@ set( constitutive_sources fluid/multifluid/CO2Brine/functions/PureWaterProperties.cpp fluid/multifluid/CO2Brine/functions/WaterDensity.cpp fluid/multifluid/compositional/models/CompositionalDensity.cpp + fluid/multifluid/compositional/models/CompositionalEnthalpy.cpp fluid/multifluid/compositional/models/ConstantViscosity.cpp fluid/multifluid/compositional/models/ImmiscibleWaterDensity.cpp fluid/multifluid/compositional/models/ImmiscibleWaterFlashModel.cpp diff --git a/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluid.hpp b/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluid.hpp index bd9faf0ce44..1d9bad1bef1 100644 --- a/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluid.hpp +++ b/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluid.hpp @@ -29,7 +29,6 @@ #include "constitutive/fluid/multifluid/compositional/models/KValueFlashModel.hpp" #include "constitutive/fluid/multifluid/compositional/models/LohrenzBrayClarkViscosity.hpp" #include "constitutive/fluid/multifluid/compositional/models/NegativeTwoPhaseFlashModel.hpp" -#include "constitutive/fluid/multifluid/compositional/models/NullModel.hpp" #include "constitutive/fluid/multifluid/compositional/models/PhaseModel.hpp" #include "constitutive/fluid/multifluid/compositional/models/PhillipsBrineDensity.hpp" #include "constitutive/fluid/multifluid/compositional/models/PhillipsBrineViscosity.hpp" @@ -50,6 +49,7 @@ namespace constitutive template< typename FLASH, typename PHASE1, typename PHASE2, typename PHASE3 = compositional::NullPhaseModel > class CompositionalMultiphaseFluid : public MultiFluidBase { + using Traits = compositional::CompositionalMultiphaseFluidTraits< FLASH, PHASE1, PHASE2, PHASE3 >; public: using FlashModel = FLASH; using Phase1Model = PHASE1; @@ -57,7 +57,7 @@ class CompositionalMultiphaseFluid : public MultiFluidBase using Phase3Model = PHASE3; // Get the number of phases - static constexpr integer NUM_PHASES = FlashModel::KernelWrapper::getNumberOfPhases(); + static constexpr integer NUM_PHASES = Traits::getNumberOfPhases(); // Currently restrict to 2 or 3 phases static_assert( NUM_PHASES == 2 || NUM_PHASES == 3 ); diff --git a/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluidUpdates.hpp b/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluidUpdates.hpp index 6af1f3366dc..5e5b176056b 100644 --- a/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluidUpdates.hpp +++ b/src/coreComponents/constitutive/fluid/multifluid/compositional/CompositionalMultiphaseFluidUpdates.hpp @@ -21,6 +21,7 @@ #define GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_COMPOSITIONAL_COMPOSITIONALMULTIPHASEFLUIDUPDATES_HPP_ #include "constitutive/fluid/multifluid/compositional/parameters/ComponentProperties.hpp" +#include "constitutive/fluid/multifluid/compositional/functions/CompositionalMultiphaseFluidUtilities.hpp" #include "constitutive/fluid/multifluid/MultiFluidBase.hpp" #include "constitutive/fluid/multifluid/MultiFluidUtils.hpp" @@ -40,6 +41,8 @@ namespace constitutive template< typename FLASH, typename PHASE1, typename PHASE2, typename PHASE3 > class CompositionalMultiphaseFluidUpdates final : public MultiFluidBase::KernelWrapper { + using Traits = compositional::CompositionalMultiphaseFluidTraits< FLASH, PHASE1, PHASE2, PHASE3 >; + static constexpr integer NUM_PHASES = Traits::getNumberOfPhases(); public: CompositionalMultiphaseFluidUpdates( compositional::ComponentProperties const & componentProperties, FLASH const & flash, @@ -191,8 +194,7 @@ CompositionalMultiphaseFluidUpdates< FLASH, PHASE1, PHASE2, PHASE3 >::compute( MultiFluidBase::FluidProp::SliceType const totalDensity ) const { integer constexpr maxNumComp = MultiFluidBase::MAX_NUM_COMPONENTS; - integer constexpr maxNumPhase = MultiFluidBase::MAX_NUM_PHASES - 1; - MultiFluidBase::PhaseComp::StackValueType< maxNumPhase *maxNumComp > kValues( 1, 1, numPhases() - 1, numComponents() ); + MultiFluidBase::PhaseComp::StackValueType< NUM_PHASES *maxNumComp > kValues( 1, 1, numPhases() - 1, numComponents() ); LvArray::forValuesInSlice( kValues[0][0], setZero ); // Force initialisation of k-Values @@ -230,7 +232,6 @@ CompositionalMultiphaseFluidUpdates< FLASH, PHASE1, PHASE2, PHASE3 >::compute( { integer constexpr maxNumComp = MultiFluidBase::MAX_NUM_COMPONENTS; integer constexpr maxNumDof = MultiFluidBase::MAX_NUM_COMPONENTS + 2; - integer constexpr maxNumPhase = MultiFluidBase::MAX_NUM_PHASES; integer const numComp = numComponents(); integer const numPhase = numPhases(); integer const numDof = numComp + 2; @@ -283,7 +284,7 @@ CompositionalMultiphaseFluidUpdates< FLASH, PHASE1, PHASE2, PHASE3 >::compute( phaseMassDensity.value[m_phaseOrder[1]], phaseMassDensity.derivs[m_phaseOrder[1]], m_useMass ); - if constexpr (2 < FLASH::KernelWrapper::getNumberOfPhases()) + if constexpr (2 < NUM_PHASES) { m_phase3.density.compute( m_componentProperties, pressure, @@ -315,7 +316,7 @@ CompositionalMultiphaseFluidUpdates< FLASH, PHASE1, PHASE2, PHASE3 >::compute( phaseViscosity.value[m_phaseOrder[1]], phaseViscosity.derivs[m_phaseOrder[1]], m_useMass ); - if constexpr (2 < FLASH::KernelWrapper::getNumberOfPhases()) + if constexpr (2 < NUM_PHASES) { m_phase3.viscosity.compute( m_componentProperties, pressure, @@ -328,9 +329,38 @@ CompositionalMultiphaseFluidUpdates< FLASH, PHASE1, PHASE2, PHASE3 >::compute( m_useMass ); } - // 5. Convert derivatives from phase composition to total composition + // 5. Calculate the phase enthapies + if constexpr (Traits::isThermalType()) + { + m_phase1.enthalpy.compute( m_componentProperties, + pressure, + temperature, + phaseCompFrac.value[m_phaseOrder[0]].toSliceConst(), + phaseEnthalpy.value[m_phaseOrder[0]], + phaseEnthalpy.derivs[m_phaseOrder[0]], + m_useMass ); + m_phase2.enthalpy.compute( m_componentProperties, + pressure, + temperature, + phaseCompFrac.value[m_phaseOrder[1]].toSliceConst(), + phaseEnthalpy.value[m_phaseOrder[1]], + phaseEnthalpy.derivs[m_phaseOrder[1]], + m_useMass ); + if constexpr (2 < NUM_PHASES) + { + m_phase3.enthalpy.compute( m_componentProperties, + pressure, + temperature, + phaseCompFrac.value[m_phaseOrder[2]].toSliceConst(), + phaseEnthalpy.value[m_phaseOrder[2]], + phaseEnthalpy.derivs[m_phaseOrder[2]], + m_useMass ); + } + } + + // 6. Convert derivatives from phase composition to total composition stackArray1d< real64, maxNumDof > workSpace( numDof ); - for( integer ip = 0; ip < FLASH::KernelWrapper::getNumberOfPhases(); ++ip ) + for( integer ip = 0; ip < NUM_PHASES; ++ip ) { convertDerivativesToTotalMoleFraction( numComp, phaseComponentFraction.derivs[ip].toSliceConst(), @@ -344,9 +374,16 @@ CompositionalMultiphaseFluidUpdates< FLASH, PHASE1, PHASE2, PHASE3 >::compute( phaseComponentFraction.derivs[ip].toSliceConst(), phaseViscosity.derivs[ip], workSpace ); + if constexpr (Traits::isThermalType()) + { + convertDerivativesToTotalMoleFraction( numComp, + phaseCompFrac.derivs[ip].toSliceConst(), + phaseEnthalpy.derivs[ip], + workSpace ); + } } - // 6. if mass variables used instead of molar, perform the conversion + // 7. if mass variables used instead of molar, perform the conversion if( m_useMass ) { real64 phaseMolecularWeight[maxNumPhase]{}; @@ -390,7 +427,7 @@ CompositionalMultiphaseFluidUpdates< FLASH, PHASE1, PHASE2, PHASE3 >::compute( } } - // 7. Compute total fluid mass/molar density and derivatives + // 8. Compute total fluid mass/molar density and derivatives computeTotalDensity( phaseFraction, phaseDensity, diff --git a/src/coreComponents/constitutive/fluid/multifluid/compositional/functions/CompositionalMultiphaseFluidUtilities.hpp b/src/coreComponents/constitutive/fluid/multifluid/compositional/functions/CompositionalMultiphaseFluidUtilities.hpp new file mode 100644 index 00000000000..75b109fd73f --- /dev/null +++ b/src/coreComponents/constitutive/fluid/multifluid/compositional/functions/CompositionalMultiphaseFluidUtilities.hpp @@ -0,0 +1,60 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file CompositionalMultiphaseFluidUtilities.hpp + */ + +#ifndef GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_COMPOSITIONAL_FUNCTIONS_COMPOSITIONALMULTIPHASEFLUIDUTILITIES_HPP_ +#define GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_COMPOSITIONAL_FUNCTIONS_COMPOSITIONALMULTIPHASEFLUIDUTILITIES_HPP_ + +#include "constitutive/fluid/multifluid/compositional/models/NullModel.hpp" + +namespace geos +{ +namespace constitutive +{ +namespace compositional +{ + +template< typename FLASH, typename PHASE1, typename PHASE2, typename PHASE3 > +struct CompositionalMultiphaseFluidTraits +{ + static constexpr integer getNumberOfPhases() + { + return FLASH::KernelWrapper::getNumberOfPhases(); + } + + static constexpr bool isThermalType() + { + if constexpr (getNumberOfPhases() == 3) + { + return !( std::is_same_v< typename PHASE1::Enthalpy, compositional::NullModel > || + std::is_same_v< typename PHASE2::Enthalpy, compositional::NullModel > || + std::is_same_v< typename PHASE3::Enthalpy, compositional::NullModel > ); + } + else + { + return !( std::is_same_v< typename PHASE1::Enthalpy, compositional::NullModel > || + std::is_same_v< typename PHASE2::Enthalpy, compositional::NullModel > ); + } + } +}; + +} //namespace compositional +} // namespace constitutive +} // namespace geos + +#endif //GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_COMPOSITIONAL_FUNCTIONS_COMPOSITIONALMULTIPHASEFLUIDUTILITIES_HPP_ diff --git a/src/coreComponents/constitutive/fluid/multifluid/compositional/models/CompositionalEnthalpy.cpp b/src/coreComponents/constitutive/fluid/multifluid/compositional/models/CompositionalEnthalpy.cpp new file mode 100644 index 00000000000..11377e5183f --- /dev/null +++ b/src/coreComponents/constitutive/fluid/multifluid/compositional/models/CompositionalEnthalpy.cpp @@ -0,0 +1,91 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file CompositionalEnthalpy.cpp + */ + +#include "CompositionalEnthalpy.hpp" +#include "constitutive/fluid/multifluid/compositional/parameters/HeatCapacityCoefficients.hpp" +#include "common/PhysicsConstants.hpp" + +namespace geos +{ +namespace constitutive +{ +namespace compositional +{ + +CompositionalEnthalpyUpdate::CompositionalEnthalpyUpdate( EquationOfStateType const equationOfState, + arrayView1d< real64 const > const & referenceEnthalpy, + arrayView2d< real64 const > const & coefficients ) + : m_equationOfState( equationOfState ), + m_referenceEnthalpy( referenceEnthalpy ), + m_coefficients( coefficients ) +{} + +CompositionalEnthalpy::CompositionalEnthalpy( string const & name, + ComponentProperties const & componentProperties, + integer const phaseIndex, + ModelParameters const & modelParameters ) + : FunctionBase( name, componentProperties ), + m_heatCapacityCoefficients ( modelParameters.get< HeatCapacityCoefficients >() ) +{ + EquationOfState const * equationOfState = modelParameters.get< EquationOfState >(); + string const eosName = equationOfState->m_equationsOfStateNames[phaseIndex]; + m_equationOfState = EnumStrings< EquationOfStateType >::fromString( eosName ); + + const auto & heatCapacityCoefficients = m_heatCapacityCoefficients->m_coefficients; + integer const numComps = heatCapacityCoefficients.size( 1 ); + integer const numCoeffs = heatCapacityCoefficients.size( 2 ); + m_referenceEnthalpy.resize( numComps ); + m_coefficients.resize( numComps, numCoeffs ); + constexpr real64 R = constants::gasConstant; + for( integer ic = 0; ic < numComps; ic++ ) + { + for( integer j = 0; j < numCoeffs; j++ ) + { + m_coefficients( ic, j ) = R * heatCapacityCoefficients( phaseIndex, ic, j ); + } + // Calculate the enthalpy at the reference temperature + real64 refEnthalpy = 0.0; + real64 refHeatCapacity = 0.0; + KernelWrapper::evaluatePolynomial( m_heatCapacityCoefficients->m_referenceTemperature[ic], + m_coefficients[ic], + refEnthalpy, + refHeatCapacity ); + m_referenceEnthalpy[ic] = m_heatCapacityCoefficients->m_referenceEnthalpy( phaseIndex, ic ) - refEnthalpy; + } +} + +CompositionalEnthalpy::KernelWrapper +CompositionalEnthalpy::createKernelWrapper() const +{ + return KernelWrapper( m_equationOfState, + m_referenceEnthalpy.toViewConst(), + m_coefficients.toViewConst()); +} + +std::unique_ptr< ModelParameters > +CompositionalEnthalpy::createParameters( std::unique_ptr< ModelParameters > parameters ) +{ + auto params = EquationOfState::create( std::move( parameters ) ); + params = HeatCapacityCoefficients::create( std::move( params ) ); + return params; +} + +} // namespace compositional +} // namespace constitutive +} // namespace geos diff --git a/src/coreComponents/constitutive/fluid/multifluid/compositional/models/CompositionalEnthalpy.hpp b/src/coreComponents/constitutive/fluid/multifluid/compositional/models/CompositionalEnthalpy.hpp new file mode 100644 index 00000000000..f6b84f86465 --- /dev/null +++ b/src/coreComponents/constitutive/fluid/multifluid/compositional/models/CompositionalEnthalpy.hpp @@ -0,0 +1,164 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file CompositionalEnthalpy.hpp + */ + +#ifndef GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_COMPOSITIONAL_MODELS_COMPOSITIONALENTHALPY_HPP_ +#define GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_COMPOSITIONAL_MODELS_COMPOSITIONALENTHALPY_HPP_ + +#include "FunctionBase.hpp" +#include "constitutive/fluid/multifluid/compositional/parameters/EquationOfState.hpp" + +#include "constitutive/fluid/multifluid/MultiFluidUtils.hpp" +#include "constitutive/fluid/multifluid/MultiFluidConstants.hpp" +#include "constitutive/fluid/multifluid/compositional/functions/CompositionalProperties.hpp" +#include "constitutive/fluid/multifluid/compositional/functions/CubicEOSPhaseModel.hpp" +#include "constitutive/fluid/multifluid/compositional/functions/SoreideWhitsonEOSPhaseModel.hpp" + +namespace geos +{ + +namespace constitutive +{ + +namespace compositional +{ + +class HeatCapacityCoefficients; + +class CompositionalEnthalpyUpdate final : public FunctionBaseUpdate +{ + using Deriv = multifluid::DerivativeOffset; +public: + CompositionalEnthalpyUpdate( EquationOfStateType const equationOfState, + arrayView1d< real64 const > const & referenceEnthalpy, + arrayView2d< real64 const > const & coefficients ); + + template< integer USD1, integer USD2 > + GEOS_HOST_DEVICE + void compute( ComponentProperties::KernelWrapper const & componentProperties, + real64 const & pressure, + real64 const & temperature, + arraySlice1d< real64 const, USD1 > const & phaseComposition, + real64 & enthalpy, + arraySlice1d< real64, USD2 > const & dEnthalpy, + bool useMass ) const; + + /** + * @brief Evaluates the Poling polynomial at a given temeperature given a list of coefficients + * @param[in] T - the temperature + * @param[in] a - the coefficients + * @param[out] enthalpy - the enthalpy + * @param[out] heatCapacity - the enthalpy derivative wrt temperature + */ + GEOS_FORCE_INLINE + GEOS_HOST_DEVICE + static void evaluatePolynomial( real64 const & T, + arraySlice1d< real64 const > const & a, + real64 & enthalpy, + real64 & heatCapacity ) + { + real64 constexpr r1 = 1.0/2.0; + real64 constexpr r2 = 1.0/3.0; + real64 constexpr r3 = 1.0/4.0; + real64 constexpr r4 = 1.0/5.0; + enthalpy = ((((r4*a[4]*T + r3*a[3])*T + r2*a[2])*T + r1*a[1])*T + a[0])*T; + heatCapacity = (((a[4]*T + a[3])*T + a[2])*T + a[1])*T + a[0]; + } + +private: + EquationOfStateType const m_equationOfState; + arrayView1d< real64 const > const m_referenceEnthalpy; + arrayView2d< real64 const > const m_coefficients; +}; + +class CompositionalEnthalpy : public FunctionBase +{ +public: + CompositionalEnthalpy( string const & name, + ComponentProperties const & componentProperties, + integer const phaseIndex, + ModelParameters const & modelParameters ); + + static string catalogName() { return "CompositionalEnthalpy"; } + + virtual FunctionType functionType() const override + { + return FunctionType::ENTHALPY; + } + + /// Type of kernel wrapper for in-kernel update + using KernelWrapper = CompositionalEnthalpyUpdate; + + /** + * @brief Create an update kernel wrapper. + * @return the wrapper + */ + KernelWrapper createKernelWrapper() const; + + // Create parameters unique to this model + static std::unique_ptr< ModelParameters > createParameters( std::unique_ptr< ModelParameters > parameters ); + +private: + EquationOfStateType m_equationOfState; + array1d< real64 > m_referenceEnthalpy; + array2d< real64 > m_coefficients; + HeatCapacityCoefficients const * const m_heatCapacityCoefficients{nullptr}; +}; + +template< integer USD1, integer USD2 > +GEOS_HOST_DEVICE +void CompositionalEnthalpyUpdate::compute( + ComponentProperties::KernelWrapper const & componentProperties, + real64 const & pressure, + real64 const & temperature, + arraySlice1d< real64 const, USD1 > const & phaseComposition, + real64 & enthalpy, + arraySlice1d< real64, USD2 > const & dEnthalpy, + bool useMass ) const +{ + GEOS_UNUSED_VAR( useMass ); + GEOS_UNUSED_VAR( pressure ); + GEOS_UNUSED_VAR( enthalpy ); + + integer const numComps = componentProperties.m_componentMolarWeight.size(); + + // 1. Calculate the ideal gas enthalpy + real64 hIdealGas = 0.0; + auto const & dhIdealGas = dEnthalpy; + dhIdealGas[Deriv::dT] = 0.0; + dhIdealGas[Deriv::dP] = 0.0; + for( integer ic = 0; ic < numComps; ++ic ) + { + real64 enthalpyI = 0.0; + real64 heatCapacityI = 0.0; + evaluatePolynomial( temperature, m_coefficients[ic], enthalpyI, heatCapacityI ); + enthalpyI += m_referenceEnthalpy[ic]; + hIdealGas += phaseComposition[ic] * enthalpyI; + dhIdealGas[Deriv::dT] += phaseComposition[ic] * heatCapacityI; + dhIdealGas[Deriv::dC+ic] = enthalpyI; + } + ((void)m_equationOfState); +} + +} // end namespace compositional + +} // end namespace constitutive + +} // end namespace geos + +#endif //GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_COMPOSITIONAL_MODELS_COMPOSITIONALENTHALPY_HPP_ diff --git a/src/coreComponents/constitutive/fluid/multifluid/compositional/parameters/HeatCapacityCoefficients.cpp b/src/coreComponents/constitutive/fluid/multifluid/compositional/parameters/HeatCapacityCoefficients.cpp new file mode 100644 index 00000000000..4ce886eece2 --- /dev/null +++ b/src/coreComponents/constitutive/fluid/multifluid/compositional/parameters/HeatCapacityCoefficients.cpp @@ -0,0 +1,131 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file HeatCapacityCoefficients.cpp + */ + +#include "HeatCapacityCoefficients.hpp" +#include "ComponentProperties.hpp" +#include "constitutive/fluid/multifluid/MultiFluidBase.hpp" +#include "dataRepository/InputFlags.hpp" + +namespace geos +{ + +namespace constitutive +{ + +namespace compositional +{ + +HeatCapacityCoefficients::HeatCapacityCoefficients( std::unique_ptr< ModelParameters > parameters ): + ModelParameters( std::move( parameters ) ) +{} + +std::unique_ptr< ModelParameters > +HeatCapacityCoefficients::create( std::unique_ptr< ModelParameters > parameters ) +{ + if( parameters && parameters->get< HeatCapacityCoefficients >() != nullptr ) + { + return parameters; + } + return std::make_unique< HeatCapacityCoefficients >( std::move( parameters ) ); +} + +void HeatCapacityCoefficients::registerParametersImpl( MultiFluidBase * fluid ) +{ + fluid->registerWrapper( viewKeyStruct::enthalpyReferenceTemperatureString(), &m_referenceTemperature ). + setInputFlag( dataRepository::InputFlags::OPTIONAL ). + setDescription( "The reference temperature for enthalpy calculation" ); + + fluid->registerWrapper( viewKeyStruct::referenceEnthalpyString(), &m_referenceEnthalpy ). + setInputFlag( dataRepository::InputFlags::OPTIONAL ). + setDescription( "The enthalpy of each component at the reference temperature" ); + + fluid->registerWrapper( viewKeyStruct::componentHeatCapacityCoefficientsString(), &m_coefficients ). + setInputFlag( dataRepository::InputFlags::REQUIRED ). + setDescription( "The polynomial coefficients for the specific heat capacity of each component in each phase" ); +} + +void HeatCapacityCoefficients::postInputInitializationImpl( MultiFluidBase const * fluid, + ComponentProperties const & componentProperties ) +{ + GEOS_UNUSED_VAR( componentProperties ); + + integer const numPhases = fluid->numFluidPhases(); + integer const numComps = fluid->numFluidComponents(); + + // If the reference temperatures are given, then there must be as many as there are components + if( m_referenceTemperature.empty()) + { + m_referenceTemperature.resize( numComps ); + m_referenceTemperature.zero(); + } + else + { + GEOS_THROW_IF_NE_MSG( m_referenceTemperature.size(), numComps, + GEOS_FMT( "{}: '{}' there must be as many reference temperatures provided as there are components", + fluid->getFullName(), + viewKeyStruct::enthalpyReferenceTemperatureString() ), + InputError ); + } + + // If the reference enthalpies are given, then there must be as many as there are components + if( m_referenceEnthalpy.empty()) + { + m_referenceEnthalpy.resize( numComps ); + m_referenceEnthalpy.zero(); + } + else + { + GEOS_THROW_IF_NE_MSG( m_referenceEnthalpy.size(), numComps, + GEOS_FMT( "{}: '{}' there must be as many reference enthalpy values provided as there are components", + fluid->getFullName(), + viewKeyStruct::referenceEnthalpyString() ), + InputError ); + } + + integer const dim0 = m_coefficients.size( 0 ); + integer const dim1 = m_coefficients.size( 1 ); + integer const dim2 = m_coefficients.size( 2 ); + + // First dimension must be equal to number of phases + GEOS_THROW_IF_NE_MSG( dim0, numPhases, + GEOS_FMT( "{}: '{}' the first dimension must be equal to the number of phases {}", + fluid->getFullName(), + viewKeyStruct::componentHeatCapacityCoefficientsString(), + numPhases ), + InputError ); + // Second dimension must be equal to number of components + GEOS_THROW_IF_NE_MSG( dim1, numComps, + GEOS_FMT( "{}: '{}' the second dimension must be equal to the number of components {}", + fluid->getFullName(), + viewKeyStruct::componentHeatCapacityCoefficientsString(), + numComps ), + InputError ); + // Third dimension must be equal to 4 + GEOS_THROW_IF_NE_MSG( dim2, 5, + GEOS_FMT( "{}: '{}' the third dimension must be equal 5", + fluid->getFullName(), + viewKeyStruct::componentHeatCapacityCoefficientsString() ), + InputError ); +} + +} // end namespace compositional + +} // end namespace constitutive + +} // end namespace geos diff --git a/src/coreComponents/constitutive/fluid/multifluid/compositional/parameters/HeatCapacityCoefficients.hpp b/src/coreComponents/constitutive/fluid/multifluid/compositional/parameters/HeatCapacityCoefficients.hpp new file mode 100644 index 00000000000..fba56cfd1a4 --- /dev/null +++ b/src/coreComponents/constitutive/fluid/multifluid/compositional/parameters/HeatCapacityCoefficients.hpp @@ -0,0 +1,66 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file HeatCapacityCoefficients.hpp + */ + +#ifndef GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_COMPOSITIONAL_PARAMETERS_HEATCAPACITYCOEFFICIENTS_HPP_ +#define GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_COMPOSITIONAL_PARAMETERS_HEATCAPACITYCOEFFICIENTS_HPP_ + +#include "ModelParameters.hpp" +#include "common/DataTypes.hpp" + +namespace geos +{ + +namespace constitutive +{ + +namespace compositional +{ + +class HeatCapacityCoefficients : public ModelParameters +{ +public: + HeatCapacityCoefficients( std::unique_ptr< ModelParameters > parameters ); + ~HeatCapacityCoefficients() override = default; + + static std::unique_ptr< ModelParameters > create( std::unique_ptr< ModelParameters > parameters ); + + struct viewKeyStruct + { + static constexpr char const * enthalpyReferenceTemperatureString() { return "enthalpyReferenceTemperature"; } + static constexpr char const * referenceEnthalpyString() { return "referenceEnthalpy"; } + static constexpr char const * componentHeatCapacityCoefficientsString() { return "componentHeatCapacityCoefficients"; } + }; + + array1d< real64 > m_referenceTemperature; + array2d< real64 > m_referenceEnthalpy; + array3d< real64 > m_coefficients; + +protected: + void registerParametersImpl( MultiFluidBase * fluid ) override; + + void postInputInitializationImpl( MultiFluidBase const * fluid, ComponentProperties const & componentProperties ) override; +}; + +} // end namespace compositional + +} // end namespace constitutive + +} // end namespace geos + +#endif //GEOS_CONSTITUTIVE_FLUID_MULTIFLUID_COMPOSITIONAL_PARAMETERS_HEATCAPACITYCOEFFICIENTS_HPP_ diff --git a/src/coreComponents/constitutive/unitTests/CMakeLists.txt b/src/coreComponents/constitutive/unitTests/CMakeLists.txt index 9dfbfc2c8a3..be40ec35dba 100644 --- a/src/coreComponents/constitutive/unitTests/CMakeLists.txt +++ b/src/coreComponents/constitutive/unitTests/CMakeLists.txt @@ -4,6 +4,7 @@ set( gtest_geosx_tests testCO2SpycherPruessModels.cpp testCO2BrinePVTModels.cpp testCompositionalDensity.cpp + testCompositionalEnthalpy.cpp testCompositionalPhillipsBrineDensity.cpp testCompositionalPhillipsBrineViscosity.cpp testCompositionalProperties.cpp diff --git a/src/coreComponents/constitutive/unitTests/testCompositionalEnthalpy.cpp b/src/coreComponents/constitutive/unitTests/testCompositionalEnthalpy.cpp new file mode 100644 index 00000000000..00d7377fa43 --- /dev/null +++ b/src/coreComponents/constitutive/unitTests/testCompositionalEnthalpy.cpp @@ -0,0 +1,245 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * SPDX-License-Identifier: LGPL-2.1-only + * + * Copyright (c) 2016-2024 Lawrence Livermore National Security LLC + * Copyright (c) 2018-2024 TotalEnergies + * Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University + * Copyright (c) 2023-2024 Chevron + * Copyright (c) 2019- GEOS/GEOSX Contributors + * All rights reserved + * + * See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details. + * ------------------------------------------------------------------------------------------------------------ + */ + +// Source includes +#include "codingUtilities/UnitTestUtilities.hpp" +#include "constitutive/fluid/multifluid/compositional/models/CompositionalEnthalpy.hpp" +#include "constitutive/fluid/multifluid/compositional/parameters/EquationOfState.hpp" +#include "constitutive/fluid/multifluid/compositional/parameters/HeatCapacityCoefficients.hpp" + +#include "TestFluid.hpp" +#include "TestFluidUtilities.hpp" + +using namespace geos::constitutive::compositional; + +namespace geos +{ +namespace testing +{ + +template< int NC > +using EnthalpyData = std::tuple< + real64 const, // pressure + real64 const, // temperature + Feed< NC > const, // phase composition + real64 const // expected enthalpy + >; + +template< int NC > +struct FluidData {}; + +template<> +struct FluidData< 4 > +{ + static std::unique_ptr< TestFluid< 4 > > createFluid() + { + return TestFluid< 4 >::create( {Fluid::CO2, Fluid::H2, Fluid::CH4, Fluid::C2H6} ); + } + + static void populateCoefficients( HeatCapacityCoefficients * coefficients ) + { + coefficients->m_referenceTemperature.resize( 4 ); + coefficients->m_referenceTemperature.zero(); + coefficients->m_referenceEnthalpy.resize( 1, 4 ); + coefficients->m_referenceEnthalpy.zero(); + coefficients->m_coefficients.resize( 1, 4, 5 ); + std::array< real64, 5*4 > coefficientsData{ + 0.0, 0.0, 0.0, 0.0, 0.0, + 2.883, 0.003681, -7.720e-06, 6.920e-09, -2.130e-12, + 4.568, -0.008975, 3.631e-05, -3.407e-08, 1.091e-11, + 4.178, -0.004427, 5.660e-05, -6.651e-08, 2.487e-11 + }; + for( int ic = 0; ic < 4; ++ic ) + { + for( int j = 0; j < 5; ++j ) + { + coefficients->m_coefficients( 0, ic, j ) = coefficientsData[ic*5 + j]; + } + } + } +}; + +template< int NC, EquationOfStateType EOS_TYPE > +class CompositionalEnthalpyTestFixture : public ::testing::TestWithParam< EnthalpyData< NC > > +{ + static constexpr real64 relTol = 1.0e-5; + static constexpr real64 absTol = 1.0e-7; + static constexpr int numComps = NC; + static constexpr int numDofs = NC + 2; + using Deriv = geos::constitutive::multifluid::DerivativeOffset; +public: + CompositionalEnthalpyTestFixture() + : m_fluid( FluidData< NC >::createFluid() ) + { + ComponentProperties const & componentProperties = this->m_fluid->getComponentProperties(); + m_parameters = CompositionalEnthalpy::createParameters( std::make_unique< ModelParameters >() ); + + auto equationOfState = const_cast< EquationOfState * >(m_parameters->get< EquationOfState >()); + string const eosName = EnumStrings< EquationOfStateType >::toString( EOS_TYPE ); + equationOfState->m_equationsOfStateNames.emplace_back( eosName ); + + auto heatCapacityCoefficients = const_cast< HeatCapacityCoefficients * >(m_parameters->get< HeatCapacityCoefficients >()); + FluidData< NC >::populateCoefficients( heatCapacityCoefficients ); + + string const name = GEOS_FMT( "PhaseEnthalpy{}{}", eosName, NC ); + m_enthalpy = std::make_unique< CompositionalEnthalpy >( name, componentProperties, 0, *m_parameters ); + } + + ~CompositionalEnthalpyTestFixture() = default; + + void testEnthalpyValues( EnthalpyData< NC > const & data ) + { + real64 const pressure = std::get< 0 >( data ); + //real64 const temperature = std::get< 1 >( data ); + stackArray1d< real64, numComps > phaseComposition; + TestFluid< NC >::createArray( phaseComposition, std::get< 2 >( data )); + //real64 const expectedEnthalpy = std::get< 3 >( data ); + + auto componentProperties = m_fluid->createKernelWrapper(); + auto kernelWrapper = m_enthalpy->createKernelWrapper(); + + real64 enthalpy = 0.0; + stackArray1d< real64, numDofs > tempDerivs( numDofs ); + + for( real64 t = 250.00; t <= 799.00; t += 50.0 ) + { + kernelWrapper.compute( componentProperties, + pressure, + t, + phaseComposition.toSliceConst(), + enthalpy, + tempDerivs.toSlice(), + false ); + std::cout << std::fixed << std::setprecision( 0 ) << t << " " + << std::fixed << std::setprecision( 5 ) << enthalpy << " " + << std::fixed << std::setprecision( 5 ) << tempDerivs[1] << " " + << std::endl; + } + //checkRelativeError( enthalpy, expectedEnthalpy, relTol, absTol ); + } + + void testEnthalpyDerivatives( EnthalpyData< NC > const & data ) + { + real64 const pressure = std::get< 0 >( data ); + real64 const temperature = std::get< 1 >( data ); + stackArray1d< real64, numComps > phaseComposition; + TestFluid< NC >::createArray( phaseComposition, std::get< 2 >( data )); + + auto componentProperties = m_fluid->createKernelWrapper(); + auto kernelWrapper = m_enthalpy->createKernelWrapper(); + + real64 enthalpy = 0.0; + stackArray2d< real64, 2*numDofs > derivSpace( 2, numDofs ); + arraySlice1d< real64 > enthalpyDerivs = derivSpace[0]; + arraySlice1d< real64 > tempDerivs = derivSpace[1]; + + kernelWrapper.compute( componentProperties, + pressure, + temperature, + phaseComposition.toSliceConst(), + enthalpy, + enthalpyDerivs, + false ); + // Compare against numerical derivatives + // -- Pressure derivative + real64 const dp = 1.0e-4 * pressure; + internal::testNumericalDerivative( + pressure, dp, enthalpyDerivs[Deriv::dP], + [&]( real64 const p ){ + real64 displacedEnthalpy = 0.0; + kernelWrapper.compute( componentProperties, + p, + temperature, + phaseComposition.toSliceConst(), + displacedEnthalpy, + tempDerivs, + false ); + return displacedEnthalpy; + } ); + + // -- Temperature derivative + real64 const dT = 1.0e-6 * temperature; + internal::testNumericalDerivative( + temperature, dT, enthalpyDerivs[Deriv::dT], + [&]( real64 const t ){ + real64 displacedEnthalpy = 0.0; + kernelWrapper.compute( componentProperties, + pressure, + t, + phaseComposition.toSliceConst(), + displacedEnthalpy, + tempDerivs, + false ); + return displacedEnthalpy; + } ); + + // -- Composition derivatives + real64 constexpr dz = 1.0e-6; + for( integer ic = 0; ic < numComps; ++ic ) + { + internal::testNumericalDerivative( + 0.0, dz, enthalpyDerivs[Deriv::dC+ic], + [&]( real64 const z ){ + real64 const z_old = phaseComposition[ic]; + phaseComposition[ic] += z; + real64 displacedEnthalpy = 0.0; + kernelWrapper.compute( componentProperties, + pressure, + temperature, + phaseComposition.toSliceConst(), + displacedEnthalpy, + tempDerivs, + false ); + phaseComposition[ic] = z_old; + return displacedEnthalpy; + } ); + } + } + +protected: + std::unique_ptr< CompositionalEnthalpy > m_enthalpy{}; + std::unique_ptr< TestFluid< NC > > m_fluid{}; + std::unique_ptr< ModelParameters > m_parameters{}; +}; + +using PengRobinson = CompositionalEnthalpyTestFixture< 4, EquationOfStateType::PengRobinson >; + +TEST_P( PengRobinson, testEnthalpyValues ) +{ + testEnthalpyValues( GetParam() ); +} +/** + TEST_P( PengRobinson, testEnthalpyDerivatives ) + { + testEnthalpyDerivatives( GetParam() ); + } + **/ +/* UNCRUSTIFY-OFF */ + +// Test data + +INSTANTIATE_TEST_SUITE_P( + CompositionalEnthalpyTest, PengRobinson, + ::testing::ValuesIn>( { + {1.0e+05, 288.15, {0.000, 1.000, 0.000, 0.000}, 5.54544e+04}, + {1.0e+06, 288.15, {0.000, 0.000, 1.000, 0.000}, 5.54769e+04} + } ) +); + +/* UNCRUSTIFY-ON */ + +} // testing + +} // geos