Reuse RimColorLegendCollection::updateColorLegend in the two roff import
sites that previously deleted and recreated the legend bound to a result,
so views referencing the legend keep their binding on re-import.
Extract the rock color matching from createColorLegendMatchDefaultRockColors
into matchDefaultRockColors so the facies branch can pass matched colors to
updateColorLegend. Remove the now-unused
RimColorLegendCollection::createColorLegend.
Address review feedback on #14199: deleting and recreating the legend left
views bound to the old legend pointing at a deleted object. Add
RimColorLegendCollection::updateColorLegend and a matching UpdateColorLegend
scriptable method that mutate the existing custom legend (name and items) so
referring views keep their binding and are notified once. A new legend is only
created when none is registered for the result or the registered legend is a
standard legend.
set_discrete_property_category_names now performs a single atomic call instead
of delete + create + per-item appends. The empty-dict removal path still uses
DeleteColorLegend.
RimColorLegendCollection::deleteColorLegend erased the default-legend map
entry before looking the legend up, so findDefaultLegendForResult always
returned null and the old legend was never removed from m_customColorLegends.
Each set_discrete_property_category_names call for the same property left an
orphaned legend behind and appended a new one.
Look up the legend before erasing the map key, and guard against deleting a
standard legend that may have been registered as a default.
When a new Fault Distance defaults to all faults, the ResInsight-generated
undefined grid faults were ticked by default. Add RimFaultInView::isGeneratedFault
and filter these faults out of the default selection in both the UI feature and
the Python command. An explicit fault selection is still honored.
When a fault distance object is deleted or renamed, the generated cell
result was only emptied via clearScalarResult, leaving its name in the
GENERATED result-variable dropdown. Follow the RimGridCalculation
deletion pattern: reset any view displaying the result to None and tag
the result entry as REMOVED so it disappears from the dropdown.
Guard against empty curve definitions and time steps in
RimCorrelationPlotCollection. When the first ensemble has no FOPT
summary vector, the tornado/cross-plot curve definition vector was
empty and calling .front() on it dereferenced invalid memory,
causing a segfault in RiaSummaryCurveDefinition::summaryAddressY().
Reloading a grid file frees and recreates RigEclipseCaseData. The contour map projection caches raw pointers into the case data and previously only refreshed them when reset explicitly via RimReloadCaseTools::updateAll3dViews(). Callers of RimEclipseCase::reloadEclipseGridFile() that bypass updateAll3dViews(), such as RimOpmFlowJob::onCompleted(), left the projection holding dangling pointers that crashed in generateResults/hasResultEntry on the next redraw. The projection now detects that its cached RigEclipseCaseData no longer matches the current case and rebuilds itself before the stale pointers are dereferenced.
When creating a grid statistics plot from a view result whose address is valid by name but not present in the case result cache, statistics() was indexed with UNDEFINED_SIZE_T and the CAF_ASSERT aborted. Guard the all-cells branch with hasResultEntry() so an empty histogram is produced instead.
In Rim2dIntersectionViewCollection::syncFromExistingIntersections the
branch reusing an existing view dereferenced the RimGridView ancestor
without a null check, while the branch creating a new view already
guarded it with "if ( view )".
Intersections created without a RimGridView ancestor (for example via
the gRPC/Python API on a detached intersection collection) have a null
ancestor, causing a crash on the cellVisibilityChanged disconnect/connect
calls. Guard these calls with the same null check used in the new-view
branch and always push the existing view back into the collection.
Enable the standard tree Delete action on RimFaultDistance via
setDeletable( true ). On deletion, remove the generated FAULTDIST result
from the case in the destructor; the object is still attached to the
project tree while the destructor body runs, so the owner case is
reachable.
Replace the transient Data Analytics tree label with a real
RimDataAnalyticsCollection object that owns the case-level fault distance
collection and the well target mappings. The folder shows its analytics
objects flattened and exposes a right-click menu to create a new Fault
Distance and a new Well Target Mapping.
Migrate case-level well target mappings from older project files into the
new collection via initAfterRead; fault distance is unreleased and needs
no migration. The case accessors faultDistanceCollection() and
addWellTargetMapping() now forward to the new object, and the creation
features resolve a selected Data Analytics folder.
The Data Analytics folder is built in RimEclipseCase::defineUiTreeOrdering and
is hidden while the fault distance collection is empty. Adding the first result
only refreshed the view (or the collection itself), not the case, so the case
tree ordering was never re-run and the folder stayed hidden until an unrelated
event rebuilt the tree.
Refresh the owner case from the collection whenever items change, mirroring the
RimDataFilterCollection precedent. onItemsChanged adds the folder when the first
result appears, and onChildDeleted removes it when the last result is deleted.
Carries the "Fault Distance" rename through to the C++ class names, file
names, PDM/scriptable keywords and the Python API (add_fault_distance,
class FaultDistance). The RicNewFaultDistanceResultFeature command class
keeps its name.
RimCase::timeStepDates() returns std::vector<QDateTime> by value, so
calling cbegin() and cend() in separate invocations produced iterators
into two different temporary vectors. Passing these mismatched iterators
to std::find is undefined behavior and walked off freed memory, crashing
in setValidTimeStepRangeForCase().
Snapshot timeStepDates() into a single local vector and use its
iterators. The same pattern was fixed in RimWellConnectivityTable.
Reloading an Eclipse grid file destroys and recreates RigEclipseCaseData and its RigCaseCellResultsData. The contour map projection caches raw pointers to this case data in updateGridInformation() and only refreshes them when the projection is rebuilt. Previously the projection was reset on reload only when a GENERATED result was selected, so for any other result type the next scheduled redraw dereferenced the freed RigCaseCellResultsData in generateResults, crashing in hasResultEntry. Reset the grid mapping for all contour map views on reload so fresh case data pointers are captured.
The combined filter node had no Delete entry in its right-click menu. The generic RicDeleteItemFeature only enables when isDeletable() is true, which defaults to false; every other cell filter opts in via setDeletable(true) in its constructor, but RimCombinedFilter omitted it. Add the call so the Delete item is offered, and assert it in the unit test.
RimEclipseView::onLoadDataAndUpdate() can be called again after a failed grid
load has cleared the view's eclipse case (RicNewViewFeature calls
loadDataAndUpdate once more after the view is added). On that second call the
open guard is skipped because eclipseCase() is already null, and the method then
dereferences eclipseCase() directly via dataFilterCollection(), crashing with a
null this pointer.
Return early when eclipseCase() is null after the open block, since there is
nothing to load without a case.
The Completion Type result was registered only as a dynamic result, which
requires simulation time steps. Loading only grid geometry (e.g. a ROFF grid)
therefore made the result unavailable, since there are no time steps to compute
it for.
Register and compute a STATIC_NATIVE variant of Completion Type in addition to
the existing DYNAMIC_NATIVE one, computed as a single time-step-independent
frame. The shared per-frame computation is factored out of
computeCompletionTypeForTimeStep so both variants reuse it. The virtual
perforation transmissibilities already handle the no-restart case by clamping to
a single frame, so the static result can be computed at calc time step 0.
The legend/category predicates and the completion-type result clearing are
broadened to recognise the static variant so it renders with the same discrete
named-category legend as the dynamic one.
A file-based well path whose source file is missing has a null wellPathGeometry(). Add null checks at the remaining call sites that dereferenced it without one, so operations on such a well path no longer crash:
- RimWellPathGroup: skip geometry merging for a child with no geometry (grouping runs on import and project load).
- RimWellPathFracture::computeFractureDirectionNormal: return undefined when geometry is missing.
- RivWellPathPartMgr: extend the existing well path null checks to also require geometry in the attribute, well measurement, and valve append paths.
- RimCompletionCellIntersectionCalc, RicExportLgrFeature, RicCreateMultipleWellPathLaterals, RigLasFileExporter, RiuWellPathComponentPlotItem: guard the geometry before use.
Sites that were already guarded or only reachable through guarded paths were left unchanged.
When a project references an RMS well path file that no longer exists, the well path geometry is null. The MSW completion export validated the eclipse case, well path, and MSW parameters but not the geometry, so generateCellSegments asserted and crashed when triggering Export Completion Data for Visible Well Paths.
Guard the single MSW export dispatcher extractSingleWellMswData with a well path geometry null check that returns an error, protecting all export callers. Harden generateCellSegments to return empty instead of asserting on null geometry.
Also log a warning on the interactive well path import path when a file cannot be read, matching the existing project-load behavior.
Fixes#14121.
The tree selection editor can grow to a large height, which is useful when it is the last field editor in the property dialog. When other field editors are located below, the heightHint is now used as the maximum height instead. The form layout tracks whether an editor occupies the bottom-most row of the form and passes this to the editor. In the Analysis Plot, the time step selection is constrained to its height hint because other input fields are located below it.
The Event Timeline stores event timestamps with full time-of-day precision, but the exported SCHEDULE deck only emitted DAY/MONTH/YEAR in the DATES keyword, dropping any sub-day precision the user had set.
Emit the optional TIME field (HH:MM:SS[.SSS]) in datesKeyword() whenever the timestamp is non-midnight, formatting with millisecond precision when present. Date-only events keep their previous DAY/MONTH/YEAR-only output. The first-date comment in the schedule generator mirrors the same time so it stays consistent with the keyword.
A grid created via the create_corner_point_grid API populated the internal
active cell info but never created an ACTNUM result property, so the grid did
not expose ACTNUM the way a grid loaded from an Eclipse file does. Since the
corner point case has no reader interface, the data cannot be loaded lazily and
is now materialized as a STATIC_NATIVE result sized per active cell, mirroring
native Eclipse ACTNUM.
Guard the remaining unguarded legendConfig() uses so a null legend config
cannot crash: both updateLegend() implementations (eclipse and statistics
projections) now capture the pointer and return early when null, and the
statistics view tree ordering skips a null legend config.
Selecting a contour map projection in the tree could crash when the
associated legend config was null. RimContourMapProjection::defineUiOrdering
called legendConfig()->uiOrdering() unguarded. Because PdmUiObjectHandle::uiOrdering
is non-virtual, the call entered with a null this and then crashed on the
virtual defineUiOrdering dispatch.
Guard the legendConfig() dereference, matching the existing null checks
elsewhere in the file. Apply the same guard to RimEclipseCellColors::defineUiOrdering.
The case-level Data Filters node (RimDataFilterCollection) was always shown under
RimEclipseCase, cluttering the tree when empty. Gate it in
RimEclipseCase::defineUiTreeOrdering on a new shouldBeVisibleInTree() check, and
refresh the owner case from onItemsChanged/onChildDeleted so the node appears and
disappears as the collection crosses the empty boundary.
Because a hidden node cannot be right-clicked to create the first filter, add a
RimEclipseCase::appendMenuItems override that exposes the filter-creation
commands as a Filters submenu on the case node. A shared
RicCellFilterFeatureTools::selectedDataFilterCollection() resolves a selected
RimEclipseCase to its data filter collection, so the cell, property and combined
filter create features target the case-level collection.
The view filter nodes were unified under a read-only RimFilterInViewCollection
facade, and the source collections are now tree-hidden. As a result, the
existing collection->updateConnectedEditors() calls no longer refresh the tree:
the filters are shown under the facade, which only refreshes when a source emits
filtersChanged/wrappersChanged.
Several property-filter mutations changed the child array without emitting that
signal (insertAt/push_back/erase do not auto-fire onChildAdded/onChildDeleted),
so newly created or removed filters did not appear under the Filters node:
property filter insert, new combined filter, add linked filter, undo of a
property-filter creation, and Show Contributing Wells.
Emit the change signal from these paths (via the existing onChildAdded hook, or
filtersChanged on removal) so the single facade handler refreshes the tree. The
cell-filter, paste, property-add and data-filter paths already emit the signal
and are unchanged.
Gate the OSDU well log import menu item and the command undo/redo history view
behind the discoverable experimental features registry instead of
RiaApplication::enableDevelopmentFeatures(), so they can be toggled from
Preferences -> System without setting the RESINSIGHT_DEVEL environment variable.
Register the 'osdu-well-logs' and 'undo-redo-view' features in
RiaExperimentalFeatures and gate the code paths via
RiaPreferencesSystem::isFeatureEnabled().
Add an align_columns option to schedule generation. When enabled, each
keyword is serialized with a '--'-prefixed column-header comment and data
rows right-aligned into fixed-width columns, instead of the compact default
form. The default output is unchanged.
A new RimKeywordFactory::deckKeywordToAlignedString introspects the OPM deck
model and renders each item in its own column (consecutive defaults are not
collapsed into 'N*'). The flag is threaded through RicScheduleDataGenerator
and exposed on the GenerateSchedule scriptable method as align_columns in the
Python generate_schedule_text wrapper.
Reparent RimFaultDistanceResultCollection so it is owned directly by
RimEclipseView instead of RimFaultInViewCollection, and show it as a
top-level node next to the Faults node in the project tree.
RimFaultDistanceResult::calculateValueOptions now reaches the fault list
through the view (the fault collection is a sibling rather than an
ancestor after the move). RicNewFaultDistanceResultFeature resolves the
view from the selection and takes both the distance collection and the
all-faults fallback from it.
The scriptable API moves with the collection: add_fault_distance_result
is now exposed on RimEclipseView (view.add_fault_distance_result(...))
via the new RimcEclipseView, replacing RimcFaultInViewCollection.
Expose the new RimFaultDistanceResult facility to Python by marking the
relevant PDM classes and fields scriptable. RimEclipseView's
m_faultCollection, RimFaultInViewCollection, RimFaultInView,
RimFaultDistanceResult, and RimFaultDistanceResultCollection are now
scriptable, so faults can be enumerated from Python via
view.fault_collection().faults().
A new Rimc method add_fault_distance_result on RimFaultInViewCollection
takes a name and a list of fault objects, creates the result
(auto-numbered FAULTDIST<n> when the name is empty), and returns the
new RimFaultDistanceResult. An empty fault list falls back to every
fault in the view, matching the static-native FAULTDIST behaviour.
Add a Python test exercising both a subset call and a parity check
that asserts the all-faults result matches the existing static-native
FAULTDIST cell-for-cell.
The existing FAULTDIST result always considered every fault in the main
grid. Users with many faults need to compute distance fields against a
named subset, so this adds a Fault Distance Results collection under
each view's Faults node. Each entry holds a multiselect of faults and a
name (FAULTDIST1, FAULTDIST2, ...) and publishes the result into the
Generated cell-result category.
The per-cell BVH-based distance loop was extracted from
RigFaultDistanceResultCalculator into a reusable utility that accepts
the subset of faults to include; the original all-faults entry point
delegates to the same utility and keeps the static-native FAULTDIST
behavior unchanged.
On project reload the simulation well disks were not drawn until the user
toggled the Simulation Wells visibility. Two load-ordering issues caused this:
scaleWellDisks() ran before synchronizeWellsWithResults() had populated the
wells list, so it iterated an empty collection. It also bailed out early on the
transient m_showWellDisks aggregate (disableIO), which is only synced from the
restored per-well states when the property panel is built.
Move scaleWellDisks() after synchronizeWellsWithResults(), and sync the
visibility checkbox state at the start of scaleWellDisks() so the disks are
scaled and marked valid before the first display-model build.
Normalize disk radius over all wells instead of only the currently
visible ones. Computing the min/max range over visible wells only made
one well's disk radius depend on whether unrelated wells were filtered
out, so toggling a range filter on one well changed another well's disk.
Fixes#14083.
Add a completion_number parameter to the add_perf_event Python API
(AddPerfEvent scriptable method) and a matching field on
RimWellEventPerf. When a timeline perforation event is materialised into
a RimPerforationInterval, a non-zero completion number is copied across,
so it flows through the existing completion-data path onto
RigCompletionData.
The schedule generator now emits COMPLUMP alongside COMPDAT: COMPDAT
generation is folded into generateCompletionsForWell, which computes the
completion data once and builds both keywords from it via the existing
RimKeywordFactory::complumpKeyword (added in #13273). COMPLUMP only
produces records for perforations that have a completion number and is
merged across wells like COMPDAT.
* Agent docs: Recommend cmake --build over ninja when sharing build/ with Visual Studio
* Disable Create Well Target Mapping for grid ensembles with individual grids
* Move RimSummaryCaseMainCollection context menu items into appendMenuItems override
* Reorganize import menus: split per-window, group submenus, rename actions, add Import/Export icons
* Show icons on "New Statistics Case" and "New Grid Case Group" menu actions
* Rename grid/summary import submenus to "More" and use the import icon
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.
Adds a collection-level checkbox in the simulation well "Visibility"
group that hides wells which are not open at the current time step.
Mirrors the existing "Wells Through Visible Cells Only" sibling toggle
in placement, default (off), and contour-map gating.
The frame-aware predicates isWellPipeVisible, isWellSpheresVisible,
isWellValvesVisible and isWellDiskVisible now short-circuit to false
when the toggle is on and RigSimWellData::isOpen(frameIndex) is false,
placed before the active-intersection bypass so the user's filter is
not overridden.
Fixes#13913
Production disks were drawn for wells that the visible-cells filter
hides, because RimSimWellInView::isWellDiskVisible() never consulted
the showWellsIntersectingVisibleCells toggle.
Add a frameIndex parameter and the same active-intersection / visible-
cells filter logic that isWellPipeVisible already uses, and update the
two call sites to pass the current frame.