https://mooseframework.inl.gov
Loading...
Searching...
No Matches
PorousFlowPeacemanBorehole.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 "RotationMatrix.h"
12#include "Function.h"
15#include "SystemBase.h"
16#include "libmesh/system.h"
17
19
22{
24 // A borehole point that the point locator cannot place in the mesh must not be silently
25 // dropped: computeWellborePressures() samples every borehole point's temperature directly
26 // (not just the ones that ended up with a Dirac source), and a missing element there corrupts
27 // the trapezoidal pressure integral for every other point up the well, not just the missing
28 // one. DiracKernelBase's own default of IGNORE would let that happen silently, so raise it to
29 // ERROR here.
30 params.set<MooseEnum>("point_not_found_behavior") = "ERROR";
31 params.addRequiredParam<FunctionName>(
32 "character",
33 "If zero then borehole does nothing. If positive the borehole acts as a sink "
34 "(production well) for porepressure > borehole pressure, and does nothing "
35 "otherwise. If negative the borehole acts as a source (injection well) for "
36 "porepressure < borehole pressure, and does nothing otherwise. The flow rate "
37 "to/from the borehole is multiplied by |character|, so usually character = +/- "
38 "1, but you can specify other quantities to provide an overall scaling to the "
39 "flow if you like.");
40 params.addRequiredParam<FunctionName>("bottom_p_or_t",
41 "For function_of=pressure, this function is the "
42 "pressure at the bottom of the borehole, "
43 "otherwise it is the temperature at the bottom of "
44 "the borehole.");
45 params.addParam<RealVectorValue>(
46 "unit_weight",
47 "(fluid_density*gravitational_acceleration) as a vector pointing downwards. "
48 "Note that the borehole pressure at a given z position is bottom_p_or_t + "
49 "unit_weight*(q - q_bottom), where q=(x,y,z) and q_bottom=(x,y,z) of the "
50 "bottom point of the borehole. The analogous formula holds for "
51 "function_of=temperature. If you don't want bottomhole pressure (or "
52 "temperature) to vary in the borehole just set unit_weight=0. Typical value "
53 "is = (0,0,-1E4), for water. Exactly one of 'unit_weight' or 'unit_weight_fp' must be "
54 "given. Use 'unit_weight_fp' instead of 'unit_weight' if you want the fluid unit weight "
55 "to vary along the borehole according to a temperature-dependent fluid "
56 "density, rather than being a single constant value.");
57 params.addParam<UserObjectName>(
58 "unit_weight_fp",
59 "SinglePhaseFluidProperties UserObject used to evaluate the in-well fluid density from "
60 "'unit_weight_temperature' at each borehole point, in order to build a wellbore pressure "
61 "profile that accounts for a thermal gradient along the borehole. Providing this "
62 "parameter activates this temperature-dependent unit-weight mode instead of the constant "
63 "'unit_weight'. Not compatible with function_of=temperature, since in that mode "
64 "bottom_p_or_t is a temperature, not a pressure, so there is no pressure profile to build. "
65 "If given, 'unit_weight_temperature' and 'unit_weight_gravity' are also required. "
66 "(Deliberately not named 'fp'/'gravity'/'temperature_variable', even though that mirrors "
67 "convention elsewhere in PorousFlow, because those are common GlobalParams names: an "
68 "input file that sets 'gravity' or 'fp' in [GlobalParams] for unrelated Darcy kernels or "
69 "fluid materials would otherwise silently activate, or fail to validate, this mode on "
70 "every PorousFlowPeacemanBorehole in the input.)");
71 params.addCoupledVar(
72 "unit_weight_temperature",
73 "The (nonlinear or auxiliary) variable holding temperature, sampled at each borehole point "
74 "to compute the in-well fluid density used to build the wellbore pressure profile. Must "
75 "be a variable, not a constant value. This is unrelated to function_of=temperature "
76 "(which instead selects whether the *outflow* driving this DiracKernel is a function of "
77 "porepressure or temperature). Only used, and required, if 'unit_weight_fp' is given.");
78 params.addParam<RealVectorValue>(
79 "unit_weight_gravity",
80 "Gravitational acceleration, pointing downwards, in the units used elsewhere in this "
81 "input file (eg (0,0,-9.81) for SI units and lengths in metres). Only used, and "
82 "required, if 'unit_weight_fp' is given. Densities computed from 'unit_weight_fp' are "
83 "always in kg/m^3, so if lengths in this input file are not metres, scale "
84 "'unit_weight_gravity' accordingly (eg (0,0,-9.81E-6) if pressures are in MPa and lengths "
85 "in metres, matching the 'gravity' convention used by PorousFlow Darcy kernels).");
86 MooseEnum temperature_unit_choice("Kelvin=0 Celsius=1", "Kelvin");
87 params.addParam<MooseEnum>(
88 "unit_weight_temperature_unit",
89 temperature_unit_choice,
90 "The unit of 'unit_weight_temperature'. Only used if 'unit_weight_fp' is given.");
91 MooseEnum pressure_unit_choice("Pa MPa", "Pa");
92 params.addParam<MooseEnum>(
93 "unit_weight_pressure_unit",
94 pressure_unit_choice,
95 "The unit of 'unit_weight_reference_pressure'. Only used if 'unit_weight_fp' is given.");
96 params.addRangeCheckedParam<Real>(
97 "unit_weight_reference_pressure",
98 101325.0, // standard atmosphere: liquid-water density is only weakly pressure-dependent
99 // (~0.9% per 20MPa), so a fixed reference pressure is used instead of the local
100 // (coupled, and hence more expensive to sample) porepressure
101 "unit_weight_reference_pressure > 0",
102 "The fixed pressure (in the units given by 'unit_weight_pressure_unit') at which the "
103 "in-well fluid density is evaluated by 'unit_weight_fp'. Only used if 'unit_weight_fp' "
104 "is given. Choose a value close to the expected wellbore pressure for the most accurate "
105 "density.");
106 params.addParam<Real>("re_constant",
107 0.28,
108 "The dimensionless constant used in evaluating the borehole effective "
109 "radius. This depends on the meshing scheme. Peacemann "
110 "finite-difference calculations give 0.28, while for rectangular finite "
111 "elements the result is closer to 0.1594. (See Eqn(4.13) of Z Chen, Y "
112 "Zhang, Well flow models for various numerical methods, Int J Num "
113 "Analysis and Modeling, 3 (2008) 375-388.)");
114 params.addParam<Real>("well_constant",
115 -1.0,
116 "Usually this is calculated internally from the element geometry, the "
117 "local borehole direction and segment length, and the permeability. "
118 "However, if this parameter is given as a positive number then this "
119 "number is used instead of the internal calculation. This speeds up "
120 "computation marginally. re_constant becomes irrelevant");
121 params.addClassDescription(
122 "Approximates a borehole in the mesh using the Peaceman approach, ie "
123 "using a number of point sinks with given radii whose positions are "
124 "read from a file. NOTE: if you are using PorousFlowPorosity that depends on volumetric "
125 "strain, you should set strain_at_nearest_qp=true in your GlobalParams, to ensure the nodal "
126 "Porosity Material uses the volumetric strain at the Dirac quadpoints, and can therefore be "
127 "computed. The wellbore pressure profile is built either from a constant fluid unit "
128 "weight ('unit_weight') or, if a thermal gradient along the borehole makes a single "
129 "constant unit weight a poor approximation, from a fluid density computed at each "
130 "borehole point from a temperature-dependent fluid-properties UserObject "
131 "('unit_weight_fp')");
132 return params;
133}
134
136 : PorousFlowLineSink(parameters),
137 _character(getFunction("character")),
138 _p_bot(getFunction("bottom_p_or_t")),
139 _unit_weight(isParamValid("unit_weight") ? getParam<RealVectorValue>("unit_weight")
140 : RealVectorValue()),
141 _use_density_from_temperature(isParamValid("unit_weight_fp")),
142 _fp(_use_density_from_temperature ? &getUserObject<SinglePhaseFluidProperties>("unit_weight_fp")
143 : nullptr),
144 _temperature_var(_use_density_from_temperature && isCoupled("unit_weight_temperature")
145 ? getFieldVar("unit_weight_temperature", 0)
146 : nullptr),
147 _temperature_system(_temperature_var ? &_temperature_var->sys().system() : nullptr),
148 _temperature_var_number(_temperature_var ? _temperature_var->number() : libMesh::invalid_uint),
149 _gravity(isParamValid("unit_weight_gravity") ? getParam<RealVectorValue>("unit_weight_gravity")
150 : RealVectorValue()),
151 _density_reference_pressure(
152 getParam<Real>("unit_weight_reference_pressure") *
153 (getParam<MooseEnum>("unit_weight_pressure_unit") == 0 ? 1.0 : 1.0E6)),
154 _t_c2k(getParam<MooseEnum>("unit_weight_temperature_unit") == 0 ? 0.0 : 273.15),
155 _re_constant(getParam<Real>("re_constant")),
156 _well_constant(getParam<Real>("well_constant")),
157 _has_permeability(
158 hasMaterialProperty<RealTensorValue>("PorousFlow_permeability_qp") &&
159 hasMaterialProperty<std::vector<RealTensorValue>>("dPorousFlow_permeability_qp_dvar")),
160 _has_thermal_conductivity(
161 hasMaterialProperty<RealTensorValue>("PorousFlow_thermal_conductivity_qp") &&
162 hasMaterialProperty<std::vector<RealTensorValue>>(
163 "dPorousFlow_thermal_conductivity_qp_dvar")),
164 _perm_or_cond(_p_or_t == PorTchoice::pressure
165 ? getMaterialProperty<RealTensorValue>("PorousFlow_permeability_qp")
166 : getMaterialProperty<RealTensorValue>("PorousFlow_thermal_conductivity_qp")),
167 _dperm_or_cond_dvar(
168 _p_or_t == PorTchoice::pressure
169 ? getMaterialProperty<std::vector<RealTensorValue>>("dPorousFlow_permeability_qp_dvar")
170 : getMaterialProperty<std::vector<RealTensorValue>>(
171 "dPorousFlow_thermal_conductivity_qp_dvar"))
172{
174 mooseError("PorousFlowPeacemanBorehole: You have specified function_of=porepressure, but you "
175 "do not have a quadpoint permeability material");
177 mooseError("PorousFlowPeacemanBorehole: You have specified function_of=temperature, but you do "
178 "not have a quadpoint thermal_conductivity material");
179
180 // The wellbore pressure profile is built from either a single constant unit_weight, or a
181 // fluid density computed from temperature at each point (via 'unit_weight_fp') - never both,
182 // and never neither. Note this deliberately does not error if 'unit_weight_temperature' or
183 // 'unit_weight_gravity' are valid while 'unit_weight_fp' is not: unlike 'unit_weight' and
184 // 'unit_weight_fp' themselves, those two are not required to detect the user's intended mode,
185 // so treating their unexpected presence as an error would only serve to reject input files
186 // that harmlessly set an identically-named GlobalParam for some unrelated object.
187 const int checkWellborePressureFormat =
188 int(isParamValid("unit_weight")) + int(isParamValid("unit_weight_fp"));
189 if (checkWellborePressureFormat > 1)
190 paramError("unit_weight",
191 "PorousFlowPeacemanBorehole: must specify only one of 'unit_weight' (a constant "
192 "fluid unit weight) or 'unit_weight_fp' (a fluid-properties UserObject, so that "
193 "the fluid unit weight is instead computed from the temperature at each borehole "
194 "point)");
195 else if (checkWellborePressureFormat == 0)
196 paramError("unit_weight",
197 "PorousFlowPeacemanBorehole: must specify at least one of 'unit_weight' or "
198 "'unit_weight_fp'");
199
201 {
203 paramError("unit_weight_fp",
204 "PorousFlowPeacemanBorehole: 'unit_weight_fp' computes a fluid density to "
205 "build a hydrostatic *pressure* profile along the borehole, which is "
206 "meaningless when function_of=temperature (bottom_p_or_t is then a "
207 "temperature, not a pressure)");
208 if (!isParamValid("unit_weight_temperature"))
209 paramError("unit_weight_temperature",
210 "PorousFlowPeacemanBorehole: 'unit_weight_temperature' must be supplied when "
211 "'unit_weight_fp' is supplied");
212 if (!isCoupled("unit_weight_temperature"))
213 paramError("unit_weight_temperature",
214 "PorousFlowPeacemanBorehole: 'unit_weight_temperature' must be a nonlinear or "
215 "auxiliary variable, not a constant value. The wellbore pressure profile is "
216 "built by sampling this variable at each borehole point, so a spatially "
217 "constant temperature has no profile to sample: use 'unit_weight' instead if "
218 "the in-well fluid density really is constant");
219 if (!isParamValid("unit_weight_gravity"))
220 paramError("unit_weight_gravity",
221 "PorousFlowPeacemanBorehole: 'unit_weight_gravity' must be supplied when "
222 "'unit_weight_fp' is supplied");
223 }
224}
225
226void
228{
230
231 if (!_point_file.empty() && _zs[0] < _zs.back())
232 mooseError("PorousFlowPeacemanBorehole: The last entry in the point_file needs to be at the "
233 "bottom of the well_bore because this is the point where the function bottom_p_or_t "
234 "is evaluated. The depth of the first point is z=",
235 _zs[0],
236 " and the last point is z=",
237 _zs.back());
238
239 // construct the rotation matrix needed to rotate the permeability
240 const unsigned int num_pts = _zs.size();
241 _rot_matrix.resize(std::max(num_pts - 1, (unsigned)1));
242 for (unsigned int i = 0; i + 1 < num_pts; ++i)
243 {
244 const RealVectorValue v2(_xs[i + 1] - _xs[i], _ys[i + 1] - _ys[i], _zs[i + 1] - _zs[i]);
246 }
247 if (num_pts == (unsigned)1)
249}
250
251void
257
258void
264
265void
267{
269 return;
270
271 const std::size_t num_pts = _z_coord->size();
272 _bh_pressure.assign(num_pts, 0.0);
273 if (num_pts == 0)
274 return;
275
276 // Sample the temperature, and hence the in-well fluid density, at every well point (not just
277 // the points owned by this processor), because the wellbore pressure at any point is a
278 // cumulative integral over all the points between it and the bottom point.
279 // System::point_value performs the parallel point-location and broadcast internally, so this
280 // is safe to call even for points this processor's mesh partition does not own.
281 std::vector<Real> density(num_pts);
282 for (const auto i : make_range(num_pts))
283 {
284 const Point p(_x_coord->at(i), _y_coord->at(i), _z_coord->at(i));
286 density[i] = _fp->rho_from_p_T(_density_reference_pressure, temperature + _t_c2k);
287 }
288
289 // Integrate the fluid unit weight (density*gravity) up the wellbore from the bottom point,
290 // where the pressure is prescribed by bottom_p_or_t, using the trapezoidal rule on each
291 // polyline segment. This is exact for a density varying linearly along the well (the target
292 // use case: a linear thermal gradient with a locally-linear density(temperature)), and it
293 // degenerates exactly to the constant-unit_weight formula when the density is constant, since
294 // the sum then telescopes to density*gravity.(x_i - x_bottom).
295 _bh_pressure[num_pts - 1] = _p_bot.value(_t, _bottom_point);
296 for (std::size_t i = num_pts - 1; i > 0; --i)
297 {
298 const RealVectorValue segment(_x_coord->at(i - 1) - _x_coord->at(i),
299 _y_coord->at(i - 1) - _y_coord->at(i),
300 _z_coord->at(i - 1) - _z_coord->at(i));
301 _bh_pressure[i - 1] =
302 _bh_pressure[i] + 0.5 * (density[i - 1] + density[i]) * (_gravity * segment);
303 }
304}
305
306Real
307PorousFlowPeacemanBorehole::wellborePressure(unsigned current_dirac_ptid) const
308{
311
312 mooseAssert(current_dirac_ptid < _bh_pressure.size(),
313 "PorousFlowPeacemanBorehole: the wellbore pressure profile has not been computed "
314 "for this Dirac point");
315 return _bh_pressure[current_dirac_ptid];
316}
317
318Real
319PorousFlowPeacemanBorehole::wellConstant(const RealTensorValue & perm,
320 const RealTensorValue & rot,
321 const Real & half_len,
322 const Elem * ele,
323 const Real & rad) const
324// Peaceman's form for the borehole well constant
325{
326 if (_well_constant > 0)
327 return _well_constant;
328
329 // rot_perm has its "2" component lying along the half segment.
330 // We want to determine the eigenvectors of rot(0:1, 0:1), since, when
331 // rotated back to the original frame we will determine the element
332 // lengths along these directions
333 const RealTensorValue rot_perm = (rot * perm) * rot.transpose();
334 const Real trace2D = rot_perm(0, 0) + rot_perm(1, 1);
335 const Real det2D = rot_perm(0, 0) * rot_perm(1, 1) - rot_perm(0, 1) * rot_perm(1, 0);
336 const Real sq = std::sqrt(std::max(0.25 * trace2D * trace2D - det2D,
337 0.0)); // the std::max accounts for wierdo precision loss
338 const Real eig_val1 = 0.5 * trace2D + sq;
339 const Real eig_val2 = 0.5 * trace2D - sq;
340 RealVectorValue eig_vec1, eig_vec2;
341 if (sq > std::abs(trace2D) * 1E-7) // matrix is not a multiple of the identity (1E-7 accounts for
342 // precision in a crude way)
343 {
344 if (rot_perm(1, 0) != 0)
345 {
346 eig_vec1(0) = eig_val1 - rot_perm(1, 1);
347 eig_vec1(1) = rot_perm(1, 0);
348 eig_vec2(0) = eig_val2 - rot_perm(1, 1);
349 eig_vec2(1) = rot_perm(1, 0);
350 }
351 else if (rot_perm(0, 1) != 0)
352 {
353 eig_vec1(0) = rot_perm(0, 1);
354 eig_vec1(1) = eig_val1 - rot_perm(0, 0);
355 eig_vec2(0) = rot_perm(0, 1);
356 eig_vec2(1) = eig_val2 - rot_perm(0, 0);
357 }
358 else // off diagonal terms are both zero
359 {
360 eig_vec1(0) = 1.0;
361 eig_vec2(1) = 1.0;
362 }
363 }
364 else // matrix is basically a multiple of the identity
365 {
366 eig_vec1(0) = 1.0;
367 eig_vec2(1) = 1.0;
368 }
369
370 // finally, rotate these to original frame and normalise
371 eig_vec1 = rot.transpose() * eig_vec1;
372 eig_vec1 /= std::sqrt(eig_vec1 * eig_vec1);
373 eig_vec2 = rot.transpose() * eig_vec2;
374 eig_vec2 /= std::sqrt(eig_vec2 * eig_vec2);
375
376 // find the "length" of the element in these directions
377 // TODO - maybe better to use variance than max&min
378 Real max1 = eig_vec1 * ele->point(0);
379 Real max2 = eig_vec2 * ele->point(0);
380 Real min1 = max1;
381 Real min2 = max2;
382 Real proj;
383 for (unsigned int i = 1; i < ele->n_nodes(); i++)
384 {
385 proj = eig_vec1 * ele->point(i);
386 max1 = (max1 < proj) ? proj : max1;
387 min1 = (min1 < proj) ? min1 : proj;
388
389 proj = eig_vec2 * ele->point(i);
390 max2 = (max2 < proj) ? proj : max2;
391 min2 = (min2 < proj) ? min2 : proj;
392 }
393 const Real ll1 = max1 - min1;
394 const Real ll2 = max2 - min2;
395
396 Real r0;
397 if (eig_val1 <= 0.0)
398 r0 = _re_constant * ll1;
399 else if (eig_val2 <= 0.0)
400 r0 = _re_constant * ll2;
401 else
402 r0 = _re_constant *
403 std::sqrt(std::sqrt(eig_val1 / eig_val2) * std::pow(ll2, 2) +
404 std::sqrt(eig_val2 / eig_val1) * std::pow(ll1, 2)) /
405 (std::pow(eig_val1 / eig_val2, 0.25) + std::pow(eig_val2 / eig_val1, 0.25));
406
407 const Real effective_perm = (det2D >= 0.0 ? std::sqrt(det2D) : 0.0);
408
409 const Real halfPi = acos(0.0);
410
411 if (r0 <= rad)
412 mooseError("The effective element size (about 0.2-times-true-ele-size) for an element "
413 "containing a Peaceman-type borehole must be (much) larger than the borehole radius "
414 "for the Peaceman formulation to be correct. Your element has effective size ",
415 r0,
416 " and the borehole radius is ",
417 rad,
418 "\n");
419
420 return 4 * halfPi * effective_perm * half_len / std::log(r0 / rad);
421}
422
423Real
424PorousFlowPeacemanBorehole::computeQpBaseOutflow(unsigned current_dirac_ptid) const
425{
426 const Real character = _character.value(_t, _q_point[_qp]);
427 if (character == 0.0)
428 return 0.0;
429
430 const Real bh_pressure = wellborePressure(current_dirac_ptid);
431 const Real pp = ptqp();
432
433 Real outflow = 0.0; // this is the flow rate from porespace out of the system
434
435 if (current_dirac_ptid > 0)
436 // contribution from half-segment "behind" this point (must have >1 point for
437 // current_dirac_ptid>0)
438 {
439 if ((character < 0.0 && pp < bh_pressure) || (character > 0.0 && pp > bh_pressure))
440 {
441 // injection, so outflow<0 || production, so outflow>0
442 const Real wc = wellConstant(_perm_or_cond[_qp],
443 _rot_matrix[current_dirac_ptid - 1],
444 _half_seg_len[current_dirac_ptid - 1],
446 _weight->at(current_dirac_ptid));
447 outflow += wc * (pp - bh_pressure);
448 }
449 }
450
451 if (current_dirac_ptid + 1 < _zs.size() || _zs.size() == 1)
452 // contribution from half-segment "ahead of" this point, or we only have one point
453 {
454 if ((character < 0.0 && pp < bh_pressure) || (character > 0.0 && pp > bh_pressure))
455 {
456 // injection, so outflow<0 || // production, so outflow>0
457 const Real wc = wellConstant(_perm_or_cond[_qp],
458 _rot_matrix[current_dirac_ptid],
459 _half_seg_len[current_dirac_ptid],
461 _weight->at(current_dirac_ptid));
462 outflow += wc * (pp - bh_pressure);
463 }
464 }
465
466 return outflow * _test[_i][_qp] * std::abs(character);
467}
468
469void
471 unsigned current_dirac_ptid,
472 Real & outflow,
473 Real & outflowp) const
474{
475 outflow = 0.0;
476 outflowp = 0.0;
477
478 const Real character = _character.value(_t, _q_point[_qp]);
479 if (character == 0.0)
480 return;
481
483 return;
484 const unsigned pvar = _dictator.porousFlowVariableNum(jvar);
485
486 // When _use_density_from_temperature is true, bh_pressure also depends on the temperature at
487 // every well point between here and the bottom point (via computeWellborePressures()), not
488 // just on the porous flow variables at this quadpoint. That dependence is deliberately not
489 // differentiated here: a DiracKernel can only assemble into the (test, phi) dofs of the
490 // element containing its own quadpoint, so the coupling to temperature dofs in other elements
491 // along the well cannot be represented in this Jacobian. residualSetup()/jacobianSetup()
492 // still recompute bh_pressure from the current nonlinear iterate before every evaluation, so
493 // the converged solution is unaffected; only the Newton convergence rate may be mildly slower.
494 // A proper fix (a relationship-managed line/segment kernel base class) is tracked separately
495 // in https://github.com/idaholab/moose/issues/33757, to be picked up after this PR merges.
496 const Real bh_pressure = wellborePressure(current_dirac_ptid);
497 const Real pp = ptqp();
498 const Real pp_prime = dptqp(pvar) * _phi[_j][_qp];
499
500 if (current_dirac_ptid > 0)
501 // contribution from half-segment "behind" this point
502 {
503 if ((character < 0.0 && pp < bh_pressure) || (character > 0.0 && pp > bh_pressure))
504 {
505 // injection, so outflow<0 || // production, so outflow>0
506 const Real wc = wellConstant(_perm_or_cond[_qp],
507 _rot_matrix[current_dirac_ptid - 1],
508 _half_seg_len[current_dirac_ptid - 1],
510 _weight->at(current_dirac_ptid));
511 outflowp += wc * pp_prime;
512 outflow += wc * (pp - bh_pressure);
513 }
514 }
515
516 if (current_dirac_ptid < _zs.size() - 1 || _zs.size() == 1)
517 // contribution from half-segment "ahead of" this point
518 {
519 if ((character < 0.0 && pp < bh_pressure) || (character > 0.0 && pp > bh_pressure))
520 {
521 // injection, so outflow<0 || // production, so outflow>0
522 const Real wc = wellConstant(_perm_or_cond[_qp],
523 _rot_matrix[current_dirac_ptid],
524 _half_seg_len[current_dirac_ptid],
526 _weight->at(current_dirac_ptid));
527 outflowp += wc * pp_prime;
528 outflow += wc * (pp - bh_pressure);
529 }
530 }
531
532 outflowp *= _test[_i][_qp] * std::abs(character);
533 outflow *= _test[_i][_qp] * std::abs(character);
534}
const Real p
void mooseError(Args &&... args)
registerMooseObject("PorousFlowApp", PorousFlowPeacemanBorehole)
void ErrorVector unsigned int
virtual bool isCoupled(const std::string &var_name, unsigned int i=0) const
unsigned int _i
const Elem *const & _current_elem
unsigned int _qp
const MooseArray< Point > & _q_point
unsigned int _j
const OutputTools< T >::VariablePhiValue & _phi
const OutputTools< T >::VariableTestValue & _test
virtual Real value(Real t, const Point &p) const
void addRequiredParam(const std::string &name, const std::string &doc_string)
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)
T & set(const std::string &name, bool quiet_mode=false)
void addCoupledVar(const std::string &name, const std::string &doc_string)
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
void paramError(const std::string &param, Args... args) const
void mooseError(Args &&... args) const
bool isParamValid(const std::string &name) const
unsigned int porousFlowVariableNum(unsigned int moose_var_num) const
The PorousFlow variable number.
bool notPorousFlowVariable(unsigned int moose_var_num) const
Returns true if moose_var_num is not a porous flow variabe.
const RealVectorValue _line_direction
Line direction. This is only used if there is only one borehole point.
std::vector< Real > _xs
x points of the borehole
std::vector< Real > _zs
z points of borehole
std::vector< Real > _ys
y points of the borehole
Point _bottom_point
The bottom point of the borehole (where bottom_pressure is defined)
const std::vector< Real > *const _z_coord
std::vector< Real > _half_seg_len
0.5*(length of polyline segments between points)
const std::string _point_file
File defining the geometry of the borehole.
const std::vector< Real > *const _y_coord
const std::vector< Real > *const _x_coord
virtual void initialSetup() override
const std::vector< Real > *const _weight
Approximates a line sink a sequence of Dirac Points.
Real dptqp(unsigned pvar) const
If _p_or_t==0, then returns d(quadpoint porepressure)/d(PorousFlow variable), else returns d(quadpoin...
const PorousFlowDictator & _dictator
PorousFlowDictator UserObject.
PorTchoice
whether the flux is a function of pressure or temperature
enum PorousFlowLineSink::PorTchoice _p_or_t
static InputParameters validParams()
Real ptqp() const
If _p_or_t==0, then returns the quadpoint porepressure, else returns the quadpoint temperature.
Approximates a borehole by a sequence of Dirac Points.
static InputParameters validParams()
Creates a new PorousFlowPeacemanBorehole This reads the file containing the lines of the form radius ...
void computeWellborePressures()
(Re)computes _bh_pressure from the temperature at each well point, when _use_density_from_temperature...
const MaterialProperty< RealTensorValue > & _perm_or_cond
Permeability or conductivity of porous material.
const bool _has_permeability
Whether there is a quadpoint permeability material (for error checking)
const unsigned int _temperature_var_number
Variable number of unit_weight_temperature within _temperature_system.
Real wellborePressure(unsigned current_dirac_ptid) const
The wellbore pressure (or temperature, for function_of=temperature) at the given Dirac point,...
const bool _use_density_from_temperature
Whether the wellbore pressure profile is built from a temperature-dependent fluid density (true if th...
const libMesh::System *const _temperature_system
The libMesh system holding _temperature_var, used to sample that variable at borehole points that may...
const Function & _character
If positive then the borehole acts as a sink (producion well) for porepressure > borehole pressure,...
const bool _has_thermal_conductivity
Whether there is a quadpoint thermal conductivity material (for error checking)
void computeQpBaseOutflowJacobian(unsigned jvar, unsigned current_dirac_ptid, Real &outflow, Real &outflowp) const override
Calculates the BaseOutflow as well as its derivative wrt jvar. Derived classes should override this.
std::vector< Real > _bh_pressure
Wellbore pressure at each well point, indexed by Dirac point ID.
const RealVectorValue _gravity
Gravitational acceleration (in the units used elsewhere in the input file), pointing downwards.
Real computeQpBaseOutflow(unsigned current_dirac_ptid) const override
Returns the flux from the line sink (before modification by mobility, etc). Derived classes should ov...
const Real _density_reference_pressure
Fixed pressure (Pa) at which the in-well fluid density is evaluated.
Real wellConstant(const RealTensorValue &perm, const RealTensorValue &rot, const Real &half_len, const Elem *ele, const Real &rad) const
Calculates Peaceman's form of the borehole well constant Z Chen, Y Zhang, Well flow models for variou...
std::vector< RealTensorValue > _rot_matrix
Rotation matrix used in well_constant calculation.
const Real _t_c2k
Conversion of unit_weight_temperature's values to Kelvin (0 for Kelvin, 273.15 for Celsius)
const SinglePhaseFluidProperties *const _fp
Fluid properties used to evaluate the in-well fluid density.
const RealVectorValue _unit_weight
Unit weight of fluid in borehole (for calculating bottomhole pressure at each Dirac Point).
const Function & _p_bot
Bottomhole pressure of borehole.
PorousFlowPeacemanBorehole(const InputParameters &parameters)
virtual void residualSetup()
virtual void jacobianSetup()
Common class for single phase fluid properties.
Number point_value(unsigned int var, const Point &p, const bool insist_on_success=true, const NumericVector< Number > *sol=nullptr) const
GenericRealTensorValue< is_ad > rotVecToZ(GenericRealVectorValue< is_ad > vec)
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...