mirror of
https://github.com/OPM/ResInsight.git
synced 2026-08-27 05:37:21 -05:00
#14559 Delta Ensemble: Refresh calculated addresses for all realizations
A delta ensemble creates summary addresses for all realizations of the source ensembles. When a calculation was created after this, only the realizations showing tree nodes had their addresses recreated. The other realizations had no calculated address, and RimDeltaSummaryCase::values() discarded the vector as being present in only one of the source ensembles. Only realization 0 was plotted until the project was reloaded. Add RifSummaryReaderInterface::refreshCalculatedAddresses() to recreate the addresses of the calculated readers without touching the native readers, and call it for all cases in an ensemble when calculations are updated.
This commit is contained in:
@@ -176,6 +176,30 @@ void RifMultipleSummaryReaders::createAndSetAddresses()
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Recreate the addresses of the calculated readers, and leave the addresses of the native readers untouched. Calculation objects can be
|
||||
/// created and modified after the addresses of a summary case have been created, and the addresses of the calculated readers must be
|
||||
/// updated to reflect this.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RifMultipleSummaryReaders::refreshCalculatedAddresses()
|
||||
{
|
||||
// If no addresses have been created, do nothing. The calculated addresses will be created along with the addresses of the native
|
||||
// readers in createAndSetAddresses().
|
||||
if ( m_allResultAddresses.empty() ) return;
|
||||
|
||||
std::erase_if( m_allResultAddresses, []( const RifEclipseSummaryAddress& adr ) { return adr.isCalculated(); } );
|
||||
|
||||
for ( const auto& r : m_readers )
|
||||
{
|
||||
if ( dynamic_cast<RifCalculatedSummaryCurveReader*>( r.get() ) == nullptr ) continue;
|
||||
|
||||
r->createAndSetAddresses();
|
||||
|
||||
const auto& resultAddresses = r->allResultAddresses();
|
||||
m_allResultAddresses.insert( resultAddresses.begin(), resultAddresses.end() );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -41,6 +41,7 @@ public:
|
||||
RiaDefines::EclipseUnitSystem unitSystem() const override;
|
||||
|
||||
void createAndSetAddresses() override;
|
||||
void refreshCalculatedAddresses() override;
|
||||
|
||||
protected:
|
||||
size_t keywordCount() const override;
|
||||
|
||||
@@ -69,6 +69,13 @@ void RifSummaryReaderInterface::createAddressesIfRequired()
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RifSummaryReaderInterface::refreshCalculatedAddresses()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -55,6 +55,10 @@ public:
|
||||
virtual void createAndSetAddresses();
|
||||
void createAddressesIfRequired();
|
||||
|
||||
// Recreate the addresses provided by calculation objects. Does nothing if no addresses have been created yet, as the calculated
|
||||
// addresses in that case are created as part of createAddressesIfRequired().
|
||||
virtual void refreshCalculatedAddresses();
|
||||
|
||||
int serialNumber() const;
|
||||
|
||||
// Returns the number of result addresses. If no addresses are present, keywordCount() is returned.
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "RifSummaryReaderInterface.h"
|
||||
|
||||
#include "RimDeltaSummaryEnsemble.h"
|
||||
#include "RimObservedSummaryData.h"
|
||||
#include "RimSummaryCalculation.h"
|
||||
#include "RimSummaryCase.h"
|
||||
@@ -87,7 +88,23 @@ void RimSummaryCalculationCollection::updateDataDependingOnCalculations()
|
||||
|
||||
if ( auto summaryCaseCollection = RiaSummaryTools::summaryCaseMainCollection() )
|
||||
{
|
||||
// A delta ensemble derives its addresses from the source ensembles, and must be updated after the source ensembles. The delta
|
||||
// ensembles are stored after the source ensembles, as an ensemble must exist before it can be used as a source.
|
||||
std::vector<RimSummaryEnsemble*> deltaEnsembles;
|
||||
|
||||
for ( auto ensemble : summaryCaseCollection->summaryEnsembles() )
|
||||
{
|
||||
if ( dynamic_cast<RimDeltaSummaryEnsemble*>( ensemble ) )
|
||||
{
|
||||
deltaEnsembles.push_back( ensemble );
|
||||
}
|
||||
else
|
||||
{
|
||||
ensemble->onCalculationUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
for ( auto ensemble : deltaEnsembles )
|
||||
{
|
||||
ensemble->onCalculationUpdated();
|
||||
}
|
||||
|
||||
@@ -420,6 +420,20 @@ void RimDeltaSummaryCase::createSummaryReaderInterface()
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// The addresses of a delta case is the union of the addresses of the source cases. Rebuild the union to pick up calculated addresses
|
||||
/// created after the source case addresses were created, and discard cached values based on calculations that may have changed.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDeltaSummaryCase::refreshCalculatedAddresses()
|
||||
{
|
||||
// If no addresses have been created, do nothing. The addresses will be created on demand in summaryReader().
|
||||
if ( m_allResultAddresses.empty() ) return;
|
||||
|
||||
std::erase_if( m_dataCache, []( const auto& item ) { return item.first.isCalculated(); } );
|
||||
|
||||
createSummaryReaderInterface();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "cafPdmObject.h"
|
||||
#include "cafPdmPtrField.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
class RifEclipseSummaryAddress;
|
||||
@@ -79,6 +80,7 @@ public:
|
||||
|
||||
void createSummaryReaderInterface() override;
|
||||
RifSummaryReaderInterface* summaryReader() override;
|
||||
void refreshCalculatedAddresses() override;
|
||||
|
||||
void updateDisplayNameFromCases();
|
||||
|
||||
|
||||
@@ -1297,6 +1297,17 @@ void RimSummaryEnsemble::onCalculationUpdated()
|
||||
}
|
||||
}
|
||||
|
||||
// The addresses of the other cases in the ensemble are not created up front for performance reasons. If addresses have been created,
|
||||
// the calculated addresses must be refreshed to reflect the current set of calculation objects.
|
||||
// https://github.com/OPM/ResInsight/issues/14559
|
||||
for ( auto summaryCase : allSummaryCases() )
|
||||
{
|
||||
if ( auto reader = summaryCase->summaryReader() )
|
||||
{
|
||||
reader->refreshCalculatedAddresses();
|
||||
}
|
||||
}
|
||||
|
||||
m_dataVectorFolders->deleteCalculatedAddresses();
|
||||
m_dataVectorFolders->updateFolderStructure( ensembleSummaryAddresses(), -1, m_ensembleId );
|
||||
|
||||
|
||||
@@ -166,6 +166,7 @@ set(SOURCE_UNITTEST_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimMockSummaryCase.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimMockSummaryCase-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RimSummaryCalculation-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RifMultipleSummaryReaders-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RiaConnectorTools-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RigWellTargetMappingTools-Test.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/RicWellPathExportMswGeometryPath-Test.cpp
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "RifEclipseSummaryAddress.h"
|
||||
#include "RifMultipleSummaryReaders.h"
|
||||
|
||||
#include "RimCalculatedSummaryCurveReader.h"
|
||||
#include "RimMockSummaryCase.h"
|
||||
#include "RimProject.h"
|
||||
#include "RimSummaryAddress.h"
|
||||
#include "RimSummaryCalculation.h"
|
||||
#include "RimSummaryCalculationCollection.h"
|
||||
#include "RimSummaryCalculationVariable.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace
|
||||
{
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Minimal stand-in for a native file reader, providing a single address
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
class StubNativeSummaryReader : public RifSummaryReaderInterface
|
||||
{
|
||||
public:
|
||||
std::vector<time_t> timeSteps( const RifEclipseSummaryAddress& resultAddress ) const override { return {}; }
|
||||
std::pair<bool, std::vector<double>> values( const RifEclipseSummaryAddress& resultAddress ) const override { return { false, {} }; }
|
||||
std::string unitName( const RifEclipseSummaryAddress& resultAddress ) const override { return {}; }
|
||||
RiaDefines::EclipseUnitSystem unitSystem() const override { return RiaDefines::EclipseUnitSystem::UNITS_METRIC; }
|
||||
|
||||
void createAndSetAddresses() override { m_allResultAddresses.insert( RifEclipseSummaryAddress::fieldAddress( "FOPT" ) ); }
|
||||
|
||||
size_t keywordCount() const override { return m_allResultAddresses.size(); }
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Create a calculation producing a single field address
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimSummaryCalculation* createFieldCalculation()
|
||||
{
|
||||
auto* calculation = dynamic_cast<RimSummaryCalculation*>( RimProject::current()->calculationCollection()->addCalculation() );
|
||||
|
||||
calculation->setExpression( "MY_CALCULATION := x + 1" );
|
||||
calculation->parseExpression();
|
||||
|
||||
auto* variable = dynamic_cast<RimSummaryCalculationVariable*>( calculation->variables()->at( 0 ) );
|
||||
|
||||
RimSummaryAddress address;
|
||||
address.setAddress( RifEclipseSummaryAddress::fieldAddress( "FOPT" ) );
|
||||
variable->setSummaryAddress( address );
|
||||
|
||||
return calculation;
|
||||
}
|
||||
|
||||
size_t calculatedAddressCount( const RifSummaryReaderInterface& reader )
|
||||
{
|
||||
size_t count = 0;
|
||||
for ( const auto& adr : reader.allResultAddresses() )
|
||||
{
|
||||
if ( adr.isCalculated() ) count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// A calculation can be created after the addresses of a summary case have been created. The addresses of the calculated readers must
|
||||
/// then be refreshed, as done by RifMultipleSummaryReaders::refreshCalculatedAddresses().
|
||||
///
|
||||
/// https://github.com/OPM/ResInsight/issues/14559
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RifMultipleSummaryReaders, RefreshCalculatedAddresses )
|
||||
{
|
||||
RimMockSummaryCase summaryCase;
|
||||
|
||||
RifMultipleSummaryReaders multipleReaders;
|
||||
multipleReaders.addReader( std::make_unique<StubNativeSummaryReader>() );
|
||||
multipleReaders.addReader( std::make_unique<RifCalculatedSummaryCurveReader>( &summaryCase ) );
|
||||
|
||||
multipleReaders.createAndSetAddresses();
|
||||
|
||||
EXPECT_EQ( 1u, multipleReaders.allResultAddresses().size() );
|
||||
EXPECT_EQ( 0u, calculatedAddressCount( multipleReaders ) );
|
||||
|
||||
auto* calculation = createFieldCalculation();
|
||||
|
||||
multipleReaders.refreshCalculatedAddresses();
|
||||
|
||||
EXPECT_EQ( 2u, multipleReaders.allResultAddresses().size() );
|
||||
EXPECT_EQ( 1u, calculatedAddressCount( multipleReaders ) );
|
||||
|
||||
RimProject::current()->calculationCollection()->deleteCalculation( calculation );
|
||||
|
||||
multipleReaders.refreshCalculatedAddresses();
|
||||
|
||||
EXPECT_EQ( 1u, multipleReaders.allResultAddresses().size() );
|
||||
EXPECT_EQ( 0u, calculatedAddressCount( multipleReaders ) );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Addresses are not created up front for the realizations of an ensemble. Refreshing the calculated addresses must not create a partial
|
||||
/// set of addresses, as the calculated addresses are created along with the native addresses in createAndSetAddresses().
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RifMultipleSummaryReaders, RefreshCalculatedAddressesForReaderWithNoAddresses )
|
||||
{
|
||||
RimMockSummaryCase summaryCase;
|
||||
|
||||
RifMultipleSummaryReaders multipleReaders;
|
||||
multipleReaders.addReader( std::make_unique<StubNativeSummaryReader>() );
|
||||
multipleReaders.addReader( std::make_unique<RifCalculatedSummaryCurveReader>( &summaryCase ) );
|
||||
|
||||
auto* calculation = createFieldCalculation();
|
||||
|
||||
multipleReaders.refreshCalculatedAddresses();
|
||||
|
||||
EXPECT_TRUE( multipleReaders.allResultAddresses().empty() );
|
||||
|
||||
// The calculated addresses are created along with the addresses of the native readers
|
||||
multipleReaders.createAndSetAddresses();
|
||||
|
||||
EXPECT_EQ( 2u, multipleReaders.allResultAddresses().size() );
|
||||
EXPECT_EQ( 1u, calculatedAddressCount( multipleReaders ) );
|
||||
|
||||
RimProject::current()->calculationCollection()->deleteCalculation( calculation );
|
||||
}
|
||||
Reference in New Issue
Block a user