https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
122void
124{
126 for (unsigned i = 0; i < _initial_order; ++i)
128}
129
130void
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}
registerMooseObject("PorousFlowApp", PorousFlowHysteresisOrder)
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)
virtual unsigned int size() const override final
Computes the hysteresis order for use by the hysteretic capillary pressure and relative-permeability ...
virtual void computeQpProperties() override
const unsigned _initial_order
Initial order.
const std::vector< Real > _previous_turning_points
Previous turning points that were encountered prior to the simulation.
const MaterialProperty< std::array< Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER > > & _hys_sat_tps_old
Old value of recorded saturation values at the turning points.
MaterialProperty< std::array< Real, PorousFlowConstants::MAX_HYSTERESIS_ORDER > > & _hys_sat_tps
Recorded saturation values at the turning points.
const MaterialProperty< std::vector< Real > > & _sat_old
Old value of saturation.
static InputParameters validParams()
const MaterialProperty< std::vector< Real > > & _sat_older
Older value of saturation.
const MaterialProperty< unsigned > & _hys_order_old
Old value of hysteresis order at the nodes or quadpoints.
virtual void initQpStatefulProperties() override
const unsigned _liquid_ph_num
Liquid phase number.
PorousFlowHysteresisOrder(const InputParameters &parameters)
MaterialProperty< unsigned > & _hys_order
Computed hysteresis order at the nodes or quadpoints.
PorousFlowMaterial is the base class for all PorousFlow Materials It allows users to specify that the...
static InputParameters validParams()
constexpr unsigned MAX_HYSTERESIS_ORDER