Constructing an Opm::Parser populates the complete keyword database (2774
keywords, measured at 2.4 ms per construction) and this was done once per
keyword event in RifEventKeywordFormatter::buildKeyword, and twice more per
keyword in the column-aligned serialisation in RimKeywordFactory. Schedule
generation was therefore completely dominated by parser construction and grew
directly with the number of events on the timeline. Use a single shared parser
instance instead, since the parser is stateless once constructed.
Also compute the events for a date once in generateDateSection and pass them to
the per-well helpers, instead of calling RimWellEventTimeline::getEventsAtDate
four times per well and date, which scaled as dates x wells x events.
Generating a schedule for a production data set with 1737 events over 490 dates
goes from 4.2 s to 0.1 s, and 21000 events over 14 wells now complete in 0.5 s.
buildKeyword placed every user-supplied item into a single DeckRecord,
so multi-record keywords such as TUNING (3 records, each terminated by
its own '/') were emitted with only one record/slash and rejected by the
simulator.
Distribute items across the keyword's records using the OPM ParserKeyword
schema: each item is matched to the record that defines it. Every record
is emitted (empty records are written as a bare '/') so the schema's
record structure is preserved, and items unknown to the schema are now
reported via a warning. Single-record keywords keep their previous
behaviour, including appending non-schema items after the canonical
block.
The schedule generator previously concatenated full keyword blocks
(`KEYWORD ... /\n/\n`) — one per well — producing verbose output where
each WELSPECS / COMPDAT / WCONHIST / WELSEGS / COMPSEGS / etc. block
appeared once per contributing well. Eclipse keywords are designed to
hold multiple records under a single header; the per-well repetition is
syntactically valid but bloats the file and obscures intent.
Switch `RicScheduleDataGenerator` to accumulate
`std::map<QString, Opm::DeckKeyword>` instead of raw concatenated text.
Each per-well helper now returns an `Opm::DeckKeyword`, and a new
`mergeKeyword` primitive appends its records into the accumulator entry,
creating the entry from the helper's keyword on first encounter. At
emission time each accumulated keyword is serialised once.
To support this, `RifEventKeywordFormatter` is split: each `format*`
QString function now wraps a corresponding `build*` function that
returns `std::optional<Opm::DeckKeyword>`. `RimWellEventKeyword` and
`RimKeywordEvent` expose a parallel `generateDeckKeyword` accessor.
The text-scraping `extractKeywordName` helper becomes dead code and is
removed.
Schedule-level keywords whose OPM ParserRecord is a single ALL-sized item
(RPTRST, RPTSCHED, ...) are not positional — they take a free-form list
of mnemonic tokens like `BASIC=2 DEN ROCKC NORST=1`. Previously each
`keyword_data` entry became its own DeckItem, and OPM emitted them
either as truncated positional values or as a quoted, space-collapsed
single string.
Detect that schema shape in `RifEventKeywordFormatter::formatKeyword`
and pack the user's entries into one RawString DeckItem of mnemonic
tokens: `KEY` for FLAG items, `KEY=VALUE` otherwise.
To distinguish flag mnemonics (`DEN`) from `KEY=1`-style entries, add a
new `FLAG` value to `RimWellEventKeywordItem::ItemType` plumbed end to
end (Python -> GRPC -> Rimc -> storage -> formatter). The Python API
now maps `bool True` to FLAG and silently drops `bool False`.
For positional keywords (WCONHIST etc.) FLAG items have no value to
emit and are rendered as default markers ("1*").
A `rawStringItem` helper is added to `RifOpmDeckTools` so the
mnemonic-list path can produce DeckItems whose OPM output is not
quoted.
`add_well_keyword_event` previously emitted items in `keyword_data` dict
insertion order. Eclipse keywords such as WCONHIST are positional, so a
non-canonical order silently corrupted the simulator input.
`RifEventKeywordFormatter::formatKeyword` now walks the OPM
`ParserRecord` schema and emits items in canonical order, inserting
`1*` defaults for intermediate gaps and dropping trailing unspecified
items. Items that are not part of the keyword schema (e.g. mnemonic-list
keywords like RPTRST/RPTSCHED) are appended after the canonical block in
caller-supplied order to preserve existing behaviour.
Fixes#14065.
Implement timeline-based event system for managing temporal changes in
well completions, perforations, valves, and production controls.
Features:
- Event timeline container with date-based querying
- Six event types: PERF, VALVE, TUBING, WSTATE, WTYPE, WCONTROL
- Schedule keyword generation (DATES, COMPDAT, WELSEGS, WCONPROD, WCONINJE)
- Multi-well schedule generation support
- Python GRPC API for event management
- YAML configuration file support
- Comprehensive test suite
Implementation:
- New directory: ApplicationLibCode/ProjectDataModel/WellEvents/
- Event classes inherit from RimWellEvent base class
- RimWellEventTimeline integrated into RimWellPath
- RicScheduleDataGenerator for multi-well schedule export
- Python API: event_timeline(), add_perf_event(), add_valve_event(), etc.
- Tests verify all event types and YAML config parsing
#13425 Add date tracking to valves and diameter/roughness intervals
Add creation date fields to RimWellPathValve and RimDiameterRoughnessInterval
to support filtering objects by date during completion data export.
- Add m_useCustomStartDate and m_startDate fields
- Add enableCustomStartDate(), setCustomStartDate(), isActiveOnDate() methods
- Set creation dates when applying valve and tubing events in RimWellEventTimeline
#13425 Add date-based filtering for schedule data export
Add optional export date parameter to MSW data collection functions to filter
valves and perforations based on their creation dates during schedule export.
- Add exportDate parameter to collectWsegvalvData, collectWsegAicdData,
collectWsegSicdData, and collectCompsegData functions
- Filter valves by checking isActiveOnDate() on the associated RimWellPathValve
- Add RimPerforationInterval reference to RicMswPerforation for date filtering
- Pass export date from RicScheduleDataGenerator through the extraction chain
This ensures segment numbers reflect the full model while only including
objects active on the export date in output keywords.
Add test to prove compsegs changes over time.
#13425 Fix mypy type checking errors in well_events.py
- Add type annotations to all function parameters
- Fix generic type hints (dict -> Dict[str, Any])
- Import Any type for proper type safety
- Add type: ignore for method reassignment
- All 31 tests pass
#13425 Add schedule-level keyword events for non-well-specific keywords
Add a new event type called "Keyword Event" for schedule-level Eclipse
keywords (RPTRST, GRUPTREE, RPTSCHED, etc.) that are NOT tied to a
specific well path. This complements the existing RimWellEventKeyword
which operates at the well level.
- Add RimKeywordEvent class inheriting from RimWellEvent with null wellPath
- Add SCHEDULE_KEYWORD enum value to EventType
- Add addKeywordEvent() method to RimWellEventTimeline
- Add AddKeywordEvent Python API method to RimcWellEventTimeline
- Update RicScheduleDataGenerator to output global keywords after well sections
- Add Python wrapper add_keyword_event() with type inference
- Add tests and examples for RPTRST, GRUPTREE keywords