///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2024- Equinor ASA // // ResInsight is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RimSummaryEnsembleTools.h" #include "Summary/RiaSummaryTools.h" #include "RifReaderRftInterface.h" #include "RifSummaryReaderInterface.h" #include "RigEnsembleParameter.h" #include "RimEnsembleCurveSet.h" #include "RimSummaryCase.h" #include "RimSummaryCurve.h" #include "RimSummaryMultiPlot.h" #include "RimSummaryMultiPlotCollection.h" #include "RimSummaryPlot.h" #include "RiuPlotMainWindow.h" #include "cafPdmUiTreeView.h" //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::set RimSummaryEnsembleTools::wellsWithRftData( const std::vector& summaryCases ) { std::set allWellNames; for ( auto summaryCase : summaryCases ) { if ( auto reader = summaryCase->rftReader() ) { std::set wellNames = reader->wellNames(); allWellNames.insert( wellNames.begin(), wellNames.end() ); } } return allWellNames; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RigEnsembleParameter RimSummaryEnsembleTools::createEnsembleParameter( const std::vector& summaryCases, const QString& paramName ) { RigEnsembleParameter eParam; eParam.name = paramName; size_t numericValuesCount = 0; size_t textValuesCount = 0; // Make sure the values list exactly matches the case count // And use an invalid value (infinity) for invalid cases. eParam.values.resize( summaryCases.size(), std::numeric_limits::infinity() ); // Prepare case realization params, and check types for ( size_t caseIdx = 0; caseIdx < summaryCases.size(); ++caseIdx ) { auto rimCase = summaryCases[caseIdx]; auto crp = rimCase->caseRealizationParameters(); if ( !crp ) continue; auto value = crp->parameterValue( paramName ); if ( !value.isValid() ) continue; if ( value.isNumeric() ) { double numVal = value.numericValue(); eParam.values[caseIdx] = QVariant( numVal ); if ( numVal < eParam.minValue ) eParam.minValue = numVal; if ( numVal > eParam.maxValue ) eParam.maxValue = numVal; numericValuesCount++; } else if ( value.isText() ) { eParam.values[caseIdx] = QVariant( value.textValue() ); textValuesCount++; } } if ( numericValuesCount && !textValuesCount ) { eParam.type = RigEnsembleParameter::TYPE_NUMERIC; } else if ( textValuesCount && !numericValuesCount ) { eParam.type = RigEnsembleParameter::TYPE_TEXT; } if ( numericValuesCount && textValuesCount ) { // A mix of types have been added to parameter values if ( numericValuesCount > textValuesCount ) { // Use numeric type for ( auto& val : eParam.values ) { if ( val.type() == QVariant::String ) { val.setValue( std::numeric_limits::infinity() ); } } eParam.type = RigEnsembleParameter::TYPE_NUMERIC; } else { // Use text type for ( auto& val : eParam.values ) { if ( val.type() == QVariant::Double ) { val.setValue( QString::number( val.value() ) ); } } eParam.type = RigEnsembleParameter::TYPE_TEXT; eParam.minValue = std::numeric_limits::infinity(); eParam.maxValue = -std::numeric_limits::infinity(); } } if ( eParam.isText() ) { // Remove duplicate texts std::set valueSet; for ( const auto& val : eParam.values ) { valueSet.insert( val.toString() ); } eParam.values.clear(); for ( const auto& val : valueSet ) { eParam.values.push_back( QVariant( val ) ); } } return eParam; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryEnsembleTools::sortByBinnedVariation( std::vector& parameterVector ) { double minStdDev = std::numeric_limits::infinity(); double maxStdDev = 0.0; for ( const auto& paramPair : parameterVector ) { double stdDev = paramPair.normalizedStdDeviation(); if ( stdDev != 0.0 ) { minStdDev = std::min( minStdDev, stdDev ); maxStdDev = std::max( maxStdDev, stdDev ); } } if ( ( maxStdDev - minStdDev ) <= 0.0 ) { return; } double delta = ( maxStdDev - minStdDev ) / (float)( RigEnsembleParameter::NR_OF_VARIATION_BINS ); std::vector bins; bins.push_back( 0.0 ); for ( int i = 0; i < RigEnsembleParameter::NR_OF_VARIATION_BINS - 1; ++i ) { bins.push_back( minStdDev + ( i + 1 ) * delta ); } for ( RigEnsembleParameter& nameParamPair : parameterVector ) { int binNumber = -1; for ( double bin : bins ) { if ( nameParamPair.normalizedStdDeviation() > bin ) { binNumber++; } } nameParamPair.variationBin = binNumber; } // Sort by variation bin (highest first) but keep name as sorting parameter when parameters have the same variation // index std::stable_sort( parameterVector.begin(), parameterVector.end(), []( const RigEnsembleParameter& lhs, const RigEnsembleParameter& rhs ) { return lhs.variationBin > rhs.variationBin; } ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimSummaryEnsembleTools::alphabeticEnsembleParameters( const std::vector& summaryCases ) { std::set paramSet; for ( RimSummaryCase* rimCase : summaryCases ) { if ( rimCase->caseRealizationParameters() != nullptr ) { auto ps = rimCase->caseRealizationParameters()->parameters(); for ( auto p : ps ) { paramSet.insert( p.first ); } } } std::vector sortedEnsembleParameters; sortedEnsembleParameters.reserve( paramSet.size() ); for ( const QString& parameterName : paramSet ) { sortedEnsembleParameters.push_back( RimSummaryEnsembleTools::createEnsembleParameter( summaryCases, parameterName ) ); } return sortedEnsembleParameters; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- std::vector RimSummaryEnsembleTools::createVariationSortedEnsembleParameters( const std::vector& summaryCases ) { std::set paramSet; for ( RimSummaryCase* rimCase : summaryCases ) { if ( rimCase->caseRealizationParameters() != nullptr ) { auto ps = rimCase->caseRealizationParameters()->parameters(); for ( const auto& p : ps ) { paramSet.insert( p.first ); } } } std::vector parameters; parameters.reserve( paramSet.size() ); for ( const QString& parameterName : paramSet ) { auto ensembleParameter = RimSummaryEnsembleTools::createEnsembleParameter( summaryCases, parameterName ); parameters.push_back( ensembleParameter ); } RimSummaryEnsembleTools::sortByBinnedVariation( parameters ); return parameters; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void clearEnsembleParametersHashes( const std::vector& summaryCases ) { for ( auto sumCase : summaryCases ) { auto crp = sumCase->caseRealizationParameters(); if ( crp ) crp->clearParametersHash(); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- size_t RimSummaryEnsembleTools::calculateEnsembleParametersIntersectionHash( const std::vector& summaryCases ) { clearEnsembleParametersHashes( summaryCases ); // Find ensemble parameters intersection std::set paramNames; for ( size_t i = 0; i < summaryCases.size(); i++ ) { auto crp = summaryCases[i]->caseRealizationParameters(); if ( !crp ) continue; auto caseParamNames = crp->parameterNames(); if ( i == 0 ) paramNames = caseParamNames; else { std::set newIntersection; std::set_intersection( paramNames.begin(), paramNames.end(), caseParamNames.begin(), caseParamNames.end(), std::inserter( newIntersection, newIntersection.end() ) ); if ( paramNames.size() != newIntersection.size() ) paramNames = newIntersection; } } for ( auto sumCase : summaryCases ) { auto crp = sumCase->caseRealizationParameters(); if ( crp ) crp->calculateParametersHash( paramNames ); } size_t commonAddressCount = 0; // Find common address count for ( const auto sumCase : summaryCases ) { const auto reader = sumCase->summaryReader(); if ( !reader ) continue; auto currAddrCount = reader->allResultAddresses().size(); if ( commonAddressCount == 0 ) { commonAddressCount = currAddrCount; } else { if ( currAddrCount != commonAddressCount ) { commonAddressCount = 0; break; } } } return commonAddressCount; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryEnsembleTools::isEnsembleCurve( RimPlotCurve* sourceCurve ) { auto summaryCurve = dynamic_cast( sourceCurve ); if ( !summaryCurve ) return false; return summaryCurve->isEnsembleCurve(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryEnsembleTools::highlightCurvesForSameRealizationFromCurve( RimPlotCurve* sourceCurve ) { auto sourceSummaryCurve = dynamic_cast( sourceCurve ); if ( !sourceSummaryCurve ) return; auto ensembleCurveSet = sourceSummaryCurve->firstAncestorOfType(); if ( !ensembleCurveSet ) return; auto sourceCase = sourceSummaryCurve->summaryCaseY(); if ( !sourceCase ) return; // Select the realization object in Data Sources Tree view if ( auto mainWindow = RiuPlotMainWindow::instance() ) { if ( auto treeView = mainWindow->getTreeViewWithItem( sourceCase ) ) { treeView->selectAsCurrentItem( sourceCase ); } } highlightCurvesForSameRealization( sourceCase ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryEnsembleTools::highlightCurvesForSameRealization( RimSummaryCase* sourceCase ) { if ( !sourceCase ) return; auto summaryPlotColl = RiaSummaryTools::summaryMultiPlotCollection(); for ( auto multiPlot : summaryPlotColl->multiPlots() ) { for ( auto plot : multiPlot->summaryPlots() ) { auto plotWidget = dynamic_cast( plot->plotWidget() ); if ( !plotWidget ) continue; auto summaryCurves = plot->summaryAndEnsembleCurves(); std::vector curvesForSameRealization; for ( auto curve : summaryCurves ) { if ( sourceCase == curve->summaryCaseY() ) { curvesForSameRealization.push_back( curve ); } } if ( !curvesForSameRealization.empty() ) { bool updateCurveOrder = false; plotWidget->resetPlotItemHighlighting( updateCurveOrder ); std::set realizationCurvesSet( curvesForSameRealization.begin(), curvesForSameRealization.end() ); plotWidget->highlightCurvesUpdateOrder( realizationCurvesSet ); plotWidget->replot(); } } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryEnsembleTools::resetHighlightAllPlots() { auto summaryPlotColl = RiaSummaryTools::summaryMultiPlotCollection(); for ( auto multiPlot : summaryPlotColl->multiPlots() ) { for ( auto plot : multiPlot->summaryPlots() ) { if ( auto plotWidget = dynamic_cast( plot->plotWidget() ) ) { plotWidget->resetPlotItemHighlighting(); plotWidget->replot(); } } } }