mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-26 07:00:17 -05:00
#14575 Orion Events: Rename WELLSPEC event to WELSPECS
This commit is contained in:
1 parent
da6eb2062c
commit
64317dfad7
4 files changed
+41
-33
No files matched your search
@@ -11,7 +11,7 @@ applied in one go with rips.orion_events.apply_orion_document().
|
||||
It demonstrates the full event coverage of the format:
|
||||
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
|
||||
2. Partial WELSPECS 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
|
||||
@@ -59,10 +59,10 @@ DURATION RAMP = 31 DAYS
|
||||
WELL W1 = "{well_name}"
|
||||
|
||||
WELL W1
|
||||
# WELLSPEC updates completion export settings and emits WELSPECS. Attributes
|
||||
# WELSPECS 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
|
||||
@2024-01-05 WELSPECS GROUP="ORION_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.
|
||||
@STARTUP SEGMENT MDSTART=0 MDEND=2500 INNER_DIAMETER=0.15 ROUGHNESS=1.0e-5 PRESSURE_COMPONENTS=HFA COMMENT="Install production segment"
|
||||
@@ -155,9 +155,9 @@ def main():
|
||||
# Apply events up to a date to materialize completions
|
||||
timeline.set_timestamp(timestamp="2024-12-24")
|
||||
|
||||
print("\n4. Verifying created completions and WELLSPEC settings...")
|
||||
print("\n4. Verifying created completions and WELSPECS settings...")
|
||||
completion_settings = well_path.completion_settings()
|
||||
print(" Completion export settings after the latest WELLSPEC:")
|
||||
print(" Completion export settings after the latest WELSPECS:")
|
||||
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}")
|
||||
|
||||
@@ -109,14 +109,14 @@ 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``, ``SEGMENT``, ``VALVE``, ``STATE`` and ``WELLSPEC``, or any
|
||||
``PERFORATION``, ``SEGMENT``, ``VALVE``, ``STATE`` and ``WELSPECS``, or any
|
||||
Eclipse well keyword (``WCONHIST``, ``WELTARG``, ``WRFTPLT``, ``WCONPROD``,
|
||||
...), which
|
||||
is passed through generically with the well name injected as WELL. Event
|
||||
``WELLSPEC`` accepts partial updates to ``GROUP``, ``CROSSFLOW``, ``REFDEPTH``
|
||||
``WELSPECS`` accepts partial updates to ``GROUP``, ``CROSSFLOW``, ``REFDEPTH``
|
||||
and ``PHASE`` (OIL/GAS/WATER/LIQUID). Omitted values inherit the previous
|
||||
WELLSPEC state, initially using the well's completion export settings. There
|
||||
may be multiple WELLSPEC events for a well, but not at the same timestamp.
|
||||
WELSPECS state, initially using the well's completion export settings. There
|
||||
may be multiple WELSPECS events for a well, but not at the same timestamp.
|
||||
Each emits a WELSPECS record with the cumulative state. Event values are
|
||||
materialized back onto completion settings by ``timeline.set_timestamp()``.
|
||||
Event types inside a GROUP block are Eclipse group keywords with the group
|
||||
@@ -273,7 +273,7 @@ class AttrValue:
|
||||
|
||||
@dataclass
|
||||
class WellSpecState:
|
||||
"""Fully resolved cumulative state for one WELLSPEC event."""
|
||||
"""Fully resolved cumulative state for one WELSPECS event."""
|
||||
|
||||
group: str
|
||||
crossflow: bool
|
||||
@@ -592,19 +592,19 @@ def _restart_validation_issues(
|
||||
|
||||
|
||||
def _wellspec_validation_issues(wells: List[WellBlock]) -> List[ParseIssue]:
|
||||
"""Reject multiple WELLSPEC events for one well at the same timestamp."""
|
||||
"""Reject multiple WELSPECS events for one well at the same timestamp."""
|
||||
seen: Dict[Tuple[str, Union[datetime.date, datetime.datetime]], OrionEvent] = {}
|
||||
issues: List[ParseIssue] = []
|
||||
for well in wells:
|
||||
for event in well.events:
|
||||
if event.event_type.upper() != "WELLSPEC":
|
||||
if event.event_type.upper() != "WELSPECS":
|
||||
continue
|
||||
key = (well.well_name, event.event_date)
|
||||
previous = seen.get(key)
|
||||
if previous is not None:
|
||||
issues.append(
|
||||
ParseIssue(
|
||||
f"{_event_context(event)}: WELLSPEC already defined "
|
||||
f"{_event_context(event)}: WELSPECS already defined "
|
||||
f"(first definition on line {previous.loc.line})",
|
||||
event.loc,
|
||||
)
|
||||
@@ -1315,7 +1315,7 @@ def _enum_text(value: Any) -> str:
|
||||
def _prepare_wellspec_events(
|
||||
events: List[OrionEvent], completion_settings: Any, report: ApplyReport
|
||||
) -> None:
|
||||
"""Validate WELLSPEC attributes and resolve partial updates chronologically."""
|
||||
"""Validate WELSPECS attributes and resolve partial updates chronologically."""
|
||||
state = WellSpecState(
|
||||
group=str(completion_settings.group_name_for_export),
|
||||
crossflow=bool(completion_settings.allow_well_cross_flow),
|
||||
@@ -1324,7 +1324,7 @@ def _prepare_wellspec_events(
|
||||
)
|
||||
|
||||
wellspecs = sorted(
|
||||
(event for event in events if event.event_type.upper() == "WELLSPEC"),
|
||||
(event for event in events if event.event_type.upper() == "WELSPECS"),
|
||||
key=lambda event: event.event_date,
|
||||
)
|
||||
for event in wellspecs:
|
||||
@@ -1334,14 +1334,14 @@ def _prepare_wellspec_events(
|
||||
report.errors.append(
|
||||
_event_message(
|
||||
event,
|
||||
f"unknown WELLSPEC attribute(s): {', '.join(sorted(unknown))}",
|
||||
f"unknown WELSPECS attribute(s): {', '.join(sorted(unknown))}",
|
||||
)
|
||||
)
|
||||
report.events_skipped += 1
|
||||
continue
|
||||
if not (set(attrs) - {"COMMENT"}):
|
||||
report.errors.append(
|
||||
_event_message(event, "WELLSPEC needs at least one setting attribute")
|
||||
_event_message(event, "WELSPECS needs at least one setting attribute")
|
||||
)
|
||||
report.events_skipped += 1
|
||||
continue
|
||||
@@ -2037,7 +2037,7 @@ _EVENT_DISPATCH: Dict[str, _EventDispatch] = {
|
||||
"SEGMENT": _apply_segment,
|
||||
"VALVE": _apply_valve,
|
||||
"STATE": _apply_state,
|
||||
"WELLSPEC": _apply_wellspec,
|
||||
"WELSPECS": _apply_wellspec,
|
||||
"WCONHIST": _apply_wconhist,
|
||||
"WELTARG": _apply_weltarg,
|
||||
}
|
||||
@@ -2049,7 +2049,7 @@ _COMPLETION_EVENT_TYPES = (
|
||||
"SEGMENT",
|
||||
"VALVE",
|
||||
"STATE",
|
||||
"WELLSPEC",
|
||||
"WELSPECS",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -494,19 +494,19 @@ class TestParsing:
|
||||
def test_duplicate_wellspec_for_well_and_date_is_rejected(self):
|
||||
text = (
|
||||
'ORIONEVENTS 2.0\nWELL "W"\n'
|
||||
" @2024-01-01 WELLSPEC GROUP=A\n"
|
||||
" @2024-01-01 WELSPECS GROUP=A\n"
|
||||
'WELL "W"\n'
|
||||
" @2024-01-01 WELLSPEC PHASE=GAS\n"
|
||||
" @2024-01-01 WELSPECS PHASE=GAS\n"
|
||||
)
|
||||
with pytest.raises(OrionParseError, match="WELLSPEC already defined.*line 3"):
|
||||
with pytest.raises(OrionParseError, match="WELSPECS already defined.*line 3"):
|
||||
parse_orion_events(text)
|
||||
|
||||
def test_wellspec_same_date_for_different_wells_is_allowed(self):
|
||||
document = parse_orion_events(
|
||||
'ORIONEVENTS 2.0\nWELL "A"\n'
|
||||
" @2024-01-01 WELLSPEC GROUP=GA\n"
|
||||
" @2024-01-01 WELSPECS GROUP=GA\n"
|
||||
'WELL "B"\n'
|
||||
" @2024-01-01 WELLSPEC GROUP=GB\n"
|
||||
" @2024-01-01 WELSPECS GROUP=GB\n"
|
||||
)
|
||||
assert [len(well.events) for well in document.wells] == [1, 1]
|
||||
|
||||
@@ -1144,8 +1144,8 @@ class TestApplying:
|
||||
def test_wellspec_partial_updates_are_cumulative_by_date(self):
|
||||
text = (
|
||||
'ORIONEVENTS 2.0\nWELL "55_33-A-1"\n'
|
||||
" @2019-01-01 WELLSPEC CROSSFLOW=False PHASE=gas\n"
|
||||
" @2018-01-01 WELLSPEC GROUP=my_group REFDEPTH=1002 PHASE=water\n"
|
||||
" @2019-01-01 WELSPECS CROSSFLOW=False PHASE=gas\n"
|
||||
" @2018-01-01 WELSPECS GROUP=my_group REFDEPTH=1002 PHASE=water\n"
|
||||
)
|
||||
timeline, report = self._apply(text)
|
||||
|
||||
@@ -1171,13 +1171,13 @@ class TestApplying:
|
||||
("REFDEPTH=deep", "REFDEPTH must be numeric"),
|
||||
("PHASE=steam", "PHASE must be OIL, GAS, WATER, or LIQUID"),
|
||||
("GROUP=1", "GROUP must be a non-empty string"),
|
||||
("UNKNOWN=1", "unknown WELLSPEC attribute"),
|
||||
("UNKNOWN=1", "unknown WELSPECS attribute"),
|
||||
("COMMENT=empty", "needs at least one setting attribute"),
|
||||
],
|
||||
)
|
||||
def test_invalid_wellspec_is_reported_and_skipped(self, attributes, expected_error):
|
||||
text = (
|
||||
f'ORIONEVENTS 2.0\nWELL "55_33-A-1"\n @2018-01-01 WELLSPEC {attributes}\n'
|
||||
f'ORIONEVENTS 2.0\nWELL "55_33-A-1"\n @2018-01-01 WELSPECS {attributes}\n'
|
||||
)
|
||||
timeline, report = self._apply(text)
|
||||
|
||||
@@ -1231,6 +1231,14 @@ class TestApplying:
|
||||
assert timeline.keyword_calls == []
|
||||
assert any("did you mean 'SEGMENT'" in warning for warning in report.warnings)
|
||||
|
||||
def test_wellspec_is_reported_as_renamed_event(self):
|
||||
text = 'ORIONEVENTS 2.0\nWELL "55_33-A-1"\n @2024-01-01 WELLSPEC GROUP=FIELD\n'
|
||||
timeline, report = self._apply(text)
|
||||
|
||||
assert report.events_skipped == 1
|
||||
assert timeline.wellspec_calls == []
|
||||
assert any("did you mean 'WELSPECS'" in warning for warning in report.warnings)
|
||||
|
||||
def test_valve_mapping(self):
|
||||
text = (
|
||||
'ORIONEVENTS 2.0\nWELL "55_33-A-1"\n'
|
||||
@@ -1806,8 +1814,8 @@ class TestOrionEventsIntegration:
|
||||
document = parse_orion_events(
|
||||
"ORIONEVENTS 2.0\n"
|
||||
f'WELL "{well.name}"\n'
|
||||
" @2018-01-01 WELLSPEC GROUP=my_group REFDEPTH=1002 PHASE=water\n"
|
||||
" @2019-01-01 WELLSPEC CROSSFLOW=False REFDEPTH=1000 PHASE=oil\n"
|
||||
" @2018-01-01 WELSPECS GROUP=my_group REFDEPTH=1002 PHASE=water\n"
|
||||
" @2019-01-01 WELSPECS CROSSFLOW=False REFDEPTH=1000 PHASE=oil\n"
|
||||
)
|
||||
|
||||
report = apply_orion_document(document, timeline, project)
|
||||
|
||||
+2
-2
@@ -77,7 +77,7 @@ 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_type = completion_event_type | eclipse_keyword ;
|
||||
completion_event_type = "PERFORATION" | "SEGMENT" | "VALVE" | "STATE" | "WELLSPEC" ;
|
||||
completion_event_type = "PERFORATION" | "SEGMENT" | "VALVE" | "STATE" | "WELSPECS" ;
|
||||
segment_attribute = "MDSTART" | "MDEND" | "INNER_DIAMETER" | "ROUGHNESS"
|
||||
| "PRESSURE_COMPONENTS" | "COMMENT" ;
|
||||
(* accepted attribute names when event_type is "SEGMENT" *)
|
||||
@@ -221,7 +221,7 @@ SCHEDULE
|
||||
@STARTUP TUNING TSINIT=1 TSMAXZ=30 NEWTMX=12
|
||||
```
|
||||
|
||||
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.
|
||||
Completion event types (`PERFORATION`, `SEGMENT`, `VALVE`, `STATE`, `WELSPECS`) are rejected in a SCHEDULE block. A later `WELL` line switches back to well events; blocks can be interleaved freely.
|
||||
|
||||
## Attributes and quoting
|
||||
|
||||
|
||||
Reference in new issue
Block a user