Materials System

The material system is the primary mechanism for defining spatially varying properties. The system allows properties to be defined in a single object (a Material) and shared among the many other systems such as the Kernel or BoundaryCondition systems. Material objects are designed to directly couple to solution variables as well as other materials and therefore allow for capturing the true nonlinear behavior of the equations.

The material system relies on a producer/consumer relationship: Material objects produce properties and other objects (including materials) consume these properties.

The properties are produced on demand, thus the computed values are always up to date. For example, a property that relies on a solution variable (e.g., thermal conductivity as function of temperature) will be computed with the current temperature during the solve iterations, so the properties are tightly coupled.

The material system supports the use of automatic differentiation for property calculations, as such there are two approaches for producing and consuming properties: with and without automatic differentiation. The following sections detail the producing and consuming properties using the two approaches. To further understand automatic differentiation, please refer to the Automatic Differentiation page for more information.

The proceeding sections briefly describe the different aspects of a Material object for producing and computing the properties as well as how other objects consume the properties. For an example of how a Material object is created and used please refer to Example 8 : Material Properties.

Producing/Computing Properties

Properties must be produced by a Material object by declaring the property with one of two methods:

  1. declareProperty<TYPE>("property_name") declares a property with a name "property_name" to be computed by the Material object.

  2. declareADProperty<TYPE> declares a property with a name "property_name" to be computed by the Material object that will include automatic differentiation.

The TYPE is any valid C++ type such an int or Real or std::vector<Real>. The properties must then be computed within the computeQpProperties method defined within the object.

The property name is an arbitrary name of the property, this name should be set such that it corresponds to the value be computed (e.g., "diffusivity"). The name provided here is the same name that will be used for consuming the property. More information on names is provided in Property Names section below.

For example, consider a simulation that requires a diffusivity term. In the Material object header a property is declared (in the C++ since) as follows.

  MaterialProperty<Real> & _diffusivity;
(examples/ex08_materials/include/materials/ExampleMaterial.h)

All properties will either be a MaterialProperty<TYPE> or ADMaterialProperty<TYPE> and must be a non-const reference. Again, the TYPE can be any C++ type. In this example, a scalar Real number is being used.

In the source file the reference is initialized in the initialization list using the aforementioned declare functions as follows. This declares the property (in the material property sense) to be computed.

    _diffusivity(declareProperty<Real>("diffusivity")),
(examples/ex08_materials/src/materials/ExampleMaterial.C)

The final step for producing a property is to compute the value. The computation occurs within a Material object computeQpProperties method. As the method name suggests, the purpose of the method is to compute the values of properties at a quadrature point. This method is a virtual method that must be overridden. To do this, in the header the virtual method is declared (again in the C++ sense).

  virtual void computeQpProperties() override;
(examples/ex08_materials/include/materials/ExampleMaterial.h)

In the source file the method is defined. For the current example this definition computes the "diffusivity" as well another term, refer to Example 8 : Material Properties.

ExampleMaterial::computeQpProperties()
{
  // Diffusivity is the value of the interpolated piece-wise function described by the user
  _diffusivity[_qp] = _piecewise_func.sample(_q_point[_qp](2));

  // Convection velocity is set equal to the gradient of the variable set by the user.
  _convection_velocity[_qp] = _diffusion_gradient[_qp];
}
(examples/ex08_materials/src/materials/ExampleMaterial.C)

The purpose of the content of this method is to assign values for the properties at a quadrature point. Recall that "_diffusivity" is a reference to a MaterialProperty type. The MaterialProperty type is a container that stores the values of a property for each quadrature point. Therefore, this container must be indexed by _qp to compute the value for a specific quadrature point.

Consuming Properties

Objects that require material properties consume them using one of two functions

  1. getMaterialProperty<TYPE>("property_name") retrieves a property with a name "property_name" to be consumed by the object.

  2. getADMaterialProperty<TYPE>("property_name") retrieves a property with a name "property_name" to be consumed by the object that will include automatic differentiation.

For an object to consume a property the same basic procedure is followed. First in the consuming objects header file a MaterialProperty with the correct type (e.g., Real for the diffusivity example) is declared (in the C++ sense) as follows. Notice, that the member variable is a const reference. The const is important. Consuming objects cannot modify a property, it only uses the property so it is marked to be constant.

  const MaterialProperty<Real> & _diffusivity;
(examples/ex08_materials/include/kernels/ExampleDiffusion.h)

In the source file the reference is initialized in the initialization list using the aforementioned get methods. This method initializes the _diffusivity member variable to reference the desired value of the property as computed by the material object.

  : Diffusion(parameters), _diffusivity(getMaterialProperty<Real>("diffusivity"))
(examples/ex08_materials/src/kernels/ExampleDiffusion.C)

The name used in the get method, "diffusivity", in this case is not arbitrary. This name corresponds with the name used to declare the property in the material object.

commentnote:The declare/get calls must correspond

If a material property is declared for automatic differentiation (AD) using declareADProperty then it must be consumed with the getADMaterialProperty. The same is true for non-automatic differentiation; properties declared with declareProperty must be consumed with the getMaterialProperty method.

Optional Properties

Objects can weakly couple to material properties that may or may not exist.

  1. getOptionalMaterialProperty<TYPE>("property_name") retrieves an optional property with a name "property_name" to be consumed by the object.

  2. getOptionalADMaterialProperty<TYPE>("property_name") retrieves an optional property with a name "property_name" to be consumed by the object that will include automatic differentiation.

This API returns a reference to an optional material property (OptionalMaterialProperty or OptionalADMaterialProperty). If the requested property is not provided by any material this reference will evaluate to false. It is the consuming object's responsibility to check for this before accessing the material property data. Note that the state of the returned reference is only finalized _after_ all materials have been constructed, so a validity check must _not_ be made in the constructor of a material class but either at time of first use in computeQpProperties or in initialSetup.

Property Names

When creating a Material object and declaring the properties that shall be computed, it is often desirable to allow for the property name to be changed via the input file. This may be accomplished by adding an input parameter for assigning the name. For example, considering the example above the following code snippet adds an input parameter, "diffusivity_name", that allows the input file to set the name of the diffusivity property, but by default the name remains "diffusivity".


params.addParam<MaterialPropertyName>("diffusivity_name", "diffusivity",
                                      "The name of the diffusivity material property.");

In the material object, the declare function is simply changed to use the parameter name rather than string by itself. By default a property will be declared with the name "diffusivity".

    _diffusivity_name(declareProperty<Real>("diffusivity_name")),
(examples/ex08_materials/src/materials/ExampleMaterial.C)

However, if the user wants to alter this name to something else, such as "not_diffusivity" then the input parameter "diffusivity_name" is simply added to the input file block for the material.


[Materials]
  [example]
    type = ExampleMaterial
    diffusivity_name = not_diffusivity
  []
[]

On the consumer side, the get method will now be required to use the name "not_diffusivity" to retrieve the property. Consuming objects can also use the same procedure to allow for custom property names by adding a parameter and using the parameter name in the get method in the same fashion.

Default Material Properties

The MaterialPropertyName input parameter also provides the ability to set default values for scalar (Real) properties. In the above example, the input file can use number or parsed function (see ParsedFunction) to define a the property value. For example, the input snippet above could set a constant value.


[Materials]
  [example]
    type = ExampleMaterial
    diffusivity_name = 12345
  []
[]

Stateful Material Properties

In general properties are computed on demand and not stored. However, in some cases values of material properties from a previous timestep may be required. To access properties two methods exist:

  • getMaterialPropertyOld<TYPE> returns a reference to the property from the previous timestep.

  • getMaterialPropertyOlder<TYPE> returns a reference to the property from two timesteps before the current.

This is often referred to as a "state" variable, in MOOSE we refer to them as "stateful material properties." As stated, material properties are usually computed on demand.

warningwarning:Stateful properties will increase memory use

When a stateful property is requested through one of the above methods this is no longer the case. When it is computed the value is also stored for every quadrature point on every element. As such, stateful properties can become memory intensive, especially if the property being stored is a vector or tensor value.

Material Property Output

Output of Material properties is enabled by setting the "outputs" parameter. The following example creates two additional variables called "mat1" and "mat2" that will show up in the output file.

[Materials]
  [block_1]
    type = OutputTestMaterial
    block = 1
    output_properties = 'real_property tensor_property'
    outputs = exodus
    variable = u
  []
  [block_2]
    type = OutputTestMaterial
    block = 2
    output_properties = 'vector_property tensor_property'
    outputs = exodus
    variable = u
  []
[]

[Outputs]
  exodus = true
[]
(test/tests/materials/output/output_block.i)

Material properties can be of arbitrary (C++) type, but not all types can be output. The following table lists the types of properties that are available for automatic output.

TypeAuxKernelVariable Name(s)
RealMaterialRealAuxprop
RealVectorValueMaterialRealVectorValueAuxprop_1, prop_2, and prop_3
RealTensorValueMaterialRealTensorValueAuxprop_11, prop_12, prop_13, prop_21, etc.

Material sorting

Materials are sorted such that one material may consume a property produced by another material and know that the consumed property will be up-to-date, e.g. the producer material will execute before the consumer material. If a cyclic dependency is detected between two materials, then MOOSE will produce an error.

Functor Material Properties

Functor material properties are properties that are evaluated on-the-fly. E.g. they can be viewed as functions of the current location in space (and time). Functor material properties provide several overloads of the operator() method for different "geometric quantities". One example of a "geometric quantity" is a const Elem *, e.g. for an FVElementalKernel, the value of a functor material property in a cell-averaged sense can be obtained by the syntax

  • _foo(_current_elem)

where here _foo is a functor material property data member of the kernel. The functor material property system introduces APIs very similar to the traditional material property system for declaring and getting properties. To declare a functor property:

  • declareFunctorProperty<TYPE>

where TYPE can be anything such as Real, ADReal, RealVectorValue, ADRealVectorValue etc. To get a functor material property:

  • getFunctor<TYPE>

It's worth noting that whereas the traditional regular material property system has different methods to declare/get non-AD and AD properties, the new functor system has single APIs for both non-AD and AD property types.

Currently, functor material property evaluations are defined using the API:


template <typename T>
template <typename PolymorphicLambda>
void FunctorMaterialProperty<T>::
setFunctor(const MooseMesh & mesh,
           const std::set<SubdomainID> & block_ids,
           PolymorphicLambda my_lammy);

where the first two arguments are used to setup block restriction and the last argument is a lambda defining the property evaluation. The lambda must be callable with two arguments, the first corresponding to space, and the second corresponding to time, and must return the type T of the FunctorMaterialProperty. An example of setting a constant functor material property that returns an ADReal looks like:


    _constant_unity_prop.setFunctor(
        _mesh, blockIDs(), [](const auto &, const auto &) -> ADReal { return 1.; });

An example of a functor material property that depends on a nonlinear variable would look like


    _u_prop.setFunctor(_mesh, blockIDs(), [this](const auto & r, const auto & t) -> ADReal {
      return _u_var(r, t);
    });

In the above example, we simply forward the calling arguments along to the variable. Variable functor implementation is described in Variable functor evaluation. A test functor material class to setup a dummy Euler problem is shown in


#include "ADCoupledVelocityMaterial.h"

registerMooseObject("MooseTestApp", ADCoupledVelocityMaterial);

InputParameters
ADCoupledVelocityMaterial::validParams()
{
  InputParameters params = FunctorMaterial::validParams();
  params.addRequiredParam<MooseFunctorName>("vel_x", "the x velocity");
  params.addParam<MooseFunctorName>("vel_y", "the y velocity");
  params.addParam<MooseFunctorName>("vel_z", "the z velocity");
  params.addRequiredParam<MooseFunctorName>("rho", "The name of the density variable");
  params.addClassDescription("A material used to create a velocity from coupled variables");
  params.addParam<MaterialPropertyName>(
      "velocity", "velocity", "The name of the velocity material property to create");
  params.addParam<MaterialPropertyName>(
      "rho_u", "rho_u", "The product of the density and the x-velocity component");
  params.addParam<MaterialPropertyName>(
      "rho_v", "rho_v", "The product of the density and the y-velocity component");
  params.addParam<MaterialPropertyName>(
      "rho_w", "rho_w", "The product of the density and the z-velocity component");
  params += SetupInterface::validParams();
  params.set<ExecFlagEnum>("execute_on") = {EXEC_ALWAYS};
  return params;
}

ADCoupledVelocityMaterial::ADCoupledVelocityMaterial(const InputParameters & parameters)
  : FunctorMaterial(parameters),
    _vel_x(getFunctor<ADReal>("vel_x")),
    _vel_y(isParamValid("vel_y") ? &getFunctor<ADReal>("vel_y") : nullptr),
    _vel_z(isParamValid("vel_z") ? &getFunctor<ADReal>("vel_z") : nullptr),
    _rho(getFunctor<ADReal>("rho"))
{
  const std::set<ExecFlagType> clearance_schedule(_execute_enum.begin(), _execute_enum.end());

  addFunctorProperty<ADRealVectorValue>(
      getParam<MaterialPropertyName>("velocity"),
      [this](const auto & r, const auto & t) -> ADRealVectorValue
      {
        ADRealVectorValue velocity(_vel_x(r, t));
        velocity(1) = _vel_y ? (*_vel_y)(r, t) : ADReal(0);
        velocity(2) = _vel_z ? (*_vel_z)(r, t) : ADReal(0);
        return velocity;
      },
      clearance_schedule);

  addFunctorProperty<ADReal>(
      getParam<MaterialPropertyName>("rho_u"),
      [this](const auto & r, const auto & t) -> ADReal { return _rho(r, t) * _vel_x(r, t); },
      clearance_schedule);

  addFunctorProperty<ADReal>(
      getParam<MaterialPropertyName>("rho_v"),
      [this](const auto & r, const auto & t) -> ADReal
      { return _vel_y ? _rho(r, t) * (*_vel_y)(r, t) : ADReal(0); },
      clearance_schedule);

  addFunctorProperty<ADReal>(
      getParam<MaterialPropertyName>("rho_w"),
      [this](const auto & r, const auto & t) -> ADReal
      { return _vel_z ? _rho(r, t) * (*_vel_z)(r, t) : ADReal(0); },
      clearance_schedule);
}
(test/src/materials/ADCoupledVelocityMaterial.C)

In the following subsections, we describe the various spatial arguments that functor (material properties) can be evaluated at. Almost no functor material developers should have to concern themselves with these details as most material property functions should just appear as functions of space and time, e.g. the same lambda defining the property evaluation should apply across all spatial and temporal arguments. However, in the case that a functor material developer wishes to create specific implementations for specific arguments (as illustrated in IMakeMyOwnFunctorProps test class) or simply wishes to know more about the system, we give the details below.

Any call to a functor (material property) looks like the following _foo(const SpatialArg & r, const TemporalArg & t). Below are the possible type overloads of SpatialArg.

FaceArg

A typedef defining a "face" evaluation calling argument. This is composed of

  • a face information object which defines our location in space

  • a limiter which defines how the functor evaluated on either side of the face should be interpolated to the face

  • a boolean which states whether the face information element is upwind of the face

  • a pair of subdomain IDs. These do not always correspond to the face info element subdomain ID and face info neighbor subdomain ID. For instance if a flux kernel is operating at a subdomain boundary on which the kernel is defined on one side but not the other, the passed-in subdomain IDs will both correspond to the subdomain ID that the flux kernel is defined on

ElemFromFaceArg

People should think of this geometric argument as corresponding to the location in space of the provided element centroid, not as corresonding to the location of the provided face information. Summary of data in this argument:

  • an element, whose centroid we should think of as the evaluation point. It is possible that the element will be a nullptr in which case, the evaluation point should be thought of as the location of a ghosted element centroid

  • a face information object. When the provided element is null or for instance when the functoris a variable that does not exist on the provided element subdomain, this face information object will be used to help construct a ghost value evaluation

  • a subdomain ID. This is useful when the functor is a material property and the user wants to indicate which material property definition should be used to evaluate the functor. For instance if we are using a flux kernel that is not defined on one side of the face, the subdomain ID will allow us to compute a ghost material property evaluation

ElemQpArg

Argument for requesting functor evaluation at a quadrature point location in an element. Data in the argument:

  • The element containing the quadrature point

  • The quadrature point index, e.g. if there are n quadrature points, we are requesting the evaluation of the ith point

  • The quadrature rule that can be used to initialize the functor on the given element

If functor material properties are functions of nonlinear degrees of freedom, evaluation with this argument will likely result in calls to libMesh FE::reinit.

ElemSideQpArg

Argument for requesting functor evaluation at quadrature point locations on an element side. Data in the argument:

  • The element

  • The element side on which the quadrature points are located

  • The quadrature point index, e.g. if there are n quadrature points, we are requesting the evaluation of the ith point

  • The quadrature rule that can be used to initialize the functor on the given element and side

If functor material properties are functions of nonlinear degrees of freedom, evaluation with this argument will likely result in calls to libMesh FE::reinit.

Functor caching

By default, functor material properties are always (re-)evaluated every time they are called with operator(). However, the base class that FunctorMaterialProperty inherits from, Moose::Functor, has a setCacheClearanceSchedule(const std::set<ExecFlagType> & clearance_schedule) API that allows control of evaluations. Supported values for the clearance_schedule are any combination of EXEC_ALWAYS, EXEC_TIMESTEP_BEGIN, EXEC_LINEAR, and EXEC_NONLINEAR. These will cause cached evaluations of functor (material properties) to be cleared always (in fact not surprisingly in this case we never fill the cache), on timestepSetup, on residualSetup, and on jacobianSetup respectively. If a functor is expected to depend on nonlinear degrees of freedom, then the cache should be cleared on EXEC_LINEAR and EXEC_NONLINEAR (the default EXEC_ALWAYS would obviously also work) in order to achieve a perfect Jacobian. Not surprisingly, if a functor evaluation is cached, then memory usage will increase.

commentnote:Caching Implementations

Functor caching is only currently implemented for ElemQpArg and ElemSideQpArg spatial overloads. This is with the idea that calls to FE::reinit can be fairly expensive whereas for the other spatial argument types, evaluation of the functor (material property) may be relatively inexpensive compared to the memory expense incurred from caching. We may definitely implement caching for other overloads, however, if use cases call for it.

Advanced Topics

Evaluation of Material Properties on Element Faces

MOOSE creates three copies of a non-boundary restricted material for evaluations on quadrature points of elements, element faces on both the current element side and the neighboring element side. The name of the element interior material is the material name from the input file, while the name of the element face material is the material name appended with _face and the name of the neighbor face material is the material name appended with _neighbor. The element material can be identified in a material with its member variable _bnd=false. The other two copies have _bnd=true. The element face material and neighbor face material differentiate with each other by the value of another member variable _neighbor. If a material declares multiple material properties and some of them are not needed on element faces, users can switch off their declaration and evaluation based on member variable _bnd.

Interface Material Objects

MOOSE allows a material to be defined on an internal boundary of a mesh with a specific material type InterfaceMaterial. Material properties declared in interface materials are available on both sides of the boundary. Interface materials allows users to evaluate the properties on element faces based on quantities on both sides of the element face. Interface materials are often used along with InterfaceKernel.

Discrete Material Objects

A "Discrete" Material is an object that may be detached from MOOSE and computed explicitly from other objects. An object inheriting from MaterialPropertyInterface may explicitly call the compute methods of a Material object via the getMaterial method.

The following should be considered when computing Material properties explicitly.

  • It is possible to disable the automatic computation of a Material object by MOOSE by setting the compute=false parameter.

  • When compute=false is set the compute method (computeQpProperties) is not called by MOOSE, instead it must be called explicitly in your application using the computeProperties method that accepts a quadrature point index.

  • When compute=false an additional method should be defined, resetQpProperties, which sets the properties to a safe value (e.g., 0) for later calls to the compute method. Not doing this can lead to erroneous material properties values.

The original intent for this functionality was to enable to ability for material properties to be computed via iteration by another object, as in the following example. First, consider define a material (RecomputeMaterial) that computes the value of a function and its derivative.

and

where v is known value and not a function of p. The following is the compute portion of this object.

void
RecomputeMaterial::computeQpProperties()
{
  Real x = _p[_qp];
  _f[_qp] = x * x - _constant;
  _f_prime[_qp] = 2 * x;
}
(test/src/materials/RecomputeMaterial.C)

Second, define another material (NewtonMaterial) that computes the value of using Newton iterations. This material declares a material property (_p) which is what is solved for by iterating on the material properties containing f and f' from RecomputeMaterial. The _discrete member is a reference to a Material object retrieved with getMaterial.

// MOOSEDOCS_START
void
NewtonMaterial::computeQpProperties()
{
  _p[_qp] = 0.5; // initial guess

  // Newton iteration for find p
  for (unsigned int i = 0; i < _max_iterations; ++i)
  {
    _discrete->computePropertiesAtQp(_qp);
    _p[_qp] -= _f[_qp] / _f_prime[_qp];
    if (std::abs(_f[_qp]) < _tol)
      break;
  }
}
(test/src/materials/NewtonMaterial.C)

To create and use a "Discrete" Material use the following to guide the process.

  1. Create a Material object by, in typical MOOSE fashion, inheriting from the Material object in your own application.

  2. In your input file, set compute=false for this new object.

  3. From within another object (e.g., another Material) that inherits from MaterialPropertyInterface call the getMaterial method. Note, this method returns a reference to a Material object, be sure to include & when calling or declaring the variable.

  4. When needed, call the computeProperties method of the Material being sure to provide the current quadrature point index to the method (_qp in most cases).

Available Objects

Available Actions