Skip to content
Open
Show file tree
Hide file tree
Changes from 23 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
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
220 changes: 220 additions & 0 deletions src/coreComponents/dataRepository/AttributeLimits.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
/*
* ------------------------------------------------------------------------------------------------------------
* 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 "codingUtilities/traits.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 LimitValueType
* @brief Structure giving the underlying value type that limits apply to.
*
* For a scalar T is T itself. For an array T is the type of the values in
* the array (Array::value_type).
*/
template< typename T, bool = traits::is_array_type< T > >
struct LimitValueType
{};

template< typename T >
struct LimitValueType< T, true >
{
using type = typename T::value_type;
};

template< typename T >
struct LimitValueType< T, false >
{
using type = T;
};

/**
* @brief Alias resolving to the type that limits apply to
*
* For a scalar value it is the scalar type itself.
* For an array it is the type of the values in the array.
*/
template< typename T >
using limit_value_type_t = typename LimitValueType< T >::type;

/**
* @struct is_limitable
* @tparam T type to check
* @brief Trait determining whether attribute limits can be applied to type @p T
*
* Limits apply to numeric types (integer, real32, real64, etc.) including arrays
* of numeric types (array1d< integer >, array2d< real64 >, etc.)
*/
template< typename T >
struct is_limitable
{
static constexpr bool value = std::is_arithmetic< limit_value_type_t< 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< limit_value_type_t< T > > > min;
std::optional< Bound< limit_value_type_t< T > > > max;

string getRangeStr() const
{
if( !this->min.has_value() && !this->max.has_value() )
{
return string();
}

string const lowerRange = this->min.has_value()
? GEOS_FMT( "{}{}", this->min->isInclusive ? "[" : "(", this->min->value )
: string( "(-inf" );
string const upperRange = this->max.has_value()
? GEOS_FMT( "{}{}", this->max->value, this->max->isInclusive ? "]" : ")" )
: string( "+inf)" );
return GEOS_FMT( "{}, {}", lowerRange, upperRange );
}
};


// 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
Loading
Loading