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.
RimMultiPlot::recreatePlotWidgets() dereferenced the viewer unconditionally. The viewer does not exist in console mode, so deleting a summary plot from Python through the generic PdmObject delete() crashed ResInsight in onChildDeleted(). Return early when there is no viewer, matching the other viewer accesses in RimMultiPlot.
Add Python regression tests for deleting a single summary plot inside a multi plot and for deleting a whole multi plot, and an example showing how project tree objects are removed with delete().
The grid calculator releases result data per time step to reduce memory
usage when calculating for many cases. If only some time steps are
released, findOrLoadKnownScalarResult() returned early because data was
present for other time steps, leaving the released time steps empty.
Results computed by ResInsight, like riOILVOLUME, then appeared as
undefined in the 3d view after running a calculation for a selected
time step.
Recompute results computed by ResInsight when data is missing for any
time step. Skip the delegation from the single time step variant when
the requested time step already has data, to avoid repeated
recomputation while the calculator releases other time steps.
findOrLoadKnownScalarResultForTimeStep() tried to read ResInsight-
calculated results like riOILVOLUME from the restart file. For lazily
loaded grid case group members these results are never computed, so
every cell ended up undefined and the grid calculator produced zero
sums for all additional cases.
Delegate to findOrLoadKnownScalarResult(), which dispatches to the
result calculators, for results flagged as must-be-calculated and for
the ri* volume results. Already computed data is not computed again.
Three related fixes found while reviewing the feature:
- Clicking the contour map surface inside a regular 3d view selected the internal RimContourMapProjection in the project tree instead of the RimContourMapInView object. Resolve the actual view from the pick event rather than climbing the projection's own ancestor chain, which pointed to a hidden internal contour map view, and select the owning RimContourMapInView when found.
- RimContourMapInView::name() returned only the raw custom name of the underlying contour map view. RimStatisticsContourMapView leaves its custom name empty and builds the displayed name entirely from generated tags, so the name shown for a statistics contour map in a 3d view was empty. Use the view's auto-generated name instead.
- RimContourMapInView is a presentation wrapper around a hidden RimEclipseContourMapView (or RimStatisticsContourMapView) that owns the actual contour map data. Add a Go to Contour Map button, following the existing pattern used by RimPolygonInView and RimDataFilterInView, so the underlying source object can be quickly selected in the project tree.
Give RimGridView a RimContourMapInViewCollection and let RimEclipseView and RimGeoMechView build their 3d model from it. A dedicated contour map view (RimEclipseContourMapView/RimStatisticsContourMapView) shows its own contour map and returns no collection of its own, so it is unaffected. Adjust RimSurfaceInViewCollection so surfaces and contour maps coexist in the same 3d view without z-fighting.
Add the data model for showing contour maps inside a regular 3d view. RimContourMapInView is a presentation wrapper that decides where a contour map is placed vertically in the 3d scene and what parts of it are drawn; the contour map itself, including the result, the aggregation and the computed geometry, remains owned by the contour map view it points at. RimContourMapInViewCollection owns the set of contour maps available in a 3d view. Add a CONTOUR_MAP entry to RiaDefines::ItemIn3dView so the project tree can be refreshed for just the contour map items.
Add RivContourMapElevationProvider and extend RivContourMapProjectionPartMgr so the contour map surface and contour lines can be projected onto the terrain/grid geometry of the 3d view instead of only being drawn as a flat map.
RicNewSummaryEnsembleCurveSetFeature::createPlotForCurveSetsAndUpdate() always
called RiaGuiApplication::instance(), which is null when running as a console
application. Calling new_summary_plot() with an ensemble and no address string
from Python did therefore terminate ResInsight. Only access the plot main
window when the GUI is running.
new_summary_plot() with an ensemble and an address string created an empty
plot. The address filters were applied to an empty list of summary cases, and
no curve sets were created. Use the summary cases of the ensemble, and make
sure the summary addresses are created before the address filters are applied.
Add example script showing how to create summary plots for an ensemble.
* Add performance unit test for RimEnsembleCurveFilter::applyFilter
* Fix O(N^2) scaling in RimEnsembleCurveFilter::applyFilter
RimEnsembleCurveSet::objectiveFunctionTimeConfig() was called once per
summary case inside the applyFilter() loop, for both the OBJECTIVE_FUNCTION
and SUMMARY_VALUE filter modes. Internally it calls allAvailableTimeSteps(),
which scans the full time series of every case in the ensemble, so
recomputing it per case turned applyFilter() into an O(caseCount^2) operation.
* Avoid redundant allAvailableTimeSteps() calls in fullTimeStepRange()
fullTimeStepRange() called allAvailableTimeSteps() three times (once for the
empty check, once for min, once for max), each re-scanning the full time
series of every case in the ensemble. Compute it once and reuse the result.
Also simplify allAvailableTimeSteps() to insert time steps directly into the
result set instead of collecting them into an intermediate vector first.
COMMENT is accepted on all keywords and is stored on the event instead of
being passed as keyword data, so it is not part of the item names reported
by opm-common. The validation error listed only the opm-common items, giving
no hint that COMMENT is allowed when a user mistyped it as COMMENTS.
Append COMMENT to the list of valid item names in the error message. Only the
diagnostic is affected, COMMENT is still handled as event metadata.
Include files containing only INCLUDE statements, such as a wrapper
collecting many lift curve (.ecl/.vfp) files, were dropped during
sector model export, and the includes for all leaf files were written
directly into the exported .DATA file.
Update the custom-opm-common submodule so FileDeck recreates
include-only files in the output directory and preserves the include
hierarchy at any nesting depth. Add a regression test exporting a deck
with a nested include-only wrapper and reloading the result.
Table keywords such as PVTO and SGOF were rewritten by opm-common as one
unbounded line when saving the deck, exceeding the 132 character line
limit of Eclipse 100 and making exported sector models unusable there.
Update the custom-opm-common submodule so DeckOutput tracks the current
line width and wraps records at item boundaries before a line exceeds
132 characters. Quoted values are never split. Add a regression test
that round-trips a deck with a long rel. perm. table and a record of
quoted mnemonics.
Sector model export removed all ACTNUM and FAULTS keywords from the deck
and re-inserted the sector-specific replacements at the end of the GRID
section, which always lands in the main DATA file. Decks that keep these
keywords in separate include files lost their file organization, leaving
the original include files empty while the data was written inline.
Collapse duplicates to a single occurrence instead of removing all
occurrences, so the subsequent in-place replacement keeps the keyword in
its original file when the deck is saved with OutputMode::COPY. ACTNUM
keeps the last occurrence since a later ACTNUM overrides an earlier one;
FAULTS keeps the first so fault definitions do not move past keywords
referencing them.
RigWellTargetMappingTools accessed cellScalarResults() and result vectors without verifying the requested time step or index was within bounds, and without checking for null case data pointers. Add the missing guards.
std::clamp() invokes undefined behavior when the low bound is greater
than the high bound. In resetMinimumCellValuesToDefault(), the
minimum/maximum bounds for saturation, pressure, permeability and
transmissibility are derived from case data and can end up inverted
when no data is available, which crashed or produced garbage values.
The branch number of a fracture followed the order the fractures were created, so appending a second set of fractures to a well put them after the existing ones regardless of where along the well they sit. Sort by the fracture start MD instead. The order also decides which fracture connects a grid cell shared by two of them.
A grid cell has a single COMPDAT connection and must be connected to exactly one segment, but the geometry-based export emitted one COMPSEGS row per branch intersecting the cell. Restore the well-global bookkeeping the removed tree path had: tag each branch with its origin and let the branch carrying most flow claim a shared cell, perforations before fishbones before fractures.
Selecting a curve or a plot inside a RimHistogramMultiPlot in the project
tree now scrolls the multi plot view to make the containing histogram plot
visible, mirroring the existing behavior for RimSummaryMultiPlot.
When creating a histogram from grid cell results (e.g. PERMX) via
"Create Grid Statistics Plot", zoom the histogram plot to fit the data
right after the curve is populated, instead of leaving it at default axis
ranges.
Reverts the unconditional change from commit 74ffc822f2 and replaces it with
a safer fix: only skip re-initializing the well path name from the source
file on reload when a name has already been set. Older projects that never
serialized the well path Name field (e.g. TestCase_RFT_PLT/RegressionTest.rsp)
still get their name populated from the file on load, while renamed well
paths in newer projects keep their custom name across reloads.
Restores the RMS well round-trip test that verifies a renamed well path
name persists after save/reopen.
Add RicImportOrionEventsFeature to File -> Import -> Well Data and the well
path collection context menu. The feature opens a file dialog and launches
the Python interpreter asynchronously with the new --apply mode of the
rips.orion_events CLI, which connects back to the running instance through
RESINSIGHT_GRPC_PORT and applies the events to the well event timeline.
Output is shown in the Process Monitor panel.
A case in an ensemble can have fewer time steps than the case defining the time step axis of a statistics case. The time step index was used to index m_cellScalarResults directly, reading past the end of the vector in Release where CAF_ASSERT is a no-op.
Summary cases are created and given a case id before they are added to the
project. The id search only sees cases already in the project, so every case
in a batch import was given the same id.
The Data Sources tree stores the owning case id in each summary address, and
all lookups resolve to the first case with that id. Selecting the same vector
from several cases therefore produced a single curve, and dropping a vector
from one of the other cases hit the duplicate check and did nothing.
The single-pass sum-of-squares formula subtracts two large and nearly equal numbers. Where a result such as PORO is identical or near-identical across the realizations, the true variance is below the rounding noise floor, the radicand turns negative and sqrt() returns NaN.
Compute the deviation in two passes instead, centering on the exact mean. This also removes the loss of significance the old formula had for values with a large common offset.
Min, max, range, mean and deviation all report HUGE_VAL when the input holds no valid values, but the sum reported the zero it was accumulated from. A caller testing the result with isValidNumber would accept that zero as real data.
Reading a caf::PdmPtrField sets the pointer to null before the reference string is resolved, and clearing a ptr field disconnects all signals between the referenced object and the field owner. The signal connections established by RimFilterInViewCollection::setSourceCollections() in the view constructor were therefore lost when a project was loaded, and adding a cell filter, property filter or data filter no longer refreshed the Filters node in the project tree. Reconnect in initAfterRead, which runs after the references have been resolved.
The toolbar is only shown when the active plot view window is a multi plot. When a child object is selected, the first ancestor view window is the sub plot, so the toolbar was cleared and hidden.
Use the summary multi plot as active plot view window when any object inside the multi plot is selected.
Viewer::setView() moves the camera to look straight at the point of interest. Panning does not move the point of interest, so this discarded the camera position restored from the project file.
Only reset the view direction if the camera is not already oriented top-down, which is the case when a contour map has been rotated by a linked 3D view.
Use a file dialog caption matching the imported file type instead of always showing "Import Grid Model".
Parent the summary vector selection dialog to the active window, so closing it does not raise the 3D main window in front of the plot window when opened from the Summary Curve Calculator.
The map size of the sample grid was recomputed from the expanded bounding box and the sample spacing stored in the project file. The expanded bounding box is an exact multiple of the sample spacing, but after the limited precision of the project file the division can land just above a whole number of cells, and the map size gets one extra cell. The cache files were then rejected, and the ensemble statistics recomputed.
Store the map size in the project file, and use it directly when the sample grid is restored from the cache.
Identify the input grids of the cache validity key by file path, size and modification time, so that the cache is also invalidated when a grid file is modified in place. Label and count the primary case and the ensemble cases, so that a primary case can not be mistaken for the first ensemble case.
A refinement level with a single reconstructed LGR keeps the bare level name (LGR_NHG_L2). A level with several LGRs now numbers all of them with 1-based suffixes (LGR_NHG_L4_1 .. LGR_NHG_L4_N) instead of leaving the first one without a suffix.
Older versions of ResInsight depend on this keyword to create the window controller for plot windows. 3D views work with both keywords, because Rim3dView creates the controller in its constructor, while plots only get one from the project file. DockWindowController is kept as a read alias so project files written by recent dev builds still load.
The renamed well path name is serialized correctly, but reopening the project reloads the source well path file and overwrites the saved name with the name from that file. The reload flag only protected the completion export name, despite the loader also updating the displayed well path name.
Only initialize the well path and export names from the source file during first import. Subsequent data reloads preserve the names stored in the project while continuing to refresh geometry and metadata.
Add an RMS well round-trip test that renames an imported well, saves and reopens the project, and verifies that the custom name is retained.
Move the call to cellCornerVertices below the K filter early out, so the corner coordinates are only computed for the cells that are actually tested against the polygon.
All unnamed critical regions map to the same implicit name, so a single process wide lock was shared by summary import, geometry generation, grid bounding box computation and NNC merging. Unrelated parallel loops therefore serialized against each other.
Give each region a critical_section_ name describing what it protects. The two regions guarding RifOpmCommonEclipseSummary::sm_createdEsmryFileCount deliberately share one name, as they protect the same counter.
All four cell filter loops guarded a push_back into a shared container with an unnamed critical section, taking a process wide lock for every cell matching the polygon. On large grids with a permissive polygon this can be slower than running single threaded, and it made the resulting cell order vary between runs.
Collect the cells in per thread buffers and append them in thread order after the parallel region, following the pattern used elsewhere in the code base. This removes eight critical sections.