Skip to content

Commit

Permalink
Add realization filtering based on text string
Browse files Browse the repository at this point in the history
Add unit tests and support min max range specifications. Example string "-5, 5, 8-10, 12-"
  • Loading branch information
magnesj committed Mar 20, 2024
1 parent 2001c94 commit b0a6ebf
Show file tree
Hide file tree
Showing 8 changed files with 218 additions and 4 deletions.
8 changes: 8 additions & 0 deletions ApplicationLibCode/Application/RiaSummaryDefines.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,11 @@ QString RiaDefines::summaryCalculated()
{
return "Calculated";
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiaDefines::summaryRealizationNumber()
{
return "RI:REALIZATION_NUM";
}
2 changes: 2 additions & 0 deletions ApplicationLibCode/Application/RiaSummaryDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,6 @@ QString summaryLgrWell();
QString summaryLgrBlock();
QString summaryCalculated();

QString summaryRealizationNumber();

}; // namespace RiaDefines
91 changes: 91 additions & 0 deletions ApplicationLibCode/Application/Tools/RiaStdStringTools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
/////////////////////////////////////////////////////////////////////////////////

#include "RiaStdStringTools.h"
#include "RiaLogging.h"

#include "fast_float/include/fast_float/fast_float.h"

Expand Down Expand Up @@ -301,3 +302,93 @@ std::string RiaStdStringTools::removeHtmlTags( const std::string& s )

return result;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<int> RiaStdStringTools::valuesFromRangeSelection( const std::string& s )
{
std::set<int> result;
std::istringstream stringStream( s );
std::string token;

while ( std::getline( stringStream, token, ',' ) )
{
std::istringstream tokenStream( token );
int startIndex, endIndex;
char dash;

if ( tokenStream >> startIndex )
{
if ( tokenStream >> dash && dash == '-' && tokenStream >> endIndex )
{
for ( int i = startIndex; i <= endIndex; ++i )
{
result.insert( i );
}
}
else
{
result.insert( startIndex );
}
}
}

return result;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<int> RiaStdStringTools::valuesFromRangeSelection( const std::string& s, int minVal, int maxVal )
{
try
{
std::set<int> result;
std::stringstream stringStream( s );
std::string token;

while ( std::getline( stringStream, token, ',' ) )
{
token = RiaStdStringTools::trimString( token );

// Check for range
size_t dashPos = token.find( '-' );
if ( dashPos != std::string::npos )
{
int startIndex = ( dashPos == 0 ) ? minVal : std::stoi( token.substr( 0, dashPos ) );
int endIndex = ( dashPos == token.size() - 1 ) ? maxVal : std::stoi( token.substr( dashPos + 1 ) );
if ( startIndex > endIndex )
{
// If start is greater than end, swap them
std::swap( startIndex, endIndex );
}
for ( int i = startIndex; i <= endIndex; ++i )
{
result.insert( i );
}
}
else
{
// Check for individual numbers
result.insert( std::stoi( token ) );
}
}

return result;
}
catch ( const std::exception& e )
{
QString str = QString( "Failed to convert text string \" %1 \" to list of integers : " ).arg( QString::fromStdString( s ) ) +
QString::fromStdString( e.what() );
RiaLogging::error( str );
}
catch ( ... )
{
QString str =
QString( "Failed to convert text string \" %1 \" to list of integers : Caught unknown exception" ).arg( QString::fromStdString( s ) );
RiaLogging::error( str );
}

return {};
}
8 changes: 8 additions & 0 deletions ApplicationLibCode/Application/Tools/RiaStdStringTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <algorithm>
#include <iterator>
#include <numeric>
#include <set>
#include <string>
#include <vector>

Expand Down Expand Up @@ -60,6 +61,13 @@ class RiaStdStringTools

static std::string removeHtmlTags( const std::string& s );

// Convert the string "1,2,5-8,10" to {1, 2, 5, 6, 7, 8, 10}
static std::set<int> valuesFromRangeSelection( const std::string& s );

// Convert the range string with support for open ended expressions. minimum and maximum value will be used to limit the ranges.
// The input "-3,5-8,10-", min:1, max:12 will produce {1, 2, 3, 5, 6, 7, 8, 10, 11, 12}
static std::set<int> valuesFromRangeSelection( const std::string& s, int minimumValue, int maximumValue );

private:
template <class Container>
static void splitByDelimiter( const std::string& str, Container& cont, char delimiter = ' ' );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "RimEnsembleCurveFilter.h"

#include "RiaCurveDataTools.h"
#include "RiaStdStringTools.h"
#include "RiaSummaryCurveDefinition.h"

#include "RimCustomObjectiveFunction.h"
Expand Down Expand Up @@ -106,6 +107,8 @@ RimEnsembleCurveFilter::RimEnsembleCurveFilter()

CAF_PDM_InitFieldNoDefault( &m_categories, "Categories", "Categories" );

CAF_PDM_InitFieldNoDefault( &m_realizationFilter, "RealizationFilter", "Realization Filter" );

setDeletable( true );
}

Expand Down Expand Up @@ -185,6 +188,11 @@ QString RimEnsembleCurveFilter::description() const
QString descriptor;
if ( m_filterMode() == FilterMode::BY_ENSEMBLE_PARAMETER )
{
if ( m_ensembleParameterName() == RiaDefines::summaryRealizationNumber() )
{
return "Realizations : " + m_realizationFilter;
}

descriptor = QString( "%0" ).arg( m_ensembleParameterName() );
}
else if ( m_filterMode() == FilterMode::BY_OBJECTIVE_FUNCTION )
Expand Down Expand Up @@ -357,7 +365,8 @@ void RimEnsembleCurveFilter::fieldChangedByUi( const caf::PdmFieldHandle* change
}
updateMaxMinAndDefaultValues( true );
}
else if ( changedField == &m_active || changedField == &m_minValue || changedField == &m_maxValue || changedField == &m_categories )
else if ( changedField == &m_active || changedField == &m_minValue || changedField == &m_maxValue || changedField == &m_categories ||
changedField == &m_realizationFilter )
{
if ( curveSet )
{
Expand Down Expand Up @@ -458,7 +467,11 @@ void RimEnsembleCurveFilter::defineUiOrdering( QString uiConfigName, caf::PdmUiO
uiOrdering.add( &m_customObjectiveFunction );
}

if ( eParam.isNumeric() )
if ( m_ensembleParameterName() == RiaDefines::summaryRealizationNumber() )
{
uiOrdering.add( &m_realizationFilter );
}
else if ( eParam.isNumeric() )
{
uiOrdering.add( &m_minValue );
uiOrdering.add( &m_maxValue );
Expand Down Expand Up @@ -506,6 +519,19 @@ std::vector<RimSummaryCase*> RimEnsembleCurveFilter::applyFilter( const std::vec
auto ensemble = curveSet ? curveSet->summaryCaseCollection() : nullptr;
if ( !ensemble || !isActive() ) return allSumCases;

bool useIntegerSelection = false;
std::set<int> integerSelection;

if ( m_ensembleParameterName() == RiaDefines::summaryRealizationNumber() )
{
auto eParam = selectedEnsembleParameter();
int minValue = eParam.minValue;
int maxValue = eParam.maxValue;

integerSelection = RiaStdStringTools::valuesFromRangeSelection( m_realizationFilter().toStdString(), minValue, maxValue );
useIntegerSelection = true;
}

std::set<RimSummaryCase*> casesToRemove;
for ( const auto& sumCase : allSumCases )
{
Expand All @@ -517,7 +543,16 @@ std::vector<RimSummaryCase*> RimEnsembleCurveFilter::applyFilter( const std::vec

auto crpValue = sumCase->caseRealizationParameters()->parameterValue( m_ensembleParameterName() );

if ( eParam.isNumeric() )
if ( useIntegerSelection )
{
int integerValue = crpValue.numericValue();

if ( !integerSelection.contains( integerValue ) )
{
casesToRemove.insert( sumCase );
}
}
else if ( eParam.isNumeric() )
{
if ( !crpValue.isNumeric() || crpValue.numericValue() < m_minValue() || crpValue.numericValue() > m_maxValue() )
{
Expand Down Expand Up @@ -653,6 +688,19 @@ void RimEnsembleCurveFilter::updateMaxMinAndDefaultValues( bool forceDefault )

m_minValue.uiCapability()->setUiName( QString( "Min (%1)" ).arg( m_lowerLimit ) );
m_maxValue.uiCapability()->setUiName( QString( "Max (%1)" ).arg( m_upperLimit ) );

if ( m_ensembleParameterName() == RiaDefines::summaryRealizationNumber() )
{
int lower = eParam.minValue;
int upper = eParam.maxValue;

m_realizationFilter.uiCapability()->setUiName( QString( "Integer Selection\n[%1..%2]" ).arg( lower ).arg( upper ) );

if ( m_realizationFilter().isEmpty() )
{
m_realizationFilter = QString( "%1-%2" ).arg( lower ).arg( upper );
}
}
}
}
else if ( m_filterMode() == FilterMode::BY_OBJECTIVE_FUNCTION )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ class RimEnsembleCurveFilter : public caf::PdmObject
caf::PdmField<double> m_maxValue;
caf::PdmField<std::vector<QString>> m_categories;

caf::PdmField<QString> m_realizationFilter;

double m_lowerLimit;
double m_upperLimit;
};
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ void addCaseRealizationParametersIfFound( RimSummaryCase& sumCase, const QString

int realizationNumber = RifCaseRealizationParametersFileLocator::realizationNumber( modelFolderOrFile );
parameters->setRealizationNumber( realizationNumber );
parameters->addParameter( "RI:REALIZATION_NUM", realizationNumber );
parameters->addParameter( RiaDefines::summaryRealizationNumber(), realizationNumber );

sumCase.setCaseRealizationParameters( parameters );
}
Expand Down
55 changes: 55 additions & 0 deletions ApplicationLibCode/UnitTests/RiaStdStringTools-Test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,58 @@ TEST( RiaStdStringToolsTest, DISABLED_PerformanceConversion )
std::cout << "std::from_chars " << std::setw( 9 ) << diff.count() << " s\n";
}
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RiaStdStringToolsTest, ValuesFromRangeSelection )
{
std::string testString = "1,2,5-10,15";
std::set<int> expectedValues = { 1, 2, 5, 6, 7, 8, 9, 10, 15 };

auto actualValues = RiaStdStringTools::valuesFromRangeSelection( testString );

ASSERT_EQ( expectedValues, actualValues );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RiaStdStringToolsTest, ValuesFromRangeSelectionMinMax )
{
std::string testString = "-3, 5, 6-8, 10, 15-";
int minimumValue = 1;
int maximumValue = 20;
std::set<int> expectedValues = { 1, 2, 3, 5, 6, 7, 8, 10, 15, 16, 17, 18, 19, 20 };

auto actualValues = RiaStdStringTools::valuesFromRangeSelection( testString, minimumValue, maximumValue );

ASSERT_EQ( expectedValues, actualValues );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RiaStdStringToolsTest, TestInvalidRangeStrings )
{
int minimumValue = 1;
int maximumValue = 20;

{
// Handle blank space and inverted from/to
std::string testString = "5, -3, 9-8";
std::set<int> expectedValues = { 1, 2, 3, 5, 8, 9 };
auto actualValues = RiaStdStringTools::valuesFromRangeSelection( testString, minimumValue, maximumValue );

ASSERT_EQ( expectedValues, actualValues );
}

{
// If the range is invalid, the result should be an empty set
std::string testString = "5, a";
std::set<int> expectedValues = {};
auto actualValues = RiaStdStringTools::valuesFromRangeSelection( testString, minimumValue, maximumValue );

ASSERT_EQ( expectedValues, actualValues );
}
}

0 comments on commit b0a6ebf

Please sign in to comment.