Well manager#4064
Conversation
| #include "physicsSolvers/fluidFlow/wells/WellInjectionConstraint.hpp" | ||
| #include "physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp" | ||
| #include "physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp" | ||
| #include "physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp" | ||
| #include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp" | ||
| #include "physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp" | ||
| #include "physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp" |
There was a problem hiding this comment.
I have moved these includes to the .cpp files that require them. An attempt to improve compile times.
| /**@}*/ | ||
| /** | ||
| * @brief Apply a given functor to a container if the container can be | ||
| * cast to one of the specified types. | ||
| * @tparam CASTTYPE the first type that will be used in the attempted casting of container | ||
| * @tparam CASTTYPES a variadic list of types that will be used in the attempted casting of container | ||
| * @tparam CONTAINERTYPE the type of container | ||
| * @tparam LAMBDA the type of lambda function to call in the function | ||
| * @param[in] container a pointer to the container which will be passed to the lambda function | ||
| * @param[in] lambda the lambda function to call in the function | ||
| * @return a boolean to indicate whether the lambda was successfully applied to the container. | ||
| */ | ||
| template< typename T0, typename T1, typename ... CASTTYPES, typename CONTAINERTYPE, typename LAMBDA > | ||
| static bool applyLambdaToContainer( CONTAINERTYPE container, LAMBDA && lambda ) | ||
| { | ||
| using Pointee = std::remove_pointer_t< std::remove_reference_t< CONTAINERTYPE > >; | ||
| using T = std::conditional_t< std::is_const< Pointee >::value, T0 const, T0 >; | ||
| T * const castedContainer = dynamic_cast< T * >( container ); | ||
|
|
||
| if( castedContainer != nullptr ) | ||
| { | ||
| lambda( *castedContainer ); | ||
| return true; | ||
| } | ||
|
|
||
| return applyLambdaToContainer< T1, CASTTYPES... >( container, std::forward< LAMBDA >( lambda ) ); | ||
| } | ||
|
|
||
| // Base case: no more types to try | ||
| template< typename CONTAINERTYPE, typename LAMBDA > | ||
| static bool applyLambdaToContainer( CONTAINERTYPE /*container*/, LAMBDA && /*lambda*/ ) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| // Single-type overload: try only T0 and stop | ||
| template< typename T0, typename CONTAINERTYPE, typename LAMBDA > | ||
| static bool applyLambdaToContainer( CONTAINERTYPE container, LAMBDA && lambda ) | ||
| { | ||
| using Pointee = std::remove_pointer_t< std::remove_reference_t< CONTAINERTYPE > >; | ||
| using T = std::conditional_t< std::is_const< Pointee >::value, T0 const, T0 >; | ||
| T * const castedContainer = dynamic_cast< T * >( container ); | ||
|
|
||
| if( castedContainer != nullptr ) | ||
| { | ||
| lambda( *castedContainer ); | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * @copydoc forInjectionConstraints(LAMBDA &&) | ||
| */ | ||
| template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA > | ||
| void forInjectionConstraints( LAMBDA && lambda ) const | ||
| { | ||
| for( auto const * constraintIter : m_injectionRateConstraintList ) | ||
| { | ||
| applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( constraintIter, [&]( auto const & castedSubGroup ) | ||
| { | ||
| lambda( castedSubGroup ); | ||
| } ); | ||
| } | ||
| } | ||
|
|
||
| // non-const overload | ||
| template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA > | ||
| void forInjectionConstraints( LAMBDA && lambda ) | ||
| { | ||
| for( auto * constraintIter : m_injectionRateConstraintList ) | ||
| { | ||
| applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( constraintIter, [&]( auto & castedSubGroup ) | ||
| { | ||
| lambda( castedSubGroup ); | ||
| } ); | ||
| } | ||
| } | ||
| /** | ||
| * @copydoc forProductionConstraints(LAMBDA &&) | ||
| */ | ||
| template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA > | ||
| void forProductionConstraints( LAMBDA && lambda ) const | ||
| { | ||
| for( auto const * constraintIter : m_productionRateConstraintList ) | ||
| { | ||
| applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( constraintIter, [&]( auto const & castedSubGroup ) | ||
| { | ||
| lambda( castedSubGroup ); | ||
| } ); | ||
| } | ||
| } | ||
|
|
||
| // non-const overload | ||
| template< typename GROUPTYPE = Group, typename ... GROUPTYPES, typename LAMBDA > | ||
| void forProductionConstraints( LAMBDA && lambda ) | ||
| { | ||
| for( auto * constraintIter : m_productionRateConstraintList ) | ||
| { | ||
| applyLambdaToContainer< GROUPTYPE, GROUPTYPES... >( constraintIter, [&]( auto & castedSubGroup ) | ||
| { | ||
| lambda( castedSubGroup ); | ||
| } ); | ||
| } | ||
| } |
There was a problem hiding this comment.
The main idea is not to repeat these methods which are already implemented by the dataRepository. We will leave those methods and simply use them here.
| /// string key for the minimum BHP presssure for a producer | ||
| static constexpr char const * minimumBHPConstraintString() { return "MinimumBHPConstraint"; } | ||
| /// string key for the maximum BHP presssure for a injection | ||
| static constexpr char const * maximumBHPConstraintString() { return "MaximumBHPConstraint"; } | ||
| /// string key for the maximum phase rate for a producer | ||
| static constexpr char const * productionPhaseVolumeRateConstraintString() { return "ProductionPhaseVolumeRateConstraint"; } | ||
| /// string key for the maximum phase rate for a injection | ||
| static constexpr char const * injectionPhaseVolumeRateConstraint() { return "InjectionPhaseVolumeRateConstraint"; } | ||
| /// string key for the maximum volume rate for a producer | ||
| static constexpr char const * productionVolumeRateConstraint() { return "ProductionVolumeRateConstraint"; } | ||
| /// string key for the maximum volume rate for a injector | ||
| static constexpr char const * injectionVolumeRateConstraint() { return "InjectionVolumeRateConstraint"; } | ||
| /// string key for the maximum mass rate for a producer | ||
| static constexpr char const * productionMassRateConstraint() { return "ProductionMassRateConstraint"; } | ||
| /// string key for the maximum mass rate for a injector | ||
| static constexpr char const * injectionMassRateConstraint() { return "InjectionMassRateConstraint"; } | ||
| /// string key for the liquid rate for a producer | ||
| static constexpr char const * productionLiquidRateConstraint() { return "ProductionLiquidRateConstraint"; } |
There was a problem hiding this comment.
Each of these keys is defined by it's own class. So we don't need to repeat them here.
| struct groupKeyStruct | ||
| { | ||
| /// string key for the well Newton solver | ||
| static constexpr char const * wellNewtonSolverString() { return "WellNewtonSolver"; } | ||
| }; |
There was a problem hiding this comment.
I have split this out from the viewStruct just to highlight that this isn't an xml field but rather a subGroup.
| std::vector< WellConstraintBase * > getInjRateConstraints() { return m_injectionRateConstraintList; } | ||
| std::vector< WellConstraintBase * > getInjRateConstraints() const { return m_injectionRateConstraintList; } | ||
| stdVector< WellConstraintBase const * > getAllConstraints() const; | ||
| stdVector< WellConstraintBase * > getAllConstraints(); |
There was a problem hiding this comment.
Here I am assuming that a well can only be a producer or an injector. So only constraints that apply to the type of well are relevant.
| m_isProducer( wellControls.isProducer() ), | ||
| m_currentControl( wellControls.getControl() ), | ||
| m_constraintValue ( wellControls.getCurrentConstraint()->getConstraintValue( time )), | ||
| m_targetBHP( wellControls.getTargetBHP( time ) ), |
There was a problem hiding this comment.
I'm enforcing the inclusion of a BHP constraint at all times. So this should always be defined. The decision of whether it's a producer or injector is in the WellControls already.
| m_iwelemControl( subRegion.getTopWellElementIndex() ), | ||
| m_isProducer( wellControls.isProducer() ), | ||
| m_currentControl( wellControls.getControl() ), | ||
| m_targetBHP( wellControls.getTargetBHP( time ) ), |
There was a problem hiding this comment.
I'm enforcing the inclusion of a BHP constraint at all times. So this should always be defined. The decision of whether it's a producer or injector is in the WellControls already.
| auto const * rateConstraint = wellControls.getRateConstraints().front(); | ||
| if( rateConstraint != nullptr ) | ||
| { | ||
| m_constraintValue = rateConstraint->getConstraintValue( time ); | ||
| } |
There was a problem hiding this comment.
This takes the first rate constraint regardless of whether that's mass, totalVolume or phaseVolume.
| else if( control == WellControls::Control::MASSRATE ) | ||
| { | ||
| connRate[iwelem] = constraintVal; | ||
| } |
There was a problem hiding this comment.
I added this here but I'm not sure if this is correct.
| { | ||
|
|
||
| // create list of all constraints to process | ||
| std::vector< WellConstraintBase * > constraintList; |
There was a problem hiding this comment.
I am quite confused by the procedure here. I can't tell what the difference between a limiting constraint and a current constraint is and how this interacts with the BHP constraint. I think in some cases we end up with the same constraint being added to the list twice.
There was a problem hiding this comment.
Pull request overview
This PR refactors well constraint creation and access to rely more on the dataRepository::Group catalog/subgroup mechanisms, reducing duplicated “manager” logic inside WellControls. It also centralizes reference-region validation and adds stronger constraint validation during input initialization.
Changes:
- Replace bespoke constraint factory/list management in
WellControlswith catalog-basedcreateChild()and subgroup iteration helpers (getBHPConstraint(),getRateConstraints(),getAllConstraints()). - Add input-time validation for constraint consistency (producer vs injector constraints, duplicates, and required presence of BHP + at least one rate constraint).
- Centralize reference-reservoir-region checks and statistics retrieval via
WellControlshelpers, used by both single-phase and compositional wells.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/coreComponents/physicsSolvers/fluidFlow/wells/WellInjectionConstraint.cpp | Adjusts catalog registration/instantiation for injection constraints (liquid-rate registration currently commented out). |
| src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.hpp | Removes constraint header includes, adds forward decls and new constraint accessors/validation helpers; minor include cleanup needed. |
| src/coreComponents/physicsSolvers/fluidFlow/wells/WellControls.cpp | Moves constraint includes to .cpp; implements catalog-based child creation, constraint validation, logging helper, and reference-region validation/statistics helpers. |
| src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.hpp | Makes setNextDtFromTables const and setNextDtFromTable static for safer use via const pointers. |
| src/coreComponents/physicsSolvers/fluidFlow/wells/WellConstraintsBase.cpp | Updates implementations to match const/static signature changes. |
| src/coreComponents/physicsSolvers/fluidFlow/wells/SinglePhaseWell.cpp | Uses centralized reference-region statistics validation instead of duplicating logic. |
| src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalSinglePhaseWellKernels.hpp | Switches BHP target normalization to use WellControls::getTargetBHP(). |
| src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/ThermalCompositionalMultiphaseWellKernels.hpp | Uses getTargetBHP() and rate-constraint accessor for normalization. |
| src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.hpp | Uses getTargetBHP() for normalization. |
| src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/SinglePhaseWellKernels.cpp | Uses new rate-constraint accessor and adds explicit MASSRATE handling in rate initialization. |
| src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.hpp | Uses getTargetBHP() and rate-constraint accessor for normalization. |
| src/coreComponents/physicsSolvers/fluidFlow/wells/kernels/CompositionalMultiphaseWellKernels.cpp | Uses new rate-constraint accessor for rate initialization. |
| src/coreComponents/physicsSolvers/fluidFlow/wells/CompositionalMultiphaseWell.cpp | Removes duplicated reference-region validation/statistics logic in favor of WellControls helpers. |
| #include "physicsSolvers/PhysicsSolverBase.hpp" | ||
| #include "common/format/EnumStrings.hpp" | ||
| #include "dataRepository/Group.hpp" | ||
| #include "functions/TableFunction.hpp" | ||
| #include "constitutive/fluid/multifluid/MultiFluidBase.hpp" | ||
|
|
||
| #include "physicsSolvers/fluidFlow/wells/WellInjectionConstraint.hpp" | ||
| #include "physicsSolvers/fluidFlow/wells/WellProductionConstraint.hpp" | ||
| #include "physicsSolvers/fluidFlow/wells/WellBHPConstraints.hpp" | ||
| #include "physicsSolvers/fluidFlow/wells/WellVolumeRateConstraint.hpp" | ||
| #include "physicsSolvers/fluidFlow/wells/WellPhaseVolumeRateConstraint.hpp" | ||
| #include "physicsSolvers/fluidFlow/wells/WellMassRateConstraint.hpp" | ||
| #include "physicsSolvers/fluidFlow/wells/WellLiquidRateConstraint.hpp" | ||
| #include "constitutive/fluid/multifluid/MultiFluidBase.hpp" | ||
| #include "constitutive/fluid/singlefluid/SingleFluidBase.hpp" | ||
| #include "physicsSolvers/fluidFlow/wells/WellNewtonSolver.hpp" |
| child = &liquidConstraint; | ||
| } | ||
| return child; | ||
| GEOS_LOG_RANK_0( GEOS_FMT( "{}: adding {} {}", getName(), childKey, childName ) ); |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
The purpose of this is mainly to avoid duplicating methods that are already defined in the
dataRepository.