Files
ResInsight/ApplicationLibCode/ProjectDataModel/Histogram/RimEnsembleSummaryVectorHistogramDataSource.cpp
Kristian Bendiksen 5674130504 #12720 Histogram: Recreate summary vector curves when dropping an ensemble
When an ensemble is dropped on a histogram plot showing summary vector curves,
create a new curve per distinct vector and time step for the dropped ensemble,
matching the existing behavior for ensemble parameter curves.
2026-08-13 07:19:18 +02:00

354 lines
14 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2025 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 <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RimEnsembleSummaryVectorHistogramDataSource.h"
#include "RiaLogging.h"
#include "RiaPreferences.h"
#include "RiaQDateTimeTools.h"
#include "RiaResultNames.h"
#include "RifSummaryReaderInterface.h"
#include "Histogram/RimHistogramPlot.h"
#include "RimProject.h"
#include "RimSummaryCase.h"
#include "RimSummaryEnsemble.h"
#include "RigStatisticsMath.h"
#include "cafPdmUiComboBoxEditor.h"
#include "cafPdmUiLineEditor.h"
CAF_PDM_XML_SOURCE_INIT( RimEnsembleSummaryVectorHistogramDataSource, "EnsembleSummaryVectorHistogramDataSource" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEnsembleSummaryVectorHistogramDataSource::RimEnsembleSummaryVectorHistogramDataSource()
{
CAF_PDM_InitObject( "Ensemble Summary Vector Histogram Data Source", );
CAF_PDM_InitFieldNoDefault( &m_ensemble, "Ensemble", "Ensemble" );
CAF_PDM_InitFieldNoDefault( &m_summaryAddressUiField, "SelectedVariableDisplayVar", "Vector" );
m_summaryAddressUiField.xmlCapability()->disableIO();
m_summaryAddressUiField.uiCapability()->setUiEditorTypeName( caf::PdmUiLineEditor::uiEditorTypeName() );
CAF_PDM_InitFieldNoDefault( &m_summaryAddress, "SummaryAddress", "Summary Address" );
m_summaryAddress.uiCapability()->setUiTreeChildrenHidden( true );
m_summaryAddress = new RimSummaryAddress;
CAF_PDM_InitFieldNoDefault( &m_timeStep, "TimeStep", "Time Step" );
m_timeStep.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() );
CAF_PDM_InitField( &m_numBins, "NumBins", 15, "Number of Bins" );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimEnsembleSummaryVectorHistogramDataSource::~RimEnsembleSummaryVectorHistogramDataSource()
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QList<caf::PdmOptionItemInfo> RimEnsembleSummaryVectorHistogramDataSource::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions )
{
QList<caf::PdmOptionItemInfo> options;
if ( fieldNeedingOptions == &m_ensemble )
{
RimProject* proj = RimProject::current();
for ( RimSummaryEnsemble* ensemble : proj->summaryEnsembles() )
{
if ( ensemble->isEnsemble() ) options.push_back( caf::PdmOptionItemInfo( ensemble->name(), ensemble ) );
}
}
else if ( fieldNeedingOptions == &m_summaryAddressUiField )
{
appendOptionItemsForSummaryAddresses( &options, m_ensemble );
}
else if ( fieldNeedingOptions == &m_timeStep )
{
if ( m_ensemble )
{
std::set<time_t> allTimeSteps = m_ensemble->ensembleTimeSteps();
std::vector<QDateTime> allDateTimes;
for ( time_t timeStep : allTimeSteps )
{
QDateTime dateTime = RiaQDateTimeTools::fromTime_t( timeStep );
options.push_back( caf::PdmOptionItemInfo( formatDateTime( dateTime ), dateTime ) );
}
}
}
return options;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleSummaryVectorHistogramDataSource::appendOptionItemsForSummaryAddresses( QList<caf::PdmOptionItemInfo>* options,
RimSummaryEnsemble* summaryCaseGroup )
{
if ( !summaryCaseGroup ) return;
auto allSummaryCases = summaryCaseGroup->allSummaryCases();
auto addressesForEnsemble = summaryCaseGroup->ensembleSummaryAddresses();
for ( const auto& addr : addressesForEnsemble )
{
std::string name = addr.uiText();
QString s = QString::fromStdString( name );
options->push_back( caf::PdmOptionItemInfo( s, QVariant::fromValue( addr ) ) );
}
options->push_front( caf::PdmOptionItemInfo( RiaResultNames::undefinedResultName(), QVariant::fromValue( RifEclipseSummaryAddress() ) ) );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleSummaryVectorHistogramDataSource::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
m_summaryAddressUiField = m_summaryAddress->address();
uiOrdering.add( &m_ensemble );
uiOrdering.add( &m_summaryAddressUiField );
uiOrdering.add( &m_timeStep );
appendBinningUiOrdering( uiOrdering, &m_numBins );
uiOrdering.skipRemainingFields( true );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleSummaryVectorHistogramDataSource::fieldChangedByUi( const caf::PdmFieldHandle* changedField,
const QVariant& oldValue,
const QVariant& newValue )
{
RimHistogramDataSource::fieldChangedByUi( changedField, oldValue, newValue );
if ( changedField == &m_ensemble )
{
updateConnectedEditors();
}
else if ( changedField == &m_summaryAddressUiField )
{
m_summaryAddress->setAddress( m_summaryAddressUiField() );
}
dataSourceChanged.send();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::string RimEnsembleSummaryVectorHistogramDataSource::unitNameY() const
{
return "";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::string RimEnsembleSummaryVectorHistogramDataSource::unitNameX() const
{
return "";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<double> RimEnsembleSummaryVectorHistogramDataSource::extractValuesFromEnsemble() const
{
std::vector<double> values;
time_t selectedTimestep = m_timeStep().toSecsSinceEpoch();
auto timeDiff = []( time_t lhs, time_t rhs ) -> time_t
{
if ( lhs >= rhs )
{
return lhs - rhs;
}
return rhs - lhs;
};
if ( m_summaryAddress && m_ensemble )
{
for ( size_t caseIdx = 0u; caseIdx < m_ensemble->allSummaryCases().size(); ++caseIdx )
{
auto summaryCase = m_ensemble->allSummaryCases()[caseIdx];
RifSummaryReaderInterface* reader = summaryCase->summaryReader();
if ( !reader ) continue;
double summaryValue = std::numeric_limits<double>::infinity();
time_t closestTimeStep = 0;
auto [isOk, summaryValues] = reader->values( m_summaryAddressUiField );
if ( isOk )
{
const std::vector<time_t>& timeSteps = reader->timeSteps( m_summaryAddressUiField );
for ( size_t i = 0; i < timeSteps.size(); ++i )
{
if ( timeDiff( timeSteps[i], selectedTimestep ) < timeDiff( selectedTimestep, closestTimeStep ) )
{
summaryValue = summaryValues[i];
closestTimeStep = timeSteps[i];
}
}
}
if ( summaryValue != std::numeric_limits<double>::infinity() ) values.push_back( summaryValue );
}
}
return values;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RimHistogramDataSource::HistogramResult RimEnsembleSummaryVectorHistogramDataSource::compute( RimHistogramPlot::GraphType graphType,
RimHistogramPlot::FrequencyType frequencyType,
bool cumulative ) const
{
RimHistogramDataSource::HistogramResult result;
std::vector<double> values = extractValuesFromEnsemble();
if ( values.empty() ) return result;
auto [min_it, max_it] = std::minmax_element( values.begin(), values.end() );
PosNegAccumulator posNegAccumulator;
posNegAccumulator.addData( values );
auto [min, max] = binRange( *min_it, *max_it, posNegAccumulator.pos );
if ( min > max || RigStatisticsTools::isInvalidNumber( min ) || RigStatisticsTools::isInvalidNumber( max ) ) return result;
result.valuesX = computeHistogramBins( min, max, m_numBins, graphType, cumulative, binningMode() );
std::vector<size_t> histogram;
RigHistogramCalculator histCalc( min, max, m_numBins, &histogram, binningMode(), outOfRangeHandling() );
histCalc.addData( values );
result.valuesY = computeHistogramFrequencies( histogram, graphType, frequencyType, cumulative );
double p10, p50, p90, mean;
RigStatisticsMath::calculateStatisticsCurves( values, &p10, &p50, &p90, &mean, RigStatisticsMath::PercentileStyle::SWITCHED );
result.p10 = p10;
result.mean = mean;
result.p90 = p90;
return result;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::string RimEnsembleSummaryVectorHistogramDataSource::name() const
{
std::string name = "";
if ( m_ensemble ) name = m_ensemble->name().toStdString();
if ( !m_summaryAddress->address().uiText().empty() ) name += ", " + m_summaryAddress->address().uiText();
if ( m_timeStep().isValid() ) name += ", " + formatDateTime( m_timeStep() ).toStdString();
return name;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleSummaryVectorHistogramDataSource::setEnsemble( RimSummaryEnsemble* ensemble )
{
m_ensemble = ensemble;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleSummaryVectorHistogramDataSource::setSummaryAddress( const RifEclipseSummaryAddress& summaryAddress )
{
m_summaryAddressUiField = summaryAddress;
m_summaryAddress->setAddress( m_summaryAddressUiField );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleSummaryVectorHistogramDataSource::setTimeStep( const QDateTime& timeStep )
{
m_timeStep = timeStep;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RifEclipseSummaryAddress RimEnsembleSummaryVectorHistogramDataSource::summaryAddress() const
{
return m_summaryAddress->address();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QDateTime RimEnsembleSummaryVectorHistogramDataSource::timeStep() const
{
return m_timeStep();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimEnsembleSummaryVectorHistogramDataSource::formatDateTime( const QDateTime& dateTime ) const
{
QString dateFormatString = RiaQDateTimeTools::dateFormatString( RiaPreferences::current()->dateFormat(),
RiaDefines::DateFormatComponents::DATE_FORMAT_YEAR_MONTH_DAY );
return RiaQDateTimeTools::toStringUsingApplicationLocale( dateTime, dateFormatString );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimEnsembleSummaryVectorHistogramDataSource::setDefaults()
{
RimProject* proj = RimProject::current();
std::vector<RimSummaryEnsemble*> ensembles = proj->summaryEnsembles();
if ( !ensembles.empty() )
{
m_ensemble = ensembles[0];
auto addr = RifEclipseSummaryAddress::fieldAddress( "FOPT" );
setSummaryAddress( addr );
std::set<time_t> allTimeSteps = m_ensemble->ensembleTimeSteps();
if ( !allTimeSteps.empty() )
{
QDateTime dateTime = RiaQDateTimeTools::fromTime_t( *( allTimeSteps.rbegin() ) );
setTimeStep( dateTime );
}
}
}