Files
ResInsight/ApplicationLibCode/ProjectDataModel/WellEvents/RimWellEventControl.cpp
T
Kristian Bendiksen a4dad97024 #13425 Add time-dependent well events and schedule data generation
Implement timeline-based event system for managing temporal changes in
well completions, perforations, valves, and production controls.

Features:
- Event timeline container with date-based querying
- Six event types: PERF, VALVE, TUBING, WSTATE, WTYPE, WCONTROL
- Schedule keyword generation (DATES, COMPDAT, WELSEGS, WCONPROD, WCONINJE)
- Multi-well schedule generation support
- Python GRPC API for event management
- YAML configuration file support
- Comprehensive test suite

Implementation:
- New directory: ApplicationLibCode/ProjectDataModel/WellEvents/
- Event classes inherit from RimWellEvent base class
- RimWellEventTimeline integrated into RimWellPath
- RicScheduleDataGenerator for multi-well schedule export
- Python API: event_timeline(), add_perf_event(), add_valve_event(), etc.
- Tests verify all event types and YAML config parsing

#13425 Add date tracking to valves and diameter/roughness intervals

Add creation date fields to RimWellPathValve and RimDiameterRoughnessInterval
to support filtering objects by date during completion data export.

- Add m_useCustomStartDate and m_startDate fields
- Add enableCustomStartDate(), setCustomStartDate(), isActiveOnDate() methods
- Set creation dates when applying valve and tubing events in RimWellEventTimeline

#13425 Add date-based filtering for schedule data export

Add optional export date parameter to MSW data collection functions to filter
valves and perforations based on their creation dates during schedule export.

- Add exportDate parameter to collectWsegvalvData, collectWsegAicdData,
  collectWsegSicdData, and collectCompsegData functions
- Filter valves by checking isActiveOnDate() on the associated RimWellPathValve
- Add RimPerforationInterval reference to RicMswPerforation for date filtering
- Pass export date from RicScheduleDataGenerator through the extraction chain

This ensures segment numbers reflect the full model while only including
objects active on the export date in output keywords.

Add test to prove compsegs changes over time.

#13425 Fix mypy type checking errors in well_events.py

- Add type annotations to all function parameters
- Fix generic type hints (dict -> Dict[str, Any])
- Import Any type for proper type safety
- Add type: ignore for method reassignment
- All 31 tests pass

#13425 Add schedule-level keyword events for non-well-specific keywords

Add a new event type called "Keyword Event" for schedule-level Eclipse
keywords (RPTRST, GRUPTREE, RPTSCHED, etc.) that are NOT tied to a
specific well path. This complements the existing RimWellEventKeyword
which operates at the well level.

- Add RimKeywordEvent class inheriting from RimWellEvent with null wellPath
- Add SCHEDULE_KEYWORD enum value to EventType
- Add addKeywordEvent() method to RimWellEventTimeline
- Add AddKeywordEvent Python API method to RimcWellEventTimeline
- Update RicScheduleDataGenerator to output global keywords after well sections
- Add Python wrapper add_keyword_event() with type inference
- Add tests and examples for RPTRST, GRUPTREE keywords
2026-02-12 08:15:22 +01:00

383 lines
14 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025- 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 "RimWellEventControl.h"
#include "cafPdmFieldScriptingCapability.h"
#include "cafPdmObjectScriptingCapability.h"
namespace caf
{
template <>
void caf::AppEnum<RimWellEventControl::ControlMode>::setUp()
{
addItem( RimWellEventControl::ControlMode::ORAT, "ORAT", "Oil Rate" );
addItem( RimWellEventControl::ControlMode::WRAT, "WRAT", "Water Rate" );
addItem( RimWellEventControl::ControlMode::GRAT, "GRAT", "Gas Rate" );
addItem( RimWellEventControl::ControlMode::LRAT, "LRAT", "Liquid Rate" );
addItem( RimWellEventControl::ControlMode::RESV, "RESV", "Reservoir Volume" );
addItem( RimWellEventControl::ControlMode::BHP, "BHP", "BHP" );
addItem( RimWellEventControl::ControlMode::THP, "THP", "THP" );
setDefault( RimWellEventControl::ControlMode::ORAT );
}
template <>
void caf::AppEnum<RimWellEventControl::WellStatus>::setUp()
{
addItem( RimWellEventControl::WellStatus::OPEN, "OPEN", "Open" );
addItem( RimWellEventControl::WellStatus::SHUT, "SHUT", "Shut" );
addItem( RimWellEventControl::WellStatus::STOP, "STOP", "Stop" );
setDefault( RimWellEventControl::WellStatus::OPEN );
}
} // namespace caf
CAF_PDM_SOURCE_INIT( RimWellEventControl, "WellEventControl" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellEventControl::RimWellEventControl()
{
CAF_PDM_InitScriptableObject( "Well Control Event", "", "", "WellEventControl" );
CAF_PDM_InitScriptableField( &m_controlMode, "ControlMode", caf::AppEnum<ControlMode>( ControlMode::ORAT ), "Control Mode" );
CAF_PDM_InitScriptableField( &m_wellStatus, "WellStatus", caf::AppEnum<WellStatus>( WellStatus::OPEN ), "Well Status" );
CAF_PDM_InitScriptableField( &m_controlValue, "ControlValue", 0.0, "Control Value" );
CAF_PDM_InitScriptableField( &m_bhpLimit, "BhpLimit", 0.0, "BHP Limit [bar]" );
CAF_PDM_InitScriptableField( &m_thpLimit, "ThpLimit", 0.0, "THP Limit [bar]" );
CAF_PDM_InitScriptableField( &m_oilRate, "OilRate", 0.0, "Oil Rate [Sm3/day]" );
CAF_PDM_InitScriptableField( &m_waterRate, "WaterRate", 0.0, "Water Rate [Sm3/day]" );
CAF_PDM_InitScriptableField( &m_gasRate, "GasRate", 0.0, "Gas Rate [Sm3/day]" );
CAF_PDM_InitScriptableField( &m_liquidRate, "LiquidRate", 0.0, "Liquid Rate [Sm3/day]" );
CAF_PDM_InitScriptableField( &m_isProducer, "IsProducer", true, "Is Producer" );
setDeletable( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellEventControl::~RimWellEventControl()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellEventControl::ControlMode RimWellEventControl::controlMode() const
{
return m_controlMode();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellEventControl::WellStatus RimWellEventControl::wellStatus() const
{
return m_wellStatus();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimWellEventControl::controlValue() const
{
return m_controlValue();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimWellEventControl::bhpLimit() const
{
return m_bhpLimit();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimWellEventControl::thpLimit() const
{
return m_thpLimit();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimWellEventControl::oilRate() const
{
return m_oilRate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimWellEventControl::waterRate() const
{
return m_waterRate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimWellEventControl::gasRate() const
{
return m_gasRate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double RimWellEventControl::liquidRate() const
{
return m_liquidRate();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimWellEventControl::isProducer() const
{
return m_isProducer();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellEventControl::setControlMode( ControlMode mode )
{
m_controlMode = mode;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellEventControl::setWellStatus( WellStatus status )
{
m_wellStatus = status;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellEventControl::setControlValue( double value )
{
m_controlValue = value;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellEventControl::setBhpLimit( double limit )
{
m_bhpLimit = limit;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellEventControl::setThpLimit( double limit )
{
m_thpLimit = limit;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellEventControl::setOilRate( double rate )
{
m_oilRate = rate;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellEventControl::setWaterRate( double rate )
{
m_waterRate = rate;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellEventControl::setGasRate( double rate )
{
m_gasRate = rate;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellEventControl::setLiquidRate( double rate )
{
m_liquidRate = rate;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellEventControl::setIsProducer( bool isProducer )
{
m_isProducer = isProducer;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimWellEvent::EventType RimWellEventControl::eventType() const
{
return EventType::WCONTROL;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimWellEventControl::generateScheduleKeyword( const QString& wellName ) const
{
if ( m_isProducer() )
{
return generateWCONPROD( wellName );
}
else
{
// Determine injector type based on control mode or use water as default
QString injectorType = "WATER";
if ( m_controlMode() == ControlMode::GRAT )
{
injectorType = "GAS";
}
return generateWCONINJE( wellName, injectorType );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimWellEventControl::generateWCONPROD( const QString& wellName ) const
{
// WCONPROD format:
// Well Status Ctrl OilRate WatRate GasRate LiqRate ResV BHP THP
auto formatValue = []( double val ) -> QString
{
if ( val == 0.0 ) return "1*";
return QString::number( val, 'f', 1 );
};
QString line = QString( "WCONPROD\n" );
line += QString( "-- Well Status Ctrl OilRate WatRate GasRate LiqRate ResV BHP THP\n" );
line += QString( " %1 %2 %3 %4 %5 %6 %7 1* %8 %9 /\n" )
.arg( wellName, -8 )
.arg( m_wellStatus().text(), -4 )
.arg( m_controlMode().text(), -4 )
.arg( formatValue( m_oilRate() ), 8 )
.arg( formatValue( m_waterRate() ), 8 )
.arg( formatValue( m_gasRate() ), 8 )
.arg( formatValue( m_liquidRate() ), 8 )
.arg( formatValue( m_bhpLimit() ), 6 )
.arg( formatValue( m_thpLimit() ), 6 );
line += "/\n\n";
return line;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimWellEventControl::generateWCONINJE( const QString& wellName, const QString& injectorType ) const
{
// WCONINJE format:
// Well Type Status Ctrl Rate BHP
QString ctrlStr;
switch ( m_controlMode() )
{
case ControlMode::WRAT:
ctrlStr = "RATE";
break;
case ControlMode::GRAT:
ctrlStr = "RATE";
break;
case ControlMode::BHP:
ctrlStr = "BHP";
break;
case ControlMode::THP:
ctrlStr = "THP";
break;
default:
ctrlStr = "RATE";
break;
}
auto formatValue = []( double val ) -> QString
{
if ( val == 0.0 ) return "1*";
return QString::number( val, 'f', 1 );
};
double rate = ( injectorType == "WATER" ) ? m_waterRate() : m_gasRate();
QString line = QString( "WCONINJE\n" );
line += QString( "-- Well Type Status Ctrl Rate BHP\n" );
line += QString( " %1 %2 %3 %4 %5 %6 /\n" )
.arg( wellName, -8 )
.arg( injectorType, -5 )
.arg( m_wellStatus().text(), -4 )
.arg( ctrlStr, -4 )
.arg( formatValue( rate ), 10 )
.arg( formatValue( m_bhpLimit() ), 8 );
line += "/\n\n";
return line;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellEventControl::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
RimWellEvent::defineUiOrdering( uiConfigName, uiOrdering );
uiOrdering.add( &m_isProducer );
uiOrdering.add( &m_wellStatus );
uiOrdering.add( &m_controlMode );
uiOrdering.add( &m_controlValue );
caf::PdmUiGroup* ratesGroup = uiOrdering.addNewGroup( "Rate Limits" );
ratesGroup->add( &m_oilRate );
ratesGroup->add( &m_waterRate );
ratesGroup->add( &m_gasRate );
ratesGroup->add( &m_liquidRate );
caf::PdmUiGroup* pressureGroup = uiOrdering.addNewGroup( "Pressure Limits" );
pressureGroup->add( &m_bhpLimit );
pressureGroup->add( &m_thpLimit );
uiOrdering.skipRemainingFields();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimWellEventControl::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName )
{
QString typeStr = m_isProducer() ? "PROD" : "INJ";
QString ctrlStr = m_controlMode().uiText();
QString valueStr = QString::number( m_controlValue(), 'f', 0 );
setUiName( QString( "WCTRL %1: %2 %3 %4" ).arg( m_eventDate().toString( "yyyy-MM-dd" ) ).arg( typeStr ).arg( ctrlStr ).arg( valueStr ) );
}