Fix crashes reported by crash telemetry

* 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.
This commit is contained in:
Magne Sjaastad
2026-08-07 12:14:03 +02:00
committed by GitHub
parent 318659e130
commit d30d5e7817
7 changed files with 100 additions and 7 deletions
@@ -646,10 +646,11 @@ void RimCustomVfpPlot::onLoadDataAndUpdate()
for ( const auto& table : tables )
{
if ( !table ) continue;
if ( !table || !table->dataSource() ) continue;
int tableNumber = table->tableNumber();
auto vfpTables = table->dataSource()->vfpTables();
if ( !vfpTables ) continue;
if ( table->tableType() == RimVfpDefines::TableType::INJECTION )
{
@@ -1445,7 +1446,8 @@ void RimCustomVfpPlot::scheduleReplot()
//--------------------------------------------------------------------------------------------------
std::vector<double> RimCustomVfpPlot::familyValuesForTable( RimVfpTable* table ) const
{
if ( !table || !m_mainDataSource || !m_mainDataSource->dataSource() || !m_mainDataSource->dataSource()->vfpTables() ) return {};
if ( !table || !table->dataSource() || !table->dataSource()->vfpTables() ) return {};
if ( !m_mainDataSource || !m_mainDataSource->dataSource() || !m_mainDataSource->dataSource()->vfpTables() ) return {};
std::vector<double> mainTableFamilyValues = valuesForProductionType( m_familyVariable() );
@@ -157,6 +157,7 @@ std::vector<double> RigEnsembleFractureStatisticsCalculator::calculateGridStatis
{
std::vector<double> samples;
if ( fractureDefinitions.empty() ) return samples;
if ( fractureDefinitions[0]->conductivityResultNames().isEmpty() ) return samples;
// TODO: heuristic to find conductivity name?
QString conductivityResultName = fractureDefinitions[0]->conductivityResultNames()[0];
@@ -255,6 +256,7 @@ std::vector<double> RigEnsembleFractureStatisticsCalculator::calculateAreaWeight
{
std::vector<double> samples;
if ( fractureDefinitions.empty() ) return samples;
if ( fractureDefinitions[0]->conductivityResultNames().isEmpty() ) return samples;
// TODO: heuristic to find conductivity name?
QString conductivityResultName = fractureDefinitions[0]->conductivityResultNames()[0];
@@ -22,6 +22,7 @@ set(SOURCE_UNITTEST_FILES
${CMAKE_CURRENT_LIST_DIR}/RigEclipseCaseDataTools-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RigEclipseCrossPlotDataExtractor-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RigEclipseResultTools-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RigEnsembleFractureStatisticsCalculator-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RigMobilePoreVolumeResultCalculator-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RigReservoir-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RigResdataGridConverter-Test.cpp
@@ -47,6 +48,7 @@ set(SOURCE_UNITTEST_FILES
${CMAKE_CURRENT_LIST_DIR}/RigHexIntersectionTools-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/ObservedDataParser-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RicExpressionParser-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuPlotUpdater-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuQwtLinearScaleEngine-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/RiuSummaryVectorDescriptionMap-Test.cpp
${CMAKE_CURRENT_LIST_DIR}/FixedWidthDataParser-Test.cpp
@@ -0,0 +1,25 @@
#include "gtest/gtest.h"
#include "RigEnsembleFractureStatisticsCalculator.h"
#include "RigStimPlanFractureDefinition.h"
#include <QStringList>
//--------------------------------------------------------------------------------------------------
/// A fracture definition without any conductivity result must not be indexed out of range
//--------------------------------------------------------------------------------------------------
TEST( RigEnsembleFractureStatisticsCalculatorTest, NoConductivityResultNames )
{
cvf::ref<RigStimPlanFractureDefinition> definition = new RigStimPlanFractureDefinition;
EXPECT_TRUE( definition->conductivityResultNames().isEmpty() );
std::vector<cvf::ref<RigStimPlanFractureDefinition>> definitions = { definition };
for ( auto propertyType : RigEnsembleFractureStatisticsCalculator::propertyTypes() )
{
if ( propertyType == RigEnsembleFractureStatisticsCalculator::PropertyType::FORMATION_DIP ) continue;
auto values = RigEnsembleFractureStatisticsCalculator::calculateProperty( definitions, propertyType );
EXPECT_TRUE( values.empty() );
}
}
@@ -0,0 +1,59 @@
#include "gtest/gtest.h"
#include "RimEclipseResultDefinition.h"
#include "Riu3dSelectionManager.h"
#include "RiuPlotUpdater.h"
#include <QWidget>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
class TestPlotUpdater : public RiuPlotUpdater
{
public:
void storeSelection( const RiuEclipseSelectionItem* selectionItem ) { storeDelayedInformation( selectionItem ); }
int queryCount() const { return m_queryCount; }
protected:
void clearPlot() override {}
QWidget* plotPanel() override { return nullptr; }
bool queryDataAndUpdatePlot( const RimEclipseResultDefinition* eclipseResDef, size_t, size_t, size_t ) override
{
m_queryCount++;
return true;
}
private:
int m_queryCount = 0;
};
//--------------------------------------------------------------------------------------------------
/// The result definition can be deleted between the selection change and the delayed update
//--------------------------------------------------------------------------------------------------
TEST( RiuPlotUpdaterTest, DelayedUpdateAfterResultDefinitionIsDeleted )
{
auto* resultDefinition = new RimEclipseResultDefinition;
RiuEclipseSelectionItem selectionItem( nullptr,
resultDefinition,
0,
0,
0,
0,
cvf::Color3f( 1.0f, 0.0f, 0.0f ),
cvf::StructGridInterface::NO_FACE,
cvf::Vec3d::ZERO );
TestPlotUpdater plotUpdater;
plotUpdater.storeSelection( &selectionItem );
delete resultDefinition;
plotUpdater.doDelayedUpdate();
EXPECT_EQ( 0, plotUpdater.queryCount() );
}
@@ -81,7 +81,7 @@ RiuEclipseSelectionItem* RiuPlotUpdater::extractEclipseSelectionItem( const RiuS
//--------------------------------------------------------------------------------------------------
void RiuPlotUpdater::doDelayedUpdate()
{
if ( m_eclipseResultDef != nullptr )
if ( m_eclipseResultDef.notNull() )
{
if ( !queryDataAndUpdatePlot( m_eclipseResultDef, m_timeStepIndex, m_gridIndex, m_gridLocalCellIndex ) )
{
@@ -17,6 +17,8 @@
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "cafPdmPointer.h"
#include <QPointer>
#include <QString>
@@ -61,8 +63,9 @@ protected:
const Rim3dView* m_viewToFollowAnimationFrom;
// cached values for delayed plot updates
const RimEclipseResultDefinition* m_eclipseResultDef;
size_t m_timeStepIndex;
size_t m_gridIndex;
size_t m_gridLocalCellIndex;
// Use a guarded pointer, as the result definition can be deleted before the delayed update is executed
caf::PdmPointer<RimEclipseResultDefinition> m_eclipseResultDef;
size_t m_timeStepIndex;
size_t m_gridIndex;
size_t m_gridLocalCellIndex;
};