#14345 Grid Calculation: Add destination ensemble

This commit is contained in:
Kristian Bendiksen committed 2026-09-16 10:53:31 +02:00
1 parent 8cbc81fe98
commit 0ba20f0b65
3 files changed
+80 -16

No files matched your search

@@ -148,6 +148,7 @@ RimGridCalculation::RimGridCalculation()
CAF_PDM_InitFieldNoDefault( &m_dataFilter, "DataFilter", "Data Filter" );
CAF_PDM_InitFieldNoDefault( &m_defaultValueType, "DefaultValueType", "Non-visible Cell Value" );
CAF_PDM_InitField( &m_defaultValue, "DefaultValue", 0.0, "Custom Value" );
CAF_PDM_InitFieldNoDefault( &m_destinationEnsemble, "DestinationEnsemble", "Destination Ensemble" );
CAF_PDM_InitFieldNoDefault( &m_destinationCase, "DestinationCase", "Destination Case" );
CAF_PDM_InitField( &m_applyToAllCases_OBSOLETE, "AllDestinationCase", false, "Apply to All Cases" );
@@ -240,8 +241,8 @@ bool RimGridCalculation::calculate()
// Equal grid size is required if there is more than one grid case in the expression. If a cell filter view is active, the visibility is
// based on one view and reused for all other grid models, and requires equal grid size. A data filter is evaluated per case.
bool checkIfGridSizeIsEqual = ( !allSourceCasesAreEqualToDestinationCase() || useCellFilterView ) &&
m_additionalCasesType != AdditionalCasesType::ENSEMBLE;
const bool calculateForEnsemble = m_destinationEnsemble() || m_additionalCasesType == AdditionalCasesType::ENSEMBLE;
bool checkIfGridSizeIsEqual = ( !allSourceCasesAreEqualToDestinationCase() || useCellFilterView ) && !calculateForEnsemble;
// Grid dimensions are required to validate the calculation, and a source case is not necessarily opened. This is the case for a
// statistics case with no computed statistics.
@@ -363,6 +364,8 @@ std::vector<RimEclipseCase*> RimGridCalculation::outputEclipseCases() const
if ( m_additionalEnsemble() ) return m_additionalEnsemble()->cases();
}
if ( m_destinationEnsemble() ) return m_destinationEnsemble()->cases();
return { m_destinationCase };
}
@@ -415,7 +418,9 @@ void RimGridCalculation::defineUiOrdering( QString uiConfigName, caf::PdmUiOrder
{
RimUserDefinedCalculation::defineUiOrdering( uiConfigName, uiOrdering );
uiOrdering.add( &m_destinationEnsemble );
uiOrdering.add( &m_destinationCase );
m_destinationCase.uiCapability()->setUiHidden( m_destinationEnsemble() != nullptr );
uiOrdering.add( &m_additionalCasesType );
uiOrdering.add( &m_additionalCaseGroup );
@@ -473,7 +478,7 @@ QList<caf::PdmOptionItemInfo> RimGridCalculation::calculateValueOptions( const c
{
// If no input cases are defined, use the destination case to determine the grid size. This will enable use of expressions
// with no input cases like "calculation := 1.0"
firstEclipseCase = m_destinationCase();
firstEclipseCase = destinationCase();
}
if ( firstEclipseCase )
@@ -493,9 +498,9 @@ QList<caf::PdmOptionItemInfo> RimGridCalculation::calculateValueOptions( const c
{
options.push_back( caf::PdmOptionItemInfo( "None", nullptr ) );
if ( m_destinationCase() )
if ( auto* destination = destinationCase() )
{
if ( auto dataFilterCollection = m_destinationCase()->dataFilterCollection() )
if ( auto dataFilterCollection = destination->dataFilterCollection() )
{
for ( RimCellFilter* filter : dataFilterCollection->filters() )
{
@@ -505,7 +510,7 @@ QList<caf::PdmOptionItemInfo> RimGridCalculation::calculateValueOptions( const c
}
// Also offer the data filters of the grid ensemble the destination case belongs to, if any
if ( auto gridEnsemble = m_destinationCase()->firstAncestorOfType<RimReservoirGridEnsemble>() )
if ( auto gridEnsemble = destination->firstAncestorOfType<RimReservoirGridEnsemble>() )
{
if ( auto dataFilterCollection = gridEnsemble->dataFilterCollection() )
{
@@ -521,6 +526,19 @@ QList<caf::PdmOptionItemInfo> RimGridCalculation::calculateValueOptions( const c
}
}
}
else if ( fieldNeedingOptions == &m_destinationEnsemble )
{
options.push_back( caf::PdmOptionItemInfo( "None", nullptr ) );
RimProject* project = RimProject::current();
if ( project->activeOilField() && project->activeOilField()->analysisModels() )
{
for ( auto* ensemble : project->activeOilField()->analysisModels()->reservoirGridEnsembles.childrenByType() )
{
options.push_back( caf::PdmOptionItemInfo( ensemble->name(), ensemble, false, ensemble->uiIconProvider() ) );
}
}
}
else if ( fieldNeedingOptions == &m_destinationCase )
{
RimEclipseCase* firstInputCase = nullptr;
@@ -600,7 +618,7 @@ void RimGridCalculation::initAfterRead()
{
gridVar->eclipseResultChanged.connect( this, &RimGridCalculation::onVariableUpdated );
if ( m_destinationCase == nullptr ) m_destinationCase = gridVar->eclipseCase();
if ( !m_destinationEnsemble() && m_destinationCase == nullptr ) m_destinationCase = gridVar->eclipseCase();
}
}
@@ -623,7 +641,10 @@ void RimGridCalculation::fieldChangedByUi( const caf::PdmFieldHandle* changedFie
{
if ( m_filterType() != FilterType::CELL_FILTER_VIEW ) m_cellFilterView = nullptr;
if ( m_filterType() != FilterType::DATA_FILTER ) m_dataFilter = nullptr;
}
if ( changedField == &m_filterType || changedField == &m_destinationEnsemble )
{
updateConnectedEditors();
}
}
@@ -634,7 +655,7 @@ void RimGridCalculation::fieldChangedByUi( const caf::PdmFieldHandle* changedFie
void RimGridCalculation::onEditNonVisibleResultAddressButtonPressed()
{
auto eclipseCase = m_nonVisibleResultAddress->eclipseCase();
if ( !eclipseCase ) eclipseCase = m_destinationCase;
if ( !eclipseCase ) eclipseCase = destinationCase();
RimResultSelectionUi selectionUi;
selectionUi.setEclipseResultAddress( eclipseCase, m_nonVisibleResultAddress->resultType(), m_nonVisibleResultAddress->resultName() );
@@ -661,7 +682,8 @@ void RimGridCalculation::onVariableUpdated( const SignalEmitter* emitter )
// interpreted as computed statistics, and will block recomputation of the statistics.
const bool canBeDestinationCase = dynamic_cast<RimEclipseStatisticsCase*>( variableCase ) == nullptr;
if ( canBeDestinationCase && ( !m_destinationCase || !m_destinationCase->isGridSizeEqualTo( variableCase ) ) )
if ( canBeDestinationCase && !m_destinationEnsemble() &&
( !m_destinationCase || !m_destinationCase->isGridSizeEqualTo( variableCase ) ) )
{
m_destinationCase = variableCase;
}
@@ -673,19 +695,30 @@ void RimGridCalculation::onVariableUpdated( const SignalEmitter* emitter )
variableUpdated.send();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEclipseCase* RimGridCalculation::destinationCase() const
{
if ( m_destinationEnsemble() ) return m_destinationEnsemble()->mainCase();
return m_destinationCase();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimGridCalculation::allSourceCasesAreEqualToDestinationCase() const
{
if ( m_destinationCase() == nullptr ) return false;
auto* destination = destinationCase();
if ( destination == nullptr ) return false;
for ( const auto& variable : m_variables )
{
auto gridVar = dynamic_cast<RimGridCalculationVariable*>( variable.p() );
if ( gridVar )
{
if ( gridVar->eclipseCase() != m_destinationCase() ) return false;
if ( gridVar->eclipseCase() != destination ) return false;
}
}
@@ -1091,7 +1124,7 @@ bool RimGridCalculation::calculateForCases( const std::vector<RimEclipseCase*>&
RimGridCalculationVariable* v = dynamic_cast<RimGridCalculationVariable*>( m_variables[i] );
CAF_ASSERT( v != nullptr );
bool useDataFromSourceCase = ( v->eclipseCase() == m_destinationCase ) ||
bool useDataFromSourceCase = ( v->eclipseCase() == destinationCase() ) || m_destinationEnsemble() ||
m_additionalCasesType == AdditionalCasesType::ENSEMBLE;
auto sourceCase = useDataFromSourceCase ? calculationCase : v->eclipseCase();
@@ -1276,7 +1309,7 @@ void RimGridCalculation::findAndEvaluateDependentCalculations( const std::vector
//--------------------------------------------------------------------------------------------------
void RimGridCalculation::assignEclipseCaseForNullPointers( RimEclipseCase* eclipseCase )
{
if ( m_destinationCase() == nullptr )
if ( destinationCase() == nullptr )
{
m_destinationCase = eclipseCase;
}
@@ -36,6 +36,7 @@ class RimGridView;
class RigEclipseResultAddress;
class RimEclipseResultAddress;
class RimIdenticalGridCaseGroup;
class RimReservoirGridEnsemble;
class RigActiveCellInfo;
//==================================================================================================
@@ -144,9 +145,10 @@ protected:
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
private:
void onVariableUpdated( const SignalEmitter* emitter );
bool allSourceCasesAreEqualToDestinationCase() const;
void onEditNonVisibleResultAddressButtonPressed();
void onVariableUpdated( const SignalEmitter* emitter );
RimEclipseCase* destinationCase() const;
bool allSourceCasesAreEqualToDestinationCase() const;
void onEditNonVisibleResultAddressButtonPressed();
static std::pair<bool, QStringList> createStatisticsText( const std::vector<std::vector<double>>& values );
@@ -156,6 +158,7 @@ private:
caf::PdmPtrField<RimCellFilter*> m_dataFilter;
caf::PdmField<caf::AppEnum<DefaultValueType>> m_defaultValueType;
caf::PdmField<double> m_defaultValue;
caf::PdmPtrField<RimReservoirGridEnsemble*> m_destinationEnsemble;
caf::PdmPtrField<RimEclipseCase*> m_destinationCase;
caf::PdmField<caf::AppEnum<AdditionalCasesType>> m_additionalCasesType;
@@ -20,6 +20,11 @@
#include "RimGridCalculation.h"
#include "RimEclipseResultCase.h"
#include "RimReservoirGridEnsemble.h"
#include "cafPdmPtrField.h"
#include <cmath>
#include <limits>
@@ -60,3 +65,26 @@ TEST( RimGridCalculationTest, ReplaceInvalidValuesWithDefaultValueNoInvalidValue
std::vector<double> emptyValues;
EXPECT_EQ( 0u, RimGridCalculation::replaceInvalidValuesWithDefaultValue( 42.0, emptyValues ) );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RimGridCalculationTest, DestinationEnsembleCasesAreOutputs )
{
RimReservoirGridEnsemble ensemble;
auto* firstCase = new RimEclipseResultCase;
auto* secondCase = new RimEclipseResultCase;
ensemble.addCase( firstCase );
ensemble.addCase( secondCase );
RimGridCalculation calculation;
auto* destinationEnsembleField =
dynamic_cast<caf::PdmPtrField<RimReservoirGridEnsemble*>*>( calculation.findField( "DestinationEnsemble" ) );
ASSERT_TRUE( destinationEnsembleField != nullptr );
destinationEnsembleField->setValue( &ensemble );
const auto outputCases = calculation.outputEclipseCases();
ASSERT_EQ( 2u, outputCases.size() );
EXPECT_EQ( firstCase, outputCases[0] );
EXPECT_EQ( secondCase, outputCases[1] );
}