diff --git a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_SPE10_layer84/immiscibleTwoPhase_SPE10_layer84_base_iterative.xml b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_SPE10_layer84/immiscibleTwoPhase_SPE10_layer84_base_iterative.xml index 3413d47ef4d..91cd28b0496 100644 --- a/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_SPE10_layer84/immiscibleTwoPhase_SPE10_layer84_base_iterative.xml +++ b/inputFiles/immiscibleMultiphaseFlow/immiscibleTwoPhase_SPE10_layer84/immiscibleTwoPhase_SPE10_layer84_base_iterative.xml @@ -91,33 +91,13 @@ - - - + regionNames="reservoir/block" + permeabilityModelName="rockPerm" + functionName="{ permxFunc, permyFunc, permzFunc }" + scale="{ 9.869233e-16, 9.869233e-16, 9.869233e-16 }"/> ( referencedGroup, getName() ); +} + string Group::processInputName( xmlWrapper::xmlNode const & targetNode, xmlWrapper::xmlNodePos const & targetNodePos, string_view parentNodeName, diff --git a/src/coreComponents/dataRepository/Group.hpp b/src/coreComponents/dataRepository/Group.hpp index 54460ef9713..2823ce630f9 100644 --- a/src/coreComponents/dataRepository/Group.hpp +++ b/src/coreComponents/dataRepository/Group.hpp @@ -1343,6 +1343,12 @@ class Group DataContext const & getDataContext() const { return *m_dataContext; } + /** + * @brief Allow to define a different data context for the instance, typically when we want to show another + * group when an error occurs. + */ + void setDataContextReference( Group const & referencedGroup ); + /** * @return DataContext object that that stores contextual information on a wrapper contained by * this group that can be used in output messages. diff --git a/src/coreComponents/dataRepository/GroupContext.cpp b/src/coreComponents/dataRepository/GroupContext.cpp index 04e1fa1f2a3..c359531cdf1 100644 --- a/src/coreComponents/dataRepository/GroupContext.cpp +++ b/src/coreComponents/dataRepository/GroupContext.cpp @@ -25,11 +25,11 @@ namespace dataRepository { -GroupContext::GroupContext( Group & group, string_view objectName ): +GroupContext::GroupContext( Group const & group, string_view objectName ): DataContext( objectName ), m_group( group ) {} -GroupContext::GroupContext( Group & group ): +GroupContext::GroupContext( Group const & group ): GroupContext( group, group.getName() ) {} diff --git a/src/coreComponents/dataRepository/GroupContext.hpp b/src/coreComponents/dataRepository/GroupContext.hpp index abdc1199e7d..d46867289f6 100644 --- a/src/coreComponents/dataRepository/GroupContext.hpp +++ b/src/coreComponents/dataRepository/GroupContext.hpp @@ -43,7 +43,14 @@ class GroupContext : public DataContext * @brief Construct a new GroupContext object * @param group The reference to the Group related to this GroupContext. */ - GroupContext( Group & group ); + GroupContext( Group const & group ); + + /** + * @brief Construct a new GroupContext object + * @param group The reference to the Group related to this GroupContext. + * @param objectName Target object name. + */ + GroupContext( Group const & group, string_view objectName ); /** * @return the reference to the Group related to this GroupContext. @@ -52,15 +59,8 @@ class GroupContext : public DataContext protected: - /** - * @brief Construct a new GroupContext object - * @param group The reference to the Group related to this GroupContext. - * @param objectName Target object name. - */ - GroupContext( Group & group, string_view objectName ); - /// The reference to the Group related to this GroupContext. - Group & m_group; + Group const & m_group; private: diff --git a/src/coreComponents/fieldSpecification/AquiferBoundaryCondition.cpp b/src/coreComponents/fieldSpecification/AquiferBoundaryCondition.cpp index 6627fc299e8..518a8916be5 100644 --- a/src/coreComponents/fieldSpecification/AquiferBoundaryCondition.cpp +++ b/src/coreComponents/fieldSpecification/AquiferBoundaryCondition.cpp @@ -304,7 +304,7 @@ AquiferBoundaryCondition::KernelWrapper AquiferBoundaryCondition::createKernelWr pressureInfluenceFunction.createKernelWrapper() ); } -REGISTER_CATALOG_ENTRY( FieldSpecification, AquiferBoundaryCondition, string const &, Group * const ) +REGISTER_CATALOG_ENTRY( FieldSpecificationABC, AquiferBoundaryCondition, string const &, Group * const ) } /* namespace geos */ diff --git a/src/coreComponents/fieldSpecification/CMakeLists.txt b/src/coreComponents/fieldSpecification/CMakeLists.txt index 1ab2ae911b0..07c83728d5e 100644 --- a/src/coreComponents/fieldSpecification/CMakeLists.txt +++ b/src/coreComponents/fieldSpecification/CMakeLists.txt @@ -26,12 +26,15 @@ set( fieldSpecification_headers DirichletBoundaryCondition.hpp EquilibriumInitialCondition.hpp FieldSpecification.hpp + FieldSpecificationABC.hpp + FieldSpecificationFactory.hpp FieldSpecificationManager.hpp FieldSpecificationImpl.hpp SourceFluxBoundaryCondition.hpp TractionBoundaryCondition.hpp AquiferBoundaryCondition.hpp PerfectlyMatchedLayer.hpp + PermeabilitySpecification.hpp ) # @@ -41,11 +44,14 @@ set( fieldSpecification_sources DirichletBoundaryCondition.cpp EquilibriumInitialCondition.cpp FieldSpecification.cpp + FieldSpecificationABC.cpp + FieldSpecificationFactory.cpp FieldSpecificationManager.cpp SourceFluxBoundaryCondition.cpp TractionBoundaryCondition.cpp AquiferBoundaryCondition.cpp PerfectlyMatchedLayer.cpp + PermeabilitySpecification.cpp ) set( dependencyList ${parallelDeps} mesh ) diff --git a/src/coreComponents/fieldSpecification/DirichletBoundaryCondition.cpp b/src/coreComponents/fieldSpecification/DirichletBoundaryCondition.cpp index 31ce54725c0..0b4733e0fcf 100644 --- a/src/coreComponents/fieldSpecification/DirichletBoundaryCondition.cpp +++ b/src/coreComponents/fieldSpecification/DirichletBoundaryCondition.cpp @@ -37,6 +37,6 @@ DirichletBoundaryCondition::~DirichletBoundaryCondition() -REGISTER_CATALOG_ENTRY( FieldSpecification, DirichletBoundaryCondition, string const &, Group * const ) +REGISTER_CATALOG_ENTRY( FieldSpecificationABC, DirichletBoundaryCondition, string const &, Group * const ) } /* namespace geos */ diff --git a/src/coreComponents/fieldSpecification/EquilibriumInitialCondition.cpp b/src/coreComponents/fieldSpecification/EquilibriumInitialCondition.cpp index d92fe6a1893..27b40a7d460 100644 --- a/src/coreComponents/fieldSpecification/EquilibriumInitialCondition.cpp +++ b/src/coreComponents/fieldSpecification/EquilibriumInitialCondition.cpp @@ -245,7 +245,7 @@ void EquilibriumInitialCondition::initializePreSubGroups() } } -REGISTER_CATALOG_ENTRY( FieldSpecification, EquilibriumInitialCondition, string const &, Group * const ) +REGISTER_CATALOG_ENTRY( FieldSpecificationABC, EquilibriumInitialCondition, string const &, Group * const ) } /* namespace geos */ diff --git a/src/coreComponents/fieldSpecification/FieldSpecification.cpp b/src/coreComponents/fieldSpecification/FieldSpecification.cpp index 7923b0fcfb4..6893f994eab 100644 --- a/src/coreComponents/fieldSpecification/FieldSpecification.cpp +++ b/src/coreComponents/fieldSpecification/FieldSpecification.cpp @@ -20,18 +20,10 @@ namespace geos using namespace dataRepository; FieldSpecification::FieldSpecification( string const & name, Group * parent ): - Group( name, parent ) + FieldSpecificationABC( name, parent ) { setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); - registerWrapper( viewKeyStruct::setNamesString(), &m_setNames ). - setRTTypeName( rtTypes::CustomTypes::groupNameRefArray ). - setInputFlag( InputFlags::REQUIRED ). - setSizedFromParent( 0 ). - setDescription( "Names of sets that the boundary condition is applied to.\n" - "A set can contain heterogeneous elements in the mesh (volumes, nodes, faces, edges).\n" - "A set can be be defined by a 'Geometry' component, or correspond to imported sets in case of an external mesh" ); - registerWrapper( viewKeyStruct::objectPathString(), &m_objectPath ). setRTTypeName( rtTypes::CustomTypes::groupNameRef ). setInputFlag( InputFlags::OPTIONAL ). @@ -54,42 +46,6 @@ FieldSpecification::FieldSpecification( string const & name, Group * parent ): setInputFlag( InputFlags::OPTIONAL ). setDescription( "Direction to apply boundary condition to." ); - registerWrapper( viewKeyStruct::functionNamesString(), &m_functionNames ). - setRTTypeName( rtTypes::CustomTypes::groupNameRefArray ). - setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Name(s) of function(s) that specifies variation of the boundary condition." ); - - registerWrapper( viewKeyStruct::scalesString(), &m_scales ). - setApplyDefaultValue( 0.0 ). - setInputFlag( InputFlags::OPTIONAL ). - setSizedFromParent( 0 ). - setDescription( "Apply scaling factor(s) for the value(s) of the boundary condition." ); - - registerWrapper( viewKeyStruct::initialConditionString(), &m_initialCondition ). - setApplyDefaultValue( 0 ). - setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Boundary condition is applied as an initial condition." ); - - registerWrapper( viewKeyStruct::beginTimeString(), &m_beginTime ). - setApplyDefaultValue( -1.0e99 ). - setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Time at which the boundary condition will start being applied." ); - - registerWrapper( viewKeyStruct::endTimeString(), &m_endTime ). - setApplyDefaultValue( 1.0e99 ). - setInputFlag( InputFlags::OPTIONAL ). - setDescription( "Time at which the boundary condition will stop being applied." ); - - registerWrapper( viewKeyStruct::errorSetModeString(), &m_emptySetErrorMode ). - setInputFlag( InputFlags::OPTIONAL ). - setApplyDefaultValue( SetErrorMode::error ). - setDescription( GEOS_FMT( "Set the log state when a “set” does not target any region\n" - "When set to \"{}\", no output.\n" - "When set to \"{}\", output a warning.\n" - "When set to \"{}\", output a throw.\n", - EnumStrings< SetErrorMode >::toString( SetErrorMode::silent ), - EnumStrings< SetErrorMode >::toString( SetErrorMode::warning ), - EnumStrings< SetErrorMode >::toString( SetErrorMode::error ) )); } @@ -106,25 +62,14 @@ FieldSpecification::getCatalog() void FieldSpecification::postInputInitialization() { - { // both conditions work together - GEOS_THROW_IF( !m_functionNames.empty() && - m_functionNames.size() != 1 && - m_functionNames.size() != static_cast< string_array::size_type >( m_scales.size() ), - GEOS_FMT ( "Size mismatch: '{}' has {} entries but '{}' has {}. " - "'{}' either must be empty, have a single entry, or be sized exactly like '{}'", - viewKeyStruct::functionNamesString(), m_functionNames.size(), - viewKeyStruct::scalesString(), m_scales.size(), - viewKeyStruct::functionNamesString(), viewKeyStruct::scalesString() ), - InputError, - getDataContext() ); - - GEOS_THROW_IF( m_component != -1 && m_scales.size() > 1, - GEOS_FMT ( "'{}' must not be set when '{}' has more than one value.", - viewKeyStruct::componentString(), - viewKeyStruct::scalesString() ), - InputError, - getDataContext() ); - } + FieldSpecificationABC::postInputInitialization(); + + GEOS_THROW_IF( m_component != -1 && m_scales.size() > 1, + GEOS_FMT ( "'{}' must not be set when '{}' has more than one value.", + viewKeyStruct::componentString(), + viewKeyStruct::scalesString() ), + InputError, + getDataContext() ); } void FieldSpecification::validateNumArrayComp( localIndex numComp ) @@ -184,6 +129,6 @@ void FieldSpecification::setMeshObjectPath( Group const & meshBodies ) -REGISTER_CATALOG_ENTRY( FieldSpecification, FieldSpecification, string const &, Group * const ) +REGISTER_CATALOG_ENTRY( FieldSpecificationABC, FieldSpecification, string const &, Group * const ) } diff --git a/src/coreComponents/fieldSpecification/FieldSpecification.hpp b/src/coreComponents/fieldSpecification/FieldSpecification.hpp index 019f50d6145..c63d2ccb421 100644 --- a/src/coreComponents/fieldSpecification/FieldSpecification.hpp +++ b/src/coreComponents/fieldSpecification/FieldSpecification.hpp @@ -22,8 +22,7 @@ #include "common/DataTypes.hpp" -#include "common/format/EnumStrings.hpp" -#include "dataRepository/Group.hpp" +#include "fieldSpecification/FieldSpecificationABC.hpp" #include "mesh/MeshObjectPath.hpp" namespace geos @@ -35,7 +34,7 @@ class Function; * @class FieldSpecification * A class to hold values for and administer a single boundary condition */ -class FieldSpecification : public dataRepository::Group +class FieldSpecification : public FieldSpecificationABC { public: @@ -50,16 +49,6 @@ class FieldSpecification : public dataRepository::Group using CatalogInterface = dataRepository::CatalogInterface< FieldSpecification, string const &, dataRepository::Group * const >; - /** - * @enum SetErrorMode - * @brief Indicate the error handling mode. - */ - enum class SetErrorMode : integer - { - silent, - error, - warning - }; /** * @brief static function to return static catalog. @@ -113,10 +102,8 @@ class FieldSpecification : public dataRepository::Group /** * @brief View keys */ - struct viewKeyStruct + struct viewKeyStruct : public FieldSpecificationABC::viewKeyStruct { - /// @return The key for setName - constexpr static char const * setNamesString() { return "setNames"; } /// @return The key for constitutivePath constexpr static char const * constitutivePathString() { return "constitutivePath"; } /// @return The key for objectPath @@ -129,43 +116,8 @@ class FieldSpecification : public dataRepository::Group constexpr static char const * componentString() { return "component"; } /// @return The key for direction constexpr static char const * directionString() { return "direction"; } - /// @return The key for scale - constexpr static char const * scalesString() { return "scale"; } - /// @return The key for functionName - constexpr static char const * functionNamesString() { return "functionName"; } - /// @return The key for initialCondition - constexpr static char const * initialConditionString() { return "initialCondition"; } - /// @return The key for beginTime - constexpr static char const * beginTimeString() { return "beginTime"; } - /// @return The key for endTime - constexpr static char const * endTimeString() { return "endTime"; } - /// @return The key errorSetMode - constexpr static char const * errorSetModeString() { return "errorSetMode"; } }; - /** - * Accessor - * @return first entry of m_functionNames, or an empty string if empty - * - * @note Legacy scalar accessor. - * Use getFunctionNames() to access the full list of function names when using non-scalar - * field specifications (eg. functionName="{ f1, f2, f3 }") - */ - string const & getFunctionName() const - { - static string const emptyName; - return m_functionNames.empty() ? emptyName : m_functionNames.front(); - } - - /** - * Accessor - * @return const reference to m_functionNames - */ - string_array const & getFunctionNames() const - { - return m_functionNames; - } - /** * Accessor * @return const reference to m_objectPath @@ -177,7 +129,7 @@ class FieldSpecification : public dataRepository::Group * Accessor * @return const reference to m_fieldName */ - virtual const string & getFieldName() const + virtual const string & getFieldName() const override { return m_fieldName; } /** @@ -194,52 +146,6 @@ class FieldSpecification : public dataRepository::Group virtual R1Tensor const & getDirection() const { return m_direction; } - /** - * Accessor - * @return const m_beginTime - */ - real64 getStartTime() const - { return m_beginTime; } - - /** - * Accessor - * @return const m_endTime - */ - real64 getEndTime() const - { return m_endTime; } - - /** - * Accessor - * @return const reference to m_setNames - */ - string_array const & getSetNames() const - { return m_setNames; } - - /** - * Accessor - * @return const m_initialCondition - */ - int initialCondition() const - { return m_initialCondition; } - - /** - * Accessor - * @return first entry of m_scales, or 0 if m_scales is empty - * - * @note Legacy scalar accessor. - * Use getScales() to access the full list of scales when using non-scalar - * field specifications (eg. scales="{ 1, 2, 3 }") - */ - real64 getScale() const - { return m_scales.empty() ? 0.0 : m_scales.front(); } - - /** - * Accessor - * @return const m_scales - */ - arrayView1d< real64 const > getScales() const - { return m_scales.toViewConst(); } - /** * Mutator * @param[in] fieldName The name of the field @@ -249,51 +155,17 @@ class FieldSpecification : public dataRepository::Group /** * Mutator - * @param[in] objectPath The path for the object - */ - void setObjectPath( string const & objectPath ) - { m_objectPath = objectPath; } - - /** - * Mutator - * @param[in] scale Scaling factor - */ - void setScale( real64 const & scale ) - { - m_scales.resize( 1 ); - m_scales[ 0 ] = scale; - } - - /** - * Mutator - * @brief Set the per-component scale factors - * @param[in] scales The tensor-valued scale - */ - void setScales( array1d< real64 > const & scales ) - { m_scales = scales; } - - /** - * Mutator - * @brief Set the per-component function names - * @param[in] functionNames The per-component function names. Must either be empty, - * have a single entry, or be sized exactly as @p m_scales + * @param[in] component The component axis or a special value. */ - void setFunctionNames( string_array const & functionNames ) - { m_functionNames = functionNames; } + void setComponent( int component ) + { m_component = component; } /** * Mutator - * @param[in] isInitialCondition Logical value to indicate if it is an initial condition - */ - void initialCondition( bool isInitialCondition ) - { m_initialCondition = isInitialCondition; } - - /** - * Mutator - * @param[in] setName The name of the set + * @param[in] objectPath The path for the object */ - void addSetName( string const & setName ) - { m_setNames.emplace_back( setName ); } + void setObjectPath( string const & objectPath ) + { m_objectPath = objectPath; } /** * @brief Set the Mesh Object Path object @@ -333,9 +205,6 @@ class FieldSpecification : public dataRepository::Group private: - /// the names of the sets that the boundary condition is applied to - string_array m_setNames; - /// the path to the object which contains the fields that the boundary condition is applied to string m_objectPath; @@ -345,40 +214,13 @@ class FieldSpecification : public dataRepository::Group /// determining whether or not to apply the boundary condition. string m_fieldName; - /// The component the boundary condition acts on. Not used if field is a scalar. int m_component; /// The direction the boundary condition acts in. R1Tensor m_direction; - - /// Whether or not the boundary condition is an initial condition. - int m_initialCondition; - - /// Name(s) of the function used to generate values for application. - string_array m_functionNames; - - /// Scale factor(s) to use on the value of the boundary condition. - array1d< real64 > m_scales; - - /// Time after which the bc is allowed to be applied - real64 m_beginTime; - - /// Time after which the bc will no longer be applied. - real64 m_endTime; - - /// Enum containing the possible output modes when an error occur - SetErrorMode m_emptySetErrorMode; }; -/** - * @brief Indicate the error handling mode - */ -ENUM_STRINGS( FieldSpecification::SetErrorMode, - "silent", - "error", - "warning" ); - } #endif //GEOS_FIELDSPECIFICATION_FIELDSPECIFICATION_HPP diff --git a/src/coreComponents/fieldSpecification/FieldSpecificationABC.cpp b/src/coreComponents/fieldSpecification/FieldSpecificationABC.cpp new file mode 100644 index 00000000000..b7b747a9f5f --- /dev/null +++ b/src/coreComponents/fieldSpecification/FieldSpecificationABC.cpp @@ -0,0 +1,104 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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. + * ------------------------------------------------------------------------------------------------------------ + */ + +#include "FieldSpecificationABC.hpp" + +namespace geos +{ +using namespace dataRepository; + +FieldSpecificationABC::FieldSpecificationABC( string const & name, Group * parent ): + Group( name, parent ) +{ + registerWrapper( viewKeyStruct::setNamesString(), &m_setNames ). + setRTTypeName( rtTypes::CustomTypes::groupNameRefArray ). + setInputFlag( InputFlags::REQUIRED ). + setSizedFromParent( 0 ). + setDescription( "Names of sets that the boundary condition is applied to.\n" + "A set can contain heterogeneous elements in the mesh (volumes, nodes, faces, edges).\n" + "A set can be be defined by a 'Geometry' component, or correspond to imported sets in case of an external mesh" ); + + registerWrapper( viewKeyStruct::functionNamesString(), &m_functionNames ). + setRTTypeName( rtTypes::CustomTypes::groupNameRefArray ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Name(s) of function(s) that specifies variation of the boundary condition." ); + + registerWrapper( viewKeyStruct::scalesString(), &m_scales ). + setApplyDefaultValue( 0.0 ). + setInputFlag( InputFlags::OPTIONAL ). + setSizedFromParent( 0 ). + setDescription( "Apply scaling factor(s) for the value(s) of the boundary condition." ); + + registerWrapper( viewKeyStruct::initialConditionString(), &m_initialCondition ). + setApplyDefaultValue( 0 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Boundary condition is applied as an initial condition." ); + + registerWrapper( viewKeyStruct::beginTimeString(), &m_beginTime ). + setApplyDefaultValue( -1.0e99 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Time at which the boundary condition will start being applied." ); + + registerWrapper( viewKeyStruct::endTimeString(), &m_endTime ). + setApplyDefaultValue( 1.0e99 ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Time at which the boundary condition will stop being applied." ); + + registerWrapper( viewKeyStruct::errorSetModeString(), &m_emptySetErrorMode ). + setInputFlag( InputFlags::OPTIONAL ). + setApplyDefaultValue( SetErrorMode::error ). + setDescription( GEOS_FMT( "Set the log state when a “set” does not target any region\n" + "When set to \"{}\", no output.\n" + "When set to \"{}\", output a warning.\n" + "When set to \"{}\", output a throw.\n", + EnumStrings< SetErrorMode >::toString( SetErrorMode::silent ), + EnumStrings< SetErrorMode >::toString( SetErrorMode::warning ), + EnumStrings< SetErrorMode >::toString( SetErrorMode::error ) )); +} + +FieldSpecificationABC::~FieldSpecificationABC() +{} + +FieldSpecificationABC::CatalogInterface::CatalogType & +FieldSpecificationABC::getCatalog() +{ + static FieldSpecificationABC::CatalogInterface::CatalogType catalog; + return catalog; +} + + +void FieldSpecificationABC::postInputInitialization() +{ + GEOS_THROW_IF( !m_functionNames.empty() && + m_functionNames.size() != 1 && + m_functionNames.size() != static_cast< string_array::size_type >( m_scales.size() ), + GEOS_FMT ( "Size mismatch: '{}' has {} entries but '{}' has {}. " + "'{}' either must be empty, have a single entry, or be sized exactly like '{}'", + viewKeyStruct::functionNamesString(), m_functionNames.size(), + viewKeyStruct::scalesString(), m_scales.size(), + viewKeyStruct::functionNamesString(), viewKeyStruct::scalesString() ), + InputError, + getDataContext() ); + + GEOS_THROW_IF( m_beginTime > m_endTime, + GEOS_FMT( "{} ({}) must be less than {} ({}) in {} '{}'", + viewKeyStruct::beginTimeString(), m_beginTime, + viewKeyStruct::endTimeString(), m_endTime, + getCatalogName(), getName() ), + InputError, + getDataContext() ); +} + +} diff --git a/src/coreComponents/fieldSpecification/FieldSpecificationABC.hpp b/src/coreComponents/fieldSpecification/FieldSpecificationABC.hpp new file mode 100644 index 00000000000..cb73d44e2da --- /dev/null +++ b/src/coreComponents/fieldSpecification/FieldSpecificationABC.hpp @@ -0,0 +1,310 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file FieldSpecificationABC.hpp + */ + +#ifndef GEOS_FIELDSPECIFICATION_FIELDSPECIFICATIONABC_HPP +#define GEOS_FIELDSPECIFICATION_FIELDSPECIFICATIONABC_HPP + + +#include "common/DataTypes.hpp" +#include "common/format/EnumStrings.hpp" +#include "dataRepository/Group.hpp" +#include "functions/FunctionBase.hpp" +#include "functions/FunctionManager.hpp" + +namespace geos +{ +class Function; + + +/** + * @class FieldSpecificationABC + * + * Abstract Base Class for concrete field modifiers (`FieldSpecification`) and high-level user-defined field specification. + */ +class FieldSpecificationABC : public dataRepository::Group +{ +public: + + /** + * @defgroup alias and functions to define statically initialized catalog + * @{ + */ + + /** + * alias to define the catalog type for this abstract type + */ + using CatalogInterface = dataRepository::CatalogInterface< FieldSpecificationABC, + string const &, + dataRepository::Group * const >; + + /** + * @enum SetErrorMode + * @brief Indicate the error handling mode. + */ + enum class SetErrorMode : integer + { + silent, + error, + warning + }; + + /** + * @brief static function to return static catalog. + * @return the static catalog to create derived types through the static factory methods. + */ + static CatalogInterface::CatalogType & getCatalog(); + + /** + * @brief return the catalog name + * @return the catalog name + */ + virtual const string getCatalogName() const = 0; + + /** + * @} + */ + + + /** + * @brief constructor + * @param name the name of the FieldSpecificationABC in the data repository + * @param parent the parent group of this group. + */ + FieldSpecificationABC( string const & name, dataRepository::Group * parent ); + + /** + * destructor + */ + virtual ~FieldSpecificationABC() override; + + + /// Deleted copy constructor + FieldSpecificationABC( FieldSpecificationABC const & ) = delete; + + /// Defaulted move constructor + FieldSpecificationABC( FieldSpecificationABC && ) = default; + + /// deleted copy assignment + FieldSpecificationABC & operator=( FieldSpecificationABC const & ) = delete; + + /// deleted move assignement + FieldSpecificationABC & operator=( FieldSpecificationABC && ) = delete; + + /** + * @brief View keys + */ + struct viewKeyStruct + { + /// @return The key for setName + constexpr static char const * setNamesString() { return "setNames"; } + /// @return The key for scale + constexpr static char const * scalesString() { return "scale"; } + /// @return The key for functionName + constexpr static char const * functionNamesString() { return "functionName"; } + /// @return The key for initialCondition + constexpr static char const * initialConditionString() { return "initialCondition"; } + /// @return The key for beginTime + constexpr static char const * beginTimeString() { return "beginTime"; } + /// @return The key for endTime + constexpr static char const * endTimeString() { return "endTime"; } + /// @return The key errorSetMode + constexpr static char const * errorSetModeString() { return "errorSetMode"; } + }; + + /** + * Accessor + * @return const reference to the field name + */ + virtual const string & getFieldName() const = 0; + + /** + * Accessor + * @return const reference to m_setNames + */ + string_array const & getSetNames() const + { return m_setNames; } + + /** + * Accessor + * @return first entry of m_functionNames, or an empty string if empty + * + * @note Legacy scalar accessor. + * Use getFunctionNames() to access the full list of function names when using non-scalar + * field specifications (eg. functionName="{ f1, f2, f3 }") + */ + string const & getFunctionName() const + { + static string const emptyName; + return m_functionNames.empty() ? emptyName : m_functionNames.front(); + } + + /** + * Accessor + * @return const reference to m_functionNames + */ + string_array const & getFunctionNames() const + { + return m_functionNames; + } + + /** + * Accessor + * @return const m_beginTime + */ + real64 getStartTime() const + { return m_beginTime; } + + /** + * Accessor + * @return const m_endTime + */ + real64 getEndTime() const + { return m_endTime; } + + /** + * Accessor + * @return const m_initialCondition + */ + int initialCondition() const + { return m_initialCondition; } + + /** + * Accessor + * @return first entry of m_scales, or 0 if m_scales is empty + * + * @note Legacy scalar accessor. + * Use getScales() to access the full list of scales when using non-scalar + * field specifications (eg. scales="{ 1, 2, 3 }") + */ + real64 getScale() const + { return m_scales.empty() ? 0.0 : m_scales.front(); } + + /** + * Accessor + * @return const m_scales + */ + array1d< real64 > getScales() const + { return m_scales; } + + /** + * Accessor + * @return const m_emptySetErrorMode + */ + SetErrorMode getErrorSetMode() const + { return m_emptySetErrorMode; } + + /** + * Mutator + * @param[in] setName The name of the set + */ + void addSetName( string const & setName ) + { m_setNames.emplace_back( setName ); } + + /** + * Mutator + * @param[in] scale Scaling factor + */ + void setScale( real64 const & scale ) + { + m_scales.resize( 1 ); + m_scales[ 0 ] = scale; + } + + /** + * Mutator + * @brief Set the per-component scale factors + * @param[in] scales The tensor-valued scale + */ + void setScales( array1d< real64 > const & scales ) + { m_scales = scales; } + + /** + * Mutator + * @brief Set the per-component function names + * @param[in] functionNames The per-component function names. Must either be empty, + * have a single entry, or be sized exactly as @p m_scales + */ + void setFunctionNames( string_array const & functionNames ) + { m_functionNames = functionNames; } + + /** + * Mutator + * @param[in] isInitialCondition Logical value to indicate if it is an initial condition + */ + void initialCondition( bool isInitialCondition ) + { m_initialCondition = isInitialCondition; } + + /** + * Mutator + * @param[in] beginTime Time after which the bc is allowed to be applied + */ + void setStartTime( real64 beginTime ) + { m_beginTime = beginTime; } + + /** + * Mutator + * @param[in] endTime Time after which the bc will no longer be applied. + */ + void setEndTime( real64 endTime ) + { m_endTime = endTime; } + + /** + * Mutator + * @param[in] errorSetMode Time after which the bc will no longer be applied. + */ + void setErrorSetMode( SetErrorMode const & errorSetMode ) + { m_emptySetErrorMode = errorSetMode; } + +protected: + + virtual void postInputInitialization() override; + + /// the names of the sets that the boundary condition is applied to + string_array m_setNames; + + /// Whether or not the boundary condition is an initial condition. + int m_initialCondition; + + /// Name(s) of the function used to generate values for application. + string_array m_functionNames; + + /// Scale factor(s) to use on the value of the boundary condition. + array1d< real64 > m_scales; + + /// Time after which the bc is allowed to be applied + real64 m_beginTime; + + /// Time after which the bc will no longer be applied. + real64 m_endTime; + + /// Enum containing the possible output modes when an error occur + SetErrorMode m_emptySetErrorMode; +}; + +/** + * @brief Indicate the error handling mode + */ +ENUM_STRINGS( FieldSpecificationABC::SetErrorMode, + "silent", + "error", + "warning" ); + +} + +#endif //GEOS_FIELDSPECIFICATION_FIELDSPECIFICATIONABC_HPP diff --git a/src/coreComponents/fieldSpecification/FieldSpecificationFactory.cpp b/src/coreComponents/fieldSpecification/FieldSpecificationFactory.cpp new file mode 100644 index 00000000000..1599cc67a82 --- /dev/null +++ b/src/coreComponents/fieldSpecification/FieldSpecificationFactory.cpp @@ -0,0 +1,29 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file FieldSpecificationFactory.cpp + */ + +#include "FieldSpecificationFactory.hpp" + +namespace geos +{ + +using Registry = FieldSpecificationProcessorRegistry; + +stdMap< string, Registry::ProcessorBase const * > Registry::s_processors; + +} // namespace geos diff --git a/src/coreComponents/fieldSpecification/FieldSpecificationFactory.hpp b/src/coreComponents/fieldSpecification/FieldSpecificationFactory.hpp new file mode 100644 index 00000000000..09c31d5d88b --- /dev/null +++ b/src/coreComponents/fieldSpecification/FieldSpecificationFactory.hpp @@ -0,0 +1,113 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file FieldSpecificationFactory.hpp + */ + +#ifndef GEOS_FIELDSPECIFICATION_FIELDSPECIFICATIONFACTORY_HPP +#define GEOS_FIELDSPECIFICATION_FIELDSPECIFICATIONFACTORY_HPP + + +#include "common/TypeDispatch.hpp" +#include "dataRepository/Group.hpp" +#include "FieldSpecificationABC.hpp" + +namespace geos +{ + +/** + * @brief Generate FieldSpecifications based on the given "higher-level" specification + * @tparam SPEC_TYPE The type of the high-level specification + * @param fs The high-level specification used as a blueprint to create FieldSpecification + * @param manager The parent to store the created FieldSpecifications + */ +template< typename SPEC_TYPE > +void expandFieldSpecification( SPEC_TYPE const & fs, + dataRepository::Group & manager ) = delete; + +class FieldSpecificationProcessorRegistry +{ +public: + + /** + * @brief Base Processor class for transforming "high-level" specification + * into FieldSpecification(s) + */ + class ProcessorBase + { +public: + /** + * @brief Generate FieldSpecifications based on the given "higher-level" specification + * @tparam SPEC_TYPE The type of the high-level specification + * @param fs The high-level specification used as a blueprint to create FieldSpecification + * @param manager The parent to store the created FieldSpecifications + */ + virtual void expandFieldSpecification( FieldSpecificationABC const & fs, + dataRepository::Group & GEOS_UNUSED_PARAM( manager ) ) const + { GEOS_ERROR( GEOS_FMT( "Processor not implemented for field specification of type '{}'.", fs.getCatalogName() ), fs.getDataContext() ); } +protected: + ProcessorBase() {} + }; + + /** + * @brief Class for a specific "high-level" specification type to process its objects + * into one or multiple equivalent FieldSpecification + */ + template< typename SPEC_TYPE > + class Processor final : public ProcessorBase + { +public: + + /** + * @brief Add the processors to the static list. Called before main() when a + * REGISTER_FIELD_SPECIFICATION_PROCESSOR( SPEC_TYPE ) is in a cpp. + */ + Processor(): ProcessorBase() + { s_processors.emplace( SPEC_TYPE::catalogName(), this ); } + + /** + * @copydoc ProcessorBase::expandFieldSpecification + */ + void expandFieldSpecification( FieldSpecificationABC const & fs, + dataRepository::Group & manager ) const override + { geos::expandFieldSpecification( dynamic_cast< SPEC_TYPE const & >( fs ), manager ); } + }; + + /** + * @return the list of field specification processors. + */ + static stdMap< string, ProcessorBase const * > const & getProcessors() + { return s_processors; } + + +private: + + /** + * @brief Storage of field specification processors. + */ + static stdMap< string, ProcessorBase const * > s_processors; + +}; + +#define REGISTER_FIELD_SPECIFICATION_PROCESSOR( SPEC_TYPE ) \ + namespace { \ + GEOS_MAYBE_UNUSED FieldSpecificationProcessorRegistry::Processor< SPEC_TYPE > g_processorOf ## SPEC_TYPE; \ + } + +} // namespace geos + + +#endif //GEOS_FIELDSPECIFICATION_FIELDSPECIFICATIONFACTORY_HPP diff --git a/src/coreComponents/fieldSpecification/FieldSpecificationImpl.hpp b/src/coreComponents/fieldSpecification/FieldSpecificationImpl.hpp index 1385a606f82..7f25aaa5916 100644 --- a/src/coreComponents/fieldSpecification/FieldSpecificationImpl.hpp +++ b/src/coreComponents/fieldSpecification/FieldSpecificationImpl.hpp @@ -30,6 +30,7 @@ #include "mesh/MeshObjectPath.hpp" #include "functions/FunctionManager.hpp" #include "common/GEOS_RAJA_Interface.hpp" +#include "FieldSpecificationABC.hpp" namespace geos { diff --git a/src/coreComponents/fieldSpecification/FieldSpecificationManager.cpp b/src/coreComponents/fieldSpecification/FieldSpecificationManager.cpp index ea42c65bda0..888478ec1ff 100644 --- a/src/coreComponents/fieldSpecification/FieldSpecificationManager.cpp +++ b/src/coreComponents/fieldSpecification/FieldSpecificationManager.cpp @@ -14,6 +14,8 @@ */ #include "FieldSpecificationManager.hpp" +#include "FieldSpecificationABC.hpp" +#include "FieldSpecificationFactory.hpp" #include "mesh/DomainPartition.hpp" #include "mesh/MeshBody.hpp" #include "mesh/MeshObjectPath.hpp" @@ -33,7 +35,6 @@ FieldSpecificationManager::FieldSpecificationManager( string const & name, Group GEOS_ERROR_IF( m_instance != nullptr, "Only one FieldSpecificationManager can exist at a time." ); m_instance = this; - } FieldSpecificationManager::~FieldSpecificationManager() @@ -53,8 +54,8 @@ FieldSpecificationManager & FieldSpecificationManager::getInstance() Group * FieldSpecificationManager::createChild( string const & childKey, string const & childName ) { GEOS_LOG_RANK_0( GEOS_FMT( "{}: adding {} {}", getName(), childKey, childName ) ); - std::unique_ptr< FieldSpecification > bc = - FieldSpecification::CatalogInterface::factory( childKey, getDataContext(), childName, this ); + std::unique_ptr< FieldSpecificationABC > bc = + FieldSpecificationABC::CatalogInterface::factory( childKey, getDataContext(), childName, this ); return &this->registerGroup( childName, std::move( bc ) ); } @@ -62,12 +63,35 @@ Group * FieldSpecificationManager::createChild( string const & childKey, string void FieldSpecificationManager::expandObjectCatalogs() { // During schema generation, register one of each type derived from BoundaryConditionBase here - for( auto & catalogIter: FieldSpecification::getCatalog()) + for( auto & catalogIter: FieldSpecificationABC::getCatalog()) { createChild( catalogIter.first, catalogIter.first ); } } +void FieldSpecificationManager::postInputInitialization() +{ + using ProcessorRegistry = FieldSpecificationProcessorRegistry; + + // as the subgroup list can change during expansion, we need an immutable list + stdVector< FieldSpecificationABC const * > fieldSpecifications; + this->forSubGroups< FieldSpecificationABC >( [&] ( FieldSpecificationABC const & fs ) + { + fieldSpecifications.push_back( &fs ); + } ); + + auto const & processors = ProcessorRegistry::getProcessors(); + for( FieldSpecificationABC const * fs : fieldSpecifications ) + { + auto it = processors.find( fs->getCatalogName()); + if( it != processors.end()) + { + ProcessorRegistry::ProcessorBase const & processor = *it->second; + processor.expandFieldSpecification( *fs, *this ); + } + } +} + void FieldSpecificationManager::validateBoundaryConditions( MeshLevel & mesh ) { DomainPartition const & domain = this->getGroupByPath< DomainPartition >( "/Problem/domain" ); diff --git a/src/coreComponents/fieldSpecification/FieldSpecificationManager.hpp b/src/coreComponents/fieldSpecification/FieldSpecificationManager.hpp index 6526f48178b..d5f931d0e56 100644 --- a/src/coreComponents/fieldSpecification/FieldSpecificationManager.hpp +++ b/src/coreComponents/fieldSpecification/FieldSpecificationManager.hpp @@ -238,8 +238,16 @@ class FieldSpecificationManager : public dataRepository::Group m_isSurfaceGenerationCase = isSurfaceGenerationCase; } + +protected: + + virtual void postInputInitialization() override; + + private: + static FieldSpecificationManager * m_instance; + /// Indicate if the SurfaceGenerator element is present bool m_isSurfaceGenerationCase = false; diff --git a/src/coreComponents/fieldSpecification/PerfectlyMatchedLayer.cpp b/src/coreComponents/fieldSpecification/PerfectlyMatchedLayer.cpp index 765db5cb976..a1a99990d94 100644 --- a/src/coreComponents/fieldSpecification/PerfectlyMatchedLayer.cpp +++ b/src/coreComponents/fieldSpecification/PerfectlyMatchedLayer.cpp @@ -123,6 +123,6 @@ void PerfectlyMatchedLayer::postInputInitialization() } -REGISTER_CATALOG_ENTRY( FieldSpecification, PerfectlyMatchedLayer, string const &, Group * const ) +REGISTER_CATALOG_ENTRY( FieldSpecificationABC, PerfectlyMatchedLayer, string const &, Group * const ) } /* namespace geos */ diff --git a/src/coreComponents/fieldSpecification/PermeabilitySpecification.cpp b/src/coreComponents/fieldSpecification/PermeabilitySpecification.cpp new file mode 100644 index 00000000000..4f407312ec3 --- /dev/null +++ b/src/coreComponents/fieldSpecification/PermeabilitySpecification.cpp @@ -0,0 +1,182 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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. + * ------------------------------------------------------------------------------------------------------------ + */ + +#include "PermeabilitySpecification.hpp" +#include "FieldSpecification.hpp" +#include "FieldSpecificationFactory.hpp" +#include "common/logger/Logger.hpp" +#include "constitutive/permeability/PermeabilityBase.hpp" +#include "mesh/DomainPartition.hpp" + +namespace geos +{ +using namespace dataRepository; +using namespace constitutive; + +PermeabilitySpecification::PermeabilitySpecification( string const & name, Group * parent ): + FieldSpecificationABC( name, parent ) +{ + setInputFlags( InputFlags::OPTIONAL_NONUNIQUE ); + + registerWrapper( viewKeyStruct::regionNamesString(), &m_regionNames ). + setRTTypeName( rtTypes::CustomTypes::groupNameRefArray ). + setInputFlag( InputFlags::REQUIRED ). + setDescription( "Names of the cell regions that boundary condition is applied to." ); + + registerWrapper( viewKeyStruct::permeabilityModelNameString(), &m_permeabilityModelName ). + setRTTypeName( rtTypes::CustomTypes::groupNameRef ). + setInputFlag( InputFlags::OPTIONAL ). + setDescription( "Name of the constitutive permeability model." ); + + getWrapper< int >( viewKeyStruct::initialConditionString() ). + setApplyDefaultValue( 1 ); +} + + +PermeabilitySpecification::~PermeabilitySpecification() +{} + + +void PermeabilitySpecification::postInputInitialization() +{ + FieldSpecificationABC::postInputInitialization(); + + for( real64 scale : getScales() ) + { + GEOS_THROW_IF( scale < 0, + GEOS_FMT( "Scale values for a permeability must be non-negative\nA value of {} was given in {} '{}'.", + scale, catalogName(), getName() ), + InputError, + getDataContext() ); + } +} + +namespace +{ + +/** + * @brief @return The element region named @p regionName in the domain if it exists, or a nullptr + */ +ElementRegionBase const * findElementRegion( DomainPartition const & domain, + string const & regionName ) +{ + ElementRegionBase const * region = nullptr; + domain.forMeshBodies( [&]( MeshBody const & meshBody ) + { + meshBody.forMeshLevels( [&]( MeshLevel const & meshLevel ) + { + if( region == nullptr && meshLevel.getElemManager().hasRegion( regionName ) ) + { + region = &meshLevel.getElemManager().getRegion( regionName ); + } + } ); + } ); + return region; +} + +/** + * @brief Validate that a region with the name @p regionName exists and is a valid cell region + * @param domain The domain + * @param regionName The name of the region to validated + * @param permSpec Reference to the current object to print its DataContext + * @note Throws if the region doesn't exists or isn't a cell region + */ +void expectValidCellRegion( DomainPartition const & domain, + string const & fullRegionName, + PermeabilitySpecification const & permSpec ) +{ + // get only region from "region/subregion" + string const regionName = fullRegionName.substr( 0, fullRegionName.find( '/' ) ); + + ElementRegionBase const * const region = findElementRegion( domain, regionName ); + + GEOS_THROW_IF( region == nullptr, + GEOS_FMT( "Region '{}' does not exist.", regionName ), + InputError, + permSpec.getDataContext() ); + + GEOS_THROW_IF( dynamic_cast< CellElementRegion const * >( region ) == nullptr, + GEOS_FMT( "Region '{}' is a '{}', but must be a '{}'", + regionName, + region->getCatalogName(), + CellElementRegion::catalogName() ), + InputError, + permSpec.getDataContext() ); +} + +/** + * @brief Validate that a certain permeability model name exists in the domain + * @param domain The domain + * @param permeabilityModelName The name of the permeability model to validated + * @param permSpec Reference to the current object to print its DataContext + * @note Throws if the permeability model doesn't exists + */ +void expectValidPermeabilityModel( DomainPartition const & domain, + string const & modelName, + PermeabilitySpecification const & permSpec ) +{ + ConstitutiveManager const & constitutiveManager = domain.getConstitutiveManager(); + + GEOS_THROW_IF( constitutiveManager.getGroupPointer< PermeabilityBase >( modelName ) == nullptr, + GEOS_FMT( "{} '{}' doesn't name a valid permeability constitutive model", + PermeabilitySpecification::viewKeyStruct::permeabilityModelNameString(), + modelName ), + InputError, + permSpec.getDataContext() ); +} + +} + + +template<> +void expandFieldSpecification< PermeabilitySpecification >( PermeabilitySpecification const & permSpec, + dataRepository::Group & manager ) +{ + Group const & problem = manager.getParent(); + DomainPartition const & domain = problem.getGroup< DomainPartition >( "domain" ); + + expectValidPermeabilityModel( domain, permSpec.getPermeabilityModelName(), permSpec ); + string const fieldName = ConstitutiveBase::makeFieldName( permSpec.getPermeabilityModelName(), + permSpec.getFieldName() ); + + for( string const & regionName : permSpec.getRegionNames() ) + { + expectValidCellRegion( domain, regionName, permSpec ); + + string const objectPath = "ElementRegions/" + regionName; + string const childName = permSpec.getName() + "_" + regionName; + + FieldSpecification & fs = manager.registerGroup< FieldSpecification >( childName ); + fs.setDataContextReference( permSpec ); + fs.setFieldName( fieldName ); + fs.setObjectPath( objectPath ); + fs.initialCondition( permSpec.initialCondition() ); + fs.setScales( permSpec.getScales() ); + fs.setFunctionNames( permSpec.getFunctionNames() ); + fs.setStartTime( permSpec.getStartTime() ); + fs.setEndTime( permSpec.getEndTime() ); + fs.setErrorSetMode( permSpec.getErrorSetMode() ); + + for( auto const & setName : permSpec.getSetNames() ) + { + fs.addSetName( setName ); + } + } +} + +REGISTER_CATALOG_ENTRY( FieldSpecificationABC, PermeabilitySpecification, string const &, Group * const ) +REGISTER_FIELD_SPECIFICATION_PROCESSOR( PermeabilitySpecification ) + +} diff --git a/src/coreComponents/fieldSpecification/PermeabilitySpecification.hpp b/src/coreComponents/fieldSpecification/PermeabilitySpecification.hpp new file mode 100644 index 00000000000..5583e2dcac1 --- /dev/null +++ b/src/coreComponents/fieldSpecification/PermeabilitySpecification.hpp @@ -0,0 +1,132 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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. + * ------------------------------------------------------------------------------------------------------------ + */ + +/** + * @file PermeabilitySpecification.hpp + */ + +#ifndef GEOS_FIELDSPECIFICATION_PERMEABILITYSPECIFICATION_HPP +#define GEOS_FIELDSPECIFICATION_PERMEABILITYSPECIFICATION_HPP + + +#include "common/DataTypes.hpp" +#include "constitutive/permeability/PermeabilityFields.hpp" +#include "mesh/ObjectManagerBase.hpp" +#include "mesh/MeshObjectPath.hpp" +#include "FieldSpecification.hpp" +#include "FieldSpecificationABC.hpp" +#include "FieldSpecificationFactory.hpp" + +namespace geos +{ + +/** + * @class PermeabilitySpecification + * + * Data class representing a permeability field specification + * + * @todo Currently the PermeabilitySpecification only supports cells. + * A future goal is to support faces too. + */ +class PermeabilitySpecification : public FieldSpecificationABC +{ +public: + + /** + * @brief Static Factory Catalog Functions + * @return the catalog name + */ + static string catalogName() { return "PermeabilitySpecification"; } + + /** + * @brief return the catalog name + * @return the catalog name + */ + virtual const string getCatalogName() const + { + return PermeabilitySpecification::catalogName(); + } + + + /** + * @brief constructor + * @param name the name of the PermeabilitySpecification in the data repository + * @param parent the parent group of this group. + */ + PermeabilitySpecification( string const & name, dataRepository::Group * parent ); + + /** + * destructor + */ + virtual ~PermeabilitySpecification() override; + + + /** + * @brief View keys + */ + struct viewKeyStruct : public FieldSpecificationABC::viewKeyStruct + { + /// @return The key for regionNames + constexpr static char const * regionNamesString() { return "regionNames"; } + /// @return The key for permeabilityModelName + constexpr static char const * permeabilityModelNameString() { return "permeabilityModelName"; } + }; + + /** + * Accessor + * @return const reference to m_regionNames + */ + string_array const & getRegionNames() const + { return m_regionNames; } + + /** + * Accessor + * @return const reference to m_permeabilityModelName + */ + string const & getPermeabilityModelName() const + { return m_permeabilityModelName; } + + /** + * Accessor + * @return const reference to m_fieldName + */ + virtual string const & getFieldName() const override + { + static string const fieldName = fields::permeability::permeability::key(); + return fieldName; + } + +protected: + + virtual void postInputInitialization() override; + +private: + + /// the names of the regions that the boundary condition is applied to + string_array m_regionNames; + + /// the name of the constitutive permeability model + string m_permeabilityModelName; + +}; + +/// @copydoc geos::FieldSpecificationFactory::generateFieldSpecifications +template<> +void expandFieldSpecification< PermeabilitySpecification >( PermeabilitySpecification const & fs, + dataRepository::Group & manager ); + +} + +#endif //GEOS_FIELDSPECIFICATION_PERMEABILITYSPECIFICATION_HPP diff --git a/src/coreComponents/fieldSpecification/SourceFluxBoundaryCondition.cpp b/src/coreComponents/fieldSpecification/SourceFluxBoundaryCondition.cpp index a702cd9bb9c..276960063b1 100644 --- a/src/coreComponents/fieldSpecification/SourceFluxBoundaryCondition.cpp +++ b/src/coreComponents/fieldSpecification/SourceFluxBoundaryCondition.cpp @@ -43,6 +43,6 @@ SourceFluxBoundaryCondition::SourceFluxBoundaryCondition( string const & name, G FieldSpecification::viewKeyStruct::functionNamesString() ) ); } -REGISTER_CATALOG_ENTRY( FieldSpecification, SourceFluxBoundaryCondition, string const &, Group * const ) +REGISTER_CATALOG_ENTRY( FieldSpecificationABC, SourceFluxBoundaryCondition, string const &, Group * const ) } /* namespace geos */ diff --git a/src/coreComponents/fieldSpecification/TractionBoundaryCondition.cpp b/src/coreComponents/fieldSpecification/TractionBoundaryCondition.cpp index 30375f025c5..cfbe0f2c019 100644 --- a/src/coreComponents/fieldSpecification/TractionBoundaryCondition.cpp +++ b/src/coreComponents/fieldSpecification/TractionBoundaryCondition.cpp @@ -440,7 +440,7 @@ void TractionBoundaryCondition::reinitScaleSet( FaceManager const & faceManager, } ); } -REGISTER_CATALOG_ENTRY( FieldSpecification, TractionBoundaryCondition, string const &, Group * const ) +REGISTER_CATALOG_ENTRY( FieldSpecificationABC, TractionBoundaryCondition, string const &, Group * const ) } /* namespace geos */ diff --git a/src/coreComponents/fieldSpecification/docs/FieldSpecification.rst b/src/coreComponents/fieldSpecification/docs/FieldSpecification.rst index 7752ed84863..b52ebee4449 100644 --- a/src/coreComponents/fieldSpecification/docs/FieldSpecification.rst +++ b/src/coreComponents/fieldSpecification/docs/FieldSpecification.rst @@ -9,4 +9,6 @@ Initial and Boundary Conditions EquilibriumInitialCondition AquiferBoundaryCondition + + PermeabilitySpecification diff --git a/src/coreComponents/fieldSpecification/docs/PermeabilitySpecification.rst b/src/coreComponents/fieldSpecification/docs/PermeabilitySpecification.rst new file mode 100644 index 00000000000..1a5c370d17f --- /dev/null +++ b/src/coreComponents/fieldSpecification/docs/PermeabilitySpecification.rst @@ -0,0 +1,49 @@ +.. _PermeabilitySpecification: + +#################################################### +Permeability specification +#################################################### + +Overview +====================== + +**PermeabilitySpecification** is a XML tag that you can place in the **FieldSpecifications** block to describe a 3-axis permeability on one or more element regions. +It is an higher-level component than **FieldSpecification**, specialized on permeability. +After the input deck is read, GEOS expands each **PermeabilitySpecification** into several **FieldSpecification** objects (one per region and per axis) that the rest of the code already understands. + +This is convenient when you want the same **setNames**, **fieldName**, and other attributes for all three permeability components, instead of repeating three nearly identical **FieldSpecification**. + +Examples +=============== + +The following illustrates a **PermeabilitySpecification** for a single region. + +.. code-block:: xml + + + ... + + ... + + +The following illustrates a **PermeabilitySpecification** for multiple regions. + +.. code-block:: xml + + + ... + + ... + diff --git a/src/coreComponents/fieldSpecification/unitTests/CMakeLists.txt b/src/coreComponents/fieldSpecification/unitTests/CMakeLists.txt index 5a0f5dc9ad7..ea8999ddc4a 100644 --- a/src/coreComponents/fieldSpecification/unitTests/CMakeLists.txt +++ b/src/coreComponents/fieldSpecification/unitTests/CMakeLists.txt @@ -1,6 +1,7 @@ # Specify list of tests set( gtest_geosx_tests - testFieldSpecificationsEnums.cpp ) + testFieldSpecificationsEnums.cpp + testPermeabilitySpecification.cpp ) # Basic dependency list: fieldSpecification library plus gtest and parallel deps set( dependencyList ${parallelDeps} fieldSpecification gtest ) diff --git a/src/coreComponents/fieldSpecification/unitTests/testPermeabilitySpecification.cpp b/src/coreComponents/fieldSpecification/unitTests/testPermeabilitySpecification.cpp new file mode 100644 index 00000000000..1234972c8a6 --- /dev/null +++ b/src/coreComponents/fieldSpecification/unitTests/testPermeabilitySpecification.cpp @@ -0,0 +1,77 @@ +/* + * ------------------------------------------------------------------------------------------------------------ + * 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. + * ------------------------------------------------------------------------------------------------------------ + */ + +#include "fieldSpecification/PermeabilitySpecification.hpp" +#include "fieldSpecification/FieldSpecificationManager.hpp" + +#include +#include + +using namespace geos; +using namespace dataRepository; + +namespace +{ + +void fillValidInput( PermeabilitySpecification & ps ) +{ + ps.getReference< string_array >( PermeabilitySpecification::viewKeyStruct::setNamesString() ) = { "all" }; + ps.getReference< string_array >( PermeabilitySpecification::viewKeyStruct::regionNamesString() ) = { "region1", "region2" }; + ps.getReference< string >( PermeabilitySpecification::viewKeyStruct::permeabilityModelNameString() ) = "rockPerm"; + array1d< real64 > & scales = ps.getReference< array1d< real64 > >( PermeabilitySpecification::viewKeyStruct::scalesString() ); + scales.resize( 3 ); + scales[0] = 9.869233e-16; + scales[1] = 9.869233e-16; + scales[2] = 9.869233e-16; +} + +} + +TEST( PermeabilitySpecificationTest, ExpansionPropagatesAttributes ) +{ + using ProcessorRegistry = FieldSpecificationProcessorRegistry; + + conduit::Node node; + Group root( "root", node ); + + FieldSpecificationManager manager( "FieldSpecifications", &root ); + root.registerGroup( manager.getName(), &manager ); + + PermeabilitySpecification permSpec( "permSpec", &manager ); + manager.registerGroup( permSpec.getName(), &permSpec ); + + + fillValidInput( permSpec ); + + // verify that the permeability specification processor exists + EXPECT_NE( ProcessorRegistry::getProcessors().find( PermeabilitySpecification::catalogName() ), + ProcessorRegistry::getProcessors().end() ) << GEOS_FMT( "Processor of {} does not exist", PermeabilitySpecification::catalogName() ); + + // indirectly call postInputInitialization() -> expandFieldSpecification() on permSpec + EXPECT_NO_THROW( root.postInputInitializationRecursive() ); + + FieldSpecification const * generatedFS = manager.getGroupPointer< FieldSpecification >( "permSpec_region2" ); + // verify that the generated ("expanded") field specification exists + EXPECT_NE( generatedFS, nullptr ) << "Field specification has not been generated."; + // verify that the scale values have been well applied on the generated ("expanded") field specification + EXPECT_DOUBLE_EQ( generatedFS->getScales()[0], 9.869233e-16 ); +} + + +int main( int argc, char * * argv ) +{ + ::testing::InitGoogleTest( &argc, argv ); + return RUN_ALL_TESTS(); +} diff --git a/src/coreComponents/schema/schema.xsd b/src/coreComponents/schema/schema.xsd index b6afd299b03..6321175f0cd 100644 --- a/src/coreComponents/schema/schema.xsd +++ b/src/coreComponents/schema/schema.xsd @@ -212,6 +212,10 @@ + + + + @@ -1307,6 +1311,7 @@ Information output from lower logLevels is added with the desired log level + @@ -1354,7 +1359,7 @@ When set to "silent", no output. When set to "warning", output a warning. When set to "error", output a throw. --> - + @@ -1371,7 +1376,7 @@ A set can be be defined by a 'Geometry' component, or correspond to imported set - + @@ -1391,7 +1396,7 @@ When set to "silent", no output. When set to "warning", output a warning. When set to "error", output a throw. --> - + @@ -1425,7 +1430,7 @@ When set to "silent", no output. When set to "warning", output a warning. When set to "error", output a throw. --> - + @@ -1468,7 +1473,7 @@ When set to "silent", no output. When set to "warning", output a warning. When set to "error", output a throw. --> - + @@ -1501,7 +1506,7 @@ When set to "silent", no output. When set to "warning", output a warning. When set to "error", output a throw. --> - + @@ -1529,6 +1534,34 @@ A set can be be defined by a 'Geometry' component, or correspond to imported set + + + + + + + + + + + + + + + + + + + + + + @@ -1544,7 +1577,7 @@ When set to "silent", no output. When set to "warning", output a warning. When set to "error", output a throw. --> - + @@ -1572,7 +1605,7 @@ When set to "silent", no output. When set to "warning", output a warning. When set to "error", output a throw. --> - + diff --git a/src/coreComponents/schema/schema.xsd.other b/src/coreComponents/schema/schema.xsd.other index 3c77f11a779..f63803784d3 100644 --- a/src/coreComponents/schema/schema.xsd.other +++ b/src/coreComponents/schema/schema.xsd.other @@ -297,6 +297,7 @@ + @@ -336,6 +337,7 @@ A field can represent a physical variable. (pressure, temperature, global compos +