From 39fc9d5c36e845b2e8dc7b1b8ee17f7f4d9eb2bc Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Tue, 7 Nov 2023 19:23:12 +0100 Subject: [PATCH] Add support for grid calculations as data source for statistics - add data source selection, either Case Property or Grid Calculation - add Time Step Selection, can be used for both dynamic case properties and Grid Calculations - optionally release grid calculation data when statistics is computed - recursive grid calculations is supported --- .../Commands/RicCreateTemporaryLgrFeature.cpp | 6 +- .../RimEclipseStatisticsCase.cpp | 372 +++++++++++++----- .../RimEclipseStatisticsCase.h | 31 +- .../RimEclipseStatisticsCaseEvaluator.cpp | 58 +-- .../RimEclipseStatisticsCaseEvaluator.h | 10 +- .../ProjectDataModel/RimGridCalculation.cpp | 276 +++++++------ .../ProjectDataModel/RimGridCalculation.h | 2 + .../RimGridCalculationCollection.cpp | 59 +++ .../RimGridCalculationCollection.h | 4 + .../RimIdenticalGridCaseGroup.cpp | 1 + .../RigCaseCellResultsData.cpp | 25 +- .../RigCaseCellResultsData.h | 3 +- 12 files changed, 565 insertions(+), 282 deletions(-) diff --git a/ApplicationLibCode/Commands/RicCreateTemporaryLgrFeature.cpp b/ApplicationLibCode/Commands/RicCreateTemporaryLgrFeature.cpp index d845392420..27d9a7cfa5 100644 --- a/ApplicationLibCode/Commands/RicCreateTemporaryLgrFeature.cpp +++ b/ApplicationLibCode/Commands/RicCreateTemporaryLgrFeature.cpp @@ -285,16 +285,18 @@ void RicCreateTemporaryLgrFeature::deleteAllCachedData( RimEclipseCase* eclipseC { if ( eclipseCase ) { + std::vector categoriesToExclude = { RiaDefines::ResultCatType::GENERATED }; + RigCaseCellResultsData* cellResultsDataMatrix = eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL ); if ( cellResultsDataMatrix ) { - cellResultsDataMatrix->freeAllocatedResultsData(); + cellResultsDataMatrix->freeAllocatedResultsData( categoriesToExclude, std::nullopt ); } RigCaseCellResultsData* cellResultsDataFracture = eclipseCase->results( RiaDefines::PorosityModelType::FRACTURE_MODEL ); if ( cellResultsDataFracture ) { - cellResultsDataFracture->freeAllocatedResultsData(); + cellResultsDataFracture->freeAllocatedResultsData( categoriesToExclude, std::nullopt ); } RigEclipseCaseData* eclipseCaseData = eclipseCase->eclipseCaseData(); diff --git a/ApplicationLibCode/ProjectDataModel/RimEclipseStatisticsCase.cpp b/ApplicationLibCode/ProjectDataModel/RimEclipseStatisticsCase.cpp index c6dc5071db..4b21ae13a0 100644 --- a/ApplicationLibCode/ProjectDataModel/RimEclipseStatisticsCase.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimEclipseStatisticsCase.cpp @@ -34,8 +34,10 @@ #include "RimEclipseCellColors.h" #include "RimEclipseStatisticsCaseEvaluator.h" #include "RimEclipseView.h" +#include "RimGridCalculationCollection.h" #include "RimIdenticalGridCaseGroup.h" #include "RimIntersectionCollection.h" +#include "RimProject.h" #include "RimReservoirCellResultsStorage.h" #include "RimSimWellInViewCollection.h" @@ -44,6 +46,7 @@ #include "cafPdmFieldScriptingCapability.h" #include "cafPdmUiPushButtonEditor.h" #include "cafPdmUiTextEditor.h" +#include "cafPdmUiTreeSelectionEditor.h" #include "cafProgressInfo.h" namespace caf @@ -51,10 +54,19 @@ namespace caf template <> void caf::AppEnum::setUp() { - addItem( RimEclipseStatisticsCase::NEAREST_OBSERVATION, "NearestObservationPercentile", "Nearest Observation" ); - addItem( RimEclipseStatisticsCase::HISTOGRAM_ESTIMATED, "HistogramEstimatedPercentile", "Histogram based estimate" ); - addItem( RimEclipseStatisticsCase::INTERPOLATED_OBSERVATION, "InterpolatedObservationPercentile", "Interpolated Observation" ); - setDefault( RimEclipseStatisticsCase::INTERPOLATED_OBSERVATION ); + addItem( RimEclipseStatisticsCase::PercentileCalcType::NEAREST_OBSERVATION, "NearestObservationPercentile", "Nearest Observation" ); + addItem( RimEclipseStatisticsCase::PercentileCalcType::HISTOGRAM_ESTIMATED, "HistogramEstimatedPercentile", "Histogram based estimate" ); + addItem( RimEclipseStatisticsCase::PercentileCalcType::INTERPOLATED_OBSERVATION, + "InterpolatedObservationPercentile", + "Interpolated Observation" ); + setDefault( RimEclipseStatisticsCase::PercentileCalcType::INTERPOLATED_OBSERVATION ); +} +template <> +void caf::AppEnum::setUp() +{ + addItem( RimEclipseStatisticsCase::DataSourceType::GRID_CALCULATION, "GridCalculation", "Grid Calculation" ); + addItem( RimEclipseStatisticsCase::DataSourceType::CASE_PROPERTY, "CaseProperty", "Case Property" ); + setDefault( RimEclipseStatisticsCase::DataSourceType::CASE_PROPERTY ); } } // namespace caf @@ -78,6 +90,14 @@ RimEclipseStatisticsCase::RimEclipseStatisticsCase() m_selectionSummary.uiCapability()->setUiEditorTypeName( caf::PdmUiTextEditor::uiEditorTypeName() ); m_selectionSummary.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::HIDDEN ); + CAF_PDM_InitFieldNoDefault( &m_dataSourceForStatistics, "DataSourceForStatistics", "Data Source" ); + + CAF_PDM_InitFieldNoDefault( &m_gridCalculation, "GridCalculation", "Grid Calculation" ); + CAF_PDM_InitField( &m_clearGridCalculationMemory, "ClearGridCalculationMemory", true, "Clear Grid Calculation Memory" ); + + CAF_PDM_InitScriptableFieldNoDefault( &m_selectedTimeSteps, "SelectedTimeSteps", "Time Step Selection" ); + m_selectedTimeSteps.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() ); + CAF_PDM_InitScriptableFieldNoDefault( &m_resultType, "ResultType", "Result Type" ); m_resultType.xmlCapability()->setIOWritable( false ); CAF_PDM_InitScriptableFieldNoDefault( &m_porosityModel, "PorosityModel", "Porosity Model" ); @@ -120,6 +140,8 @@ RimEclipseStatisticsCase::RimEclipseStatisticsCase() m_flipXAxis.uiCapability()->setUiHidden( true ); m_flipYAxis.uiCapability()->setUiHidden( true ); m_activeFormationNames.uiCapability()->setUiHidden( true ); + + m_displayNameOption = RimCaseDisplayNameTools::DisplayName::CUSTOM; } //-------------------------------------------------------------------------------------------------- @@ -174,6 +196,8 @@ bool RimEclipseStatisticsCase::openEclipseGridFile() m_populateSelectionAfterLoadingGrid = false; } + initializeSelectedTimeSteps(); + return true; } @@ -240,6 +264,26 @@ void RimEclipseStatisticsCase::setSourceProperties( RiaDefines::ResultCatType pr } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEclipseStatisticsCase::selectAllTimeSteps() +{ + RimIdenticalGridCaseGroup* idgcg = caseGroup(); + if ( idgcg && idgcg->mainCase() ) + { + int timeStepCount = idgcg->mainCase()->timeStepStrings().size(); + + if ( timeStepCount > 0 ) + { + std::vector allTimeSteps; + allTimeSteps.resize( timeStepCount ); + std::iota( allTimeSteps.begin(), allTimeSteps.end(), 0 ); + m_selectedTimeSteps = allTimeSteps; + } + } +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -256,15 +300,32 @@ void RimEclipseStatisticsCase::computeStatistics() std::vector sourceCases = getSourceCases(); + if ( m_dataSourceForStatistics() == DataSourceType::GRID_CALCULATION && m_gridCalculation() ) + { + auto proj = RimProject::current(); + auto calcColl = proj->gridCalculationCollection(); + + auto dependentCalculations = calcColl->dependentCalculations( m_gridCalculation() ); + if ( dependentCalculations.empty() ) + { + // No calculations found, usually triggered by a circular dependency of calculations. Error message displayed by + // dependentCalculations(). + return; + } + + std::vector timeStepIndices( m_selectedTimeSteps().begin(), m_selectedTimeSteps().end() ); + + for ( auto calc : dependentCalculations ) + { + calc->calculateForCases( sourceCases, timeStepIndices ); + } + } + if ( sourceCases.empty() || !sourceCases.at( 0 )->results( RiaDefines::PorosityModelType::MATRIX_MODEL ) ) { return; } - // The first source has been read completely from disk, and contains grid and meta data - // Use this information for all cases in the case group - size_t timeStepCount = sourceCases.at( 0 )->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->maxTimeStepCount(); - RimStatisticsConfig statisticsConfig; statisticsConfig.m_calculatePercentiles = m_calculatePercentiles(); @@ -273,11 +334,7 @@ void RimEclipseStatisticsCase::computeStatistics() statisticsConfig.m_pMinPos = m_lowPercentile(); statisticsConfig.m_pValMethod = m_percentileCalculationType(); - std::vector timeStepIndices; - for ( size_t i = 0; i < timeStepCount; i++ ) - { - timeStepIndices.push_back( i ); - } + auto timeStepIndices = m_selectedTimeSteps(); // If no dynamic data is present, we might end up with no time steps. Make sure we have at least one. if ( timeStepIndices.empty() ) @@ -289,63 +346,79 @@ void RimEclipseStatisticsCase::computeStatistics() QList resultSpecification; - for ( size_t pIdx = 0; pIdx < m_selectedDynamicProperties().size(); ++pIdx ) + if ( m_dataSourceForStatistics() == DataSourceType::CASE_PROPERTY ) { - resultSpecification.append( RimEclipseStatisticsCaseEvaluator::ResSpec( RiaDefines::PorosityModelType::MATRIX_MODEL, - RiaDefines::ResultCatType::DYNAMIC_NATIVE, - m_selectedDynamicProperties()[pIdx] ) ); - } + for ( size_t pIdx = 0; pIdx < m_selectedDynamicProperties().size(); ++pIdx ) + { + resultSpecification.append( RimEclipseStatisticsCaseEvaluator::ResSpec( RiaDefines::PorosityModelType::MATRIX_MODEL, + RiaDefines::ResultCatType::DYNAMIC_NATIVE, + m_selectedDynamicProperties()[pIdx] ) ); + } - for ( size_t pIdx = 0; pIdx < m_selectedStaticProperties().size(); ++pIdx ) - { - resultSpecification.append( RimEclipseStatisticsCaseEvaluator::ResSpec( RiaDefines::PorosityModelType::MATRIX_MODEL, - RiaDefines::ResultCatType::STATIC_NATIVE, - m_selectedStaticProperties()[pIdx] ) ); - } + for ( size_t pIdx = 0; pIdx < m_selectedStaticProperties().size(); ++pIdx ) + { + resultSpecification.append( RimEclipseStatisticsCaseEvaluator::ResSpec( RiaDefines::PorosityModelType::MATRIX_MODEL, + RiaDefines::ResultCatType::STATIC_NATIVE, + m_selectedStaticProperties()[pIdx] ) ); + } - for ( size_t pIdx = 0; pIdx < m_selectedGeneratedProperties().size(); ++pIdx ) + for ( size_t pIdx = 0; pIdx < m_selectedGeneratedProperties().size(); ++pIdx ) + { + resultSpecification.append( RimEclipseStatisticsCaseEvaluator::ResSpec( RiaDefines::PorosityModelType::MATRIX_MODEL, + RiaDefines::ResultCatType::GENERATED, + m_selectedGeneratedProperties()[pIdx] ) ); + } + + for ( size_t pIdx = 0; pIdx < m_selectedInputProperties().size(); ++pIdx ) + { + resultSpecification.append( RimEclipseStatisticsCaseEvaluator::ResSpec( RiaDefines::PorosityModelType::MATRIX_MODEL, + RiaDefines::ResultCatType::INPUT_PROPERTY, + m_selectedInputProperties()[pIdx] ) ); + } + + for ( size_t pIdx = 0; pIdx < m_selectedFractureDynamicProperties().size(); ++pIdx ) + { + resultSpecification.append( RimEclipseStatisticsCaseEvaluator::ResSpec( RiaDefines::PorosityModelType::FRACTURE_MODEL, + RiaDefines::ResultCatType::DYNAMIC_NATIVE, + m_selectedFractureDynamicProperties()[pIdx] ) ); + } + + for ( size_t pIdx = 0; pIdx < m_selectedFractureStaticProperties().size(); ++pIdx ) + { + resultSpecification.append( RimEclipseStatisticsCaseEvaluator::ResSpec( RiaDefines::PorosityModelType::FRACTURE_MODEL, + RiaDefines::ResultCatType::STATIC_NATIVE, + m_selectedFractureStaticProperties()[pIdx] ) ); + } + + for ( size_t pIdx = 0; pIdx < m_selectedFractureGeneratedProperties().size(); ++pIdx ) + { + resultSpecification.append( RimEclipseStatisticsCaseEvaluator::ResSpec( RiaDefines::PorosityModelType::FRACTURE_MODEL, + RiaDefines::ResultCatType::GENERATED, + m_selectedFractureGeneratedProperties()[pIdx] ) ); + } + + for ( size_t pIdx = 0; pIdx < m_selectedFractureInputProperties().size(); ++pIdx ) + { + resultSpecification.append( RimEclipseStatisticsCaseEvaluator::ResSpec( RiaDefines::PorosityModelType::FRACTURE_MODEL, + RiaDefines::ResultCatType::INPUT_PROPERTY, + m_selectedFractureInputProperties()[pIdx] ) ); + } + } + else if ( m_dataSourceForStatistics() == DataSourceType::GRID_CALCULATION && m_gridCalculation() ) { + auto calculationName = m_gridCalculation->shortName(); + resultSpecification.append( RimEclipseStatisticsCaseEvaluator::ResSpec( RiaDefines::PorosityModelType::MATRIX_MODEL, RiaDefines::ResultCatType::GENERATED, - m_selectedGeneratedProperties()[pIdx] ) ); + calculationName ) ); } - for ( size_t pIdx = 0; pIdx < m_selectedInputProperties().size(); ++pIdx ) - { - resultSpecification.append( RimEclipseStatisticsCaseEvaluator::ResSpec( RiaDefines::PorosityModelType::MATRIX_MODEL, - RiaDefines::ResultCatType::INPUT_PROPERTY, - m_selectedInputProperties()[pIdx] ) ); - } - - for ( size_t pIdx = 0; pIdx < m_selectedFractureDynamicProperties().size(); ++pIdx ) - { - resultSpecification.append( RimEclipseStatisticsCaseEvaluator::ResSpec( RiaDefines::PorosityModelType::FRACTURE_MODEL, - RiaDefines::ResultCatType::DYNAMIC_NATIVE, - m_selectedFractureDynamicProperties()[pIdx] ) ); - } - - for ( size_t pIdx = 0; pIdx < m_selectedFractureStaticProperties().size(); ++pIdx ) - { - resultSpecification.append( RimEclipseStatisticsCaseEvaluator::ResSpec( RiaDefines::PorosityModelType::FRACTURE_MODEL, - RiaDefines::ResultCatType::STATIC_NATIVE, - m_selectedFractureStaticProperties()[pIdx] ) ); - } - - for ( size_t pIdx = 0; pIdx < m_selectedFractureGeneratedProperties().size(); ++pIdx ) - { - resultSpecification.append( RimEclipseStatisticsCaseEvaluator::ResSpec( RiaDefines::PorosityModelType::FRACTURE_MODEL, - RiaDefines::ResultCatType::GENERATED, - m_selectedFractureGeneratedProperties()[pIdx] ) ); - } - - for ( size_t pIdx = 0; pIdx < m_selectedFractureInputProperties().size(); ++pIdx ) - { - resultSpecification.append( RimEclipseStatisticsCaseEvaluator::ResSpec( RiaDefines::PorosityModelType::FRACTURE_MODEL, - RiaDefines::ResultCatType::INPUT_PROPERTY, - m_selectedFractureInputProperties()[pIdx] ) ); - } - - RimEclipseStatisticsCaseEvaluator stat( sourceCases, timeStepIndices, statisticsConfig, resultCase, gridCaseGroup ); + RimEclipseStatisticsCaseEvaluator stat( sourceCases, + timeStepIndices, + statisticsConfig, + resultCase, + gridCaseGroup, + m_clearGridCalculationMemory() ); if ( m_useZeroAsInactiveCellValue ) { @@ -419,37 +492,63 @@ void RimEclipseStatisticsCase::defineUiOrdering( QString uiConfigName, caf::PdmU uiOrdering.add( &m_calculateEditCommand ); - caf::PdmUiGroup* group = uiOrdering.addNewGroup( "Summary of Calculation Setup" ); - group->add( &m_useZeroAsInactiveCellValue ); - m_useZeroAsInactiveCellValue.uiCapability()->setUiHidden( hasComputedStatistics() ); - group->add( &m_selectionSummary ); + { + caf::PdmUiGroup* group = uiOrdering.addNewGroup( "Summary of Calculation Setup" ); + group->add( &m_useZeroAsInactiveCellValue ); + m_useZeroAsInactiveCellValue.uiCapability()->setUiHidden( hasComputedStatistics() ); + group->add( &m_selectionSummary ); + } - group = uiOrdering.addNewGroup( "Properties to consider" ); - group->setUiHidden( hasComputedStatistics() ); - group->add( &m_resultType ); - group->add( &m_porosityModel ); - group->add( &m_selectedDynamicProperties ); - group->add( &m_selectedStaticProperties ); - group->add( &m_selectedGeneratedProperties ); - group->add( &m_selectedInputProperties ); - group->add( &m_selectedFractureDynamicProperties ); - group->add( &m_selectedFractureStaticProperties ); - group->add( &m_selectedFractureGeneratedProperties ); - group->add( &m_selectedFractureInputProperties ); + { + auto group = uiOrdering.addNewGroup( "Properties to consider" ); + group->setUiHidden( hasComputedStatistics() ); + group->add( &m_dataSourceForStatistics ); - group = uiOrdering.addNewGroup( "Percentile setup" ); - group->setUiHidden( hasComputedStatistics() ); - group->add( &m_calculatePercentiles ); - group->add( &m_percentileCalculationType ); - group->add( &m_lowPercentile ); - group->add( &m_midPercentile ); - group->add( &m_highPercentile ); + if ( m_dataSourceForStatistics() == DataSourceType::GRID_CALCULATION ) + { + group->add( &m_gridCalculation ); + group->add( &m_clearGridCalculationMemory ); + } + else + { + group->add( &m_resultType ); + group->add( &m_porosityModel ); + group->add( &m_selectedDynamicProperties ); + group->add( &m_selectedStaticProperties ); + group->add( &m_selectedGeneratedProperties ); + group->add( &m_selectedInputProperties ); + group->add( &m_selectedFractureDynamicProperties ); + group->add( &m_selectedFractureStaticProperties ); + group->add( &m_selectedFractureGeneratedProperties ); + group->add( &m_selectedFractureInputProperties ); + } + } - group = uiOrdering.addNewGroup( "Case Options" ); - group->add( &m_wellDataSourceCase ); - group->add( &m_activeFormationNames ); - group->add( &m_flipXAxis ); - group->add( &m_flipYAxis ); + { + auto group = uiOrdering.addNewGroup( "Time Step Selection" ); + group->setCollapsedByDefault(); + group->add( &m_selectedTimeSteps ); + } + + { + auto group = uiOrdering.addNewGroup( "Percentile setup" ); + group->setUiHidden( hasComputedStatistics() ); + group->add( &m_calculatePercentiles ); + group->add( &m_percentileCalculationType ); + group->add( &m_lowPercentile ); + group->add( &m_midPercentile ); + group->add( &m_highPercentile ); + } + + { + auto group = uiOrdering.addNewGroup( "Case Options" ); + group->add( &m_wellDataSourceCase ); + group->add( &m_activeFormationNames ); + group->add( &m_flipXAxis ); + group->add( &m_flipYAxis ); + } + + uiOrdering.skipRemainingFields(); } QList toOptionList( const QStringList& varList ) @@ -468,16 +567,63 @@ QList toOptionList( const QStringList& varList ) //-------------------------------------------------------------------------------------------------- QList RimEclipseStatisticsCase::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) { - QList options; - RimIdenticalGridCaseGroup* idgcg = caseGroup(); if ( !( caseGroup() && caseGroup()->mainCase() && caseGroup()->mainCase()->eclipseCaseData() ) ) { + return {}; + } + + if ( &m_dataSourceForStatistics == fieldNeedingOptions ) + { + QList options; + + { + caf::IconProvider iconProvider( ":/Case48x48.png" ); + options.push_back( caf::PdmOptionItemInfo( caf::AppEnum::uiText( DataSourceType::CASE_PROPERTY ), + DataSourceType::CASE_PROPERTY, + false, + iconProvider ) ); + } + { + caf::IconProvider iconProvider( ":/Calculator.svg" ); + options.push_back( caf::PdmOptionItemInfo( caf::AppEnum::uiText( DataSourceType::GRID_CALCULATION ), + DataSourceType::GRID_CALCULATION, + false, + iconProvider ) ); + } + + return options; + } + + if ( &m_gridCalculation == fieldNeedingOptions ) + { + QList options; + + for ( auto calc : RimProject::current()->gridCalculationCollection()->calculations() ) + { + options.push_back( caf::PdmOptionItemInfo( calc->shortName(), calc ) ); + } + return options; } RigEclipseCaseData* caseData = idgcg->mainCase()->eclipseCaseData(); + if ( &m_selectedTimeSteps == fieldNeedingOptions ) + { + QList options; + + const auto timeStepStrings = idgcg->mainCase()->timeStepStrings(); + + int index = 0; + for ( const auto& text : timeStepStrings ) + { + options.push_back( caf::PdmOptionItemInfo( text, index++ ) ); + } + + return options; + } + if ( &m_selectedDynamicProperties == fieldNeedingOptions ) { QStringList varList = @@ -540,9 +686,7 @@ QList RimEclipseStatisticsCase::calculateValueOptions( c return toOptionList( sourceCaseNames ); } - if ( options.empty() ) options = RimEclipseCase::calculateValueOptions( fieldNeedingOptions ); - - return options; + return RimEclipseCase::calculateValueOptions( fieldNeedingOptions ); } //-------------------------------------------------------------------------------------------------- @@ -662,15 +806,26 @@ void RimEclipseStatisticsCase::updateSelectionSummaryLabel() } html += "

"; - addPropertySetToHtmlText( html, "Dynamic properties", m_selectedDynamicProperties() ); - addPropertySetToHtmlText( html, "Static properties", m_selectedStaticProperties() ); - addPropertySetToHtmlText( html, "Generated properties", m_selectedGeneratedProperties() ); - addPropertySetToHtmlText( html, "Input properties", m_selectedInputProperties() ); + if ( m_dataSourceForStatistics() == DataSourceType::CASE_PROPERTY ) + { + addPropertySetToHtmlText( html, "Dynamic properties", m_selectedDynamicProperties() ); + addPropertySetToHtmlText( html, "Static properties", m_selectedStaticProperties() ); + addPropertySetToHtmlText( html, "Generated properties", m_selectedGeneratedProperties() ); + addPropertySetToHtmlText( html, "Input properties", m_selectedInputProperties() ); - addPropertySetToHtmlText( html, "Dynamic properties, fracture model", m_selectedFractureDynamicProperties() ); - addPropertySetToHtmlText( html, "Static properties, fracture model", m_selectedFractureStaticProperties() ); - addPropertySetToHtmlText( html, "Generated properties, fracture model", m_selectedFractureGeneratedProperties() ); - addPropertySetToHtmlText( html, "Input properties, fracture model", m_selectedFractureInputProperties() ); + addPropertySetToHtmlText( html, "Dynamic properties, fracture model", m_selectedFractureDynamicProperties() ); + addPropertySetToHtmlText( html, "Static properties, fracture model", m_selectedFractureStaticProperties() ); + addPropertySetToHtmlText( html, "Generated properties, fracture model", m_selectedFractureGeneratedProperties() ); + addPropertySetToHtmlText( html, "Input properties, fracture model", m_selectedFractureInputProperties() ); + } + + if ( m_dataSourceForStatistics() == DataSourceType::GRID_CALCULATION && m_gridCalculation() ) + { + html += "

Grid calculation:

"; + html += "

"; + html += m_gridCalculation()->shortName(); + html += "

"; + } m_selectionSummary = html; } @@ -699,6 +854,17 @@ void RimEclipseStatisticsCase::defineEditorAttribute( const caf::PdmFieldHandle* } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimEclipseStatisticsCase::initializeSelectedTimeSteps() +{ + if ( RimProject::current()->isProjectFileVersionEqualOrOlderThan( "2023.10.0" ) ) + { + selectAllTimeSteps(); + } +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/RimEclipseStatisticsCase.h b/ApplicationLibCode/ProjectDataModel/RimEclipseStatisticsCase.h index 1bade5cb64..91eba7bb7d 100644 --- a/ApplicationLibCode/ProjectDataModel/RimEclipseStatisticsCase.h +++ b/ApplicationLibCode/ProjectDataModel/RimEclipseStatisticsCase.h @@ -35,6 +35,7 @@ class RigSimWellData; class RimEclipseResultDefinition; class RimEclipseStatisticsCaseCollection; class RimIdenticalGridCaseGroup; +class RimGridCalculation; //================================================================================================== // @@ -45,6 +46,20 @@ class RimEclipseStatisticsCase : public RimEclipseCase { CAF_PDM_HEADER_INIT; +public: + enum class PercentileCalcType + { + NEAREST_OBSERVATION, + HISTOGRAM_ESTIMATED, + INTERPOLATED_OBSERVATION + }; + + enum class DataSourceType + { + GRID_CALCULATION, + CASE_PROPERTY + }; + public: RimEclipseStatisticsCase(); ~RimEclipseStatisticsCase() override; @@ -63,18 +78,12 @@ public: RimCaseCollection* parentStatisticsCaseCollection() const; - enum PercentileCalcType - { - NEAREST_OBSERVATION, - HISTOGRAM_ESTIMATED, - INTERPOLATED_OBSERVATION - }; - caf::PdmField m_calculateEditCommand; void populateResultSelectionAfterLoadingGrid(); void setSourceProperties( RiaDefines::ResultCatType propertyType, const std::vector& propertyNames ); + void selectAllTimeSteps(); private: void scheduleACTIVEGeometryRegenOnReservoirViews(); @@ -95,8 +104,16 @@ private: void loadSimulationWellDataFromSourceCase(); void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override; + void initializeSelectedTimeSteps(); private: + caf::PdmField> m_dataSourceForStatistics; + + caf::PdmPtrField m_gridCalculation; + caf::PdmField m_clearGridCalculationMemory; + + caf::PdmField> m_selectedTimeSteps; + caf::PdmField> m_resultType; caf::PdmField> m_porosityModel; diff --git a/ApplicationLibCode/ProjectDataModel/RimEclipseStatisticsCaseEvaluator.cpp b/ApplicationLibCode/ProjectDataModel/RimEclipseStatisticsCaseEvaluator.cpp index 2801701125..dbc20ecfcd 100644 --- a/ApplicationLibCode/ProjectDataModel/RimEclipseStatisticsCaseEvaluator.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimEclipseStatisticsCaseEvaluator.cpp @@ -116,23 +116,23 @@ void RimEclipseStatisticsCaseEvaluator::evaluateForResults( const QList for ( size_t timeIndicesIdx = 0; timeIndicesIdx < m_timeStepIndices.size(); timeIndicesIdx++ ) { - size_t timeStepIdx = m_timeStepIndices[timeIndicesIdx]; + auto timeStepIdx = m_timeStepIndices[timeIndicesIdx]; for ( size_t gridIdx = 0; gridIdx < m_destinationCase->gridCount(); gridIdx++ ) { RigGridBase* grid = m_destinationCase->grid( gridIdx ); - for ( int resSpecIdx = 0; resSpecIdx < resultSpecification.size(); resSpecIdx++ ) + for ( const auto& resSpec : resultSpecification ) { - RiaDefines::PorosityModelType poroModel = resultSpecification[resSpecIdx].m_poroModel; - RiaDefines::ResultCatType resultType = resultSpecification[resSpecIdx].m_resType; - QString resultName = resultSpecification[resSpecIdx].m_resVarName; + RiaDefines::PorosityModelType poroModel = resSpec.m_poroModel; + RiaDefines::ResultCatType resultType = resSpec.m_resType; + QString resultName = resSpec.m_resVarName; size_t activeCellCount = m_destinationCase->activeCellInfo( poroModel )->reservoirActiveCellCount(); if ( activeCellCount == 0 ) continue; - size_t dataAccessTimeStepIndex = timeStepIdx; + auto dataAccessTimeStepIndex = static_cast( timeStepIdx ); // Always evaluate statistics once, and always use time step index zero if ( resultType == RiaDefines::ResultCatType::STATIC_NATIVE ) @@ -145,10 +145,8 @@ void RimEclipseStatisticsCaseEvaluator::evaluateForResults( const QList // Build data access objects for source scalar results cvf::Collection sourceDataAccessList; - for ( size_t caseIdx = 0; caseIdx < m_sourceCases.size(); caseIdx++ ) + for ( RimEclipseCase* sourceCase : m_sourceCases ) { - RimEclipseCase* sourceCase = m_sourceCases.at( caseIdx ); - // Trigger loading of dataset // NB! Many other functions are based on loading of all time steps at the same time // Use this concept carefully @@ -184,21 +182,21 @@ void RimEclipseStatisticsCaseEvaluator::evaluateForResults( const QList statisticalResultNames[PMID] = createResultNamePVal( resultName, m_statisticsConfig.m_pMidPos ); statisticalResultNames[PMAX] = createResultNamePVal( resultName, m_statisticsConfig.m_pMaxPos ); - for ( size_t stIdx = 0; stIdx < statisticalResultNames.size(); ++stIdx ) + for ( const auto& statisticalResultName : statisticalResultNames ) { cvf::ref resultModifier = RigResultModifierFactory::createResultModifier( m_destinationCase, grid->gridIndex(), poroModel, dataAccessTimeStepIndex, - RigEclipseResultAddress( resultType, statisticalResultNames[stIdx] ) ); + RigEclipseResultAddress( resultType, statisticalResultName ) ); destinationDataAccessList.push_back( resultModifier.p() ); } std::vector statParams( STAT_PARAM_COUNT, HUGE_VAL ); std::vector values( sourceDataAccessList.size(), HUGE_VAL ); - int cellCount = static_cast( grid->cellCount() ); + auto cellCount = static_cast( grid->cellCount() ); // Loop over the cells in the grid, get the case values, and calculate the cell statistics #pragma omp parallel for schedule( dynamic ) firstprivate( statParams, values ) @@ -245,7 +243,7 @@ void RimEclipseStatisticsCaseEvaluator::evaluateForResults( const QList // Calculate percentiles if ( m_statisticsConfig.m_calculatePercentiles ) { - if ( m_statisticsConfig.m_pValMethod == RimEclipseStatisticsCase::NEAREST_OBSERVATION ) + if ( m_statisticsConfig.m_pValMethod == RimEclipseStatisticsCase::PercentileCalcType::NEAREST_OBSERVATION ) { std::vector pValPoss; pValPoss.push_back( m_statisticsConfig.m_pMinPos ); @@ -259,7 +257,7 @@ void RimEclipseStatisticsCaseEvaluator::evaluateForResults( const QList statParams[PMID] = pVals[1]; statParams[PMAX] = pVals[2]; } - else if ( m_statisticsConfig.m_pValMethod == RimEclipseStatisticsCase::HISTOGRAM_ESTIMATED ) + else if ( m_statisticsConfig.m_pValMethod == RimEclipseStatisticsCase::PercentileCalcType::HISTOGRAM_ESTIMATED ) { std::vector histogram; RigHistogramCalculator histCalc( statParams[MIN], statParams[MAX], 100, &histogram ); @@ -271,7 +269,8 @@ void RimEclipseStatisticsCaseEvaluator::evaluateForResults( const QList statParams[PMAX] = histCalc.calculatePercentil( m_statisticsConfig.m_pMaxPos / 100.0, RigStatisticsMath::PercentileStyle::SWITCHED ); } - else if ( m_statisticsConfig.m_pValMethod == RimEclipseStatisticsCase::INTERPOLATED_OBSERVATION ) + else if ( m_statisticsConfig.m_pValMethod == + RimEclipseStatisticsCase::PercentileCalcType::INTERPOLATED_OBSERVATION ) { std::vector pValPoss; pValPoss.push_back( m_statisticsConfig.m_pMinPos ); @@ -310,14 +309,15 @@ void RimEclipseStatisticsCaseEvaluator::evaluateForResults( const QList // Microsoft note: On Windows, the maximum number of files open at the same time is 512 // http://msdn.microsoft.com/en-us/library/kdfaxaay%28vs.71%29.aspx - for ( size_t caseIdx = 0; caseIdx < m_sourceCases.size(); caseIdx++ ) - { - RimEclipseCase* eclipseCase = m_sourceCases.at( caseIdx ); + std::vector categoriesToExclude; + if ( !m_clearGridCalculationMemory ) categoriesToExclude.push_back( RiaDefines::ResultCatType::GENERATED ); + for ( RimEclipseCase* eclipseCase : m_sourceCases ) + { if ( eclipseCase->reservoirViews.empty() ) { - eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->freeAllocatedResultsData(); - eclipseCase->results( RiaDefines::PorosityModelType::FRACTURE_MODEL )->freeAllocatedResultsData(); + eclipseCase->results( RiaDefines::PorosityModelType::MATRIX_MODEL )->freeAllocatedResultsData( categoriesToExclude, timeStepIdx ); + eclipseCase->results( RiaDefines::PorosityModelType::FRACTURE_MODEL )->freeAllocatedResultsData( categoriesToExclude, timeStepIdx ); } } @@ -330,11 +330,11 @@ void RimEclipseStatisticsCaseEvaluator::evaluateForResults( const QList //-------------------------------------------------------------------------------------------------- void RimEclipseStatisticsCaseEvaluator::addNamedResults( const QList& resultSpecification ) { - for ( int i = 0; i < resultSpecification.size(); i++ ) + for ( const auto& resSpec : resultSpecification ) { - RiaDefines::PorosityModelType poroModel = resultSpecification[i].m_poroModel; - RiaDefines::ResultCatType resultType = resultSpecification[i].m_resType; - QString resultName = resultSpecification[i].m_resVarName; + RiaDefines::PorosityModelType poroModel = resSpec.m_poroModel; + RiaDefines::ResultCatType resultType = resSpec.m_resType; + QString resultName = resSpec.m_resVarName; size_t activeCellCount = m_destinationCase->activeCellInfo( poroModel )->reservoirActiveCellCount(); if ( activeCellCount == 0 ) continue; @@ -361,9 +361,9 @@ void RimEclipseStatisticsCaseEvaluator::addNamedResults( const QList& r statisticalResultNames.push_back( createResultNamePVal( resultName, m_statisticsConfig.m_pMaxPos ) ); } - for ( size_t j = 0; j < statisticalResultNames.size(); ++j ) + for ( const auto& statisticalResultName : statisticalResultNames ) { - addNamedResult( destCellResultsData, resultType, statisticalResultNames[j], activeCellCount ); + addNamedResult( destCellResultsData, resultType, statisticalResultName, activeCellCount ); } } } @@ -372,10 +372,11 @@ void RimEclipseStatisticsCaseEvaluator::addNamedResults( const QList& r /// //-------------------------------------------------------------------------------------------------- RimEclipseStatisticsCaseEvaluator::RimEclipseStatisticsCaseEvaluator( const std::vector& sourceCases, - const std::vector& timeStepIndices, + const std::vector& timeStepIndices, const RimStatisticsConfig& statisticsConfig, RigEclipseCaseData* destinationCase, - RimIdenticalGridCaseGroup* identicalGridCaseGroup ) + RimIdenticalGridCaseGroup* identicalGridCaseGroup, + bool clearGridCalculationMemory ) : m_sourceCases( sourceCases ) , m_statisticsConfig( statisticsConfig ) , m_destinationCase( destinationCase ) @@ -383,6 +384,7 @@ RimEclipseStatisticsCaseEvaluator::RimEclipseStatisticsCaseEvaluator( const std: , m_timeStepIndices( timeStepIndices ) , m_identicalGridCaseGroup( identicalGridCaseGroup ) , m_useZeroAsInactiveCellValue( false ) + , m_clearGridCalculationMemory( clearGridCalculationMemory ) { if ( !sourceCases.empty() ) { diff --git a/ApplicationLibCode/ProjectDataModel/RimEclipseStatisticsCaseEvaluator.h b/ApplicationLibCode/ProjectDataModel/RimEclipseStatisticsCaseEvaluator.h index 7b086d9bd9..c92798276e 100644 --- a/ApplicationLibCode/ProjectDataModel/RimEclipseStatisticsCaseEvaluator.h +++ b/ApplicationLibCode/ProjectDataModel/RimEclipseStatisticsCaseEvaluator.h @@ -38,7 +38,7 @@ public: , m_pMinPos( 10.0 ) , m_pMidPos( 50.0 ) , m_pMaxPos( 90.0 ) - , m_pValMethod( RimEclipseStatisticsCase::INTERPOLATED_OBSERVATION ) + , m_pValMethod( RimEclipseStatisticsCase::PercentileCalcType::INTERPOLATED_OBSERVATION ) { } @@ -54,10 +54,11 @@ class RimEclipseStatisticsCaseEvaluator { public: RimEclipseStatisticsCaseEvaluator( const std::vector& sourceCases, - const std::vector& timeStepIndices, + const std::vector& timeStepIndices, const RimStatisticsConfig& statisticsConfig, RigEclipseCaseData* destinationCase, - RimIdenticalGridCaseGroup* identicalGridCaseGroup ); + RimIdenticalGridCaseGroup* identicalGridCaseGroup, + bool clearGridCalculationMemory ); struct ResSpec { @@ -105,11 +106,12 @@ private: private: std::vector m_sourceCases; - std::vector m_timeStepIndices; + std::vector m_timeStepIndices; size_t m_reservoirCellCount; RimStatisticsConfig m_statisticsConfig; RigEclipseCaseData* m_destinationCase; RimIdenticalGridCaseGroup* m_identicalGridCaseGroup; bool m_useZeroAsInactiveCellValue; + bool m_clearGridCalculationMemory; }; diff --git a/ApplicationLibCode/ProjectDataModel/RimGridCalculation.cpp b/ApplicationLibCode/ProjectDataModel/RimGridCalculation.cpp index fefeec8f7e..6540fb534e 100644 --- a/ApplicationLibCode/ProjectDataModel/RimGridCalculation.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimGridCalculation.cpp @@ -44,6 +44,8 @@ #include "expressionparser/ExpressionParser.h" +#include "cafProgressInfo.h" + #include CAF_PDM_SOURCE_INIT( RimGridCalculation, "RimGridCalculation" ); @@ -109,137 +111,9 @@ RimGridCalculationVariable* RimGridCalculation::createVariable() //-------------------------------------------------------------------------------------------------- bool RimGridCalculation::calculate() { - QString leftHandSideVariableName = RimGridCalculation::findLeftHandSide( m_expression ); + auto timeSteps = std::nullopt; - if ( !m_destinationCase ) - { - RiaLogging::errorInMessageBox( nullptr, - "Grid Property Calculator", - QString( "No destination case found for calculation : %1" ).arg( leftHandSideVariableName ) ); - - return false; - } - - auto [isOk, errorMessage] = validateVariables(); - if ( !isOk ) - { - RiaLogging::errorInMessageBox( nullptr, "Grid Property Calculator", errorMessage ); - return false; - } - - for ( auto variableCase : inputCases() ) - { - if ( !m_destinationCase->isGridSizeEqualTo( variableCase ) ) - { - QString msg = "Detected IJK mismatch between input cases and destination case. All grid " - "cases must have identical IJK sizes."; - RiaLogging::errorInMessageBox( nullptr, "Grid Property Calculator", msg ); - return false; - } - } - - const bool isMultipleCasesPresent = outputEclipseCases().size() > 1; - - if ( isMultipleCasesPresent ) - { - QString txt = "Starting calculation " + description() + " for " + QString::number( outputEclipseCases().size() ) + " cases."; - RiaLogging::info( txt ); - } - - bool anyErrorsDetected = false; - for ( RimEclipseCase* outputEclipseCase : outputEclipseCases() ) - { - if ( !outputEclipseCase ) - { - RiaLogging::errorInMessageBox( nullptr, - "Grid Property Calculator", - QString( "No case found for calculation : %1" ).arg( leftHandSideVariableName ) ); - anyErrorsDetected = true; - continue; - } - - auto porosityModel = RiaDefines::PorosityModelType::MATRIX_MODEL; - - RigEclipseResultAddress resAddr( RiaDefines::ResultCatType::GENERATED, leftHandSideVariableName ); - - if ( !outputEclipseCase->results( porosityModel )->ensureKnownResultLoaded( resAddr ) ) - { - bool needsToBeStored = false; - outputEclipseCase->results( porosityModel )->createResultEntry( resAddr, needsToBeStored ); - } - - outputEclipseCase->results( porosityModel )->clearScalarResult( resAddr ); - - // If an input grid is present, max time step count is zero. Make sure the time step count for the calculation is - // always 1 or more. - const size_t timeStepCount = std::max( size_t( 1 ), outputEclipseCase->results( porosityModel )->maxTimeStepCount() ); - - std::vector>* scalarResultFrames = - outputEclipseCase->results( porosityModel )->modifiableCellScalarResultTimesteps( resAddr ); - scalarResultFrames->resize( timeStepCount ); - - for ( size_t tsId = 0; tsId < timeStepCount; tsId++ ) - { - std::vector> values; - for ( size_t i = 0; i < m_variables.size(); i++ ) - { - RimGridCalculationVariable* v = dynamic_cast( m_variables[i] ); - CAF_ASSERT( v != nullptr ); - values.push_back( getInputVectorForVariable( v, tsId, porosityModel, outputEclipseCase ) ); - } - - ExpressionParser parser; - for ( size_t i = 0; i < m_variables.size(); i++ ) - { - RimGridCalculationVariable* v = dynamic_cast( m_variables[i] ); - CAF_ASSERT( v != nullptr ); - parser.assignVector( v->name(), values[i] ); - } - - std::vector resultValues; - resultValues.resize( values[0].size() ); - parser.assignVector( leftHandSideVariableName, resultValues ); - - QString errorText; - bool evaluatedOk = parser.expandIfStatementsAndEvaluate( m_expression, &errorText ); - - if ( evaluatedOk ) - { - if ( m_cellFilterView() ) - { - filterResults( m_cellFilterView(), values, m_defaultValueType(), m_defaultValue(), resultValues, porosityModel, outputEclipseCase ); - } - - scalarResultFrames->at( tsId ) = resultValues; - - m_isDirty = false; - } - else - { - QString s = "The following error message was received from the parser library : \n\n"; - s += errorText; - - RiaLogging::errorInMessageBox( nullptr, "Grid Property Calculator", s ); - return false; - } - - outputEclipseCase->updateResultAddressCollection(); - } - - if ( isMultipleCasesPresent ) - { - QString txt = " " + outputEclipseCase->caseUserDescription(); - RiaLogging::info( txt ); - } - } - - if ( isMultipleCasesPresent ) - { - QString txt = "Completed calculation " + description() + " for " + QString::number( outputEclipseCases().size() ) + " cases"; - RiaLogging::info( txt ); - } - - return !anyErrorsDetected; + return calculateForCases( outputEclipseCases(), timeSteps ); } //-------------------------------------------------------------------------------------------------- @@ -639,6 +513,148 @@ void RimGridCalculation::removeDependentObjects() } } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimGridCalculation::calculateForCases( const std::vector& calculationCases, std::optional> timeSteps ) +{ + if ( calculationCases.empty() ) return true; + + QString leftHandSideVariableName = RimGridCalculation::findLeftHandSide( m_expression ); + + auto [isOk, errorMessage] = validateVariables(); + if ( !isOk ) + { + RiaLogging::errorInMessageBox( nullptr, "Grid Property Calculator", errorMessage ); + return false; + } + + for ( auto calculationCase : calculationCases ) + { + for ( auto inputCase : inputCases() ) + { + if ( !calculationCase->isGridSizeEqualTo( inputCase ) ) + { + QString msg = "Detected IJK mismatch between input cases and destination case. All grid " + "cases must have identical IJK sizes."; + RiaLogging::errorInMessageBox( nullptr, "Grid Property Calculator", msg ); + return false; + } + } + } + + const bool isMultipleCasesPresent = calculationCases.size() > 1; + + if ( isMultipleCasesPresent ) + { + QString txt = "Starting calculation " + description() + " for " + QString::number( calculationCases.size() ) + " cases."; + RiaLogging::info( txt ); + } + + caf::ProgressInfo progressInfo( calculationCases.size(), "Processing Grid Calculations" ); + size_t progressIndex = 0; + + bool anyErrorsDetected = false; + for ( RimEclipseCase* calculationCase : calculationCases ) + { + if ( !calculationCase ) + { + RiaLogging::errorInMessageBox( nullptr, + "Grid Property Calculator", + QString( "No case found for calculation : %1" ).arg( leftHandSideVariableName ) ); + anyErrorsDetected = true; + continue; + } + + auto porosityModel = RiaDefines::PorosityModelType::MATRIX_MODEL; + + RigEclipseResultAddress resAddr( RiaDefines::ResultCatType::GENERATED, leftHandSideVariableName ); + + if ( !calculationCase->results( porosityModel )->ensureKnownResultLoaded( resAddr ) ) + { + bool needsToBeStored = false; + calculationCase->results( porosityModel )->createResultEntry( resAddr, needsToBeStored ); + } + + calculationCase->results( porosityModel )->clearScalarResult( resAddr ); + + // If an input grid is present, max time step count is zero. Make sure the time step count for the calculation is + // always 1 or more. + const size_t timeStepCount = std::max( size_t( 1 ), calculationCase->results( porosityModel )->maxTimeStepCount() ); + + std::vector>* scalarResultFrames = + calculationCase->results( porosityModel )->modifiableCellScalarResultTimesteps( resAddr ); + scalarResultFrames->resize( timeStepCount ); + + for ( size_t tsId = 0; tsId < timeStepCount; tsId++ ) + { + // Skip time steps that are not in the list of time steps to calculate + if ( timeSteps && std::find( timeSteps->begin(), timeSteps->end(), tsId ) == timeSteps->end() ) continue; + + std::vector> values; + for ( size_t i = 0; i < m_variables.size(); i++ ) + { + RimGridCalculationVariable* v = dynamic_cast( m_variables[i] ); + CAF_ASSERT( v != nullptr ); + values.push_back( getInputVectorForVariable( v, tsId, porosityModel, calculationCase ) ); + } + + ExpressionParser parser; + for ( size_t i = 0; i < m_variables.size(); i++ ) + { + RimGridCalculationVariable* v = dynamic_cast( m_variables[i] ); + CAF_ASSERT( v != nullptr ); + parser.assignVector( v->name(), values[i] ); + } + + std::vector resultValues; + resultValues.resize( values[0].size() ); + parser.assignVector( leftHandSideVariableName, resultValues ); + + QString errorText; + bool evaluatedOk = parser.expandIfStatementsAndEvaluate( m_expression, &errorText ); + + if ( evaluatedOk ) + { + if ( m_cellFilterView() ) + { + filterResults( m_cellFilterView(), values, m_defaultValueType(), m_defaultValue(), resultValues, porosityModel, calculationCase ); + } + + scalarResultFrames->at( tsId ) = resultValues; + + m_isDirty = false; + } + else + { + QString s = "The following error message was received from the parser library : \n\n"; + s += errorText; + + RiaLogging::errorInMessageBox( nullptr, "Grid Property Calculator", s ); + return false; + } + + calculationCase->updateResultAddressCollection(); + } + + if ( isMultipleCasesPresent ) + { + QString txt = " " + calculationCase->caseUserDescription(); + RiaLogging::info( txt ); + } + + progressInfo.setProgress( progressIndex++ ); + } + + if ( isMultipleCasesPresent ) + { + QString txt = "Completed calculation " + description() + " for " + QString::number( outputEclipseCases().size() ) + " cases"; + RiaLogging::info( txt ); + } + + return !anyErrorsDetected; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/RimGridCalculation.h b/ApplicationLibCode/ProjectDataModel/RimGridCalculation.h index 9091ec359b..719b72d6fc 100644 --- a/ApplicationLibCode/ProjectDataModel/RimGridCalculation.h +++ b/ApplicationLibCode/ProjectDataModel/RimGridCalculation.h @@ -49,11 +49,13 @@ public: bool preCalculate() const override; bool calculate() override; + void updateDependentObjects() override; void removeDependentObjects() override; std::vector outputEclipseCases() const; RigEclipseResultAddress outputAddress() const; + bool calculateForCases( const std::vector& calculationCases, std::optional> timeSteps ); std::vector inputCases() const; diff --git a/ApplicationLibCode/ProjectDataModel/RimGridCalculationCollection.cpp b/ApplicationLibCode/ProjectDataModel/RimGridCalculationCollection.cpp index e5f78b91c7..3a85a89e6d 100644 --- a/ApplicationLibCode/ProjectDataModel/RimGridCalculationCollection.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimGridCalculationCollection.cpp @@ -94,6 +94,22 @@ std::vector RimGridCalculationCollection::sortedGridCalcula return sortedCalculations; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +std::vector RimGridCalculationCollection::dependentCalculations( RimGridCalculation* sourceCalculation ) const +{ + // Find all dependent grid calculations recursively. The ordering of calculations is least dependent first. + + std::vector calculations; + + if ( !dependentCalculationsRecursively( sourceCalculation, calculations ) ) return {}; + + std::reverse( calculations.begin(), calculations.end() ); + + return calculations; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -102,6 +118,49 @@ void RimGridCalculationCollection::rebuildCaseMetaData() ensureValidCalculationIds(); } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RimGridCalculation* RimGridCalculationCollection::findCalculation( const QString& calculationName ) const +{ + for ( auto userCalculation : calculations() ) + { + auto gridCalculation = dynamic_cast( userCalculation ); + if ( gridCalculation && gridCalculation->shortName() == calculationName ) return gridCalculation; + } + + return nullptr; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RimGridCalculationCollection::dependentCalculationsRecursively( RimGridCalculation* sourceCalculation, + std::vector& calculations ) const +{ + if ( std::find( calculations.begin(), calculations.end(), sourceCalculation ) != calculations.end() ) + { + RiaLogging::error( "Detected circular dependency for " + sourceCalculation->description() ); + return false; + } + + calculations.push_back( sourceCalculation ); + + for ( auto v : sourceCalculation->allVariables() ) + { + auto gridVariable = dynamic_cast( v ); + if ( gridVariable->resultCategoryType() == RiaDefines::ResultCatType::GENERATED ) + { + if ( auto other = findCalculation( gridVariable->resultVariable() ) ) + { + if ( !dependentCalculationsRecursively( other, calculations ) ) return false; + } + } + } + + return true; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/RimGridCalculationCollection.h b/ApplicationLibCode/ProjectDataModel/RimGridCalculationCollection.h index 03a767837a..bc57397e43 100644 --- a/ApplicationLibCode/ProjectDataModel/RimGridCalculationCollection.h +++ b/ApplicationLibCode/ProjectDataModel/RimGridCalculationCollection.h @@ -36,7 +36,11 @@ public: RimGridCalculation* createCalculation() const override; std::vector sortedGridCalculations() const; + std::vector dependentCalculations( RimGridCalculation* sourceCalculation ) const; private: + RimGridCalculation* findCalculation( const QString& calculationName ) const; + bool dependentCalculationsRecursively( RimGridCalculation* sourceCalculation, std::vector& calculations ) const; + void initAfterRead() override; }; diff --git a/ApplicationLibCode/ProjectDataModel/RimIdenticalGridCaseGroup.cpp b/ApplicationLibCode/ProjectDataModel/RimIdenticalGridCaseGroup.cpp index 1f9065017e..6d95200da1 100644 --- a/ApplicationLibCode/ProjectDataModel/RimIdenticalGridCaseGroup.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimIdenticalGridCaseGroup.cpp @@ -433,6 +433,7 @@ RimEclipseStatisticsCase* RimIdenticalGridCaseGroup::createStatisticsCase( bool newStatisticsCase->openEclipseGridFile(); newStatisticsCase->eclipseCaseData()->computeActiveCellBoundingBoxes(); + newStatisticsCase->selectAllTimeSteps(); return newStatisticsCase; } diff --git a/ApplicationLibCode/ReservoirDataModel/RigCaseCellResultsData.cpp b/ApplicationLibCode/ReservoirDataModel/RigCaseCellResultsData.cpp index 94c44903c9..7db146a91d 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigCaseCellResultsData.cpp +++ b/ApplicationLibCode/ReservoirDataModel/RigCaseCellResultsData.cpp @@ -699,23 +699,34 @@ void RigCaseCellResultsData::clearAllResults() /// Removes all the actual numbers put into this object, and frees up the memory. /// Does not touch the metadata in any way //-------------------------------------------------------------------------------------------------- -void RigCaseCellResultsData::freeAllocatedResultsData() +void RigCaseCellResultsData::freeAllocatedResultsData( std::vector categoriesToExclude, + std::optional timeStepIndexToRelease ) { for ( size_t resultIdx = 0; resultIdx < m_cellScalarResults.size(); ++resultIdx ) { - if ( resultIdx < m_resultInfos.size() && - m_resultInfos[resultIdx].eclipseResultAddress().resultCatType() == RiaDefines::ResultCatType::GENERATED ) + if ( resultIdx < m_resultInfos.size() ) { - // Not possible to recreate generated results, keep them - continue; + auto resultCategory = m_resultInfos[resultIdx].eclipseResultAddress().resultCatType(); + if ( std::find( categoriesToExclude.begin(), categoriesToExclude.end(), resultCategory ) != categoriesToExclude.end() ) + { + // Keep generated results for these categories + continue; + } } - for ( auto& tsIdx : m_cellScalarResults[resultIdx] ) + for ( size_t index = 0; index < m_cellScalarResults[resultIdx].size(); index++ ) { + if ( timeStepIndexToRelease && index != *timeStepIndexToRelease ) + { + // Keep generated results for these time steps + continue; + } + + auto& dataForTimeStep = m_cellScalarResults[resultIdx][index]; // Using swap with an empty vector as that is the safest way to really get rid of the allocated data in a // vector std::vector empty; - tsIdx.swap( empty ); + dataForTimeStep.swap( empty ); } } } diff --git a/ApplicationLibCode/ReservoirDataModel/RigCaseCellResultsData.h b/ApplicationLibCode/ReservoirDataModel/RigCaseCellResultsData.h index c6ac4577c9..4165204a09 100644 --- a/ApplicationLibCode/ReservoirDataModel/RigCaseCellResultsData.h +++ b/ApplicationLibCode/ReservoirDataModel/RigCaseCellResultsData.h @@ -29,6 +29,7 @@ #include #include +#include #include class RifReaderInterface; @@ -116,7 +117,7 @@ public: void clearScalarResult( RiaDefines::ResultCatType type, const QString& resultName ); void clearScalarResult( const RigEclipseResultAddress& resultAddress ); void clearAllResults(); - void freeAllocatedResultsData(); + void freeAllocatedResultsData( std::vector categoriesToExclude, std::optional timeStepIndexToRelease ); void eraseAllSourSimData(); void setRemovedTagOnGeneratedResult( const RigEclipseResultAddress& resultAddress );