diff --git a/inputFiles/singlePhaseFlow/sourceFlux_2d.xml b/inputFiles/singlePhaseFlow/sourceFlux_2d.xml
index 4a0d5cf3a28..991417ed719 100644
--- a/inputFiles/singlePhaseFlow/sourceFlux_2d.xml
+++ b/inputFiles/singlePhaseFlow/sourceFlux_2d.xml
@@ -145,31 +145,12 @@
scale="5e6"/>
-
-
-
-
+ scales="{ 2.0e-22, 2.0e-22, 2.0e-22 }"/>
( m_scales.size() ),
+ GEOS_FMT ( "Size mismatch: '{}' has {} entries but '{}' has {}. "
+ "Either leave '{}' empty or size it exactly like '{}'",
+ viewKeyStruct::functionNamesString(), m_functionNames.size(),
+ viewKeyStruct::scalesString(), m_scales.size(),
+ viewKeyStruct::functionNamesString(), viewKeyStruct::scalesString() ),
+ InputError,
+ getDataContext() );
+
+ if( usesNonScalarValues() )
+ {
+ GEOS_THROW_IF( m_component != -1,
+ GEOS_FMT ( "'{}' must not be set when '{}' is set.",
+ viewKeyStruct::componentString(),
+ viewKeyStruct::scalesString() ),
+ InputError,
+ getDataContext() );
+
+ GEOS_THROW_IF( !m_functionName.empty(),
+ GEOS_FMT ( "'{}' must not be set when '{}' is set."
+ "Use '{}' to provide one function per component instead",
+ viewKeyStruct::functionNameString(),
+ viewKeyStruct::scalesString(),
+ viewKeyStruct::functionNamesString() ),
+ InputError,
+ getDataContext() );
+ }
+
+ if( !m_regionNames.empty() )
+ {
+ GEOS_THROW_IF( !m_objectPath.empty(),
+ GEOS_FMT ( "'{}' must not be set when '{}' is set.",
+ viewKeyStruct::objectPathString(),
+ viewKeyStruct::regionNamesString() ),
+ InputError,
+ getDataContext() );
+ }
+}
void FieldSpecificationBase::setMeshObjectPath( Group const & meshBodies )
{
+ string const path = m_regionNames.empty()
+ ? m_objectPath
+ : "ElementRegions/{" + stringutilities::join( m_regionNames, ' ' ) + "}";
try
{
- m_meshObjectPaths = std::make_unique< MeshObjectPath >( m_objectPath, meshBodies );
+ m_meshObjectPaths = std::make_unique< MeshObjectPath >( path, meshBodies );
}
catch( std::exception const & e )
{
ErrorLogger::global().modifyCurrentExceptionMessage()
.addToMsg( getWrapperDataContext( viewKeyStruct::objectPathString() ).toString() +
- " is a wrong objectPath: " + m_objectPath + "\n" )
+ " is a wrong objectPath: " + path + "\n" )
.addContextInfo( getWrapperDataContext( viewKeyStruct::objectPathString() ).getContextInfo()
.setPriority( 2 ) );
throw InputError( e, getWrapperDataContext( viewKeyStruct::objectPathString() ).toString() +
- " is a wrong objectPath: " + m_objectPath + "\n" );
+ " is a wrong objectPath: " + path + "\n" );
}
}
diff --git a/src/coreComponents/fieldSpecification/FieldSpecificationBase.hpp b/src/coreComponents/fieldSpecification/FieldSpecificationBase.hpp
index 1905a4e5c36..53044d3a466 100644
--- a/src/coreComponents/fieldSpecification/FieldSpecificationBase.hpp
+++ b/src/coreComponents/fieldSpecification/FieldSpecificationBase.hpp
@@ -355,6 +355,54 @@ class FieldSpecificationBase : public dataRepository::Group
arrayView1d< real64 > const & rhsContribution,
LAMBDA && lambda ) const;
+ /**
+ * @brief Compute the contributions that will be added/enforced to the right-hand side,
+ * and collect the corresponding dof numbers
+ * @tparam FIELD_OP A wrapper struct to define how the boundary condition operates on the variables.
+ * Either \ref OpEqual or \ref OpAdd.
+ * @tparam POLICY Execution policy to use when iterating over target set.
+ * @tparam LAMBDA The type of lambda function passed into the parameter list.
+ * @param[in] component The field component to apply the boundary condition to.
+ * @param[in] scale The scale factor to apply to the boundary condition value.
+ * @param[in] functionName The name of the function used to evaluate the boundary condition value.
+ * @param[in] targetSet The set of indices which the boundary condition will be applied.
+ * @param[in] time The time at which any time dependent functions are to be evaluated as part of the
+ * application of the boundary condition.
+ * @param[in] dt time step size which is applied as a factor to bc values
+ * @param[in] dataGroup The Group that contains the field to apply the boundary condition to.
+ * @param[in] dofMap The map from the local index of the primary field to the global degree of
+ * freedom number.
+ * @param[in] dofRankOffset Offset of dof indices on current rank.
+ * @param[inout] matrix Local part of the system matrix.
+ * @param[inout] dof array storing the degrees of freedom of the rhsContribution, to know where
+ * in the rhs they will be added/enforced
+ * @param[inout] rhsContribution array storing the values that will be added/enforced to the right-hand side
+ * @param[in] lambda A lambda function which defines how the value that is passed into the functions
+ * provided by the FIELD_OP templated type.
+ *
+ * This overload behaves like the legacy computeRhsContribution, but takes the component, scale
+ * and functionName as explicit arguments rather than reading them from the corresponding members.
+ * This is the variant called when using non-scalar valued field specifications/boundary conditions,
+ * where each component is applied in turn.
+ *
+ * Note that this function only computes the rhs contributions, but does not apply them to the right-hand side.
+ * The application of these rhs contributions is done in applyBoundaryConditionToSystem.
+ */
+ template< typename FIELD_OP, typename POLICY, typename LAMBDA >
+ void
+ computeRhsContribution( integer const component,
+ real64 const scale,
+ string const & functionName,
+ SortedArrayView< localIndex const > const & targetSet,
+ real64 const time,
+ real64 const dt,
+ dataRepository::Group const & dataGroup,
+ arrayView1d< globalIndex const > const & dofMap,
+ globalIndex const dofRankOffset,
+ CRSMatrixView< real64, globalIndex const > const & matrix,
+ arrayView1d< globalIndex > const & dof,
+ arrayView1d< real64 > const & rhsContribution,
+ LAMBDA && lambda ) const;
/**
* @brief Function to zero matrix rows to apply boundary conditions
@@ -382,6 +430,8 @@ class FieldSpecificationBase : public dataRepository::Group
constexpr static char const * constitutivePathString() { return "constitutivePath"; }
/// @return The key for objectPath
constexpr static char const * objectPathString() { return "objectPath"; }
+ /// @return The key for regionNames
+ constexpr static char const * regionNamesString() { return "regionNames"; }
/// @return The key for fieldName
constexpr static char const * fieldNameString() { return "fieldName"; }
/// @return The key for dataType
@@ -394,8 +444,12 @@ class FieldSpecificationBase : public dataRepository::Group
constexpr static char const * bcApplicationTableNameString() { return "bcApplicationTableName"; }
/// @return The key for scale
constexpr static char const * scaleString() { return "scale"; }
+ /// @return The key for scales
+ constexpr static char const * scalesString() { return "scales"; }
/// @return The key for functionName
constexpr static char const * functionNameString() { return "functionName"; }
+ /// @return The key for functionNames
+ constexpr static char const * functionNamesString() { return "functionNames"; }
/// @return The key for initialCondition
constexpr static char const * initialConditionString() { return "initialCondition"; }
/// @return The key for beginTime
@@ -415,6 +469,15 @@ class FieldSpecificationBase : public dataRepository::Group
return m_functionName;
}
+ /**
+ * Accessor
+ * @return const reference to m_functionNames
+ */
+ string_array const & getFunctionNames() const
+ {
+ return m_functionNames;
+ }
+
/**
* Accessor
* @return const reference to m_objectPath
@@ -424,6 +487,15 @@ class FieldSpecificationBase : public dataRepository::Group
return m_objectPath;
}
+ /**
+ * Accessor
+ * @return const reference to m_regionNames
+ */
+ string_array const & getRegionNames() const
+ {
+ return m_regionNames;
+ }
+
/**
* Accessor
* @return const reference to m_fieldName
@@ -497,6 +569,15 @@ class FieldSpecificationBase : public dataRepository::Group
return m_scale;
}
+ /**
+ * Accessor
+ * @return const m_scales
+ */
+ arrayView1d< real64 const > getScales() const
+ {
+ return m_scales.toViewConst();
+ }
+
/**
* Mutator
* @param[in] fieldName The name of the field
@@ -515,6 +596,15 @@ class FieldSpecificationBase : public dataRepository::Group
m_objectPath = objectPath;
}
+ /**
+ * Mutator
+ * @param[in] regionNames The region names
+ */
+ void setRegionNames( string_array const & regionNames )
+ {
+ m_regionNames = regionNames;
+ }
+
/**
* Mutator
* @param[in] scale Scaling factor
@@ -524,6 +614,27 @@ class FieldSpecificationBase : public dataRepository::Group
m_scale = 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 have the same
+ * size as @p m_scales or be empty.
+ */
+ void setFunctionNames( string_array const & functionNames )
+ {
+ m_functionNames = functionNames;
+ }
+
/**
* Mutator
* @param[in] isInitialCondition Logical value to indicate if it is an initial condition
@@ -559,9 +670,45 @@ class FieldSpecificationBase : public dataRepository::Group
return *(m_meshObjectPaths.get());
}
+ /**
+ * @brief Query whether this field specification uses non-scalar scales
+ * @return True if the field specification uses the non-scalar 'scales' attribute
+ * or the non-scalar 'functionNames' attribute
+ */
+ bool usesNonScalarValues() const
+ {
+ return !m_scales.empty() || !m_functionNames.empty();
+ }
+
+ /**
+ * @brief Apply the lambda to each component of the field specification
+ * @tparam LAMBDA The type of lambda function passed into the parameter list.
+ * @param lambda The lambda being executed
+ */
+ template< typename LAMBDA >
+ void forEachComponent( LAMBDA && lambda ) const
+ {
+ if( usesNonScalarValues() )
+ {
+ localIndex const numComponents = m_scales.size();
+ for( localIndex comp = 0; comp < numComponents; ++comp )
+ {
+ string const & functionName = m_functionNames.empty() ? string{}
+ : m_functionNames[ comp ];
+ lambda( comp, m_scales[ comp ], functionName );
+ }
+ }
+ else
+ {
+ lambda( getComponent(), m_scale, m_functionName );
+ }
+ }
+
protected:
+ virtual void postInputInitialization() override;
+
private:
@@ -574,6 +721,9 @@ class FieldSpecificationBase : public dataRepository::Group
std::unique_ptr< MeshObjectPath > m_meshObjectPaths;
+ /// the region names where the field specification is applied
+ string_array m_regionNames;
+
/// the name of the field the boundary condition is applied to or a key string to use for
/// determining whether or not to apply the boundary condition.
string m_fieldName;
@@ -591,9 +741,16 @@ class FieldSpecificationBase : public dataRepository::Group
/// The name of the function used to generate values for application.
string m_functionName;
+ /// Per-component function names used when @p m_scales.size() > 1
+ /// Either empty or sized exactly like @p m_scales
+ string_array m_functionNames;
+
/// The scale factor to use on the value of the boundary condition.
real64 m_scale;
+ /// Per-component scale factors
+ array1d< real64 > m_scales;
+
/// Time after which the bc is allowed to be applied
real64 m_beginTime;
@@ -614,25 +771,28 @@ void FieldSpecificationBase::applyFieldValueKernel( ArrayView< T, N, USD > const
real64 const time,
Group & dataGroup ) const
{
- integer const component = getComponent();
FunctionManager & functionManager = FunctionManager::getInstance();
- if( m_functionName.empty() )
+ forEachComponent( [&]( integer const component,
+ real64 const scale,
+ string const & functionName )
{
- real64 const value = m_scale;
- forAll< POLICY >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const i )
+ if( functionName.empty() )
{
- localIndex const a = targetSet[ i ];
- FIELD_OP::SpecifyFieldValue( field, a, component, value );
- } );
- }
- else
- {
+ real64 const value = scale;
+ forAll< POLICY >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const i )
+ {
+ localIndex const a = targetSet[ i ];
+ FIELD_OP::SpecifyFieldValue( field, a, component, value );
+ } );
+ return;
+ }
+
FunctionBase const & function = [&]() -> FunctionBase const &
{
try
{
- return functionManager.getGroup< FunctionBase >( m_functionName );
+ return functionManager.getGroup< FunctionBase >( functionName );
}
catch( std::exception const & e )
{
@@ -648,7 +808,7 @@ void FieldSpecificationBase::applyFieldValueKernel( ArrayView< T, N, USD > const
if( function.isFunctionOfTime()==2 )
{
- real64 const value = m_scale * function.evaluate( &time );
+ real64 const value = scale * function.evaluate( &time );
forAll< POLICY >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const i )
{
localIndex const a = targetSet[ i ];
@@ -660,14 +820,14 @@ void FieldSpecificationBase::applyFieldValueKernel( ArrayView< T, N, USD > const
real64_array result( static_cast< localIndex >( targetSet.size() ) );
function.evaluate( dataGroup, time, targetSet, result );
arrayView1d< real64 const > const & resultView = result.toViewConst();
- real64 const scale = m_scale;
+ real64 const localScale = scale;
forAll< POLICY >( targetSet.size(), [=] GEOS_HOST_DEVICE ( localIndex const i )
{
localIndex const a = targetSet[ i ];
- FIELD_OP::SpecifyFieldValue( field, a, component, scale * resultView[i] );
+ FIELD_OP::SpecifyFieldValue( field, a, component, localScale * resultView[i] );
} );
}
- }
+ } );
}
@@ -703,13 +863,38 @@ void FieldSpecificationBase::applyBoundaryConditionToSystemKernel( SortedArrayVi
arrayView1d< real64 > const & rhs,
ArrayView< T const, NDIM, USD > const & fieldView ) const
{
- integer const component = getComponent();
- this->applyBoundaryConditionToSystem< FIELD_OP, POLICY >( targetSet, time, dataGroup, dofMap, dofRankOffset, matrix, rhs,
- [fieldView, component] GEOS_HOST_DEVICE ( localIndex const a )
+ forEachComponent( [&]( integer const component,
+ real64 const scale,
+ string const & functionName )
{
- real64 value = 0.0;
- FieldSpecificationEqual::readFieldValue( fieldView, a, component, value );
- return value;
+ integer const comp = ( component >= 0 ) ? component : 0;
+
+ array1d< globalIndex > dofArray( targetSet.size() );
+ arrayView1d< globalIndex > const & dof = dofArray.toView();
+
+ array1d< real64 > rhsContributionArray( targetSet.size() );
+ arrayView1d< real64 > const & rhsContribution = rhsContributionArray.toView();
+
+ computeRhsContribution< FIELD_OP, POLICY >( comp,
+ scale,
+ functionName,
+ targetSet,
+ time,
+ 1.0,
+ dataGroup,
+ dofMap,
+ dofRankOffset,
+ matrix,
+ dof,
+ rhsContribution,
+ [fieldView, component] GEOS_HOST_DEVICE ( localIndex const a )
+ {
+ real64 value = 0.0;
+ FieldSpecificationEqual::readFieldValue( fieldView, a, component, value );
+ return value;
+ } );
+
+ FIELD_OP::template prescribeRhsValues< POLICY >( rhs, dof, dofRankOffset, rhsContribution );
} );
}
@@ -779,30 +964,43 @@ FieldSpecificationBase::
arrayView1d< real64 > const & rhs,
LAMBDA && lambda ) const
{
- array1d< globalIndex > dofArray( targetSet.size() );
- arrayView1d< globalIndex > const & dof = dofArray.toView();
-
- array1d< real64 > rhsContributionArray( targetSet.size() );
- arrayView1d< real64 > const & rhsContribution = rhsContributionArray.toView();
-
- computeRhsContribution< FIELD_OP, POLICY, LAMBDA >( targetSet,
- time,
- dt,
- dataGroup,
- dofMap,
- dofRankOffset,
- matrix,
- dof,
- rhsContribution,
- std::forward< LAMBDA >( lambda ) );
-
- FIELD_OP::template prescribeRhsValues< POLICY >( rhs, dof, dofRankOffset, rhsContribution );
+ forEachComponent( [&]( integer const component,
+ real64 const scale,
+ string const & functionName )
+ {
+ integer const comp = ( component >= 0 ) ? component : 0;
+
+ array1d< globalIndex > dofArray( targetSet.size() );
+ arrayView1d< globalIndex > const & dof = dofArray.toView();
+
+ array1d< real64 > rhsContributionArray( targetSet.size() );
+ arrayView1d< real64 > const & rhsContribution = rhsContributionArray.toView();
+
+ computeRhsContribution< FIELD_OP, POLICY >( comp,
+ scale,
+ functionName,
+ targetSet,
+ time,
+ dt,
+ dataGroup,
+ dofMap,
+ dofRankOffset,
+ matrix,
+ dof,
+ rhsContribution,
+ lambda );
+
+ FIELD_OP::template prescribeRhsValues< POLICY >( rhs, dof, dofRankOffset, rhsContribution );
+ } );
}
template< typename FIELD_OP, typename POLICY, typename LAMBDA >
void
FieldSpecificationBase::
- computeRhsContribution( SortedArrayView< localIndex const > const & targetSet,
+ computeRhsContribution( integer const component,
+ real64 const scale,
+ string const & functionName,
+ SortedArrayView< localIndex const > const & targetSet,
real64 const time,
real64 const dt,
dataRepository::Group const & dataGroup,
@@ -813,8 +1011,6 @@ FieldSpecificationBase::
arrayView1d< real64 > const & rhsContribution,
LAMBDA && lambda ) const
{
- integer const component = ( getComponent() >=0 ) ? getComponent() : 0;
- string const & functionName = getReference< string >( viewKeyStruct::functionNameString() );
FunctionManager & functionManager = FunctionManager::getInstance();
// Compute the value of the rhs terms, and collect the dof numbers
@@ -822,7 +1018,7 @@ FieldSpecificationBase::
if( functionName.empty() || functionManager.getGroup< FunctionBase >( functionName ).isFunctionOfTime() == 2 )
{
- real64 value = m_scale * dt;
+ real64 value = scale * dt;
if( !functionName.empty() )
{
FunctionBase const & function = functionManager.getGroup< FunctionBase >( functionName );
@@ -849,7 +1045,7 @@ FieldSpecificationBase::
real64_array resultsArray( targetSet.size() );
function.evaluate( dataGroup, time, targetSet, resultsArray );
arrayView1d< real64 const > const & results = resultsArray.toViewConst();
- real64 const value = m_scale * dt;
+ real64 const value = scale * dt;
forAll< POLICY >( targetSet.size(),
[targetSet, dof, dofMap, dofRankOffset, component, matrix, rhsContribution, results, value, lambda] GEOS_HOST_DEVICE (
@@ -867,6 +1063,36 @@ FieldSpecificationBase::
}
}
+template< typename FIELD_OP, typename POLICY, typename LAMBDA >
+void
+FieldSpecificationBase::
+ computeRhsContribution( SortedArrayView< localIndex const > const & targetSet,
+ real64 const time,
+ real64 const dt,
+ dataRepository::Group const & dataGroup,
+ arrayView1d< globalIndex const > const & dofMap,
+ globalIndex const dofRankOffset,
+ CRSMatrixView< real64, globalIndex const > const & matrix,
+ arrayView1d< globalIndex > const & dof,
+ arrayView1d< real64 > const & rhsContribution,
+ LAMBDA && lambda ) const
+{
+ computeRhsContribution< FIELD_OP, POLICY, LAMBDA >( ( getComponent() >= 0 ) ? getComponent() : 0,
+ m_scale,
+ m_functionName,
+ targetSet,
+ time,
+ dt,
+ dataGroup,
+ dofMap,
+ dofRankOffset,
+ matrix,
+ dof,
+ rhsContribution,
+ std::forward< LAMBDA >( lambda ) );
+
+}
+
template< typename POLICY >
void FieldSpecificationBase::zeroSystemRowsForBoundaryCondition( SortedArrayView< localIndex const > const & targetSet,
@@ -874,19 +1100,25 @@ void FieldSpecificationBase::zeroSystemRowsForBoundaryCondition( SortedArrayView
CRSMatrixView< real64, globalIndex const > const & matrix ) const
{
- integer const component = ( getComponent() >=0 ) ? getComponent() : 0;
- forAll< POLICY >( targetSet.size(), [targetSet, dofMap, matrix, component] GEOS_HOST_DEVICE ( localIndex const i )
+ forEachComponent( [&]( integer const component,
+ real64 const scale,
+ string const & functionName )
{
- localIndex const a = targetSet[ i ];
- globalIndex const dof = dofMap[ a ] + component;
+ GEOS_UNUSED_VAR( scale, functionName );
+ integer const comp = ( component >= 0 ) ? component : 0;
+ forAll< POLICY >( targetSet.size(), [targetSet, dofMap, matrix, component] GEOS_HOST_DEVICE ( localIndex const i )
+ {
+ localIndex const a = targetSet[ i ];
+ globalIndex const dof = dofMap[ a ] + component;
- arraySlice1d< real64 > const entries = matrix.getEntries( dof );
- localIndex const numEntries = matrix.numNonZeros( dof );
+ arraySlice1d< real64 > const entries = matrix.getEntries( dof );
+ localIndex const numEntries = matrix.numNonZeros( dof );
- for( localIndex j = 0; j < numEntries; ++j )
- {
- entries[ j ] = 0;
- }
+ for( localIndex j = 0; j < numEntries; ++j )
+ {
+ entries[ j ] = 0;
+ }
+ } );
} );
}
diff --git a/src/coreComponents/fieldSpecification/PerfectlyMatchedLayer.cpp b/src/coreComponents/fieldSpecification/PerfectlyMatchedLayer.cpp
index 7e71480536c..ae56811c914 100644
--- a/src/coreComponents/fieldSpecification/PerfectlyMatchedLayer.cpp
+++ b/src/coreComponents/fieldSpecification/PerfectlyMatchedLayer.cpp
@@ -78,6 +78,8 @@ PerfectlyMatchedLayer::PerfectlyMatchedLayer( string const & name, Group * const
void PerfectlyMatchedLayer::postInputInitialization()
{
+ FieldSpecificationBase::postInputInitialization();
+
GEOS_THROW_IF( (m_xMax[0]( getDirection() ) < 1e-20,
diff --git a/src/coreComponents/fieldSpecification/docs/FieldSpecification.rst b/src/coreComponents/fieldSpecification/docs/FieldSpecification.rst
index 7752ed84863..a609bdbcd44 100644
--- a/src/coreComponents/fieldSpecification/docs/FieldSpecification.rst
+++ b/src/coreComponents/fieldSpecification/docs/FieldSpecification.rst
@@ -6,6 +6,8 @@ Initial and Boundary Conditions
.. toctree::
:maxdepth: 1
+ FieldSpecificationBase
+
EquilibriumInitialCondition
AquiferBoundaryCondition
diff --git a/src/coreComponents/fieldSpecification/docs/FieldSpecificationBase.rst b/src/coreComponents/fieldSpecification/docs/FieldSpecificationBase.rst
new file mode 100644
index 00000000000..2dfd21fdf45
--- /dev/null
+++ b/src/coreComponents/fieldSpecification/docs/FieldSpecificationBase.rst
@@ -0,0 +1,81 @@
+.. _FieldSpecificationBase:
+
+####################################################
+FieldSpecification Options
+####################################################
+
+Overview
+======================
+
+`FieldSpecification` entries live under the `FieldSpecifications` block and are used to define initial and boundary conditions for fields registered on mesh managers (nodes, faces, or element subregions).
+This page documents options added for per-component values and region-based targeting.
+
+Region-based targetting
+===============================
+
+Use `regionNames` to target one or more element regions without specifying an explicit `objectPath`.
+When `regionNames` is provided, GEOS builds the `objectPath` internally and applies the specification to all regions.
+You can also specify subregions in `regionNames` by using a syntax similar to the objectPath (cf. examples).
+
+.. note::
+ `regionNames` and `objectPath` are mutually exclusive. Use one or the other.
+
+Non-scalar values
+======================
+
+Use `scales` and (optionally) `functionNames` to apply per-component values.
+
+- `scales` replaces `scale` when you want one value per component.
+- `functionNames` is optional and must be empty or have the same size as `scales`.
+- When `scales` is used, do not set `component` or `functionName`.
+
+For scalar fields, you can use `scale`, or `scales` with a single value.
+
+Examples
+======================
+
+The snippet below shows a specification using `scales` and `functionNames`.
+
+.. code-block:: xml
+
+
+
+
+
+The snippet below shows a specification using `regionNames` on a single region.
+
+.. code-block:: xml
+
+
+
+
+
+The snippet below shows a specification using `regionNames` on a multiple subregions.
+
+.. code-block:: xml
+
+
+
+
\ No newline at end of file
diff --git a/src/coreComponents/schema/schema.xsd b/src/coreComponents/schema/schema.xsd
index 9a9bb51d843..580fd27ba05 100644
--- a/src/coreComponents/schema/schema.xsd
+++ b/src/coreComponents/schema/schema.xsd
@@ -1363,13 +1363,20 @@ When set to "error", output a throw.
+
+
+
+
+
+
@@ -1405,12 +1412,19 @@ A field can represent a physical variable. (pressure, temperature, global compos
+
+
+
+
+
+
@@ -1441,12 +1455,19 @@ A field can represent a physical variable. (pressure, temperature, global compos
+
+
+
+
+
+
@@ -1483,6 +1504,9 @@ When set to "error", output a throw.
+
+
@@ -1491,8 +1515,12 @@ When set to "error", output a throw.
+
+
+
+
@@ -1518,12 +1546,19 @@ When set to "error", output a throw.
+
+
+
+
+
+
@@ -1563,12 +1598,19 @@ When set to "error", output a throw.
+
+
+
+
+
+
@@ -1593,6 +1635,9 @@ When set to "error", output a throw.
+
+
@@ -1601,10 +1646,14 @@ When set to "error", output a throw.
+
+
+
+
diff --git a/src/coreComponents/schema/schema.xsd.other b/src/coreComponents/schema/schema.xsd.other
index c7434b7b99c..384d1e4a799 100644
--- a/src/coreComponents/schema/schema.xsd.other
+++ b/src/coreComponents/schema/schema.xsd.other
@@ -526,7 +526,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+
@@ -1603,7 +1603,7 @@ A field can represent a physical variable. (pressure, temperature, global compos
-
+