Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2f08163
(WIP) add min/max limit to Wrapper.hpp
kdrienCG May 13, 2026
45713ea
move min/max optionals in a struct instanciated only when T is limitable
kdrienCG May 13, 2026
c07a75a
create is_limitable trait
kdrienCG May 13, 2026
4c49318
add limits validation for input values
kdrienCG May 13, 2026
19d2ae8
add limits modes to limits
kdrienCG May 13, 2026
fd14de4
implement limits modes in validateLimits()
kdrienCG May 13, 2026
32f1586
remove setMinValue() and setMaxValue()
kdrienCG May 13, 2026
796bb16
add convenience alias is_limitable_v
kdrienCG May 13, 2026
14bda7b
modify outside range message
kdrienCG May 13, 2026
581b6d8
add inclusive-exclusive properties using a Bound type
kdrienCG May 20, 2026
2590521
Merge branch 'develop' into feature/kdrienCG/attributeLimitMetadata
kdrienCG May 20, 2026
7436f3c
support arrays of numbers
kdrienCG May 22, 2026
a456aca
set validateLimit() to public
kdrienCG May 22, 2026
80a9541
add tests
kdrienCG May 22, 2026
65779e4
add getDataContext() to the limit validation message
kdrienCG May 22, 2026
54053c7
add range to the limit validation message
kdrienCG May 22, 2026
7d08bd2
set default mode to Error
kdrienCG May 22, 2026
ace25d5
move range string creation in the Limits struct
kdrienCG May 26, 2026
0f51019
add limits to generated documentation
kdrienCG May 29, 2026
3ccdc0f
add GEOS_UNUSED_PARAM to setLimits overload for non-limits
kdrienCG May 29, 2026
931c469
modify BCBakerRelPerm to use limits
kdrienCG Jun 22, 2026
7ceb5a6
Merge branch 'develop' into feature/kdrienCG/attributeLimitMetadata
kdrienCG Jun 22, 2026
9d275da
Merge branch 'develop' into feature/kdrienCG/attributeLimitMetadata
kdrienCG Jun 22, 2026
9067eec
remove un-needed inclusions
kdrienCG Jun 23, 2026
600d6cf
rename Bound struct to WrapperBound
kdrienCG Jun 23, 2026
3de9174
rename Limits to WrapperLimits
kdrienCG Jun 23, 2026
2a0af9f
rename file AttributeLimits.hpp to WrapperLimits.hpp
kdrienCG Jun 23, 2026
23d56ce
add doxygen group delimiters for helper methods in WrapperLimits
kdrienCG Jun 23, 2026
1af0ec5
remove static qualifier
kdrienCG Jun 23, 2026
919b359
add check for min <= max when setting a limit
kdrienCG Jun 23, 2026
0022a39
remove includes
kdrienCG Jun 23, 2026
d0cb744
move getDataContext() in logger macros
kdrienCG Jun 23, 2026
7affa36
remove implicit conversions for WrapperBound
kdrienCG Jun 24, 2026
718d221
fix: rename conduit node "root" to "problem
kdrienCG Jun 24, 2026
98cdb7f
add schema.xsd.other
kdrienCG Jun 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,29 +33,34 @@ BrooksCoreyBakerRelativePermeability::BrooksCoreyBakerRelativePermeability( stri
{
registerWrapper( viewKeyStruct::phaseMinVolumeFractionString(), &m_phaseMinVolumeFraction ).
setApplyDefaultValue( 0.0 ).
setLimits( 0.0, 1.0 ).
setInputFlag( InputFlags::OPTIONAL ).
setDescription( "Minimum volume fraction value for each phase (between 0 and 1) used to compute the relative permeability " );

registerWrapper( viewKeyStruct::waterOilRelPermExponentString(), &m_waterOilRelPermExponent ).
setApplyDefaultValue( 1.0 ).
setLimits( 0.0, std::nullopt ).
setInputFlag( InputFlags::OPTIONAL ).
setDescription( "Rel perm power law exponent for the pair (water phase, oil phase) at residual gas saturation\n"
"The expected format is \"{ waterExp, oilExp }\", in that order" );

registerWrapper( viewKeyStruct::waterOilRelPermMaxValueString(), &m_waterOilRelPermMaxValue ).
setApplyDefaultValue( 0.0 ).
setLimits( 0.0, 1.0 ).
setInputFlag( InputFlags::OPTIONAL ).
setDescription( "Maximum rel perm value for the pair (water phase, oil phase) at residual gas saturation\n"
"The expected format is \"{ waterMax, oilMax }\", in that order" );

registerWrapper( viewKeyStruct::gasOilRelPermExponentString(), &m_gasOilRelPermExponent ).
setApplyDefaultValue( 1.0 ).
setLimits( 0.0, std::nullopt ).
setInputFlag( InputFlags::OPTIONAL ).
setDescription( "Rel perm power law exponent for the pair (gas phase, oil phase) at residual water saturation\n"
"The expected format is \"{ gasExp, oilExp }\", in that order" );

registerWrapper( viewKeyStruct::gasOilRelPermMaxValueString(), &m_gasOilRelPermMaxValue ).
setApplyDefaultValue( 0.0 ).
setLimits( 0.0, 1.0 ).
setInputFlag( InputFlags::OPTIONAL ).
setDescription( "Maximum rel perm value for the pair (gas phase, oil phase) at residual water saturation\n"
"The expected format is \"{ gasMax, oilMax }\", in that order" );
Expand Down Expand Up @@ -97,17 +102,6 @@ void BrooksCoreyBakerRelativePermeability::postInputInitialization()
m_volFracScale = 1.0;
for( integer ip = 0; ip < numFluidPhases(); ++ip )
{
auto const errorMsg = [&]( auto const & attribute )
{
return GEOS_FMT( "invalid value at {}[{}]", attribute, ip );
};
GEOS_UNUSED_VAR( errorMsg );
GEOS_THROW_IF_LT_MSG( m_phaseMinVolumeFraction[ip], 0.0,
errorMsg( viewKeyStruct::phaseMinVolumeFractionString() ),
InputError, getDataContext() );
GEOS_THROW_IF_GT_MSG( m_phaseMinVolumeFraction[ip], 1.0,
errorMsg( viewKeyStruct::phaseMinVolumeFractionString() ),
InputError, getDataContext() );
m_volFracScale -= m_phaseMinVolumeFraction[ip];
}

Expand All @@ -116,40 +110,6 @@ void BrooksCoreyBakerRelativePermeability::postInputInitialization()
InputError, getDataContext() );


for( integer ip = 0; ip < 2; ++ip )
{
auto const errorMsg = [&]( auto const & attribute )
{
return GEOS_FMT( "invalid value at {}[{}]", attribute, ip );
};
GEOS_UNUSED_VAR( errorMsg );
if( m_phaseOrder[PhaseType::WATER] >= 0 )
{
GEOS_THROW_IF_LT_MSG( m_waterOilRelPermExponent[ip], 0.0,
errorMsg( viewKeyStruct::waterOilRelPermExponentString() ),
InputError, getDataContext() );
GEOS_THROW_IF_LT_MSG( m_waterOilRelPermMaxValue[ip], 0.0,
errorMsg( viewKeyStruct::waterOilRelPermMaxValueString() ),
InputError, getDataContext() );
GEOS_THROW_IF_GT_MSG( m_waterOilRelPermMaxValue[ip], 1.0,
errorMsg( viewKeyStruct::waterOilRelPermMaxValueString() ),
InputError, getDataContext() );
}

if( m_phaseOrder[PhaseType::GAS] >= 0 )
{
GEOS_THROW_IF_LT_MSG( m_gasOilRelPermExponent[ip], 0.0,
errorMsg( viewKeyStruct::gasOilRelPermExponentString() ),
InputError, getDataContext() );
GEOS_THROW_IF_LT_MSG( m_gasOilRelPermMaxValue[ip], 0.0,
errorMsg( viewKeyStruct::gasOilRelPermMaxValueString() ),
InputError, getDataContext() );
GEOS_THROW_IF_GT_MSG( m_gasOilRelPermMaxValue[ip], 1.0,
errorMsg( viewKeyStruct::gasOilRelPermMaxValueString() ),
InputError, getDataContext() );
}
}

if( m_phaseOrder[PhaseType::WATER] >= 0 && m_phaseOrder[PhaseType::GAS] >= 0 )
{
real64 const mean = 0.5 * ( m_gasOilRelPermMaxValue[GasOilPairPhaseType::OIL]
Expand Down
1 change: 1 addition & 0 deletions src/coreComponents/dataRepository/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ set( dataRepository_headers
Wrapper.hpp
WrapperBase.hpp
wrapperHelpers.hpp
WrapperLimits.hpp
xmlWrapper.hpp
DataContext.hpp
GroupContext.hpp
Expand Down
170 changes: 169 additions & 1 deletion src/coreComponents/dataRepository/Wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
#include "WrapperBase.hpp"

// System includes
#include <type_traits>
#include <cstdlib>
#include <type_traits>

Expand Down Expand Up @@ -204,6 +203,7 @@ class Wrapper final : public WrapperBase
m_ownsData = castedSource.m_ownsData;
m_default = castedSource.m_default;
m_dimLabels = castedSource.m_dimLabels;
m_limits = castedSource.m_limits;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -718,6 +718,137 @@ class Wrapper final : public WrapperBase
return ss.str();
}

/**
* @brief Set both bounds for this attribute's value.
* @param min the minimum allowed value (inclusive)
* @param max the maximum allowed value (inclusive)
* @param mode the enforcement mode
* @return pointer to Wrapper<T>
*
* @note @p min and @p max are std::optional(s).
* Set them to std::nullopt to disable a limit.
*
* @code
* registerWrapper( viewKeysStruct::fooString(), &m_foo )
* .setLimits( 0.0, 1.0 ) // sets a minimum value of 0.0 and a maximum value of 1.0
* .setLimits( 0.0, std::nullopt ) // sets a minimum value of 0.0 and no maximum value
* .setLimits( inclusive( 0.0 ), std::nullopt ) // sets an inclusive (default) minimum value
* .setLimits( exclusive( 0.0 ), std::nullopt ) // sets an exclusive maximum value
* @endcode
*/
template< typename U=T >
std::enable_if_t< is_limitable_v< U >, Wrapper< T > & >
setLimits( std::optional< LimitArg< limit_value_type_t< T > > > min,
std::optional< LimitArg< limit_value_type_t< T > > > max,
WrapperLimitsMode mode = WrapperLimitsMode::Error )
{
using LimitT = limit_value_type_t< T >;
std::optional< WrapperBound< LimitT > > const minBound = toWrapperBound< LimitT >( min );
std::optional< WrapperBound< LimitT > > const maxBound = toWrapperBound< LimitT >( max );
if( minBound.has_value() && maxBound.has_value() )
{
GEOS_ASSERT_LE_MSG( minBound.value(), maxBound.value(),
"Min value should be less or equal to max value." );
}
m_limits.min = minBound;
m_limits.max = maxBound;
m_limitsMode = mode;
return *this;
}

template< typename U=T >
std::enable_if_t< !is_limitable_v< U > && !traits::is_array_type< U >, Wrapper< T > & >
setLimits( std::optional< LimitArg< limit_value_type_t< T > > >,
std::optional< LimitArg< limit_value_type_t< T > > >,
WrapperLimitsMode GEOS_UNUSED_PARAM( mode ) )
{
static_assert( is_limitable_v< U >,
"setLimits is only supported on scalar arithmetic types." );
return *this;
}

/**
* @brief Accessor for the minimum bound of this attribute's value.
* @return optional containing the typed minimum value, empty if not set
* @note Only available when T is a limitable type
*/
template< typename U=T >
std::enable_if_t< is_limitable_v< U >, std::optional< WrapperBound< limit_value_type_t< T > > > const & >
getMinValue() const
{
return m_limits.min;
}

/**
* @brief Accessor for the maximum bound of this attribute's value.
* @return optional containing the typed maximum value, empty if not set
* @note Only available when T is a limitable type
*/
template< typename U=T >
std::enable_if_t< is_limitable_v< U >, std::optional< WrapperBound< limit_value_type_t< T > > > const & >
getMaxValue() const
{
return m_limits.max;
}

/**
* @copydoc WrapperBase::getLimitsString()
*/
virtual string getLimitsString() const override
{
return getLimitsStringImpl();
}

template< typename U=T >
std::enable_if_t< is_limitable_v< U >, string >
getLimitsStringImpl() const
{
return m_limits.getRangeStr();
}

template< typename U=T >
std::enable_if_t< !is_limitable_v< U >, string >
getLimitsStringImpl() const
{
return string();
}

template< typename U=T >
std::enable_if_t< is_limitable_v< U > && !traits::is_array_type< U >, void >
validateLimits()
{
if( (!m_limits.min.has_value() && !m_limits.max.has_value()) ||
m_limitsMode == WrapperLimitsMode::Indicative )
{
return;
}
validateLimitValue( reference() );
}

template< typename U=T >
std::enable_if_t< is_limitable_v< U > && traits::is_array_type< U >, void >
validateLimits()
{
if( (!m_limits.min.has_value() && !m_limits.max.has_value()) ||
m_limitsMode == WrapperLimitsMode::Indicative )
{
return;
}
auto const values = m_data->toViewConst();
for( limit_value_type_t< T > value : values )
{
validateLimitValue( value );
}
}

template< typename U=T >
std::enable_if_t< !is_limitable_v< U >, void >
validateLimits()
{
/* no-op */
}


virtual bool processInputFile( xmlWrapper::xmlNode const & targetNode,
xmlWrapper::xmlNodePos const & nodePos ) override
{
Expand Down Expand Up @@ -749,6 +880,11 @@ class Wrapper final : public WrapperBase
targetNode,
getDefaultValueStruct() );
}

if( m_successfulReadFromInput )
{
validateLimits();
}
}
catch( std::exception const & ex )
{
Expand Down Expand Up @@ -1086,6 +1222,35 @@ class Wrapper final : public WrapperBase
return this->packByIndexImpl< false >( dummy, packList, withMetadata, onDevice, events );
}

template< typename V >
void validateLimitValue( V const & value ) const
{
bool const belowMin = m_limits.min.has_value() ? isValueBelowMin( value, *m_limits.min ) : false;
bool const aboveMax = m_limits.max.has_value() ? isValueAboveMax( value, *m_limits.max ) : false;
if( !belowMin && !aboveMax )
{
return;
}

string const msg = GEOS_FMT( "Value {} is outside the allowed range {}.",
value, m_limits.getRangeStr() );

switch( m_limitsMode )
{
case WrapperLimitsMode::Warning:
GEOS_WARNING( msg, getDataContext() );
break;

case WrapperLimitsMode::Error:
GEOS_THROW( msg, InputError, getDataContext() );
break;

default:
GEOS_LOG_RANK_0( "Unimplemented WrapperLimitsMode" );
break;
}
}

/// flag to indicate whether or not this wrapper is responsible for allocation/deallocation of the object at the
/// address of m_data
bool m_ownsData;
Expand All @@ -1100,6 +1265,9 @@ class Wrapper final : public WrapperBase

/// stores dimension labels (used mainly for plotting) for multidimensional arrays, empty member otherwise
wrapperHelpers::ArrayDimLabels< T > m_dimLabels;

/// stores the (optional) min/max bounds for the wrapped value.
WrapperLimits< T > m_limits;
};

}
Expand Down
2 changes: 2 additions & 0 deletions src/coreComponents/dataRepository/WrapperBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ WrapperBase::WrapperBase( string const & name,
m_inputFlag( InputFlags::INVALID ),
m_successfulReadFromInput( false ),
m_description(),
m_limitsMode( WrapperLimitsMode::Indicative ),
m_rtTypeName( rtTypeName ),
m_registeringObjects(),
m_conduitNode( parent.getConduitNode()[ name ] ),
Expand All @@ -61,6 +62,7 @@ void WrapperBase::copyWrapperAttributes( WrapperBase const & source )
m_plotLevel = source.m_plotLevel;
m_inputFlag = source.m_inputFlag;
m_description = source.m_description;
m_limitsMode = source.m_limitsMode;
m_rtTypeName = source.m_rtTypeName;
}

Expand Down
20 changes: 20 additions & 0 deletions src/coreComponents/dataRepository/WrapperBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "common/DataTypes.hpp"
#include "common/GEOS_RAJA_Interface.hpp"
#include "common/Span.hpp"
#include "dataRepository/WrapperLimits.hpp"
#include "InputFlags.hpp"
#include "xmlWrapper.hpp"
#include "RestartFlags.hpp"
Expand Down Expand Up @@ -203,6 +204,13 @@ class WrapperBase
*/
virtual string getDefaultValueString() const = 0;

/**
* @brief Return a string representing the allowed value range.
* @return A string of the form "[min, max]" using standard interval notation, or
* an empty string if no limits are set.
*/
virtual string getLimitsString() const = 0;

/**
* @brief Initialize the wrapper from the input xml node.
* @param targetNode the xml node to initialize from.
Expand Down Expand Up @@ -529,6 +537,15 @@ class WrapperBase
return m_description;
}

/**
* @brief Get the enforcement mode of the (optional) attribute limits
* @return the WrapperLimitsMode of the wrapper
*/
WrapperLimitsMode getLimitsMode() const
{
return m_limitsMode;
}

/**
* @brief Get the list of names of groups that registered this wrapper.
* @return vector of object names
Expand Down Expand Up @@ -699,6 +716,9 @@ class WrapperBase
/// A string description of the wrapped object
string m_description;

/// Enforcement mode of the (optional) attribute limits
WrapperLimitsMode m_limitsMode;

/// A string regex to validate the input values string to parse for the wrapped object
string m_rtTypeName;

Expand Down
Loading
Loading