Tie-in valve improvements (#13661)

Support (bool, something) pairs as tuples on python side.
Support custom outlet valve md
Add python test for custom outlet valve md
Use tie-in depth as default for custom valve md
Standalone valve creation and display updates
Update MSW valve export
Add valve open/shut flag.
Add python examples
This commit is contained in:
JJ
2026-03-06 12:01:14 +01:00
committed by GitHub
parent f47de8457c
commit 24be78f105
34 changed files with 1139 additions and 157 deletions
@@ -32,6 +32,7 @@ set(SOURCE_GROUP_HEADER_FILES
${CMAKE_CURRENT_LIST_DIR}/RimNonDarcyPerforationParameters.h
${CMAKE_CURRENT_LIST_DIR}/RimWellPathComponentInterface.h
${CMAKE_CURRENT_LIST_DIR}/RimWellPathValve.h
${CMAKE_CURRENT_LIST_DIR}/RimValveCollection.h
${CMAKE_CURRENT_LIST_DIR}/RimMultipleValveLocations.h
${CMAKE_CURRENT_LIST_DIR}/RimWellPathAicdParameters.h
${CMAKE_CURRENT_LIST_DIR}/RimWellPathSicdParameters.h
@@ -74,6 +75,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RimMswCompletionParameters.cpp
${CMAKE_CURRENT_LIST_DIR}/RimNonDarcyPerforationParameters.cpp
${CMAKE_CURRENT_LIST_DIR}/RimWellPathValve.cpp
${CMAKE_CURRENT_LIST_DIR}/RimValveCollection.cpp
${CMAKE_CURRENT_LIST_DIR}/RimMultipleValveLocations.cpp
${CMAKE_CURRENT_LIST_DIR}/RimWellPathAicdParameters.cpp
${CMAKE_CURRENT_LIST_DIR}/RimWellPathSicdParameters.cpp
@@ -0,0 +1,156 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RimValveCollection.h"
#include "RimProject.h"
#include "RimValveTemplate.h"
#include "RimValveTemplateCollection.h"
#include "RimWellPathValve.h"
#include "Riu3DMainWindowTools.h"
#include "cafPdmFieldScriptingCapability.h"
#include "cafPdmObjectScriptingCapability.h"
CAF_PDM_SOURCE_INIT( RimValveCollection, "ValveCollection" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimValveCollection::RimValveCollection()
{
CAF_PDM_InitScriptableObject( "Valves", ":/ICVValve16x16.png" );
nameField()->uiCapability()->setUiHidden( true );
setName( "Valves" );
CAF_PDM_InitScriptableFieldNoDefault( &m_valves, "Valves", "Valves" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimValveCollection::initAfterRead()
{
for ( auto& valve : m_valves() )
{
valve->setComponentTypeFilter( { RiaDefines::WellPathComponentType::ICV } );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimValveCollection::hasValves() const
{
return !m_valves.empty();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellPathValve* RimValveCollection::addIcvValve( double valveMd )
{
RimWellPathValve* valve = new RimWellPathValve();
auto templateCollections = RimProject::current()->allValveTemplateCollections();
if ( templateCollections.empty() ) return nullptr;
auto tempColl = templateCollections.front();
auto icvTemplate = tempColl->getOrCreateIcvTemplate();
valve->setValveTemplate( icvTemplate );
m_valves.push_back( valve );
valve->setComponentTypeFilter( { RiaDefines::WellPathComponentType::ICV } );
valve->setMeasuredDepthAndCount( valveMd, 1.0, 1 );
return valve;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimWellPathValve*> RimValveCollection::valves() const
{
std::vector<RimWellPathValve*> valvesToReturn;
for ( const auto& v : m_valves() )
{
valvesToReturn.push_back( v );
}
return valvesToReturn;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RimWellPathValve*> RimValveCollection::activeValves() const
{
std::vector<RimWellPathValve*> valvesToReturn;
if ( !isChecked() ) return valvesToReturn;
for ( const auto& v : m_valves() )
{
if ( v->isChecked() )
{
valvesToReturn.push_back( v );
}
}
return valvesToReturn;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimValveCollection::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
uiOrdering.skipRemainingFields( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimValveCollection::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
{
RimProject* proj = RimProject::current();
if ( changedField == &m_isChecked )
{
proj->reloadCompletionTypeResultsInAllViews();
}
else
{
proj->scheduleCreateDisplayModelAndRedrawAllViews();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimValveCollection::onChildDeleted( caf::PdmChildArrayFieldHandle* childArray, std::vector<caf::PdmObjectHandle*>& referringObjects )
{
RimProject* proj = RimProject::current();
proj->reloadCompletionTypeResultsInAllViews();
updateConnectedEditors();
}
@@ -0,0 +1,56 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2026 Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "RimCheckableNamedObject.h"
#include "cafPdmChildArrayField.h"
#include "cafPdmChildField.h"
#include "cafPdmField.h"
#include "cafPdmObject.h"
class RimWellPathValve;
//==================================================================================================
//
//
//
//==================================================================================================
class RimValveCollection : public RimCheckableNamedObject
{
CAF_PDM_HEADER_INIT;
public:
RimValveCollection();
bool hasValves() const;
RimWellPathValve* addIcvValve( double valveMd );
std::vector<RimWellPathValve*> valves() const;
std::vector<RimWellPathValve*> activeValves() const;
private:
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
void onChildDeleted( caf::PdmChildArrayFieldHandle* childArray, std::vector<caf::PdmObjectHandle*>& referringObjects ) override;
void initAfterRead() override;
private:
caf::PdmChildArrayField<RimWellPathValve*> m_valves;
};
@@ -93,3 +93,24 @@ void RimValveTemplateCollection::addDefaultValveTemplates()
addItem( icv );
addItem( sicd );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimValveTemplate* RimValveTemplateCollection::getOrCreateIcvTemplate()
{
for ( RimValveTemplate* templateItem : items() )
{
if ( templateItem->type() == RiaDefines::WellPathComponentType::ICV )
{
return templateItem;
}
}
RimValveTemplate* icv = new RimValveTemplate;
icv->setType( RiaDefines::WellPathComponentType::ICV );
icv->setUserLabel( "ICV default template" );
addItem( icv );
return icv;
}
@@ -38,7 +38,8 @@ public:
~RimValveTemplateCollection() override;
// Domain-specific methods
void addDefaultValveTemplates();
void addDefaultValveTemplates();
RimValveTemplate* getOrCreateIcvTemplate();
caf::AppEnum<RiaDefines::EclipseUnitSystem> defaultUnitSystemType() const;
void setDefaultUnitSystemBasedOnLoadedCases();
@@ -29,6 +29,7 @@
#include "RimProject.h"
#include "RimStimPlanModel.h"
#include "RimStimPlanModelCollection.h"
#include "RimValveCollection.h"
#include "RimWellPath.h"
#include "RimWellPathComponentInterface.h"
#include "RimWellPathFracture.h"
@@ -58,6 +59,9 @@ RimWellPathCompletions::RimWellPathCompletions()
CAF_PDM_InitScriptableFieldNoDefault( &m_perforationCollection, "Perforations", "Perforations" );
m_perforationCollection = new RimPerforationCollection;
CAF_PDM_InitScriptableFieldNoDefault( &m_valveCollection, "Valves", "Valves" );
m_valveCollection = new RimValveCollection();
CAF_PDM_InitScriptableFieldNoDefault( &m_fishbonesCollection, "Fishbones", "Fishbones" );
m_fishbonesCollection = new RimFishbonesCollection;
@@ -114,6 +118,16 @@ RimPerforationCollection* RimWellPathCompletions::perforationCollection() const
return m_perforationCollection;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimValveCollection* RimWellPathCompletions::valveCollection() const
{
CVF_ASSERT( m_valveCollection );
return m_valveCollection;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -258,7 +272,8 @@ bool RimWellPathCompletions::hasCompletions() const
return true;
}
return !m_fishbonesCollection->allFishbonesSubs().empty() || !m_perforationCollection->perforations().empty();
return !m_fishbonesCollection->allFishbonesSubs().empty() || !m_perforationCollection->perforations().empty() ||
m_valveCollection->hasValves();
}
//--------------------------------------------------------------------------------------------------
@@ -284,7 +299,12 @@ void RimWellPathCompletions::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTre
{
uiTreeOrdering.skipRemainingChildren( true );
if ( !m_perforationCollection->perforations().empty() )
if ( m_valveCollection->hasValves() )
{
uiTreeOrdering.add( &m_valveCollection );
}
if ( m_perforationCollection->hasPerforations() )
{
uiTreeOrdering.add( &m_perforationCollection );
}
@@ -29,6 +29,7 @@
class RimFishbonesCollection;
class RimMswSegmentCollection;
class RimPerforationCollection;
class RimValveCollection;
class RimStimPlanModelCollection;
class RimWellPathComponentInterface;
class RimWellPathFracture;
@@ -48,6 +49,7 @@ public:
RimFishbonesCollection* fishbonesCollection() const;
RimPerforationCollection* perforationCollection() const;
RimValveCollection* valveCollection() const;
RimWellPathFractureCollection* fractureCollection() const;
RimStimPlanModelCollection* stimPlanModelCollection() const;
RimMswSegmentCollection* mswSegmentCollection() const;
@@ -72,6 +74,7 @@ private:
private:
caf::PdmChildField<RimFishbonesCollection*> m_fishbonesCollection;
caf::PdmChildField<RimPerforationCollection*> m_perforationCollection;
caf::PdmChildField<RimValveCollection*> m_valveCollection;
caf::PdmChildField<RimWellPathFractureCollection*> m_fractureCollection;
caf::PdmChildField<RimStimPlanModelCollection*> m_stimPlanModelCollection;
caf::PdmChildField<RimMswSegmentCollection*> m_mswSegmentCollection;
@@ -30,6 +30,7 @@
#include "RimMultipleValveLocations.h"
#include "RimPerforationInterval.h"
#include "RimProject.h"
#include "RimValveCollection.h"
#include "RimValveTemplate.h"
#include "RimWellPath.h"
@@ -51,9 +52,8 @@ RimWellPathValve::RimWellPathValve()
CAF_PDM_InitFieldNoDefault( &m_valveTemplate, "ValveTemplate", "Valve Template" );
CAF_PDM_InitScriptableField( &m_measuredDepth, "StartMeasuredDepth", 0.0, "Start MD" );
CAF_PDM_InitScriptableField( &m_isOpen, "IsOpen", true, "Valve is Open" );
CAF_PDM_InitFieldNoDefault( &m_multipleValveLocations, "ValveLocations", "Valve Locations" );
CAF_PDM_InitField( &m_editValveTemplate, "EditTemplate", false, "Edit" );
CAF_PDM_InitField( &m_createValveTemplate, "CreateTemplate", false, "Create" );
CAF_PDM_InitField( &m_useCustomStartDate, "UseCustomStartDate", false, "Custom Start Date" );
CAF_PDM_InitField( &m_startDate, "StartDate", QDateTime::currentDateTime(), "Start Date" );
@@ -61,10 +61,6 @@ RimWellPathValve::RimWellPathValve()
m_measuredDepth.uiCapability()->setUiEditorTypeName( caf::PdmUiDoubleSliderEditor::uiEditorTypeName() );
m_multipleValveLocations = new RimMultipleValveLocations;
m_multipleValveLocations.uiCapability()->setUiTreeChildrenHidden( true );
m_editValveTemplate.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::LabelPosition::HIDDEN );
m_editValveTemplate.uiCapability()->setUiEditorTypeName( caf::PdmUiToolButtonEditor::uiEditorTypeName() );
m_createValveTemplate.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::LabelPosition::HIDDEN );
m_createValveTemplate.uiCapability()->setUiEditorTypeName( caf::PdmUiToolButtonEditor::uiEditorTypeName() );
nameField()->uiCapability()->setUiReadOnly( true );
@@ -152,6 +148,15 @@ double RimWellPathValve::orificeDiameter( RiaDefines::EclipseUnitSystem unitSyst
return 0.0;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimWellPathValve::area( RiaDefines::EclipseUnitSystem unitSystem ) const
{
const double orificeRadius = orificeDiameter( unitSystem ) / 2.0;
return orificeRadius * orificeRadius * cvf::PI_D;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -165,6 +170,22 @@ double RimWellPathValve::flowCoefficient() const
return 0.0;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimWellPathValve::isOpen() const
{
return m_isOpen();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellPathValve::setOpen( bool openFlag )
{
m_isOpen = openFlag;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -317,39 +338,46 @@ double RimWellPathValve::convertOrificeDiameter( double o
//--------------------------------------------------------------------------------------------------
std::vector<std::pair<double, double>> RimWellPathValve::valveSegments() const
{
auto perforationInterval = firstAncestorOrThisOfType<RimPerforationInterval>();
std::vector<std::pair<double, double>> segments;
if ( componentType() == RiaDefines::WellPathComponentType::ICV )
if ( auto perforationInterval = firstAncestorOrThisOfType<RimPerforationInterval>() )
{
// Flow for ICV is defined as the complete perforation interval
segments.push_back( std::make_pair( perforationInterval->startMD(), perforationInterval->endMD() ) );
}
else
{
// ICD/AICD : Use the valve start/end, can be a subset of perforation interval
double startMD = this->startMD();
double endMD = this->endMD();
std::vector<double> valveMDs = valveLocations();
segments.reserve( valveMDs.size() );
for ( size_t i = 0; i < valveMDs.size(); ++i )
if ( componentType() == RiaDefines::WellPathComponentType::ICV )
{
double segmentStart = startMD;
double segmentEnd = endMD;
if ( i > 0 )
{
segmentStart = 0.5 * ( valveMDs[i - 1] + valveMDs[i] );
}
if ( i < valveMDs.size() - 1u )
{
segmentEnd = 0.5 * ( valveMDs[i] + valveMDs[i + 1] );
}
segments.push_back( std::make_pair( segmentStart, segmentEnd ) );
// Flow for ICV is defined as the complete perforation interval
segments.push_back( std::make_pair( perforationInterval->startMD(), perforationInterval->endMD() ) );
}
else
{
// ICD/AICD : Use the valve start/end, can be a subset of perforation interval
double startMD = this->startMD();
double endMD = this->endMD();
std::vector<double> valveMDs = valveLocations();
segments.reserve( valveMDs.size() );
for ( size_t i = 0; i < valveMDs.size(); ++i )
{
double segmentStart = startMD;
double segmentEnd = endMD;
if ( i > 0 )
{
segmentStart = 0.5 * ( valveMDs[i - 1] + valveMDs[i] );
}
if ( i < valveMDs.size() - 1u )
{
segmentEnd = 0.5 * ( valveMDs[i] + valveMDs[i + 1] );
}
segments.push_back( std::make_pair( segmentStart, segmentEnd ) );
}
}
}
else if ( firstAncestorOrThisOfType<RimValveCollection>() )
{
// stand-alone ICV valve
segments.emplace_back( startMD(), endMD() );
}
return segments;
@@ -368,8 +396,15 @@ void RimWellPathValve::setComponentTypeFilter( const std::set<RiaDefines::WellPa
//--------------------------------------------------------------------------------------------------
bool RimWellPathValve::isEnabled() const
{
auto perforationInterval = firstAncestorOrThisOfType<RimPerforationInterval>();
return perforationInterval->isEnabled() && isChecked();
if ( auto perforationInterval = firstAncestorOrThisOfType<RimPerforationInterval>() )
{
return perforationInterval->isEnabled() && isChecked();
}
if ( auto valveCollection = firstAncestorOrThisOfType<RimValveCollection>() )
{
return valveCollection->isChecked() && isChecked();
}
return false;
}
//--------------------------------------------------------------------------------------------------
@@ -528,17 +563,20 @@ QList<caf::PdmOptionItemInfo> RimWellPathValve::calculateValueOptions( const caf
{
QList<caf::PdmOptionItemInfo> options;
RimProject* project = RimProject::current();
std::vector<RimValveTemplate*> allTemplates = project->allValveTemplates();
for ( RimValveTemplate* valveTemplate : allTemplates )
if ( fieldNeedingOptions == &m_valveTemplate )
{
if ( !m_componentTypeFilter.empty() )
{
if ( m_componentTypeFilter.count( valveTemplate->type() ) == 0 ) continue;
}
RimProject* project = RimProject::current();
options.push_back( caf::PdmOptionItemInfo( valveTemplate->name(), valveTemplate ) );
std::vector<RimValveTemplate*> allTemplates = project->allValveTemplates();
for ( RimValveTemplate* valveTemplate : allTemplates )
{
if ( !m_componentTypeFilter.empty() )
{
if ( m_componentTypeFilter.count( valveTemplate->type() ) == 0 ) continue;
}
options.push_back( caf::PdmOptionItemInfo( valveTemplate->name(), valveTemplate ) );
}
}
return options;
@@ -554,15 +592,6 @@ void RimWellPathValve::fieldChangedByUi( const caf::PdmFieldHandle* changedField
applyValveLabelAndIcon();
updateConnectedEditors();
}
else if ( changedField == &m_createValveTemplate )
{
m_createValveTemplate = false;
RicNewValveTemplateFeature::createNewValveTemplateForValveAndUpdate( this );
}
else if ( changedField == &m_editValveTemplate )
{
Riu3DMainWindowTools::selectAsCurrentItem( m_valveTemplate() );
}
auto perfInterval = firstAncestorOrThisOfType<RimPerforationInterval>();
if ( perfInterval )
@@ -581,14 +610,14 @@ void RimWellPathValve::defineUiOrdering( QString uiConfigName, caf::PdmUiOrderin
{
uiOrdering.skipRemainingFields( true );
uiOrdering.add( &m_valveTemplate, { .totalColumnSpan = 2, .leftLabelColumnSpan = 1 } );
uiOrdering.add( &m_valveTemplate );
{
if ( m_valveTemplate() != nullptr )
{
uiOrdering.appendToRow( &m_editValveTemplate );
uiOrdering.addNewButton( "Edit Template", [this]() { Riu3DMainWindowTools::selectAsCurrentItem( m_valveTemplate() ); } );
}
uiOrdering.appendToRow( &m_createValveTemplate );
uiOrdering.addNewButton( "Create Template", [this]() { RicNewValveTemplateFeature::createNewValveTemplateForValveAndUpdate( this ); } );
}
if ( uiConfigName != "TemplateOnly" )
@@ -613,6 +642,8 @@ void RimWellPathValve::defineUiOrdering( QString uiConfigName, caf::PdmUiOrderin
}
}
uiOrdering.add( &m_isOpen );
if ( componentType() == RiaDefines::WellPathComponentType::ICD || componentType() == RiaDefines::WellPathComponentType::AICD ||
componentType() == RiaDefines::WellPathComponentType::SICD )
{
@@ -56,12 +56,16 @@ public:
std::vector<double> valveLocations() const;
double orificeDiameter( RiaDefines::EclipseUnitSystem unitSystem ) const;
double flowCoefficient() const;
double area( RiaDefines::EclipseUnitSystem unitSystem ) const;
RimValveTemplate* valveTemplate() const;
void setValveTemplate( RimValveTemplate* valveTemplate );
void applyValveLabelAndIcon();
const RimWellPathAicdParameters* aicdParameters() const;
const RimWellPathSicdParameters* sicdParameters() const;
bool isOpen() const;
void setOpen( bool openFlag );
void enableCustomStartDate( bool enable );
void setCustomStartDate( const QDate& date );
bool isActiveOnDate( const QDateTime& date ) const;
@@ -96,9 +100,8 @@ private:
private:
caf::PdmPtrField<RimValveTemplate*> m_valveTemplate;
caf::PdmField<double> m_measuredDepth;
caf::PdmField<bool> m_isOpen;
caf::PdmChildField<RimMultipleValveLocations*> m_multipleValveLocations;
caf::PdmField<bool> m_editValveTemplate;
caf::PdmField<bool> m_createValveTemplate;
caf::PdmField<bool> m_useCustomStartDate;
caf::PdmField<QDateTime> m_startDate;