feat: well_manager + well estimator#3972
Conversation
…aint class hierachy , 3) Remove all old constraint handling methods/variables, 4) it compiles ...
…lephase still needs testing
…t compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml
… to fixed and inputFiles regenerated
…d convert compositionalMultiphaseFlow/soreideWhitson/1D_100cells/1D_benchmark.xml" This reverts commit fa61ab1.
…well initialization vagaries assoiated with useSurfaceConditions=0
…for resvol constraint, compositional only
| // set the reference well element where the BHP control is applied | ||
| wellControls.setReferenceGravityCoef( refElev * gravVector[2] ); | ||
| wellControls.setReferenceGravityCoef( refElev * gravVector[2] ); // tjb remove |
| rankNegPressureIds, | ||
| rankNegDensityIds, | ||
| rankTotalNegDensityIds ); | ||
| globalCheck = MpiWrapper::min( localCheck ); |
There was a problem hiding this comment.
Should we move this MPI call outside the for loop (for performance reasons)?
| for( integer i=0; i<numNorm; i++ ) | ||
| { | ||
| localResidualNorm[i] = 0.0; | ||
| } |
There was a problem hiding this comment.
If I'm reading it correctly, there might be an issue when an open unconverged well is followed by a closed well. This branch resets the manager's accumulated maximum residual to zero, while I think closed wells should contribute nothing without clearing residuals already calculated for other wells.
There was a problem hiding this comment.
I think optionizing the wellResidualNorm on open wells and then max the localResidualNorm sounds safer
if( wellControls.isWellOpen() )
{
wellResidualNorm = wellControls.calculateLocalWellResidualNorm( time_n, dt,
m_nonlinearSolverParameters,
subRegion, dofManager, localRhs );
for( integer i = 0; i < numNorm; i++ )
{
localResidualNorm[i] = LvArray::math::max( localResidualNorm[i], wellResidualNorm[i] );
}
}
| ENUM_STRINGS( WellControls_Control, | ||
| "BHP", | ||
| "phaseVolRate", | ||
| "totalVolRate", | ||
| "massRate", | ||
| "uninitialized" ); |
There was a problem hiding this comment.
The enum ConstraintTypeId has 7 values, while we are listing only five here. Should we update this list?
| // Assumes useMass is true | ||
| currentMassRate = currentTotalRate; |
There was a problem hiding this comment.
Should we add a check here on useMass instead of assuming it's true?
| } | ||
| void WellManager::initializePostSubGroups() | ||
| { | ||
| #if 0 |
There was a problem hiding this comment.
Just curious why this code block is ifdef'ed out
| // 13.4) Make sure there is at least one non-BHP constraint | ||
| bool const rate_match_found = std::any_of( constraints.begin(), constraints.end(), [&bhp_type]( const auto & constraint_tuple ) | ||
| { | ||
| return std::get< 1 >( constraint_tuple ) != bhp_type; | ||
| } ); | ||
| GEOS_THROW_IF( !rate_match_found, | ||
| GEOS_FMT( "Missing rate constraint for {} well {}", | ||
| (isProducerWell ? "producer" : "injector"), getName() ), |
There was a problem hiding this comment.
if a well has phaseVolRate while defining only a total-volume rate constraint, this check would flag things are good, but i guess initialization would hit issues later.
| string const bhp_type = isProducerWell ? MinimumBHPConstraint::catalogName() : MaximumBHPConstraint::catalogName(); | ||
| bool const no_match_found = std::none_of( constraints.begin(), constraints.end(), [&bhp_type]( const auto & constraint_tuple ) | ||
| { | ||
| return std::get< 1 >( constraint_tuple ) == bhp_type; | ||
| } ); | ||
| GEOS_THROW_IF( no_match_found, |
There was a problem hiding this comment.
When the sole BHP or rate constraint has constraintActive=0, these existence checks still accept it, while getBHPConstraint and the rate-list helpers filter it out. Subsequent selection either dereferences null or erases an end() iterator because the current constraint is absent from the active list.
| if( !hasZeroRate ) | ||
| { | ||
| for( auto const * constraint : getRateConstraints() ) | ||
| { | ||
| if( isZero( constraint->getConstraintValue( currentTime ) ) ) | ||
| { | ||
| hasZeroRate = true; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
When one active secondary rate schedule evaluates to zero while another rate target remains positive, this loop closes the entire well on the first zero (code below). Should we close the well only when every active rate target is zero?
| well.setThermal( isThermal() ); | ||
| well.setUseMass( m_useMass ); | ||
| well.registerWellDataOnMesh( subRegion ); |
There was a problem hiding this comment.
Shouldn't this also be setting useTotalMassEquation and allowLocalCompDensityChopping?
| forSubGroups< InjectionConstraint< PhaseVolumeRateConstraint >, ProductionConstraint< PhaseVolumeRateConstraint > >( [&]( auto & constraint ) | ||
| { | ||
| constraint.validatePhaseType( fluid ); | ||
| } ); |
There was a problem hiding this comment.
When a liquid-rate constraint becomes limiting, only phase-volume constraints have their phase names validated here. The liquid constraint's m_phaseIndices remains empty, which I believe could lead to issues during assembly (zero Jacobian).
| { | ||
| real64 const currentValue = currentConstraint.liquidRate(); | ||
| real64 const constraintValue = this->getConstraintValue( currentTime ); | ||
| return ( LvArray::math::abs( currentValue ) <= LvArray::math::abs( constraintValue ) ); |
There was a problem hiding this comment.
If I read correctly, for a production liquid-rate maximum, this comparison marks values below the target as violations and accepts values above it. This is the reverse of the other maximum-rate constraints and switches control incorrectly.
| * @param domain the physical domain object | ||
| */ | ||
| void chopNegativeDensities( DomainPartition & domain ); | ||
| #if 0 |
| m_useMass( false ), | ||
| m_useTotalMassEquation( 1 ), | ||
| m_isThermal( 0 ), | ||
| m_isCompositional( true ), |
There was a problem hiding this comment.
should'nt it be an integer and registered for restart ? what happen if we want to restard a non-compositional well ?
| array1d< real64 > localResidualNorm, wellResidalNorm; | ||
| array1d< real64 > localResidualNormalizer; | ||
|
|
||
| if( isThermal() ) |
There was a problem hiding this comment.
Sticking to conventionm shouldn't it be set by NDOF or so ?
| if( wellResidalNorm[i] > localResidualNorm[i] ) | ||
| { | ||
| localResidualNorm[i] = wellResidalNorm[i]; | ||
| } |
There was a problem hiding this comment.
| if( wellResidalNorm[i] > localResidualNorm[i] ) | |
| { | |
| localResidualNorm[i] = wellResidalNorm[i]; | |
| } | |
| localResidualNorm[i] = LvArray::math::max( localResidualNorm[i], wellResidualNorm[i] ); |
| // step 1: compute the norm in the subRegion | ||
| if( wellControls.isWellOpen( ) ) | ||
| { | ||
| wellResidalNorm = wellControls.calculateLocalWellResidualNorm( time_n, |
There was a problem hiding this comment.
| wellResidalNorm = wellControls.calculateLocalWellResidualNorm( time_n, | |
| wellResidualNorm = wellControls.calculateLocalWellResidualNorm( time_n, |
| for( integer i=0; i<numNorm; i++ ) | ||
| { | ||
| localResidualNorm[i] = 0.0; | ||
| } |
There was a problem hiding this comment.
I think optionizing the wellResidualNorm on open wells and then max the localResidualNorm sounds safer
if( wellControls.isWellOpen() )
{
wellResidualNorm = wellControls.calculateLocalWellResidualNorm( time_n, dt,
m_nonlinearSolverParameters,
subRegion, dofManager, localRhs );
for( integer i = 0; i < numNorm; i++ )
{
localResidualNorm[i] = LvArray::math::max( localResidualNorm[i], wellResidualNorm[i] );
}
}
Replaces PR #3735 and extends PR #3971
Overview
This PR introduces
An improved well schema layout and code refactor better suited for well modeling. The previous implementation primarily targeted Jacobian generation requirements for the coupled reservoir and well system.
The well estimator is used to select the active well constraint by solving the well system assuming fixed reservoir conditions and selecting the constraint with the highest or lowest well flowing pressure.
This is a breaking change, detailed migration instructions and migration script are posted at #4081