mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-30 17:10:19 -05:00
#14423 Rebuild delta ensemble derived cases declaratively
Derived cases were pooled through an m_inUse flag on RimDeltaSummaryCase. Every regeneration marked all cases not in use, which also severed their source references and cleared their caches, then handed them back out one by one and deleted whatever was left over. The flag conflated pool bookkeeping with owning the source references, allSummaryCases() hid the not-in-use cases from the rest of the project, and cases taken from the pool were pushed straight into m_cases without connecting nameChanged, so a renamed source case never propagated to the derived case. Replace the pooling with desiredSourceCasePairs(), a pure computation of the source case pairs the ensemble should have, and rebuildDerivedCases(), which diffs that against the existing derived cases keyed on the source case pointer pair. Matching cases are reused, missing ones are created, and surplus ones are detached and returned to the caller instead of being deleted in place. Keying on the pointer pair makes the rebuild idempotent, also right after project load where the derived cases arrive from XML with their sources already resolved. Add the protected RimSummaryEnsemble::addCaseWithoutDependencyUpdate(), used both by addCase() and by the rebuild, so a derived case gets nameChanged connected without triggering the dependent-ensemble notification that the rebuild is already performing itself. Remove setAllCasesNotInUse(), firstCaseNotInUse(), deleteCasesNoInUse(), RimDeltaSummaryCase::setInUse()/isInUse() and the m_inUse field, and add clearSourceCases() for the one thing setInUse(false) was actually needed for. Old project files keep loading, unknown XML keywords are skipped. The activeOnly parameter of allDerivedCases() is gone and RimDeltaSummaryEnsemble no longer overrides allSummaryCases().
This commit is contained in:
1 parent
f0d6081448
commit
6c0cc9c363
7 files changed
+227
-153
No files matched your search
@@ -183,34 +183,10 @@ RimDeltaSummaryCase::RimDeltaSummaryCase()
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_useFixedTimeStep, "UseFixedTimeStep", "Use Fixed Time Step" );
|
||||
CAF_PDM_InitField( &m_fixedTimeStepIndex, "FixedTimeStepIndex", 0, "Time Step" );
|
||||
CAF_PDM_InitField( &m_inUse, "InUse", false, "In Use" );
|
||||
m_fixedTimeStepIndex.uiCapability()->setUiEditorTypeName( caf::PdmUiTreeSelectionEditor::uiEditorTypeName() );
|
||||
m_fixedTimeStepIndex.uiCapability()->setUiLabelPosition( caf::PdmUiItemInfo::LabelPosition::HIDDEN );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDeltaSummaryCase::setInUse( bool inUse )
|
||||
{
|
||||
m_inUse = inUse;
|
||||
|
||||
if ( !m_inUse )
|
||||
{
|
||||
m_summaryCase1 = nullptr;
|
||||
m_summaryCase2 = nullptr;
|
||||
m_dataCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimDeltaSummaryCase::isInUse() const
|
||||
{
|
||||
return m_inUse;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -222,6 +198,18 @@ void RimDeltaSummaryCase::setSummaryCases( RimSummaryCase* sumCase1, RimSummaryC
|
||||
clearCache();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Sever the references to the source cases. Used when a derived case is detached from its ensemble,
|
||||
/// so it does not keep the source cases alive in any way after the ensemble has moved on.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDeltaSummaryCase::clearSourceCases()
|
||||
{
|
||||
m_summaryCase1 = nullptr;
|
||||
m_summaryCase2 = nullptr;
|
||||
|
||||
clearCache();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -56,9 +56,8 @@ public:
|
||||
public:
|
||||
RimDeltaSummaryCase();
|
||||
|
||||
void setInUse( bool inUse );
|
||||
bool isInUse() const;
|
||||
void setSummaryCases( RimSummaryCase* sumCase1, RimSummaryCase* sumCase2 );
|
||||
void clearSourceCases();
|
||||
void setOperator( DerivedSummaryOperator oper );
|
||||
void setFixedTimeSteps( int fixedTimeStepCase1, int fixedTimeStepCase2 );
|
||||
|
||||
@@ -111,8 +110,6 @@ private:
|
||||
caf::PdmField<caf::AppEnum<FixedTimeStepMode>> m_useFixedTimeStep;
|
||||
caf::PdmField<int> m_fixedTimeStepIndex;
|
||||
|
||||
caf::PdmField<bool> m_inUse;
|
||||
|
||||
// Local cache considered mutable
|
||||
mutable std::map<RifEclipseSummaryAddress, std::pair<std::vector<time_t>, std::vector<double>>> m_dataCache;
|
||||
};
|
||||
@@ -121,17 +121,6 @@ void RimDeltaSummaryEnsemble::setEnsemble2( RimSummaryEnsemble* ensemble )
|
||||
RiaSummaryTools::updateSummaryEnsembleNames();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RimSummaryCase*> RimDeltaSummaryEnsemble::allSummaryCases() const
|
||||
{
|
||||
std::vector<RimSummaryCase*> cases;
|
||||
for ( auto sumCase : allDerivedCases( true ) )
|
||||
cases.push_back( sumCase );
|
||||
return cases;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -164,48 +153,7 @@ void RimDeltaSummaryEnsemble::createDerivedEnsembleCases()
|
||||
{
|
||||
if ( !m_ensemble1 || !m_ensemble2 ) return;
|
||||
|
||||
setAllCasesNotInUse();
|
||||
|
||||
const auto cases1 = m_ensemble1->allSummaryCases();
|
||||
const auto cases2 = m_ensemble2->allSummaryCases();
|
||||
|
||||
for ( auto& sumCase1 : cases1 )
|
||||
{
|
||||
auto crp = sumCase1->caseRealizationParameters();
|
||||
if ( !crp ) continue;
|
||||
|
||||
RimSummaryCase* summaryCase2 = nullptr;
|
||||
if ( m_matchOnParameters )
|
||||
{
|
||||
summaryCase2 = findCaseByParametersHash( cases2, crp->parametersHash() );
|
||||
}
|
||||
else
|
||||
{
|
||||
summaryCase2 = findCaseByRealizationNumber( cases2, crp->realizationNumber() );
|
||||
}
|
||||
if ( !summaryCase2 ) continue;
|
||||
|
||||
auto derivedCase = firstCaseNotInUse();
|
||||
derivedCase->setSummaryCases( sumCase1, summaryCase2 );
|
||||
derivedCase->setOperator( m_operator() );
|
||||
|
||||
int fixedTimeStepCase1 = -1;
|
||||
int fixedTimeStepCase2 = -1;
|
||||
if ( m_useFixedTimeStep == FixedTimeStepMode::FIXED_TIME_STEP_CASE_1 )
|
||||
{
|
||||
fixedTimeStepCase1 = m_fixedTimeStepIndex;
|
||||
}
|
||||
else if ( m_useFixedTimeStep == FixedTimeStepMode::FIXED_TIME_STEP_CASE_2 )
|
||||
{
|
||||
fixedTimeStepCase2 = m_fixedTimeStepIndex;
|
||||
}
|
||||
|
||||
derivedCase->setFixedTimeSteps( fixedTimeStepCase1, fixedTimeStepCase2 );
|
||||
derivedCase->createSummaryReaderInterface();
|
||||
derivedCase->setCaseRealizationParameters( crp );
|
||||
derivedCase->setInUse( true );
|
||||
derivedCase->updateDisplayNameFromCases();
|
||||
}
|
||||
auto orphanedCases = rebuildDerivedCases();
|
||||
|
||||
// If other derived ensembles are referring to this ensemble, update their cases as well
|
||||
for ( auto referring : RimSummaryEnsembleTools::dependentDeltaEnsembles( this ) )
|
||||
@@ -213,7 +161,111 @@ void RimDeltaSummaryEnsemble::createDerivedEnsembleCases()
|
||||
referring->createDerivedEnsembleCases();
|
||||
}
|
||||
|
||||
deleteCasesNoInUse();
|
||||
for ( auto orphanedCase : orphanedCases )
|
||||
{
|
||||
delete orphanedCase;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// The source case pairs this ensemble should have derived cases for, in the order of the cases in
|
||||
/// the first source ensemble. Pure computation, the object graph is not modified.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<std::pair<RimSummaryCase*, RimSummaryCase*>> RimDeltaSummaryEnsemble::desiredSourceCasePairs() const
|
||||
{
|
||||
std::vector<std::pair<RimSummaryCase*, RimSummaryCase*>> casePairs;
|
||||
|
||||
if ( !m_ensemble1 || !m_ensemble2 ) return casePairs;
|
||||
|
||||
const auto cases1 = m_ensemble1->allSummaryCases();
|
||||
const auto cases2 = m_ensemble2->allSummaryCases();
|
||||
|
||||
for ( auto sumCase1 : cases1 )
|
||||
{
|
||||
auto crp = sumCase1->caseRealizationParameters();
|
||||
if ( !crp ) continue;
|
||||
|
||||
RimSummaryCase* summaryCase2 = m_matchOnParameters ? findCaseByParametersHash( cases2, crp->parametersHash() )
|
||||
: findCaseByRealizationNumber( cases2, crp->realizationNumber() );
|
||||
if ( !summaryCase2 ) continue;
|
||||
|
||||
casePairs.emplace_back( sumCase1, summaryCase2 );
|
||||
}
|
||||
|
||||
return casePairs;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Bring the derived cases in sync with desiredSourceCasePairs(). Existing derived cases are matched
|
||||
/// on the source case pair, so a rebuild producing the same pairs reuses the same objects.
|
||||
///
|
||||
/// Surplus cases are detached and returned, never deleted. Destroying them is the responsibility of
|
||||
/// the caller, which may be several frames above a caller still iterating over these very objects.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RimDeltaSummaryCase*> RimDeltaSummaryEnsemble::rebuildDerivedCases()
|
||||
{
|
||||
std::vector<RimDeltaSummaryCase*> orphanedCases;
|
||||
|
||||
std::map<std::pair<RimSummaryCase*, RimSummaryCase*>, RimDeltaSummaryCase*> reusableCases;
|
||||
for ( auto derivedCase : allDerivedCases() )
|
||||
{
|
||||
auto sourceCase1 = derivedCase->summaryCase1();
|
||||
auto sourceCase2 = derivedCase->summaryCase2();
|
||||
|
||||
// A case with unresolved sources, or a duplicate of a pair already seen, can not be reused
|
||||
if ( sourceCase1 && sourceCase2 && reusableCases.try_emplace( { sourceCase1, sourceCase2 }, derivedCase ).second ) continue;
|
||||
|
||||
orphanedCases.push_back( derivedCase );
|
||||
}
|
||||
|
||||
int fixedTimeStepCase1 = -1;
|
||||
int fixedTimeStepCase2 = -1;
|
||||
if ( m_useFixedTimeStep == FixedTimeStepMode::FIXED_TIME_STEP_CASE_1 )
|
||||
{
|
||||
fixedTimeStepCase1 = m_fixedTimeStepIndex;
|
||||
}
|
||||
else if ( m_useFixedTimeStep == FixedTimeStepMode::FIXED_TIME_STEP_CASE_2 )
|
||||
{
|
||||
fixedTimeStepCase2 = m_fixedTimeStepIndex;
|
||||
}
|
||||
|
||||
for ( const auto& [sourceCase1, sourceCase2] : desiredSourceCasePairs() )
|
||||
{
|
||||
RimDeltaSummaryCase* derivedCase = nullptr;
|
||||
|
||||
auto it = reusableCases.find( { sourceCase1, sourceCase2 } );
|
||||
if ( it != reusableCases.end() )
|
||||
{
|
||||
derivedCase = it->second;
|
||||
reusableCases.erase( it );
|
||||
}
|
||||
else
|
||||
{
|
||||
derivedCase = new RimDeltaSummaryCase();
|
||||
addCaseWithoutDependencyUpdate( derivedCase );
|
||||
derivedCase->setSummaryCases( sourceCase1, sourceCase2 );
|
||||
}
|
||||
|
||||
derivedCase->setOperator( m_operator() );
|
||||
derivedCase->setFixedTimeSteps( fixedTimeStepCase1, fixedTimeStepCase2 );
|
||||
derivedCase->createSummaryReaderInterface();
|
||||
derivedCase->setCaseRealizationParameters( sourceCase1->caseRealizationParameters() );
|
||||
derivedCase->updateDisplayNameFromCases();
|
||||
}
|
||||
|
||||
// The reusable cases not claimed by a desired pair are surplus
|
||||
for ( const auto& [casePair, derivedCase] : reusableCases )
|
||||
{
|
||||
orphanedCases.push_back( derivedCase );
|
||||
}
|
||||
|
||||
for ( auto orphanedCase : orphanedCases )
|
||||
{
|
||||
removeCase( orphanedCase, false );
|
||||
orphanedCase->clearSourceCases();
|
||||
}
|
||||
|
||||
return orphanedCases;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -431,68 +483,17 @@ void RimDeltaSummaryEnsemble::defineEditorAttribute( const caf::PdmFieldHandle*
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDeltaSummaryEnsemble::setAllCasesNotInUse()
|
||||
std::vector<RimDeltaSummaryCase*> RimDeltaSummaryEnsemble::allDerivedCases() const
|
||||
{
|
||||
for ( auto derCase : allDerivedCases( true ) )
|
||||
derCase->setInUse( false );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDeltaSummaryEnsemble::deleteCasesNoInUse()
|
||||
{
|
||||
std::vector<RimDeltaSummaryCase*> inactiveCases;
|
||||
auto allCases = allDerivedCases( false );
|
||||
std::copy_if( allCases.begin(),
|
||||
allCases.end(),
|
||||
std::back_inserter( inactiveCases ),
|
||||
[]( RimDeltaSummaryCase* derCase ) { return !derCase->isInUse(); } );
|
||||
|
||||
for ( auto derCase : inactiveCases )
|
||||
std::vector<RimDeltaSummaryCase*> derivedCases;
|
||||
for ( auto sumCase : allSummaryCases() )
|
||||
{
|
||||
removeCase( derCase );
|
||||
delete derCase;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimDeltaSummaryCase* RimDeltaSummaryEnsemble::firstCaseNotInUse()
|
||||
{
|
||||
auto allCases = allDerivedCases( false );
|
||||
auto itr = std::find_if( allCases.begin(), allCases.end(), []( RimDeltaSummaryCase* derCase ) { return !derCase->isInUse(); } );
|
||||
if ( itr != allCases.end() )
|
||||
{
|
||||
return *itr;
|
||||
}
|
||||
|
||||
// If no active case was found, add a new case to the collection
|
||||
auto newCase = new RimDeltaSummaryCase();
|
||||
|
||||
// Show realization data source for the first case. If we create for all, the performance will be bad
|
||||
newCase->setShowTreeNodes( m_cases.empty() );
|
||||
|
||||
m_cases.push_back( newCase );
|
||||
return newCase;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<RimDeltaSummaryCase*> RimDeltaSummaryEnsemble::allDerivedCases( bool activeOnly ) const
|
||||
{
|
||||
std::vector<RimDeltaSummaryCase*> activeCases;
|
||||
for ( auto sumCase : RimSummaryEnsemble::allSummaryCases() )
|
||||
{
|
||||
auto derivedCase = dynamic_cast<RimDeltaSummaryCase*>( sumCase );
|
||||
if ( derivedCase && ( !activeOnly || derivedCase->isInUse() ) )
|
||||
if ( auto derivedCase = dynamic_cast<RimDeltaSummaryCase*>( sumCase ) )
|
||||
{
|
||||
activeCases.push_back( derivedCase );
|
||||
derivedCases.push_back( derivedCase );
|
||||
}
|
||||
}
|
||||
return activeCases;
|
||||
return derivedCases;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -500,7 +501,7 @@ std::vector<RimDeltaSummaryCase*> RimDeltaSummaryEnsemble::allDerivedCases( bool
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimDeltaSummaryEnsemble::updateDerivedEnsembleCases()
|
||||
{
|
||||
for ( auto& derivedCase : allDerivedCases( true ) )
|
||||
for ( auto& derivedCase : allDerivedCases() )
|
||||
{
|
||||
derivedCase->createSummaryReaderInterface();
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@ public:
|
||||
void setEnsemble1( RimSummaryEnsemble* ensemble );
|
||||
void setEnsemble2( RimSummaryEnsemble* ensemble );
|
||||
|
||||
std::vector<RimSummaryCase*> allSummaryCases() const override;
|
||||
std::set<RifEclipseSummaryAddress> ensembleSummaryAddresses() const override;
|
||||
|
||||
bool hasCaseReference( const RimSummaryCase* sumCase ) const;
|
||||
@@ -63,6 +62,13 @@ public:
|
||||
void onSourceEnsembleChanged();
|
||||
void createDerivedEnsembleCases();
|
||||
|
||||
std::vector<std::pair<RimSummaryCase*, RimSummaryCase*>> desiredSourceCasePairs() const;
|
||||
|
||||
// Detaches and returns the derived cases no longer backed by a source case pair. The caller owns them.
|
||||
[[nodiscard]] std::vector<RimDeltaSummaryCase*> rebuildDerivedCases();
|
||||
|
||||
std::vector<RimDeltaSummaryCase*> allDerivedCases() const;
|
||||
|
||||
bool discardMissingOrIncompleteRealizations() const;
|
||||
|
||||
std::pair<std::string, std::string> nameKeys() const override;
|
||||
@@ -76,11 +82,6 @@ private:
|
||||
|
||||
void onSwapEnsemblesButtonClicked();
|
||||
|
||||
void setAllCasesNotInUse();
|
||||
void deleteCasesNoInUse();
|
||||
RimDeltaSummaryCase* firstCaseNotInUse();
|
||||
std::vector<RimDeltaSummaryCase*> allDerivedCases( bool activeOnly ) const;
|
||||
|
||||
void updateDerivedEnsembleCases();
|
||||
bool isValid() const;
|
||||
|
||||
|
||||
@@ -155,13 +155,7 @@ void RimSummaryEnsemble::removeCase( RimSummaryCase* summaryCase, bool notifyCha
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSummaryEnsemble::addCase( RimSummaryCase* summaryCase, bool notifyChange )
|
||||
{
|
||||
summaryCase->nameChanged.connect( this, &RimSummaryEnsemble::onCaseNameChanged );
|
||||
|
||||
summaryCase->setShowTreeNodes( m_cases.empty() );
|
||||
|
||||
m_cases.push_back( summaryCase );
|
||||
m_cachedSortedEnsembleParameters.clear();
|
||||
m_analyzer.reset();
|
||||
addCaseWithoutDependencyUpdate( summaryCase );
|
||||
|
||||
// Update derived ensemble cases (if any)
|
||||
std::vector<RimDeltaSummaryEnsemble*> referringObjects = objectsWithReferringPtrFieldsOfType<RimDeltaSummaryEnsemble>();
|
||||
@@ -180,6 +174,21 @@ void RimSummaryEnsemble::addCase( RimSummaryCase* summaryCase, bool notifyChange
|
||||
}
|
||||
|
||||
if ( notifyChange ) updateReferringCurveSetsZoomAll();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimSummaryEnsemble::addCaseWithoutDependencyUpdate( RimSummaryCase* summaryCase )
|
||||
{
|
||||
summaryCase->nameChanged.connect( this, &RimSummaryEnsemble::onCaseNameChanged );
|
||||
|
||||
// Show realization data source for the first case. If we create for all, the performance will be bad
|
||||
summaryCase->setShowTreeNodes( m_cases.empty() );
|
||||
|
||||
m_cases.push_back( summaryCase );
|
||||
m_cachedSortedEnsembleParameters.clear();
|
||||
m_analyzer.reset();
|
||||
|
||||
clearChildNodes();
|
||||
}
|
||||
|
||||
@@ -128,6 +128,10 @@ public:
|
||||
protected:
|
||||
virtual void onLoadDataAndUpdate();
|
||||
|
||||
// Add a case without notifying the delta ensembles depending on this ensemble. Used when the caller is itself the
|
||||
// owner of the dependency update, as is the case when a delta ensemble rebuilds its derived cases.
|
||||
void addCaseWithoutDependencyUpdate( RimSummaryCase* summaryCase );
|
||||
|
||||
void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override;
|
||||
void buildMetaData();
|
||||
void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override;
|
||||
|
||||
Reference in new issue
Block a user