The group boxes were destroyed with a raw delete, which destroys the child widgets immediately and defeats the deleteLater() used by PdmUiFieldEditorHandle to keep field editor widgets alive. When the rebuild is triggered from fieldChangedByUi(), Qt is still using those widgets further up the stack, giving a use-after-free.
Hide and detach the group box before scheduling a deferred delete, so it leaves the layout, the visual tree and the focus chain immediately. Detaching first is what plain deleteLater() did not do in issue 9719.
The tool button and the layout are owned by m_placeholder, which is not returned as the editor widget when auto value is not supported. Nothing tracks m_placeholder in that case, so it can be destroyed together with a parent widget while the reparented m_lineEdit survives. A later configureAndUpdateUi() then found a non-null m_lineEdit and a null m_autoValueToolButton, and crashed on hide().
Guard both pointers before use, matching how m_label and m_lineEdit are already guarded in the same function. This is a separate defect from the use-after-free in issue 14505, found while reproducing it in cafTestApplication.
Adds a ReentrantEditorRebuild demo object that rebuilds all property editors from inside fieldChangedByUi(), while Qt is still executing QLineEdit::focusOutEvent. Type a value into Realization Filter and press Tab to trigger. The change handler drops both the line edit field and its parent group from the ui ordering, so PdmUiFormLayoutObjectEditor::configureAndUpdateUi() tears down widgets that are still in use further up the stack. Uncheck Rebuild Layout On Change to run the same edit without the reentrant rebuild.
Declare FILTER HIPORO with a qualified result name (static.PORO) and
reference it from the first perforation, passing case= to the applier
and printing the attached filter per materialized perforation interval.
The FILTER parts are included only when the project has a loaded case,
so the example keeps working in caseless projects.
Add a FILTER declaration (referenced by two perforations) and an inline
filter expression to the shipped well_events.orion sample, and extend
import_orion_events.py to pass case= to the applier and list the
combined data filters created from the declarations. Skip schedule
generation gracefully when no events were applied, and give FilterExpr
a __str__ returning the raw expression so variable listings stay
readable.
Add a typed FILTER declaration to the ORIONEVENTS 2.0 grammar, e.g.
FILTER POROPERM = "PORO > 0.4 AND PERMX > 100.0". A PERFORATION event
references it with FILTER=POROPERM or supplies an inline quoted
expression. When applied, each used filter is materialized as a
case-level combined data filter (one property filter per term, one-sided
inclusive bounds, AND/OR combine mode) and attached to the perforation.
Unqualified result names are searched in STATIC_NATIVE, DYNAMIC_NATIVE
then GENERATED results; a TYPE. qualifier restricts the search. Missing
results raise before any event is applied. apply_orion_document gains an
optional case= parameter defaulting to the project's first case.
RimWellEventPerf carries a RimCellFilter ptr-field with scriptable
AddFilter/cell_filter methods, and the filter is copied onto the
RimPerforationInterval when the timeline materializes completions.
Qt is distributed both with and without pdb files. When they are present,
copy them next to the Qt dlls so symbols are available when debugging.
Pass --pdb to windeployqt for the configurations built with debug
information, and copy the matching pdb file in the AppFwk test
applications and unit tests using a helper script that skips missing
files.
deleteSummaryCaseCollection() looped over every case in the ensemble, and for each case scanned every summary plot and called updateConnectedEditors() on every multi plot. For an ensemble with many realizations this scanned each plot once per realization, and updated the connected editors once per realization and multi plot. This showed up as a hot spot in the profiler.
Replace deleteCurvesAssosiatedWithCase() with deleteCurvesAssosiatedWithCases(), which takes a set of cases and returns whether anything was deleted. Each plot is now scanned once, and the connected editors of a multi plot are updated only when that multi plot actually lost a curve.
RicCloseSummaryCaseFeature and RicCloseObservedDataFeature are updated to the new signature. RicCloseSummaryCaseFeature also scans each plot once instead of once per case.
The destructor called updateReferringCurveSets(), which runs loadDataAndUpdate() on every referring curve set. At that point the summary cases are already deleted, so reloading the curve data has nothing to read. The reload also syncs UI fields, reloads the curve filters, rebuilds the address list, recomputes statistics over an empty case set and updates three legends, and it calls updateAll() on the parent plot once per curve set instead of once per plot.
Add clearReferringCurveSets(), which deletes the ensemble and statistics curves directly and updates each affected plot once. For an ensemble with many realizations spread over several curve sets in the same plot this removes most of the work done while deleting.
Query the referring objects as RimEnsembleCurveSet instead of walking all referring objects and casting. The previous loop discarded everything that was not a curve set, so the behavior is unchanged.
The lower limit of a logarithmic color range was computed from the value closest to zero, which is not always available. When this value was zero, log10() returned -inf and the lower limit ended up as zero. Use the closest power of ten below the lowest value instead, and use the value closest to zero only when the lowest value is zero or negative. Guard computeTenExponentCeil() and computeTenExponentFloor() against zero, and make sure the range always spans at least one decade.
A new contour map created from the right-click menu on a case used a hardcoded SOIL result, resulting in an empty view when SOIL was not present in the case. Use the cell result of an existing view for the same case if one is available, and otherwise fall back to RigCaseCellResultsData::defaultResult(), which verifies that the result exists before selecting it. Completion Type is a category result and is now excluded as a default result for both 3d views and contour maps.
Use I as the default direction when creating an IJK intersection, and center the fixed index on the I axis.
Allow multiselect of surfaces for a Surface Curve. The field keyword is changed from Surface1 to Surfaces, with a keyword alias so existing project files are read as before. One curve is drawn per selected surface, each with its own label and color.
Use the color defined in the Surfaces collection for Surface Band and Surface Curve. The color is shown as read only, and a Custom Color option allows the user to specify a color. Bands created from an ensemble surface keep the structural uncertainty colors, as the statistics surfaces share the same color.
RigAllGridCellsResultAccessor bounds checked the cell index with an assert that is compiled out in release builds, and then called std::vector::at(), which throws. RimGridCalculation::getActiveCellValues() calls the accessor from an OpenMP loop, using active cell indices from the destination case to read results from the source case, so an index beyond the source result vector threw an exception that could not escape the parallel region and called std::terminate. Return undefined for an out of range index instead, and guard against a null result accessor.
computeRiTransComponent() checks the transmissibility index and the neighbor
cell permeability index for cvf::UNDEFINED_SIZE_T, but not the permeability
index of the cell itself. The transmissibility and permeability results are
indexed independently, so a defined transmissibility index does not imply a
defined permeability index. For a case where transmissibility is stored per
cell and permeability per active cell, an inactive cell passes the
transmissibility check and then indexes the permeability vector with
UNDEFINED_SIZE_T.
Check the permeability index of the cell before use, matching the guard in
computeNncCombRiTrans().
loadMainCaseAndActiveCellInfo() calls openAndReadActiveCellData() for each
source case in a grid case group. That function returns false before
setReservoirData() when the grid file is missing, and the loop only continues,
so the case stays in the collection with a null eclipseCaseData().
computeUnionOfActiveCells() then iterates every reservoir and calls
eclipseCaseData()->activeCellInfo() on it, which reads m_activeCellInfo off a
null case data and crashes.
Collect the source cases that have case data once, before the cell loops, and
iterate those. This also removes the per-cell case lookup from the innermost
loop.
transferStaticNNCData() takes the connection count from the INIT file using
ecl_nnc_data_get_size(), but iterates the geometry from the grid file using
ecl_nnc_geometry_iget(). The two counts were only compared in an assert, which
is compiled out in release builds, so an INIT file out of sync with the grid
reads past the end of the geometry vector inside resdata.
Compare the counts at run time, log a warning and iterate the smaller of the
two. Both nncConnections and transmissibilityValuesTemp are filled in the same
loop and stay aligned.
addRftCurve() selects the first Eclipse result case in the project without
checking that it has an RFT reader, and then dereferences rftReader(). The
command is enabled by hasRftData(), which returns true if any case has a
reader, so with several Eclipse cases loaded an enabled menu entry can select
a case with no RFT data and crash.
Null check the reader before use, matching the guard already used by
hasRftData() and hasRftDataForWell().
The COMPSEGS rows were emitted in the order the branches are listed in, so they followed the WELSEGS order where the completion branches come before the laterals with a lower branch number.
The rows are now collected while the segments are visited and sorted by branch number before they are added. The sort is stable, so the rows of a branch keep their order along the well path.
Branch numbers are handed out from two counters. The well path laterals are numbered first, so that the main bore and the laterals occupy the lowest branch numbers. The completion branches (valves, fractures and fishbones) are numbered after all laterals, and are still listed immediately after the lateral they are connected to.
The multiple_laterals test project is reduced to a single Eclipse case, and is used by two new unit tests covering the branch numbers and the order the branches are listed in.
RiaCurveMerger::addCurveData and RiaWellLogCurveMerger::addCurveData only
validated that the X and Y vectors have the same size with CAF_ASSERT, which is
compiled out in optimized builds. Mismatching sizes are a legitimate run-time
condition for data read from file, so the check has to hold in release builds as
well.
In RiaCurveMerger the shared-X fast path in computeInterpolatedValues() indexes
the Y vector of every curve using the sample count of the first curve. A curve
with identical X values but fewer Y values therefore read past the end of its
heap buffer inside the OpenMP loop. RimEnsembleStatisticsCase passes time steps
and values straight from the summary reader without reconciling the sizes, and
realizations in an ensemble normally share time steps, so this was reachable for
an ensemble containing an ongoing simulation.
Both mergers now truncate the incoming data to the common sample count instead.
For RiaWellLogCurveMerger this is also an improvement in behaviour, since
lookupYValue() used to discard the whole curve when the sizes differed.
Add unit tests covering a single curve with fewer values than time steps and
curves with shared time steps where one curve has fewer values.
When several perforation intervals overlap the same grid cell, the main bore COMPSEGS table emitted one row per interval. Two perforations placed back to back therefore reported the same IJK twice, while COMPDAT correctly reported a single connection for that cell.
The candidate intersections collected for one cell all describe the same IJK, so they are now merged into a single COMPSEGS row spanning the union of the overlapping measured depth ranges.
Add TestModels/msw-export/project-files/perf_two_back_to_back.rsp covering the case.
Deleting PDM objects on a worker thread is not safe. PdmObjectHandle::prepareForDelete()
mutates state owned by other objects, it nulls the guarded pointers held by other objects
and clears m_pointersReferencingMe, an unsynchronised std::set that every PdmPointer
construction and destruction touches. Destroying an object off the main thread therefore
races with the main thread on the shared object graph. See issue 14491.
Profiling a summary ensemble teardown shows the mechanism does not pay for itself. Releasing
397 Drogon realizations takes 0.24 s sequentially and 0.24 s in parallel, and for a heavy
case the parallel release is slower than the sequential one, because free() is serialised
inside the allocator. The PDM bookkeeping itself is 0.4 to 2.8 percent of the teardown.
Remove the class and deleteChildrenAsync(), and delete synchronously instead. The call sites
that used clearWithoutDelete() and a manual delete loop to work around the race can now call
deleteChildren() directly. Add caf::PdmObjectHandleTools::deleteObjects() for the case where
the objects are no longer owned by a child array field.
The observer disconnection from issue 12262 does not depend on clearWithoutDelete(). ~Signal()
unregisters itself from every observer, so a deleted child detaches itself. That fix addressed
the async race, where ~Signal() mutated the observer list from a worker thread. The unit test
is updated to assert the observed signal count directly instead of relying on a crash.
Remove 95 cvf include lines that the including file does not use. Found by
extracting the symbols declared by every VizFwk header, including symbols they
re-export, and flagging includes where none of those symbols appear anywhere in
the file. Each removal is verified by a full build.
Most of them are cvfVector3.h and cvfObject.h, left behind as the files they
were once needed by changed.
pch.h is left untouched. It includes cvfObject.h and cvfVector3.h on purpose, so
that the files using the precompiled header do not have to.
Note that a clean build after removing an include does not prove the include was
unnecessary, only that the declarations still arrive some other way. That path
can differ between platforms and with RESINSIGHT_ENABLE_UNITY_BUILD, so this
needs a CI round on Linux as well.
cvfBase.h includes cvfAssert.h, so these six files were the last indirect route
into the assert header that the previous commit removed from ApplicationLibCode.
Five files dropped the include with no further change. RimSeismicAlphaMapper
declared alphaValue as returning cvf::ubyte, a typedef defined in cvfBase.h
itself, so no smaller VizFwk header provides it. Since cvf::ubyte is
unsigned char, use that directly in the declaration, the definition and the
cast. The type is identical, so callers are unaffected, and the class no longer
depends on VizFwk at all.
Migrate all assert macros in ApplicationLibCode to CAF_ASSERT and remove every
use of cvfAssert.h.
CVF_ASSERT is replaced one to one. CVF_TIGHT_ASSERT is also replaced by
CAF_ASSERT, which is semantically exact: CVF_ENABLE_TIGHT_ASSERTS is 1 only
under _DEBUG, and that is what CAF_ASSERT now does. The two CVF_FAIL_MSG sites
become CAF_ASSERT( false && "message" ), preserving the message with the idiom
already used elsewhere in the code base.
Counts before and after: CVF_ASSERT 1044 to 0, CVF_TIGHT_ASSERT 66 to 0,
CVF_FAIL_MSG 2 to 0, cvfAssert.h references 154 to 0.
Include handling: files that included cvfAssert.h directly now include
cafAssert.h instead, includes left dead by the migration are removed, and files
that were relying on cvfAssert.h transitively get an explicit cafAssert.h. Files
that reach cafAssert.h through another caf header are left unchanged; a missing
include here is a compile error, not a silently disabled assert.
ResultStatisticsCache links only LibCore and therefore had no path to
cafAssert.h. Add the cafPdmCore directory as a private include path rather than
linking the library, since cafAssert.h is header only.
Note that this stops these asserts from firing in Release and RelWithDebInfo,
where CVF_ASSERT was previously active.
cvfAssert.h tests CVF_ENABLE_ASSERTS but did not include cvfConfigCore.h, which
is where that macro gets its default value of 1. A translation unit processing
cvfAssert.h before cvfConfigCore.h evaluated the undefined identifier as 0 and
silently compiled every CVF_ASSERT, CVF_ASSERT_MSG and CVF_FAIL_MSG in that unit
into a no-op, without any warning and without evaluating the expressions.
Because the header uses pragma once, including cvfConfigCore.h later in the same
unit did not recover the macros. Alphabetical include sorting actively produces
the broken order, since cvfAssert.h sorts before cvfBase.h.
Include cvfConfigCore.h before the macro definitions. It only defines macros, has
pragma once and no dependencies, so this is immune to future include reordering.
This turns the asserts back on in the affected translation units, in both debug
and release builds, and immediately exposed one assert that had never compiled:
in RigFemPartResultCalculatorNormalized::calculate, isNormalizableResult was
called unqualified even though it is a static member of
RigFemPartResultsCollection. Qualify it, matching isMatching in the same file.
CAF_ASSERT was unconditionally active in every build configuration. Make it
follow the semantics of the standard assert(): active in Debug, compiled out in
optimized builds (NDEBUG).
Add the CMake option RESINSIGHT_ENABLE_ASSERTS_IN_RELEASE (default OFF) for
developers who want the asserts to stay active in an optimized build, which is
useful when reproducing a problem in RelWithDebInfo with a debugger attached.
When compiled out, the expression is kept inside an unevaluated sizeof rather
than discarded. It is not evaluated, so there is no run-time cost and no side
effects, but it is still type checked and any variable used only by the assert
still counts as referenced, avoiding a wave of unused-variable warnings.
CAF_ENABLE_ASSERTS is given a default in cafAssert.h so the header stays
self-contained and include order can never silently switch the asserts off.
Document in docs/agents/coding-style.md that CAF_ASSERT is the assert to use,
that CVF_ASSERT is legacy, and that asserts are for broken invariants rather
than for run-time conditions that need real error handling.
Relocate import_orion_events.py and well_event_schedule_orion.py from
wells_and_fractures to a new experimental folder, and update the doc
reference in docs/orionEvents.md accordingly.
Roll back the __getattr__ lazy-import hook in rips/__init__.py. The hook
existed only so that a plain import rips would expose rips.orion_events
as an attribute; that eager exposure is also what triggered runpy's
found in sys.modules warning under python -m rips.orion_events.
Instead do not auto-expose the submodule from the package and have the
two examples import rips.orion_events explicitly, which is idiomatic and
warning-free.
Document the ORIONEVENTS file format in docs/orionEvents.md: grammar,
line types, typed declarations, date expressions, WELL and SCHEDULE
blocks, the built-in completion event attribute tables with their
WellEventTimeline mappings, generic Eclipse keyword pass-through,
diagnostics, the validator CLI and Python apply usage.
All code examples in the document are validated against the real parser.
Extend the 2.0 format to cover the full WellEventTimeline API demonstrated
by well_event_schedule.py.
Add built-in TUBING, VALVE and STATE event types mapping to
add_tubing_event, add_valve_event (including the AICD parameters) and
add_state_event, validated with the same required/unknown-attribute
handling as PERFORATION.
Pass any other event type inside a WELL block through generically as an
Eclipse well keyword via add_well_keyword_event with the well name
injected, so WRFTPLT, WCONPROD and the rest work without enumeration.
Event types that closely resemble a misspelled built-in are handled by
the on_unknown_event policy with a did-you-mean hint instead of being
passed through.
Add a SCHEDULE block: a bare SCHEDULE line opens a block of well-less
keyword events (RPTRST, GRUPTREE, TUNING, ...) applied through
add_keyword_event; completion events in a SCHEDULE block are rejected.
Parsed schedule events live in OrionDocument.schedule_events.
Accept ISO datetimes in event dates (@2024-05-15T14:45:30.500); the
time-of-day survives into the generated DATES keyword's TIME field with
millisecond precision. Day-offset arithmetic applies to datetimes too.
Ship well_event_schedule_orion.py, an ORIONEVENTS counterpart to
well_event_schedule.py, and extend the test suite from 55 to 70 tests
including an integration test that applies every event kind and verifies
the generated schedule text.
Replace the 1.0 grammar with a version 2.0 designed to be easier to parse
and easier to write. Variables are now typed: DATE, DURATION (whole days)
and WELL alias declarations replace the untyped SET, and using a variable
of the wrong type produces an error citing both the use and declaration
site. Well blocks open with an explicit WELL keyword (alias or
double-quoted literal name), quoting is double quotes throughout, and
date arithmetic accepts signed chains of integers and DURATION variables
such as @START + RAMP - 2. ORIONEVENTS 1.x headers are rejected with a
migration message.
FILTER and PERFID are no longer parse errors; any KEY=VALUE attribute
parses and the applier warns and ignores the not-yet-supported keys
(FILTER, PERFID, DSHIFT) while still applying the rest of the event.
The parser now recovers per line and reports every error in one pass via
OrionParseError.errors, unknown variables, event types and keywords get
difflib-based did-you-mean hints, and a stdlib-only validator CLI runs as
python3 -m rips.orion_events <file>. The eager orion_events import in
rips/__init__.py is replaced by a PEP 562 lazy import so the -m entry
point does not trigger runpy's double-import warning.
Update the shipped sample file and example script to the 2.0 syntax and
extend the test suite from 27 to 55 tests covering typed declarations,
offset arithmetic, alias resolution, multi-error diagnostics and the CLI.
Add a pure-Python parser (rips.orion_events) that reads an ORIONEVENTS
text file into a structured representation, plus an applier that drives
the existing WellEventTimeline API to create perforation, WCONHIST and
WELTARG events.
The parser is split into a stdlib-only Layer A (parse_orion_events ->
OrionDocument) that is unit-testable without a running ResInsight, and a
Layer B applier (apply_orion_document) that maps the document onto
add_perf_event/add_well_keyword_event and resolves well names via the
project. RADIUS is mapped to diameter, dated SET variables support '+ N'
day arithmetic, and FILTER/PERFID are reserved but currently raise a
located error while DSHIFT is ignored with a warning.
Ship a documented grammar in the module docstring, a clean sample input
(example_input_files/well_events.orion), a runnable example
(import_orion_events.py), and tests covering parsing, mapping and an
end-to-end integration test against a loaded case.
The first segment of a lateral had its comment cleared when the lateral had a tie-in valve, leaving the rows unlabelled. Write the well path name instead, so the exported segments can be traced back to the lateral they belong to.
The geometry path emitted one WELSEGS row per cell intersection of a fishbones lateral, ignoring the max segment length and the custom segment intervals. The tree path split them like every other completion, in collectCompletionWelsegsSegments.
Split each cell intersection with createSubSegmentMDPairs and chain the resulting rows. The lateral geometry is not part of the well path, so the TVD of a sub-segment cannot be interpolated along the well path. Interpolate linearly between the start and end TVD of the intersection instead, as the tree path did for fishbones. The cell is connected by the first sub-segment only, leaving COMPSEGS unchanged, and the lateral label stays on the first row.
An intersection now covers several segment numbers that share one effective diameter, and must still contribute once to the sum for its cell. FishbonesLateralSegment therefore holds the segment numbers of an intersection rather than a single one, and the intersections are grouped per lateral in FishbonesLateral. This replaces the separate list of first and second segment numbers, since the reduction rule is now expressed on the intersections of a lateral.
Verified with a copy of fishbones.rsp with max segment length enforced at 4 m: the three laterals are split into three or four rows each, depths are evenly interpolated, effective diameters are unchanged at 0.01664 and 0.00960, and COMPSEGS is identical. The sweep against TestModels/msw-export is unchanged, as none of the projects enforce a max segment length.
The summation reproduced the tree implementation, where an ICD sub connected to several cells took part in the sum of each of them, reading areas that earlier cells had already replaced. The result depended on the order the cells were visited in, and an ICD sub could contribute an already combined area to the next cell.
Compute every cell sum from the original areas instead. An ICD sub spans a 0.1 m valve segment and normally connects to a single cell, and when it reaches into more than one it now reports the largest of the sums it takes part in.
Behaviour is unchanged for the common case of one ICD sub per cell, and the sweep against TestModels/msw-export is unaffected. Added tests for the multi-cell case and for independence of the recording order.
The legacy tree path gave every fishbones ICD sub connected to the same grid cell the sum of their areas, so the cell sees the total flow area of the ICDs completing it. That was part of updateDataForMultipleItemsInSameGridCell and disappeared with the rest of the tree code, leaving each ICD sub with its own area.
Record the grid cells each ICD sub connects to while the branches are built, and apply the summation together with the effective diameters. FishbonesDiameterContext now carries both, and is renamed to FishbonesExportContext.
An ICD sub connected to several cells takes part in the sum of each of them and ends up with the value of the last cell in ascending cell order. This is order dependent, and is kept because the tree implementation behaved the same way.
None of the projects in TestModels/msw-export has more than one ICD sub per cell, so the sweep against the stored reference output is unchanged. Added unit tests for applyIcdAreaPerCell and applyEffectiveDiameters instead, since neither function had coverage.
The geometry path exported the raw equivalent diameter for every fishbones lateral segment. The legacy tree path replaced it with an effective diameter, computed in updateDataForMultipleItemsInSameGridCell, which was removed together with the rest of the tree code.
Reinstate both rules. Laterals sharing a grid cell get Deff = sqrt(d1^2 + d2^2 + ..) over the lateral segments in that cell (#7686). The first segment of a lateral shares its cell with the main bore and with the first segment of every other lateral on the same sub, so it inherits the effective diameter of the second segment of the same lateral (#7731). A lateral contained in a single grid cell has no second segment and keeps the combined value, which is where the difference from the tree path was most visible.
Lateral segments are recorded while the branches are built, since the grid cell of a segment is not recoverable afterwards. COMPSEGS deduplication clears the cell intersections of segments in an already connected cell. The context is shared by the main bore and all tie-in laterals, and applied once the branches are assembled.
Verified against the twelve projects in TestModels/msw-export: every well with stored reference output now matches the legacy values, including the fishbones laterals that previously differed.
The fishbones ICD sub segment left RigMswSegment::diameter and roughness unset, and the formatter renders an empty optional as the 1* default marker. The legacy tree path emitted 0.15 and 5.0e-5 for these rows, taken from the RicMswSegment constructor defaults. RigMswSegment has never had such defaults, so the comment claiming the tree behaviour was matched was wrong.
Set both values explicitly, as buildFractureBranches already does. Verified against TestModels/msw-export/project-files/fishbones.rsp: the ICD sub WELSEGS row again exports 0.15000 and 0.0000500, matching the stored legacy reference output.
Four crashes reported from release builds share the same shape: a pointer that
is null during teardown or before a view is fully created is dereferenced
without a check.
RiuViewer::paintOverlayItems() used the owner case of the view and of the
comparison view without checking it, and dereferenced the result of a
dynamic_cast directly. The owner case is null while a case is being closed.
RicIntersectionFeatureImpl::createIntersectionBoxSlize() checked
activeMainOrComparisonGridView() but dereferenced activeGridView(), which is a
different object and can be null or have no viewer.
RimEclipseContourMapView::onCreateDisplayModel() called viewer()->mainCamera()
before the viewer exists.
RimCorrelationPlotCollection::applyFirstEnsembleFieldAddressesToPlot() called
front() on the ensemble parameter vector, which is empty for an ensemble
without realization parameters.
RivSingleCellPartGenerator uses the grid and cell index stored in the 3D
selection item. These are plain indices, and they are not revalidated when
the case data is rebuilt behind a live selection. Both RigMainGrid::gridByIndex()
and RigGridBase::cell() guard only with CVF_ASSERT, so in release builds a stale
index reads out of bounds and the resulting garbage corner indices crash in
RigGridBase::cellCornerVertices().
Validate the grid and cell index before creating the mesh drawable.
* Use guarded pointer for delayed plot updates
The cached result definition can be deleted before the delayed update is
executed, causing a crash in the PVT and relative permeability plot panels.
* Guard against fracture definition without conductivity result
The list of conductivity result names is empty for some fracture
definitions, causing an out of range access when computing statistics.
* Guard against missing data source in custom VFP plot
The data source of a VFP table can be null, and the VFP tables are not
available until the data has been imported.
* Recompute well cell arrays when the grids change
computeWellCellsPrGrid() returned early whenever the arrays had been computed once, so the
size check below it was unreachable and a stale array was kept when the grids changed. The
too-short array triggered an assert in RivReservoirViewPartMgr::computeNativeVisibility.
Return early only when the cached arrays still match the current set of grids.
* Guard against missing GUI application when applying style sheet
RiaGuiApplication::instance() does a dynamic_cast and returns null outside a GUI context. The
assert guarding this is compiled out in release, so the null pointer was dereferenced.
* Guard against missing case in flow characteristics plot field change
The case field can be set to nothing from the UI. RimEclipseResultCase::defaultFlowDiagSolution()
and reservoirViews() were then called on a null pointer.
* Guard against missing case and out of range time steps when updating flow characteristics plot
onLoadDataAndUpdate() dereferenced the case without checking, although the same function handles
a missing case further down. The time step indices come from the flow diagnostics solution, and
were used to index arrays sized by the number of case time steps without a range check.
* #14411 Enforce name uniqueness for surfaces, polygons and folders
Names must be unique among siblings sharing the same parent folder. Items in different folders may keep identical names, and comparison is case sensitive.
Siblings are the objects held by the same caf::PdmChildArrayField. Items and folders live in two distinct child arrays in caf::PdmNestedCollection, so a folder and an item may share a name under the same parent.
The Python API gets an OnNameConflict flag on AddFolder, CreatePolygon, ImportSurface and NewRegularSurface, supporting FAIL (default), AUTO_RENAME and OVERWRITE. NewSurface is left out, as grid case surfaces derive their tree label from the case and K index and carry no name of their own.
The ensemble file set removed the file extension by searching for the last dot in the complete path. When the path pattern was already without extension, the last dot was found in a folder name, truncating the path pattern and causing the search for SMSPEC files to fail.
Add RiaFilePathTools::removeFileExtension based on std::filesystem::path::replace_extension, and use it both in RimEnsembleFileSet and in the grid and summary ensemble import, replacing a duplicated lambda doing the same operation.
The label is moved from the anchor point towards the camera to be drawn in front of other geometry. The offset was derived from the zoom level, and was not related to the distance between the camera and the anchor point. In a 3D view the offset is usually larger than this distance, moving the label behind the camera. Labels outside the view frustum are silently discarded by the text renderer.
Limit the offset to the smaller of half the distance to the anchor point and the distance to the near plane. The limit is derived from the anchor point and not from the near plane, as the label parts are part of the scene and are used to compute the clipping planes.
Reject anchor point candidates behind the camera. Coordinates behind the camera are mirrored into the viewport by the perspective divide in cvf::Camera::project(), and could be selected as the coordinate closest to the label position.
Rim2dIntersectionViewCollection::syncFromExistingIntersections() found the
intersections by walking the PDM child tree of the case. Views located in a
grid ensemble view collection or in the project level view collection are not
children of the case, so no 2D intersection view was created for their
intersections and "Show 2D Intersection View" did nothing.
Collect the intersections from the grid views displaying the case instead.
Display the 2D intersection views as a child of RimReservoirGridEnsemble, next
to the 3D views they are created from, and skip the node on the case to avoid
showing the same object twice in the project tree.
Triage 4-5 signatures per run into a single fork PR instead of one issue
plus PR per signature. The call stack that motivated each fix now lives in
the PR body.
Make registry.json status the single source of truth for what has been
handled, document the status lifecycle, and mark a signature resolved only
once the PR is merged.