https://mooseframework.inl.gov
PorousFlowHysteresisOrder.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 #include "Conversion.h"
12 
14 
17 {
19  params.addPrivateParam<std::string>("pf_material_type", "hysteresis_order");
20  params.addParam<unsigned>("liquid_phase", 0, "The phase number of the liquid phase");
21  params.addParam<unsigned>(
22  "initial_order",
23  0,
24  "The initial order. 0 means the simulation will begin on the primary drying curve. 1 means "
25  "the simulation will begin on the first-order wetting curve. 2 means the simulation will "
26  "begin on the second-order drying curve, etc.");
27  params.addParam<std::vector<Real>>(
28  "previous_turning_points",
29  std::vector<Real>(),
30  "The turning points (liquid saturation values) at occured prior to the simulation. There "
31  "must be exactly initial_order of these values. The first value is the liquid saturation at "
32  "the transition from primary-drying to first-order-wetting; the second value is the liquid "
33  "saturation at the transition from first-order-wetting to second-order-drying; and so on. "
34  "You must ensure that your initial saturation field is commensurate with the "
35  "previous_turning_points.");
36  params.addClassDescription("Computes hysteresis order for use in hysteretic capillary pressures "
37  "and relative permeabilities");
38  return params;
39 }
40 
43  _liquid_ph_num(getParam<unsigned>("liquid_phase")),
44  _liquid_phase(Moose::stringify(_liquid_ph_num)),
45  _initial_order(getParam<unsigned>("initial_order")),
46  _previous_turning_points(getParam<std::vector<Real>>("previous_turning_points")),
47  _sat_old(_nodal_material
48  ? getMaterialPropertyOld<std::vector<Real>>("PorousFlow_saturation_nodal")
49  : getMaterialPropertyOld<std::vector<Real>>("PorousFlow_saturation_qp")),
50  _sat_older(_nodal_material
51  ? getMaterialPropertyOlder<std::vector<Real>>("PorousFlow_saturation_nodal")
52  : getMaterialPropertyOlder<std::vector<Real>>("PorousFlow_saturation_qp")),
53  _hys_order(_nodal_material ? declareProperty<unsigned>("PorousFlow_hysteresis_order_nodal")
54  : declareProperty<unsigned>("PorousFlow_hysteresis_order_qp")),
55  _hys_order_old(_nodal_material
56  ? getMaterialPropertyOld<unsigned>("PorousFlow_hysteresis_order_nodal")
57  : getMaterialPropertyOld<unsigned>("PorousFlow_hysteresis_order_qp")),
58  _hys_sat_tps(_nodal_material
59  ? declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
60  "PorousFlow_hysteresis_saturation_tps_nodal")
61  : declareProperty<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
62  "PorousFlow_hysteresis_saturation_tps_qp")),
63  _hys_sat_tps_old(
64  _nodal_material
65  ? getMaterialPropertyOld<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
66  "PorousFlow_hysteresis_saturation_tps_nodal")
67  : getMaterialPropertyOld<std::array<Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER>>(
68  "PorousFlow_hysteresis_saturation_tps_qp"))
69 {
70  if (_liquid_ph_num >= _dictator.numPhases())
71  paramError("liquid_phase",
72  "The Dictator proclaims that the number of fluid phases is ",
73  _dictator.numPhases(),
74  " while you have foolishly entered ",
76  " for your liquid phase number. Remember that indexing starts at 0. Be aware that "
77  "the Dictator does not tolerate mistakes.");
79  paramError("initial_order",
80  "The initial order must be less than the max order, which is hard-coded to ",
83  paramError("previous_turning_points",
84  "initial_order is ",
86  " so there must be this many previous_turning_points");
87  for (const auto & tp : _previous_turning_points)
88  if (tp < 0.0 || tp > 1.0)
89  paramError("previous_turning_points",
90  "Each previous_turning_point must lie in the range [0.0, 1.0]");
91  // check that the previous_turning_points are ordered correctly
92  Real previous_tp = 1.0;
93  Real previous_jump = std::numeric_limits<Real>::max();
94  bool drying = true;
95  for (unsigned i = 0; i < _initial_order; ++i)
96  {
97  const Real this_jump = _previous_turning_points[i] - previous_tp;
98  if (drying)
99  {
100  if (this_jump >= 0)
101  paramError("previous_turning_points",
102  "The previous_turning_points vector, {a, b, c, ...}, must be obey the following "
103  "order: a < 1; b > a; a < c < b, etc");
104  }
105  else
106  {
107  if (this_jump <= 0)
108  paramError("previous_turning_points",
109  "The previous_turning_points vector, {a, b, c, ...}, must be obey the following "
110  "order: a < 1; b > a; a < c < b, etc");
111  }
112  if (std::abs(this_jump) >= previous_jump)
113  paramError("previous_turning_points",
114  "The previous_turning_points vector, {a, b, c, ...}, must be obey the following "
115  "order: a < 1; b > a; a < c < b, etc");
116  drying = !drying;
117  previous_tp = _previous_turning_points[i];
118  previous_jump = std::abs(this_jump);
119  }
120 }
121 
122 void
124 {
125  _hys_order[_qp] = _initial_order;
126  for (unsigned i = 0; i < _initial_order; ++i)
127  _hys_sat_tps[_qp].at(i) = _previous_turning_points[i];
128 }
129 
130 void
132 {
133  // NOTE to readers: the computed properties depend on old and older quantities only. Hence, it
134  // would be nice if we could compute them only at the start of each timestep instead of every
135  // nonlinear iteration
136 
137  // For finite elements involved in boundary-condition calculations, number of nodes > number of
138  // quadpoints. Material values on these elements are not computed during initial setup, which
139  // means that for timestep = 1, _sat_older.size() = number of quadpoints. Since
140  // computeQpProperties() is computed with 0 <= _qp < number of nodes (for nodal materials), this
141  // results in an out-of-bounds error on _sat_older. PorousFlow's implementation of hysteresis
142  // lags one timestep behind, and assumes that _sat_older = _sat_old at timestep = 1 anyway, so
143  // just use it:
144  const MaterialProperty<std::vector<Real>> & sat_older =
145  (_nodal_material && _sat_old.size() != _sat_older.size()) ? _sat_old : _sat_older;
146 
147  // whether saturation has been reducing ("drying" or "draining"):
148  const bool drying = (_hys_order_old[_qp] % 2 == 0);
149  // whether have been drying but are now increasing saturation ("wetting" or "imbibing"):
150  const bool dry2wet = drying && (_sat_old[_qp][_liquid_ph_num] > sat_older[_qp][_liquid_ph_num]);
151  // whether have been wetting but are now decreasing saturation:
152  const bool wet2dry = !drying && (_sat_old[_qp][_liquid_ph_num] < sat_older[_qp][_liquid_ph_num]);
153  if ((dry2wet || wet2dry) && _hys_order_old[_qp] < PorousFlowConstants::MAX_HYSTERESIS_ORDER)
154  {
155  _hys_order[_qp] = _hys_order_old[_qp] + 1;
156  _hys_sat_tps[_qp] = _hys_sat_tps_old[_qp];
157  _hys_sat_tps[_qp].at(_hys_order_old[_qp]) = sat_older[_qp][_liquid_ph_num];
158  }
159  else
160  {
161  // no change in wetting/drying direction
162  _hys_order[_qp] = _hys_order_old[_qp];
163  _hys_sat_tps[_qp] = _hys_sat_tps_old[_qp];
164  }
165  if (_sat_old[_qp][_liquid_ph_num] >= 1.0)
166  _hys_order[_qp] = 0; // assume the system gets back to the original drying curve
167 
168  // reduce order by 1 if _hys_order = max and saturation exceeds bounds of last turning-point
170  {
171  // the maxiumum-order curve is a drying curve, and the saturation exceeds the previous turning
172  // point:
173  const bool drying_and_high_sat =
174  (_hys_order[_qp] % 2 == 0) &&
175  (_sat_old[_qp][_liquid_ph_num] >= _hys_sat_tps[_qp].at(_hys_order[_qp] - 1));
176  // the maxiumum-order curve is a wetting curve, and the saturation is less than the previous
177  // turning point:
178  const bool wetting_and_low_sat =
179  (_hys_order[_qp] % 2 == 1) &&
180  (_sat_old[_qp][_liquid_ph_num] <= _hys_sat_tps[_qp].at(_hys_order[_qp] - 1));
181  if (drying_and_high_sat || wetting_and_low_sat)
182  _hys_order[_qp] -= 1;
183  }
184 
185  // reduce order by 2 if saturation exceeds the bounds of the second-to-last turning-point
186  // encountered
187  bool can_reduce_order = (_hys_order[_qp] > 1);
188  while (can_reduce_order)
189  {
190  can_reduce_order = false;
191  // are drying and the saturation is <= a previously-encountered turning point:
192  const bool drying_and_low_sat =
193  (_hys_order[_qp] % 2 == 0) &&
194  (_sat_old[_qp][_liquid_ph_num] <= _hys_sat_tps[_qp].at(_hys_order[_qp] - 2));
195  // are wetting and the saturation is >= a previously-encountered turning point:
196  const bool wetting_and_high_sat =
197  (_hys_order[_qp] % 2 == 1) &&
198  (_sat_old[_qp][_liquid_ph_num] >= _hys_sat_tps[_qp].at(_hys_order[_qp] - 2));
199  if (drying_and_low_sat || wetting_and_high_sat)
200  {
201  _hys_order[_qp] -= 2;
202  can_reduce_order = (_hys_order[_qp] > 1);
203  }
204  }
205 }
PorousFlowMaterial is the base class for all PorousFlow Materials It allows users to specify that the...
const MaterialProperty< std::array< Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER > > & _hys_sat_tps_old
Old value of recorded saturation values at the turning points.
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void addPrivateParam(const std::string &name, const T &value)
Computes the hysteresis order for use by the hysteretic capillary pressure and relative-permeability ...
static InputParameters validParams()
MaterialProperty< std::array< Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER > > & _hys_sat_tps
Recorded saturation values at the turning points.
virtual void computeQpProperties() override
MaterialProperty< unsigned > & _hys_order
Computed hysteresis order at the nodes or quadpoints.
const std::vector< Real > _previous_turning_points
Previous turning points that were encountered prior to the simulation.
constexpr unsigned MAX_HYSTERESIS_ORDER
registerMooseObject("PorousFlowApp", PorousFlowHysteresisOrder)
const MaterialProperty< std::vector< Real > > & _sat_older
Older value of saturation.
std::string stringify(const T &t)
virtual unsigned int size() const override final
static InputParameters validParams()
const MaterialProperty< std::vector< Real > > & _sat_old
Old value of saturation.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const unsigned _liquid_ph_num
Liquid phase number.
void addClassDescription(const std::string &doc_string)
virtual void initQpStatefulProperties() override
PorousFlowHysteresisOrder(const InputParameters &parameters)
const MaterialProperty< unsigned > & _hys_order_old
Old value of hysteresis order at the nodes or quadpoints.
const unsigned _initial_order
Initial order.