diff --git a/GrpcInterface/Python/rips/PythonExamples/experimental/import_orion_events.py b/GrpcInterface/Python/rips/PythonExamples/experimental/import_orion_events.py index 0557018be2..6ce0628fec 100644 --- a/GrpcInterface/Python/rips/PythonExamples/experimental/import_orion_events.py +++ b/GrpcInterface/Python/rips/PythonExamples/experimental/import_orion_events.py @@ -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() diff --git a/GrpcInterface/Python/rips/PythonExamples/experimental/well_event_schedule_orion.py b/GrpcInterface/Python/rips/PythonExamples/experimental/well_event_schedule_orion.py index 3b40d06f8d..6e92ddae0b 100644 --- a/GrpcInterface/Python/rips/PythonExamples/experimental/well_event_schedule_orion.py +++ b/GrpcInterface/Python/rips/PythonExamples/experimental/well_event_schedule_orion.py @@ -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)}") diff --git a/GrpcInterface/Python/rips/example_input_files/well_events.orion b/GrpcInterface/Python/rips/example_input_files/well_events.orion index 4dbf5dbfa9..b364acb592 100644 --- a/GrpcInterface/Python/rips/example_input_files/well_events.orion +++ b/GrpcInterface/Python/rips/example_input_files/well_events.orion @@ -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