mirror of
https://github.com/OPM/ResInsight.git
synced 2026-08-27 05:37:21 -05:00
#14557 Orion Events: Rename tubing event to segment
This commit is contained in:
@@ -9,7 +9,7 @@ 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().
|
||||
|
||||
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. Partial WELLSPEC updates that cumulatively change completion export settings
|
||||
and generate dated WELSPECS records
|
||||
@@ -65,7 +65,7 @@ WELL W1
|
||||
@2024-04-15 WELLSPEC CROSSFLOW=False REFDEPTH=1000 PHASE=OIL
|
||||
|
||||
# COMMENT is stored on the event and safely emitted as a schedule comment.
|
||||
@STARTUP TUBING MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 ROUGHNESS=1.0e-5 COMMENT="Install production tubing"
|
||||
@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} COMMENT="Open high-priority interval"
|
||||
|
||||
@@ -95,7 +95,7 @@ Notes on the grammar:
|
||||
attribute values, e.g. ``FILTER="SOIL > 0.8 AND PERMX > 200"``.
|
||||
* Every attribute is ``KEY=VALUE``; bare positional tokens are rejected.
|
||||
* Event types inside a WELL block are either the built-in completion events
|
||||
``PERFORATION``, ``TUBING``, ``VALVE``, ``STATE`` and ``WELLSPEC``, or any
|
||||
``PERFORATION``, ``SEGMENT``, ``VALVE``, ``STATE`` and ``WELLSPEC``, or any
|
||||
Eclipse well keyword (``WCONHIST``, ``WELTARG``, ``WRFTPLT``, ``WCONPROD``,
|
||||
...), which
|
||||
is passed through generically with the well name injected as WELL. Event
|
||||
@@ -987,8 +987,16 @@ _PERF_KNOWN = {
|
||||
"FILTER",
|
||||
"COMMENT",
|
||||
}
|
||||
_TUBING_REQUIRED = ("MDSTART", "MDEND")
|
||||
_TUBING_KNOWN = {"MDSTART", "MDEND", "INNER_DIAMETER", "ROUGHNESS", "COMMENT"}
|
||||
_SEGMENT_REQUIRED = ("MDSTART", "MDEND")
|
||||
_SEGMENT_KNOWN = {
|
||||
"MDSTART",
|
||||
"MDEND",
|
||||
"INNER_DIAMETER",
|
||||
"ROUGHNESS",
|
||||
"PRESSURE_COMPONENTS",
|
||||
"COMMENT",
|
||||
}
|
||||
_PRESSURE_COMPONENTS = {"H--", "HF-", "HFA"}
|
||||
_VALVE_REQUIRED = ("MD", "TYPE")
|
||||
_VALVE_KNOWN = {"MD", "TYPE", "STATE", "CV", "AREA", "COMMENT"} | {
|
||||
"AICD_STRENGTH",
|
||||
@@ -1389,6 +1397,8 @@ def _materialize_filter(ctx: _FilterContext, event_filter: EventFilter) -> Any:
|
||||
|
||||
def _suspected_typo(event_type: str) -> Optional[str]:
|
||||
"""Return the built-in event type this one looks like a misspelling of."""
|
||||
if event_type == "TUBING":
|
||||
return "SEGMENT"
|
||||
close = difflib.get_close_matches(event_type, _EVENT_DISPATCH, n=1, cutoff=0.8)
|
||||
return close[0] if close else None
|
||||
|
||||
@@ -1573,7 +1583,7 @@ def _apply_perforation(
|
||||
report.events_applied += 1
|
||||
|
||||
|
||||
def _apply_tubing(
|
||||
def _apply_segment(
|
||||
event: OrionEvent,
|
||||
well_path: Any,
|
||||
timeline: Any,
|
||||
@@ -1581,17 +1591,19 @@ def _apply_tubing(
|
||||
ctx: Optional[_FilterContext] = None,
|
||||
) -> None:
|
||||
if not _check_completion_attrs(
|
||||
event, "TUBING", _TUBING_KNOWN, _TUBING_REQUIRED, report
|
||||
event, "SEGMENT", _SEGMENT_KNOWN, _SEGMENT_REQUIRED, report
|
||||
):
|
||||
return
|
||||
|
||||
attrs = event.attributes
|
||||
try:
|
||||
start_md = float(_as_number(attrs["MDSTART"], event.loc))
|
||||
end_md = float(_as_number(attrs["MDEND"], event.loc))
|
||||
kwargs: Dict[str, Any] = {
|
||||
"event_date": _iso_event_date(event.event_date),
|
||||
"well_path": well_path,
|
||||
"start_md": float(_as_number(attrs["MDSTART"], event.loc)),
|
||||
"end_md": float(_as_number(attrs["MDEND"], event.loc)),
|
||||
"start_md": start_md,
|
||||
"end_md": end_md,
|
||||
}
|
||||
if "INNER_DIAMETER" in attrs:
|
||||
kwargs["inner_diameter"] = float(
|
||||
@@ -1599,12 +1611,26 @@ def _apply_tubing(
|
||||
)
|
||||
if "ROUGHNESS" in attrs:
|
||||
kwargs["roughness"] = float(_as_number(attrs["ROUGHNESS"], event.loc))
|
||||
pressure_components = None
|
||||
if "PRESSURE_COMPONENTS" in attrs:
|
||||
pressure_components = str(attrs["PRESSURE_COMPONENTS"].value).upper()
|
||||
if pressure_components not in _PRESSURE_COMPONENTS:
|
||||
raise OrionParseError(
|
||||
"PRESSURE_COMPONENTS must be H--, HF-, or HFA", event.loc
|
||||
)
|
||||
except OrionParseError as exc:
|
||||
report.errors.append(str(exc))
|
||||
report.events_skipped += 1
|
||||
return
|
||||
|
||||
timeline_event = timeline.add_tubing_event(**kwargs)
|
||||
well_path.completion_settings().add_custom_segment_interval(
|
||||
start_md=start_md, end_md=end_md
|
||||
)
|
||||
if pressure_components is not None:
|
||||
msw_settings = well_path.msw_settings()
|
||||
msw_settings.pressure_drop = pressure_components
|
||||
msw_settings.update()
|
||||
_apply_event_comment(event, timeline_event)
|
||||
report.events_applied += 1
|
||||
|
||||
@@ -1775,7 +1801,7 @@ _EventDispatch = Callable[
|
||||
# built-in, which is governed by the on_unknown_event policy).
|
||||
_EVENT_DISPATCH: Dict[str, _EventDispatch] = {
|
||||
"PERFORATION": _apply_perforation,
|
||||
"TUBING": _apply_tubing,
|
||||
"SEGMENT": _apply_segment,
|
||||
"VALVE": _apply_valve,
|
||||
"STATE": _apply_state,
|
||||
"WELLSPEC": _apply_wellspec,
|
||||
@@ -1787,7 +1813,7 @@ _EVENT_DISPATCH: Dict[str, _EventDispatch] = {
|
||||
# block or be emitted as Eclipse keywords.
|
||||
_COMPLETION_EVENT_TYPES = (
|
||||
"PERFORATION",
|
||||
"TUBING",
|
||||
"SEGMENT",
|
||||
"VALVE",
|
||||
"STATE",
|
||||
"WELLSPEC",
|
||||
|
||||
@@ -61,16 +61,33 @@ class FakeCompletionSettings:
|
||||
self.allow_well_cross_flow = True
|
||||
self.reference_depth_for_export = None
|
||||
self.well_type_for_export = "OIL"
|
||||
self.custom_segment_calls = []
|
||||
|
||||
def add_custom_segment_interval(self, **kwargs):
|
||||
self.custom_segment_calls.append(kwargs)
|
||||
|
||||
|
||||
class FakeMswSettings:
|
||||
def __init__(self):
|
||||
self.pressure_drop = "HF-"
|
||||
self.update_calls = 0
|
||||
|
||||
def update(self):
|
||||
self.update_calls += 1
|
||||
|
||||
|
||||
class FakeWellPath:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self._completion_settings = FakeCompletionSettings()
|
||||
self._msw_settings = FakeMswSettings()
|
||||
|
||||
def completion_settings(self):
|
||||
return self._completion_settings
|
||||
|
||||
def msw_settings(self):
|
||||
return self._msw_settings
|
||||
|
||||
|
||||
class FakeProject:
|
||||
def __init__(self, names, cases=()):
|
||||
@@ -1100,10 +1117,11 @@ class TestApplying:
|
||||
assert expected_error in report.errors[0]
|
||||
assert timeline.wellspec_calls == []
|
||||
|
||||
def test_tubing_mapping(self):
|
||||
def test_segment_mapping_creates_custom_interval_and_sets_pressure_drop(self):
|
||||
text = (
|
||||
'ORIONEVENTS 2.0\nWELL "55_33-A-1"\n'
|
||||
" @2024-01-01 TUBING MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 ROUGHNESS=1.0e-5\n"
|
||||
" @2024-01-01 SEGMENT MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 "
|
||||
"ROUGHNESS=1.0e-5 PRESSURE_COMPONENTS=HFA\n"
|
||||
)
|
||||
timeline, report = self._apply(text)
|
||||
assert report.events_applied == 1
|
||||
@@ -1113,6 +1131,37 @@ class TestApplying:
|
||||
assert call["inner_diameter"] == 0.15
|
||||
assert call["roughness"] == pytest.approx(1.0e-5)
|
||||
|
||||
well = call["well_path"]
|
||||
assert well.completion_settings().custom_segment_calls == [
|
||||
{"start_md": 0.0, "end_md": 2500.0}
|
||||
]
|
||||
assert well.msw_settings().pressure_drop == "HFA"
|
||||
assert well.msw_settings().update_calls == 1
|
||||
|
||||
def test_segment_rejects_invalid_pressure_components(self):
|
||||
text = (
|
||||
'ORIONEVENTS 2.0\nWELL "55_33-A-1"\n'
|
||||
" @2024-01-01 SEGMENT MDSTART=0 MDEND=2500 "
|
||||
"PRESSURE_COMPONENTS=INVALID\n"
|
||||
)
|
||||
timeline, report = self._apply(text)
|
||||
|
||||
assert report.events_applied == 0
|
||||
assert report.events_skipped == 1
|
||||
assert "PRESSURE_COMPONENTS must be H--, HF-, or HFA" in report.errors[0]
|
||||
assert timeline.tubing_calls == []
|
||||
|
||||
def test_tubing_is_reported_as_renamed_event(self):
|
||||
text = (
|
||||
'ORIONEVENTS 2.0\nWELL "55_33-A-1"\n'
|
||||
" @2024-01-01 TUBING MDSTART=0 MDEND=2500\n"
|
||||
)
|
||||
timeline, report = self._apply(text)
|
||||
|
||||
assert report.events_skipped == 1
|
||||
assert timeline.keyword_calls == []
|
||||
assert any("did you mean 'SEGMENT'" in warning for warning in report.warnings)
|
||||
|
||||
def test_valve_mapping(self):
|
||||
text = (
|
||||
'ORIONEVENTS 2.0\nWELL "55_33-A-1"\n'
|
||||
@@ -1702,7 +1751,7 @@ class TestOrionEventsIntegration:
|
||||
"DATE STARTUP = 2024-01-01\n"
|
||||
"DURATION RAMP = 31 DAYS\n"
|
||||
f'WELL "{well.name}"\n'
|
||||
" @STARTUP TUBING MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 ROUGHNESS=1.0e-5\n"
|
||||
" @STARTUP SEGMENT MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 ROUGHNESS=1.0e-5 PRESSURE_COMPONENTS=HFA\n"
|
||||
" @STARTUP + RAMP PERFORATION MDSTART=2000 MDEND=2200 RADIUS=0.05 SKIN=0.5 COMPLETION_NUMBER=1\n"
|
||||
" @2024-05-15T14:45:30.500 PERFORATION MDSTART=2300 MDEND=2350 RADIUS=0.05 SKIN=0.4 COMPLETION_NUMBER=2\n"
|
||||
" @2024-03-01 VALVE MD=2100 TYPE=ICV STATE=OPEN CV=0.7 AREA=0.0001\n"
|
||||
@@ -1721,6 +1770,13 @@ class TestOrionEventsIntegration:
|
||||
assert report.warnings == []
|
||||
assert report.events_applied == 11
|
||||
|
||||
custom_segments = well.descendants(rips.CustomSegmentInterval)
|
||||
assert any(
|
||||
segment.start_md == 0.0 and segment.end_md == 2500.0
|
||||
for segment in custom_segments
|
||||
)
|
||||
assert well.msw_settings().pressure_drop == "HFA"
|
||||
|
||||
timeline.set_timestamp(timestamp="2024-12-24")
|
||||
schedule = timeline.generate_schedule_text(
|
||||
eclipse_case=case, export_msw_for_wells=[well]
|
||||
|
||||
+25
-13
@@ -34,7 +34,7 @@ FILTER POROPERM = "PORO > 0.4 AND PERMX > 100.0"
|
||||
WELL A1 = "55_33-A-1" # alias declaration (has '=')
|
||||
|
||||
WELL A1 # opens an event block via the alias
|
||||
@STARTUP TUBING MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 ROUGHNESS=1.0e-5
|
||||
@STARTUP SEGMENT MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 ROUGHNESS=1.0e-5 PRESSURE_COMPONENTS=HFA
|
||||
@STARTUP + RAMP PERFORATION MDSTART=2000 MDEND=2200 RADIUS=0.05 SKIN=0.5 COMPLETION_NUMBER=1 FILTER=POROPERM
|
||||
@2024-05-15T14:45:30.500 PERFORATION MDSTART=2300 MDEND=2350 RADIUS=0.05
|
||||
@2024-03-01 VALVE MD=2100 TYPE=ICV STATE=OPEN CV=0.7 AREA=0.0001
|
||||
@@ -56,9 +56,10 @@ SCHEDULE # schedule-level keywords, not tied to a well
|
||||
```
|
||||
document = header , { statement } ;
|
||||
header = "ORIONEVENTS" , "2.0" ; (* first meaningful line *)
|
||||
statement = unit_directive | declaration | well_block_open
|
||||
| schedule_block_open | event_line ;
|
||||
statement = unit_directive | declaration | report_line | well_block_open
|
||||
| group_block_open | schedule_block_open | event_line ;
|
||||
unit_directive = "UNIT" , ( "METRIC" | "FIELD" | "LAB" ) ;
|
||||
report_line = "REPORT" , date_expr ;
|
||||
|
||||
declaration = date_decl | duration_decl | well_decl | filter_decl ;
|
||||
date_decl = "DATE" , ident , "=" , date_expr ; (* DATE X = 2018-03-01 + 9 *)
|
||||
@@ -72,8 +73,14 @@ filter_term = [ result_type , "." ] , ident , comp_op , number ;
|
||||
comp_op = ">" | ">=" | "<" | "<=" ;
|
||||
|
||||
well_block_open = "WELL" , ( quoted_string | ident ) ; (* no "=" present *)
|
||||
group_block_open = "GROUP" , quoted_string ; (* group keyword events *)
|
||||
schedule_block_open = "SCHEDULE" ; (* well-less keyword events *)
|
||||
event_line = "@" , date_expr , event_type , { attribute } ;
|
||||
event_line = "@" , date_expr , event_type , { attribute } ;
|
||||
event_type = completion_event_type | eclipse_keyword ;
|
||||
completion_event_type = "PERFORATION" | "SEGMENT" | "VALVE" | "STATE" | "WELLSPEC" ;
|
||||
segment_attribute = "MDSTART" | "MDEND" | "INNER_DIAMETER" | "ROUGHNESS"
|
||||
| "PRESSURE_COMPONENTS" | "COMMENT" ;
|
||||
(* accepted attribute names when event_type is "SEGMENT" *)
|
||||
|
||||
date_expr = ( iso_date | iso_datetime | date_ident ) , { sign , term } ;
|
||||
duration_expr = ( integer | dur_ident ) , { sign , term } , [ "DAYS" | "days" ] ;
|
||||
@@ -98,9 +105,11 @@ The format is line-oriented. Every non-blank line is dispatched on its first tok
|
||||
| `DATE` | Declare a typed date variable |
|
||||
| `DURATION` | Declare a typed whole-day duration variable |
|
||||
| `FILTER` | Declare a typed cell-filter expression |
|
||||
| `REPORT` | Add a date that must appear in generated schedule output |
|
||||
| `WELL` | With `=`: declare a well-name alias. Without `=`: open a well event block |
|
||||
| `GROUP` | Open a block of group-level keyword events |
|
||||
| `SCHEDULE` | Open a block of schedule-level keyword events (bare keyword, no arguments) |
|
||||
| `@` | An event line, appended to the enclosing WELL or SCHEDULE block |
|
||||
| `@` | An event line, appended to the enclosing WELL, GROUP or SCHEDULE block |
|
||||
| `#` | Comment (also allowed trailing on any line, outside double quotes) |
|
||||
|
||||
## Typed declarations
|
||||
@@ -142,7 +151,7 @@ Whitespace around `+`/`-` is optional but conventional. A time-of-day is preserv
|
||||
|
||||
## Event types
|
||||
|
||||
Event lines are `@<date_expr> <EVENT_TYPE> KEY=VALUE ...`. Inside a WELL block, the event type is either one of the four built-in completion events or any Eclipse well keyword.
|
||||
Event lines are `@<date_expr> <EVENT_TYPE> KEY=VALUE ...`. Inside a WELL block, the event type is either one of the five built-in completion events or any Eclipse well keyword.
|
||||
|
||||
### Built-in completion events
|
||||
|
||||
@@ -157,14 +166,17 @@ Event lines are `@<date_expr> <EVENT_TYPE> KEY=VALUE ...`. Inside a WELL block,
|
||||
| `COMPLETION_NUMBER` | no | `completion_number` (COMPLUMP grouping) |
|
||||
| `FILTER` | no | case-level combined data filter attached to the perforation: a declared `FILTER` variable (`FILTER=POROPERM`) or an inline quoted expression (`FILTER="PORO > 0.4"`) — see [Filter expressions](#filter-expressions) |
|
||||
|
||||
**TUBING** → `add_tubing_event`
|
||||
**SEGMENT** → `add_tubing_event` and `add_custom_segment_interval`
|
||||
|
||||
Each event adds the underlying tubing timeline event and a corresponding **Custom Segment Interval** with the same measured-depth range.
|
||||
|
||||
| Attribute | Required | Maps to |
|
||||
|---|---|---|
|
||||
| `MDSTART` | yes | `start_md` |
|
||||
| `MDEND` | yes | `end_md` |
|
||||
| `INNER_DIAMETER` | no | `inner_diameter` [m] |
|
||||
| `ROUGHNESS` | no | `roughness` [m] |
|
||||
| `MDSTART` | yes | tubing `start_md` and custom interval `start_md` |
|
||||
| `MDEND` | yes | tubing `end_md` and custom interval `end_md` |
|
||||
| `INNER_DIAMETER` | no | tubing `inner_diameter` [m] |
|
||||
| `ROUGHNESS` | no | tubing `roughness` [m] |
|
||||
| `PRESSURE_COMPONENTS` | no | MSW `pressure_drop`: `H--`, `HF-` or `HFA` |
|
||||
|
||||
**VALVE** → `add_valve_event` (requires an existing perforation at the MD)
|
||||
|
||||
@@ -209,7 +221,7 @@ SCHEDULE
|
||||
@STARTUP TUNING TSINIT=1 TSMAXZ=30 NEWTMX=12
|
||||
```
|
||||
|
||||
Completion event types (`PERFORATION`, `TUBING`, `VALVE`, `STATE`) are rejected in a SCHEDULE block. A later `WELL` line switches back to well events; blocks can be interleaved freely.
|
||||
Completion event types (`PERFORATION`, `SEGMENT`, `VALVE`, `STATE`, `WELLSPEC`) are rejected in a SCHEDULE block. A later `WELL` line switches back to well events; blocks can be interleaved freely.
|
||||
|
||||
## Attributes and quoting
|
||||
|
||||
@@ -291,5 +303,5 @@ 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 plus generic keyword pass-through, perforation data filters, multi-error diagnostics, validator CLI.
|
||||
- **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.
|
||||
|
||||
Reference in New Issue
Block a user