#14571 Orion Events: Expand Python usage examples

This commit is contained in:
Kristian Bendiksen
2026-08-24 13:26:19 +02:00
parent 41b7df5a20
commit 565d7b7819
3 changed files with 77 additions and 8 deletions
@@ -5,10 +5,12 @@ Example: import an ORIONEVENTS well-event-timeline file into ResInsight.
This example shows how to:
1. Parse an ORIONEVENTS text file into a structured document
2. Apply its events to the well event timeline (perforations, WCONHIST, WELTARG),
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
attached to the perforations
3. Generate Eclipse schedule text from the resulting timeline
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
@@ -46,6 +48,36 @@ def main():
f" Variables: { {k: f'{v.kind} {v.value}' for k, v in document.variables.items()} }"
)
# 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)
source_perforation_count = sum(
event.event_type == "PERFORATION"
for well in document.wells
for event in well.events
)
normalized_perforation_count = sum(
event.event_type == "PERFORATION"
for well in normalized.wells
for event in well.events
)
print(
" Perforations retained during normalization: "
f"{source_perforation_count} -> {normalized_perforation_count}"
)
# RAW_TEXT bodies bypass keyword parsing and formatting. Placement and an
# optional anchor control where each block appears in the generated schedule.
raw_text_events = [
event for event in document.schedule_events if event.event_type == "RAW_TEXT"
]
for event in raw_text_events:
print(
f" RAW_TEXT: {event.raw_placement} {event.raw_anchor or ''} "
f"(priority {event.raw_priority})"
)
# The sample file uses FILTER declarations, so a case is needed to resolve
# the referenced result names and to own the created combined filters.
cases = project.cases()
@@ -17,15 +17,16 @@ It demonstrates the full event coverage of the format:
materialized as a case-level combined data filter
4. COMMENT attributes preserved on timeline events and emitted before their
generated schedule keywords
5. Same-owner/type/date WCONHIST lines merged into one event, with later
attributes extending or overriding earlier attributes
5. Same-owner/type/date WCONHIST lines merged into one event, while same-date
perforations remain separate
6. Well keyword events: WCONHIST and WELTARG (with attribute translation) and
WRFTPLT (generic Eclipse well keyword pass-through)
7. A GROUP-level MEMBER event expanded to one GRUPTREE record per member
8. SCHEDULE-level keyword events not tied to a well: RPTRST, GRUPTREE, TUNING
9. REPORT dates, passed to generate_schedule_text(additional_dates=...) so
9. Multiline RAW_TEXT inserted at a chosen position without parsing its contents
10. REPORT dates, passed to generate_schedule_text(additional_dates=...) so
they appear as bare DATES keywords (summary-report triggers)
10. Schedule metadata, COMPORD generation and aligned-column output
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 project (like well_event_schedule.py, which uses wells[0]), so the example
@@ -67,9 +68,10 @@ WELL W1
# COMMENT is stored on the event and safely emitted as a schedule comment.
@STARTUP SEGMENT MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 ROUGHNESS=1.0e-5 PRESSURE_COMPONENTS=HFA COMMENT="Install production segment"
# Perforations; COMPLETION_NUMBER groups connections for COMPLUMP.{filter_comment}
# Perforations; COMPLETION_NUMBER groups connections for COMPLUMP. Same-date
# perforations are kept as separate events during normalization.{filter_comment}
@STARTUP + RAMP PERFORATION MDSTART=2000 MDEND=2200 RADIUS=0.05 SKIN=0.5 COMPLETION_NUMBER=1{filter_ref} COMMENT="Open high-priority interval"
@2024-04-01 PERFORATION MDSTART=2400 MDEND=2600 RADIUS=0.05 SKIN=0.3 COMPLETION_NUMBER=2
@STARTUP + RAMP PERFORATION MDSTART=2400 MDEND=2600 RADIUS=0.05 SKIN=0.3 COMPLETION_NUMBER=2
# Time-of-day is preserved and emitted as the TIME field of DATES
@2024-05-15T14:45:30.500 PERFORATION MDSTART=2300 MDEND=2350 RADIUS=0.05 SKIN=0.4 COMPLETION_NUMBER=3
@@ -97,6 +99,15 @@ SCHEDULE
@STARTUP GRUPTREE CHILD_GROUP=OP PARENT_GROUP=FIELD
@STARTUP TUNING TSINIT=1 TSMAXZ=30 TMAXWC=1 NEWTMX=12 NEWTMN=1 LITMAX=50 LITMIN=1 MXWSIT=50 MXWPIT=50
# RAW_TEXT preserves its body verbatim. This block is emitted after RPTRST;
# PRIORITY orders multiple raw blocks sharing the same placement and anchor.
@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 /
/
END_RAW_TEXT
# Report dates: emitted as bare DATES keywords so Eclipse/Flow writes a
# summary report at these dates even though no events fall on them.
REPORT 2024-07-01
@@ -131,10 +142,24 @@ def main():
document = rips.orion_events.parse_orion_events(orion_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(
event.event_type == "PERFORATION"
for well in document.wells
for event in well.events
)
normalized = rips.orion_events.coalesce_orion_document(document)
merged_well_event_count = sum(len(w.events) for w in normalized.wells)
merged_perforation_count = sum(
event.event_type == "PERFORATION"
for well in normalized.wells
for event in well.events
)
print(f" Source well-event lines: {source_well_event_count}")
print(f" Events after same-date merge: {merged_well_event_count}")
print(
" Perforations retained during merge: "
f"{source_perforation_count} -> {merged_perforation_count}"
)
print(f" Groups: {[group.group_name for group in document.groups]}")
print(f" Schedule events: {len(document.schedule_events)}")
@@ -207,6 +232,7 @@ def main():
"RPTRST",
"GRUPTREE",
"TUNING",
"WTRACER",
]
found = [kw for kw in expected_keywords if kw in schedule_text]
print(f"\n Keywords found: {', '.join(found)}")
@@ -24,6 +24,17 @@ WELL A1 = "55_33-A-1"
REPORT 2018-07-01
REPORT A2_STARTUP + 90
# Raw schedule text is preserved verbatim. This block is inserted immediately
# after the date declaration; lower priorities are emitted first when multiple
# blocks use the same placement.
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 /
/
END_RAW_TEXT
WELL A1
@A1_STARTUP PERFORATION MDSTART=1644.49 MDEND=1664.28 RADIUS=0.12065 SKIN=5 COMPLETION_NUMBER=1 FILTER=POROPERM
@A1_STARTUP PERFORATION MDSTART=1664.28 MDEND=1674.18 RADIUS=0.12065 SKIN=5 COMPLETION_NUMBER=2 FILTER=POROPERM