https://mooseframework.inl.gov
RichardsFullyUpwindFlux.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 
11 
12 // MOOSE includes
13 #include "Assembly.h"
14 #include "MooseVariable.h"
15 #include "SystemBase.h"
16 
17 #include "libmesh/quadrature.h"
18 
20 
23 {
25  params.addRequiredParam<std::vector<UserObjectName>>(
26  "relperm_UO", "List of names of user objects that define relative permeability");
27  params.addRequiredParam<std::vector<UserObjectName>>(
28  "seff_UO",
29  "List of name of user objects that define effective saturation as a function of "
30  "pressure list");
31  params.addRequiredParam<std::vector<UserObjectName>>(
32  "density_UO", "List of names of user objects that define the fluid density");
33  params.addRequiredParam<UserObjectName>(
34  "richardsVarNames_UO", "The UserObject that holds the list of Richards variable names.");
35  return params;
36 }
37 
39  : Kernel(parameters),
40  _richards_name_UO(getUserObject<RichardsVarNames>("richardsVarNames_UO")),
41  _num_p(_richards_name_UO.num_v()),
42  _pvar(_richards_name_UO.richards_var_num(_var.number())),
43  _density_UO(getUserObjectByName<RichardsDensity>(
44  getParam<std::vector<UserObjectName>>("density_UO")[_pvar])),
45  _seff_UO(
46  getUserObjectByName<RichardsSeff>(getParam<std::vector<UserObjectName>>("seff_UO")[_pvar])),
47  _relperm_UO(getUserObjectByName<RichardsRelPerm>(
48  getParam<std::vector<UserObjectName>>("relperm_UO")[_pvar])),
49  _viscosity(getMaterialProperty<std::vector<Real>>("viscosity")),
50  _flux_no_mob(getMaterialProperty<std::vector<RealVectorValue>>("flux_no_mob")),
51  _dflux_no_mob_dv(
52  getMaterialProperty<std::vector<std::vector<RealVectorValue>>>("dflux_no_mob_dv")),
53  _dflux_no_mob_dgradv(
54  getMaterialProperty<std::vector<std::vector<RealTensorValue>>>("dflux_no_mob_dgradv")),
55  _num_nodes(0),
56  _mobility(0),
57  _dmobility_dv(0)
58 {
59  _ps_at_nodes.resize(_num_p);
60  for (unsigned int pnum = 0; pnum < _num_p; ++pnum)
62 }
63 
64 void
66 {
67  _num_nodes = (*_ps_at_nodes[_pvar]).size();
68 
69  Real p;
70  Real density;
71  Real ddensity_dp;
72  Real seff;
73  std::vector<Real> dseff_dp;
74  Real relperm;
75  Real drelperm_ds;
76  _mobility.resize(_num_nodes);
77  _dmobility_dv.resize(_num_nodes);
78  dseff_dp.resize(_num_p);
79  for (unsigned int nodenum = 0; nodenum < _num_nodes; ++nodenum)
80  {
81  // retrieve and calculate basic things at the node
82  p = (*_ps_at_nodes[_pvar])[nodenum]; // pressure of fluid _pvar at node nodenum
83  density = _density_UO.density(p); // density of fluid _pvar at node nodenum
84  ddensity_dp = _density_UO.ddensity(p); // d(density)/dP
85  seff =
86  _seff_UO.seff(_ps_at_nodes, nodenum); // effective saturation of fluid _pvar at node nodenum
87  _seff_UO.dseff(_ps_at_nodes, nodenum, dseff_dp); // d(seff)/d(P_ph), for ph = 0, ..., _num_p - 1
88  relperm = _relperm_UO.relperm(seff); // relative permeability of fluid _pvar at node nodenum
89  drelperm_ds = _relperm_UO.drelperm(seff); // d(relperm)/dseff
90 
91  // calculate the mobility and its derivatives wrt (variable_ph = porepressure_ph)
92  _mobility[nodenum] =
93  density * relperm / _viscosity[0][_pvar]; // assume viscosity is constant throughout element
94  _dmobility_dv[nodenum].resize(_num_p);
95  for (unsigned int ph = 0; ph < _num_p; ++ph)
96  _dmobility_dv[nodenum][ph] = density * drelperm_ds * dseff_dp[ph] / _viscosity[0][_pvar];
97  _dmobility_dv[nodenum][_pvar] += ddensity_dp * relperm / _viscosity[0][_pvar];
98  }
99 }
100 
101 Real
103 {
104  // note this is not the complete residual:
105  // the upwind mobility parts get added in computeResidual
106  return _grad_test[_i][_qp] * _flux_no_mob[_qp][_pvar];
107 }
108 
109 void
111 {
112  upwind(true, false, 0);
113  return;
114 }
115 
116 void
118 {
119  upwind(false, true, jvar);
120  return;
121 }
122 
123 Real
125 {
126  // this is just the derivative of the flux WITHOUT the upstream mobility terms
127  // Those terms get added in during computeJacobian()
128  return _grad_test[_i][_qp] * (_dflux_no_mob_dgradv[_qp][_pvar][dvar] * _grad_phi[_j][_qp] +
129  _dflux_no_mob_dv[_qp][_pvar][dvar] * _phi[_j][_qp]);
130 }
131 
132 void
133 RichardsFullyUpwindFlux::upwind(bool compute_res, bool compute_jac, unsigned int jvar)
134 {
135  if (compute_jac && _richards_name_UO.not_richards_var(jvar))
136  return;
137 
138  // calculate the mobility values and their derivatives
140 
141  // compute the residual without the mobility terms
142  // Even if we are computing the jacobian we still need this
143  // in order to see which nodes are upwind and which are downwind
145  for (_i = 0; _i < _test.size(); _i++)
146  for (_qp = 0; _qp < _qrule->n_points(); _qp++)
148 
149  const unsigned int dvar = _richards_name_UO.richards_var_num(jvar);
150  if (compute_jac)
151  {
153  for (_i = 0; _i < _test.size(); _i++)
154  for (_j = 0; _j < _phi.size(); _j++)
155  for (_qp = 0; _qp < _qrule->n_points(); _qp++)
156  _local_ke(_i, _j) += _JxW[_qp] * _coord[_qp] * computeQpJac(dvar);
157  }
158 
159  // Now perform the upwinding by multiplying the residuals at the
160  // upstream nodes by their mobilities
161  //
162  // The residual for the kernel is the darcy flux.
163  // This is
164  // R_i = int{mobility*flux_no_mob} = int{mobility*grad(pot)*permeability*grad(test_i)}
165  // for node i. where int is the integral over the element.
166  // However, in fully-upwind, the first step is to take the mobility outside the
167  // integral, which was done in the _local_re calculation above.
168  //
169  // NOTE: Physically _local_re(_i) is a measure of fluid flowing out of node i
170  // If we had left in mobility, it would be exactly the mass flux flowing out of node i.
171  //
172  // This leads to the definition of upwinding:
173  // ***
174  // If _local_re(i) is positive then we use mobility_i. That is
175  // we use the upwind value of mobility.
176  // ***
177  //
178  // The final subtle thing is we must also conserve fluid mass: the total mass
179  // flowing out of node i must be the sum of the masses flowing
180  // into the other nodes.
181 
182  // FIRST:
183  // this is a dirty way of getting around precision loss problems
184  // and problems at steadystate where upwinding oscillates from
185  // node-to-node causing nonconvergence.
186  // I'm not sure if i actually need to do this in moose. Certainly
187  // in cosflow it is necessary.
188  // I will code a better algorithm if necessary
189  bool reached_steady = true;
190  for (unsigned int nodenum = 0; nodenum < _num_nodes; ++nodenum)
191  {
192  if (_local_re(nodenum) >= 1E-20)
193  {
194  reached_steady = false;
195  break;
196  }
197  }
198 
199  // DEFINE VARIABLES USED TO ENSURE MASS CONSERVATION
200  // total mass out - used for mass conservation
201  Real total_mass_out = 0;
202  // total flux in
203  Real total_in = 0;
204 
205  // the following holds derivatives of these
206  std::vector<Real> dtotal_mass_out;
207  std::vector<Real> dtotal_in;
208  if (compute_jac)
209  {
210  dtotal_mass_out.resize(_num_nodes);
211  dtotal_in.resize(_num_nodes);
212  for (unsigned int nodenum = 0; nodenum < _num_nodes; ++nodenum)
213  {
214  dtotal_mass_out[nodenum] = 0;
215  dtotal_in[nodenum] = 0;
216  }
217  }
218 
219  // PERFORM THE UPWINDING!
220  for (unsigned int nodenum = 0; nodenum < _num_nodes; ++nodenum)
221  {
222  if (_local_re(nodenum) >= 0 || reached_steady) // upstream node
223  {
224  if (compute_jac)
225  {
226  for (_j = 0; _j < _phi.size(); _j++)
227  _local_ke(nodenum, _j) *= _mobility[nodenum];
228  _local_ke(nodenum, nodenum) += _dmobility_dv[nodenum][dvar] * _local_re(nodenum);
229  for (_j = 0; _j < _phi.size(); _j++)
230  dtotal_mass_out[_j] += _local_ke(nodenum, _j);
231  }
232  _local_re(nodenum) *= _mobility[nodenum];
233  total_mass_out += _local_re(nodenum);
234  }
235  else
236  {
237  total_in -= _local_re(nodenum); // note the -= means the result is positive
238  if (compute_jac)
239  for (_j = 0; _j < _phi.size(); _j++)
240  dtotal_in[_j] -= _local_ke(nodenum, _j);
241  }
242  }
243 
244  // CONSERVE MASS
245  // proportion the total_mass_out mass to the inflow nodes, weighting by their _local_re values
246  if (!reached_steady)
247  for (unsigned int nodenum = 0; nodenum < _num_nodes; ++nodenum)
248  if (_local_re(nodenum) < 0)
249  {
250  if (compute_jac)
251  for (_j = 0; _j < _phi.size(); _j++)
252  {
253  _local_ke(nodenum, _j) *= total_mass_out / total_in;
254  _local_ke(nodenum, _j) +=
255  _local_re(nodenum) * (dtotal_mass_out[_j] / total_in -
256  dtotal_in[_j] * total_mass_out / total_in / total_in);
257  }
258  _local_re(nodenum) *= total_mass_out / total_in;
259  }
260 
261  // ADD RESULTS TO RESIDUAL OR JACOBIAN
262  if (compute_res)
263  {
265  if (_has_save_in)
266  for (unsigned int i = 0; i < _save_in.size(); i++)
267  _save_in[i]->sys().solution().add_vector(_local_re, _save_in[i]->dofIndices());
268  }
269 
270  if (compute_jac)
271  {
273  if (_has_diag_save_in && dvar == _pvar)
274  {
275  const unsigned int rows = _local_ke.m();
276  DenseVector<Number> diag(rows);
277  for (unsigned int i = 0; i < rows; i++)
278  diag(i) = _local_ke(i, i);
279 
280  for (unsigned int i = 0; i < _diag_save_in.size(); i++)
281  _diag_save_in[i]->sys().solution().add_vector(diag, _diag_save_in[i]->dofIndices());
282  }
283  }
284 }
virtual void dseff(std::vector< const VariableValue *> p, unsigned int qp, std::vector< Real > &result) const =0
derivative(s) of effective saturation as a function of porepressure(s) at given quadpoint of the elem...
static InputParameters validParams()
unsigned int _num_p
number of richards variables
void accumulateTaggedLocalResidual()
virtual Real drelperm(Real seff) const =0
derivative of relative permeability wrt effective saturation This must be over-ridden in your derived...
MooseVariable & _var
std::vector< MooseVariableFEBase *> _save_in
virtual Real ddensity(Real p) const =0
derivative of fluid density wrt porepressure This must be over-ridden in derived classes to provide a...
const MooseArray< Real > & _JxW
unsigned int number() const
const MaterialProperty< std::vector< std::vector< RealVectorValue > > > & _dflux_no_mob_dv
d(_flux_no_mob)/d(variable)
Base class for effective saturation as a function of porepressure(s) The functions seff...
Definition: RichardsSeff.h:18
static InputParameters validParams()
Base class for Richards relative permeability classes that provide relative permeability as a functio...
const VariablePhiGradient & _grad_phi
std::vector< const VariableValue * > _ps_at_nodes
Holds the values of pressures at all the nodes of the element Eg: _ps_at_nodes[_pvar] is a pointer to...
const VariableValue * nodal_var(unsigned int richards_var_num) const
The nodal variable values for the given richards_var_num To extract a the value of pressure variable ...
std::vector< std::vector< Real > > _dmobility_dv
d(_mobility)/d(variable_ph) (variable_ph is the variable for phase=ph) These are used in the jacobian...
static const std::string density
Definition: NS.h:33
virtual Real computeQpResidual() override
Note that this is not the complete residual for the quadpoint In computeResidual we sum over the quad...
bool not_richards_var(unsigned int moose_var_num) const
returns true if moose_var_num is not a richards var
unsigned int m() const
This holds maps between pressure_var or pressure_var, sat_var used in RichardsMaterial and kernels...
bool _has_diag_save_in
registerMooseObject("RichardsApp", RichardsFullyUpwindFlux)
DenseMatrix< Number > _local_ke
void addRequiredParam(const std::string &name, const std::string &doc_string)
virtual Real density(Real p) const =0
fluid density as a function of porepressure This must be over-ridden in derived classes to provide an...
TensorValue< Real > RealTensorValue
const VariableTestValue & _test
const RichardsRelPerm & _relperm_UO
user object defining the relative permeability
std::vector< MooseVariableFEBase *> _diag_save_in
void upwind(bool compute_res, bool compute_jac, unsigned int jvar)
Do the upwinding for both the residual and jacobian I&#39;ve put both calculations in the same code to tr...
const QBase *const & _qrule
virtual Real seff(std::vector< const VariableValue *> p, unsigned int qp) const =0
effective saturation as a function of porepressure(s) at given quadpoint of the element ...
bool _has_save_in
unsigned int _i
virtual Real relperm(Real seff) const =0
relative permeability as a function of effective saturation This must be over-ridden in your derived ...
void accumulateTaggedLocalMatrix()
const MaterialProperty< std::vector< RealVectorValue > > & _flux_no_mob
permeability*(grad(pressure) - density*gravity) (a vector of these in the multiphase case) ...
const RichardsSeff & _seff_UO
user object defining the effective saturation
RichardsFullyUpwindFlux(const InputParameters &parameters)
const MooseArray< Real > & _coord
Assembly & _assembly
unsigned int _j
unsigned int _pvar
the index of this variable in the list of Richards variables held by _richards_name_UO.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void prepareNodalValues()
calculates the nodal values of mobility, and derivatives thereof
unsigned int richards_var_num(unsigned int moose_var_num) const
the richards variable number
const VariableTestGradient & _grad_test
DenseVector< Number > _local_re
const MaterialProperty< std::vector< std::vector< RealTensorValue > > > & _dflux_no_mob_dgradv
d(_flux_no_mob)/d(grad(variable))
Real computeQpJac(unsigned int dvar)
the derivative of the flux without the upstream mobility terms
unsigned int _num_nodes
number of nodes in this element
const RichardsVarNames & _richards_name_UO
holds info regarding the names of the Richards variables and methods for extracting values of these v...
const MaterialProperty< std::vector< Real > > & _viscosity
viscosities
Base class for fluid density as a function of porepressure The functions density, ddensity and d2dens...
This is a fully upwinded version of RichardsFlux.
void prepareVectorTag(Assembly &assembly, unsigned int ivar)
virtual void computeResidual() override
This simply calls upwind.
void prepareMatrixTag(Assembly &assembly, unsigned int ivar, unsigned int jvar)
const RichardsDensity & _density_UO
user object defining the density
const VariablePhiValue & _phi
unsigned int _qp
std::vector< Real > _mobility
nodal values of mobility = density*relperm/viscosity These are multiplied by _flux_no_mob to give the...
virtual void computeOffDiagJacobian(unsigned int jvar) override
this simply calls upwind