Line data Source code
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 :
10 : #include "BrineFluidProperties.h"
11 :
12 : registerMooseObject("FluidPropertiesApp", BrineFluidProperties);
13 :
14 : InputParameters
15 98 : BrineFluidProperties::validParams()
16 : {
17 98 : InputParameters params = MultiComponentFluidProperties::validParams();
18 196 : params.addParam<UserObjectName>("water_fp",
19 : "The name of the FluidProperties UserObject for water");
20 98 : params.addClassDescription("Fluid properties for brine");
21 98 : return params;
22 0 : }
23 :
24 52 : BrineFluidProperties::BrineFluidProperties(const InputParameters & parameters)
25 52 : : MultiComponentFluidProperties(parameters), _water_fp_derivs(true)
26 : {
27 : // There are two possibilities to consider:
28 : // 1) No water_fp has been supplied (in which case one is constructed)
29 : // 2) A water_fp hase been supplied (in which case it is used)
30 : // In both cases, though, a Water97FluidProperties UserObject must be added
31 : // Note: this UserObject is only used to gain access to the Henry's constant
32 : // formulation. All property calculations are performed using _water_fp
33 52 : const std::string water_name = name() + ":water";
34 : {
35 52 : const std::string class_name = "Water97FluidProperties";
36 52 : InputParameters params = _app.getFactory().getValidParams(class_name);
37 52 : if (_tid == 0)
38 46 : _fe_problem.addUserObject(class_name, water_name, params);
39 52 : }
40 52 : _water97_fp = &_fe_problem.getUserObject<Water97FluidProperties>(water_name);
41 :
42 104 : if (parameters.isParamSetByUser("water_fp"))
43 : {
44 : // SinglePhaseFluidPropertiesPT UserObject for water
45 22 : _water_fp = &getUserObject<SinglePhaseFluidProperties>("water_fp");
46 :
47 : // Check that a water userobject has actually been supplied
48 44 : if (_water_fp->fluidName() != "water")
49 0 : paramError("water_fp", "A water FluidProperties UserObject must be supplied");
50 : }
51 : else
52 : {
53 : // Construct a SinglePhaseFluidProperties UserObject for water
54 30 : _water_fp = &_fe_problem.getUserObject<SinglePhaseFluidProperties>(water_name);
55 : }
56 :
57 : // SinglePhaseFluidProperties UserObject for NaCl
58 52 : const std::string nacl_name = name() + ":nacl";
59 : {
60 52 : const std::string class_name = "NaClFluidProperties";
61 52 : InputParameters params = _app.getFactory().getValidParams(class_name);
62 52 : if (_tid == 0)
63 46 : _fe_problem.addUserObject(class_name, nacl_name, params);
64 52 : }
65 52 : _nacl_fp = &_fe_problem.getUserObject<SinglePhaseFluidProperties>(nacl_name);
66 :
67 : // Molar mass of NaCl and H20
68 52 : _Mnacl = _nacl_fp->molarMass();
69 52 : _Mh2o = _water_fp->molarMass();
70 52 : }
71 :
72 104 : BrineFluidProperties::~BrineFluidProperties() {}
73 :
74 : const SinglePhaseFluidProperties &
75 10 : BrineFluidProperties::getComponent(unsigned int component) const
76 : {
77 10 : switch (component)
78 : {
79 9 : case WATER:
80 9 : return *_water_fp;
81 :
82 1 : case NACL:
83 1 : return *_nacl_fp;
84 :
85 0 : default:
86 0 : mooseError("BrineFluidProperties::getComponent has been provided an incorrect component");
87 : }
88 : }
89 :
90 : std::string
91 1 : BrineFluidProperties::fluidName() const
92 : {
93 1 : return "brine";
94 : }
95 :
96 : FPADReal
97 2679 : BrineFluidProperties::molarMass(const FPADReal & xnacl) const
98 : {
99 8037 : return 1.0 / (xnacl / _Mnacl + (1.0 - xnacl) / _Mh2o);
100 : }
101 :
102 : Real
103 436 : BrineFluidProperties::molarMass(Real xnacl) const
104 : {
105 436 : return 1.0 / (xnacl / _Mnacl + (1.0 - xnacl) / _Mh2o);
106 : }
107 :
108 : Real
109 2 : BrineFluidProperties::molarMassNaCl() const
110 : {
111 2 : return _Mnacl;
112 : }
113 :
114 : Real
115 2 : BrineFluidProperties::molarMassH2O() const
116 : {
117 2 : return _Mh2o;
118 : }
119 :
120 : FPADReal
121 895 : BrineFluidProperties::rho_from_p_T_X(const FPADReal & pressure,
122 : const FPADReal & temperature,
123 : const FPADReal & xnacl) const
124 : {
125 : // The correlation requires the pressure in bar, not Pa.
126 895 : FPADReal pbar = pressure * 1.0e-5;
127 895 : FPADReal pbar2 = pbar * pbar;
128 895 : FPADReal pbar3 = pbar2 * pbar;
129 :
130 : // The correlation requires mole fraction
131 895 : const FPADReal Xnacl = massFractionToMoleFraction(xnacl);
132 :
133 895 : const FPADReal n11 = -54.2958 - 45.7623 * std::exp(-9.44785e-4 * pbar);
134 895 : const FPADReal n21 = -2.6142 - 2.39092e-4 * pbar;
135 895 : const FPADReal n22 = 0.0356828 + 4.37235e-6 * pbar + 2.0566e-9 * pbar2;
136 895 : const FPADReal n1x1 = 330.47 + 0.942876 * std::sqrt(pbar) + 0.0817193 * pbar -
137 1790 : 2.47556e-8 * pbar2 + 3.45052e-10 * pbar3;
138 895 : const FPADReal n2x1 = -0.0370751 + 0.00237723 * std::sqrt(pbar) + 5.42049e-5 * pbar +
139 1790 : 5.84709e-9 * pbar2 - 5.99373e-13 * pbar3;
140 895 : const FPADReal n12 = -n1x1 - n11;
141 895 : const FPADReal n20 = 1.0 - n21 * std::sqrt(n22);
142 895 : const FPADReal n23 = n2x1 - n20 - n21 * std::sqrt(1.0 + n22);
143 :
144 : // The temperature Tv where the brine has the same molar volume as pure water
145 : // Note: correlation uses temperature in Celcius
146 895 : const FPADReal n1 = n1x1 + n11 * (1.0 - Xnacl) + n12 * (1.0 - Xnacl) * (1.0 - Xnacl);
147 895 : const FPADReal n2 = n20 + n21 * std::sqrt(Xnacl + n22) + n23 * Xnacl;
148 895 : const FPADReal Tv = n1 + n2 * (temperature - _T_c2k);
149 :
150 : // The density of water at temperature Tv
151 : // Note: convert Tv to Kelvin to calculate water density
152 : FPADReal water_density;
153 895 : if (_water_fp_derivs)
154 : {
155 : Real rho, drho_dp, drho_dT;
156 6 : _water_fp->rho_from_p_T(pressure.value(), Tv.value() + _T_c2k, rho, drho_dp, drho_dT);
157 : water_density = rho;
158 :
159 12 : water_density.derivatives() = pressure.derivatives() * drho_dp + Tv.derivatives() * drho_dT;
160 : }
161 : else
162 889 : water_density = _water_fp->rho_from_p_T(pressure.value(), Tv.value() + _T_c2k);
163 :
164 : // The brine density is given by the water density scaled by the ratio of
165 : // brine molar mass to pure water molar mass
166 1790 : return water_density * molarMass(xnacl) / _Mh2o;
167 : }
168 :
169 : Real
170 889 : BrineFluidProperties::rho_from_p_T_X(Real pressure, Real temperature, Real xnacl) const
171 : {
172 : // Initialise the AD value (no derivatives required)
173 : FPADReal p = pressure;
174 : FPADReal T = temperature;
175 : FPADReal x = xnacl;
176 :
177 889 : _water_fp_derivs = false;
178 889 : FPADReal ad_rho = this->rho_from_p_T_X(p, T, x);
179 :
180 889 : return ad_rho.value();
181 : }
182 :
183 : void
184 4 : BrineFluidProperties::rho_from_p_T_X(Real pressure,
185 : Real temperature,
186 : Real xnacl,
187 : Real & rho,
188 : Real & drho_dp,
189 : Real & drho_dT,
190 : Real & drho_dx) const
191 : {
192 : // Initialise the AD value and derivatives
193 : FPADReal p = pressure;
194 : Moose::derivInsert(p.derivatives(), 0, 1.0);
195 : FPADReal T = temperature;
196 : Moose::derivInsert(T.derivatives(), 1, 1.0);
197 : FPADReal x = xnacl;
198 : Moose::derivInsert(x.derivatives(), 2, 1.0);
199 :
200 4 : _water_fp_derivs = true;
201 4 : FPADReal ad_rho = this->rho_from_p_T_X(p, T, x);
202 :
203 4 : rho = ad_rho.value();
204 4 : drho_dp = ad_rho.derivatives()[0];
205 4 : drho_dT = ad_rho.derivatives()[1];
206 4 : drho_dx = ad_rho.derivatives()[2];
207 4 : }
208 :
209 : Real
210 13 : BrineFluidProperties::mu_from_p_T_X(Real pressure, Real temperature, Real xnacl) const
211 : {
212 : // Correlation requires molal concentration (mol/kg)
213 13 : const Real mol = massFractionToMolalConc(xnacl);
214 13 : const Real mol2 = mol * mol;
215 13 : const Real mol3 = mol2 * mol;
216 :
217 : // Correlation requires temperature in C
218 13 : const Real Tc = temperature - _T_c2k;
219 :
220 13 : const Real a = 1.0 + 0.0816 * mol + 0.0122 * mol2 + 0.128e-3 * mol3 +
221 13 : 0.629e-3 * Tc * (1.0 - std::exp(-0.7 * mol));
222 :
223 13 : const Real water_viscosity = _water_fp->mu_from_p_T(pressure, temperature);
224 :
225 13 : return a * water_viscosity;
226 : }
227 :
228 : void
229 4 : BrineFluidProperties::mu_from_p_T_X(Real pressure,
230 : Real temperature,
231 : Real xnacl,
232 : Real & mu,
233 : Real & dmu_dp,
234 : Real & dmu_dT,
235 : Real & dmu_dx) const
236 : {
237 : // Viscosity of water and derivatives wrt pressure and temperature
238 : Real muw, dmuw_dp, dmuw_dT;
239 4 : _water_fp->mu_from_p_T(pressure, temperature, muw, dmuw_dp, dmuw_dT);
240 :
241 : // Correlation requires molal concentration (mol/kg)
242 4 : Real mol = massFractionToMolalConc(xnacl);
243 4 : Real dmol_dx = 1.0 / ((1.0 - xnacl) * (1.0 - xnacl) * _Mnacl);
244 4 : Real mol2 = mol * mol;
245 4 : Real mol3 = mol2 * mol;
246 :
247 : // Correlation requires temperature in C
248 4 : Real Tc = temperature - _T_c2k;
249 :
250 4 : Real a = 1.0 + 0.0816 * mol + 0.0122 * mol2 + 0.128e-3 * mol3 +
251 4 : 0.629e-3 * Tc * (1.0 - std::exp(-0.7 * mol));
252 : Real da_dx =
253 4 : (0.0816 + 0.0244 * mol + 3.84e-4 * mol2 + 4.403e-4 * Tc * std::exp(-0.7 * mol)) * dmol_dx;
254 4 : Real da_dT = 0.629e-3 * (1.0 - std::exp(-0.7 * mol));
255 :
256 4 : mu = a * muw;
257 4 : dmu_dp = a * dmuw_dp;
258 4 : dmu_dx = da_dx * muw;
259 4 : dmu_dT = da_dT * muw + a * dmuw_dT;
260 4 : }
261 :
262 : FPADReal
263 889 : BrineFluidProperties::h_from_p_T_X(const FPADReal & pressure,
264 : const FPADReal & temperature,
265 : const FPADReal & xnacl) const
266 : {
267 : FPADReal q1, q2, q10, q11, q12, q20, q21, q22, q23, q1x1, q2x1, Th;
268 :
269 : // The correlation requires the pressure in bar, not Pa.
270 889 : const FPADReal pbar = pressure * 1.0e-5;
271 889 : const FPADReal pbar2 = pbar * pbar;
272 :
273 : // The correlation requires mole fraction
274 889 : const FPADReal Xnacl = massFractionToMoleFraction(xnacl);
275 :
276 889 : q11 = -32.1724 + 0.0621255 * pbar;
277 889 : q21 = -1.69513 - 4.52781e-4 * pbar - 6.04279e-8 * pbar2;
278 889 : q22 = 0.0612567 + 1.88082e-5 * pbar;
279 :
280 889 : q1x1 = 47.9048 - 9.36994e-3 * pbar + 6.51059e-6 * pbar2;
281 889 : q2x1 = 0.241022 + 3.45087e-5 * pbar - 4.28356e-9 * pbar2;
282 :
283 1778 : q12 = -q11 - q1x1;
284 : q10 = q1x1;
285 :
286 889 : q20 = 1.0 - q21 * std::sqrt(q22);
287 889 : q23 = q2x1 - q20 - q21 * std::sqrt(1.0 + q22);
288 :
289 889 : q1 = q10 + q11 * (1.0 - Xnacl) + q12 * (1.0 - Xnacl) * (1.0 - Xnacl);
290 1778 : q2 = q20 + q21 * std::sqrt(Xnacl + q22) + q23 * Xnacl;
291 : // The temperature Th where the brine has the same enthalpy as pure water
292 : // Note: correlation uses temperature in Celcius
293 1778 : Th = q1 + q2 * (temperature - _T_c2k);
294 :
295 : // The brine enthalpy is then given by the enthalpy of water at temperature Th
296 : // Note: water enthalpy requires temperature in Kelvin
297 : FPADReal enthalpy;
298 889 : if (_water_fp_derivs)
299 : {
300 : Real h, dh_dp, dh_dT;
301 4 : _water_fp->h_from_p_T(pressure.value(), Th.value() + _T_c2k, h, dh_dp, dh_dT);
302 : enthalpy = h;
303 :
304 8 : enthalpy.derivatives() = pressure.derivatives() * dh_dp + Th.derivatives() * dh_dT;
305 : }
306 : else
307 885 : enthalpy = _water_fp->h_from_p_T(pressure.value(), Th.value() + _T_c2k);
308 :
309 889 : return enthalpy;
310 : }
311 :
312 : Real
313 885 : BrineFluidProperties::h_from_p_T_X(Real pressure, Real temperature, Real xnacl) const
314 : {
315 : // Initialise the AD value (no derivatives required)
316 : FPADReal p = pressure;
317 : FPADReal T = temperature;
318 : FPADReal x = xnacl;
319 :
320 885 : _water_fp_derivs = false;
321 885 : return h_from_p_T_X(p, T, x).value();
322 : }
323 :
324 : void
325 2 : BrineFluidProperties::h_from_p_T_X(Real pressure,
326 : Real temperature,
327 : Real xnacl,
328 : Real & h,
329 : Real & dh_dp,
330 : Real & dh_dT,
331 : Real & dh_dx) const
332 : {
333 : // Initialise the AD value and derivatives
334 : FPADReal p = pressure;
335 : Moose::derivInsert(p.derivatives(), 0, 1.0);
336 : FPADReal T = temperature;
337 : Moose::derivInsert(T.derivatives(), 1, 1.0);
338 : FPADReal x = xnacl;
339 : Moose::derivInsert(x.derivatives(), 2, 1.0);
340 :
341 2 : _water_fp_derivs = true;
342 2 : FPADReal ad_h = h_from_p_T_X(p, T, x);
343 :
344 2 : h = ad_h.value();
345 2 : dh_dp = ad_h.derivatives()[0];
346 2 : dh_dT = ad_h.derivatives()[1];
347 2 : dh_dx = ad_h.derivatives()[2];
348 2 : }
349 :
350 : Real
351 435 : BrineFluidProperties::cp_from_p_T_X(Real pressure, Real temperature, Real xnacl) const
352 : {
353 : Real q1, q2, q10, q11, q12, q20, q21, q22, q23, q1x1, q2x1, Th;
354 :
355 : // The correlation requires the pressure in bar, not Pa.
356 435 : Real pbar = pressure * 1.0e-5;
357 435 : Real pbar2 = pbar * pbar;
358 :
359 : // The correlation requires mole fraction
360 435 : Real Xnacl = massFractionToMoleFraction(xnacl);
361 :
362 435 : q11 = -32.1724 + 0.0621255 * pbar;
363 435 : q21 = -1.69513 - 4.52781e-4 * pbar - 6.04279e-8 * pbar2;
364 435 : q22 = 0.0612567 + 1.88082e-5 * pbar;
365 :
366 435 : q1x1 = 47.9048 - 9.36994e-3 * pbar + 6.51059e-6 * pbar2;
367 435 : q2x1 = 0.241022 + 3.45087e-5 * pbar - 4.28356e-9 * pbar2;
368 :
369 435 : q12 = -q11 - q1x1;
370 : q10 = q1x1;
371 :
372 435 : q20 = 1.0 - q21 * std::sqrt(q22);
373 435 : q23 = q2x1 - q20 - q21 * std::sqrt(1.0 + q22);
374 :
375 435 : q1 = q10 + q11 * (1.0 - Xnacl) + q12 * (1.0 - Xnacl) * (1.0 - Xnacl);
376 435 : q2 = q20 + q21 * std::sqrt(Xnacl + q22) + q23 * Xnacl;
377 : // The temperature Th where the brine has the same isobaric heat capacity
378 : // as pure water. Note: correlation uses temperature in Celcius
379 435 : Th = q1 + q2 * (temperature - _T_c2k);
380 :
381 : // The brine isobaric heat capacity is then given by the isobaric heat
382 : // capacity of water at temperature Th multiplied by q2
383 : // Note: water isobaric heat capacity requires temperature in Kelvin
384 435 : return q2 * _water_fp->cp_from_p_T(pressure, Th + _T_c2k);
385 : }
386 :
387 : FPADReal
388 2 : BrineFluidProperties::e_from_p_T_X(const FPADReal & pressure,
389 : const FPADReal & temperature,
390 : const FPADReal & xnacl) const
391 : {
392 2 : FPADReal enthalpy = h_from_p_T_X(pressure, temperature, xnacl);
393 2 : FPADReal density = rho_from_p_T_X(pressure, temperature, xnacl);
394 :
395 2 : return enthalpy - pressure / density;
396 : }
397 :
398 : Real
399 444 : BrineFluidProperties::e_from_p_T_X(Real pressure, Real temperature, Real xnacl) const
400 : {
401 444 : Real enthalpy = h_from_p_T_X(pressure, temperature, xnacl);
402 444 : Real density = rho_from_p_T_X(pressure, temperature, xnacl);
403 :
404 444 : return enthalpy - pressure / density;
405 : }
406 :
407 : void
408 2 : BrineFluidProperties::e_from_p_T_X(Real pressure,
409 : Real temperature,
410 : Real xnacl,
411 : Real & e,
412 : Real & de_dp,
413 : Real & de_dT,
414 : Real & de_dx) const
415 : {
416 : // Initialise the AD value and derivatives
417 : FPADReal p = pressure;
418 : Moose::derivInsert(p.derivatives(), 0, 1.0);
419 : FPADReal T = temperature;
420 : Moose::derivInsert(T.derivatives(), 1, 1.0);
421 : FPADReal x = xnacl;
422 : Moose::derivInsert(x.derivatives(), 2, 1.0);
423 :
424 2 : _water_fp_derivs = true;
425 2 : FPADReal ad_e = e_from_p_T_X(p, T, x);
426 :
427 2 : e = ad_e.value();
428 2 : de_dp = ad_e.derivatives()[0];
429 2 : de_dT = ad_e.derivatives()[1];
430 2 : de_dx = ad_e.derivatives()[2];
431 2 : }
432 :
433 : Real
434 3 : BrineFluidProperties::k_from_p_T_X(Real pressure, Real temperature, Real xnacl) const
435 : {
436 : // Correlation requires molal concentration (mol/kg)
437 3 : Real mol = massFractionToMolalConc(xnacl);
438 : // Correlation requires temperature in C
439 3 : Real Tc = temperature - _T_c2k;
440 :
441 3 : Real S = 100.0 * _Mnacl * mol / (1.0 + _Mnacl * mol);
442 3 : Real lambdaw = _water_fp->k_from_p_T(pressure, temperature);
443 3 : Real lambda = 1.0 - (2.3434e-3 - 7.924e-6 * Tc + 3.924e-8 * Tc * Tc) * S +
444 3 : (1.06e-5 - 2.0e-8 * Tc - 1.2e-10 * Tc * Tc) * S * S;
445 :
446 3 : return lambda * lambdaw;
447 : }
448 :
449 : Real
450 3 : BrineFluidProperties::vaporPressure(Real temperature, Real xnacl) const
451 : {
452 : // Correlation requires molal concentration (mol/kg)
453 3 : Real mol = massFractionToMolalConc(xnacl);
454 3 : Real mol2 = mol * mol;
455 3 : Real mol3 = mol2 * mol;
456 :
457 3 : Real a = 1.0 + 5.93582e-6 * mol - 5.19386e-5 * mol2 + 1.23156e-5 * mol3;
458 3 : Real b = 1.1542e-6 * mol + 1.41254e-7 * mol2 - 1.92476e-8 * mol3 - 1.70717e-9 * mol * mol3 +
459 3 : 1.0539e-10 * mol2 * mol3;
460 :
461 : // The temperature of pure water at the same pressure as the brine is given by
462 3 : Real th20 = std::exp(std::log(temperature) / (a + b * temperature));
463 :
464 : // The brine vapour pressure is then found by evaluating the saturation pressure for pure water
465 : // using this effective temperature
466 3 : return _water_fp->vaporPressure(th20);
467 : }
468 :
469 : Real
470 3 : BrineFluidProperties::haliteSolubility(Real temperature) const
471 : {
472 : // This correlation requires temperature in Celcius
473 3 : Real Tc = temperature - _T_c2k;
474 :
475 3 : return (26.18 + 7.2e-3 * Tc + 1.06e-4 * Tc * Tc) / 100.0;
476 : }
477 :
478 : Real
479 23 : BrineFluidProperties::massFractionToMolalConc(Real xnacl) const
480 : {
481 23 : return xnacl / ((1.0 - xnacl) * _Mnacl);
482 : }
483 :
484 : Real
485 435 : BrineFluidProperties::massFractionToMoleFraction(Real xnacl) const
486 : {
487 : // The average molar mass of brine from the mass fraction
488 435 : Real Mbrine = molarMass(xnacl);
489 : // The mole fraction is then
490 435 : return xnacl * Mbrine / _Mnacl;
491 : }
492 :
493 : FPADReal
494 1784 : BrineFluidProperties::massFractionToMoleFraction(const FPADReal & xnacl) const
495 : {
496 : // The average molar mass of brine from the mass fraction
497 1784 : FPADReal Mbrine = molarMass(xnacl);
498 : // The mole fraction is then
499 1784 : return xnacl * Mbrine / _Mnacl;
500 : }
501 :
502 : Real
503 0 : BrineFluidProperties::henryConstant(Real temperature, const std::vector<Real> & coeffs) const
504 : {
505 0 : return _water97_fp->henryConstant(temperature, coeffs);
506 : }
507 :
508 : void
509 0 : BrineFluidProperties::henryConstant(Real temperature,
510 : const std::vector<Real> & coeffs,
511 : Real & Kh,
512 : Real & dKh_dT) const
513 : {
514 0 : _water97_fp->henryConstant(temperature, coeffs, Kh, dKh_dT);
515 0 : }
516 :
517 : ADReal
518 0 : BrineFluidProperties::henryConstant(const ADReal & temperature,
519 : const std::vector<Real> & coeffs) const
520 : {
521 : Real Kh, dKh_dT;
522 0 : henryConstant(temperature.value(), coeffs, Kh, dKh_dT);
523 :
524 0 : ADReal henry = Kh;
525 0 : henry.derivatives() = temperature.derivatives() * dKh_dT;
526 :
527 0 : return henry;
528 : }
|