mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-26 07:00:17 -05:00
COMMENT is accepted on all keywords and is stored on the event instead of being passed as keyword data, so it is not part of the item names reported by opm-common. The validation error listed only the opm-common items, giving no hint that COMMENT is allowed when a user mistyped it as COMMENTS. Append COMMENT to the list of valid item names in the error message. Only the diagnostic is affected, COMMENT is still handled as event metadata.
874 lines
39 KiB
C++
874 lines
39 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 "RimcWellEventTimeline.h"
|
|
|
|
#include "CompletionExportCommands/RicScheduleDataGenerator.h"
|
|
#include "RiaOpmKeywordTools.h"
|
|
#include "RimEclipseCase.h"
|
|
#include "RimKeywordEvent.h"
|
|
#include "RimWellEventControl.h"
|
|
#include "RimWellEventKeyword.h"
|
|
#include "RimWellEventPerf.h"
|
|
#include "RimWellEventRawText.h"
|
|
#include "RimWellEventState.h"
|
|
#include "RimWellEventTimeline.h"
|
|
#include "RimWellEventTubing.h"
|
|
#include "RimWellEventValve.h"
|
|
#include "RimWellEventWellSpec.h"
|
|
#include "RimWellPath.h"
|
|
#include "RimWellPathCompletionSettings.h"
|
|
#include "RimcDataContainerString.h"
|
|
|
|
#include "cafAppEnum.h"
|
|
#include "cafPdmAbstractFieldScriptingCapability.h"
|
|
#include "cafPdmFieldScriptingCapability.h"
|
|
|
|
#include <QDateTime>
|
|
#include <QStringList>
|
|
|
|
#include <algorithm>
|
|
#include <set>
|
|
|
|
namespace
|
|
{
|
|
std::expected<void, QString> validateKeywordItems( const QString& keywordName, const std::vector<QString>& itemNames )
|
|
{
|
|
auto keywordInfo = RiaOpmKeywordTools::keywordInfo( keywordName );
|
|
if ( !keywordInfo )
|
|
{
|
|
return std::unexpected( QString( "Keyword '%1' is not recognized by opm-common." ).arg( keywordName ) );
|
|
}
|
|
|
|
if ( keywordInfo->acceptsArbitraryItems ) return {};
|
|
|
|
QStringList invalidNames;
|
|
for ( const auto& itemName : itemNames )
|
|
{
|
|
if ( std::find( keywordInfo->itemNames.begin(), keywordInfo->itemNames.end(), itemName ) == keywordInfo->itemNames.end() )
|
|
{
|
|
invalidNames.push_back( itemName );
|
|
}
|
|
}
|
|
|
|
if ( invalidNames.empty() ) return {};
|
|
|
|
QStringList validNames;
|
|
for ( const auto& itemName : keywordInfo->itemNames )
|
|
{
|
|
validNames.push_back( itemName );
|
|
}
|
|
|
|
// COMMENT is accepted on all keywords. It is stored on the event and emitted as a schedule comment instead of being
|
|
// passed as keyword data, so it is not part of the item names reported by opm-common. Include it in the list of
|
|
// valid names to help spot typos like COMMENTS.
|
|
const QString commentItemName( "COMMENT" );
|
|
if ( !validNames.contains( commentItemName ) ) validNames.push_back( commentItemName );
|
|
|
|
return std::unexpected( QString( "Keyword '%1' contains invalid item names: %2. Valid item names are: %3." )
|
|
.arg( keywordName.toUpper(), invalidNames.join( ", " ), validNames.join( ", " ) ) );
|
|
}
|
|
} // namespace
|
|
|
|
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellEventTimeline, RimcWellEventTimeline_addPerfEvent, "AddPerfEvent" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimcWellEventTimeline_addPerfEvent::RimcWellEventTimeline_addPerfEvent( caf::PdmObjectHandle* self )
|
|
: caf::PdmObjectCreationMethod( self )
|
|
{
|
|
CAF_PDM_InitObject( "Add Perforation Event", "", "", "Add a perforation event to the timeline" );
|
|
|
|
CAF_PDM_InitScriptableField( &m_eventDate, "EventDate", QString( "2024-01-01" ), "", "", "", "Event Date (YYYY-MM-DD)" );
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_wellPath, "WellPath", "", "", "", "Well Path" );
|
|
CAF_PDM_InitScriptableField( &m_startMd, "StartMd", 0.0, "", "", "", "Start Measured Depth" );
|
|
CAF_PDM_InitScriptableField( &m_endMd, "EndMd", 0.0, "", "", "", "End Measured Depth" );
|
|
CAF_PDM_InitScriptableField( &m_diameter, "Diameter", 0.216, "", "", "", "Diameter [m]" );
|
|
CAF_PDM_InitScriptableField( &m_skinFactor, "SkinFactor", 0.0, "", "", "", "Skin Factor" );
|
|
auto defaultState = RimWellEventPerf::State::OPEN;
|
|
CAF_PDM_InitScriptableField( &m_state, "State", defaultState, "", "", "", "State" );
|
|
CAF_PDM_InitScriptableField( &m_completionNumber, "CompletionNumber", 0, "", "", "", "Completion Number (for COMPLUMP, 0 = none)" );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::expected<caf::PdmObjectHandle*, QString> RimcWellEventTimeline_addPerfEvent::execute()
|
|
{
|
|
auto timeline = self<RimWellEventTimeline>();
|
|
|
|
if ( !m_wellPath() )
|
|
{
|
|
return std::unexpected( QString( "Well path is required" ) );
|
|
}
|
|
|
|
QDateTime date = QDateTime::fromString( m_eventDate(), Qt::ISODate );
|
|
if ( !date.isValid() )
|
|
{
|
|
return std::unexpected( QString( "Invalid date format: %1. Expected YYYY-MM-DD" ).arg( m_eventDate() ) );
|
|
}
|
|
|
|
auto* event = timeline->addPerforationEvent( m_wellPath(), date );
|
|
event->setStartMD( m_startMd() );
|
|
event->setEndMD( m_endMd() );
|
|
event->setDiameter( m_diameter() );
|
|
event->setSkinFactor( m_skinFactor() );
|
|
event->setState( m_state() );
|
|
event->setCompletionNumber( m_completionNumber() );
|
|
|
|
return event;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimcWellEventTimeline_addPerfEvent::classKeywordReturnedType() const
|
|
{
|
|
return RimWellEventPerf::classKeywordStatic();
|
|
}
|
|
|
|
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellEventTimeline, RimcWellEventTimeline_addValveEvent, "AddValveEvent" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimcWellEventTimeline_addValveEvent::RimcWellEventTimeline_addValveEvent( caf::PdmObjectHandle* self )
|
|
: caf::PdmObjectCreationMethod( self )
|
|
{
|
|
CAF_PDM_InitObject( "Add Valve Event", "", "", "Add a valve event to the timeline" );
|
|
|
|
CAF_PDM_InitScriptableField( &m_eventDate, "EventDate", QString( "2024-01-01" ), "", "", "", "Event Date (YYYY-MM-DD)" );
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_wellPath, "WellPath", "", "", "", "Well Path" );
|
|
CAF_PDM_InitScriptableField( &m_measuredDepth, "MeasuredDepth", 0.0, "", "", "", "Measured Depth" );
|
|
auto defaultValveType = RimWellEventValve::ValveType::ICV;
|
|
CAF_PDM_InitScriptableField( &m_valveType, "ValveType", defaultValveType, "", "", "", "Valve Type" );
|
|
auto defaultState = RimWellEventValve::State::OPEN;
|
|
CAF_PDM_InitScriptableField( &m_state, "State", defaultState, "", "", "", "State" );
|
|
CAF_PDM_InitScriptableField( &m_flowCoefficient, "FlowCoefficient", 0.7, "", "", "", "Flow Coefficient" );
|
|
CAF_PDM_InitScriptableField( &m_area, "Area", 0.0001, "", "", "", "Area [m2]" );
|
|
CAF_PDM_InitScriptableField( &m_aicdStrength, "AicdStrength", 0.00021, "", "", "", "AICD Strength" );
|
|
CAF_PDM_InitScriptableField( &m_aicdDensityCalibFluid, "AicdDensityCalibFluid", 1000.0, "", "", "", "AICD Density of Calibration Fluid [kg/m3]" );
|
|
CAF_PDM_InitScriptableField( &m_aicdViscosityCalibFluid, "AicdViscosityCalibFluid", 1.0, "", "", "", "AICD Viscosity of Calibration Fluid [cP]" );
|
|
CAF_PDM_InitScriptableField( &m_aicdVolFlowExp, "AicdVolFlowExp", 2.1, "", "", "", "AICD Volume Flow Rate Exponent" );
|
|
CAF_PDM_InitScriptableField( &m_aicdViscFuncExp, "AicdViscFuncExp", 0.5, "", "", "", "AICD Viscosity Function Exponent" );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::expected<caf::PdmObjectHandle*, QString> RimcWellEventTimeline_addValveEvent::execute()
|
|
{
|
|
auto timeline = self<RimWellEventTimeline>();
|
|
|
|
if ( !m_wellPath() )
|
|
{
|
|
return std::unexpected( QString( "Well path is required" ) );
|
|
}
|
|
|
|
QDateTime date = QDateTime::fromString( m_eventDate(), Qt::ISODate );
|
|
if ( !date.isValid() )
|
|
{
|
|
return std::unexpected( QString( "Invalid date format: %1. Expected YYYY-MM-DD" ).arg( m_eventDate() ) );
|
|
}
|
|
|
|
auto* event = timeline->addValveEvent( m_wellPath(), date );
|
|
event->setMeasuredDepth( m_measuredDepth() );
|
|
event->setFlowCoefficient( m_flowCoefficient() );
|
|
event->setArea( m_area() );
|
|
event->setValveType( m_valveType() );
|
|
event->setState( m_state() );
|
|
event->setAicdStrength( m_aicdStrength() );
|
|
event->setAicdDensityCalibFluid( m_aicdDensityCalibFluid() );
|
|
event->setAicdViscosityCalibFluid( m_aicdViscosityCalibFluid() );
|
|
event->setAicdVolFlowExp( m_aicdVolFlowExp() );
|
|
event->setAicdViscFuncExp( m_aicdViscFuncExp() );
|
|
|
|
return event;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimcWellEventTimeline_addValveEvent::classKeywordReturnedType() const
|
|
{
|
|
return RimWellEventValve::classKeywordStatic();
|
|
}
|
|
|
|
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellEventTimeline, RimcWellEventTimeline_addStateEvent, "AddStateEvent" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimcWellEventTimeline_addStateEvent::RimcWellEventTimeline_addStateEvent( caf::PdmObjectHandle* self )
|
|
: caf::PdmObjectCreationMethod( self )
|
|
{
|
|
CAF_PDM_InitObject( "Add State Event", "", "", "Add a well state event to the timeline" );
|
|
|
|
CAF_PDM_InitScriptableField( &m_eventDate, "EventDate", QString( "2024-01-01" ), "", "", "", "Event Date (YYYY-MM-DD)" );
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_wellPath, "WellPath", "", "", "", "Well Path" );
|
|
auto defaultWellState = RimWellEventState::WellState::OPEN;
|
|
CAF_PDM_InitScriptableField( &m_wellState, "WellState", defaultWellState, "", "", "", "Well State" );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::expected<caf::PdmObjectHandle*, QString> RimcWellEventTimeline_addStateEvent::execute()
|
|
{
|
|
auto timeline = self<RimWellEventTimeline>();
|
|
|
|
if ( !m_wellPath() )
|
|
{
|
|
return std::unexpected( QString( "Well path is required" ) );
|
|
}
|
|
|
|
QDateTime date = QDateTime::fromString( m_eventDate(), Qt::ISODate );
|
|
if ( !date.isValid() )
|
|
{
|
|
return std::unexpected( QString( "Invalid date format: %1. Expected YYYY-MM-DD" ).arg( m_eventDate() ) );
|
|
}
|
|
|
|
auto* event = timeline->addStateEvent( m_wellPath(), date );
|
|
event->setWellState( m_wellState() );
|
|
|
|
return event;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimcWellEventTimeline_addStateEvent::classKeywordReturnedType() const
|
|
{
|
|
return RimWellEventState::classKeywordStatic();
|
|
}
|
|
|
|
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellEventTimeline, RimcWellEventTimeline_addControlEvent, "AddControlEvent" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimcWellEventTimeline_addControlEvent::RimcWellEventTimeline_addControlEvent( caf::PdmObjectHandle* self )
|
|
: caf::PdmObjectCreationMethod( self )
|
|
{
|
|
CAF_PDM_InitObject( "Add Control Event", "", "", "Add a well control event to the timeline" );
|
|
|
|
CAF_PDM_InitScriptableField( &m_eventDate, "EventDate", QString( "2024-01-01" ), "", "", "", "Event Date (YYYY-MM-DD)" );
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_wellPath, "WellPath", "", "", "", "Well Path" );
|
|
auto defaultControlMode = RimWellEventControl::ControlMode::ORAT;
|
|
CAF_PDM_InitScriptableField( &m_controlMode, "ControlMode", defaultControlMode, "", "", "", "Control Mode" );
|
|
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_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_isProducer, "IsProducer", true, "", "", "", "Is Producer" );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::expected<caf::PdmObjectHandle*, QString> RimcWellEventTimeline_addControlEvent::execute()
|
|
{
|
|
auto timeline = self<RimWellEventTimeline>();
|
|
|
|
if ( !m_wellPath() )
|
|
{
|
|
return std::unexpected( QString( "Well path is required" ) );
|
|
}
|
|
|
|
QDateTime date = QDateTime::fromString( m_eventDate(), Qt::ISODate );
|
|
if ( !date.isValid() )
|
|
{
|
|
return std::unexpected( QString( "Invalid date format: %1. Expected YYYY-MM-DD" ).arg( m_eventDate() ) );
|
|
}
|
|
|
|
auto* event = timeline->addControlEvent( m_wellPath(), date );
|
|
event->setControlValue( m_controlValue() );
|
|
event->setBhpLimit( m_bhpLimit() );
|
|
event->setOilRate( m_oilRate() );
|
|
event->setWaterRate( m_waterRate() );
|
|
event->setGasRate( m_gasRate() );
|
|
event->setIsProducer( m_isProducer() );
|
|
event->setControlMode( m_controlMode() );
|
|
|
|
return event;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimcWellEventTimeline_addControlEvent::classKeywordReturnedType() const
|
|
{
|
|
return RimWellEventControl::classKeywordStatic();
|
|
}
|
|
|
|
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellEventTimeline, RimcWellEventTimeline_addTubingEvent, "AddTubingEvent" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimcWellEventTimeline_addTubingEvent::RimcWellEventTimeline_addTubingEvent( caf::PdmObjectHandle* self )
|
|
: caf::PdmObjectCreationMethod( self )
|
|
{
|
|
CAF_PDM_InitObject( "Add Tubing Event", "", "", "Add a tubing event to the timeline" );
|
|
|
|
CAF_PDM_InitScriptableField( &m_eventDate, "EventDate", QString( "2024-01-01" ), "", "", "", "Event Date (YYYY-MM-DD)" );
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_wellPath, "WellPath", "", "", "", "Well Path" );
|
|
CAF_PDM_InitScriptableField( &m_startMd, "StartMd", 0.0, "", "", "", "Start Measured Depth" );
|
|
CAF_PDM_InitScriptableField( &m_endMd, "EndMd", 0.0, "", "", "", "End Measured Depth" );
|
|
CAF_PDM_InitScriptableField( &m_innerDiameter, "InnerDiameter", 0.15, "", "", "", "Inner Diameter [m]" );
|
|
CAF_PDM_InitScriptableField( &m_roughness, "Roughness", 1.0e-5, "", "", "", "Roughness [m]" );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::expected<caf::PdmObjectHandle*, QString> RimcWellEventTimeline_addTubingEvent::execute()
|
|
{
|
|
auto timeline = self<RimWellEventTimeline>();
|
|
|
|
if ( !m_wellPath() )
|
|
{
|
|
return std::unexpected( QString( "Well path is required" ) );
|
|
}
|
|
|
|
QDateTime date = QDateTime::fromString( m_eventDate(), Qt::ISODate );
|
|
if ( !date.isValid() )
|
|
{
|
|
return std::unexpected( QString( "Invalid date format: %1. Expected YYYY-MM-DD" ).arg( m_eventDate() ) );
|
|
}
|
|
|
|
auto* event = timeline->addTubingEvent( m_wellPath(), date );
|
|
event->setStartMD( m_startMd() );
|
|
event->setEndMD( m_endMd() );
|
|
event->setInnerDiameter( m_innerDiameter() );
|
|
event->setRoughness( m_roughness() );
|
|
|
|
return event;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimcWellEventTimeline_addTubingEvent::classKeywordReturnedType() const
|
|
{
|
|
return RimWellEventTubing::classKeywordStatic();
|
|
}
|
|
|
|
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellEventTimeline, RimcWellEventTimeline_addWellSpecEvent, "AddWellspecEvent" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimcWellEventTimeline_addWellSpecEvent::RimcWellEventTimeline_addWellSpecEvent( caf::PdmObjectHandle* self )
|
|
: caf::PdmObjectCreationMethod( self )
|
|
{
|
|
CAF_PDM_InitObject( "Add Well Specification Event", "", "", "Add a WELLSPEC event to the timeline" );
|
|
|
|
CAF_PDM_InitScriptableField( &m_eventDate, "EventDate", QString( "2024-01-01" ), "", "", "", "Event Date (YYYY-MM-DD)" );
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_wellPath, "WellPath", "", "", "", "Well Path" );
|
|
CAF_PDM_InitScriptableField( &m_groupName, "GroupName", QString(), "", "", "", "Group Name" );
|
|
CAF_PDM_InitScriptableField( &m_allowCrossFlow, "AllowCrossFlow", true, "", "", "", "Allow Well Cross-Flow" );
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_referenceDepth, "ReferenceDepth", "", "", "", "Reference Depth" );
|
|
CAF_PDM_InitScriptableField( &m_wellType,
|
|
"WellType",
|
|
caf::AppEnum<RimWellPathCompletionSettings::WellType>( RimWellPathCompletionSettings::OIL ),
|
|
"",
|
|
"",
|
|
"",
|
|
"Preferred Fluid Phase" );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::expected<caf::PdmObjectHandle*, QString> RimcWellEventTimeline_addWellSpecEvent::execute()
|
|
{
|
|
auto timeline = self<RimWellEventTimeline>();
|
|
if ( !m_wellPath() ) return std::unexpected( QString( "Well path is required" ) );
|
|
|
|
QDateTime date = QDateTime::fromString( m_eventDate(), Qt::ISODate );
|
|
if ( !date.isValid() )
|
|
{
|
|
return std::unexpected( QString( "Invalid date format: %1. Expected YYYY-MM-DD" ).arg( m_eventDate() ) );
|
|
}
|
|
|
|
for ( const auto* existing : timeline->events() )
|
|
{
|
|
if ( existing->eventType() == RimWellEvent::EventType::WELLSPEC && existing->wellPath() == m_wellPath() && existing->eventDate() == date )
|
|
{
|
|
return std::unexpected(
|
|
QString( "A WELLSPEC event already exists for well '%1' at %2" ).arg( m_wellPath()->name(), m_eventDate() ) );
|
|
}
|
|
}
|
|
|
|
auto* event = timeline->addWellSpecEvent( m_wellPath(), date );
|
|
event->setWellSpecData( { m_groupName(), m_allowCrossFlow(), m_referenceDepth(), m_wellType() } );
|
|
return event;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimcWellEventTimeline_addWellSpecEvent::classKeywordReturnedType() const
|
|
{
|
|
return RimWellEventWellSpec::classKeywordStatic();
|
|
}
|
|
|
|
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellEventTimeline, RimcWellEventTimeline_addWellKeywordEvent, "AddWellKeywordEventInternal" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimcWellEventTimeline_addWellKeywordEvent::RimcWellEventTimeline_addWellKeywordEvent( caf::PdmObjectHandle* self )
|
|
: caf::PdmObjectCreationMethod( self )
|
|
{
|
|
CAF_PDM_InitObject( "Add Well Keyword Event", "", "", "Add a well keyword event to the timeline" );
|
|
|
|
CAF_PDM_InitScriptableField( &m_eventDate, "EventDate", QString( "2024-01-01" ), "", "", "", "Event Date (YYYY-MM-DD)" );
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_wellPath, "WellPath", "", "", "", "Well Path" );
|
|
CAF_PDM_InitScriptableField( &m_keywordName, "KeywordName", QString(), "", "", "", "Keyword Name" );
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_itemNames, "ItemNames", "", "", "", "Item Names" );
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_itemTypes, "ItemTypes", "", "", "", "Item Types" );
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_itemValues, "ItemValues", "", "", "", "Item Values" );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::expected<caf::PdmObjectHandle*, QString> RimcWellEventTimeline_addWellKeywordEvent::execute()
|
|
{
|
|
auto timeline = self<RimWellEventTimeline>();
|
|
|
|
if ( !m_wellPath() )
|
|
{
|
|
return std::unexpected( QString( "Well path is required" ) );
|
|
}
|
|
|
|
if ( m_keywordName().isEmpty() )
|
|
{
|
|
return std::unexpected( QString( "Keyword name is required" ) );
|
|
}
|
|
|
|
QDateTime date = QDateTime::fromString( m_eventDate(), Qt::ISODate );
|
|
if ( !date.isValid() )
|
|
{
|
|
return std::unexpected( QString( "Invalid date format: %1. Expected YYYY-MM-DD" ).arg( m_eventDate() ) );
|
|
}
|
|
|
|
// Validate array lengths
|
|
if ( m_itemNames().size() != m_itemTypes().size() || m_itemNames().size() != m_itemValues().size() )
|
|
{
|
|
return std::unexpected( QString( "Item arrays must have same length" ) );
|
|
}
|
|
|
|
auto validationResult = validateKeywordItems( m_keywordName(), m_itemNames() );
|
|
if ( !validationResult )
|
|
{
|
|
return std::unexpected( validationResult.error() );
|
|
}
|
|
|
|
// Create event
|
|
auto* event = timeline->addWellKeywordEvent( m_wellPath(), date, m_keywordName() );
|
|
|
|
// Add items with type conversion
|
|
for ( size_t i = 0; i < m_itemNames().size(); ++i )
|
|
{
|
|
QString type = m_itemTypes()[i].toUpper();
|
|
QString name = m_itemNames()[i];
|
|
QString value = m_itemValues()[i];
|
|
|
|
if ( type == "STRING" )
|
|
{
|
|
event->addStringItem( name, value );
|
|
}
|
|
else if ( type == "INT" )
|
|
{
|
|
bool ok;
|
|
int intValue = value.toInt( &ok );
|
|
if ( !ok )
|
|
{
|
|
return std::unexpected( QString( "Invalid integer value for '%1': %2" ).arg( name ).arg( value ) );
|
|
}
|
|
event->addIntItem( name, intValue );
|
|
}
|
|
else if ( type == "DOUBLE" )
|
|
{
|
|
bool ok;
|
|
double doubleValue = value.toDouble( &ok );
|
|
if ( !ok )
|
|
{
|
|
return std::unexpected( QString( "Invalid double value for '%1': %2" ).arg( name ).arg( value ) );
|
|
}
|
|
event->addDoubleItem( name, doubleValue );
|
|
}
|
|
else if ( type == "FLAG" )
|
|
{
|
|
event->addFlagItem( name );
|
|
}
|
|
else
|
|
{
|
|
return std::unexpected( QString( "Unknown item type: %1. Must be STRING, INT, DOUBLE, or FLAG" ).arg( type ) );
|
|
}
|
|
}
|
|
|
|
return event;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimcWellEventTimeline_addWellKeywordEvent::classKeywordReturnedType() const
|
|
{
|
|
return RimWellEventKeyword::classKeywordStatic();
|
|
}
|
|
|
|
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellEventTimeline, RimcWellEventTimeline_addKeywordEvent, "AddKeywordEventInternal" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimcWellEventTimeline_addKeywordEvent::RimcWellEventTimeline_addKeywordEvent( caf::PdmObjectHandle* self )
|
|
: caf::PdmObjectCreationMethod( self )
|
|
{
|
|
CAF_PDM_InitObject( "Add Keyword Event", "", "", "Add a schedule-level keyword event to the timeline (not tied to a well)" );
|
|
|
|
CAF_PDM_InitScriptableField( &m_eventDate, "EventDate", QString( "2024-01-01" ), "", "", "", "Event Date (YYYY-MM-DD)" );
|
|
CAF_PDM_InitScriptableField( &m_keywordName, "KeywordName", QString(), "", "", "", "Keyword Name" );
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_itemNames, "ItemNames", "", "", "", "Item Names" );
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_itemTypes, "ItemTypes", "", "", "", "Item Types" );
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_itemValues, "ItemValues", "", "", "", "Item Values" );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::expected<caf::PdmObjectHandle*, QString> RimcWellEventTimeline_addKeywordEvent::execute()
|
|
{
|
|
auto timeline = self<RimWellEventTimeline>();
|
|
|
|
if ( m_keywordName().isEmpty() )
|
|
{
|
|
return std::unexpected( QString( "Keyword name is required" ) );
|
|
}
|
|
|
|
QDateTime date = QDateTime::fromString( m_eventDate(), Qt::ISODate );
|
|
if ( !date.isValid() )
|
|
{
|
|
return std::unexpected( QString( "Invalid date format: %1. Expected YYYY-MM-DD" ).arg( m_eventDate() ) );
|
|
}
|
|
|
|
// Validate array lengths
|
|
if ( m_itemNames().size() != m_itemTypes().size() || m_itemNames().size() != m_itemValues().size() )
|
|
{
|
|
return std::unexpected( QString( "Item arrays must have same length" ) );
|
|
}
|
|
|
|
auto validationResult = validateKeywordItems( m_keywordName(), m_itemNames() );
|
|
if ( !validationResult )
|
|
{
|
|
return std::unexpected( validationResult.error() );
|
|
}
|
|
|
|
// Create event (note: no wellPath parameter)
|
|
auto* event = timeline->addKeywordEvent( date, m_keywordName() );
|
|
|
|
// Add items with type conversion
|
|
for ( size_t i = 0; i < m_itemNames().size(); ++i )
|
|
{
|
|
QString type = m_itemTypes()[i].toUpper();
|
|
QString name = m_itemNames()[i];
|
|
QString value = m_itemValues()[i];
|
|
|
|
if ( type == "STRING" )
|
|
{
|
|
event->addStringItem( name, value );
|
|
}
|
|
else if ( type == "INT" )
|
|
{
|
|
bool ok;
|
|
int intValue = value.toInt( &ok );
|
|
if ( !ok )
|
|
{
|
|
return std::unexpected( QString( "Invalid integer value for '%1': %2" ).arg( name ).arg( value ) );
|
|
}
|
|
event->addIntItem( name, intValue );
|
|
}
|
|
else if ( type == "DOUBLE" )
|
|
{
|
|
bool ok;
|
|
double doubleValue = value.toDouble( &ok );
|
|
if ( !ok )
|
|
{
|
|
return std::unexpected( QString( "Invalid double value for '%1': %2" ).arg( name ).arg( value ) );
|
|
}
|
|
event->addDoubleItem( name, doubleValue );
|
|
}
|
|
else if ( type == "FLAG" )
|
|
{
|
|
event->addFlagItem( name );
|
|
}
|
|
else
|
|
{
|
|
return std::unexpected( QString( "Unknown item type: %1. Must be STRING, INT, DOUBLE, or FLAG" ).arg( type ) );
|
|
}
|
|
}
|
|
|
|
return event;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimcWellEventTimeline_addKeywordEvent::classKeywordReturnedType() const
|
|
{
|
|
return RimKeywordEvent::classKeywordStatic();
|
|
}
|
|
|
|
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellEventTimeline, RimcWellEventTimeline_addRawTextEvent, "AddRawTextEventInternal" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimcWellEventTimeline_addRawTextEvent::RimcWellEventTimeline_addRawTextEvent( caf::PdmObjectHandle* self )
|
|
: caf::PdmObjectCreationMethod( self )
|
|
{
|
|
CAF_PDM_InitObject( "Add Raw Text Event", "", "", "Add raw text at a specific position in a dated schedule section" );
|
|
|
|
CAF_PDM_InitScriptableField( &m_eventDate, "EventDate", QString( "2024-01-01" ), "", "", "", "Event Date (YYYY-MM-DD)" );
|
|
CAF_PDM_InitScriptableField( &m_text, "Text", QString(), "", "", "", "Raw schedule text" );
|
|
CAF_PDM_InitScriptableField( &m_placement,
|
|
"Placement",
|
|
RimWellEventRawText::Placement::AFTER_DATE,
|
|
"",
|
|
"",
|
|
"",
|
|
"Position in the dated schedule section" );
|
|
CAF_PDM_InitScriptableField( &m_anchorKeyword, "AnchorKeyword", QString(), "", "", "", "Keyword used for before/after placement" );
|
|
CAF_PDM_InitScriptableField( &m_priority, "Priority", 0, "", "", "", "Ascending order among raw text events at the same position" );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::expected<caf::PdmObjectHandle*, QString> RimcWellEventTimeline_addRawTextEvent::execute()
|
|
{
|
|
auto timeline = self<RimWellEventTimeline>();
|
|
|
|
QDateTime date = QDateTime::fromString( m_eventDate(), Qt::ISODate );
|
|
if ( !date.isValid() )
|
|
{
|
|
return std::unexpected( QString( "Invalid date format: %1. Expected YYYY-MM-DD" ).arg( m_eventDate() ) );
|
|
}
|
|
if ( m_text().isEmpty() )
|
|
{
|
|
return std::unexpected( QString( "Raw text is required" ) );
|
|
}
|
|
|
|
const bool anchored = m_placement() == RimWellEventRawText::Placement::BEFORE_KEYWORD ||
|
|
m_placement() == RimWellEventRawText::Placement::AFTER_KEYWORD;
|
|
if ( anchored && m_anchorKeyword().trimmed().isEmpty() )
|
|
{
|
|
return std::unexpected( QString( "Anchor keyword is required for BEFORE_KEYWORD and AFTER_KEYWORD placement" ) );
|
|
}
|
|
if ( !anchored && !m_anchorKeyword().trimmed().isEmpty() )
|
|
{
|
|
return std::unexpected( QString( "Anchor keyword is only valid for BEFORE_KEYWORD and AFTER_KEYWORD placement" ) );
|
|
}
|
|
|
|
auto* event = timeline->addRawTextEvent( date );
|
|
event->setText( m_text() );
|
|
event->setPlacement( m_placement() );
|
|
event->setAnchorKeyword( m_anchorKeyword().trimmed() );
|
|
event->setPriority( m_priority() );
|
|
return event;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimcWellEventTimeline_addRawTextEvent::classKeywordReturnedType() const
|
|
{
|
|
return RimWellEventRawText::classKeywordStatic();
|
|
}
|
|
|
|
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellEventTimeline, RimcWellEventTimeline_setTimestamp, "SetTimestamp" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimcWellEventTimeline_setTimestamp::RimcWellEventTimeline_setTimestamp( caf::PdmObjectHandle* self )
|
|
: caf::PdmVoidObjectMethod( self )
|
|
{
|
|
CAF_PDM_InitObject( "Set Timestamp", "", "", "Apply well events up to a given timestamp" );
|
|
|
|
CAF_PDM_InitScriptableField( &m_timestamp, "Timestamp", QString( "2024-01-01" ), "", "", "", "Timestamp (YYYY-MM-DD)" );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::expected<caf::PdmObjectHandle*, QString> RimcWellEventTimeline_setTimestamp::execute()
|
|
{
|
|
auto timeline = self<RimWellEventTimeline>();
|
|
|
|
QDateTime date = QDateTime::fromString( m_timestamp(), Qt::ISODate );
|
|
if ( !date.isValid() )
|
|
{
|
|
return std::unexpected( QString( "Invalid date format: %1. Expected YYYY-MM-DD" ).arg( m_timestamp() ) );
|
|
}
|
|
|
|
bool success = timeline->applyEventsUpToDate( date );
|
|
|
|
if ( !success )
|
|
{
|
|
return std::unexpected( QString( "No events were applied up to date: %1" ).arg( m_timestamp() ) );
|
|
}
|
|
|
|
return nullptr; // Return nullptr on success (no specific object to return)
|
|
}
|
|
|
|
CAF_PDM_OBJECT_METHOD_SOURCE_INIT( RimWellEventTimeline, RimcWellEventTimeline_generateSchedule, "GenerateSchedule" );
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
RimcWellEventTimeline_generateSchedule::RimcWellEventTimeline_generateSchedule( caf::PdmObjectHandle* self )
|
|
: caf::PdmObjectMethod( self, PdmObjectMethod::NullPointerType::NULL_IS_INVALID, PdmObjectMethod::ResultType::PERSISTENT_FALSE )
|
|
{
|
|
CAF_PDM_InitObject( "Generate Schedule", "", "", "Generate Eclipse schedule text for all wells in the collection" );
|
|
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_eclipseCase, "EclipseCase", "", "", "", "Eclipse Case" );
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_exportMswForWells,
|
|
"ExportMswForWells",
|
|
"",
|
|
"",
|
|
"",
|
|
"Wells for which multi-segment-well keywords (WELSEGS, COMPSEGS, WSEGVALV, WSEGAICD) are "
|
|
"exported" );
|
|
CAF_PDM_InitScriptableField( &m_firstDateAsComment,
|
|
"FirstDateAsComment",
|
|
true,
|
|
"",
|
|
"",
|
|
"",
|
|
"Emit the first (simulation-start) date as a comment instead of a DATES keyword" );
|
|
CAF_PDM_InitScriptableField( &m_alignColumns,
|
|
"AlignColumns",
|
|
false,
|
|
"",
|
|
"",
|
|
"",
|
|
"Emit a column-header comment and right-aligned, fixed-width columns instead of the compact form" );
|
|
CAF_PDM_InitScriptableFieldNoDefault( &m_additionalDates,
|
|
"AdditionalDates",
|
|
"",
|
|
"",
|
|
"",
|
|
"Additional dates (YYYY-MM-DD or full ISO timestamp) emitted as DATES keywords, e.g. to "
|
|
"force summary reports at those dates" );
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
std::expected<caf::PdmObjectHandle*, QString> RimcWellEventTimeline_generateSchedule::execute()
|
|
{
|
|
auto timeline = self<RimWellEventTimeline>();
|
|
|
|
RimEclipseCase* eclipseCase = m_eclipseCase();
|
|
if ( !eclipseCase )
|
|
{
|
|
return std::unexpected( QString( "Eclipse case is required" ) );
|
|
}
|
|
|
|
// Get the last applied timestamp (from set_timestamp call)
|
|
QDateTime lastTimestamp = timeline->lastAppliedTimestamp();
|
|
|
|
// If no timestamp was set, use all events
|
|
std::vector<QDateTime> dates;
|
|
std::vector<RimWellPath*> wellPathsWithEvents;
|
|
|
|
if ( lastTimestamp.isValid() )
|
|
{
|
|
// Filter to only include dates up to the last applied timestamp
|
|
dates = timeline->getAllEventDates();
|
|
dates.erase( std::remove_if( dates.begin(), dates.end(), [&lastTimestamp]( const QDateTime& date ) { return date > lastTimestamp; } ),
|
|
dates.end() );
|
|
|
|
// Get only well paths that have events up to the last applied timestamp
|
|
wellPathsWithEvents = timeline->getWellPathsWithEventsUpToDate( lastTimestamp );
|
|
}
|
|
else
|
|
{
|
|
// No timestamp set - include all events and wells
|
|
dates = timeline->getAllEventDates();
|
|
wellPathsWithEvents = timeline->getWellPathsWithEvents();
|
|
}
|
|
|
|
// Merge in user-specified additional dates: each becomes a DATES keyword even when no events
|
|
// fall on it (e.g. to force a summary report). They are deliberately not filtered by the last
|
|
// applied timestamp.
|
|
if ( !m_additionalDates().empty() )
|
|
{
|
|
std::set<QDateTime> mergedDates( dates.begin(), dates.end() );
|
|
for ( const QString& dateString : m_additionalDates() )
|
|
{
|
|
QDateTime additionalDate = QDateTime::fromString( dateString, Qt::ISODate );
|
|
if ( !additionalDate.isValid() )
|
|
{
|
|
return std::unexpected( QString( "Invalid date format: %1. Expected YYYY-MM-DD" ).arg( dateString ) );
|
|
}
|
|
mergedDates.insert( additionalDate );
|
|
}
|
|
dates.assign( mergedDates.begin(), mergedDates.end() );
|
|
}
|
|
|
|
if ( dates.empty() )
|
|
{
|
|
return std::unexpected( QString( "No events or additional dates found in timeline" ) );
|
|
}
|
|
|
|
std::vector<RimWellPath*> mswWellPaths = m_exportMswForWells.ptrReferencedObjectsByType();
|
|
std::set<const RimWellPath*> mswWells( mswWellPaths.begin(), mswWellPaths.end() );
|
|
|
|
auto scheduleText = RicScheduleDataGenerator::generateSchedule( *timeline,
|
|
*eclipseCase,
|
|
wellPathsWithEvents,
|
|
dates,
|
|
mswWells,
|
|
m_firstDateAsComment(),
|
|
m_alignColumns() );
|
|
if ( !scheduleText ) return std::unexpected( scheduleText.error() );
|
|
|
|
// Return the schedule text in a data container
|
|
auto* dataObject = new RimcDataContainerString();
|
|
dataObject->m_stringValues = { *scheduleText };
|
|
|
|
return dataObject;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
///
|
|
//--------------------------------------------------------------------------------------------------
|
|
QString RimcWellEventTimeline_generateSchedule::classKeywordReturnedType() const
|
|
{
|
|
return RimcDataContainerString::classKeywordStatic();
|
|
}
|