11 #include "RotationMatrix.h"
21 params.addRequiredParam<FunctionName>(
23 "If zero then borehole does nothing. If positive the borehole acts as a sink "
24 "(production well) for porepressure > borehole pressure, and does nothing "
25 "otherwise. If negative the borehole acts as a source (injection well) for "
26 "porepressure < borehole pressure, and does nothing otherwise. The flow rate "
27 "to/from the borehole is multiplied by |character|, so usually character = +/- "
28 "1, but you can specify other quantities to provide an overall scaling to the "
30 params.addRequiredParam<Real>(
"bottom_p_or_t",
31 "For function_of=pressure, this parameter is the "
32 "pressure at the bottom of the borehole, "
33 "otherwise it is the temperature at the bottom of "
35 params.addRequiredParam<RealVectorValue>(
37 "(fluid_density*gravitational_acceleration) as a vector pointing downwards. "
38 "Note that the borehole pressure at a given z position is bottom_p_or_t + "
39 "unit_weight*(q - q_bottom), where q=(x,y,z) and q_bottom=(x,y,z) of the "
40 "bottom point of the borehole. The analogous formula holds for "
41 "function_of=temperature. If you don't want bottomhole pressure (or "
42 "temperature) to vary in the borehole just set unit_weight=0. Typical value "
43 "is = (0,0,-1E4), for water");
44 params.addParam<Real>(
"re_constant",
46 "The dimensionless constant used in evaluating the borehole effective "
47 "radius. This depends on the meshing scheme. Peacemann "
48 "finite-difference calculations give 0.28, while for rectangular finite "
49 "elements the result is closer to 0.1594. (See Eqn(4.13) of Z Chen, Y "
50 "Zhang, Well flow models for various numerical methods, Int J Num "
51 "Analysis and Modeling, 3 (2008) 375-388.)");
52 params.addParam<Real>(
"well_constant",
54 "Usually this is calculated internally from the element geometry, the "
55 "local borehole direction and segment length, and the permeability. "
56 "However, if this parameter is given as a positive number then this "
57 "number is used instead of the internal calculation. This speeds up "
58 "computation marginally. re_constant becomes irrelevant");
59 params.addClassDescription(
60 "Approximates a borehole in the mesh using the Peaceman approach, ie "
61 "using a number of point sinks with given radii whose positions are "
62 "read from a file. NOTE: if you are using PorousFlowPorosity that depends on volumetric "
63 "strain, you should set strain_at_nearest_qp=true in your GlobalParams, to ensure the nodal "
64 "Porosity Material uses the volumetric strain at the Dirac quadpoints, and can therefore be "
71 _character(getFunction(
"character")),
72 _p_bot(getParam<Real>(
"bottom_p_or_t")),
73 _unit_weight(getParam<RealVectorValue>(
"unit_weight")),
74 _re_constant(getParam<Real>(
"re_constant")),
75 _well_constant(getParam<Real>(
"well_constant")),
77 hasMaterialProperty<RealTensorValue>(
"PorousFlow_permeability_qp") &&
78 hasMaterialProperty<std::vector<RealTensorValue>>(
"dPorousFlow_permeability_qp_dvar")),
79 _has_thermal_conductivity(
80 hasMaterialProperty<RealTensorValue>(
"PorousFlow_thermal_conductivity_qp") &&
81 hasMaterialProperty<std::vector<RealTensorValue>>(
82 "dPorousFlow_thermal_conductivity_qp_dvar")),
84 ? getMaterialProperty<RealTensorValue>(
"PorousFlow_permeability_qp")
85 : getMaterialProperty<RealTensorValue>(
"PorousFlow_thermal_conductivity_qp")),
88 ? getMaterialProperty<std::vector<RealTensorValue>>(
"dPorousFlow_permeability_qp_dvar")
89 : getMaterialProperty<std::vector<RealTensorValue>>(
90 "dPorousFlow_thermal_conductivity_qp_dvar"))
93 mooseError(
"PorousFlowPeacemanBorehole: You have specified function_of=porepressure, but you "
94 "do not have a quadpoint permeability material");
96 mooseError(
"PorousFlowPeacemanBorehole: You have specified function_of=temperature, but you do "
97 "not have a quadpoint thermal_conductivity material");
100 const unsigned int num_pts =
_zs.size();
101 _rot_matrix.resize(std::max(num_pts - 1, (
unsigned)1));
102 for (
unsigned int i = 0; i + 1 < num_pts; ++i)
104 const RealVectorValue v2(
_xs[i + 1] -
_xs[i],
_ys[i + 1] -
_ys[i],
_zs[i + 1] -
_zs[i]);
107 if (num_pts == (
unsigned)1)
113 const RealTensorValue & rot,
114 const Real & half_len,
116 const Real & rad)
const
119 if (_well_constant > 0)
120 return _well_constant;
126 const RealTensorValue rot_perm = (rot * perm) * rot.transpose();
127 const Real trace2D = rot_perm(0, 0) + rot_perm(1, 1);
128 const Real det2D = rot_perm(0, 0) * rot_perm(1, 1) - rot_perm(0, 1) * rot_perm(1, 0);
129 const Real sq = std::sqrt(std::max(0.25 * trace2D * trace2D - det2D,
131 const Real eig_val1 = 0.5 * trace2D + sq;
132 const Real eig_val2 = 0.5 * trace2D - sq;
133 RealVectorValue eig_vec1, eig_vec2;
134 if (sq > std::abs(trace2D) * 1E-7)
137 if (rot_perm(1, 0) != 0)
139 eig_vec1(0) = eig_val1 - rot_perm(1, 1);
140 eig_vec1(1) = rot_perm(1, 0);
141 eig_vec2(0) = eig_val2 - rot_perm(1, 1);
142 eig_vec2(1) = rot_perm(1, 0);
144 else if (rot_perm(0, 1) != 0)
146 eig_vec1(0) = rot_perm(0, 1);
147 eig_vec1(1) = eig_val1 - rot_perm(0, 0);
148 eig_vec2(0) = rot_perm(0, 1);
149 eig_vec2(1) = eig_val2 - rot_perm(0, 0);
164 eig_vec1 = rot.transpose() * eig_vec1;
165 eig_vec1 /= std::sqrt(eig_vec1 * eig_vec1);
166 eig_vec2 = rot.transpose() * eig_vec2;
167 eig_vec2 /= std::sqrt(eig_vec2 * eig_vec2);
171 Real max1 = eig_vec1 * ele->point(0);
172 Real max2 = eig_vec2 * ele->point(0);
176 for (
unsigned int i = 1; i < ele->n_nodes(); i++)
178 proj = eig_vec1 * ele->point(i);
179 max1 = (max1 < proj) ? proj : max1;
180 min1 = (min1 < proj) ? min1 : proj;
182 proj = eig_vec2 * ele->point(i);
183 max2 = (max2 < proj) ? proj : max2;
184 min2 = (min2 < proj) ? min2 : proj;
186 const Real ll1 = max1 - min1;
187 const Real ll2 = max2 - min2;
191 r0 = _re_constant * ll1;
192 else if (eig_val2 <= 0.0)
193 r0 = _re_constant * ll2;
195 r0 = _re_constant * std::sqrt(std::sqrt(eig_val1 / eig_val2) *
std::pow(ll2, 2) +
196 std::sqrt(eig_val2 / eig_val1) *
std::pow(ll1, 2)) /
197 (
std::pow(eig_val1 / eig_val2, 0.25) +
std::pow(eig_val2 / eig_val1, 0.25));
199 const Real effective_perm = (det2D >= 0.0 ? std::sqrt(det2D) : 0.0);
201 const Real halfPi = acos(0.0);
204 mooseError(
"The effective element size (about 0.2-times-true-ele-size) for an element "
205 "containing a Peaceman-type borehole must be (much) larger than the borehole radius "
206 "for the Peaceman formulation to be correct. Your element has effective size ",
208 " and the borehole radius is ",
212 return 4 * halfPi * effective_perm * half_len / std::log(r0 / rad);
218 const Real character =
_character.value(_t, _q_point[_qp]);
219 if (character == 0.0)
223 const Real pp =
ptqp();
227 if (current_dirac_ptid > 0)
231 if ((character < 0.0 && pp < bh_pressure) || (character > 0.0 && pp > bh_pressure))
238 _rs[current_dirac_ptid]);
239 outflow += wc * (pp - bh_pressure);
243 if (current_dirac_ptid + 1 <
_zs.size() ||
_zs.size() == 1)
246 if ((character < 0.0 && pp < bh_pressure) || (character > 0.0 && pp > bh_pressure))
253 _rs[current_dirac_ptid]);
254 outflow += wc * (pp - bh_pressure);
258 return outflow * _test[_i][_qp] * std::abs(character);
263 unsigned current_dirac_ptid,
265 Real & outflowp)
const
270 const Real character =
_character.value(_t, _q_point[_qp]);
271 if (character == 0.0)
279 const Real pp =
ptqp();
280 const Real pp_prime =
dptqp(pvar) * _phi[_j][_qp];
282 if (current_dirac_ptid > 0)
285 if ((character < 0.0 && pp < bh_pressure) || (character > 0.0 && pp > bh_pressure))
292 _rs[current_dirac_ptid]);
293 outflowp += wc * pp_prime;
294 outflow += wc * (pp - bh_pressure);
298 if (current_dirac_ptid <
_zs.size() - 1 ||
_zs.size() == 1)
301 if ((character < 0.0 && pp < bh_pressure) || (character > 0.0 && pp > bh_pressure))
308 _rs[current_dirac_ptid]);
309 outflowp += wc * pp_prime;
310 outflow += wc * (pp - bh_pressure);
314 outflowp *= _test[_i][_qp] * std::abs(character);
315 outflow *= _test[_i][_qp] * std::abs(character);