www.mooseframework.org
PorousFlowFluidMass.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://www.mooseframework.org
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 "PorousFlowFluidMass.h"
11 
12 #include "MooseVariable.h"
13 
14 #include "libmesh/quadrature.h"
15 
18 
19 template <bool is_ad>
22 {
24  params.addParam<unsigned int>(
25  "fluid_component",
26  0,
27  "The index corresponding to the fluid component that this Postprocessor acts on");
28  params.addRequiredParam<UserObjectName>(
29  "PorousFlowDictator", "The UserObject that holds the list of PorousFlow variable names.");
30  params.addParam<std::vector<unsigned int>>("phase",
31  {},
32  "The index of the fluid phase that this "
33  "Postprocessor is restricted to. Multiple "
34  "indices can be entered");
35  params.addRangeCheckedParam<Real>("saturation_threshold",
36  1.0,
37  "saturation_threshold >= 0 & saturation_threshold <= 1",
38  "The saturation threshold below which the mass is calculated "
39  "for a specific phase. Default is 1.0. Note: only one "
40  "phase_index can be entered");
41  params.addParam<unsigned int>("kernel_variable_number",
42  0,
43  "The PorousFlow variable number (according to the dictator) of "
44  "the fluid-mass kernel. This is required only in the unusual "
45  "situation where a variety of different finite-element "
46  "interpolation schemes are employed in the simulation");
47  params.addParam<std::string>(
48  "base_name",
49  "For non-mechanically-coupled systems with no TensorMechanics strain calculators, base_name "
50  "need not be set. For mechanically-coupled systems, base_name should be the same base_name "
51  "as given to the TensorMechanics object that computes strain, so that this Postprocessor can "
52  "correctly account for changes in mesh volume. For non-mechanically-coupled systems, "
53  "base_name should not be the base_name of any TensorMechanics strain calculators.");
54  params.set<bool>("use_displaced_mesh") = false;
55  params.suppressParameter<bool>("use_displaced_mesh");
56  params.addClassDescription("Calculates the mass of a fluid component in a region");
57  return params;
58 }
59 
60 template <bool is_ad>
62  : ElementIntegralPostprocessor(parameters),
63 
64  _dictator(getUserObject<PorousFlowDictator>("PorousFlowDictator")),
65  _fluid_component(getParam<unsigned int>("fluid_component")),
66  _phase_index(getParam<std::vector<unsigned int>>("phase")),
67  _base_name(isParamValid("base_name") ? getParam<std::string>("base_name") + "_" : ""),
68  _has_total_strain(hasMaterialProperty<RankTwoTensor>(_base_name + "total_strain")),
69  _total_strain(_has_total_strain
70  ? &getMaterialProperty<RankTwoTensor>(_base_name + "total_strain")
71  : nullptr),
72  _porosity(getGenericMaterialProperty<Real, is_ad>("PorousFlow_porosity_nodal")),
73  _fluid_density(getGenericMaterialProperty<std::vector<Real>, is_ad>(
74  "PorousFlow_fluid_phase_density_nodal")),
75  _fluid_saturation(
76  getGenericMaterialProperty<std::vector<Real>, is_ad>("PorousFlow_saturation_nodal")),
77  _mass_fraction(getGenericMaterialProperty<std::vector<std::vector<Real>>, is_ad>(
78  "PorousFlow_mass_frac_nodal")),
79  _saturation_threshold(getParam<Real>("saturation_threshold")),
80  _var(getParam<unsigned>("kernel_variable_number") < _dictator.numVariables()
81  ? &_fe_problem.getStandardVariable(
82  _tid,
83  _dictator
84  .getCoupledStandardMooseVars()[getParam<unsigned>("kernel_variable_number")]
85  ->name())
86  : nullptr)
87 {
88  const unsigned int num_phases = _dictator.numPhases();
89  const unsigned int num_components = _dictator.numComponents();
90 
91  // Check that the number of components entered is not greater than the total number of components
92  if (_fluid_component >= num_components)
93  paramError(
94  "fluid_component",
95  "The Dictator proclaims that the number of components in this simulation is ",
96  num_components,
97  " whereas you have used a component index of ",
99  ". Remember that indexing starts at 0. The Dictator does not take such mistakes lightly.");
100 
101  // Check that the number of phases entered is not more than the total possible phases
102  if (_phase_index.size() > num_phases)
103  paramError("phase",
104  "The Dictator decrees that the number of phases in this simulation is ",
105  num_phases,
106  " but you have entered ",
107  _phase_index.size(),
108  " phases.");
109 
110  // Check that kernel_variable_number is OK
111  if (getParam<unsigned>("kernel_variable_number") >= _dictator.numVariables())
112  paramError("kernel_variable_number",
113  "The Dictator pronounces that the number of PorousFlow variables is ",
115  ", however you have used ",
116  getParam<unsigned>("kernel_variable_number"),
117  ". This is an error");
118 
119  // Now that we know kernel_variable_number is OK, _var must be OK,
120  // so ensure that reinit is called on _var:
122 
123  // Also check that the phase indices entered are not greater than the number of phases
124  // to avoid a segfault. Note that the input parser takes care of negative inputs so we
125  // don't need to guard against them
126  if (!_phase_index.empty())
127  {
128  unsigned int max_phase_num = *std::max_element(_phase_index.begin(), _phase_index.end());
129  if (max_phase_num > num_phases - 1)
130  paramError("phase",
131  "The Dictator proclaims that the phase index ",
132  max_phase_num,
133  " is greater than the largest phase index possible, which is ",
134  num_phases - 1);
135  }
136 
137  // Using saturation_threshold only makes sense for a specific phase_index
138  if (_saturation_threshold < 1.0 && _phase_index.size() != 1)
139  paramError("saturation_threshold",
140  "A single phase_index must be entered when prescribing a saturation_threshold");
141 
142  // If _phase_index is empty, create vector of all phase numbers to calculate mass over all phases
143  if (_phase_index.empty())
144  for (unsigned int i = 0; i < num_phases; ++i)
145  _phase_index.push_back(i);
146 }
147 
148 template <bool is_ad>
149 Real
151 {
152  Real sum = 0;
153 
154  // The use of _test in the loops below mean that the
155  // integral is exactly the same as the one computed
156  // by the PorousFlowMassTimeDerivative Kernel. Because that
157  // Kernel is lumped, this Postprocessor also needs to
158  // be lumped. Hence the use of the "nodal" Material
159  // Properties
160  const VariableTestValue & test = _var->phi();
161 
162  for (unsigned node = 0; node < test.size(); ++node)
163  {
164  Real nodal_volume = 0.0;
165  for (_qp = 0; _qp < _qrule->n_points(); ++_qp)
166  {
167  const Real n_v = _JxW[_qp] * _coord[_qp] * test[node][_qp];
168  if (_has_total_strain)
169  nodal_volume += n_v * (1.0 + (*_total_strain)[_qp].trace());
170  else
171  nodal_volume += n_v;
172  }
173 
174  Real mass = 0.0;
175  for (auto ph : _phase_index)
176  {
177  if (MetaPhysicL::raw_value(_fluid_saturation[node][ph]) <= _saturation_threshold)
178  mass += MetaPhysicL::raw_value(_fluid_density[node][ph] * _fluid_saturation[node][ph] *
179  _mass_fraction[node][ph][_fluid_component]);
180  }
181  sum += nodal_volume * MetaPhysicL::raw_value(_porosity[node]) * mass;
182  }
183 
184  return sum;
185 }
186 
187 template <bool is_ad>
188 Real
190 {
191  return 0.0;
192 }
193 
194 template class PorousFlowFluidMassTempl<false>;
195 template class PorousFlowFluidMassTempl<true>;
virtual Real computeIntegral() override
virtual Real computeQpIntegral() override
const unsigned int _fluid_component
The fluid component index that this Postprocessor applies to.
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
Postprocessor produces the mass of a given fluid component in a region.
T & set(const std::string &name, bool quiet_mode=false)
unsigned int numComponents() const
The number of fluid components.
MooseVariable *const _var
The variable for the corresponding PorousFlowMassTimeDerivative Kernel: this provides test functions...
auto raw_value(const Eigen::Map< T > &in)
void addRequiredParam(const std::string &name, const std::string &doc_string)
PorousFlowFluidMassTempl(const InputParameters &parameters)
void suppressParameter(const std::string &name)
registerMooseObject("PorousFlowApp", PorousFlowFluidMass)
static InputParameters validParams()
std::vector< unsigned int > _phase_index
The phase indices that this Postprocessor is restricted to.
OutputTools< Real >::VariableTestValue VariableTestValue
const std::string name
Definition: Setup.h:20
void paramError(const std::string &param, Args... args) const
const PorousFlowDictator & _dictator
PorousFlowDictator UserObject.
unsigned int numPhases() const
The number of fluid phases.
unsigned int numVariables() const
The number of PorousFlow variables.
const Real _saturation_threshold
Saturation threshold - only fluid mass at saturations below this are calculated.
void addMooseVariableDependency(MooseVariableFieldBase *var)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
This holds maps between the nonlinear variables used in a PorousFlow simulation and the variable numb...
void addClassDescription(const std::string &doc_string)
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
static InputParameters validParams()
void ErrorVector unsigned int