Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
39 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
340897a
add new line character to the documentated ranges
kdrienCG Jun 25, 2026
8a68e85
add a namespace for wrapper limits
kdrienCG Jun 26, 2026
07c20e1
rename getter methods
kdrienCG Jun 26, 2026
f535f76
Merge branch 'develop' into feature/kdrienCG/attributeLimitMetadata
kdrienCG Jul 16, 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
170 changes: 170 additions & 0 deletions src/coreComponents/dataRepository/AttributeLimits.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
* ------------------------------------------------------------------------------------------------------------
* SPDX-License-Identifier: LGPL-2.1-only
*
* Copyright (c) 2016-2024 Lawrence Livermore National Security LLC
* Copyright (c) 2018-2024 TotalEnergies
* Copyright (c) 2018-2024 The Board of Trustees of the Leland Stanford Junior University
* Copyright (c) 2023-2024 Chevron
* Copyright (c) 2019- GEOS/GEOSX Contributors
* All rights reserved
*
* See top level LICENSE, COPYRIGHT, CONTRIBUTORS, NOTICE, and ACKNOWLEDGEMENTS files for details.
* ------------------------------------------------------------------------------------------------------------
*/


#ifndef GEOS_DATAREPOSITORY_ATTRIBUTELIMITS_HPP_
#define GEOS_DATAREPOSITORY_ATTRIBUTELIMITS_HPP_

#include "common/DataTypes.hpp"
#include "common/format/EnumStrings.hpp"
#include <optional>
#include <type_traits>

namespace geos
{

namespace dataRepository
{

/**
* @enum LimitsMode
* @brief Enforcement mode associated with the limits of an attribute
*
* - Indicative: the limits are documentation only, no runtime check is performed.
* - Warning: a value outside the limits emits a runtime warning.
* - Error: a value outside the limits throws.
*/
enum class LimitsMode : integer
{
Indicative,
Warning,
Error
};

ENUM_STRINGS( LimitsMode,
"Indicative",
"Warning",
"Error" );

/**
* @struct is_limitable
* @tparam T type to check
* @brief Trait determining whether attribute limits can be applied to type @p T
*
* Limits apply to scalar numeric types (integer, real32, real64, etc.)
*/
template< typename T >
struct is_limitable
{
static constexpr bool value = std::is_arithmetic< T >::value;
};

/**
* @brief Convenience variable template alias for is_limitable< T >::value
*/
template< typename T >
inline constexpr bool is_limitable_v = is_limitable< T >::value;

/**
* @struct Bound
* @brief Structure containing informations about an attribute limit.
*/
template< typename T >
struct Bound
Comment thread
MelReyCG marked this conversation as resolved.
{
T value;
bool isInclusive = true;

/**
* @brief Bound constructor to write a limit without the "Bound{ ... }" syntax
* @param value The limit value to set
* @param isInclusive Wether the limit should be inclusive or not
*
Comment thread
MelReyCG marked this conversation as resolved.
Outdated
* @code
* .setLimits( 0.0, 1.0 ) // where setLimits takes `Bound` parameters, those parameters can
* // be written only with the value. The isInclusive property will
* // default to true.
* @endcode
*/
Bound( T v, bool inclusive = true )
Comment thread
MelReyCG marked this conversation as resolved.
Outdated
: value( v ), isInclusive( inclusive )
{}
};

/**
* @brief Creates an inclusive limit of @p value
* @param value The inclusive limit value to set
*/
template< typename T >
Bound< T > inclusive( T value )
{
return Bound< T >{ value, /*isInclusive*/ true };
}

/**
* @brief Creates an exclusive limit of @p value
* @param value The inclusive limit value to set
*/
template< typename T >
Bound< T > exclusive( T value )
{
return Bound< T >{ value, /*isInclusive*/ false };
}

/**
* @struct Limits
* @brief Storage for the optional min/max bounds of a wrapped value.
*
* Specialized so that the members (std::optional< T >) are only instanciated
* for limitable types. Preventing instantiation of non-limitable types, especially
* abstract types that can't be instantiated with std::optional< absT >.
*/
template< typename T, bool = is_limitable_v< T > >
struct Limits
Comment thread
MelReyCG marked this conversation as resolved.
{};

template< typename T >
struct Limits< T, true >
{
std::optional< Bound< T > > min;
std::optional< Bound< T > > max;
};


// Helper methods
Comment thread
MelReyCG marked this conversation as resolved.
Outdated

/**
* @brief Compare the given value with the min limit, taking account for the inclusive xor exclusive
* property of the limit.
* @param value The value to compare to the limit
* @param minLimit The min limit containing the inclusive
* @return True if the value is below the min limit, false otherwise
*/
template< typename T >
static bool isValueBelowMin( T const & value, Bound< T > const & minLimit )
Comment thread
MelReyCG marked this conversation as resolved.
Outdated
{
return minLimit.isInclusive ? ( value < minLimit.value )
: ( value <= minLimit.value );
}

/**
* @brief Compare the given value with the max limit, taking account for the inclusive xor exclusive
* property of the limit.
* @param value The value to compare to the limit
* @param maxLimit The max limit containing the inclusive
* @return True if the value is above the max limit, false otherwise
*/
template< typename T >
static bool isValueAboveMax( T const & value, Bound< T > const & maxLimit )
{
return maxLimit.isInclusive ? ( value > maxLimit.value )
: ( value >= maxLimit.value );
}

} /* namespace dataRepository */

} /* namespace geos */

#endif /* GEOS_DATAREPOSITORY_ATTRIBUTELIMITS_HPP_ */
1 change: 1 addition & 0 deletions src/coreComponents/dataRepository/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Also contains a wrapper to process entries from an xml file into data types.
# Specify all headers
#
set( dataRepository_headers
AttributeLimits.hpp
BufferOps.hpp
BufferOpsDevice.hpp
BufferOps_inline.hpp
Expand Down
123 changes: 122 additions & 1 deletion src/coreComponents/dataRepository/Wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
#define GEOS_DATAREPOSITORY_WRAPPER_HPP_

// Source inclues
#include "common/format/Format.hpp"
#include "common/logger/Logger.hpp"
Comment thread
MelReyCG marked this conversation as resolved.
Outdated
#include "dataRepository/AttributeLimits.hpp"
#include "wrapperHelpers.hpp"
#include "KeyNames.hpp"
#include "LvArray/src/limits.hpp"
Expand All @@ -37,8 +40,8 @@
#include "WrapperBase.hpp"

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

namespace geos
Expand Down Expand Up @@ -204,6 +207,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 +722,71 @@ 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< Bound< T > > min,
std::optional< Bound< T > > max,
LimitsMode mode = LimitsMode::Warning )
Comment thread
kdrienCG marked this conversation as resolved.
Outdated
{
m_limits.min = min;
m_limits.max = max;
Comment thread
MelReyCG marked this conversation as resolved.
Outdated
m_limitsMode = mode;
return *this;
}

template< typename U=T >
std::enable_if_t< !is_limitable_v< U >, Wrapper< T > & >
setLimits( std::optional< Bound< T > >,
std::optional< Bound< T > >,
LimitsMode mode = LimitsMode::Warning )
{
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< Bound< T > > const & >
getMinValue() const
{
return m_limits.min;
}
Comment thread
MelReyCG marked this conversation as resolved.
Outdated

/**
* @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< Bound< T > > const & >
getMaxValue() const
{
return m_limits.max;
}

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

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

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

T const & value = reference();
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 {} for attribute '{}' is outside the allowed range.",
Comment thread
MelReyCG marked this conversation as resolved.
Outdated
value, getName() );

switch( m_limitsMode )
{
case LimitsMode::Warning:
GEOS_WARNING( msg );
break;

case LimitsMode::Error:
GEOS_THROW( msg, InputError );
break;

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

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

/// 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 +1218,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.
Limits< 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( LimitsMode::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
Loading
Loading