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