mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-24 14:17:52 -05:00
#14958 Sim Events: Move PRESSURE_COMPONENTS to WELSPECS
Move Pressure Drop and Length and Depth from the segment collection to the well path completion settings. PRESSURE_COMPONENTS is now a dated WELSPECS attribute used for the WELSEGS header at export, and is rejected on SEGMENT events.
This commit is contained in:
1 parent
2f86d4c210
commit
c56e921160
21 files changed
+326
-186
No files matched your search
+3
-3
@@ -83,7 +83,7 @@ std::vector<RigMswBranch> buildLateralBranches( RimEclipseCase*
|
||||
auto mswParameters = wellPath->segmentCollection();
|
||||
if ( !mswParameters ) return result;
|
||||
|
||||
const std::string infoType = mswParameters->lengthAndDepth().text().toStdString();
|
||||
const std::string infoType = wellPath->completionSettings()->lengthAndDepth().text().toStdString();
|
||||
const std::vector<std::pair<double, double>> segmentIntervals = mswParameters->getSegmentIntervals();
|
||||
const double tieInMD = wellPath->wellPathTieIn()->tieInMeasuredDepth();
|
||||
const double tieInTVD = -wellPath->wellPathGeometry()->interpolatedPointAlongWellPath( tieInMD ).z();
|
||||
@@ -313,7 +313,7 @@ RigMswWellExportData buildMswWellExportData( RimEclipseCase*
|
||||
|
||||
const RiaDefines::EclipseUnitSystem unitSystem = eclipseCase->eclipseCaseData()->unitsType();
|
||||
const RigMainGrid* mainGrid = eclipseCase->mainGrid();
|
||||
const std::string infoType = mswParameters->lengthAndDepth().text().toStdString();
|
||||
const std::string infoType = wellPath->completionSettings()->lengthAndDepth().text().toStdString();
|
||||
|
||||
auto cellIntersections = RicWellPathExportMswTableData::generateCellSegments( eclipseCase, wellPath );
|
||||
double initialMD = RicWellPathExportMswTableData::computeIntitialMeasuredDepth( eclipseCase, wellPath, mswParameters, cellIntersections );
|
||||
@@ -327,7 +327,7 @@ RigMswWellExportData buildMswWellExportData( RimEclipseCase*
|
||||
header.topLength = initialMD;
|
||||
header.topDepth = initialTVD;
|
||||
header.infoType = infoType;
|
||||
header.pressureComponents = mswParameters->pressureDrop().text().toStdString();
|
||||
header.pressureComponents = wellPath->completionSettings()->pressureDrop().text().toStdString();
|
||||
|
||||
const bool includePerforations = ( completionType & CompletionType::PERFORATIONS ) == CompletionType::PERFORATIONS;
|
||||
const std::vector<const RimPerforationInterval*> perforationIntervals =
|
||||
|
||||
@@ -66,6 +66,37 @@ bool eventBelongsToWell( const RimWellEvent& event, const RimWellPath& well )
|
||||
const RimWellPath* eventWell = event.wellPath();
|
||||
return eventWell && exportNameForWell( *eventWell ) == exportNameForWell( well );
|
||||
}
|
||||
|
||||
// WELSPECS settings in effect for a well at a date: the latest WELSPECS event at or before the date, otherwise the
|
||||
// baseline captured by the first event. Returns nullopt when the well has no WELSPECS events.
|
||||
std::optional<RimWellSpecData> wellSpecDataAtDate( const RimWellEventTimeline& timeline, const RimWellPath& well, const QDateTime& date )
|
||||
{
|
||||
const RimWellEventWellSpec* firstWellSpec = nullptr;
|
||||
const RimWellEventWellSpec* latestWellSpec = nullptr;
|
||||
for ( auto* event : timeline.events() )
|
||||
{
|
||||
if ( event->eventType() != RimWellEvent::EventType::WELLSPEC || !eventBelongsToWell( *event, well ) ) continue;
|
||||
|
||||
auto* wellSpec = dynamic_cast<RimWellEventWellSpec*>( event );
|
||||
if ( !wellSpec ) continue;
|
||||
if ( !firstWellSpec || wellSpec->eventDate() < firstWellSpec->eventDate() ) firstWellSpec = wellSpec;
|
||||
if ( wellSpec->eventDate() <= date && ( !latestWellSpec || wellSpec->eventDate() > latestWellSpec->eventDate() ) )
|
||||
{
|
||||
latestWellSpec = wellSpec;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<RimWellSpecData> wellSpecData;
|
||||
if ( latestWellSpec )
|
||||
{
|
||||
wellSpecData = latestWellSpec->wellSpecData();
|
||||
}
|
||||
else if ( firstWellSpec )
|
||||
{
|
||||
wellSpecData = firstWellSpec->baselineData();
|
||||
}
|
||||
return wellSpecData;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -388,7 +419,7 @@ std::expected<QString, QString> RicScheduleDataGenerator::generateDateSection( c
|
||||
// MSW keywords describe the shared well tree, including all its laterals.
|
||||
if ( mswIt != group.end() )
|
||||
{
|
||||
generateMswForWell( eclipseCase, *( *mswIt )->topLevelWellPath(), date, events, keywordBlocks, unmergedBlocks, emittedSegmentLayouts );
|
||||
generateMswForWell( timeline, eclipseCase, *( *mswIt )->topLevelWellPath(), date, events, keywordBlocks, unmergedBlocks, emittedSegmentLayouts );
|
||||
}
|
||||
|
||||
for ( auto* well : group )
|
||||
@@ -561,30 +592,7 @@ std::optional<Opm::DeckKeyword> RicScheduleDataGenerator::generateWelspecsForWel
|
||||
|
||||
if ( !hasEvents ) return std::nullopt;
|
||||
|
||||
const RimWellEventWellSpec* firstWellSpec = nullptr;
|
||||
const RimWellEventWellSpec* latestWellSpec = nullptr;
|
||||
for ( auto* event : timeline.events() )
|
||||
{
|
||||
if ( event->eventType() != RimWellEvent::EventType::WELLSPEC || !eventBelongsToWell( *event, well ) ) continue;
|
||||
|
||||
auto* wellSpec = dynamic_cast<RimWellEventWellSpec*>( event );
|
||||
if ( !wellSpec ) continue;
|
||||
if ( !firstWellSpec || wellSpec->eventDate() < firstWellSpec->eventDate() ) firstWellSpec = wellSpec;
|
||||
if ( wellSpec->eventDate() <= date && ( !latestWellSpec || wellSpec->eventDate() > latestWellSpec->eventDate() ) )
|
||||
{
|
||||
latestWellSpec = wellSpec;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<RimWellSpecData> wellSpecData;
|
||||
if ( latestWellSpec )
|
||||
{
|
||||
wellSpecData = latestWellSpec->wellSpecData();
|
||||
}
|
||||
else if ( firstWellSpec )
|
||||
{
|
||||
wellSpecData = firstWellSpec->baselineData();
|
||||
}
|
||||
const auto wellSpecData = wellSpecDataAtDate( timeline, well, date );
|
||||
|
||||
std::string wellGroupName = well.completionSettings()->groupNameForExport().toStdString();
|
||||
auto welspecsKw = RimKeywordFactory::welspecsKeyword( wellGroupName, &eclipseCase, &well, wellSpecData ? &wellSpecData.value() : nullptr );
|
||||
@@ -639,7 +647,8 @@ void RicScheduleDataGenerator::generateCompletionsForWellGroup( RimEclipseCase&
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RicScheduleDataGenerator::generateMswForWell( RimEclipseCase& eclipseCase,
|
||||
void RicScheduleDataGenerator::generateMswForWell( const RimWellEventTimeline& timeline,
|
||||
RimEclipseCase& eclipseCase,
|
||||
RimWellPath& wellPath,
|
||||
const QDateTime& date,
|
||||
const std::vector<RimWellEvent*>& eventsAtDate,
|
||||
@@ -684,7 +693,15 @@ void RicScheduleDataGenerator::generateMswForWell( RimEclipseCase&
|
||||
|
||||
if ( !mswDataResult.has_value() ) return;
|
||||
|
||||
const auto& mswData = mswDataResult.value();
|
||||
auto& mswData = mswDataResult.value();
|
||||
|
||||
// The pressure drop components are a WELSPECS event setting, so they can change over time.
|
||||
if ( const auto wellSpecData = wellSpecDataAtDate( timeline, wellPath, date ) )
|
||||
{
|
||||
auto header = mswData.welsegsHeader();
|
||||
header.pressureComponents = RimWellPathCompletionSettings::PressureDropEnum( wellSpecData->pressureDrop ).text().toStdString();
|
||||
mswData.setWelsegsHeader( header );
|
||||
}
|
||||
|
||||
// WELSEGS and COMPSEGS are not merged across wells (see generateDateSection): each well keeps
|
||||
// its own block, appended in well order.
|
||||
|
||||
@@ -111,7 +111,9 @@ private:
|
||||
// The caller decides which wells get MSW keywords (see mswWells in generateSchedule).
|
||||
// WELSEGS / COMPSEGS are written for segment/perforation events, and for valve events when the
|
||||
// segment layout differs from the one last written for the well (tracked in emittedSegmentLayouts).
|
||||
static void generateMswForWell( RimEclipseCase& eclipseCase,
|
||||
// The WELSEGS pressure drop components come from the WELSPECS event in effect at the date.
|
||||
static void generateMswForWell( const RimWellEventTimeline& timeline,
|
||||
RimEclipseCase& eclipseCase,
|
||||
RimWellPath& well,
|
||||
const QDateTime& date,
|
||||
const std::vector<RimWellEvent*>& eventsAtDate,
|
||||
|
||||
@@ -50,29 +50,6 @@ void RimSegmentCollection::ReferenceMDEnum::setUp()
|
||||
setDefault( RimSegmentCollection::ReferenceMDType::AUTO_REFERENCE_MD );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
template <>
|
||||
void RimSegmentCollection::PressureDropEnum::setUp()
|
||||
{
|
||||
addItem( RimSegmentCollection::PressureDropType::HYDROSTATIC, "H--", "Hydrostatic" );
|
||||
addItem( RimSegmentCollection::PressureDropType::HYDROSTATIC_FRICTION, "HF-", "Hydrostatic + Friction" );
|
||||
addItem( RimSegmentCollection::PressureDropType::HYDROSTATIC_FRICTION_ACCELERATION, "HFA", "Hydrostatic + Friction + Acceleration" );
|
||||
setDefault( RimSegmentCollection::PressureDropType::HYDROSTATIC_FRICTION );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
template <>
|
||||
void RimSegmentCollection::LengthAndDepthEnum::setUp()
|
||||
{
|
||||
addItem( RimSegmentCollection::LengthAndDepthType::INC, "INC", "Incremental" );
|
||||
addItem( RimSegmentCollection::LengthAndDepthType::ABS, "ABS", "Absolute" );
|
||||
setDefault( RimSegmentCollection::LengthAndDepthType::ABS );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -104,8 +81,6 @@ RimSegmentCollection::RimSegmentCollection()
|
||||
CAF_PDM_InitScriptableField( &m_roughnessFactor, "RoughnessFactor", defaultRoughnessFactor( unitSystem ), "Roughness Factor" );
|
||||
CAF_PDM_InitScriptableFieldNoDefault( &m_segmentIntervals, "Intervals", "Segment Intervals" );
|
||||
|
||||
CAF_PDM_InitScriptableFieldNoDefault( &m_pressureDrop, "PressureDrop", "Pressure Drop" );
|
||||
CAF_PDM_InitScriptableFieldNoDefault( &m_lengthAndDepth, "LengthAndDepth", "Length and Depth" );
|
||||
CAF_PDM_InitScriptableField( &m_enforceMaxSegmentLength, "EnforceMaxSegmentLength", false, "Enforce Max Segment Length" );
|
||||
CAF_PDM_InitScriptableField( &m_maxSegmentLength, "MaxSegmentLength", 200.0, "Max Segment Length" );
|
||||
m_maxSegmentLength.uiCapability()->setUiHidden( true );
|
||||
@@ -200,22 +175,6 @@ double RimSegmentCollection::roughnessFactor() const
|
||||
return m_roughnessFactor();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimSegmentCollection::PressureDropEnum RimSegmentCollection::pressureDrop() const
|
||||
{
|
||||
return m_pressureDrop();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimSegmentCollection::LengthAndDepthEnum RimSegmentCollection::lengthAndDepth() const
|
||||
{
|
||||
return m_lengthAndDepth();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -304,22 +263,6 @@ void RimSegmentCollection::setRoughnessFactor( double roughnessFactor )
|
||||
m_roughnessFactor = roughnessFactor;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSegmentCollection::setPressureDrop( PressureDropType pressureDropType )
|
||||
{
|
||||
m_pressureDrop = pressureDropType;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSegmentCollection::setLengthAndDepth( LengthAndDepthType lengthAndDepthType )
|
||||
{
|
||||
m_lengthAndDepth = lengthAndDepthType;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -501,8 +444,6 @@ void RimSegmentCollection::updateFromTopLevelWell( const RimSegmentCollection* t
|
||||
{
|
||||
m_refMDType = topLevelWellParameters->m_refMDType();
|
||||
m_refMD = topLevelWellParameters->m_refMD();
|
||||
m_pressureDrop = topLevelWellParameters->m_pressureDrop();
|
||||
m_lengthAndDepth = topLevelWellParameters->m_lengthAndDepth();
|
||||
m_enforceMaxSegmentLength = topLevelWellParameters->m_enforceMaxSegmentLength();
|
||||
m_maxSegmentLength = topLevelWellParameters->m_maxSegmentLength();
|
||||
m_compsegsSegmentNumber = topLevelWellParameters->m_compsegsSegmentNumber();
|
||||
@@ -522,22 +463,16 @@ void RimSegmentCollection::importLegacyData( const RimMswCompletionParameters* l
|
||||
legacyParameters->linerDiameter() != defaultLinerDiameter( RiaDefines::EclipseUnitSystem::UNITS_METRIC ) ||
|
||||
legacyParameters->roughnessFactor() != defaultRoughnessFactor( RiaDefines::EclipseUnitSystem::UNITS_METRIC ) ||
|
||||
legacyParameters->diameterRoughnessMode() != RimMswCompletionParameters::DiameterRoughnessMode::UNIFORM ||
|
||||
legacyParameters->pressureDrop() != RimMswCompletionParameters::PressureDropType::HYDROSTATIC_FRICTION ||
|
||||
legacyParameters->lengthAndDepth() != RimMswCompletionParameters::LengthAndDepthType::ABS ||
|
||||
legacyParameters->enforceMaxSegmentLength() || legacyParameters->storedMaxSegmentLength() != 200.0 ||
|
||||
!legacyParameters->diameterRoughnessIntervals()->intervals().empty() ||
|
||||
!legacyParameters->customSegmentIntervals()->intervals().empty();
|
||||
if ( !hasLegacyData ) return;
|
||||
|
||||
m_refMDType = static_cast<ReferenceMDType>( static_cast<int>( legacyParameters->referenceMDType() ) );
|
||||
m_refMD = legacyParameters->storedReferenceMD();
|
||||
m_customValuesForLateral = legacyParameters->customValuesForLateral();
|
||||
m_linerDiameter = legacyParameters->linerDiameter();
|
||||
m_roughnessFactor = legacyParameters->roughnessFactor();
|
||||
m_pressureDrop = static_cast<PressureDropType>(
|
||||
static_cast<int>( static_cast<RimMswCompletionParameters::PressureDropType>( legacyParameters->pressureDrop() ) ) );
|
||||
m_lengthAndDepth = static_cast<LengthAndDepthType>(
|
||||
static_cast<int>( static_cast<RimMswCompletionParameters::LengthAndDepthType>( legacyParameters->lengthAndDepth() ) ) );
|
||||
m_refMDType = static_cast<ReferenceMDType>( static_cast<int>( legacyParameters->referenceMDType() ) );
|
||||
m_refMD = legacyParameters->storedReferenceMD();
|
||||
m_customValuesForLateral = legacyParameters->customValuesForLateral();
|
||||
m_linerDiameter = legacyParameters->linerDiameter();
|
||||
m_roughnessFactor = legacyParameters->roughnessFactor();
|
||||
m_enforceMaxSegmentLength = legacyParameters->enforceMaxSegmentLength();
|
||||
m_maxSegmentLength = legacyParameters->storedMaxSegmentLength();
|
||||
|
||||
@@ -636,8 +571,6 @@ void RimSegmentCollection::defineUiOrdering( QString uiConfigName, caf::PdmUiOrd
|
||||
|
||||
if ( wellPath->isTopLevelWellPath() )
|
||||
{
|
||||
uiOrdering.add( &m_pressureDrop );
|
||||
uiOrdering.add( &m_lengthAndDepth );
|
||||
uiOrdering.add( &m_compsegsSegmentNumber );
|
||||
|
||||
auto* segmentationGroup = uiOrdering.addNewGroup( "Segmentation" );
|
||||
|
||||
@@ -47,19 +47,6 @@ public:
|
||||
MANUAL_REFERENCE_MD
|
||||
};
|
||||
|
||||
enum class PressureDropType
|
||||
{
|
||||
HYDROSTATIC,
|
||||
HYDROSTATIC_FRICTION,
|
||||
HYDROSTATIC_FRICTION_ACCELERATION
|
||||
};
|
||||
|
||||
enum class LengthAndDepthType
|
||||
{
|
||||
ABS,
|
||||
INC
|
||||
};
|
||||
|
||||
enum class CompsegsSegmentNumberType
|
||||
{
|
||||
ASSIGNED_BY_SIMULATOR,
|
||||
@@ -67,31 +54,25 @@ public:
|
||||
};
|
||||
|
||||
using ReferenceMDEnum = caf::AppEnum<ReferenceMDType>;
|
||||
using PressureDropEnum = caf::AppEnum<PressureDropType>;
|
||||
using LengthAndDepthEnum = caf::AppEnum<LengthAndDepthType>;
|
||||
using CompsegsSegmentNumberEnum = caf::AppEnum<CompsegsSegmentNumberType>;
|
||||
|
||||
RimSegmentCollection();
|
||||
|
||||
ReferenceMDType referenceMDType() const;
|
||||
double manualReferenceMD() const;
|
||||
double linerDiameter( RiaDefines::EclipseUnitSystem unitSystem ) const;
|
||||
double linerDiameter() const;
|
||||
double roughnessFactor() const;
|
||||
double roughnessFactor( RiaDefines::EclipseUnitSystem unitSystem ) const;
|
||||
PressureDropEnum pressureDrop() const;
|
||||
LengthAndDepthEnum lengthAndDepth() const;
|
||||
double maxSegmentLength() const;
|
||||
bool singleSegmentBeforeFirstPerforation() const;
|
||||
bool singleSegmentAfterLastPerforation() const;
|
||||
bool exportCompsegsSegmentNumber() const;
|
||||
ReferenceMDType referenceMDType() const;
|
||||
double manualReferenceMD() const;
|
||||
double linerDiameter( RiaDefines::EclipseUnitSystem unitSystem ) const;
|
||||
double linerDiameter() const;
|
||||
double roughnessFactor() const;
|
||||
double roughnessFactor( RiaDefines::EclipseUnitSystem unitSystem ) const;
|
||||
double maxSegmentLength() const;
|
||||
bool singleSegmentBeforeFirstPerforation() const;
|
||||
bool singleSegmentAfterLastPerforation() const;
|
||||
bool exportCompsegsSegmentNumber() const;
|
||||
|
||||
void setReferenceMDType( ReferenceMDType refType );
|
||||
void setManualReferenceMD( double manualRefMD );
|
||||
void setLinerDiameter( double diameter );
|
||||
void setRoughnessFactor( double roughnessFactor );
|
||||
void setPressureDrop( PressureDropType pressureDropType );
|
||||
void setLengthAndDepth( LengthAndDepthType lengthAndDepthType );
|
||||
void setCompsegsSegmentNumber( CompsegsSegmentNumberType segmentNumberType );
|
||||
void setSingleSegmentBeforeFirstPerforation( bool enable );
|
||||
void setSingleSegmentAfterLastPerforation( bool enable );
|
||||
@@ -142,12 +123,10 @@ private:
|
||||
|
||||
caf::PdmChildArrayField<RimSegmentInterval*> m_segmentIntervals;
|
||||
|
||||
caf::PdmField<PressureDropEnum> m_pressureDrop;
|
||||
caf::PdmField<LengthAndDepthEnum> m_lengthAndDepth;
|
||||
caf::PdmField<bool> m_enforceMaxSegmentLength;
|
||||
caf::PdmField<double> m_maxSegmentLength;
|
||||
caf::PdmField<bool> m_singleSegmentBeforeFirstPerforation;
|
||||
caf::PdmField<bool> m_singleSegmentAfterLastPerforation;
|
||||
caf::PdmField<bool> m_enforceMaxSegmentLength;
|
||||
caf::PdmField<double> m_maxSegmentLength;
|
||||
caf::PdmField<bool> m_singleSegmentBeforeFirstPerforation;
|
||||
caf::PdmField<bool> m_singleSegmentAfterLastPerforation;
|
||||
|
||||
caf::PdmField<CompsegsSegmentNumberEnum> m_compsegsSegmentNumber;
|
||||
};
|
||||
@@ -74,6 +74,23 @@ void RimWellPathCompletionSettings::HydrostaticDensityEnum::setUp()
|
||||
setDefault( RimWellPathCompletionSettings::SEGMENTED );
|
||||
}
|
||||
|
||||
template <>
|
||||
void RimWellPathCompletionSettings::PressureDropEnum::setUp()
|
||||
{
|
||||
addItem( RimWellPathCompletionSettings::PressureDropType::HYDROSTATIC, "H--", "Hydrostatic" );
|
||||
addItem( RimWellPathCompletionSettings::PressureDropType::HYDROSTATIC_FRICTION, "HF-", "Hydrostatic + Friction" );
|
||||
addItem( RimWellPathCompletionSettings::PressureDropType::HYDROSTATIC_FRICTION_ACCELERATION, "HFA", "Hydrostatic + Friction + Acceleration" );
|
||||
setDefault( RimWellPathCompletionSettings::PressureDropType::HYDROSTATIC_FRICTION );
|
||||
}
|
||||
|
||||
template <>
|
||||
void RimWellPathCompletionSettings::LengthAndDepthEnum::setUp()
|
||||
{
|
||||
addItem( RimWellPathCompletionSettings::LengthAndDepthType::INC, "INC", "Incremental" );
|
||||
addItem( RimWellPathCompletionSettings::LengthAndDepthType::ABS, "ABS", "Absolute" );
|
||||
setDefault( RimWellPathCompletionSettings::LengthAndDepthType::ABS );
|
||||
}
|
||||
|
||||
} // namespace caf
|
||||
|
||||
namespace
|
||||
@@ -120,6 +137,9 @@ RimWellPathCompletionSettings::RimWellPathCompletionSettings()
|
||||
CAF_PDM_InitScriptableFieldNoDefault( &m_hydrostaticDensity, "HydrostaticDensity", "Hydrostatic Density" );
|
||||
CAF_PDM_InitScriptableField( &m_fluidInPlaceRegion, "FluidInPlaceRegion", 0, "Fluid In-Place Region" );
|
||||
|
||||
CAF_PDM_InitScriptableFieldNoDefault( &m_pressureDrop, "PressureDrop", "Pressure Drop" );
|
||||
CAF_PDM_InitScriptableFieldNoDefault( &m_lengthAndDepth, "LengthAndDepth", "Length and Depth" );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_mswParameters, "MswParameters", "Multi Segment Well Parameters" );
|
||||
m_mswParameters = new RimMswCompletionParameters;
|
||||
m_mswParameters.uiCapability()->setUiTreeChildrenHidden( true );
|
||||
@@ -190,6 +210,8 @@ RimWellPathCompletionSettings& RimWellPathCompletionSettings::operator=( const R
|
||||
m_wellBoreFluidPVTTable = rhs.m_wellBoreFluidPVTTable;
|
||||
m_hydrostaticDensity = rhs.m_hydrostaticDensity;
|
||||
m_fluidInPlaceRegion = rhs.m_fluidInPlaceRegion;
|
||||
m_pressureDrop = rhs.m_pressureDrop;
|
||||
m_lengthAndDepth = rhs.m_lengthAndDepth;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -427,6 +449,38 @@ int RimWellPathCompletionSettings::fluidInPlaceRegion() const
|
||||
return m_fluidInPlaceRegion();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimWellPathCompletionSettings::PressureDropEnum RimWellPathCompletionSettings::pressureDrop() const
|
||||
{
|
||||
return m_pressureDrop();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimWellPathCompletionSettings::LengthAndDepthEnum RimWellPathCompletionSettings::lengthAndDepth() const
|
||||
{
|
||||
return m_lengthAndDepth();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellPathCompletionSettings::setPressureDrop( PressureDropType pressureDropType )
|
||||
{
|
||||
m_pressureDrop = pressureDropType;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellPathCompletionSettings::setLengthAndDepth( LengthAndDepthType lengthAndDepthType )
|
||||
{
|
||||
m_lengthAndDepth = lengthAndDepthType;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -443,6 +497,26 @@ RimMswCompletionParameters* RimWellPathCompletionSettings::mswCompletionParamete
|
||||
return m_mswParameters();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Copy WELSEGS header settings from legacy MSW parameters, if they differ from the defaults
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimWellPathCompletionSettings::importLegacyMswData( const RimMswCompletionParameters* legacyParameters )
|
||||
{
|
||||
if ( !legacyParameters ) return;
|
||||
|
||||
const auto legacyPressureDrop = static_cast<RimMswCompletionParameters::PressureDropType>( legacyParameters->pressureDrop() );
|
||||
if ( legacyPressureDrop != RimMswCompletionParameters::PressureDropType::HYDROSTATIC_FRICTION )
|
||||
{
|
||||
m_pressureDrop = static_cast<PressureDropType>( static_cast<int>( legacyPressureDrop ) );
|
||||
}
|
||||
|
||||
const auto legacyLengthAndDepth = static_cast<RimMswCompletionParameters::LengthAndDepthType>( legacyParameters->lengthAndDepth() );
|
||||
if ( legacyLengthAndDepth != RimMswCompletionParameters::LengthAndDepthType::ABS )
|
||||
{
|
||||
m_lengthAndDepth = static_cast<LengthAndDepthType>( static_cast<int>( legacyLengthAndDepth ) );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -464,6 +538,10 @@ void RimWellPathCompletionSettings::defineUiOrdering( QString uiConfigName, caf:
|
||||
compExportGroup->add( &m_wellBoreFluidPVTTable );
|
||||
compExportGroup->add( &m_hydrostaticDensity );
|
||||
compExportGroup->add( &m_fluidInPlaceRegion );
|
||||
|
||||
caf::PdmUiGroup* mswGroup = uiOrdering.addNewGroup( "Multi Segment Well (WELSEGS)" );
|
||||
mswGroup->add( &m_pressureDrop );
|
||||
mswGroup->add( &m_lengthAndDepth );
|
||||
}
|
||||
|
||||
uiOrdering.skipRemainingFields( true );
|
||||
|
||||
@@ -65,6 +65,21 @@ public:
|
||||
};
|
||||
using HydrostaticDensityEnum = caf::AppEnum<HydrostaticDensity>;
|
||||
|
||||
enum class PressureDropType
|
||||
{
|
||||
HYDROSTATIC,
|
||||
HYDROSTATIC_FRICTION,
|
||||
HYDROSTATIC_FRICTION_ACCELERATION
|
||||
};
|
||||
using PressureDropEnum = caf::AppEnum<PressureDropType>;
|
||||
|
||||
enum class LengthAndDepthType
|
||||
{
|
||||
ABS,
|
||||
INC
|
||||
};
|
||||
using LengthAndDepthEnum = caf::AppEnum<LengthAndDepthType>;
|
||||
|
||||
public:
|
||||
RimWellPathCompletionSettings();
|
||||
RimWellPathCompletionSettings( const RimWellPathCompletionSettings& rhs );
|
||||
@@ -75,10 +90,15 @@ public:
|
||||
WellType wellType() const;
|
||||
bool allowWellCrossFlow() const;
|
||||
|
||||
PressureDropEnum pressureDrop() const;
|
||||
LengthAndDepthEnum lengthAndDepth() const;
|
||||
|
||||
void setGroupName( const QString& name );
|
||||
void setReferenceDepth( std::optional<double> depth );
|
||||
void setWellType( WellType wellType );
|
||||
void setAllowWellCrossFlow( bool allowCrossFlow );
|
||||
void setPressureDrop( PressureDropType pressureDropType );
|
||||
void setLengthAndDepth( LengthAndDepthType lengthAndDepthType );
|
||||
|
||||
void setWellNameForExport( const QString& name );
|
||||
void updateWellPathNameHasChanged( const QString& newWellPathName, const QString& previousWellPathName );
|
||||
@@ -103,6 +123,7 @@ public:
|
||||
static QRegularExpression wellNameForExportRegExp();
|
||||
|
||||
RimMswCompletionParameters* mswCompletionParameters() const;
|
||||
void importLegacyMswData( const RimMswCompletionParameters* legacyParameters );
|
||||
|
||||
protected:
|
||||
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
|
||||
@@ -127,6 +148,9 @@ private:
|
||||
caf::PdmField<HydrostaticDensityEnum> m_hydrostaticDensity;
|
||||
caf::PdmField<int> m_fluidInPlaceRegion;
|
||||
|
||||
caf::PdmField<PressureDropEnum> m_pressureDrop;
|
||||
caf::PdmField<LengthAndDepthEnum> m_lengthAndDepth;
|
||||
|
||||
caf::PdmChildField<RimMswCompletionParameters*> m_mswParameters;
|
||||
|
||||
// OBSOLETE fields - kept for backward compatibility when reading old files
|
||||
|
||||
@@ -295,7 +295,8 @@ RimWellEventWellSpec* RimWellEventTimeline::addWellSpecEvent( RimWellPath* wellP
|
||||
if ( wellPath && wellPath->completionSettings() )
|
||||
{
|
||||
auto* settings = wellPath->completionSettings();
|
||||
event->setBaselineData( { settings->groupName(), settings->allowWellCrossFlow(), settings->referenceDepth(), settings->wellType() } );
|
||||
event->setBaselineData(
|
||||
{ settings->groupName(), settings->allowWellCrossFlow(), settings->referenceDepth(), settings->wellType(), settings->pressureDrop() } );
|
||||
}
|
||||
|
||||
m_events.push_back( event );
|
||||
@@ -531,6 +532,7 @@ bool RimWellEventTimeline::applyWellSpecEvent( const RimWellEventWellSpec& event
|
||||
settings->setAllowWellCrossFlow( data.allowCrossFlow );
|
||||
settings->setReferenceDepth( data.referenceDepth );
|
||||
settings->setWellType( data.wellType );
|
||||
settings->setPressureDrop( data.pressureDrop );
|
||||
settings->updateConnectedEditors();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ RimWellEventWellSpec::RimWellEventWellSpec()
|
||||
"WellType",
|
||||
caf::AppEnum<RimWellPathCompletionSettings::WellType>( RimWellPathCompletionSettings::OIL ),
|
||||
"Preferred Fluid Phase" );
|
||||
CAF_PDM_InitScriptableFieldNoDefault( &m_pressureDrop, "PressureDrop", "Pressure Drop" );
|
||||
|
||||
CAF_PDM_InitField( &m_baselineGroupName, "BaselineGroupName", QString(), "Baseline Group Name" );
|
||||
CAF_PDM_InitField( &m_baselineAllowCrossFlow, "BaselineAllowCrossFlow", true, "Baseline Allow Cross-Flow" );
|
||||
@@ -46,6 +47,7 @@ RimWellEventWellSpec::RimWellEventWellSpec()
|
||||
"BaselineWellType",
|
||||
caf::AppEnum<RimWellPathCompletionSettings::WellType>( RimWellPathCompletionSettings::OIL ),
|
||||
"Baseline Preferred Fluid Phase" );
|
||||
CAF_PDM_InitFieldNoDefault( &m_baselinePressureDrop, "BaselinePressureDrop", "Baseline Pressure Drop" );
|
||||
|
||||
setDeletable( true );
|
||||
}
|
||||
@@ -62,7 +64,7 @@ RimWellEventWellSpec::~RimWellEventWellSpec()
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimWellSpecData RimWellEventWellSpec::wellSpecData() const
|
||||
{
|
||||
return { m_groupName(), m_allowCrossFlow(), m_referenceDepth(), m_wellType() };
|
||||
return { m_groupName(), m_allowCrossFlow(), m_referenceDepth(), m_wellType(), m_pressureDrop() };
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -70,7 +72,7 @@ RimWellSpecData RimWellEventWellSpec::wellSpecData() const
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimWellSpecData RimWellEventWellSpec::baselineData() const
|
||||
{
|
||||
return { m_baselineGroupName(), m_baselineAllowCrossFlow(), m_baselineReferenceDepth(), m_baselineWellType() };
|
||||
return { m_baselineGroupName(), m_baselineAllowCrossFlow(), m_baselineReferenceDepth(), m_baselineWellType(), m_baselinePressureDrop() };
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -82,6 +84,7 @@ void RimWellEventWellSpec::setWellSpecData( const RimWellSpecData& data )
|
||||
m_allowCrossFlow = data.allowCrossFlow;
|
||||
m_referenceDepth = data.referenceDepth;
|
||||
m_wellType = data.wellType;
|
||||
m_pressureDrop = data.pressureDrop;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -93,6 +96,7 @@ void RimWellEventWellSpec::setBaselineData( const RimWellSpecData& data )
|
||||
m_baselineAllowCrossFlow = data.allowCrossFlow;
|
||||
m_baselineReferenceDepth = data.referenceDepth;
|
||||
m_baselineWellType = data.wellType;
|
||||
m_baselinePressureDrop = data.pressureDrop;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -121,6 +125,7 @@ void RimWellEventWellSpec::defineUiOrdering( QString uiConfigName, caf::PdmUiOrd
|
||||
uiOrdering.add( &m_allowCrossFlow );
|
||||
uiOrdering.add( &m_referenceDepth );
|
||||
uiOrdering.add( &m_wellType );
|
||||
uiOrdering.add( &m_pressureDrop );
|
||||
uiOrdering.skipRemainingFields();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,10 +27,11 @@
|
||||
|
||||
struct RimWellSpecData
|
||||
{
|
||||
QString groupName;
|
||||
bool allowCrossFlow = true;
|
||||
std::optional<double> referenceDepth;
|
||||
RimWellPathCompletionSettings::WellType wellType = RimWellPathCompletionSettings::OIL;
|
||||
QString groupName;
|
||||
bool allowCrossFlow = true;
|
||||
std::optional<double> referenceDepth;
|
||||
RimWellPathCompletionSettings::WellType wellType = RimWellPathCompletionSettings::OIL;
|
||||
RimWellPathCompletionSettings::PressureDropType pressureDrop = RimWellPathCompletionSettings::PressureDropType::HYDROSTATIC_FRICTION;
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
@@ -64,8 +65,10 @@ private:
|
||||
caf::PdmField<bool> m_allowCrossFlow;
|
||||
caf::PdmField<std::optional<double>> m_referenceDepth;
|
||||
caf::PdmField<caf::AppEnum<RimWellPathCompletionSettings::WellType>> m_wellType;
|
||||
caf::PdmField<RimWellPathCompletionSettings::PressureDropEnum> m_pressureDrop;
|
||||
caf::PdmField<QString> m_baselineGroupName;
|
||||
caf::PdmField<bool> m_baselineAllowCrossFlow;
|
||||
caf::PdmField<std::optional<double>> m_baselineReferenceDepth;
|
||||
caf::PdmField<caf::AppEnum<RimWellPathCompletionSettings::WellType>> m_baselineWellType;
|
||||
caf::PdmField<RimWellPathCompletionSettings::PressureDropEnum> m_baselinePressureDrop;
|
||||
};
|
||||
@@ -583,6 +583,7 @@ void RimWellPath::initAfterRead()
|
||||
}
|
||||
|
||||
m_completions->segmentCollection()->importLegacyData( m_completionSettings->mswCompletionParameters() );
|
||||
m_completionSettings->importLegacyMswData( m_completionSettings->mswCompletionParameters() );
|
||||
|
||||
// Migrate an old direct file path reference into the project-level well formations collection.
|
||||
if ( RimProject::current()->isProjectFileVersionEqualOrOlderThan( "2026.09.2" ) && !m_wellFormationsFile() &&
|
||||
|
||||
@@ -394,6 +394,13 @@ RimcWellEventTimeline_addWellSpecEvent::RimcWellEventTimeline_addWellSpecEvent(
|
||||
"",
|
||||
"",
|
||||
"Preferred Fluid Phase" );
|
||||
CAF_PDM_InitScriptableField( &m_pressureDrop,
|
||||
"PressureDrop",
|
||||
QString(),
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"WELSEGS pressure drop components (H--, HF- or HFA). Empty keeps the current completion setting" );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -419,8 +426,18 @@ std::expected<caf::PdmObjectHandle*, QString> RimcWellEventTimeline_addWellSpecE
|
||||
}
|
||||
}
|
||||
|
||||
auto pressureDrop = m_wellPath()->completionSettings()->pressureDrop();
|
||||
if ( !m_pressureDrop().isEmpty() )
|
||||
{
|
||||
if ( !RimWellPathCompletionSettings::PressureDropEnum::isValid( m_pressureDrop() ) )
|
||||
{
|
||||
return std::unexpected( QString( "Invalid pressure drop: %1. Expected H--, HF- or HFA" ).arg( m_pressureDrop() ) );
|
||||
}
|
||||
pressureDrop = RimWellPathCompletionSettings::PressureDropEnum::fromText( m_pressureDrop() );
|
||||
}
|
||||
|
||||
auto* event = timeline->addWellSpecEvent( m_wellPath(), date );
|
||||
event->setWellSpecData( { m_groupName(), m_allowCrossFlow(), m_referenceDepth(), m_wellType() } );
|
||||
event->setWellSpecData( { m_groupName(), m_allowCrossFlow(), m_referenceDepth(), m_wellType(), pressureDrop } );
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
@@ -177,6 +177,7 @@ private:
|
||||
caf::PdmField<bool> m_allowCrossFlow;
|
||||
caf::PdmField<std::optional<double>> m_referenceDepth;
|
||||
caf::PdmField<caf::AppEnum<RimWellPathCompletionSettings::WellType>> m_wellType;
|
||||
caf::PdmField<QString> m_pressureDrop;
|
||||
};
|
||||
|
||||
//==================================================================================================
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "RimSegmentCollection.h"
|
||||
#include "RimSegmentInterval.h"
|
||||
#include "RimWellPath.h"
|
||||
#include "RimWellPathCompletionSettings.h"
|
||||
#include "RimWellPathCompletions.h"
|
||||
|
||||
#include "cafPdmDefaultObjectFactory.h"
|
||||
@@ -178,6 +179,8 @@ TEST( RimWellPathCompletions, LegacyMigrationPreservesDisabledSettings )
|
||||
|
||||
RimSegmentCollection segments;
|
||||
segments.importLegacyData( &legacyParameters );
|
||||
RimWellPathCompletionSettings completionSettings;
|
||||
completionSettings.importLegacyMswData( &legacyParameters );
|
||||
|
||||
EXPECT_EQ( RimSegmentCollection::ReferenceMDType::AUTO_REFERENCE_MD, segments.referenceMDType() );
|
||||
auto* migratedMaxLength = dynamic_cast<caf::PdmField<double>*>( segments.findField( "MaxSegmentLength" ) );
|
||||
@@ -189,8 +192,8 @@ TEST( RimWellPathCompletions, LegacyMigrationPreservesDisabledSettings )
|
||||
EXPECT_DOUBLE_EQ( 345.0, migratedMaxLength->value() );
|
||||
EXPECT_TRUE( migratedCustomValues->value() );
|
||||
EXPECT_FALSE( enforceMaxLength->value() );
|
||||
EXPECT_EQ( RimSegmentCollection::PressureDropType::HYDROSTATIC_FRICTION_ACCELERATION, segments.pressureDrop() );
|
||||
EXPECT_EQ( RimSegmentCollection::LengthAndDepthType::INC, segments.lengthAndDepth() );
|
||||
EXPECT_EQ( RimWellPathCompletionSettings::PressureDropType::HYDROSTATIC_FRICTION_ACCELERATION, completionSettings.pressureDrop() );
|
||||
EXPECT_EQ( RimWellPathCompletionSettings::LengthAndDepthType::INC, completionSettings.lengthAndDepth() );
|
||||
|
||||
segments.setReferenceMDType( RimSegmentCollection::ReferenceMDType::MANUAL_REFERENCE_MD );
|
||||
EXPECT_DOUBLE_EQ( 123.0, segments.manualReferenceMD() );
|
||||
|
||||
+3
-2
@@ -63,11 +63,12 @@ WELL W1 = "{well_name}"
|
||||
WELL W1
|
||||
# WELSPECS updates completion export settings and emits WELSPECS. Attributes
|
||||
# are optional: the second event inherits GROUP from the first event.
|
||||
2024-01-05 WELSPECS GROUP="SIMULATOR_EVENTS_GROUP" CROSSFLOW=True REFDEPTH=1002 PHASE=WATER
|
||||
# PRESSURE_COMPONENTS sets the WELSEGS pressure drop components for the well.
|
||||
2024-01-05 WELSPECS GROUP="SIMULATOR_EVENTS_GROUP" CROSSFLOW=True REFDEPTH=1002 PHASE=WATER PRESSURE_COMPONENTS=HFA
|
||||
2024-04-15 WELSPECS CROSSFLOW=False REFDEPTH=1000 PHASE=OIL
|
||||
|
||||
# COMMENT is stored on the event and safely emitted as a schedule comment.
|
||||
STARTUP SEGMENT MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 ROUGHNESS=1.0e-5 PRESSURE_COMPONENTS=HFA COMMENT="Install production segment"
|
||||
STARTUP SEGMENT MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 ROUGHNESS=1.0e-5 COMMENT="Install production segment"
|
||||
|
||||
# Perforations; COMPLETION_NUMBER groups connections for COMPLUMP. Same-date
|
||||
# perforations are kept as separate events during normalization.{filter_comment}
|
||||
|
||||
@@ -84,12 +84,17 @@ msw_settings.custom_values_for_lateral = False
|
||||
msw_settings.enforce_max_segment_length = False
|
||||
msw_settings.liner_diameter = 0.152
|
||||
msw_settings.max_segment_length = 200
|
||||
msw_settings.pressure_drop = "HF-"
|
||||
msw_settings.reference_md_type = "GridEntryPoint"
|
||||
msw_settings.roughness_factor = 1e-05
|
||||
msw_settings.user_defined_reference_md = 0
|
||||
msw_settings.update()
|
||||
|
||||
# The WELSEGS header settings are part of the completion settings
|
||||
completion_settings = well_path.completion_settings()
|
||||
completion_settings.pressure_drop = "HF-"
|
||||
completion_settings.length_and_depth = "ABS"
|
||||
completion_settings.update()
|
||||
|
||||
# Optionally update the Perforation Non-Darcy settings
|
||||
non_darcy_parameters = perforation_coll.non_darcy_parameters()
|
||||
non_darcy_parameters.non_darcy_flow_type = "UserDefined"
|
||||
|
||||
@@ -151,8 +151,9 @@ Notes on the grammar:
|
||||
Eclipse well keyword (``WCONHIST``, ``WELTARG``, ``WRFTPLT``, ``WCONPROD``,
|
||||
...), which
|
||||
is passed through generically with the well name injected as WELL. Event
|
||||
``WELSPECS`` accepts partial updates to ``GROUP``, ``CROSSFLOW``, ``REFDEPTH``
|
||||
and ``PHASE`` (OIL/GAS/WATER/LIQUID). Omitted values inherit the previous
|
||||
``WELSPECS`` accepts partial updates to ``GROUP``, ``CROSSFLOW``, ``REFDEPTH``,
|
||||
``PHASE`` (OIL/GAS/WATER/LIQUID) and ``PRESSURE_COMPONENTS`` (H--/HF-/HFA,
|
||||
the WELSEGS pressure drop components). Omitted values inherit the previous
|
||||
WELSPECS state, initially using the well's completion export settings. There
|
||||
may be multiple WELSPECS events for a well, but not at the same timestamp.
|
||||
Each emits a WELSPECS record with the cumulative state. Event values are
|
||||
@@ -426,6 +427,7 @@ class WellSpecState:
|
||||
crossflow: bool
|
||||
refdepth: Optional[float]
|
||||
phase: str
|
||||
pressure_components: str
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -1786,7 +1788,6 @@ _SEGMENT_KNOWN = {
|
||||
"MDEND",
|
||||
"INNER_DIAMETER",
|
||||
"ROUGHNESS",
|
||||
"PRESSURE_COMPONENTS",
|
||||
"COMMENT",
|
||||
}
|
||||
_PRESSURE_COMPONENTS = {"H--", "HF-", "HFA"}
|
||||
@@ -1800,7 +1801,14 @@ _VALVE_KNOWN = {"MD", "TYPE", "STATE", "CV", "AREA", "COMMENT"} | {
|
||||
}
|
||||
_STATE_REQUIRED = ("STATE",)
|
||||
_STATE_KNOWN = {"STATE", "COMMENT"}
|
||||
_WELLSPEC_KNOWN = {"GROUP", "CROSSFLOW", "REFDEPTH", "PHASE", "COMMENT"}
|
||||
_WELLSPEC_KNOWN = {
|
||||
"GROUP",
|
||||
"CROSSFLOW",
|
||||
"REFDEPTH",
|
||||
"PHASE",
|
||||
"PRESSURE_COMPONENTS",
|
||||
"COMMENT",
|
||||
}
|
||||
_WELLSPEC_PHASES = {"OIL", "GAS", "WATER", "LIQUID"}
|
||||
_COMPLETION_IGNORED = {"FILTER"}
|
||||
_PERF_IGNORED = _COMPLETION_IGNORED # backwards-compatible alias
|
||||
@@ -2007,6 +2015,7 @@ def _prepare_wellspec_events(
|
||||
crossflow=bool(completion_settings.allow_well_cross_flow),
|
||||
refdepth=completion_settings.reference_depth_for_export,
|
||||
phase=_enum_text(completion_settings.well_type_for_export),
|
||||
pressure_components=_enum_text(completion_settings.pressure_drop),
|
||||
)
|
||||
|
||||
wellspecs = sorted(
|
||||
@@ -2059,6 +2068,13 @@ def _prepare_wellspec_events(
|
||||
errors.append("PHASE must be OIL, GAS, WATER, or LIQUID")
|
||||
else:
|
||||
next_state.phase = phase
|
||||
if "PRESSURE_COMPONENTS" in attrs:
|
||||
value = attrs["PRESSURE_COMPONENTS"].value
|
||||
components = value.upper() if isinstance(value, str) else ""
|
||||
if components not in _PRESSURE_COMPONENTS:
|
||||
errors.append("PRESSURE_COMPONENTS must be H--, HF-, or HFA")
|
||||
else:
|
||||
next_state.pressure_components = components
|
||||
|
||||
if errors:
|
||||
report.errors.extend(_event_message(event, error) for error in errors)
|
||||
@@ -2543,13 +2559,6 @@ def _apply_segment(
|
||||
)
|
||||
if "ROUGHNESS" in attrs:
|
||||
kwargs["roughness"] = float(_as_number(attrs["ROUGHNESS"], event.loc))
|
||||
pressure_components = None
|
||||
if "PRESSURE_COMPONENTS" in attrs:
|
||||
pressure_components = str(attrs["PRESSURE_COMPONENTS"].value).upper()
|
||||
if pressure_components not in _PRESSURE_COMPONENTS:
|
||||
raise SimulatorEventsParseError(
|
||||
"PRESSURE_COMPONENTS must be H--, HF-, or HFA", event.loc
|
||||
)
|
||||
except SimulatorEventsParseError as exc:
|
||||
_record_event_parse_error(event, exc, report)
|
||||
return
|
||||
@@ -2561,10 +2570,6 @@ def _apply_segment(
|
||||
diameter=kwargs.get("inner_diameter", 0.152),
|
||||
roughness_factor=kwargs.get("roughness", 1.0e-5),
|
||||
)
|
||||
if pressure_components is not None:
|
||||
msw_settings = well_path.segment_collection()
|
||||
msw_settings.pressure_drop = pressure_components
|
||||
msw_settings.update()
|
||||
_apply_event_comment(event, timeline_event)
|
||||
report.events_applied += 1
|
||||
|
||||
@@ -2654,6 +2659,7 @@ def _apply_wellspec(
|
||||
allow_cross_flow=state.crossflow,
|
||||
reference_depth=state.refdepth,
|
||||
well_type=state.phase,
|
||||
pressure_drop=state.pressure_components,
|
||||
)
|
||||
_apply_event_comment(event, timeline_event)
|
||||
report.events_applied += 1
|
||||
|
||||
@@ -249,14 +249,12 @@ def test_msw_settings_read(rips_instance, initialize_test):
|
||||
assert msw_settings.liner_diameter == 0.152, "Liner diameter should be 0.152m"
|
||||
assert msw_settings.roughness_factor == 1e-05, "Roughness factor should be 1e-05"
|
||||
|
||||
# Check pressure drop setting
|
||||
assert msw_settings.pressure_drop == "HF-", "Pressure drop should be HF-"
|
||||
|
||||
# Check max segment length setting
|
||||
assert msw_settings.max_segment_length == 200.0, "Max segment length should be 200m"
|
||||
|
||||
# Verify completion settings
|
||||
completion_settings = well_y1.completion_settings()
|
||||
assert completion_settings.pressure_drop == "HF-", "Pressure drop should be HF-"
|
||||
assert completion_settings.well_name_for_export == "Well-1", (
|
||||
"Well name for export should be Well-1"
|
||||
)
|
||||
|
||||
@@ -67,11 +67,11 @@ class FakeCompletionSettings:
|
||||
self.allow_well_cross_flow = True
|
||||
self.reference_depth_for_export = None
|
||||
self.well_type_for_export = "OIL"
|
||||
self.pressure_drop = "HF-"
|
||||
|
||||
|
||||
class FakeSegmentCollection:
|
||||
def __init__(self):
|
||||
self.pressure_drop = "HF-"
|
||||
self.segment_interval_calls = []
|
||||
self.update_calls = 0
|
||||
|
||||
@@ -1880,17 +1880,38 @@ class TestApplying:
|
||||
"allow_cross_flow": False,
|
||||
"reference_depth": 1002.0,
|
||||
"well_type": "GAS",
|
||||
"pressure_drop": "HF-",
|
||||
}
|
||||
assert timeline.wellspec_calls[1]["group_name"] == "my_group"
|
||||
assert timeline.wellspec_calls[1]["allow_cross_flow"] is True
|
||||
assert timeline.wellspec_calls[1]["well_type"] == "WATER"
|
||||
|
||||
def test_wellspec_pressure_components_are_cumulative(self):
|
||||
text = (
|
||||
'SIMEVENTS 1.2\nWELL "55_33-A-1"\n'
|
||||
" 2018-01-01 WELSPECS PRESSURE_COMPONENTS=hfa\n"
|
||||
" 2019-01-01 WELSPECS GROUP=G1\n"
|
||||
" 2020-01-01 WELSPECS PRESSURE_COMPONENTS=H--\n"
|
||||
)
|
||||
timeline, report = self._apply(text)
|
||||
|
||||
assert report.errors == []
|
||||
assert [call["pressure_drop"] for call in timeline.wellspec_calls] == [
|
||||
"HFA",
|
||||
"HFA",
|
||||
"H--",
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"attributes,expected_error",
|
||||
[
|
||||
("CROSSFLOW=YES", "CROSSFLOW must be True or False"),
|
||||
("REFDEPTH=deep", "REFDEPTH must be numeric"),
|
||||
("PHASE=steam", "PHASE must be OIL, GAS, WATER, or LIQUID"),
|
||||
(
|
||||
"PRESSURE_COMPONENTS=INVALID",
|
||||
"PRESSURE_COMPONENTS must be H--, HF-, or HFA",
|
||||
),
|
||||
("GROUP=1", "GROUP must be a non-empty string"),
|
||||
("UNKNOWN=1", "unknown WELSPECS attribute"),
|
||||
("COMMENT=empty", "needs at least one setting attribute"),
|
||||
@@ -1905,11 +1926,11 @@ class TestApplying:
|
||||
assert expected_error in report.errors[0]
|
||||
assert timeline.wellspec_calls == []
|
||||
|
||||
def test_segment_mapping_creates_custom_interval_and_sets_pressure_drop(self):
|
||||
def test_segment_mapping_creates_custom_interval(self):
|
||||
text = (
|
||||
'SIMEVENTS 1.2\nWELL "55_33-A-1"\n'
|
||||
" 2024-01-01 SEGMENT MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 "
|
||||
"ROUGHNESS=1.0e-5 PRESSURE_COMPONENTS=HFA\n"
|
||||
"ROUGHNESS=1.0e-5\n"
|
||||
)
|
||||
timeline, report = self._apply(text)
|
||||
assert report.events_applied == 1
|
||||
@@ -1928,20 +1949,17 @@ class TestApplying:
|
||||
"roughness_factor": pytest.approx(1.0e-5),
|
||||
}
|
||||
]
|
||||
assert well.segment_collection().pressure_drop == "HFA"
|
||||
assert well.segment_collection().update_calls == 1
|
||||
|
||||
def test_segment_rejects_invalid_pressure_components(self):
|
||||
def test_segment_rejects_pressure_components(self):
|
||||
text = (
|
||||
'SIMEVENTS 1.2\nWELL "55_33-A-1"\n'
|
||||
" 2024-01-01 SEGMENT MDSTART=0 MDEND=2500 "
|
||||
"PRESSURE_COMPONENTS=INVALID\n"
|
||||
" 2024-01-01 SEGMENT MDSTART=0 MDEND=2500 PRESSURE_COMPONENTS=HFA\n"
|
||||
)
|
||||
timeline, report = self._apply(text)
|
||||
|
||||
assert report.events_applied == 0
|
||||
assert report.events_skipped == 1
|
||||
assert "PRESSURE_COMPONENTS must be H--, HF-, or HFA" in report.errors[0]
|
||||
assert "unknown SEGMENT attribute(s): PRESSURE_COMPONENTS" in report.errors[0]
|
||||
assert timeline.segment_calls == []
|
||||
|
||||
def test_tubing_is_reported_as_renamed_event(self):
|
||||
@@ -3009,7 +3027,8 @@ class TestSimulatorEventsIntegration:
|
||||
"DATE STARTUP = 2024-01-01\n"
|
||||
"DURATION RAMP = 31d\n"
|
||||
f'WELL "{well.name}"\n'
|
||||
" STARTUP SEGMENT MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 ROUGHNESS=1.0e-5 PRESSURE_COMPONENTS=HFA\n"
|
||||
" STARTUP WELSPECS PRESSURE_COMPONENTS=HFA\n"
|
||||
" STARTUP SEGMENT MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 ROUGHNESS=1.0e-5\n"
|
||||
" STARTUP + RAMP PERFORATION MDSTART=2000 MDEND=2200 DIAMETER=0.1 SKIN=0.5 COMPLETION_NUMBER=1\n"
|
||||
" 2024-05-15T14:45:30.700 PERFORATION MDSTART=2300 MDEND=2350 DIAMETER=0.1 SKIN=0.4 COMPLETION_NUMBER=2\n"
|
||||
" 2024-03-01 VALVE MD=2100 TYPE=ICV STATE=OPEN CV=0.7 AREA=0.0001\n"
|
||||
@@ -3026,16 +3045,16 @@ class TestSimulatorEventsIntegration:
|
||||
report = apply_simulator_events_document(document, timeline, project)
|
||||
assert report.errors == []
|
||||
assert report.warnings == []
|
||||
assert report.events_applied == 11
|
||||
assert report.events_applied == 12
|
||||
|
||||
segment_intervals = well.descendants(rips.SegmentInterval)
|
||||
assert any(
|
||||
segment.start_md == 0.0 and segment.end_md == 2500.0
|
||||
for segment in segment_intervals
|
||||
)
|
||||
assert well.segment_collection().pressure_drop == "HFA"
|
||||
|
||||
timeline.set_timestamp(timestamp="2024-12-24")
|
||||
assert well.completion_settings().pressure_drop == "HFA"
|
||||
schedule = timeline.generate_schedule_text(
|
||||
eclipse_case=case, export_msw_for_wells=[well]
|
||||
)
|
||||
@@ -3052,6 +3071,32 @@ class TestSimulatorEventsIntegration:
|
||||
assert keyword in schedule, f"{keyword} missing from schedule"
|
||||
# The datetime perforation must surface as a DATES entry with TIME.
|
||||
assert "14:45:31" in schedule
|
||||
assert "'HFA'" in schedule.split("WELSEGS", 1)[1].split("/", 1)[0]
|
||||
|
||||
def test_welsegs_uses_pressure_components_from_wellspec_at_date(
|
||||
self, project_with_case_and_wells
|
||||
):
|
||||
project, case, timeline = project_with_case_and_wells
|
||||
well = next(wp for wp in project.well_paths() if "A" in wp.name)
|
||||
document = parse_simulator_events(
|
||||
"SIMEVENTS 1.2\n"
|
||||
f'WELL "{well.name}"\n'
|
||||
" 2024-01-01 WELSPECS PRESSURE_COMPONENTS=HFA\n"
|
||||
" 2024-01-01 SEGMENT MDSTART=0 MDEND=2500\n"
|
||||
" 2024-01-01 PERFORATION MDSTART=2000 MDEND=2200\n"
|
||||
" 2024-06-01 WELSPECS PRESSURE_COMPONENTS=H--\n"
|
||||
" 2024-06-01 PERFORATION MDSTART=2300 MDEND=2350\n"
|
||||
)
|
||||
report = apply_simulator_events_document(document, timeline, project)
|
||||
assert report.errors == []
|
||||
|
||||
schedule = timeline.generate_schedule_text(
|
||||
eclipse_case=case, export_msw_for_wells=[well]
|
||||
)
|
||||
headers = [block.split("/", 1)[0] for block in schedule.split("WELSEGS\n")[1:]]
|
||||
assert len(headers) == 2
|
||||
assert "'HFA'" in headers[0]
|
||||
assert "'H--'" in headers[1]
|
||||
|
||||
def test_apply_unknown_well_warns_and_applies_nothing(
|
||||
self, project_with_case_and_wells
|
||||
|
||||
@@ -107,12 +107,18 @@ def test_add_well_path_completions(rips_instance, initialize_test):
|
||||
assert completions_settings_updated.well_name_for_export == "TestWellName"
|
||||
assert completions_settings_updated.well_type_for_export == "LIQUID"
|
||||
|
||||
completions_settings.pressure_drop = "HFA"
|
||||
completions_settings.length_and_depth = "INC"
|
||||
completions_settings.update()
|
||||
completions_settings_updated = well_path.completion_settings()
|
||||
assert completions_settings_updated.pressure_drop == "HFA"
|
||||
assert completions_settings_updated.length_and_depth == "INC"
|
||||
|
||||
segment_collection = well_path.segment_collection()
|
||||
segment_collection.custom_values_for_lateral = True
|
||||
segment_collection.enforce_max_segment_length = True
|
||||
segment_collection.liner_diameter = 20.0
|
||||
segment_collection.max_segment_length = 123.05
|
||||
segment_collection.pressure_drop = "HFA"
|
||||
segment_collection.reference_md_type = "UserDefined"
|
||||
segment_collection.roughness_factor = 1.3
|
||||
segment_collection.user_defined_reference_md = 1234.56
|
||||
@@ -123,7 +129,6 @@ def test_add_well_path_completions(rips_instance, initialize_test):
|
||||
assert segment_collection_updated.enforce_max_segment_length
|
||||
assert segment_collection_updated.liner_diameter == 20.0
|
||||
assert segment_collection_updated.max_segment_length == 123.05
|
||||
assert segment_collection_updated.pressure_drop == "HFA"
|
||||
assert segment_collection_updated.reference_md_type == "UserDefined"
|
||||
assert segment_collection_updated.roughness_factor == 1.3
|
||||
assert segment_collection_updated.user_defined_reference_md == 1234.56
|
||||
|
||||
+18
-3
@@ -34,7 +34,8 @@ FILTER POROPERM = "PORO > 0.4 AND PERMX > 100.0"
|
||||
WELL A1 = "55_33-A-1" # alias declaration (has '=')
|
||||
|
||||
WELL A1 # opens an event block via the alias
|
||||
@STARTUP SEGMENT MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 ROUGHNESS=1.0e-5 PRESSURE_COMPONENTS=HFA
|
||||
@STARTUP WELSPECS GROUP="OP" PRESSURE_COMPONENTS=HFA
|
||||
@STARTUP SEGMENT MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 ROUGHNESS=1.0e-5
|
||||
@STARTUP + RAMP PERFORATION MDSTART=2000 MDEND=2200 RADIUS=0.05 SKIN=0.5 COMPLETION_NUMBER=1 FILTER=POROPERM
|
||||
@2024-05-15T14:45:30 PERFORATION MDSTART=2300 MDEND=2350 RADIUS=0.05
|
||||
@2024-03-01 VALVE MD=2100 TYPE=ICV STATE=OPEN CV=0.7 AREA=0.0001
|
||||
@@ -79,8 +80,11 @@ event_line = "@" , date_expr , event_type , { attribute } ;
|
||||
event_type = completion_event_type | eclipse_keyword ;
|
||||
completion_event_type = "PERFORATION" | "SEGMENT" | "VALVE" | "STATE" | "WELSPECS" ;
|
||||
segment_attribute = "MDSTART" | "MDEND" | "INNER_DIAMETER" | "ROUGHNESS"
|
||||
| "PRESSURE_COMPONENTS" | "COMMENT" ;
|
||||
| "COMMENT" ;
|
||||
(* accepted attribute names when event_type is "SEGMENT" *)
|
||||
wellspec_attribute = "GROUP" | "CROSSFLOW" | "REFDEPTH" | "PHASE"
|
||||
| "PRESSURE_COMPONENTS" | "COMMENT" ;
|
||||
(* accepted attribute names when event_type is "WELSPECS" *)
|
||||
|
||||
date_expr = ( iso_datetime | date_ident ) , { sign , term } ;
|
||||
duration_expr = term , { sign , term } ;
|
||||
@@ -215,7 +219,6 @@ Each event adds the underlying segment timeline event and a corresponding **Segm
|
||||
| `MDEND` | yes | segment event and segment interval `end_md` |
|
||||
| `INNER_DIAMETER` | no | segment event `inner_diameter` [m] |
|
||||
| `ROUGHNESS` | no | segment event `roughness` [m] |
|
||||
| `PRESSURE_COMPONENTS` | no | MSW `pressure_drop`: `H--`, `HF-` or `HFA` |
|
||||
|
||||
**VALVE** → `add_valve_event` (requires an existing perforation at the MD)
|
||||
|
||||
@@ -234,6 +237,18 @@ Each event adds the underlying segment timeline event and a corresponding **Segm
|
||||
|---|---|---|
|
||||
| `STATE` | yes | `well_state`: `OPEN`, `SHUT` or `STOP` |
|
||||
|
||||
**WELSPECS** → `add_wellspec_event`
|
||||
|
||||
Well-level settings. At least one setting attribute is required; omitted values inherit the previous WELSPECS event for the well, initially the well's completion settings. Only one WELSPECS event per well and timestamp is allowed. `timeline.set_timestamp()` writes the values in effect back to the completion settings.
|
||||
|
||||
| Attribute | Required | Maps to |
|
||||
|---|---|---|
|
||||
| `GROUP` | no | `group_name` |
|
||||
| `CROSSFLOW` | no | `allow_cross_flow`: `True` or `False` |
|
||||
| `REFDEPTH` | no | `reference_depth` [m] |
|
||||
| `PHASE` | no | `well_type`: `OIL`, `GAS`, `WATER` or `LIQUID` |
|
||||
| `PRESSURE_COMPONENTS` | no | `pressure_drop`: `H--`, `HF-` or `HFA`. Written to the WELSEGS header of the MSW keywords exported at or after the event date |
|
||||
|
||||
Unknown attributes on a completion event are an error and skip the event. On completion events other than `PERFORATION`, `FILTER` is ignored with a warning.
|
||||
|
||||
### Well keyword events
|
||||
|
||||
Reference in new issue
Block a user