https://mooseframework.inl.gov
Loading...
Searching...
No Matches
PorousFlowMaterial.C
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
10#include "PorousFlowMaterial.h"
11
13
14#include "libmesh/quadrature.h"
15#include "libmesh/fe_interface.h"
16
17#include <limits>
18
21{
23 params.addRequiredParam<UserObjectName>(
24 "PorousFlowDictator", "The UserObject that holds the list of PorousFlow variable names");
25 params.addParam<bool>(
26 "at_nodes", false, "Evaluate Material properties at nodes instead of quadpoints");
27 params.addPrivateParam<std::string>("pf_material_type", "pf_material");
28 params.addClassDescription("This generalises MOOSE's Material class to allow for Materials that "
29 "hold information related to the nodes in the finite element");
30
31 // Needed due to the custom tomfoolery going on with nodal material sizing in
32 // initStatefulProperties()
33 params.set<bool>("_force_stateful_init") = true;
34
35 return params;
36}
37
39 : Material(parameters),
40 _nodal_material(getParam<bool>("at_nodes")),
41 _dictator(getUserObject<PorousFlowDictator>("PorousFlowDictator")),
42 _pressure_variable_name("pressure_variable"),
43 _saturation_variable_name("saturation_variable"),
44 _temperature_variable_name("temperature_variable"),
45 _mass_fraction_variable_name("mass_fraction_variable")
46{
47}
48
49void
50PorousFlowMaterial::checkNodalVariables(const std::vector<std::string> & coupled_var_params) const
51{
52 // When this is a nodal Material, the named parameters are read with coupledGenericDofValue, ie
53 // once per node of the current element, so the variables supplied to them must be nodal
54 // (Lagrange). For an element-local variable there is no nodal value to read, and the loop over
55 // nodes runs off the end of its value array.
56 //
57 // Only the named parameters are checked, because a Material's other coupled variables may
58 // legitimately be non-nodal:
59 // - those read behind an _is_*_nodal check, which falls back to quadpoint values (eg xnacl in
60 // PorousFlowFluidState, x in PorousFlowMultiComponentFluid, the mass fractions in
61 // PorousFlowMassFraction);
62 // - a PorousFlow variable that is a purely local unknown and never reaches a nodal Material at
63 // all (eg the adsorbed concentration in the desorption tests, which has no flux term).
64 for (const auto & param : coupled_var_params)
65 {
66 if (!isCoupled(param))
67 continue;
68 for (const auto i : make_range(coupledComponents(param)))
69 {
70 const auto * const var = getFieldVar(param, i);
71 if (!var->isNodal())
72 mooseError("This Material has at_nodes = true, so it reads '",
73 param,
74 "' at the nodes, but the variable supplied to it ('",
75 var->name(),
76 "') is not a nodal (Lagrange) variable. A variable read at the nodes must be "
77 "LAGRANGE. Other coupled variables of a nodal Material may well be "
78 "element-local: those read at the quadpoints instead, such as a reference "
79 "temperature or mineral concentration, accept a CONSTANT MONOMIAL.");
80 }
81 }
82}
83
84void
86{
87 if (!_nodal_material)
88 return;
89
90 // Tell the Dictator the FE type of every variable this Material reads at the
91 // nodes, so that a single node indexing can be shared by all nodal Materials.
92 // Elemental coupled variables are read by quadpoint instead (see the isNodal
93 // guard that should exist in the derived classes) and so do not take part.
94 for (const auto * const var : getCoupledMooseVars())
95 if (var->isNodal())
97
100 if (!storage.hasStatefulProperties())
101 return;
102
103 auto & stateful_prop_id_to_prop_id = storage.statefulProps();
104 for (const auto i : index_range(stateful_prop_id_to_prop_id))
105 {
106 const auto prop_id = stateful_prop_id_to_prop_id[i];
107 if (_supplied_prop_ids.count(prop_id))
108 _supplied_old_prop_ids.push_back(i);
109 }
110}
111
112void
114{
115 if (_nodal_material)
116 {
117 // size the properties to max(number_of_nodes, number_of_quadpoints)
119
120 // compute the values for each node that carries a degree of freedom
122 }
123 else
125}
126
127void
129{
130 const unsigned int numnodes = nodalDofCount();
131
132 // compute the values for all nodes that carry a degree of freedom
133 for (_qp = 0; _qp < numnodes; ++_qp)
135
136 // If number_of_nodes < number_of_quadpoints, the remaining values in the
137 // material data array are zero (for scalars) and empty (for vectors).
138 // Unfortunately, this can cause issues with adaptivity, where the empty
139 // value can be transferred to a node in a child element. This can lead
140 // to a segfault when accessing stateful properties, see #14428.
141 // To prevent this, we copy the last node value to the empty array positions.
142 if (numnodes < _qrule->n_points())
143 {
145
146 // Copy from qp = nodalDofCount() - 1 to qp = _qrule->n_points() - 1
147 for (const auto & prop_id : _supplied_prop_ids)
148 for (unsigned int qp = numnodes; qp < _qrule->n_points(); ++qp)
149 props[prop_id].qpCopy(qp, props[prop_id], numnodes - 1);
150 }
151}
152
153void
155{
156 if (_nodal_material)
157 {
158 // size the properties to max(number_of_nodes, number_of_quadpoints)
160
162 }
163 else
165}
166
167void
169{
170 /*
171 * For nodal materials, the Properties should be sized as the maximum of
172 * the number of nodes and the number of quadpoints.
173 * We only actually need "number of nodes" pieces of information, which are
174 * computed by computeProperties(), so the n_points - _current_elem->n_nodes()
175 * elements at the end of the std::vector will always be zero, but they
176 * are needed because MOOSE does copy operations (etc) that assumes that
177 * the std::vector is sized to number of quadpoints.
178 *
179 * On boundary materials, the number of nodes may be larger than the number of
180 * qps on the face of the element, in which case the remaining entries in the
181 * material properties storage will be zero.
182 *
183 * \author lindsayad: MooseArray currently has the unfortunate side effect that if your new size
184 * is greater than the current size, then we clear the whole data structure. Consequently this
185 * call has the potential to clear material property evaluations done earlier in the material
186 * dependency chain. So instead we selectively resize just our own properties and not everyone's
187 */
188 // _material_data.resize(std::max(_current_elem->n_nodes(), _qrule->n_points()));
189
190 const auto new_size = std::max(_current_elem->n_nodes(), _qrule->n_points());
192
193 auto & props = _material_data.props();
194 for (const auto prop_id : _supplied_prop_ids)
195 props[prop_id].resize(new_size);
196
197 for (const auto state : storage.statefulIndexRange())
198 for (const auto prop_id : _supplied_old_prop_ids)
199 if (_material_data.props(state).hasValue(prop_id))
200 _material_data.props(state)[prop_id].resize(new_size);
201}
202
203const VariableValue &
204PorousFlowMaterial::nodalOrQpValue(const std::string & var_name, unsigned int comp)
205{
206 const bool is_nodal = isCoupled(var_name) ? getFieldVar(var_name, comp)->isNodal() : false;
207 return (_nodal_material && is_nodal) ? coupledDofValues(var_name, comp)
208 : coupledValue(var_name, comp);
209}
210
211unsigned int
213{
214 // If no nodal Material reads any variable at the nodes then there is no short
215 // array to overrun, and every node is visited as it always has been
216 const auto & fe_type = _dictator.shareNodalVariableFEType();
217 if (!fe_type)
218 return _current_elem->n_nodes();
219
220 const auto num_dofs = libMesh::FEInterface::n_dofs(*fe_type, _current_elem);
221
222 mooseAssert(num_dofs <= _current_elem->n_nodes(),
223 "A nodal Material would visit " << num_dofs << " nodes of an element that has only "
224 << _current_elem->n_nodes());
225
226 // Everything here rests on libMesh numbering element nodes vertices-first and
227 // numbering a LAGRANGE variable's degrees of freedom to match, so that degree
228 // of freedom i lives at node i and looping 0 .. num_dofs - 1 visits exactly
229 // the nodes that carry one. That is a convention rather than something the
230 // code enforces, and an unchecked convention of precisely this kind produced
231 // the out-of-bounds read this count exists to prevent, so check it.
232 mooseAssert(fe_type->family != libMesh::LAGRANGE || fe_type->order != libMesh::FIRST ||
233 num_dofs == _current_elem->n_vertices(),
234 "First-order LAGRANGE has "
235 << num_dofs << " degrees of freedom on an element with "
236 << _current_elem->n_vertices()
237 << " vertices, so the assumption that they sit on the vertices, in order, does "
238 "not hold for this element type");
239
240 return num_dofs;
241}
242
243unsigned
244PorousFlowMaterial::nearestQP(unsigned nodenum) const
245{
246 unsigned nearest_qp = 0;
247 Real smallest_dist = std::numeric_limits<Real>::max();
248 for (const auto qp : make_range(_qrule->n_points()))
249 {
250 const Real this_dist = (_current_elem->point(nodenum) - _q_point[qp]).norm();
251 if (this_dist < smallest_dist)
252 {
253 nearest_qp = qp;
254 smallest_dist = this_dist;
255 }
256 }
257 return nearest_qp;
258}
virtual const VariableValue & coupledValue(const std::string &var_name, unsigned int comp=0) const
const std::vector< MooseVariableFieldBase * > & getCoupledMooseVars() const
const MooseVariableFieldBase * getFieldVar(const std::string &var_name, unsigned int comp) const
unsigned int coupledComponents(const std::string &var_name) const
virtual bool isCoupled(const std::string &var_name, unsigned int i=0) const
virtual const VariableValue & coupledDofValues(const std::string &var_name, unsigned int comp=0) const
void addRequiredParam(const std::string &name, const std::string &doc_string)
void addPrivateParam(const std::string &name, const T &value)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void addClassDescription(const std::string &doc_string)
T & set(const std::string &name, bool quiet_mode=false)
virtual void computeQpProperties()
virtual void initStatefulProperties(const unsigned int n_points)
unsigned int _qp
std::set< unsigned int > _supplied_prop_ids
void onlyResizeIfSmaller(bool flag)
const MaterialPropertyStorage & getMaterialPropertyStorage() const
const MaterialProperties & props(const unsigned int state=0) const
void resize(const std::size_t size, const WriteKey)
MaterialData & _material_data
const std::vector< unsigned int > & statefulProps() const
virtual void computeProperties() override
static InputParameters validParams()
const Elem *const & _current_elem
const QBase *const & _qrule
const MooseArray< Point > & _q_point
void mooseError(Args &&... args) const
virtual bool isNodal() const
This holds maps between the nonlinear variables used in a PorousFlow simulation and the variable numb...
void registerNodalVariable(const VariableName &var_name) const
Register a variable that a nodal Material reads by degree of freedom.
const std::optional< libMesh::FEType > & shareNodalVariableFEType() const
The FE type shared by every variable that nodal Materials read by degree of freedom,...
std::vector< unsigned int > _supplied_old_prop_ids
stateful material property ids that this material supplies
virtual void initStatefulProperties(unsigned int n_points) override
Correctly sizes nodal materials, then initialises using Material::initStatefulProperties.
static InputParameters validParams()
virtual void initialSetup() override
virtual void computeProperties() override
Correctly sizes nodal materials, then computes using Material::computeProperties.
unsigned nearestQP(unsigned nodenum) const
Find the nearest quadpoint to the node labelled by nodenum in the current element.
void computeNodalProperties()
Compute the material properties at each node, and if the number of nodes is less than the number of q...
const VariableValue & nodalOrQpValue(const std::string &var_name, unsigned int comp=0)
The values of a coupled variable for this Material to read: its degree-of-freedom values if this is a...
void sizeNodalProperties()
Resizes properties to be equal to max(number of nodes, number of quadpoints) in the current element.
PorousFlowMaterial(const InputParameters &parameters)
unsigned int nodalDofCount() const
The number of nodes of the current element that carry a degree of freedom of the variables that nodal...
const PorousFlowDictator & _dictator
The variable names UserObject for the PorousFlow variables.
const bool _nodal_material
Whether the derived class holds nodal values.
void checkNodalVariables(const std::vector< std::string > &coupled_var_params) const
Error if this is a nodal Material but a variable supplied to one of the named coupled-variable parame...
bool hasValue(const std::size_t i) const
static unsigned int n_dofs(const unsigned int dim, const FEType &fe_t, const ElemType t)
VariableValueTempl< false > VariableValue
const dof_id_type n_nodes