#14612 Simulator Events: Remove Orion project naming

This commit is contained in:
Kristian Bendiksen committed 2026-09-09 13:01:26 +02:00
1 parent 1fc5e424e4
commit 71c5b5a97d
13 files changed
+620 -589

No files matched your search

@@ -33,7 +33,7 @@ const std::vector<RiaExperimentalFeatures::Feature>& RiaExperimentalFeatures::av
{ "osdu-well-logs", "OSDU Well Logs", "Enable import of well logs from OSDU." },
{ "undo-redo-view", "Undo/Redo View", "Show the command undo/redo history view." },
{ "oil-volume-result", "Oil Volume Result", "Compute the derived oil volume cell result." },
{ "orion-events-import", "Orion Events Import", "Enable import of Orion well events from file." },
{ "simulator-events-import", "Simulator Events Import", "Enable import of simulator events from file." },
{ "remember-dialog-size",
"Remember Dialog Size",
"Remember the size of property and preferences dialogs between sessions and restore it the next time they "
@@ -17,7 +17,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RicNewPolylineTargetFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicDeletePolylineTargetFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicImportWellMeasurementsFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicImportOrionEventsFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicImportSimulatorEventsFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicNewWellPathLateralAtDepthFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicNewWellPathLateralFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicPasteModeledWellPathFeature.cpp
@@ -16,7 +16,7 @@
//
/////////////////////////////////////////////////////////////////////////////////
#include "RicImportOrionEventsFeature.h"
#include "RicImportSimulatorEventsFeature.h"
#include "RiaApplication.h"
#include "RiaGuiApplication.h"
@@ -30,12 +30,12 @@
#include <QFileInfo>
#include <QMessageBox>
CAF_CMD_SOURCE_INIT( RicImportOrionEventsFeature, "RicImportOrionEventsFeature" );
CAF_CMD_SOURCE_INIT( RicImportSimulatorEventsFeature, "RicImportSimulatorEventsFeature" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicImportOrionEventsFeature::onActionTriggered( bool isChecked )
void RicImportSimulatorEventsFeature::onActionTriggered( bool isChecked )
{
// The import runs as a Python child process which applies the events to this instance through
// gRPC, so both the script server and a Python interpreter must be available.
@@ -43,7 +43,7 @@ void RicImportOrionEventsFeature::onActionTriggered( bool isChecked )
if ( !app->activeGrpcPortNumber() )
{
QMessageBox::warning( Riu3DMainWindowTools::mainWindowWidget(),
"Import Orion Events",
"Import Simulator Events",
"The Python script server is not running. Enable it in Preferences -> Scripting and restart ResInsight." );
return;
}
@@ -51,19 +51,19 @@ void RicImportOrionEventsFeature::onActionTriggered( bool isChecked )
if ( app->pythonPath().isEmpty() )
{
QMessageBox::warning( Riu3DMainWindowTools::mainWindowWidget(),
"Import Orion Events",
"Import Simulator Events",
"No Python executable is configured in Preferences -> Scripting." );
return;
}
QString defaultDir = app->lastUsedDialogDirectory( "ORION_EVENTS_DIR" );
QString defaultDir = app->lastUsedDialogDirectory( "SIMULATOR_EVENTS_DIR" );
QString fileName = RiuFileDialogTools::getOpenFileName( Riu3DMainWindowTools::mainWindowWidget(),
"Import Orion Events",
"Import Simulator Events",
defaultDir,
"Orion Events Files (*.orion);;All Files (*.*)" );
"Simulator Events Files (*.events);;All Files (*.*)" );
if ( fileName.isEmpty() ) return;
app->setLastUsedDialogDirectory( "ORION_EVENTS_DIR", QFileInfo( fileName ).absolutePath() );
app->setLastUsedDialogDirectory( "SIMULATOR_EVENTS_DIR", QFileInfo( fileName ).absolutePath() );
// Show the process monitor so the report and any errors from the child process are visible.
RiuMainWindow* mainWindow = RiuMainWindow::instance();
@@ -75,7 +75,7 @@ void RicImportOrionEventsFeature::onActionTriggered( bool isChecked )
// Unbuffered output ("-u") so the report streams into the process monitor promptly. The child
// process finds this instance through the RESINSIGHT_GRPC_PORT environment variable.
QStringList arguments = { "-u", "-m", "rips.orion_events", "--apply", fileName };
QStringList arguments = { "-u", "-m", "rips.simulator_events", "--apply", fileName };
if ( !app->launchProcess( app->pythonPath(), arguments, app->pythonProcessEnvironment() ) )
{
RiaLogging::error( "Failed to launch the Python interpreter. Another script may already be running." );
@@ -85,8 +85,8 @@ void RicImportOrionEventsFeature::onActionTriggered( bool isChecked )
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicImportOrionEventsFeature::setupActionLook( QAction* actionToSetup )
void RicImportSimulatorEventsFeature::setupActionLook( QAction* actionToSetup )
{
actionToSetup->setText( "Import Orion Events" );
actionToSetup->setText( "Import Simulator Events" );
actionToSetup->setIcon( QIcon( ":/Well.svg" ) );
}
@@ -23,7 +23,7 @@
//==================================================================================================
///
//==================================================================================================
class RicImportOrionEventsFeature : public caf::CmdFeature
class RicImportSimulatorEventsFeature : public caf::CmdFeature
{
CAF_CMD_HEADER_INIT;
@@ -351,9 +351,9 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection()
menuBuilder.addSeparator();
menuBuilder << "RicWellPathImportPerforationIntervalsFeature";
menuBuilder << "RicImportWellMeasurementsFeature";
if ( RiaPreferencesSystem::current()->isFeatureEnabled( "orion-events-import" ) )
if ( RiaPreferencesSystem::current()->isFeatureEnabled( "simulator-events-import" ) )
{
menuBuilder << "RicImportOrionEventsFeature";
menuBuilder << "RicImportSimulatorEventsFeature";
}
menuBuilder.subMenuEnd();
menuBuilder.addSeparator();
@@ -766,11 +766,11 @@ TEST( RifEventKeywordFormatterTest, BuildKeywordOverridesWellName )
wellItem.setItemName( "WELL" );
wellItem.setStringValue( "WellPathA" );
auto keyword = RifEventKeywordFormatter::buildKeyword( "WCONHIST", { &wellItem }, QString( "ORION_EXPORT_ALIAS" ) );
auto keyword = RifEventKeywordFormatter::buildKeyword( "WCONHIST", { &wellItem }, QString( "SIMULATOR_EVENTS_EXPORT_ALIAS" ) );
ASSERT_TRUE( keyword.has_value() );
ASSERT_EQ( keyword->size(), 1 );
EXPECT_EQ( keyword->getRecord( 0 ).getItem( "WELL" ).getTrimmedString( 0 ), "ORION_EXPORT_ALIAS" );
EXPECT_EQ( keyword->getRecord( 0 ).getItem( "WELL" ).getTrimmedString( 0 ), "SIMULATOR_EVENTS_EXPORT_ALIAS" );
}
//--------------------------------------------------------------------------------------------------
@@ -140,9 +140,9 @@ void RiuMenuBarBuildTools::addImportMenuForMainWindow( QObject* parent, QMenu* m
importWellMenu->addAction( cmdFeatureMgr->action( "RicWellLogsImportFileFeature" ) );
importWellMenu->addAction( cmdFeatureMgr->action( "RicWellPathFormationsImportFileFeature" ) );
importWellMenu->addAction( cmdFeatureMgr->action( "RicImportWellMeasurementsFeature" ) );
if ( RiaPreferencesSystem::current()->isFeatureEnabled( "orion-events-import" ) )
if ( RiaPreferencesSystem::current()->isFeatureEnabled( "simulator-events-import" ) )
{
importWellMenu->addAction( cmdFeatureMgr->action( "RicImportOrionEventsFeature" ) );
importWellMenu->addAction( cmdFeatureMgr->action( "RicImportSimulatorEventsFeature" ) );
}
importMenu->addSeparator();
@@ -1,10 +1,10 @@
#!/usr/bin/env python3
"""
Example: import an ORIONEVENTS well-event-timeline file into ResInsight.
Example: import a SIMEVENTS well-event-timeline file into ResInsight.
This example shows how to:
1. Parse an ORIONEVENTS text file into a structured document
1. Parse a SIMEVENTS text file into a structured document
2. Normalize matching keyword events while retaining same-date perforations
3. Apply its events to the well event timeline (perforations, WCONHIST, WELTARG),
materializing FILTER declarations as case-level combined data filters
@@ -12,9 +12,9 @@ This example shows how to:
4. Insert multiline RAW_TEXT at a selected position in the generated schedule
5. Generate Eclipse schedule text from the resulting timeline
The ORIONEVENTS format is a compact, human-authored description of dated well
events. See rips/orion_events.py for the grammar. A sample input file ships at
rips/example_input_files/well_events.orion.
The SIMEVENTS format is a compact, human-authored description of dated well
events. See rips/simulator_events.py for the grammar. A sample input file ships at
rips/example_input_files/simulator_events.events.
The well names in the file ("55_33-A-1", ...) must match well paths that exist
in the open project, so this example assumes a project with matching wells and
@@ -26,22 +26,22 @@ result raises before any event is applied.
import os
import rips
import rips.orion_events
import rips.simulator_events
def main():
resinsight = rips.Instance.find()
project = resinsight.project
print("Import ORIONEVENTS Example")
print("Import SIMEVENTS Example")
print("=" * 50)
# Locate the sample ORIONEVENTS file shipped alongside the rips package.
orion_file = os.path.join(
os.path.dirname(rips.__file__), "example_input_files", "well_events.orion"
# Locate the sample SIMEVENTS file shipped alongside the rips package.
simulator_events_file = os.path.join(
os.path.dirname(rips.__file__), "example_input_files", "simulator_events.events"
)
print(f"\n1. Parsing: {orion_file}")
document = rips.orion_events.parse_orion_events_file(orion_file)
print(f"\n1. Parsing: {simulator_events_file}")
document = rips.simulator_events.parse_simulator_events_file(simulator_events_file)
print(f" Version: {document.version}, units: {document.unit_system}")
print(f" Wells: {[w.well_name for w in document.wells]}")
print(
@@ -51,7 +51,7 @@ def main():
# Normalization merges matching keyword events, but events that create or
# expand domain objects remain separate. The sample has three perforations
# at A1_STARTUP; all three are retained.
normalized = rips.orion_events.coalesce_orion_document(document)
normalized = rips.simulator_events.coalesce_simulator_events_document(document)
source_perforation_count = sum(
event.event_type == "PERFORATION"
for well in document.wells
@@ -94,7 +94,7 @@ def main():
well_path_coll = project.descendants(rips.WellPathCollection)[0]
timeline = well_path_coll.event_timeline()
report = rips.orion_events.apply_orion_document(
report = rips.simulator_events.apply_simulator_events_document(
document, timeline, project, case=case, on_unknown_well="warn"
)
print(f" Events applied: {report.events_applied}")
@@ -1,12 +1,12 @@
#!/usr/bin/env python3
"""
Example: the well_event_schedule.py timeline expressed as an ORIONEVENTS file.
Example: the well_event_schedule.py timeline expressed as a SIMEVENTS file.
This is the ORIONEVENTS counterpart to well_event_schedule.py: instead of
This is the SIMEVENTS counterpart to well_event_schedule.py: instead of
calling the WellEventTimeline API methods one by one, the same events are
written as ORIONEVENTS 2.0 text (see rips/orion_events.py for the grammar) and
applied in one go with rips.orion_events.apply_orion_document().
written as SIMEVENTS 1.0 text (see rips/simulator_events.py for the grammar) and
applied in one go with rips.simulator_events.apply_simulator_events_document().
It demonstrates the full event coverage of the format:
1. SEGMENT, PERFORATION (incl. a time-of-day date), VALVE and STATE completion
@@ -29,7 +29,7 @@ It demonstrates the full event coverage of the format:
generate_schedule_text(additional_dates=...) as summary-report triggers
11. Schedule metadata, COMPORD generation and aligned-column output
The ORIONEVENTS text is built inline with the name of the first well path in
The SIMEVENTS text is built inline with the name of the first well path in
the project (like well_event_schedule.py, which uses wells[0]), so the example
works with any project that has at least one well path. Applying a FILTER needs
a loaded Eclipse case (to resolve the result name and own the created filter),
@@ -37,10 +37,10 @@ so the FILTER parts are included only when the project has a case.
"""
import rips
import rips.orion_events
import rips.simulator_events
def build_orion_text(well_name, with_filter):
def build_simulator_events_text(well_name, with_filter):
# 'static.PORO' restricts the result lookup to STATIC_NATIVE results; an
# unqualified name would search STATIC_NATIVE, DYNAMIC_NATIVE, GENERATED.
filter_decl = 'FILTER HIPORO = "static.PORO > 0.15"\n' if with_filter else ""
@@ -51,7 +51,7 @@ def build_orion_text(well_name, with_filter):
)
filter_ref = " FILTER=HIPORO" if with_filter else ""
return f"""\
ORIONEVENTS 2.0
SIMEVENTS 1.0
UNIT METRIC
# Typed declarations
@@ -63,7 +63,7 @@ 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="ORION_GROUP" CROSSFLOW=True REFDEPTH=1002 PHASE=WATER
2024-01-05 WELSPECS GROUP="SIMULATOR_EVENTS_GROUP" CROSSFLOW=True REFDEPTH=1002 PHASE=WATER
2024-04-15 WELSPECS CROSSFLOW=False REFDEPTH=1000 PHASE=OIL
# COMMENT is stored on the event and safely emitted as a schedule comment.
@@ -110,7 +110,7 @@ SCHEDULE
STARTUP RAW_TEXT PLACEMENT=AFTER_KEYWORD ANCHOR=RPTRST PRIORITY=10
-- Custom schedule text not modeled by the timeline API
WTRACER
'{well_name}' 'ORION_TRACER' 1.0 /
'{well_name}' 'SIMULATOR_EVENTS_TRACER' 1.0 /
/
END_RAW_TEXT
@@ -125,7 +125,7 @@ def main():
resinsight = rips.Instance.find()
project = resinsight.project
print("Well Event Schedule (ORIONEVENTS) Example")
print("Well Event Schedule (SIMEVENTS) Example")
print("=" * 50)
print("\n1. Finding well")
@@ -142,10 +142,12 @@ def main():
if case is None:
print(" No Eclipse case loaded - FILTER parts are left out.")
print("\n2. Parsing ORIONEVENTS text...")
orion_text = build_orion_text(well_path.name, with_filter=case is not None)
print(orion_text)
document = rips.orion_events.parse_orion_events(orion_text)
print("\n2. Parsing SIMEVENTS text...")
simulator_events_text = build_simulator_events_text(
well_path.name, with_filter=case is not None
)
print(simulator_events_text)
document = rips.simulator_events.parse_simulator_events(simulator_events_text)
print(f" Wells: {[w.well_name for w in document.wells]}")
source_well_event_count = sum(len(w.events) for w in document.wells)
source_perforation_count = sum(
@@ -153,7 +155,7 @@ def main():
for well in document.wells
for event in well.events
)
normalized = rips.orion_events.coalesce_orion_document(document)
normalized = rips.simulator_events.coalesce_simulator_events_document(document)
merged_well_event_count = sum(len(w.events) for w in normalized.wells)
merged_perforation_count = sum(
event.event_type == "PERFORATION"
@@ -172,7 +174,7 @@ def main():
print("\n3. Applying events to the timeline...")
well_path_coll = project.descendants(rips.WellPathCollection)[0]
timeline = well_path_coll.event_timeline()
report = rips.orion_events.apply_orion_document(
report = rips.simulator_events.apply_simulator_events_document(
document, timeline, project, case=case
)
print(f" Events applied: {report.events_applied}")
@@ -1,8 +1,8 @@
# Example ORIONEVENTS well-event-timeline file (version 2.0).
# Parsed by rips.orion_events; see that module's docstring for the grammar.
# Validate with: python3 -m rips.orion_events well_events.orion
# Example SIMEVENTS well-event-timeline file (version 1.0).
# Parsed by rips.simulator_events; see that module's docstring for the grammar.
# Validate with: python3 -m rips.simulator_events simulator_events.events
ORIONEVENTS 2.0
SIMEVENTS 1.0
UNIT METRIC
# Typed declarations: dates, whole-day durations, well-name aliases, and
@@ -32,7 +32,7 @@ SCHEDULE
A1_STARTUP + RAMP RAW_TEXT PLACEMENT=AFTER_DATE PRIORITY=10
-- Custom schedule text not modeled by the timeline API
WTRACER
'55_33-A-1' 'ORION_TRACER' 1.0 /
'55_33-A-1' 'SIMULATOR_EVENTS_TRACER' 1.0 /
/
END_RAW_TEXT
+17 -18
View File
@@ -1,28 +1,28 @@
# ORIONEVENTS File Format — Version 2.0
# SIMEVENTS File Format — Version 1.0
## Overview
ORIONEVENTS is a compact, human-authored text format describing dated well events (completions and Eclipse schedule keywords) over time. ResInsight reads it through the pure-Python parser in `GrpcInterface/Python/rips/orion_events.py` and applies the events to a project via the `WellEventTimeline` API, from which Eclipse schedule text (COMPDAT, WELSEGS, WCONHIST, ...) can be generated.
SIMEVENTS is a compact, human-authored text format describing dated well events (completions and Eclipse schedule keywords) over time. ResInsight reads it through the pure-Python parser in `GrpcInterface/Python/rips/simulator_events.py` and applies the events to a project via the `WellEventTimeline` API, from which Eclipse schedule text (COMPDAT, WELSEGS, WCONHIST, ...) can be generated.
The implementation has two independent layers:
| Layer | Entry points | Needs ResInsight? |
|---|---|---|
| A — parser | `rips.orion_events.parse_orion_events(text)` / `parse_orion_events_file(path)` | No |
| B — applier | `rips.orion_events.apply_orion_document(document, timeline, project)` / `apply_orion_events_file(path, timeline, project)` | Yes |
| A — parser | `rips.simulator_events.parse_simulator_events(text)` / `parse_simulator_events_file(path)` | No |
| B — applier | `rips.simulator_events.apply_simulator_events_document(document, timeline, project)` / `apply_simulator_events_file(path, timeline, project)` | Yes |
A standalone validator runs Layer A only:
```
python3 -m rips.orion_events myfile.orion
python3 -m rips.simulator_events myfile.events
```
Runnable examples: `rips/PythonExamples/experimental/import_orion_events.py` (reads the shipped sample `rips/example_input_files/well_events.orion`) and `well_event_schedule_orion.py` (full event coverage, generates schedule text).
Runnable examples: `rips/PythonExamples/experimental/import_simulator_events.py` (reads the shipped sample `rips/example_input_files/simulator_events.events`) and `well_event_schedule_simulator_events.py` (full event coverage, generates schedule text).
## Quick example
```
ORIONEVENTS 2.0
SIMEVENTS 1.0
UNIT METRIC
# Typed declarations
@@ -55,7 +55,7 @@ SCHEDULE # schedule-level keywords, not tied to a well
```
document = header , { statement } ;
header = "ORIONEVENTS" , "2.0" ; (* first meaningful line *)
header = "SIMEVENTS" , "1.0" ; (* first meaningful line *)
statement = unit_directive | declaration | report_line | well_block_open
| group_block_open | schedule_block_open | event_line ;
unit_directive = "UNIT" , ( "METRIC" | "FIELD" | "LAB" ) ;
@@ -100,7 +100,7 @@ The format is line-oriented. Every non-blank line is dispatched on its first tok
| First token | Meaning |
|---|---|
| `ORIONEVENTS` | Header with version; must be the first meaningful line, exactly once |
| `SIMEVENTS` | Header with version; must be the first meaningful line, exactly once |
| `UNIT` | Unit system: `METRIC`, `FIELD` or `LAB` (default `METRIC`) |
| `DATE` | Declare a typed date variable |
| `DURATION` | Declare a typed whole-day duration variable |
@@ -251,10 +251,10 @@ When applied, each used filter becomes a case-level **combined data filter** (un
## Diagnostics
The parser recovers per line and reports **all** errors in one pass. The raised `OrionParseError` carries one `ParseIssue` (message + source line) per problem, and unknown names get difflib-based "did you mean" suggestions:
The parser recovers per line and reports **all** errors in one pass. The raised `SimulatorEventsParseError` carries one `ParseIssue` (message + source line) per problem, and unknown names get difflib-based "did you mean" suggestions:
```
Line 3: SET is ORIONEVENTS 1.x syntax; declare a typed variable instead, e.g. DATE NAME = 2018-01-01
Line 3: SET is not supported; declare a typed variable instead, e.g. DATE NAME = 2018-01-01
Line 5: Malformed WELL line: 'WELL 55_33-A-2' (well names containing special characters must be double-quoted, e.g. WELL "55_33-A-1")
Line 6: Unknown variable 'A1_STRTUP'; did you mean 'A1_STARTUP'?
Line 7: Variable 'RAMP' is a DURATION (declared line 3) but a DATE is required here
@@ -263,8 +263,8 @@ Line 7: Variable 'RAMP' is a DURATION (declared line 3) but a DATE is required
The validator CLI prints these and exits non-zero:
```
$ python3 -m rips.orion_events well_events.orion
well_events.orion: OK (ORIONEVENTS 2.0, units METRIC)
$ python3 -m rips.simulator_events simulator_events.events
simulator_events.events: OK (SIMEVENTS 1.0, units METRIC)
4 variable(s), 2 well block(s), 8 well event(s), 0 schedule event(s)
```
@@ -279,8 +279,8 @@ project = resinsight.project
well_path_coll = project.descendants(rips.WellPathCollection)[0]
timeline = well_path_coll.event_timeline()
report = rips.orion_events.apply_orion_events_file(
"well_events.orion", timeline, project, case=case, on_unknown_well="warn"
report = rips.simulator_events.apply_simulator_events_file(
"simulator_events.events", timeline, project, case=case, on_unknown_well="warn"
)
print(report.events_applied, report.events_skipped, report.warnings, report.errors)
@@ -290,7 +290,7 @@ schedule_text = timeline.generate_schedule_text(
)
```
`apply_orion_document` / `apply_orion_events_file` accept an optional `case` — the `rips.Case` used to resolve filter result names and to own the created combined filters. It is only needed when the document uses `FILTER` and defaults to the project's first case; a `RipsError` is raised if a filter is used and no case is available, or if a filter references a result missing from the case (checked before any event is applied).
`apply_simulator_events_document` / `apply_simulator_events_file` accept an optional `case` — the `rips.Case` used to resolve filter result names and to own the created combined filters. It is only needed when the document uses `FILTER` and defaults to the project's first case; a `RipsError` is raised if a filter is used and no case is available, or if a filter references a result missing from the case (checked before any event is applied).
They also accept two policies, each `"warn"` (default), `"error"` or `"skip"`:
@@ -303,5 +303,4 @@ The returned `ApplyReport` carries `events_applied`, `events_skipped`, `warnings
## Version history
- **2.0** — current: typed `DATE`/`DURATION`/`WELL`/`FILTER` declarations, `WELL`/`SCHEDULE` blocks, signed day-offset chains, ISO datetimes, built-in completion events (including `SEGMENT` custom intervals and pressure components) plus generic keyword pass-through, perforation data filters, multi-error diagnostics, validator CLI.
- **1.x** — unsupported. Files using `SET` variables and single-quoted well names are rejected with a migration message pointing at this grammar.
- **1.0** — current: typed `DATE`/`DURATION`/`WELL`/`FILTER` declarations, `WELL`/`SCHEDULE` blocks, signed day-offset chains, ISO datetimes, built-in completion events (including `SEGMENT` custom intervals and pressure components) plus generic keyword pass-through, perforation data filters, multi-error diagnostics, validator CLI.