mirror of
https://github.com/OPM/ResInsight.git
synced 2026-08-26 21:27:15 -05:00
#7750 Histogram Plot: Add logarithmic binning and user-defined bin range
Add BinningMode (Linear/Logarithmic) and OutOfRangeHandling (Exclude/Include in Boundary Bins) to RigHistogramCalculator, with defaulted constructor arguments so existing callers are unaffected. All four histogram plot data sources get shared options for binning mode and an optional user-defined bin range through new PDM fields on RimHistogramDataSource. The grid statistics data source computes non-default histograms through a new non-caching RigStatisticsDataCache::computeHistogram() pass-through, leaving the cached statistics used by the 3D overlay and the P10/P90/mean annotation lines untouched. Selecting logarithmic binning enables logarithmic scale on the plot X axis once, via a new signal handled by RimHistogramCurve. Also fix the bin index computation to use uniform bins of width range/nBins, consistent with calculatePercentil() and the bin edges drawn by the plots. The previous divisor (nBins - 1) shifted counts by up to one bin width relative to the drawn bars and slightly biased histogram-based percentile estimates.
This commit is contained in:
committed by
Magne Sjaastad
parent
f742fdcac4
commit
d96159fb7c
+13
-3
@@ -78,6 +78,7 @@ void RimEnsembleFractureHistogramDataSource ::defineUiOrdering( QString uiConfig
|
||||
uiOrdering.add( &m_ensembleFractureStatistics );
|
||||
uiOrdering.add( &m_property );
|
||||
uiOrdering.add( &m_numBins );
|
||||
appendBinningUiOrdering( uiOrdering );
|
||||
|
||||
uiOrdering.skipRemainingFields( true );
|
||||
}
|
||||
@@ -89,6 +90,8 @@ void RimEnsembleFractureHistogramDataSource::fieldChangedByUi( const caf::PdmFie
|
||||
const QVariant& oldValue,
|
||||
const QVariant& newValue )
|
||||
{
|
||||
RimHistogramDataSource::fieldChangedByUi( changedField, oldValue, newValue );
|
||||
|
||||
dataSourceChanged.send();
|
||||
}
|
||||
|
||||
@@ -119,15 +122,22 @@ RimHistogramDataSource::HistogramResult RimEnsembleFractureHistogramDataSource::
|
||||
|
||||
if ( !m_ensembleFractureStatistics() ) return result;
|
||||
|
||||
RigHistogramData histogramData =
|
||||
RigEnsembleFractureStatisticsCalculator::createStatisticsData( m_ensembleFractureStatistics(), m_property(), m_numBins() );
|
||||
std::optional<std::pair<double, double>> customBinRange;
|
||||
if ( useUserDefinedBinRange() ) customBinRange = std::make_pair( m_binRangeMin(), m_binRangeMax() );
|
||||
|
||||
RigHistogramData histogramData = RigEnsembleFractureStatisticsCalculator::createStatisticsData( m_ensembleFractureStatistics(),
|
||||
m_property(),
|
||||
m_numBins(),
|
||||
binningMode(),
|
||||
customBinRange,
|
||||
outOfRangeHandling() );
|
||||
|
||||
if ( !histogramData.isHistogramVectorValid() ) return result;
|
||||
|
||||
double min = histogramData.min;
|
||||
double max = histogramData.max;
|
||||
|
||||
result.valuesX = computeHistogramBins( min, max, m_numBins, graphType, cumulative );
|
||||
result.valuesX = computeHistogramBins( min, max, m_numBins, graphType, cumulative, binningMode() );
|
||||
result.valuesY = computeHistogramFrequencies( histogramData.histogram, graphType, frequencyType, cumulative );
|
||||
|
||||
result.p10 = histogramData.p10;
|
||||
|
||||
+12
-5
@@ -92,6 +92,7 @@ void RimEnsembleParameterHistogramDataSource ::defineUiOrdering( QString uiConfi
|
||||
uiOrdering.add( &m_ensemble );
|
||||
uiOrdering.add( &m_parameter );
|
||||
uiOrdering.add( &m_numBins );
|
||||
appendBinningUiOrdering( uiOrdering );
|
||||
|
||||
uiOrdering.skipRemainingFields( true );
|
||||
}
|
||||
@@ -103,6 +104,8 @@ void RimEnsembleParameterHistogramDataSource::fieldChangedByUi( const caf::PdmFi
|
||||
const QVariant& oldValue,
|
||||
const QVariant& newValue )
|
||||
{
|
||||
RimHistogramDataSource::fieldChangedByUi( changedField, oldValue, newValue );
|
||||
|
||||
if ( changedField == &m_ensemble )
|
||||
{
|
||||
if ( m_ensemble )
|
||||
@@ -158,18 +161,22 @@ RimHistogramDataSource::HistogramResult RimEnsembleParameterHistogramDataSource:
|
||||
auto parameter = m_ensemble->ensembleParameter( m_parameter );
|
||||
if ( !parameter.isNumeric() || !parameter.isValid() ) return result;
|
||||
|
||||
double min = parameter.minValue;
|
||||
double max = parameter.maxValue;
|
||||
result.valuesX = computeHistogramBins( min, max, m_numBins, graphType, cumulative );
|
||||
|
||||
std::vector<double> values;
|
||||
for ( const QVariant& v : parameter.values )
|
||||
{
|
||||
values.push_back( v.toDouble() );
|
||||
}
|
||||
|
||||
PosNegAccumulator posNegAccumulator;
|
||||
posNegAccumulator.addData( values );
|
||||
|
||||
auto [min, max] = binRange( parameter.minValue, parameter.maxValue, 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 );
|
||||
RigHistogramCalculator histCalc( min, max, m_numBins, &histogram, binningMode(), outOfRangeHandling() );
|
||||
histCalc.addData( values );
|
||||
|
||||
result.valuesY = computeHistogramFrequencies( histogram, graphType, frequencyType, cumulative );
|
||||
|
||||
+11
-4
@@ -137,6 +137,7 @@ void RimEnsembleSummaryVectorHistogramDataSource::defineUiOrdering( QString uiCo
|
||||
uiOrdering.add( &m_summaryAddressUiField );
|
||||
uiOrdering.add( &m_timeStep );
|
||||
uiOrdering.add( &m_numBins );
|
||||
appendBinningUiOrdering( uiOrdering );
|
||||
|
||||
uiOrdering.skipRemainingFields( true );
|
||||
}
|
||||
@@ -148,6 +149,8 @@ void RimEnsembleSummaryVectorHistogramDataSource::fieldChangedByUi( const caf::P
|
||||
const QVariant& oldValue,
|
||||
const QVariant& newValue )
|
||||
{
|
||||
RimHistogramDataSource::fieldChangedByUi( changedField, oldValue, newValue );
|
||||
|
||||
if ( changedField == &m_ensemble )
|
||||
{
|
||||
updateConnectedEditors();
|
||||
@@ -240,12 +243,16 @@ RimHistogramDataSource::HistogramResult RimEnsembleSummaryVectorHistogramDataSou
|
||||
|
||||
auto [min_it, max_it] = std::minmax_element( values.begin(), values.end() );
|
||||
|
||||
double min = *min_it;
|
||||
double max = *max_it;
|
||||
result.valuesX = computeHistogramBins( min, max, m_numBins, graphType, cumulative );
|
||||
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 );
|
||||
RigHistogramCalculator histCalc( min, max, m_numBins, &histogram, binningMode(), outOfRangeHandling() );
|
||||
histCalc.addData( values );
|
||||
|
||||
result.valuesY = computeHistogramFrequencies( histogram, graphType, frequencyType, cumulative );
|
||||
|
||||
+8
-1
@@ -131,6 +131,7 @@ void RimGridStatisticsHistogramDataSource ::defineUiOrdering( QString uiConfigNa
|
||||
}
|
||||
|
||||
uiOrdering.add( &m_numBins );
|
||||
appendBinningUiOrdering( uiOrdering );
|
||||
|
||||
uiOrdering.skipRemainingFields( true );
|
||||
}
|
||||
@@ -142,6 +143,8 @@ void RimGridStatisticsHistogramDataSource::fieldChangedByUi( const caf::PdmField
|
||||
const QVariant& oldValue,
|
||||
const QVariant& newValue )
|
||||
{
|
||||
RimHistogramDataSource::fieldChangedByUi( changedField, oldValue, newValue );
|
||||
|
||||
if ( changedField == &m_case )
|
||||
{
|
||||
if ( RimEclipseCase* eclipseCase = dynamic_cast<RimEclipseCase*>( m_case.value() ) )
|
||||
@@ -195,7 +198,7 @@ RimHistogramDataSource::HistogramResult RimGridStatisticsHistogramDataSource::co
|
||||
double min = histogramData.min;
|
||||
double max = histogramData.max;
|
||||
|
||||
result.valuesX = computeHistogramBins( min, max, m_numBins, graphType, cumulative );
|
||||
result.valuesX = computeHistogramBins( min, max, m_numBins, graphType, cumulative, binningMode() );
|
||||
result.valuesY = computeHistogramFrequencies( histogramData.histogram, graphType, frequencyType, cumulative );
|
||||
|
||||
result.p10 = histogramData.p10;
|
||||
@@ -213,6 +216,10 @@ RigHistogramData RimGridStatisticsHistogramDataSource::createStatisticsData() co
|
||||
std::unique_ptr<RimHistogramCalculator> histogramCalculator = std::make_unique<RimHistogramCalculator>();
|
||||
histogramCalculator->setNumBins( static_cast<size_t>( m_numBins() ) );
|
||||
|
||||
std::optional<std::pair<double, double>> customBinRange;
|
||||
if ( useUserDefinedBinRange() ) customBinRange = std::make_pair( m_binRangeMin(), m_binRangeMax() );
|
||||
histogramCalculator->setBinningParameters( binningMode(), outOfRangeHandling(), customBinRange );
|
||||
|
||||
RimHistogramCalculator::StatisticsCellRangeType cellRange = RimHistogramCalculator::StatisticsCellRangeType::ALL_CELLS;
|
||||
|
||||
RimHistogramCalculator::StatisticsTimeRangeType timeRange = RimHistogramCalculator::StatisticsTimeRangeType::ALL_TIMESTEPS;
|
||||
|
||||
@@ -117,6 +117,7 @@ void RimHistogramCurve::setDataSource( RimHistogramDataSource* dataSource )
|
||||
m_dataSource->uiCapability()->setUiTreeHidden( true );
|
||||
m_dataSource->dataSourceChanged.connect( this, &RimHistogramCurve::onDataSourceChanged );
|
||||
m_dataSource->cumulativeChanged.connect( this, &RimHistogramCurve::onCumulativeChanged );
|
||||
m_dataSource->logarithmicBinningEnabled.connect( this, &RimHistogramCurve::onLogarithmicBinningEnabled );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,6 +605,19 @@ void RimHistogramCurve::onCumulativeChanged( const caf::SignalEmitter* emitter )
|
||||
updateCumulativeCurve();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Logarithmic bins are best viewed on a logarithmic axis: enable it once when logarithmic binning
|
||||
/// is selected. The user stays in control of the axis setting afterwards.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimHistogramCurve::onLogarithmicBinningEnabled( const caf::SignalEmitter* emitter )
|
||||
{
|
||||
auto plot = firstAncestorOrThisOfType<RimHistogramPlot>();
|
||||
if ( !plot ) return;
|
||||
|
||||
auto axisProperties = dynamic_cast<RimPlotAxisProperties*>( plot->axisPropertiesForPlotAxis( axisX() ) );
|
||||
if ( axisProperties ) axisProperties->setLogarithmicScaleEnabled( true );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Create or delete the cumulative companion curve to match the data source setting. The companion
|
||||
/// curve shares this curve's data source and is shown on the right axis.
|
||||
|
||||
@@ -86,6 +86,7 @@ protected:
|
||||
|
||||
void onDataSourceChanged( const caf::SignalEmitter* emitter );
|
||||
void onCumulativeChanged( const caf::SignalEmitter* emitter );
|
||||
void onLogarithmicBinningEnabled( const caf::SignalEmitter* emitter );
|
||||
|
||||
void updateCumulativeCurve();
|
||||
void connectReferencedDataSourceSignals();
|
||||
|
||||
@@ -18,16 +18,54 @@
|
||||
|
||||
#include "RimHistogramDataSource.h"
|
||||
|
||||
#include "cafPdmUiOrdering.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
CAF_PDM_XML_ABSTRACT_SOURCE_INIT( RimHistogramDataSource, "HistogramDataSource" );
|
||||
|
||||
namespace caf
|
||||
{
|
||||
template <>
|
||||
void caf::AppEnum<RigHistogramCalculator::BinningMode>::setUp()
|
||||
{
|
||||
addItem( RigHistogramCalculator::BinningMode::LINEAR, "LINEAR", "Linear" );
|
||||
addItem( RigHistogramCalculator::BinningMode::LOGARITHMIC, "LOGARITHMIC", "Logarithmic" );
|
||||
setDefault( RigHistogramCalculator::BinningMode::LINEAR );
|
||||
}
|
||||
|
||||
template <>
|
||||
void caf::AppEnum<RimHistogramDataSource::BinRangeMode>::setUp()
|
||||
{
|
||||
addItem( RimHistogramDataSource::BinRangeMode::AUTOMATIC, "AUTOMATIC", "Automatic" );
|
||||
addItem( RimHistogramDataSource::BinRangeMode::USER_DEFINED, "USER_DEFINED", "User Defined" );
|
||||
setDefault( RimHistogramDataSource::BinRangeMode::AUTOMATIC );
|
||||
}
|
||||
|
||||
template <>
|
||||
void caf::AppEnum<RigHistogramCalculator::OutOfRangeHandling>::setUp()
|
||||
{
|
||||
addItem( RigHistogramCalculator::OutOfRangeHandling::EXCLUDE, "EXCLUDE", "Exclude" );
|
||||
addItem( RigHistogramCalculator::OutOfRangeHandling::INCLUDE_IN_BOUNDARY_BINS, "INCLUDE_IN_BOUNDARY_BINS", "Include in Boundary Bins" );
|
||||
setDefault( RigHistogramCalculator::OutOfRangeHandling::EXCLUDE );
|
||||
}
|
||||
} // namespace caf
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RimHistogramDataSource::RimHistogramDataSource()
|
||||
: dataSourceChanged( this )
|
||||
, cumulativeChanged( this )
|
||||
, logarithmicBinningEnabled( this )
|
||||
{
|
||||
CAF_PDM_InitObject( "Histogram Data Source", );
|
||||
|
||||
CAF_PDM_InitFieldNoDefault( &m_binningMode, "BinningMode", "Binning Mode" );
|
||||
CAF_PDM_InitFieldNoDefault( &m_binRangeMode, "BinRangeMode", "Bin Range" );
|
||||
CAF_PDM_InitField( &m_binRangeMin, "BinRangeMin", 0.0, "Minimum" );
|
||||
CAF_PDM_InitField( &m_binRangeMax, "BinRangeMax", 1.0, "Maximum" );
|
||||
CAF_PDM_InitFieldNoDefault( &m_outOfRangeHandling, "OutOfRangeHandling", "Out of Range Values" );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -55,18 +93,115 @@ void RimHistogramDataSource::setShowCumulativeCurve( bool showCumulativeCurve )
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<double>
|
||||
RimHistogramDataSource::computeHistogramBins( double min, double max, int numBins, RimHistogramPlot::GraphType graphType, bool cumulative )
|
||||
void RimHistogramDataSource::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue )
|
||||
{
|
||||
if ( changedField == &m_binningMode && m_binningMode() == RigHistogramCalculator::BinningMode::LOGARITHMIC )
|
||||
{
|
||||
logarithmicBinningEnabled.send();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimHistogramDataSource::appendBinningUiOrdering( caf::PdmUiOrdering& uiOrdering )
|
||||
{
|
||||
uiOrdering.add( &m_binningMode );
|
||||
uiOrdering.add( &m_binRangeMode );
|
||||
if ( useUserDefinedBinRange() )
|
||||
{
|
||||
uiOrdering.add( &m_binRangeMin );
|
||||
uiOrdering.add( &m_binRangeMax );
|
||||
uiOrdering.add( &m_outOfRangeHandling );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::pair<double, double> RimHistogramDataSource::computeBinRange( BinRangeMode binRangeMode,
|
||||
double userMin,
|
||||
double userMax,
|
||||
double dataMin,
|
||||
double dataMax,
|
||||
RigHistogramCalculator::BinningMode binningMode,
|
||||
double smallestPositiveValue )
|
||||
{
|
||||
double min = dataMin;
|
||||
double max = dataMax;
|
||||
if ( binRangeMode == BinRangeMode::USER_DEFINED )
|
||||
{
|
||||
min = userMin;
|
||||
max = userMax;
|
||||
}
|
||||
|
||||
if ( binningMode == RigHistogramCalculator::BinningMode::LOGARITHMIC && min <= 0.0 )
|
||||
{
|
||||
min = smallestPositiveValue;
|
||||
}
|
||||
|
||||
return { min, max };
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::pair<double, double> RimHistogramDataSource::binRange( double dataMin, double dataMax, double smallestPositiveValue ) const
|
||||
{
|
||||
return computeBinRange( m_binRangeMode(), m_binRangeMin(), m_binRangeMax(), dataMin, dataMax, m_binningMode(), smallestPositiveValue );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigHistogramCalculator::BinningMode RimHistogramDataSource::binningMode() const
|
||||
{
|
||||
return m_binningMode();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Out of range values only exist for a user-defined bin range: an automatic range covers the data
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigHistogramCalculator::OutOfRangeHandling RimHistogramDataSource::outOfRangeHandling() const
|
||||
{
|
||||
if ( !useUserDefinedBinRange() ) return RigHistogramCalculator::OutOfRangeHandling::EXCLUDE;
|
||||
|
||||
return m_outOfRangeHandling();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bool RimHistogramDataSource::useUserDefinedBinRange() const
|
||||
{
|
||||
return m_binRangeMode() == BinRangeMode::USER_DEFINED;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
std::vector<double> RimHistogramDataSource::computeHistogramBins( double min,
|
||||
double max,
|
||||
int numBins,
|
||||
RimHistogramPlot::GraphType graphType,
|
||||
bool cumulative,
|
||||
RigHistogramCalculator::BinningMode binningMode )
|
||||
{
|
||||
const bool isLogarithmic = ( binningMode == RigHistogramCalculator::BinningMode::LOGARITHMIC && min > 0.0 );
|
||||
|
||||
const double logMin = isLogarithmic ? std::log10( min ) : 0.0;
|
||||
const double logStep = isLogarithmic ? ( std::log10( max ) - logMin ) / numBins : 0.0;
|
||||
const double binSize = ( max - min ) / numBins;
|
||||
|
||||
auto binEdge = [=]( int i ) { return isLogarithmic ? std::pow( 10.0, logMin + logStep * i ) : min + binSize * i; };
|
||||
|
||||
std::vector<double> values;
|
||||
for ( int i = 0; i < numBins; i++ )
|
||||
{
|
||||
if ( graphType == RimHistogramPlot::GraphType::BAR_GRAPH )
|
||||
{
|
||||
const double binMin = min + binSize * i;
|
||||
const double binMax = min + binSize * ( i + 1 );
|
||||
const double binMin = binEdge( i );
|
||||
const double binMax = binEdge( i + 1 );
|
||||
|
||||
// Close first on left side
|
||||
if ( i == 0 ) values.push_back( binMin );
|
||||
@@ -80,7 +215,8 @@ std::vector<double>
|
||||
}
|
||||
else if ( graphType == RimHistogramPlot::GraphType::LINE_GRAPH )
|
||||
{
|
||||
double centerOfBin = min + binSize * i + binSize / 2.0;
|
||||
// For logarithmic bins the center is the geometric mean of the bin edges
|
||||
double centerOfBin = isLogarithmic ? std::pow( 10.0, logMin + logStep * ( i + 0.5 ) ) : min + binSize * i + binSize / 2.0;
|
||||
values.push_back( centerOfBin );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RigStatisticsMath.h"
|
||||
|
||||
#include "RimHistogramPlot.h"
|
||||
|
||||
#include "cafAppEnum.h"
|
||||
#include "cafPdmField.h"
|
||||
#include "cafPdmObject.h"
|
||||
#include "cafSignal.h"
|
||||
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
//==================================================================================================
|
||||
///
|
||||
@@ -33,6 +39,12 @@ class RimHistogramDataSource : public caf::PdmObject
|
||||
CAF_PDM_HEADER_INIT;
|
||||
|
||||
public:
|
||||
enum class BinRangeMode
|
||||
{
|
||||
AUTOMATIC,
|
||||
USER_DEFINED
|
||||
};
|
||||
|
||||
struct HistogramResult
|
||||
{
|
||||
HistogramResult()
|
||||
@@ -54,6 +66,7 @@ public:
|
||||
|
||||
caf::Signal<> dataSourceChanged;
|
||||
caf::Signal<> cumulativeChanged;
|
||||
caf::Signal<> logarithmicBinningEnabled;
|
||||
|
||||
virtual std::string unitNameX() const = 0;
|
||||
virtual std::string unitNameY() const = 0;
|
||||
@@ -69,7 +82,12 @@ public:
|
||||
virtual std::string name() const = 0;
|
||||
|
||||
static std::vector<double>
|
||||
computeHistogramBins( double min, double max, int numBins, RimHistogramPlot::GraphType graphType, bool cumulative = false );
|
||||
computeHistogramBins( double min,
|
||||
double max,
|
||||
int numBins,
|
||||
RimHistogramPlot::GraphType graphType,
|
||||
bool cumulative = false,
|
||||
RigHistogramCalculator::BinningMode binningMode = RigHistogramCalculator::BinningMode::LINEAR );
|
||||
static std::vector<double> computeHistogramFrequencies( const std::vector<size_t>& values,
|
||||
RimHistogramPlot::GraphType graphType,
|
||||
RimHistogramPlot::FrequencyType frequencyType,
|
||||
@@ -78,4 +96,31 @@ public:
|
||||
RimHistogramPlot::GraphType graphType,
|
||||
RimHistogramPlot::FrequencyType frequencyType,
|
||||
bool cumulative = false );
|
||||
|
||||
// Resolve the effective bin range: the user-defined range when USER_DEFINED, otherwise [dataMin, dataMax].
|
||||
// For logarithmic binning a non-positive minimum is replaced by smallestPositiveValue.
|
||||
static std::pair<double, double> computeBinRange( BinRangeMode binRangeMode,
|
||||
double userMin,
|
||||
double userMax,
|
||||
double dataMin,
|
||||
double dataMax,
|
||||
RigHistogramCalculator::BinningMode binningMode,
|
||||
double smallestPositiveValue );
|
||||
|
||||
protected:
|
||||
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
|
||||
|
||||
void appendBinningUiOrdering( caf::PdmUiOrdering& uiOrdering );
|
||||
|
||||
std::pair<double, double> binRange( double dataMin, double dataMax, double smallestPositiveValue ) const;
|
||||
|
||||
RigHistogramCalculator::BinningMode binningMode() const;
|
||||
RigHistogramCalculator::OutOfRangeHandling outOfRangeHandling() const;
|
||||
bool useUserDefinedBinRange() const;
|
||||
|
||||
caf::PdmField<caf::AppEnum<RigHistogramCalculator::BinningMode>> m_binningMode;
|
||||
caf::PdmField<caf::AppEnum<BinRangeMode>> m_binRangeMode;
|
||||
caf::PdmField<double> m_binRangeMin;
|
||||
caf::PdmField<double> m_binRangeMax;
|
||||
caf::PdmField<caf::AppEnum<RigHistogramCalculator::OutOfRangeHandling>> m_outOfRangeHandling;
|
||||
};
|
||||
|
||||
@@ -86,6 +86,61 @@ void RimHistogramCalculator::setNumBins( size_t numBins )
|
||||
m_numBins = numBins;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimHistogramCalculator::setBinningParameters( RigHistogramCalculator::BinningMode binningMode,
|
||||
RigHistogramCalculator::OutOfRangeHandling outOfRangeHandling,
|
||||
std::optional<std::pair<double, double>> customBinRange )
|
||||
{
|
||||
m_binningMode = binningMode;
|
||||
m_outOfRangeHandling = outOfRangeHandling;
|
||||
m_customBinRange = customBinRange;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Recompute the histogram with non-default binning (logarithmic and/or a custom bin range) directly
|
||||
/// from the underlying data. The statistics cached for other consumers are left untouched.
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimHistogramCalculator::applyCustomBinning( RigStatisticsDataCache* statisticsCache,
|
||||
std::optional<size_t> timeStepIndex,
|
||||
RigHistogramData& histData )
|
||||
{
|
||||
const bool useCustomBinning = ( m_binningMode != RigHistogramCalculator::BinningMode::LINEAR ) || m_customBinRange.has_value();
|
||||
if ( !useCustomBinning || !statisticsCache ) return;
|
||||
|
||||
double binMin = m_customBinRange ? m_customBinRange->first : histData.min;
|
||||
double binMax = m_customBinRange ? m_customBinRange->second : histData.max;
|
||||
|
||||
if ( m_binningMode == RigHistogramCalculator::BinningMode::LOGARITHMIC && binMin <= 0.0 )
|
||||
{
|
||||
// Use the smallest positive value as the logarithmic range minimum. HUGE_VAL when there are none.
|
||||
double pos = HUGE_VAL;
|
||||
double neg = -HUGE_VAL;
|
||||
if ( timeStepIndex.has_value() )
|
||||
statisticsCache->posNegClosestToZero( *timeStepIndex, pos, neg );
|
||||
else
|
||||
statisticsCache->posNegClosestToZero( pos, neg );
|
||||
|
||||
binMin = pos;
|
||||
}
|
||||
|
||||
histData.histogram.clear();
|
||||
|
||||
if ( binMin <= binMax && RigStatisticsTools::isValidNumber( binMin ) && RigStatisticsTools::isValidNumber( binMax ) )
|
||||
{
|
||||
RigHistogramCalculator histCalc( binMin, binMax, m_numBins, &histData.histogram, m_binningMode, m_outOfRangeHandling );
|
||||
if ( timeStepIndex.has_value() )
|
||||
statisticsCache->computeHistogram( *timeStepIndex, histCalc );
|
||||
else
|
||||
statisticsCache->computeHistogram( histCalc );
|
||||
|
||||
// Let the plot draw bin edges that match the histogram
|
||||
histData.min = binMin;
|
||||
histData.max = binMax;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -198,6 +253,9 @@ RigHistogramData RimHistogramCalculator::histogramData( RimEclipseView*
|
||||
|
||||
RigHistogramData histData;
|
||||
|
||||
RigStatisticsDataCache* statisticsCache = nullptr;
|
||||
std::optional<size_t> timeStepContext;
|
||||
|
||||
eclResultDefinition->loadResult();
|
||||
|
||||
if ( eclResultDefinition->isFlowDiagOrInjectionFlooding() )
|
||||
@@ -217,6 +275,9 @@ RigHistogramData RimHistogramCalculator::histogramData( RimEclipseView*
|
||||
fldResults->mobileVolumeWeightedMean( resAddr, timeStep, &histData.weightedMean );
|
||||
|
||||
histData.histogram = fldResults->scalarValuesHistogram( resAddr, timeStep );
|
||||
|
||||
statisticsCache = fldResults->statistics( resAddr );
|
||||
timeStepContext = static_cast<size_t>( timeStep );
|
||||
}
|
||||
else if ( cellRange == StatisticsCellRangeType::VISIBLE_CELLS )
|
||||
{
|
||||
@@ -232,6 +293,9 @@ RigHistogramData RimHistogramCalculator::histogramData( RimEclipseView*
|
||||
m_visibleCellStatistics->mobileVolumeWeightedMean( timeStep, histData.weightedMean );
|
||||
|
||||
histData.histogram = m_visibleCellStatistics->cellScalarValuesHistogram( timeStep );
|
||||
|
||||
statisticsCache = m_visibleCellStatistics.p();
|
||||
timeStepContext = static_cast<size_t>( timeStep );
|
||||
}
|
||||
}
|
||||
else if ( timeRange == StatisticsTimeRangeType::ALL_TIMESTEPS )
|
||||
@@ -249,6 +313,8 @@ RigHistogramData RimHistogramCalculator::histogramData( RimEclipseView*
|
||||
fldResults->mobileVolumeWeightedMean( resAddr, &histData.weightedMean );
|
||||
|
||||
histData.histogram = fldResults->scalarValuesHistogram( resAddr );
|
||||
|
||||
statisticsCache = fldResults->statistics( resAddr );
|
||||
}
|
||||
else if ( cellRange == StatisticsCellRangeType::VISIBLE_CELLS )
|
||||
{
|
||||
@@ -264,6 +330,8 @@ RigHistogramData RimHistogramCalculator::histogramData( RimEclipseView*
|
||||
m_visibleCellStatistics->mobileVolumeWeightedMean( histData.weightedMean );
|
||||
|
||||
histData.histogram = m_visibleCellStatistics->cellScalarValuesHistogram();
|
||||
|
||||
statisticsCache = m_visibleCellStatistics.p();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -282,6 +350,8 @@ RigHistogramData RimHistogramCalculator::histogramData( RimEclipseView*
|
||||
cellResults->sumCellScalarValues( eclResAddr, histData.sum );
|
||||
cellResults->mobileVolumeWeightedMean( eclResAddr, histData.weightedMean );
|
||||
histData.histogram = cellResults->cellScalarValuesHistogram( eclResAddr );
|
||||
|
||||
statisticsCache = cellResults->statistics( eclResAddr );
|
||||
}
|
||||
else if ( timeRange == StatisticsTimeRangeType::CURRENT_TIMESTEP )
|
||||
{
|
||||
@@ -292,6 +362,9 @@ RigHistogramData RimHistogramCalculator::histogramData( RimEclipseView*
|
||||
cellResults->sumCellScalarValues( eclResAddr, timeStep, histData.sum );
|
||||
cellResults->mobileVolumeWeightedMean( eclResAddr, timeStep, histData.weightedMean );
|
||||
histData.histogram = cellResults->cellScalarValuesHistogram( eclResAddr, timeStep );
|
||||
|
||||
statisticsCache = cellResults->statistics( eclResAddr );
|
||||
timeStepContext = static_cast<size_t>( timeStep );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -310,6 +383,8 @@ RigHistogramData RimHistogramCalculator::histogramData( RimEclipseView*
|
||||
m_visibleCellStatistics->mobileVolumeWeightedMean( histData.weightedMean );
|
||||
|
||||
histData.histogram = m_visibleCellStatistics->cellScalarValuesHistogram();
|
||||
|
||||
statisticsCache = m_visibleCellStatistics.p();
|
||||
}
|
||||
else if ( timeRange == StatisticsTimeRangeType::CURRENT_TIMESTEP )
|
||||
{
|
||||
@@ -321,8 +396,14 @@ RigHistogramData RimHistogramCalculator::histogramData( RimEclipseView*
|
||||
m_visibleCellStatistics->mobileVolumeWeightedMean( timeStep, histData.weightedMean );
|
||||
|
||||
histData.histogram = m_visibleCellStatistics->cellScalarValuesHistogram( timeStep );
|
||||
|
||||
statisticsCache = m_visibleCellStatistics.p();
|
||||
timeStepContext = static_cast<size_t>( timeStep );
|
||||
}
|
||||
}
|
||||
|
||||
applyCustomBinning( statisticsCache, timeStepContext, histData );
|
||||
|
||||
return histData;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,13 @@
|
||||
#include "RigHistogramData.h"
|
||||
|
||||
#include "RigStatisticsDataCache.h"
|
||||
#include "RigStatisticsMath.h"
|
||||
|
||||
#include "cvfObject.h"
|
||||
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
class RimEclipseResultDefinition;
|
||||
class RimGeoMechContourMapView;
|
||||
class RimEclipseContourMapView;
|
||||
@@ -55,6 +59,10 @@ public:
|
||||
|
||||
void setNumBins( size_t numBins );
|
||||
|
||||
void setBinningParameters( RigHistogramCalculator::BinningMode binningMode,
|
||||
RigHistogramCalculator::OutOfRangeHandling outOfRangeHandling,
|
||||
std::optional<std::pair<double, double>> customBinRange );
|
||||
|
||||
RigHistogramData histogramData( RimEclipseContourMapView* contourMap );
|
||||
RigHistogramData histogramData( RimGeoMechContourMapView* contourMap );
|
||||
RigHistogramData histogramData( RimEclipseView* eclipseView, StatisticsCellRangeType cellRange, StatisticsTimeRangeType timeRange );
|
||||
@@ -74,9 +82,15 @@ private:
|
||||
|
||||
void updateVisCellStatsIfNeeded( RimGeoMechView* geoMechView );
|
||||
|
||||
void applyCustomBinning( RigStatisticsDataCache* statisticsCache, std::optional<size_t> timeStepIndex, RigHistogramData& histData );
|
||||
|
||||
std::vector<RigEclipseResultAddress> sourcesForMultiPropertyResults( const QString& resultName );
|
||||
|
||||
bool m_isVisCellStatUpToDate;
|
||||
cvf::ref<RigStatisticsDataCache> m_visibleCellStatistics;
|
||||
size_t m_numBins;
|
||||
|
||||
RigHistogramCalculator::BinningMode m_binningMode = RigHistogramCalculator::BinningMode::LINEAR;
|
||||
RigHistogramCalculator::OutOfRangeHandling m_outOfRangeHandling = RigHistogramCalculator::OutOfRangeHandling::EXCLUDE;
|
||||
std::optional<std::pair<double, double>> m_customBinRange;
|
||||
};
|
||||
|
||||
@@ -795,3 +795,15 @@ bool RimPlotAxisProperties::isLogarithmicScaleEnabled() const
|
||||
{
|
||||
return m_isLogarithmicScaleEnabled;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RimPlotAxisProperties::setLogarithmicScaleEnabled( bool enabled )
|
||||
{
|
||||
if ( m_isLogarithmicScaleEnabled() == enabled ) return;
|
||||
|
||||
m_isLogarithmicScaleEnabled = enabled;
|
||||
updateConnectedEditors();
|
||||
logarithmicChanged.send( enabled );
|
||||
}
|
||||
|
||||
@@ -118,6 +118,7 @@ public:
|
||||
void removeAllAnnotations() override;
|
||||
|
||||
bool isLogarithmicScaleEnabled() const override;
|
||||
void setLogarithmicScaleEnabled( bool enabled );
|
||||
bool isActive() const override;
|
||||
|
||||
void showAnnotationObjectsInProjectTree();
|
||||
|
||||
@@ -158,6 +158,8 @@ public:
|
||||
|
||||
void setStatisticsDataCacheNumBins( const RigEclipseResultAddress& resultAddress, size_t numBins );
|
||||
|
||||
RigStatisticsDataCache* statistics( const RigEclipseResultAddress& resVarAddr );
|
||||
|
||||
size_t addStaticScalarResult( RiaDefines::ResultCatType type, const QString& resultName, bool needsToBeStored, size_t resultValueCount );
|
||||
|
||||
RigEclipseResultAddress defaultResult() const;
|
||||
@@ -247,8 +249,6 @@ private:
|
||||
|
||||
void assignValuesToTemporaryLgrs( const QString& resultName, std::vector<double>& values );
|
||||
|
||||
RigStatisticsDataCache* statistics( const RigEclipseResultAddress& resVarAddr );
|
||||
|
||||
bool isRadialModel() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -57,9 +57,12 @@ void caf::AppEnum<RigEnsembleFractureStatisticsCalculator::PropertyType>::setUp(
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigHistogramData RigEnsembleFractureStatisticsCalculator::createStatisticsData( const RimEnsembleFractureStatistics* esf,
|
||||
PropertyType propertyType,
|
||||
int numBins )
|
||||
RigHistogramData RigEnsembleFractureStatisticsCalculator::createStatisticsData( const RimEnsembleFractureStatistics* esf,
|
||||
PropertyType propertyType,
|
||||
int numBins,
|
||||
RigHistogramCalculator::BinningMode binningMode,
|
||||
std::optional<std::pair<double, double>> customBinRange,
|
||||
RigHistogramCalculator::OutOfRangeHandling outOfRangeHandling )
|
||||
{
|
||||
std::vector<cvf::ref<RigStimPlanFractureDefinition>> fractureDefinitions = esf->readFractureDefinitions();
|
||||
|
||||
@@ -68,7 +71,7 @@ RigHistogramData RigEnsembleFractureStatisticsCalculator::createStatisticsData(
|
||||
fractureDefinitions = RigEnsembleFractureStatisticsCalculator::removeZeroWidthDefinitions( fractureDefinitions );
|
||||
}
|
||||
|
||||
return createStatisticsData( fractureDefinitions, propertyType, numBins );
|
||||
return createStatisticsData( fractureDefinitions, propertyType, numBins, binningMode, customBinRange, outOfRangeHandling );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -76,8 +79,11 @@ RigHistogramData RigEnsembleFractureStatisticsCalculator::createStatisticsData(
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigHistogramData
|
||||
RigEnsembleFractureStatisticsCalculator::createStatisticsData( const std::vector<cvf::ref<RigStimPlanFractureDefinition>>& fractureDefinitions,
|
||||
PropertyType propertyType,
|
||||
int numBins )
|
||||
PropertyType propertyType,
|
||||
int numBins,
|
||||
RigHistogramCalculator::BinningMode binningMode,
|
||||
std::optional<std::pair<double, double>> customBinRange,
|
||||
RigHistogramCalculator::OutOfRangeHandling outOfRangeHandling )
|
||||
{
|
||||
std::vector<double> samples = calculateProperty( fractureDefinitions, propertyType );
|
||||
|
||||
@@ -97,12 +103,30 @@ RigHistogramData
|
||||
&mean,
|
||||
RigStatisticsMath::PercentileStyle::SWITCHED );
|
||||
|
||||
std::vector<size_t> histogram;
|
||||
RigHistogramCalculator histogramCalculator( histogramData.min, histogramData.max, numBins, &histogram );
|
||||
for ( auto s : samples )
|
||||
histogramCalculator.addValue( s );
|
||||
double binMin = customBinRange ? customBinRange->first : histogramData.min;
|
||||
double binMax = customBinRange ? customBinRange->second : histogramData.max;
|
||||
|
||||
histogramData.histogram = histogram;
|
||||
if ( binningMode == RigHistogramCalculator::BinningMode::LOGARITHMIC && binMin <= 0.0 )
|
||||
{
|
||||
// Use the smallest positive value as the logarithmic range minimum. HUGE_VAL when there are none.
|
||||
PosNegAccumulator posNegAccumulator;
|
||||
posNegAccumulator.addData( samples );
|
||||
binMin = posNegAccumulator.pos;
|
||||
}
|
||||
|
||||
if ( !samples.empty() && binMin <= binMax && RigStatisticsTools::isValidNumber( binMin ) && RigStatisticsTools::isValidNumber( binMax ) )
|
||||
{
|
||||
std::vector<size_t> histogram;
|
||||
RigHistogramCalculator histogramCalculator( binMin, binMax, numBins, &histogram, binningMode, outOfRangeHandling );
|
||||
for ( auto s : samples )
|
||||
histogramCalculator.addValue( s );
|
||||
|
||||
histogramData.histogram = histogram;
|
||||
|
||||
// Let the plot draw bin edges that match the histogram
|
||||
histogramData.min = binMin;
|
||||
histogramData.max = binMax;
|
||||
}
|
||||
|
||||
return histogramData;
|
||||
}
|
||||
|
||||
@@ -21,12 +21,15 @@
|
||||
#include "RiaDefines.h"
|
||||
|
||||
#include "RigHistogramData.h"
|
||||
#include "RigStatisticsMath.h"
|
||||
|
||||
#include "cafPdmUiNumberFormat.h"
|
||||
|
||||
#include "cvfObject.h"
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class RimEnsembleFractureStatistics;
|
||||
@@ -51,11 +54,21 @@ public:
|
||||
FORMATION_DIP
|
||||
};
|
||||
|
||||
static RigHistogramData createStatisticsData( const RimEnsembleFractureStatistics* esf, PropertyType propertyType, int numBins );
|
||||
static RigHistogramData createStatisticsData(
|
||||
const RimEnsembleFractureStatistics* esf,
|
||||
PropertyType propertyType,
|
||||
int numBins,
|
||||
RigHistogramCalculator::BinningMode binningMode = RigHistogramCalculator::BinningMode::LINEAR,
|
||||
std::optional<std::pair<double, double>> customBinRange = std::nullopt,
|
||||
RigHistogramCalculator::OutOfRangeHandling outOfRangeHandling = RigHistogramCalculator::OutOfRangeHandling::EXCLUDE );
|
||||
|
||||
static RigHistogramData createStatisticsData( const std::vector<cvf::ref<RigStimPlanFractureDefinition>>& fractureDefinitions,
|
||||
PropertyType propertyType,
|
||||
int numBins );
|
||||
static RigHistogramData createStatisticsData(
|
||||
const std::vector<cvf::ref<RigStimPlanFractureDefinition>>& fractureDefinitions,
|
||||
PropertyType propertyType,
|
||||
int numBins,
|
||||
RigHistogramCalculator::BinningMode binningMode = RigHistogramCalculator::BinningMode::LINEAR,
|
||||
std::optional<std::pair<double, double>> customBinRange = std::nullopt,
|
||||
RigHistogramCalculator::OutOfRangeHandling outOfRangeHandling = RigHistogramCalculator::OutOfRangeHandling::EXCLUDE );
|
||||
|
||||
static std::vector<cvf::ref<RigStimPlanFractureDefinition>>
|
||||
removeZeroWidthDefinitions( const std::vector<cvf::ref<RigStimPlanFractureDefinition>>& fractureDefinitions );
|
||||
|
||||
@@ -97,6 +97,8 @@ public:
|
||||
|
||||
void setStatisticsDataCacheNumBins( const RigFlowDiagResultAddress& resVarAddr, size_t numBins );
|
||||
|
||||
RigStatisticsDataCache* statistics( const RigFlowDiagResultAddress& resVarAddr );
|
||||
|
||||
private:
|
||||
const std::vector<double>* findOrCalculateResult( const RigFlowDiagResultAddress& resVarAddr, size_t timeStepIndex );
|
||||
void calculateNativeResultsIfNotPreviouslyAttempted( size_t timeStepIndex, RigFlowDiagResultAddress::PhaseSelection phaseSelection );
|
||||
@@ -129,8 +131,6 @@ private:
|
||||
size_t activeCellCount,
|
||||
std::vector<double>* sumOfFractions );
|
||||
|
||||
RigStatisticsDataCache* statistics( const RigFlowDiagResultAddress& resVarAddr );
|
||||
|
||||
RigFlowDiagResultFrames* createScalarResult( const RigFlowDiagResultAddress& resVarAddr );
|
||||
RigFlowDiagResultFrames* findScalarResult( const RigFlowDiagResultAddress& resVarAddr );
|
||||
std::vector<double>* findScalarResultFrame( const RigFlowDiagResultAddress& resVarAddr, size_t timeStepIndex );
|
||||
|
||||
@@ -283,6 +283,22 @@ const std::vector<size_t>& RigStatisticsDataCache::cellScalarValuesHistogram( si
|
||||
return m_statsPrTs[timeStepIndex].m_histogram;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigStatisticsDataCache::computeHistogram( RigHistogramCalculator& histogramCalculator )
|
||||
{
|
||||
m_statisticsCalculator->addDataToHistogramCalculator( histogramCalculator );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
void RigStatisticsDataCache::computeHistogram( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator )
|
||||
{
|
||||
m_statisticsCalculator->addDataToHistogramCalculator( timeStepIndex, histogramCalculator );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -57,6 +57,10 @@ public:
|
||||
const std::vector<size_t>& cellScalarValuesHistogram();
|
||||
const std::vector<size_t>& cellScalarValuesHistogram( size_t timeStepIndex );
|
||||
|
||||
// Fills the supplied histogram calculator from the underlying data. Nothing is cached.
|
||||
void computeHistogram( RigHistogramCalculator& histogramCalculator );
|
||||
void computeHistogram( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator );
|
||||
|
||||
const std::vector<int>& uniqueCellScalarValues();
|
||||
const std::vector<int>& uniqueCellScalarValues( size_t timeStepIndex );
|
||||
|
||||
|
||||
@@ -451,7 +451,12 @@ double RigStatisticsMath::calculateMean( const std::vector<double>& values )
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
RigHistogramCalculator::RigHistogramCalculator( double min, double max, size_t nBins, std::vector<size_t>* histogram )
|
||||
RigHistogramCalculator::RigHistogramCalculator( double min,
|
||||
double max,
|
||||
size_t nBins,
|
||||
std::vector<size_t>* histogram,
|
||||
BinningMode binningMode,
|
||||
OutOfRangeHandling outOfRangeHandling )
|
||||
{
|
||||
assert( histogram );
|
||||
assert( nBins > 0 );
|
||||
@@ -461,16 +466,33 @@ RigHistogramCalculator::RigHistogramCalculator( double min, double max, size_t n
|
||||
nBins = 1;
|
||||
} // Avoid dividing on 0 range
|
||||
|
||||
m_histogram = histogram;
|
||||
m_min = min;
|
||||
m_observationCount = 0;
|
||||
// Logarithmic binning requires a positive range
|
||||
if ( binningMode == BinningMode::LOGARITHMIC && min <= 0.0 )
|
||||
{
|
||||
binningMode = BinningMode::LINEAR;
|
||||
}
|
||||
|
||||
m_histogram = histogram;
|
||||
m_binningMode = binningMode;
|
||||
m_outOfRangeHandling = outOfRangeHandling;
|
||||
m_observationCount = 0;
|
||||
|
||||
// Initialize bins
|
||||
m_histogram->resize( nBins );
|
||||
for ( size_t i = 0; i < m_histogram->size(); ++i )
|
||||
( *m_histogram )[i] = 0;
|
||||
|
||||
m_range = max - min;
|
||||
if ( m_binningMode == BinningMode::LOGARITHMIC )
|
||||
{
|
||||
m_min = std::log10( min );
|
||||
m_max = std::log10( max );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_min = min;
|
||||
m_max = max;
|
||||
}
|
||||
m_range = m_max - m_min;
|
||||
m_maxIndex = nBins - 1;
|
||||
}
|
||||
|
||||
@@ -481,15 +503,37 @@ void RigHistogramCalculator::addValue( double value )
|
||||
{
|
||||
if ( RigStatisticsTools::isInvalidNumber<double>( value ) ) return;
|
||||
|
||||
double transformed = value;
|
||||
if ( m_binningMode == BinningMode::LOGARITHMIC )
|
||||
{
|
||||
if ( value <= 0.0 )
|
||||
{
|
||||
// Non-positive values cannot be represented on a logarithmic scale and are below any valid range
|
||||
if ( m_outOfRangeHandling != OutOfRangeHandling::INCLUDE_IN_BOUNDARY_BINS ) return;
|
||||
( *m_histogram )[0]++;
|
||||
m_observationCount++;
|
||||
return;
|
||||
}
|
||||
transformed = std::log10( value );
|
||||
}
|
||||
|
||||
if ( transformed < m_min || transformed > m_max )
|
||||
{
|
||||
if ( m_outOfRangeHandling != OutOfRangeHandling::INCLUDE_IN_BOUNDARY_BINS ) return;
|
||||
size_t boundaryIndex = transformed < m_min ? 0 : m_maxIndex;
|
||||
( *m_histogram )[boundaryIndex]++;
|
||||
m_observationCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t index = 0;
|
||||
|
||||
if ( m_maxIndex > 0 ) index = (size_t)( m_maxIndex * ( value - m_min ) / m_range );
|
||||
// Uniform bins of width m_range / nBins, consistent with calculatePercentil() and the bin edges
|
||||
// drawn by the histogram plots. The maximum value is included in the last bin.
|
||||
if ( m_maxIndex > 0 ) index = std::min( (size_t)( m_histogram->size() * ( transformed - m_min ) / m_range ), m_maxIndex );
|
||||
|
||||
if ( index < m_histogram->size() ) // Just clip to the max min range (-index will overflow to positive )
|
||||
{
|
||||
( *m_histogram )[index]++;
|
||||
m_observationCount++;
|
||||
}
|
||||
( *m_histogram )[index]++;
|
||||
m_observationCount++;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -545,8 +589,11 @@ double RigHistogramCalculator::calculatePercentil( double pVal, RigStatisticsMat
|
||||
pValClamped = 1.0 - pValClamped;
|
||||
}
|
||||
|
||||
auto toDomainValue = [this]( double binSpaceValue )
|
||||
{ return m_binningMode == BinningMode::LOGARITHMIC ? std::pow( 10.0, binSpaceValue ) : binSpaceValue; };
|
||||
|
||||
double pValObservationCount = pValClamped * m_observationCount;
|
||||
if ( pValObservationCount == 0.0 ) return m_min;
|
||||
if ( pValObservationCount == 0.0 ) return toDomainValue( m_min );
|
||||
|
||||
size_t accObsCount = 0;
|
||||
double binWidth = m_range / m_histogram->size();
|
||||
@@ -564,7 +611,8 @@ double RigHistogramCalculator::calculatePercentil( double pVal, RigStatisticsMat
|
||||
|
||||
// See https://resinsight.org/docs/casegroupsandstatistics/#percentile-methods for details
|
||||
|
||||
return histogramBasedEstimate;
|
||||
// For logarithmic binning the interpolation happens in log10 space
|
||||
return toDomainValue( histogramBasedEstimate );
|
||||
}
|
||||
}
|
||||
assert( false );
|
||||
|
||||
@@ -65,7 +65,24 @@ public:
|
||||
class RigHistogramCalculator
|
||||
{
|
||||
public:
|
||||
RigHistogramCalculator( double min, double max, size_t nBins, std::vector<size_t>* histogram );
|
||||
enum class BinningMode
|
||||
{
|
||||
LINEAR,
|
||||
LOGARITHMIC
|
||||
};
|
||||
|
||||
enum class OutOfRangeHandling
|
||||
{
|
||||
EXCLUDE,
|
||||
INCLUDE_IN_BOUNDARY_BINS
|
||||
};
|
||||
|
||||
RigHistogramCalculator( double min,
|
||||
double max,
|
||||
size_t nBins,
|
||||
std::vector<size_t>* histogram,
|
||||
BinningMode binningMode = BinningMode::LINEAR,
|
||||
OutOfRangeHandling outOfRangeHandling = OutOfRangeHandling::EXCLUDE );
|
||||
|
||||
void addData( const std::vector<double>& data );
|
||||
void addData( const std::vector<float>& data );
|
||||
@@ -74,7 +91,7 @@ public:
|
||||
|
||||
/// Calculates the estimated percentile from the histogram.
|
||||
/// the percentile is the domain value at which pVal of the observations are below it.
|
||||
/// Will only consider observed values between min and max, as all other values are discarded from the histogram
|
||||
/// The estimate is only meaningful when the histogram covers the full range of the observed values.
|
||||
|
||||
double calculatePercentil( double pVal, RigStatisticsMath::PercentileStyle percentileStyle );
|
||||
|
||||
@@ -82,6 +99,9 @@ private:
|
||||
size_t m_maxIndex;
|
||||
double m_range;
|
||||
double m_min;
|
||||
double m_max;
|
||||
BinningMode m_binningMode;
|
||||
OutOfRangeHandling m_outOfRangeHandling;
|
||||
size_t m_observationCount;
|
||||
std::vector<size_t>* m_histogram;
|
||||
};
|
||||
|
||||
@@ -23,3 +23,31 @@ TEST( RigEnsembleFractureStatisticsCalculatorTest, NoConductivityResultNames )
|
||||
EXPECT_TRUE( values.empty() );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// The default binning arguments must produce an invalid histogram for empty input, for both the
|
||||
/// default and the custom binning code paths
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigEnsembleFractureStatisticsCalculatorTest, EmptyDefinitionsProduceInvalidHistogram )
|
||||
{
|
||||
std::vector<cvf::ref<RigStimPlanFractureDefinition>> definitions;
|
||||
|
||||
{
|
||||
RigHistogramData histogramData =
|
||||
RigEnsembleFractureStatisticsCalculator::createStatisticsData( definitions,
|
||||
RigEnsembleFractureStatisticsCalculator::PropertyType::HEIGHT,
|
||||
50 );
|
||||
EXPECT_FALSE( histogramData.isHistogramVectorValid() );
|
||||
}
|
||||
|
||||
{
|
||||
RigHistogramData histogramData =
|
||||
RigEnsembleFractureStatisticsCalculator::createStatisticsData( definitions,
|
||||
RigEnsembleFractureStatisticsCalculator::PropertyType::HEIGHT,
|
||||
50,
|
||||
RigHistogramCalculator::BinningMode::LOGARITHMIC,
|
||||
std::make_pair( 1.0, 100.0 ),
|
||||
RigHistogramCalculator::OutOfRangeHandling::INCLUDE_IN_BOUNDARY_BINS );
|
||||
EXPECT_FALSE( histogramData.isHistogramVectorValid() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,12 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "RigStatisticsDataCache.h"
|
||||
#include "RigStatisticsMath.h"
|
||||
|
||||
#include "QElapsedTimer"
|
||||
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -136,8 +138,8 @@ TEST( RigStatisticsMath, HistogramPercentiles )
|
||||
p90 = histCalc.calculatePercentil( 0.9, RigStatisticsMath::PercentileStyle::REGULAR );
|
||||
|
||||
EXPECT_DOUBLE_EQ( -76273.240559989776, p10 );
|
||||
EXPECT_DOUBLE_EQ( 5312.1312871307755, p50 );
|
||||
EXPECT_DOUBLE_EQ( 94818.413022321271, p90 );
|
||||
EXPECT_DOUBLE_EQ( 7292.3587591482656, p50 );
|
||||
EXPECT_DOUBLE_EQ( 96798.640494338761, p90 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
@@ -348,6 +350,263 @@ TEST( RigStatisticsMath, calculateMean )
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// Default constructor arguments: linear binning, out-of-range values excluded
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigStatisticsMath, LinearHistogramBinning )
|
||||
{
|
||||
std::vector<size_t> histogram;
|
||||
RigHistogramCalculator histCalc( 0.0, 10.0, 5, &histogram );
|
||||
|
||||
histCalc.addData( std::vector<double>{ -0.1, 0.0, 3.0, 4.9, 7.0, 9.9, 10.0, 10.1, HUGE_VAL, -HUGE_VAL, std::nan( "" ) } );
|
||||
|
||||
// Uniform bins of width 2. Values outside [0, 10] and invalid numbers are discarded.
|
||||
// The maximum value is included in the last bin.
|
||||
std::vector<size_t> expected = { 1, 1, 1, 1, 2 };
|
||||
EXPECT_EQ( expected, histogram );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigStatisticsMath, LogarithmicHistogramBinning )
|
||||
{
|
||||
std::vector<size_t> histogram;
|
||||
RigHistogramCalculator histCalc( 1.0, 1000.0, 3, &histogram, RigHistogramCalculator::BinningMode::LOGARITHMIC );
|
||||
|
||||
histCalc.addData( std::vector<double>{ 0.5, 1.0, 2.0, 20.0, 200.0, 999.0, 1000.0, 2000.0 } );
|
||||
|
||||
// One bin per decade: [1, 10), [10, 100), [100, 1000]. Values outside the range are discarded.
|
||||
std::vector<size_t> expected = { 2, 1, 3 };
|
||||
EXPECT_EQ( expected, histogram );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigStatisticsMath, CustomRangeExcludesOutOfRangeValues )
|
||||
{
|
||||
std::vector<size_t> histogram;
|
||||
RigHistogramCalculator histCalc( 10.0, 20.0, 2, &histogram );
|
||||
|
||||
histCalc.addData( std::vector<double>{ 5.0, 12.0, 18.0, 20.0, 25.0 } );
|
||||
|
||||
std::vector<size_t> expected = { 1, 2 };
|
||||
EXPECT_EQ( expected, histogram );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigStatisticsMath, CustomRangeClampsToBoundaryBins )
|
||||
{
|
||||
{
|
||||
std::vector<size_t> histogram;
|
||||
RigHistogramCalculator histCalc( 10.0,
|
||||
20.0,
|
||||
2,
|
||||
&histogram,
|
||||
RigHistogramCalculator::BinningMode::LINEAR,
|
||||
RigHistogramCalculator::OutOfRangeHandling::INCLUDE_IN_BOUNDARY_BINS );
|
||||
|
||||
histCalc.addData( std::vector<double>{ 5.0, 12.0, 18.0, 20.0, 25.0, HUGE_VAL, -HUGE_VAL, std::nan( "" ) } );
|
||||
|
||||
// Below range -> first bin, above range -> last bin, invalid numbers still skipped
|
||||
std::vector<size_t> expected = { 2, 3 };
|
||||
EXPECT_EQ( expected, histogram );
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<size_t> histogram;
|
||||
RigHistogramCalculator histCalc( 10.0,
|
||||
1000.0,
|
||||
2,
|
||||
&histogram,
|
||||
RigHistogramCalculator::BinningMode::LOGARITHMIC,
|
||||
RigHistogramCalculator::OutOfRangeHandling::INCLUDE_IN_BOUNDARY_BINS );
|
||||
|
||||
histCalc.addData( std::vector<double>{ 5.0, 50.0, 2000.0 } );
|
||||
|
||||
std::vector<size_t> expected = { 2, 1 };
|
||||
EXPECT_EQ( expected, histogram );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigStatisticsMath, LogarithmicBinningNonPositiveValues )
|
||||
{
|
||||
{
|
||||
std::vector<size_t> histogram;
|
||||
RigHistogramCalculator histCalc( 1.0, 100.0, 2, &histogram, RigHistogramCalculator::BinningMode::LOGARITHMIC );
|
||||
|
||||
histCalc.addData( std::vector<double>{ 0.0, -5.0, 5.0 } );
|
||||
|
||||
std::vector<size_t> expected = { 1, 0 };
|
||||
EXPECT_EQ( expected, histogram );
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<size_t> histogram;
|
||||
RigHistogramCalculator histCalc( 1.0,
|
||||
100.0,
|
||||
2,
|
||||
&histogram,
|
||||
RigHistogramCalculator::BinningMode::LOGARITHMIC,
|
||||
RigHistogramCalculator::OutOfRangeHandling::INCLUDE_IN_BOUNDARY_BINS );
|
||||
|
||||
histCalc.addData( std::vector<double>{ 0.0, -5.0, 5.0 } );
|
||||
|
||||
// Non-positive values cannot be represented on a logarithmic scale and count in the first bin
|
||||
std::vector<size_t> expected = { 3, 0 };
|
||||
EXPECT_EQ( expected, histogram );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigStatisticsMath, LogarithmicHistogramPercentiles )
|
||||
{
|
||||
std::vector<size_t> histogram;
|
||||
RigHistogramCalculator histCalc( 1.0, 10000.0, 4, &histogram, RigHistogramCalculator::BinningMode::LOGARITHMIC );
|
||||
|
||||
histCalc.addData( std::vector<double>{ 1.0, 10.0, 100.0, 1000.0, 10000.0 } );
|
||||
|
||||
// Percentile interpolation happens in log10 space, results are transformed back to the value domain
|
||||
double p10 = histCalc.calculatePercentil( 0.1, RigStatisticsMath::PercentileStyle::REGULAR );
|
||||
double p50 = histCalc.calculatePercentil( 0.5, RigStatisticsMath::PercentileStyle::REGULAR );
|
||||
|
||||
EXPECT_DOUBLE_EQ( std::pow( 10.0, 0.5 ), p10 );
|
||||
EXPECT_DOUBLE_EQ( std::pow( 10.0, 2.5 ), p50 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigStatisticsMath, HistogramDegenerateRanges )
|
||||
{
|
||||
{
|
||||
// Zero range collapses to a single bin
|
||||
std::vector<size_t> histogram;
|
||||
RigHistogramCalculator histCalc( 5.0, 5.0, 3, &histogram );
|
||||
histCalc.addValue( 5.0 );
|
||||
|
||||
std::vector<size_t> expected = { 1 };
|
||||
EXPECT_EQ( expected, histogram );
|
||||
}
|
||||
|
||||
{
|
||||
// Logarithmic binning with a non-positive minimum falls back to linear binning
|
||||
std::vector<size_t> histogram;
|
||||
RigHistogramCalculator histCalc( 0.0, 10.0, 2, &histogram, RigHistogramCalculator::BinningMode::LOGARITHMIC );
|
||||
histCalc.addValue( 7.0 );
|
||||
|
||||
std::vector<size_t> expected = { 0, 1 };
|
||||
EXPECT_EQ( expected, histogram );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
namespace
|
||||
{
|
||||
class VectorBackedStatisticsCalculator : public RigStatisticsCalculator
|
||||
{
|
||||
public:
|
||||
explicit VectorBackedStatisticsCalculator( const std::vector<std::vector<double>>& valuesPerTimeStep )
|
||||
: m_valuesPerTimeStep( valuesPerTimeStep )
|
||||
{
|
||||
}
|
||||
|
||||
void minMaxCellScalarValues( size_t timeStepIndex, double& min, double& max ) override
|
||||
{
|
||||
MinMaxAccumulator accumulator( min, max );
|
||||
accumulator.addData( m_valuesPerTimeStep[timeStepIndex] );
|
||||
min = accumulator.min;
|
||||
max = accumulator.max;
|
||||
}
|
||||
|
||||
void posNegClosestToZero( size_t timeStepIndex, double& pos, double& neg ) override
|
||||
{
|
||||
PosNegAccumulator accumulator( pos, neg );
|
||||
accumulator.addData( m_valuesPerTimeStep[timeStepIndex] );
|
||||
pos = accumulator.pos;
|
||||
neg = accumulator.neg;
|
||||
}
|
||||
|
||||
void valueSumAndSampleCount( size_t timeStepIndex, double& valueSum, size_t& sampleCount ) override
|
||||
{
|
||||
SumCountAccumulator accumulator( valueSum, sampleCount );
|
||||
accumulator.addData( m_valuesPerTimeStep[timeStepIndex] );
|
||||
valueSum = accumulator.valueSum;
|
||||
sampleCount = accumulator.sampleCount;
|
||||
}
|
||||
|
||||
void addDataToHistogramCalculator( size_t timeStepIndex, RigHistogramCalculator& histogramCalculator ) override
|
||||
{
|
||||
histogramCalculator.addData( m_valuesPerTimeStep[timeStepIndex] );
|
||||
}
|
||||
|
||||
void uniqueValues( size_t timeStepIndex, std::set<int>& values ) override {}
|
||||
|
||||
size_t timeStepCount() override { return m_valuesPerTimeStep.size(); }
|
||||
|
||||
private:
|
||||
std::vector<std::vector<double>> m_valuesPerTimeStep;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
/// computeHistogram() fills a custom-configured histogram calculator without disturbing the
|
||||
/// statistics cached for other consumers
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RigStatisticsMath, StatisticsDataCacheComputeHistogramPassThrough )
|
||||
{
|
||||
cvf::ref<VectorBackedStatisticsCalculator> calculator = new VectorBackedStatisticsCalculator( { { 1.0, 10.0, 100.0 }, { 1000.0, 0.5 } } );
|
||||
|
||||
cvf::ref<RigStatisticsDataCache> cache = new RigStatisticsDataCache( calculator.p() );
|
||||
|
||||
std::vector<size_t> cachedHistogramBefore = cache->cellScalarValuesHistogram();
|
||||
|
||||
double minBefore, maxBefore, p10Before, p90Before;
|
||||
cache->minMaxCellScalarValues( minBefore, maxBefore );
|
||||
cache->p10p90CellScalarValues( p10Before, p90Before );
|
||||
|
||||
{
|
||||
std::vector<size_t> histogram;
|
||||
RigHistogramCalculator histCalc( 1.0, 1000.0, 3, &histogram, RigHistogramCalculator::BinningMode::LOGARITHMIC );
|
||||
cache->computeHistogram( histCalc );
|
||||
|
||||
// All time steps: {1, 10, 100, 1000, 0.5} in decade bins, 0.5 is below range and discarded
|
||||
std::vector<size_t> expected = { 1, 1, 2 };
|
||||
EXPECT_EQ( expected, histogram );
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<size_t> histogram;
|
||||
RigHistogramCalculator histCalc( 1.0, 1000.0, 3, &histogram, RigHistogramCalculator::BinningMode::LOGARITHMIC );
|
||||
cache->computeHistogram( 0, histCalc );
|
||||
|
||||
std::vector<size_t> expected = { 1, 1, 1 };
|
||||
EXPECT_EQ( expected, histogram );
|
||||
}
|
||||
|
||||
// The cached statistics are not perturbed by the custom histogram computations
|
||||
EXPECT_EQ( cachedHistogramBefore, cache->cellScalarValuesHistogram() );
|
||||
|
||||
double minAfter, maxAfter, p10After, p90After;
|
||||
cache->minMaxCellScalarValues( minAfter, maxAfter );
|
||||
cache->p10p90CellScalarValues( p10After, p90After );
|
||||
|
||||
EXPECT_DOUBLE_EQ( minBefore, minAfter );
|
||||
EXPECT_DOUBLE_EQ( maxBefore, maxAfter );
|
||||
EXPECT_DOUBLE_EQ( p10Before, p10After );
|
||||
EXPECT_DOUBLE_EQ( p90Before, p90After );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -99,3 +99,128 @@ TEST( RimHistogramDataSourceTest, NonCumulativeIsUnchanged )
|
||||
EXPECT_NEAR( 2.0, frequencies[1], 1e-9 );
|
||||
EXPECT_NEAR( 3.0, frequencies[2], 1e-9 );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimHistogramDataSourceTest, LinearBinsUnchangedWithDefaultBinningMode )
|
||||
{
|
||||
std::vector<double> xValues = RimHistogramDataSource::computeHistogramBins( 0.0, 10.0, 2, RimHistogramPlot::GraphType::BAR_GRAPH, false );
|
||||
|
||||
std::vector<double> expected = { 0.0, 0.0, 5.0, 5.0, 10.0, 10.0 };
|
||||
ASSERT_EQ( expected.size(), xValues.size() );
|
||||
for ( size_t i = 0; i < expected.size(); i++ )
|
||||
{
|
||||
EXPECT_NEAR( expected[i], xValues[i], 1e-9 );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimHistogramDataSourceTest, LogarithmicBinsBarGraph )
|
||||
{
|
||||
std::vector<double> xValues = RimHistogramDataSource::computeHistogramBins( 1.0,
|
||||
1000.0,
|
||||
3,
|
||||
RimHistogramPlot::GraphType::BAR_GRAPH,
|
||||
false,
|
||||
RigHistogramCalculator::BinningMode::LOGARITHMIC );
|
||||
|
||||
std::vector<double> expected = { 1.0, 1.0, 10.0, 10.0, 100.0, 100.0, 1000.0, 1000.0 };
|
||||
ASSERT_EQ( expected.size(), xValues.size() );
|
||||
for ( size_t i = 0; i < expected.size(); i++ )
|
||||
{
|
||||
EXPECT_NEAR( expected[i], xValues[i], 1e-9 );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimHistogramDataSourceTest, LogarithmicBinsCumulativeBarGraph )
|
||||
{
|
||||
std::vector<double> xValues = RimHistogramDataSource::computeHistogramBins( 1.0,
|
||||
1000.0,
|
||||
3,
|
||||
RimHistogramPlot::GraphType::BAR_GRAPH,
|
||||
true,
|
||||
RigHistogramCalculator::BinningMode::LOGARITHMIC );
|
||||
|
||||
// A cumulative curve is not closed on the right side
|
||||
std::vector<double> expected = { 1.0, 1.0, 10.0, 10.0, 100.0, 100.0, 1000.0 };
|
||||
ASSERT_EQ( expected.size(), xValues.size() );
|
||||
for ( size_t i = 0; i < expected.size(); i++ )
|
||||
{
|
||||
EXPECT_NEAR( expected[i], xValues[i], 1e-9 );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimHistogramDataSourceTest, LogarithmicBinsLineGraph )
|
||||
{
|
||||
std::vector<double> xValues = RimHistogramDataSource::computeHistogramBins( 1.0,
|
||||
1000.0,
|
||||
3,
|
||||
RimHistogramPlot::GraphType::LINE_GRAPH,
|
||||
false,
|
||||
RigHistogramCalculator::BinningMode::LOGARITHMIC );
|
||||
|
||||
// Bin centers are the geometric means of the bin edges
|
||||
std::vector<double> expected = { std::pow( 10.0, 0.5 ), std::pow( 10.0, 1.5 ), std::pow( 10.0, 2.5 ) };
|
||||
ASSERT_EQ( expected.size(), xValues.size() );
|
||||
for ( size_t i = 0; i < expected.size(); i++ )
|
||||
{
|
||||
EXPECT_NEAR( expected[i], xValues[i], 1e-9 );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
///
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
TEST( RimHistogramDataSourceTest, ComputeBinRange )
|
||||
{
|
||||
using BinRangeMode = RimHistogramDataSource::BinRangeMode;
|
||||
using BinningMode = RigHistogramCalculator::BinningMode;
|
||||
|
||||
const double smallestPositive = 0.01;
|
||||
|
||||
{
|
||||
auto [min, max] =
|
||||
RimHistogramDataSource::computeBinRange( BinRangeMode::AUTOMATIC, 5.0, 6.0, -2.0, 100.0, BinningMode::LINEAR, smallestPositive );
|
||||
EXPECT_DOUBLE_EQ( -2.0, min );
|
||||
EXPECT_DOUBLE_EQ( 100.0, max );
|
||||
}
|
||||
|
||||
{
|
||||
auto [min, max] =
|
||||
RimHistogramDataSource::computeBinRange( BinRangeMode::USER_DEFINED, 5.0, 6.0, -2.0, 100.0, BinningMode::LINEAR, smallestPositive );
|
||||
EXPECT_DOUBLE_EQ( 5.0, min );
|
||||
EXPECT_DOUBLE_EQ( 6.0, max );
|
||||
}
|
||||
|
||||
{
|
||||
// A non-positive minimum is replaced by the smallest positive value for logarithmic binning
|
||||
auto [min, max] =
|
||||
RimHistogramDataSource::computeBinRange( BinRangeMode::AUTOMATIC, 5.0, 6.0, -2.0, 100.0, BinningMode::LOGARITHMIC, smallestPositive );
|
||||
EXPECT_DOUBLE_EQ( smallestPositive, min );
|
||||
EXPECT_DOUBLE_EQ( 100.0, max );
|
||||
}
|
||||
|
||||
{
|
||||
auto [min, max] =
|
||||
RimHistogramDataSource::computeBinRange( BinRangeMode::USER_DEFINED, 0.0, 6.0, -2.0, 100.0, BinningMode::LOGARITHMIC, smallestPositive );
|
||||
EXPECT_DOUBLE_EQ( smallestPositive, min );
|
||||
EXPECT_DOUBLE_EQ( 6.0, max );
|
||||
}
|
||||
|
||||
{
|
||||
// A positive minimum is used unchanged for logarithmic binning
|
||||
auto [min, max] =
|
||||
RimHistogramDataSource::computeBinRange( BinRangeMode::USER_DEFINED, 5.0, 6.0, -2.0, 100.0, BinningMode::LOGARITHMIC, smallestPositive );
|
||||
EXPECT_DOUBLE_EQ( 5.0, min );
|
||||
EXPECT_DOUBLE_EQ( 6.0, max );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user