Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 51 additions & 15 deletions docs/rips/PythonExamples/experimental/well_event_schedule_orion.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,23 @@
applied in one go with rips.orion_events.apply_orion_document().

It demonstrates the full event coverage of the format:
1. TUBING, PERFORATION (incl. a time-of-day date), VALVE and STATE completion
1. SEGMENT, PERFORATION (incl. a time-of-day date), VALVE and STATE completion
events on a well
2. A FILTER declaration (qualified result name) referenced by a perforation,
2. Partial WELLSPEC updates that cumulatively change completion export settings
and generate dated WELSPECS records
3. A FILTER declaration (qualified result name) referenced by a perforation,
materialized as a case-level combined data filter
3. Well keyword events: WCONHIST and WELTARG (with attribute translation) and
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
6. Well keyword events: WCONHIST and WELTARG (with attribute translation) and
WRFTPLT (generic Eclipse well keyword pass-through)
4. SCHEDULE-level keyword events not tied to a well: RPTRST, GRUPTREE, TUNING
5. REPORT dates, passed to generate_schedule_text(additional_dates=...) so
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
they appear as bare DATES keywords (summary-report triggers)
6. Generating Eclipse schedule text from the resulting timeline
10. 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
Expand Down Expand Up @@ -52,11 +59,16 @@ def build_orion_text(well_name, with_filter):
WELL W1 = "{well_name}"

WELL W1
# Tubing installed early (MD 0-2500m)
@STARTUP TUBING MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 ROUGHNESS=1.0e-5
# WELLSPEC updates completion export settings and emits WELSPECS. Attributes
# are optional: the second event inherits GROUP from the first event.
@2024-01-05 WELLSPEC GROUP="ORION_GROUP" CROSSFLOW=True REFDEPTH=1002 PHASE=WATER
@2024-04-15 WELLSPEC CROSSFLOW=False REFDEPTH=1000 PHASE=OIL

# 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}
@STARTUP + RAMP PERFORATION MDSTART=2000 MDEND=2200 RADIUS=0.05 SKIN=0.5 COMPLETION_NUMBER=1{filter_ref}
@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

# Time-of-day is preserved and emitted as the TIME field of DATES
Expand All @@ -66,11 +78,19 @@ def build_orion_text(well_name, with_filter):
@2024-03-01 VALVE MD=2100 TYPE=ICV STATE=OPEN CV=0.7 AREA=0.0001
@2024-02-15 STATE STATE=OPEN

# Well keyword events; WRFTPLT is passed through as a generic Eclipse keyword
@2024-01-15 WCONHIST STATUS=OPEN CMODE=RESV ORAT=3999.99 WRAT=0.01 GRAT=550678.44 VFP=1
# Matching owner/type/date lines merge. The second line extends the first;
# repeated attributes would use the value from the later line.
@2024-01-15 WCONHIST STATUS=OPEN CMODE=RESV COMMENT="Start production history controls"
@2024-01-15 WCONHIST ORAT=3999.99 WRAT=0.01 GRAT=550678.44 VFP=1

# WRFTPLT is passed through as a generic Eclipse keyword.
@2024-05-01 WELTARG CMODE=ORAT VALUE=5000.0
@2024-06-01 WRFTPLT OUTPUT_RFT=YES OUTPUT_PLT=NO OUTPUT_SEGMENT=NO

# MEMBER expands into one GRUPTREE record per unique comma-delimited member.
GROUP "OP"
@STARTUP MEMBER MEMBERS="{well_name},OBSERVER" COMMENT="Define operating group members"

# Schedule-level keywords (not tied to a well)
SCHEDULE
@STARTUP RPTRST BASIC=2 FREQ=1
Expand Down Expand Up @@ -110,7 +130,12 @@ def main():
print(orion_text)
document = rips.orion_events.parse_orion_events(orion_text)
print(f" Wells: {[w.well_name for w in document.wells]}")
print(f" Well events: {sum(len(w.events) for w in document.wells)}")
source_well_event_count = sum(len(w.events) for w in document.wells)
normalized = rips.orion_events.coalesce_orion_document(document)
merged_well_event_count = sum(len(w.events) for w in normalized.wells)
print(f" Source well-event lines: {source_well_event_count}")
print(f" Events after same-date merge: {merged_well_event_count}")
print(f" Groups: {[group.group_name for group in document.groups]}")
print(f" Schedule events: {len(document.schedule_events)}")

print("\n3. Applying events to the timeline...")
Expand All @@ -130,7 +155,14 @@ def main():
# Apply events up to a date to materialize completions
timeline.set_timestamp(timestamp="2024-12-24")

print("\n4. Verifying created completions...")
print("\n4. Verifying created completions and WELLSPEC settings...")
completion_settings = well_path.completion_settings()
print(" Completion export settings after the latest WELLSPEC:")
print(f" Group: {completion_settings.group_name_for_export}")
print(f" Cross-flow: {completion_settings.allow_well_cross_flow}")
print(f" Ref. depth: {completion_settings.reference_depth_for_export}")
print(f" Phase: {completion_settings.well_type_for_export}")

perforations = well_path.completions().perforations().perforations()
print(f" Perforations created: {len(perforations)}")
for perf in perforations:
Expand All @@ -147,12 +179,14 @@ def main():
if case is None:
print(" No Eclipse case loaded - skipping schedule generation.")
return
# REPORT dates from the ORIONEVENTS text become bare DATES keywords
# (summary-report triggers) via additional_dates.
# REPORT dates become bare DATES keywords via additional_dates. Aligned output
# adds column-title comments; the schedule header identifies its timestamp and
# user, and each generated WELSPECS record has a matching COMPORD INPUT record.
schedule_text = timeline.generate_schedule_text(
eclipse_case=case,
export_msw_for_wells=[well_path],
additional_dates=report.report_dates,
align_columns=True,
)
if schedule_text:
print(f" Generated schedule text ({len(schedule_text)} characters)")
Expand All @@ -163,6 +197,8 @@ def main():

expected_keywords = [
"DATES",
"WELSPECS",
"COMPORD",
"COMPDAT",
"COMPLUMP",
"WCONHIST",
Expand Down
85 changes: 63 additions & 22 deletions docs/rips/generated/generated_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,18 +278,18 @@ class WellStatus(StrEnum):
STOP = "STOP"

class WellType(StrEnum):
OIL = "OIL"
GAS = "GAS"
WATER = "WATER"
LIQUID = "LIQUID"

class WellType2(StrEnum):
OIL_PRODUCER = "OIL_PRODUCER"
GAS_PRODUCER = "GAS_PRODUCER"
WATER_PRODUCER = "WATER_PRODUCER"
WATER_INJECTOR = "WATER_INJECTOR"
GAS_INJECTOR = "GAS_INJECTOR"

class WellTypeForExport(StrEnum):
OIL = "OIL"
GAS = "GAS"
WATER = "WATER"
LIQUID = "LIQUID"

class CellFilterCollection(PdmObjectBase):
"""
Attributes:
Expand Down Expand Up @@ -1773,11 +1773,13 @@ class WellEvent(PdmObjectBase):
WellEvent

Attributes:
comment (str): Comment
well_path (Optional[WellPath]): Well Path
"""
__custom_init__ = None #: Assign a custom init routine to be run at __init__

def __init__(self, pb2_object: Optional[PdmObject_pb2.PdmObject]=None, channel: Optional[grpc.Channel]=None) -> None:
self.comment: str = ""
self.well_path: Optional[WellPath] = None
PdmObjectBase.__init__(self, pb2_object, channel)
if WellEvent.__custom_init__ is not None:
Expand Down Expand Up @@ -4109,6 +4111,23 @@ def add_well_keyword_event_internal(self, event_date: str="2024-01-01", well_pat
return self._call_pdm_method_return_value("AddWellKeywordEventInternal", WellEventKeyword, event_date=event_date, well_path=well_path, keyword_name=keyword_name, item_names=item_names, item_types=item_types, item_values=item_values)


def add_wellspec_event(self, event_date: str="2024-01-01", well_path: Optional[WellPath]=None, group_name: str="", allow_cross_flow: bool=True, reference_depth: Optional[Optional[float]]=None, well_type: WellType=WellType.OIL) -> WellEventWellSpec:
"""
Add a WELLSPEC event to the timeline

Arguments:
event_date (str): Event Date (YYYY-MM-DD)
well_path (Optional[WellPath]): Well Path
group_name (str): Group Name
allow_cross_flow (bool): Allow Well Cross-Flow
reference_depth (Optional[Optional[float]]): Reference Depth
well_type (WellType): One of [OIL, GAS, WATER, LIQUID]
Returns:
WellEventWellSpec
"""
return self._call_pdm_method_return_value("AddWellspecEvent", WellEventWellSpec, event_date=event_date, well_path=well_path, group_name=group_name, allow_cross_flow=allow_cross_flow, reference_depth=reference_depth, well_type=well_type)


def events(self) -> List[WellEvent]:
"""Events

Expand Down Expand Up @@ -4172,12 +4191,12 @@ class WellEventType(WellEvent):
WellEventType

Attributes:
well_type (WellType): One of [OIL_PRODUCER, GAS_PRODUCER, WATER_PRODUCER, WATER_INJECTOR, GAS_INJECTOR]
well_type (WellType2): One of [OIL_PRODUCER, GAS_PRODUCER, WATER_PRODUCER, WATER_INJECTOR, GAS_INJECTOR]
"""
__custom_init__ = None #: Assign a custom init routine to be run at __init__

def __init__(self, pb2_object: Optional[PdmObject_pb2.PdmObject]=None, channel: Optional[grpc.Channel]=None) -> None:
self.well_type: WellType = WellType.OIL_PRODUCER
self.well_type: WellType2 = WellType2.OIL_PRODUCER
WellEvent.__init__(self, pb2_object, channel)
if WellEventType.__custom_init__ is not None:
WellEventType.__custom_init__(self, pb2_object=pb2_object, channel=channel)
Expand Down Expand Up @@ -4217,6 +4236,27 @@ def __init__(self, pb2_object: Optional[PdmObject_pb2.PdmObject]=None, channel:
if WellEventValve.__custom_init__ is not None:
WellEventValve.__custom_init__(self, pb2_object=pb2_object, channel=channel)

class WellEventWellSpec(WellEvent):
"""
WellEventWellSpec

Attributes:
allow_cross_flow (bool): Allow Cross-Flow
group_name (str): Group Name
reference_depth (Optional[float]): Reference Depth
well_type (WellType): One of [OIL, GAS, WATER, LIQUID]
"""
__custom_init__ = None #: Assign a custom init routine to be run at __init__

def __init__(self, pb2_object: Optional[PdmObject_pb2.PdmObject]=None, channel: Optional[grpc.Channel]=None) -> None:
self.allow_cross_flow: bool = True
self.group_name: str = ""
self.reference_depth: Optional[float] = None
self.well_type: WellType = WellType.OIL
WellEvent.__init__(self, pb2_object, channel)
if WellEventWellSpec.__custom_init__ is not None:
WellEventWellSpec.__custom_init__(self, pb2_object=pb2_object, channel=channel)

class PlotCurve(PdmObjectBase):
__custom_init__ = None #: Assign a custom init routine to be run at __init__

Expand Down Expand Up @@ -4326,18 +4366,6 @@ def add_extraction_curve(self, case: Optional[Reservoir]=None, well_path: Option
return self._call_pdm_method_return_value("AddExtractionCurve", WellLogExtractionCurve, case=case, well_path=well_path, property_type=property_type, property_name=property_name, time_step=time_step)


class FileWellPath(WellPath):
"""
Well Paths Loaded From File

"""
__custom_init__ = None #: Assign a custom init routine to be run at __init__

def __init__(self, pb2_object: Optional[PdmObject_pb2.PdmObject]=None, channel: Optional[grpc.Channel]=None) -> None:
WellPath.__init__(self, pb2_object, channel)
if FileWellPath.__custom_init__ is not None:
FileWellPath.__custom_init__(self, pb2_object=pb2_object, channel=channel)

class WellPathAicdParameters(PdmObjectBase):
"""
Attributes:
Expand Down Expand Up @@ -4381,6 +4409,18 @@ def __init__(self, pb2_object: Optional[PdmObject_pb2.PdmObject]=None, channel:
if WellPathAicdParameters.__custom_init__ is not None:
WellPathAicdParameters.__custom_init__(self, pb2_object=pb2_object, channel=channel)

class FileWellPath(WellPath):
"""
Well Paths Loaded From File

"""
__custom_init__ = None #: Assign a custom init routine to be run at __init__

def __init__(self, pb2_object: Optional[PdmObject_pb2.PdmObject]=None, channel: Optional[grpc.Channel]=None) -> None:
WellPath.__init__(self, pb2_object, channel)
if FileWellPath.__custom_init__ is not None:
FileWellPath.__custom_init__(self, pb2_object=pb2_object, channel=channel)

class WellPathCompletionSettings(PdmObjectBase):
"""
Attributes:
Expand All @@ -4396,7 +4436,7 @@ class WellPathCompletionSettings(PdmObjectBase):
reference_depth_for_export (Optional[float]): BHP Reference Depth
well_bore_fluid_pvt_table (int): Wellbore Fluid PVT table
well_name_for_export (str): Well Name
well_type_for_export (WellTypeForExport): One of [OIL, GAS, WATER, LIQUID]
well_type_for_export (WellType): One of [OIL, GAS, WATER, LIQUID]
"""
__custom_init__ = None #: Assign a custom init routine to be run at __init__

Expand All @@ -4413,7 +4453,7 @@ def __init__(self, pb2_object: Optional[PdmObject_pb2.PdmObject]=None, channel:
self.reference_depth_for_export: Optional[float] = None
self.well_bore_fluid_pvt_table: int = 0
self.well_name_for_export: str = ""
self.well_type_for_export: WellTypeForExport = WellTypeForExport.OIL
self.well_type_for_export: WellType = WellType.OIL
PdmObjectBase.__init__(self, pb2_object, channel)
if WellPathCompletionSettings.__custom_init__ is not None:
WellPathCompletionSettings.__custom_init__(self, pb2_object=pb2_object, channel=channel)
Expand Down Expand Up @@ -4857,6 +4897,7 @@ def class_dict() -> Dict[str, Type[PdmObjectBase]]:
classes['WellEventTubing'] = WellEventTubing
classes['WellEventType'] = WellEventType
classes['WellEventValve'] = WellEventValve
classes['WellEventWellSpec'] = WellEventWellSpec
classes['WellLog'] = WellLog
classes['WellLogExtractionCurve'] = WellLogExtractionCurve
classes['WellLogFileInterface'] = WellLogFileInterface
Expand Down
Loading