https://mooseframework.inl.gov
Loading...
Searching...
No Matches
TabulatedFluidProperties.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 "MooseUtils.h"
12#include "Conversion.h"
13#include "KDTree.h"
15#include "NewtonInversion.h"
16
17// C++ includes
18#include <fstream>
19#include <ctime>
20#include <cmath>
21#include <regex>
22
25{
28 "Single phase fluid properties computed using bi-dimensional interpolation of tabulated "
29 "values.");
30
31 // Which interpolations to create
32 params.addParam<bool>("create_pT_interpolations",
33 true,
34 "Whether to load (from file) or create (from a fluid property object) "
35 "properties interpolations from pressure and temperature");
36 params.addParam<bool>(
37 "create_ve_interpolations",
38 false,
39 "Whether to load (from file) or create (from a fluid property object) "
40 "properties interpolations from specific volume and specific internal energy");
41
42 // Input / output
43 params.addParam<UserObjectName>("fp", "The name of the FluidProperties UserObject");
44 params.deprecateParam("fp", "input_fp", "12/12/26");
45 // deprecate to be able to put "fp" in the GlobalParams without creating issues with TabulatedFP
46 params.addParam<FileName>("fluid_property_file",
47 "Name of the csv file containing the tabulated fluid property data.");
48 params.addParam<FileName>(
49 "fluid_property_ve_file",
50 "Name of the csv file containing the tabulated (v,e) fluid property data.");
51 params.addParam<FileName>("fluid_property_output_file",
52 "Name of the CSV file which can be output with the tabulation. This "
53 "file can then be read as a 'fluid_property_file'");
54 params.addParam<FileName>(
55 "fluid_property_ve_output_file",
56 "Name of the CSV file which can be output with the (v,e) tabulation. This "
57 "file can then be read as a 'fluid_property_ve_file'");
58 params.addDeprecatedParam<bool>(
59 "save_file",
60 "Whether to save the csv fluid properties file",
61 "This parameter is no longer required. Whether to save a CSV tabulation file is controlled "
62 "by specifying the 'fluid_property_output_file' parameter");
63 params.addParam<bool>("skip_header_tabulation",
64 false,
65 "Whether to skip the header in the tabulation output, useful for testing");
66
67 // Data source on a per-property basis
68 MultiMooseEnum properties(
69 "density enthalpy internal_energy viscosity k c cv cp entropy pressure temperature",
70 "density enthalpy internal_energy viscosity");
71 params.addParam<MultiMooseEnum>("interpolated_properties",
72 properties,
73 "Properties to interpolate. If unspecified and a data file is "
74 "provided, the properties from the data file will be used. If "
75 "specified, some properties from the data file can be ignored.");
76
77 // (p,T) grid parameters
78 params.addRangeCheckedParam<Real>(
79 "temperature_min", 300, "temperature_min > 0", "Minimum temperature for tabulated data.");
80 params.addParam<Real>("temperature_max", 500, "Maximum temperature for tabulated data.");
81 params.addRangeCheckedParam<Real>(
82 "pressure_min", 1e5, "pressure_min > 0", "Minimum pressure for tabulated data.");
83 params.addParam<Real>("pressure_max", 50.0e6, "Maximum pressure for tabulated data.");
84 params.addRangeCheckedParam<unsigned int>(
85 "num_T", 100, "num_T > 0", "Number of points to divide temperature range.");
86 params.addRangeCheckedParam<unsigned int>(
87 "num_p", 100, "num_p > 0", "Number of points to divide pressure range.");
88
89 // (v,e) grid parameters
90 params.addParam<Real>("e_min", "Minimum specific internal energy for tabulated data.");
91 params.addParam<Real>("e_max", "Maximum specific internal energy for tabulated data.");
92 params.addRangeCheckedParam<Real>(
93 "v_min", "v_min > 0", "Minimum specific volume for tabulated data.");
94 params.addRangeCheckedParam<Real>(
95 "v_max", "v_max > 0", "Maximum specific volume for tabulated data.");
96 params.addParam<bool>("construct_pT_from_ve",
97 false,
98 "If the lookup table (p, T) as functions of (v, e) should be constructed.");
99 params.addParam<bool>("construct_pT_from_vh",
100 false,
101 "If the lookup table (p, T) as functions of (v, h) should be constructed.");
102 params.addRangeCheckedParam<unsigned int>(
103 "num_v",
104 100,
105 "num_v > 0",
106 "Number of points to divide specific volume range for (v,e) lookups.");
107 params.addRangeCheckedParam<unsigned int>("num_e",
108 100,
109 "num_e > 0",
110 "Number of points to divide specific internal energy "
111 "range for (v,e) lookups.");
112 params.addParam<bool>(
113 "use_log_grid_v",
114 false,
115 "Option to use a base-10 logarithmically-spaced grid for specific volume instead of a "
116 "linearly-spaced grid.");
117 params.addParam<bool>(
118 "use_log_grid_e",
119 false,
120 "Option to use a base-10 logarithmically-spaced grid for specific internal energy instead "
121 "of a linearly-spaced grid.");
122 params.addParam<bool>(
123 "use_log_grid_h",
124 false,
125 "Option to use a base-10 logarithmically-spaced grid for specific enthalpy instead "
126 "of a linearly-spaced grid.");
127
128 // Out of bounds behavior
129 params.addDeprecatedParam<bool>(
130 "error_on_out_of_bounds",
131 "Whether pressure or temperature from tabulation exceeding user-specified bounds leads to "
132 "an error.",
133 "This parameter has been replaced by the 'out_of_bounds_behavior' parameter which offers "
134 "more flexibility. The option to error is called 'throw' in that parameter.");
135 // NOTE: this enum must remain the same as OOBBehavior in the header
136 MooseEnum OOBBehavior("ignore throw declare_invalid warn_invalid set_to_closest_bound", "throw");
137 params.addParam<MooseEnum>("out_of_bounds_behavior",
139 "Property evaluation behavior when evaluated outside the "
140 "user-specified or tabulation-specified bounds");
141
142 // This is generally a bad idea. However, several properties have not been tabulated so several
143 // tests are relying on the original fp object to provide the value (for example for the
144 // vaporPressure())
145 params.addParam<bool>(
146 "allow_fp_and_tabulation", false, "Whether to allow the two sources of data concurrently");
147
148 params.addParamNamesToGroup("fluid_property_file fluid_property_ve_file "
149 "fluid_property_output_file fluid_property_ve_output_file",
150 "Tabulation file read/write");
151 params.addParamNamesToGroup("construct_pT_from_ve construct_pT_from_vh",
152 "Variable set conversion");
153 params.addParamNamesToGroup("temperature_min temperature_max pressure_min pressure_max e_min "
154 "e_max v_min v_max error_on_out_of_bounds out_of_bounds_behavior",
155 "Tabulation and interpolation bounds");
157 "num_T num_p num_v num_e use_log_grid_v use_log_grid_e use_log_grid_h",
158 "Tabulation and interpolation discretization");
159
160 return params;
161}
162
164 : SinglePhaseFluidProperties(parameters),
165 _file_name_in(isParamValid("fluid_property_file") ? getParam<FileName>("fluid_property_file")
166 : ""),
167 _file_name_ve_in(
168 isParamValid("fluid_property_ve_file") ? getParam<FileName>("fluid_property_ve_file") : ""),
169 _file_name_out(isParamValid("fluid_property_output_file")
170 ? getParam<FileName>("fluid_property_output_file")
171 : ""),
172 _file_name_ve_out(isParamValid("fluid_property_ve_output_file")
173 ? getParam<FileName>("fluid_property_ve_output_file")
174 : ""),
175 _save_file(isParamValid("save_file") ? getParam<bool>("save_file")
176 : (isParamValid("fluid_property_output_file") ||
177 isParamValid("fluid_property_ve_output_file"))),
178 _create_direct_pT_interpolations(getParam<bool>("create_pT_interpolations")),
179 _create_direct_ve_interpolations(getParam<bool>("create_ve_interpolations")),
180 _temperature_min(getParam<Real>("temperature_min")),
181 _temperature_max(getParam<Real>("temperature_max")),
182 _pressure_min(getParam<Real>("pressure_min")),
183 _pressure_max(getParam<Real>("pressure_max")),
184 _num_T(getParam<unsigned int>("num_T")),
185 _num_p(getParam<unsigned int>("num_p")),
186 // work-around to allow use of 'fp' in GlobalParams
187 _fp(isParamValid("input_fp") ? ((getParam<UserObjectName>("input_fp") != name())
188 ? &getUserObject<SinglePhaseFluidProperties>("input_fp")
189 : nullptr)
190 : nullptr),
191 _allow_fp_and_tabulation(getParam<bool>("allow_fp_and_tabulation")),
192 _interpolated_properties_enum(getParam<MultiMooseEnum>("interpolated_properties")),
193 _interpolated_properties(),
194 _interpolate_density(false),
195 _interpolate_enthalpy(false),
196 _interpolate_internal_energy(false),
197 _interpolate_viscosity(false),
198 _interpolate_k(false),
199 _interpolate_c(false),
200 _interpolate_cp(false),
201 _interpolate_cv(false),
202 _interpolate_entropy(false),
203 _interpolate_pressure(false),
204 _interpolate_temperature(false),
205 _density_idx(libMesh::invalid_uint),
206 _enthalpy_idx(libMesh::invalid_uint),
207 _internal_energy_idx(libMesh::invalid_uint),
208 _viscosity_idx(libMesh::invalid_uint),
209 _k_idx(libMesh::invalid_uint),
210 _c_idx(libMesh::invalid_uint),
211 _cp_idx(libMesh::invalid_uint),
212 _cv_idx(libMesh::invalid_uint),
213 _entropy_idx(libMesh::invalid_uint),
214 _p_idx(libMesh::invalid_uint),
215 _T_idx(libMesh::invalid_uint),
216 _csv_reader(_file_name_in, &_communicator),
217 _construct_pT_from_ve(getParam<bool>("construct_pT_from_ve")),
218 _construct_pT_from_vh(getParam<bool>("construct_pT_from_vh")),
219 _initial_setup_done(false),
220 _num_v(getParam<unsigned int>("num_v")),
221 _num_e(getParam<unsigned int>("num_e")),
222 _log_space_v(getParam<bool>("use_log_grid_v")),
223 _log_space_e(getParam<bool>("use_log_grid_e")),
224 _log_space_h(getParam<bool>("use_log_grid_h")),
225 _OOBBehavior(getParam<MooseEnum>("out_of_bounds_behavior")),
226 _e_min(0),
227 _e_max(0),
228 _v_min(0),
229 _v_max(0)
230{
231 // Check that initial guess (used in Newton Method) is within min and max values
232 checkInitialGuess(false);
233 // Sanity check on minimum and maximum temperatures and pressures
235 mooseError("temperature_max must be greater than temperature_min");
237 mooseError("pressure_max must be greater than pressure_min");
238
239 // Set (v,e) bounds if specified by the user
240 if (isParamValid("e_min") && isParamValid("e_max"))
241 {
242 _e_min = getParam<Real>("e_min");
243 _e_max = getParam<Real>("e_max");
244 _e_bounds_specified = true;
245 }
246 else if (isParamValid("e_min") || isParamValid("e_max"))
247 paramError("e_min",
248 "Either both or none of the min and max values of the specific internal energy "
249 "should be specified");
250 else
251 _e_bounds_specified = false;
252 if (isParamValid("v_min") && isParamValid("v_max"))
253 {
254 _v_min = getParam<Real>("v_min");
255 _v_max = getParam<Real>("v_max");
256 _v_bounds_specified = true;
257 }
258 else if (isParamValid("v_min") || isParamValid("v_max"))
259 paramError("v_min",
260 "Either both or none of the min and max values of the specific volume "
261 "should be specified");
262 else
263 _v_bounds_specified = false;
264
265 // Handle out of bounds behavior parameters and deprecation
266 if (isParamValid("error_on_out_of_bounds") && getParam<bool>("error_on_out_of_bounds") &&
268 paramError("out_of_bounds_behavior", "Inconsistent selection of out of bounds behavior.");
269 else if (isParamValid("error_on_out_of_bounds") && !getParam<bool>("error_on_out_of_bounds"))
271
272 // Lines starting with # in the data file are treated as comments
274
275 // Can only and must receive one source of data between fp and tabulations
276 if (_fp && (!_file_name_in.empty() || !_file_name_ve_in.empty()) && !_allow_fp_and_tabulation)
278 "fluid_property_file",
279 "Cannot supply both a fluid properties object with 'input_fp' and a source tabulation "
280 "file with 'fluid_property_file', unless 'allow_fp_and_tabulation' is set to true");
281 if (!_fp && _file_name_in.empty() && _file_name_ve_in.empty())
283 "fluid_property_file",
284 "Either a fluid properties object with the parameter 'input_fp' and a source tabulation "
285 "file with the parameter 'fluid_property_file' or 'fluid_property_ve_file' should "
286 "be provided.");
288 paramError("create_pT_interpolations", "Must create either (p,T) or (v,e) interpolations");
289
290 // Some parameters are not used when reading a tabulation
291 if (!_fp && !_file_name_in.empty() &&
292 (isParamSetByUser("pressure_min") || isParamSetByUser("pressure_max") ||
293 isParamSetByUser("temperature_min") || isParamSetByUser("temperature_max")))
294 mooseWarning("User-specified bounds in pressure and temperature are ignored when reading a "
295 "'fluid_property_file'. The tabulation bounds are selected "
296 "from the bounds of the input tabulation.");
297 if (!_fp && !_file_name_in.empty() && (isParamSetByUser("num_p") || isParamSetByUser("num_T")))
298 mooseWarning("User-specified grid sizes in pressure and temperature are ignored when reading a "
299 "'fluid_property_file'. The tabulation bounds are selected "
300 "from the bounds of the input tabulation.");
301 if (!_fp && !_file_name_ve_in.empty() &&
302 (isParamSetByUser("v_min") || isParamSetByUser("v_max") || isParamSetByUser("e_min") ||
303 isParamSetByUser("e_max")))
305 "User-specified bounds in specific volume and internal energy are ignored when reading a "
306 "'fluid_property_ve_file'. The tabulation bounds are selected "
307 "from the bounds of the input tabulation.");
308 if (!_fp && !_file_name_ve_in.empty() && (isParamSetByUser("num_e") || isParamSetByUser("num_v")))
309 mooseWarning("User-specified grid sizes in specific volume and internal energy are ignored "
310 "when reading a 'fluid_property_ve_file'. The tabulation widths are read "
311 "from the input tabulation.");
312 if (!_file_name_ve_in.empty() && (_log_space_v || _log_space_e))
314 "User specfied logarithmic grids in specific volume and energy are ignored when reading a "
315 "'fluid_properties_ve_file'. The tabulation grid is read from the input tabulation");
316}
317
318void
320{
322 return;
323 _initial_setup_done = true;
324
326 {
327 // If the user specified a (p, T) tabulation to read, use that
328 if (!_file_name_in.empty())
330 else
331 {
332 if (!_fp)
333 paramError("create_pT_interpolations",
334 "No FluidProperties (specified with 'input_fp' parameter) exists. Either "
335 "specify a 'input_fp' or "
336 "specify a (p, T) tabulation file with the 'fluid_property_file' parameter");
337 _console << name() + ": Generating (p, T) tabulated data\n";
338 _console << std::flush;
339
341 }
342 }
343
345 {
346 // If the user specified a (v, e) tabulation to read, use that
347 if (!_file_name_ve_in.empty())
349 else
350 {
351 if (!_fp)
352 paramError("create_ve_interpolations",
353 "No FluidProperties (specified with 'input_fp' parameter) exists. Either "
354 "specify a 'input_fp' or "
355 "specify a (v, e) tabulation file with the 'fluid_property_ve_file' parameter");
356 _console << name() + ": Generating (v, e) tabulated data\n";
357 _console << std::flush;
358
360 }
361 }
362
365
366 // Could be needed to get bounds computed from (p, T) bounds
369
370 // Write tabulated data to file
371 if (_save_file)
372 {
373 _console << name() + ": Writing tabulated data to " << _file_name_out << "\n";
375 }
376}
377
378std::string
380{
381 if (_fp)
382 return _fp->fluidName();
383 else
384 return "TabulationFromFile";
385}
386
387Real
389{
390 if (_fp)
391 return _fp->molarMass();
392 else
394}
395
396Real
397TabulatedFluidProperties::v_from_p_T(Real pressure, Real temperature) const
398{
400 {
401 checkInputVariables(pressure, temperature);
402 return 1.0 / _property_ipol[_density_idx]->sample(pressure, temperature);
403 }
405 {
406 checkInputVariables(pressure, temperature);
407 Real v, e;
408 auto p_from_v_e = [&](Real v, Real e, Real & new_p, Real & dp_dv, Real & dp_de)
409 { this->p_from_v_e(v, e, new_p, dp_dv, dp_de); };
410 auto T_from_v_e = [&](Real v, Real e, Real & new_T, Real & dT_dv, Real & dT_de)
411 { this->T_from_v_e(v, e, new_T, dT_dv, dT_de); };
413 temperature,
414 (_v_min + _v_max) / 2,
415 (_e_min + _e_max) / 2,
416 v,
417 e,
422 name() + "::v_from_p_T",
425 return v;
426 }
427 else
428 {
429 if (_fp)
430 return 1.0 / _fp->rho_from_p_T(pressure, temperature);
431 else
432 NeedTabulationOrFPError("AD v_from_p_T", "density");
433 }
434}
435
436ADReal
437TabulatedFluidProperties::v_from_p_T(const ADReal & pressure, const ADReal & temperature) const
438{
440 {
441 ADReal pressure_nc = pressure, temperature_nc = temperature;
442 checkInputVariables(pressure_nc, temperature_nc);
443 return 1.0 / _property_ipol[_density_idx]->sample(pressure_nc, temperature_nc);
444 }
446 {
447 ADReal pressure_nc = pressure, temperature_nc = temperature;
448 checkInputVariables(pressure_nc, temperature_nc);
449 ADReal v, e;
450 auto p_from_v_e = [&](ADReal v, ADReal e, ADReal & new_p, ADReal & dp_dv, ADReal & dp_de)
451 { this->p_from_v_e(v, e, new_p, dp_dv, dp_de); };
452 auto T_from_v_e = [&](ADReal v, ADReal e, ADReal & new_T, ADReal & dT_dv, ADReal & dT_de)
453 { this->T_from_v_e(v, e, new_T, dT_dv, dT_de); };
455 temperature_nc,
456 (_v_min + _v_max) / 2,
457 (_e_min + _e_max) / 2,
458 v,
459 e,
464 name() + "::v_from_p_T",
467 return v;
468 }
469 else
470 {
471 if (_fp)
472 return 1.0 / _fp->rho_from_p_T(pressure, temperature);
473 else
474 NeedTabulationOrFPError("AD v_from_p_T", "density");
475 }
476}
477
478void
480 Real pressure, Real temperature, Real & v, Real & dv_dp, Real & dv_dT) const
481{
482 Real rho = 0, drho_dp = 0, drho_dT = 0;
484 {
485 checkInputVariables(pressure, temperature);
486 _property_ipol[_density_idx]->sampleValueAndDerivatives(
487 pressure, temperature, rho, drho_dp, drho_dT);
488 }
489 else
490 {
491 if (_fp)
492 _fp->rho_from_p_T(pressure, temperature, rho, drho_dp, drho_dT);
493 else
494 NeedTabulationOrFPError("v_from_p_T with derivatives", "density");
495 }
496 // convert from rho to v
497 v = 1.0 / rho;
498 dv_dp = -drho_dp / (rho * rho);
499 dv_dT = -drho_dT / (rho * rho);
500}
501
502Real
503TabulatedFluidProperties::rho_from_p_T(Real pressure, Real temperature) const
504{
506 {
507 checkInputVariables(pressure, temperature);
508 return _property_ipol[_density_idx]->sample(pressure, temperature);
509 }
511 return 1. / v_from_p_T(pressure, temperature);
512 else
513 {
514 if (_fp)
515 return _fp->rho_from_p_T(pressure, temperature);
516 else
517 NeedTabulationOrFPError("rho_from_p_T", "density");
518 }
519}
520
521ADReal
522TabulatedFluidProperties::rho_from_p_T(const ADReal & pressure, const ADReal & temperature) const
523{
525 {
526 ADReal pressure_nc = pressure, temperature_nc = temperature;
527 checkInputVariables(pressure_nc, temperature_nc);
528 return _property_ipol[_density_idx]->sample(pressure_nc, temperature_nc);
529 }
531 return 1. / v_from_p_T(pressure, temperature);
532 else
533 {
534 if (_fp)
535 return _fp->rho_from_p_T(pressure, temperature);
536 else
537 NeedTabulationOrFPError("AD rho_from_p_T", "density");
538 }
539}
540
541void
543 Real pressure, Real temperature, Real & rho, Real & drho_dp, Real & drho_dT) const
544{
546 {
547 checkInputVariables(pressure, temperature);
548 _property_ipol[_density_idx]->sampleValueAndDerivatives(
549 pressure, temperature, rho, drho_dp, drho_dT);
550 }
552 {
553 checkInputVariables(pressure, temperature);
554 // use finite differencing stencil
555 rho = rho_from_p_T(pressure, temperature);
556 Real eps = 1e-8;
557 drho_dp = (rho_from_p_T(pressure * (1 + eps), temperature) - rho) / (eps * pressure);
558 drho_dT = (rho_from_p_T(pressure, temperature * (1 + eps)) - rho) / (eps * temperature);
559 }
560 else
561 {
562 if (_fp)
563 _fp->rho_from_p_T(pressure, temperature, rho, drho_dp, drho_dT);
564 else
565 NeedTabulationOrFPError("rho_from_p_T with derivatives", "density");
566 }
567}
568
569void
571 const ADReal & temperature,
572 ADReal & rho,
573 ADReal & drho_dp,
574 ADReal & drho_dT) const
575{
577 {
578 ADReal p = pressure, T = temperature;
580 _property_ipol[_density_idx]->sampleValueAndDerivatives(p, T, rho, drho_dp, drho_dT);
581 }
582 else
583 {
584 if (_fp)
585 _fp->rho_from_p_T(pressure, temperature, rho, drho_dp, drho_dT);
586 else
587 NeedTabulationOrFPError("AD rho_from_p_T with derivatives", "density");
588 }
589}
590
591Real
593{
594 Real T = T_from_p_s(p, s);
595 return rho_from_p_T(p, T);
596}
597
598void
600 Real p, Real s, Real & rho, Real & drho_dp, Real & drho_ds) const
601{
602 Real T, dT_dp, dT_ds;
603 T_from_p_s(p, s, T, dT_dp, dT_ds);
604 Real drho_dp_T, drho_dT;
605 rho_from_p_T(p, T, rho, drho_dp_T, drho_dT);
606 drho_dp = drho_dT * dT_dp + drho_dp_T;
607 drho_ds = drho_dT * dT_ds;
608}
609
610Real
611TabulatedFluidProperties::e_from_p_T(Real pressure, Real temperature) const
612{
614 {
615 checkInputVariables(pressure, temperature);
617 return _property_ipol[_internal_energy_idx]->sample(pressure, temperature);
618 else
619 NeedTabulationError("internal_energy");
620 }
622 {
623 checkInputVariables(pressure, temperature);
624 const Real rho = rho_from_p_T(pressure, temperature);
625 auto lambda = [&](Real v, Real current_e, Real & new_T, Real & dT_dv, Real & dT_de)
626 { T_from_v_e(v, current_e, new_T, dT_dv, dT_de); };
627 const auto pair = FluidPropertiesUtils::NewtonSolve(1. / rho,
628 temperature,
629 /*initial guess*/ (_e_min + _e_max) / 2,
631 lambda,
632 name() + "::e_from_p_T",
635 return pair.first;
636 }
637 else
638 {
639 if (_fp)
640 return _fp->e_from_p_T(pressure, temperature);
641 else
642 NeedTabulationOrFPError("e_from_p_T", "internal_energy");
643 }
644}
645
646ADReal
647TabulatedFluidProperties::e_from_p_T(const ADReal & pressure, const ADReal & temperature) const
648{
650 {
651 ADReal pressure_nc = pressure, temperature_nc = temperature;
652 checkInputVariables(pressure_nc, temperature_nc);
654 return _property_ipol[_internal_energy_idx]->sample(pressure_nc, temperature_nc);
655 else
656 NeedTabulationError("internal_energy");
657 }
659 {
660 ADReal pressure_nc = pressure, temperature_nc = temperature;
661 checkInputVariables(pressure_nc, temperature_nc);
662 const ADReal rho = rho_from_p_T(pressure_nc, temperature_nc);
663 auto lambda = [&](ADReal v, ADReal current_e, ADReal & new_T, ADReal & dT_dv, ADReal & dT_de)
664 { T_from_v_e(v, current_e, new_T, dT_dv, dT_de); };
665 const auto pair = FluidPropertiesUtils::NewtonSolve(1. / rho,
666 temperature_nc,
667 /*initial guess*/ (_e_min + _e_max) / 2,
669 lambda,
670 name() + "::e_from_p_T",
673 return pair.first;
674 }
675 else
676 {
677 if (_fp)
678 return _fp->e_from_p_T(pressure, temperature);
679 else
680 NeedTabulationOrFPError("AD e_from_p_T", "internal_energy");
681 }
682}
683
684void
686 Real pressure, Real temperature, Real & e, Real & de_dp, Real & de_dT) const
687{
689 {
690 checkInputVariables(pressure, temperature);
692 _property_ipol[_internal_energy_idx]->sampleValueAndDerivatives(
693 pressure, temperature, e, de_dp, de_dT);
694 else
695 NeedTabulationError("internal_energy");
696 }
698 {
699 checkInputVariables(pressure, temperature);
700 // use finite differencing stencil
701 e = e_from_p_T(pressure, temperature);
702 Real eps = 1e-8;
703 de_dp = (e_from_p_T(pressure * (1 + eps), temperature) - e) / (eps * pressure);
704 de_dT = (e_from_p_T(pressure, temperature * (1 + eps)) - e) / (eps * temperature);
705 }
706 else
707 {
708 if (_fp)
709 _fp->e_from_p_T(pressure, temperature, e, de_dp, de_dT);
710 else
711 NeedTabulationOrFPError("e_from_p_T with derivatives", "internal_energy");
712 }
713}
714
715ADReal
717{
718 ADReal e;
719 // Use v,e data, we already have v from rho
721 {
722 auto lambda = [&](ADReal v, ADReal current_e, ADReal & new_p, ADReal & dp_dv, ADReal & dp_de)
723 { p_from_v_e(v, current_e, new_p, dp_dv, dp_de); };
724 const auto pair = FluidPropertiesUtils::NewtonSolve(1. / rho,
725 pressure,
726 /*initial guess*/ (_e_min + _e_max) / 2,
728 lambda,
729 name() + "::e_from_p_rho",
732 e = pair.first;
733 }
734 // May use rho_from_p_T with derivatives in a Newton solve
735 else
736 {
737 const auto T = T_from_p_rho(pressure, rho);
738 e = e_from_p_T(pressure, T);
739 }
740 return e;
741}
742
743Real
745{
746 Real e;
747 // Use v,e data, we already have v from rho
749 {
750 Real T_dummy = _T_initial_guess;
751 checkInputVariables(pressure, T_dummy);
752 auto lambda = [&](Real v, Real current_e, Real & new_p, Real & dp_dv, Real & dp_de)
753 { p_from_v_e(v, current_e, new_p, dp_dv, dp_de); };
754 const auto pair = FluidPropertiesUtils::NewtonSolve(1. / rho,
755 pressure,
756 /*initial guess*/ (_e_min + _e_max) / 2,
758 lambda,
759 name() + "::e_from_p_rho",
762 e = pair.first;
763 }
764 // May use rho_from_p_T with derivatives in a Newton solve
765 else
766 {
767 Real T = T_from_p_rho(pressure, rho);
768 e = e_from_p_T(pressure, T);
769 }
770 return e;
771}
772
773void
775 Real pressure, Real rho, Real & e, Real & de_dp, Real & de_drho) const
776{
777 // get derivatives of T wrt to pressure and density
778 Real T, dT_dp, dT_drho;
779 T_from_p_rho(pressure, rho, T, dT_dp, dT_drho);
780
781 // Get e, then derivatives of e wrt pressure and temperature
782 Real de_dp_at_const_T, de_dT;
783 e_from_p_T(pressure, T, e, de_dp_at_const_T, de_dT);
784
785 // Get the derivatives of density wrt pressure and temperature
786 Real rho_pT, drho_dp, drho_dT;
787 rho_from_p_T(pressure, T, rho_pT, drho_dp, drho_dT);
788
789 // derivatives of e wrt pressure and rho (what we want from e_from_p_rho)
790 de_drho = de_dT * dT_drho;
791 de_dp = de_dp_at_const_T - (de_drho * drho_dp);
792}
793
794Real
796{
797 Real T = _T_initial_guess;
799 {
800 checkInputVariables(pressure, T);
801 auto lambda = [&](Real p, Real current_T, Real & new_rho, Real & drho_dp, Real & drho_dT)
802 { rho_from_p_T(p, current_T, new_rho, drho_dp, drho_dT); };
804 rho,
807 lambda,
808 name() + "::T_from_p_rho",
811 .first;
812 }
814 T = T_from_v_e(1. / rho, e_from_p_rho(pressure, rho));
815 else
816 NeedTabulationOrFPError("T_from_p_rho", "temperature");
817 // check for nans
818 if (std::isnan(T))
819 mooseError("Conversion from pressure (p = ",
820 pressure,
821 ") and density (rho = ",
822 rho,
823 ") to temperature failed to converge.");
824 return T;
825}
826
827ADReal
829{
831 {
832 auto lambda =
833 [&](ADReal p, ADReal current_T, ADReal & new_rho, ADReal & drho_dp, ADReal & drho_dT)
834 { rho_from_p_T(p, current_T, new_rho, drho_dp, drho_dT); };
836 rho,
839 lambda,
840 name() + "::T_from_p_rho",
843 .first;
844 // check for nans
845 if (std::isnan(T.value()))
846 mooseError("Conversion from pressure (p = ",
847 pressure,
848 ") and density (rho = ",
849 rho,
850 ") to temperature failed to converge.");
851 return T;
852 }
854 return T_from_v_e(1. / rho, e_from_p_rho(pressure, rho));
855 else
856 NeedTabulationOrFPError("AD T_from_p_rho", "temperature");
857}
858
859void
861 Real pressure, Real rho, Real & T, Real & dT_dp, Real & dT_drho) const
862{
863 T = T_from_p_rho(pressure, rho);
864 Real eps = 1e-8;
865 dT_dp = (T_from_p_rho(pressure * (1 + eps), rho) - T) / (eps * pressure);
866 dT_drho = (T_from_p_rho(pressure, rho * (1 + eps)) - T) / (eps * rho);
867}
868
869Real
870TabulatedFluidProperties::T_from_p_s(Real pressure, Real s) const
871{
872 auto lambda = [&](Real p, Real current_T, Real & new_s, Real & ds_dp, Real & ds_dT)
873 { s_from_p_T(p, current_T, new_s, ds_dp, ds_dT); };
874 Real T = FluidPropertiesUtils::NewtonSolve(pressure,
875 s,
878 lambda,
879 name() + "::T_from_p_s",
882 .first;
883 // check for nans
884 if (std::isnan(T))
885 mooseError("Conversion from pressure (p = ",
886 pressure,
887 ") and entropy (s = ",
888 s,
889 ") to temperature failed to converge.");
890 return T;
891}
892
893void
895 Real pressure, Real s, Real & T, Real & dT_dp, Real & dT_ds) const
896{
897 T = T_from_p_s(pressure, s);
898 Real eps = 1e-8;
899 dT_dp = (T_from_p_s(pressure * (1 + eps), s) - T) / (eps * pressure);
900 dT_ds = (T_from_p_s(pressure, s * (1 + eps)) - T) / (eps * s);
901}
902
903Real
904TabulatedFluidProperties::h_from_p_T(Real pressure, Real temperature) const
905{
907 {
908 checkInputVariables(pressure, temperature);
910 return _property_ipol[_enthalpy_idx]->sample(pressure, temperature);
912 {
913 Real v, e;
914 SinglePhaseFluidProperties::v_e_from_p_T(pressure, temperature, v, e);
915 return _property_ve_ipol[_enthalpy_idx]->sample(v, e);
916 }
917 else
918 NeedTabulationError("enthalpy");
919 }
920 else
921 {
922 if (_fp)
923 return _fp->h_from_p_T(pressure, temperature);
924 else
925 NeedTabulationOrFPError("h_from_p_T", "enthalpy");
926 }
927}
928
929ADReal
930TabulatedFluidProperties::h_from_p_T(const ADReal & pressure, const ADReal & temperature) const
931{
933 {
934 ADReal pressure_nc = pressure, temperature_nc = temperature;
935 checkInputVariables(pressure_nc, temperature_nc);
937 return _property_ipol[_enthalpy_idx]->sample(pressure_nc, temperature_nc);
939 {
940 ADReal v, e;
941 SinglePhaseFluidProperties::v_e_from_p_T(pressure_nc, temperature_nc, v, e);
942 return _property_ve_ipol[_enthalpy_idx]->sample(v, e);
943 }
944 else
945 NeedTabulationError("enthalpy");
946 }
947 else if (_fp) // Assuming _fp can handle ADReal types
948 return _fp->h_from_p_T(pressure, temperature);
949 else
950 NeedTabulationOrFPError("AD h_from_p_T", "enthalpy");
951}
952
953void
955 Real pressure, Real temperature, Real & h, Real & dh_dp, Real & dh_dT) const
956{
958 {
959 checkInputVariables(pressure, temperature);
961 _property_ipol[_enthalpy_idx]->sampleValueAndDerivatives(
962 pressure, temperature, h, dh_dp, dh_dT);
964 {
965 Real v, e, dv_dp, dv_dT, de_dp, de_dT;
967 pressure, temperature, v, dv_dp, dv_dT, e, de_dp, de_dT);
968 Real dh_dv, dh_de;
969 _property_ve_ipol[_enthalpy_idx]->sampleValueAndDerivatives(v, e, h, dh_dv, dh_de);
970 dh_dp = dh_dv * dv_dp + dh_de * de_dp;
971 dh_dT = dh_dv * dv_dT + dh_de * de_dT;
972 }
973 else
974 NeedTabulationError("enthalpy");
975 }
976 else
977 {
978 if (_fp)
979 _fp->h_from_p_T(pressure, temperature, h, dh_dp, dh_dT);
980 else
981 NeedTabulationOrFPError("h_from_p_T with derivatives", "enthalpy");
982 }
983}
984
985Real
986TabulatedFluidProperties::mu_from_p_T(Real pressure, Real temperature) const
987{
989 {
990 checkInputVariables(pressure, temperature);
991 return _property_ipol[_viscosity_idx]->sample(pressure, temperature);
992 }
993 else
994 {
995 if (_fp)
996 return _fp->mu_from_p_T(pressure, temperature);
997 else
998 NeedTabulationOrFPError("mu_from_p_T", "viscosity");
999 }
1000}
1001
1002void
1004 Real pressure, Real temperature, Real & mu, Real & dmu_dp, Real & dmu_dT) const
1005{
1007 {
1008 checkInputVariables(pressure, temperature);
1009 _property_ipol[_viscosity_idx]->sampleValueAndDerivatives(
1010 pressure, temperature, mu, dmu_dp, dmu_dT);
1011 }
1012 else
1013 {
1014 if (_fp)
1015 _fp->mu_from_p_T(pressure, temperature, mu, dmu_dp, dmu_dT);
1016 else
1017 NeedTabulationOrFPError("mu_from_p_T with derivatives", "viscosity");
1018 }
1019}
1020
1021Real
1022TabulatedFluidProperties::c_from_p_T(Real pressure, Real temperature) const
1023{
1025 {
1026 checkInputVariables(pressure, temperature);
1027 return _property_ipol[_c_idx]->sample(pressure, temperature);
1028 }
1029 else
1030 {
1031 if (_fp)
1032 return _fp->c_from_p_T(pressure, temperature);
1033 else
1034 NeedTabulationOrFPError("c_from_p_T", "c");
1035 }
1036}
1037
1038void
1040 Real pressure, Real temperature, Real & c, Real & dc_dp, Real & dc_dT) const
1041{
1043 {
1044 checkInputVariables(pressure, temperature);
1045 _property_ipol[_c_idx]->sampleValueAndDerivatives(pressure, temperature, c, dc_dp, dc_dT);
1046 }
1047 else
1048 {
1049 if (_fp)
1050 _fp->c_from_p_T(pressure, temperature, c, dc_dp, dc_dT);
1051 else
1052 NeedTabulationOrFPError("c_from_p_T with derivatives", "c");
1053 }
1054}
1055
1056Real
1057TabulatedFluidProperties::cp_from_p_T(Real pressure, Real temperature) const
1058{
1060 {
1061 checkInputVariables(pressure, temperature);
1062 return _property_ipol[_cp_idx]->sample(pressure, temperature);
1063 }
1064 else
1065 {
1066 if (_fp)
1067 return _fp->cp_from_p_T(pressure, temperature);
1068 else
1069 NeedTabulationOrFPError("cp_from_p_T", "c");
1070 }
1071}
1072
1073void
1075 Real pressure, Real temperature, Real & cp, Real & dcp_dp, Real & dcp_dT) const
1076{
1078 {
1079 checkInputVariables(pressure, temperature);
1080 _property_ipol[_cp_idx]->sampleValueAndDerivatives(pressure, temperature, cp, dcp_dp, dcp_dT);
1081 }
1082 else
1083 {
1084 if (_fp)
1085 _fp->cp_from_p_T(pressure, temperature, cp, dcp_dp, dcp_dT);
1086 else
1087 NeedTabulationOrFPError("cp_from_p_T with derivatives", "cp");
1088 }
1089}
1090
1091Real
1092TabulatedFluidProperties::cv_from_p_T(Real pressure, Real temperature) const
1093{
1095 {
1096 checkInputVariables(pressure, temperature);
1097 return _property_ipol[_cv_idx]->sample(pressure, temperature);
1098 }
1099 else
1100 {
1101 if (_fp)
1102 return _fp->cv_from_p_T(pressure, temperature);
1103 else
1104 NeedTabulationOrFPError("cv_from_p_T", "cv");
1105 }
1106}
1107
1108void
1110 Real pressure, Real temperature, Real & cv, Real & dcv_dp, Real & dcv_dT) const
1111{
1112 if (_interpolate_cv)
1113 {
1114 checkInputVariables(pressure, temperature);
1115 _property_ipol[_cv_idx]->sampleValueAndDerivatives(pressure, temperature, cv, dcv_dp, dcv_dT);
1116 }
1117 else
1118 {
1119 if (_fp)
1120 _fp->cv_from_p_T(pressure, temperature, cv, dcv_dp, dcv_dT);
1121 else
1122 NeedTabulationOrFPError("cv_from_p_T with derivatives", "cv");
1123 }
1124}
1125
1126Real
1127TabulatedFluidProperties::k_from_p_T(Real pressure, Real temperature) const
1128{
1130 {
1131 checkInputVariables(pressure, temperature);
1132 return _property_ipol[_k_idx]->sample(pressure, temperature);
1133 }
1134 else
1135 {
1136 if (_fp)
1137 return _fp->k_from_p_T(pressure, temperature);
1138 else
1139 NeedTabulationOrFPError("k_from_p_T", "k");
1140 }
1141}
1142
1143void
1145 Real pressure, Real temperature, Real & k, Real & dk_dp, Real & dk_dT) const
1146{
1148 {
1149 checkInputVariables(pressure, temperature);
1150 return _property_ipol[_k_idx]->sampleValueAndDerivatives(
1151 pressure, temperature, k, dk_dp, dk_dT);
1152 }
1153 else
1154 {
1155 if (_fp)
1156 return _fp->k_from_p_T(pressure, temperature, k, dk_dp, dk_dT);
1157 else
1158 NeedTabulationOrFPError("k_from_p_T with derivatives", "k");
1159 }
1160}
1161
1162Real
1163TabulatedFluidProperties::s_from_p_T(Real pressure, Real temperature) const
1164{
1166 {
1167 checkInputVariables(pressure, temperature);
1168 return _property_ipol[_entropy_idx]->sample(pressure, temperature);
1169 }
1170 else
1171 {
1172 if (_fp)
1173 return _fp->s_from_p_T(pressure, temperature);
1174 else
1175 NeedTabulationOrFPError("s_from_p_T", "entropy");
1176 }
1177}
1178
1179void
1180TabulatedFluidProperties::s_from_p_T(Real p, Real T, Real & s, Real & ds_dp, Real & ds_dT) const
1181{
1183 {
1186 _property_ipol[_entropy_idx]->sampleValueAndDerivatives(p, T, s, ds_dp, ds_dT);
1188 {
1189 Real v, e, dv_dp, dv_dT, de_dp, de_dT;
1190 SinglePhaseFluidProperties::v_e_from_p_T(p, T, v, dv_dp, dv_dT, e, de_dp, de_dT);
1191 Real ds_dv, ds_de;
1192 _property_ve_ipol[_entropy_idx]->sampleValueAndDerivatives(v, e, s, ds_dv, ds_de);
1193 ds_dp = ds_dv * dv_dp + ds_de * de_dp;
1194 ds_dT = ds_dv * dv_dT + ds_de * de_dT;
1195 }
1196 else
1197 NeedTabulationError("entropy");
1198 }
1199 else
1200 {
1201 if (_fp)
1202 _fp->s_from_p_T(p, T, s, ds_dp, ds_dT);
1203 else
1204 NeedTabulationOrFPError("s_from_p_T with derivatives", "entropy");
1205 }
1206}
1207
1208Real
1210{
1212 {
1213 const Real p = _p_from_v_h_ipol->sample(v, h);
1214 const Real T = _T_from_v_h_ipol->sample(v, h);
1215 return e_from_p_T(p, T);
1216 }
1218 {
1219 // Lambda computes h from v and the current_e
1220 auto lambda = [&](Real v, Real current_e, Real & new_h, Real & dh_dv, Real & dh_de)
1221 { h_from_v_e(v, current_e, new_h, dh_dv, dh_de); };
1223 h,
1224 /*e initial guess*/ h - _p_initial_guess * v,
1225 _tolerance,
1226 lambda,
1227 name() + "::e_from_v_h",
1230 .first;
1231 return e;
1232 }
1233 else if (_fp)
1234 return _fp->e_from_v_h(v, h);
1235 else
1236 NeedTabulationOrFPError("e_from_v_h", "internal_energy");
1237}
1238
1239void
1240TabulatedFluidProperties::e_from_v_h(Real v, Real h, Real & e, Real & de_dv, Real & de_dh) const
1241{
1243 {
1244 Real p = 0, dp_dv = 0, dp_dh = 0;
1245 _p_from_v_h_ipol->sampleValueAndDerivatives(v, h, p, dp_dv, dp_dh);
1246 Real T = 0, dT_dv = 0, dT_dh = 0;
1247 _T_from_v_h_ipol->sampleValueAndDerivatives(v, h, T, dT_dv, dT_dh);
1248 Real de_dp, de_dT;
1249 e_from_p_T(p, T, e, de_dp, de_dT);
1250 de_dv = de_dp * dp_dv + de_dT * dT_dv;
1251 de_dh = de_dp * dp_dh + de_dT * dT_dh;
1252 }
1254 {
1255 // Lambda computes h from v and the current_e
1256 auto lambda = [&](Real v, Real current_e, Real & new_h, Real & dh_dv, Real & dh_de)
1257 { h_from_v_e(v, current_e, new_h, dh_dv, dh_de); };
1258 const auto e_data =
1260 h,
1261 /*e initial guess*/ h - _p_initial_guess * v,
1262 _tolerance,
1263 lambda,
1264 name() + "::e_from_v_h",
1267 e = e_data.first;
1268 // Finite difference approximation
1269 const auto e2 = e_from_v_h(v * (1 + TOLERANCE), h);
1270 de_dv = (e2 - e) / (TOLERANCE * v);
1271 de_dh = 1. / e_data.second;
1272 }
1273 else if (_fp)
1274 _fp->e_from_v_h(v, h, e, de_dv, de_dh);
1275 else
1276 NeedTabulationOrFPError("e_from_v_h", "internal_energy");
1277}
1278
1279std::vector<Real>
1281{
1282 if (_fp)
1283 return _fp->henryCoefficients();
1284 else
1285 TabulationNotImplementedError("henryCoefficients");
1286}
1287
1288Real
1290{
1291 if (_fp)
1292 return _fp->vaporPressure(temperature);
1293 else
1294 TabulationNotImplementedError("vaporPressure");
1295}
1296
1297void
1298TabulatedFluidProperties::vaporPressure(Real temperature, Real & psat, Real & dpsat_dT) const
1299{
1300 if (_fp)
1301 _fp->vaporPressure(temperature, psat, dpsat_dT);
1302 else
1303 TabulationNotImplementedError("vaporPressure");
1304}
1305
1306Real
1308{
1309 if (_fp)
1310 return _fp->vaporTemperature(pressure);
1311 else
1312 TabulationNotImplementedError("vaporTemperature");
1313}
1314
1315void
1316TabulatedFluidProperties::vaporTemperature(Real pressure, Real & Tsat, Real & dTsat_dp) const
1317{
1318
1319 if (_fp)
1320 _fp->vaporTemperature(pressure, Tsat, dTsat_dp);
1321 else
1322 TabulationNotImplementedError("vaporTemperature");
1323}
1324
1325Real
1327{
1328
1329 if (_fp)
1330 return _fp->triplePointPressure();
1331 else
1332 TabulationNotImplementedError("triplePointPressure");
1333}
1334
1335Real
1337{
1338
1339 if (_fp)
1340 return _fp->triplePointTemperature();
1341 else
1342 TabulationNotImplementedError("triplePointTemperature");
1343}
1344
1345Real
1347{
1348
1349 if (_fp)
1350 return _fp->criticalPressure();
1351 else
1352 TabulationNotImplementedError("criticalPressure");
1353}
1354
1355Real
1357{
1358
1359 if (_fp)
1360 return _fp->criticalTemperature();
1361 else
1362 TabulationNotImplementedError("criticalTemperature");
1363}
1364
1365Real
1367{
1368 if (_fp)
1369 return _fp->criticalDensity();
1370 else
1371 TabulationNotImplementedError("criticalDensity");
1372}
1373
1374Real
1376{
1378 missingVEInterpolationError(__PRETTY_FUNCTION__);
1380
1382 return _property_ve_ipol[_p_idx]->sample(v, e);
1383 else if (_construct_pT_from_ve)
1384 return _p_from_v_e_ipol->sample(v, e);
1385 else if (_fp)
1386 return _fp->p_from_v_e(v, e);
1387 else
1388 NeedTabulationOrFPError("p_from_v_e", "pressure");
1389}
1390
1391void
1392TabulatedFluidProperties::p_from_v_e(Real v, Real e, Real & p, Real & dp_dv, Real & dp_de) const
1393{
1395 missingVEInterpolationError(__PRETTY_FUNCTION__);
1397
1399 _property_ve_ipol[_p_idx]->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1400 else if (_construct_pT_from_ve)
1401 _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1402 else if (_fp)
1403 _fp->p_from_v_e(v, e, p, dp_dv, dp_de);
1404 else
1405 NeedTabulationOrFPError("p_from_v_e with derivatives", "pressure");
1406}
1407
1408void
1410 const ADReal & v, const ADReal & e, ADReal & p, ADReal & dp_dv, ADReal & dp_de) const
1411{
1413 missingVEInterpolationError(__PRETTY_FUNCTION__);
1414 ADReal vc = v, ec = e;
1415 checkInputVariablesVE(vc, ec);
1416
1418 _property_ve_ipol[_p_idx]->sampleValueAndDerivatives(vc, ec, p, dp_dv, dp_de);
1419 else if (_construct_pT_from_ve)
1420 _p_from_v_e_ipol->sampleValueAndDerivatives(vc, ec, p, dp_dv, dp_de);
1421 else if (_fp)
1422 _fp->p_from_v_e(vc, ec, p, dp_dv, dp_de);
1423 else
1424 NeedTabulationOrFPError("AD p_from_v_e with derivatives", "pressure");
1425}
1426
1427Real
1429{
1431 missingVEInterpolationError(__PRETTY_FUNCTION__);
1433
1435 return _property_ve_ipol[_T_idx]->sample(v, e);
1436 else if (_construct_pT_from_ve)
1437 return _T_from_v_e_ipol->sample(v, e);
1438 else if (_fp)
1439 return _fp->T_from_v_e(v, e);
1440 else
1441 NeedTabulationOrFPError("T_from_v_e", "temperature");
1442}
1443
1444void
1445TabulatedFluidProperties::T_from_v_e(Real v, Real e, Real & T, Real & dT_dv, Real & dT_de) const
1446{
1448 missingVEInterpolationError(__PRETTY_FUNCTION__);
1450
1452 _property_ve_ipol[_T_idx]->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1453 else if (_construct_pT_from_ve)
1454 _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1455 else if (_fp)
1456 _fp->T_from_v_e(v, e, T, dT_dv, dT_de);
1457 else
1458 NeedTabulationOrFPError("T_from_v_e with derivatives", "temperature");
1459}
1460
1461void
1463 const ADReal & v, const ADReal & e, ADReal & T, ADReal & dT_dv, ADReal & dT_de) const
1464{
1466 missingVEInterpolationError(__PRETTY_FUNCTION__);
1467 ADReal vc = v, ec = e;
1468 checkInputVariablesVE(vc, ec);
1469
1471 _property_ve_ipol[_T_idx]->sampleValueAndDerivatives(vc, ec, T, dT_dv, dT_de);
1472 else if (_construct_pT_from_ve)
1473 _T_from_v_e_ipol->sampleValueAndDerivatives(vc, ec, T, dT_dv, dT_de);
1474 else if (_fp)
1475 _fp->T_from_v_e(vc, ec, T, dT_dv, dT_de);
1476 else
1477 NeedTabulationOrFPError("AD T_from_v_e with derivatives", "temperature");
1478}
1479
1480Real
1482{
1484 missingVEInterpolationError(__PRETTY_FUNCTION__);
1486
1488 return _property_ve_ipol[_c_idx]->sample(v, e);
1489 else if (_construct_pT_from_ve)
1490 {
1491 Real p = _p_from_v_e_ipol->sample(v, e);
1492 Real T = _T_from_v_e_ipol->sample(v, e);
1493 return c_from_p_T(p, T);
1494 }
1495 else if (_fp)
1496 return _fp->c_from_v_e(v, e);
1497 else
1498 NeedTabulationOrFPError("c_from_v_e", "c");
1499}
1500
1501void
1502TabulatedFluidProperties::c_from_v_e(Real v, Real e, Real & c, Real & dc_dv, Real & dc_de) const
1503{
1505 missingVEInterpolationError(__PRETTY_FUNCTION__);
1507
1509 _property_ve_ipol[_c_idx]->sampleValueAndDerivatives(v, e, c, dc_dv, dc_de);
1510 else if (_construct_pT_from_ve)
1511 {
1512 Real p, dp_dv, dp_de;
1513 _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1514 Real T, dT_dv, dT_de;
1515 _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1516 Real dc_dp, dc_dT;
1517 c_from_p_T(p, T, c, dc_dp, dc_dT);
1518 dc_dv = dc_dp * dp_dv + dc_dT * dT_dv;
1519 dc_de = dc_dp * dp_de + dc_dT * dT_de;
1520 }
1521 else if (_fp)
1522 _fp->c_from_v_e(v, e, c, dc_dv, dc_de);
1523 else
1524 NeedTabulationOrFPError("c_from_v_e with derivatives", "c");
1525}
1526
1527Real
1529{
1531 missingVEInterpolationError(__PRETTY_FUNCTION__);
1533
1535 return _property_ve_ipol[_cp_idx]->sample(v, e);
1536 else if (_construct_pT_from_ve)
1537 {
1538 Real p = _p_from_v_e_ipol->sample(v, e);
1539 Real T = _T_from_v_e_ipol->sample(v, e);
1540 return cp_from_p_T(p, T);
1541 }
1542 else if (_fp)
1543 return _fp->cp_from_v_e(v, e);
1544 else
1545 NeedTabulationOrFPError("cp_from_v_e", "cp");
1546}
1547
1548void
1549TabulatedFluidProperties::cp_from_v_e(Real v, Real e, Real & cp, Real & dcp_dv, Real & dcp_de) const
1550{
1552 missingVEInterpolationError(__PRETTY_FUNCTION__);
1554
1556 _property_ve_ipol[_cp_idx]->sampleValueAndDerivatives(v, e, cp, dcp_dv, dcp_de);
1557 else if (_construct_pT_from_ve)
1558 {
1559 Real p, dp_dv, dp_de;
1560 _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1561 Real T, dT_dv, dT_de;
1562 _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1563 Real dcp_dp, dcp_dT;
1564 cp_from_p_T(p, T, cp, dcp_dp, dcp_dT);
1565 dcp_dv = dcp_dp * dp_dv + dcp_dT * dT_dv;
1566 dcp_de = dcp_dp * dp_de + dcp_dT * dT_de;
1567 }
1568 else if (_fp)
1569 _fp->cp_from_v_e(v, e, cp, dcp_dv, dcp_de);
1570 else
1571 NeedTabulationOrFPError("cp_from_v_e with derivatives", "cp");
1572}
1573
1574Real
1576{
1578 missingVEInterpolationError(__PRETTY_FUNCTION__);
1580
1582 return _property_ve_ipol[_cv_idx]->sample(v, e);
1583 else if (_construct_pT_from_ve)
1584 {
1585 Real p = _p_from_v_e_ipol->sample(v, e);
1586 Real T = _T_from_v_e_ipol->sample(v, e);
1587 return cv_from_p_T(p, T);
1588 }
1589 else if (_fp)
1590 return _fp->cv_from_v_e(v, e);
1591 else
1592 NeedTabulationOrFPError("cv_from_v_e", "cv");
1593}
1594
1595void
1596TabulatedFluidProperties::cv_from_v_e(Real v, Real e, Real & cv, Real & dcv_dv, Real & dcv_de) const
1597{
1599 missingVEInterpolationError(__PRETTY_FUNCTION__);
1601
1603 _property_ve_ipol[_cv_idx]->sampleValueAndDerivatives(v, e, cv, dcv_dv, dcv_de);
1604 else if (_construct_pT_from_ve)
1605 {
1606 Real p, dp_dv, dp_de;
1607 _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1608 Real T, dT_dv, dT_de;
1609 _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1610 Real dcv_dp, dcv_dT;
1611 cv_from_p_T(p, T, cv, dcv_dp, dcv_dT);
1612 dcv_dv = dcv_dp * dp_dv + dcv_dT * dT_dv;
1613 dcv_de = dcv_dp * dp_de + dcv_dT * dT_de;
1614 }
1615 else if (_fp)
1616 _fp->cv_from_v_e(v, e, cv, dcv_dv, dcv_de);
1617 else
1618 NeedTabulationOrFPError("cv_from_v_e with derivatives", "cv");
1619}
1620
1621Real
1623{
1625 missingVEInterpolationError(__PRETTY_FUNCTION__);
1627
1629 return _property_ve_ipol[_viscosity_idx]->sample(v, e);
1630 else if (_construct_pT_from_ve)
1631 {
1632 Real p = _p_from_v_e_ipol->sample(v, e);
1633 Real T = _T_from_v_e_ipol->sample(v, e);
1634 return mu_from_p_T(p, T);
1635 }
1636 else if (_fp)
1637 return _fp->mu_from_v_e(v, e);
1638 else
1639 NeedTabulationOrFPError("mu_from_v_e", "viscosity");
1640}
1641
1642void
1643TabulatedFluidProperties::mu_from_v_e(Real v, Real e, Real & mu, Real & dmu_dv, Real & dmu_de) const
1644{
1646 missingVEInterpolationError(__PRETTY_FUNCTION__);
1648
1650 _property_ve_ipol[_viscosity_idx]->sampleValueAndDerivatives(v, e, mu, dmu_dv, dmu_de);
1651 else if (_construct_pT_from_ve)
1652 {
1653 Real p, dp_dv, dp_de;
1654 _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1655 Real T, dT_dv, dT_de;
1656 _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1657 Real dmu_dp, dmu_dT;
1658 mu_from_p_T(p, T, mu, dmu_dp, dmu_dT);
1659 dmu_dv = dmu_dp * dp_dv + dmu_dT * dT_dv;
1660 dmu_de = dmu_dp * dp_de + dmu_dT * dT_de;
1661 }
1662 else if (_fp)
1663 _fp->mu_from_v_e(v, e, mu, dmu_dv, dmu_de);
1664 else
1665 NeedTabulationOrFPError("mu_from_v_e with derivatives", "viscosity");
1666}
1667
1668Real
1670{
1672 missingVEInterpolationError(__PRETTY_FUNCTION__);
1674
1676 return _property_ve_ipol[_k_idx]->sample(v, e);
1677 else if (_construct_pT_from_ve)
1678 {
1679 Real T = _T_from_v_e_ipol->sample(v, e);
1680 Real p = _p_from_v_e_ipol->sample(v, e);
1681 return k_from_p_T(p, T);
1682 }
1683 else if (_fp)
1684 return _fp->k_from_v_e(v, e);
1685 else
1686 NeedTabulationOrFPError("k_from_v_e", "k");
1687}
1688
1689void
1690TabulatedFluidProperties::k_from_v_e(Real v, Real e, Real & k, Real & dk_dv, Real & dk_de) const
1691{
1693 missingVEInterpolationError(__PRETTY_FUNCTION__);
1695
1697 _property_ve_ipol[_k_idx]->sampleValueAndDerivatives(v, e, k, dk_dv, dk_de);
1698 else if (_construct_pT_from_ve)
1699 {
1700 Real p, dp_dv, dp_de;
1701 _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1702 Real T, dT_dv, dT_de;
1703 _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1704 Real dk_dp, dk_dT;
1705 k_from_p_T(p, T, k, dk_dp, dk_dT);
1706 dk_dv = dk_dp * dp_dv + dk_dT * dT_dv;
1707 dk_de = dk_dp * dp_de + dk_dT * dT_de;
1708 }
1709 else if (_fp)
1710 _fp->k_from_v_e(v, e, k, dk_dv, dk_de);
1711 else
1712 NeedTabulationOrFPError("k_from_v_e with derivatives", "k");
1713}
1714
1715Real
1717{
1719 missingVEInterpolationError(__PRETTY_FUNCTION__);
1721
1723 return _property_ve_ipol[_entropy_idx]->sample(v, e);
1724 else if (_construct_pT_from_ve)
1725 {
1726 Real T = _T_from_v_e_ipol->sample(v, e);
1727 Real p = _p_from_v_e_ipol->sample(v, e);
1728 return s_from_p_T(p, T);
1729 }
1730 else if (_fp)
1731 return _fp->s_from_v_e(v, e);
1732 else
1733 NeedTabulationOrFPError("s_from_v_e", "entropy");
1734}
1735
1736void
1737TabulatedFluidProperties::s_from_v_e(Real v, Real e, Real & s, Real & ds_dv, Real & ds_de) const
1738{
1740 missingVEInterpolationError(__PRETTY_FUNCTION__);
1742
1744 _property_ve_ipol[_entropy_idx]->sampleValueAndDerivatives(v, e, s, ds_dv, ds_de);
1745 else if (_construct_pT_from_ve)
1746 {
1747 Real p, T, dT_dv, dT_de, dp_dv, dp_de;
1748 _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1749 _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1750 Real ds_dp, ds_dT;
1751 s_from_p_T(p, T, s, ds_dp, ds_dT);
1752 ds_dv = ds_dp * dp_dv + ds_dT * dT_dv;
1753 ds_dv = ds_dp * dp_de + ds_dT * dT_de;
1754 }
1755 else if (_fp)
1756 _fp->s_from_v_e(v, e, s, ds_dv, ds_de);
1757 else
1758 NeedTabulationOrFPError("s_from_v_e", "entropy");
1759}
1760
1761Real
1763{
1764 if (!_construct_pT_from_ve &&
1767 missingVEInterpolationError(__PRETTY_FUNCTION__);
1769
1770 Real h, T = 0, s;
1772 {
1773 s = _property_ve_ipol[_entropy_idx]->sample(v, e);
1774 h = _property_ve_ipol[_enthalpy_idx]->sample(v, e);
1775 T = _property_ve_ipol[_T_idx]->sample(v, e);
1776 }
1778 {
1779 Real p0 = _p_initial_guess;
1780 Real T0 = _T_initial_guess;
1781 Real p = 0;
1782 bool conversion_succeeded;
1783 p_T_from_v_e(v, e, p0, T0, p, T, conversion_succeeded);
1784 s = s_from_p_T(p, T);
1785 h = h_from_p_T(p, T);
1786 }
1787 else
1788 mooseError(__PRETTY_FUNCTION__,
1789 "\nNo tabulation or fluid property 'input_fp' object to compute value");
1790 return h - T * s;
1791}
1792
1793Real
1795{
1796 Real p0 = _p_initial_guess;
1797 Real T0 = _T_initial_guess;
1798 Real p, T;
1799 bool conversion_succeeded;
1800 p_T_from_h_s(h, s, p0, T0, p, T, conversion_succeeded);
1801 return T;
1802}
1803
1804Real
1805TabulatedFluidProperties::T_from_p_h(Real pressure, Real enthalpy) const
1806{
1808 {
1809 auto lambda = [&](Real pressure, Real current_T, Real & new_h, Real & dh_dp, Real & dh_dT)
1810 { h_from_p_T(pressure, current_T, new_h, dh_dp, dh_dT); };
1811 Real T = FluidPropertiesUtils::NewtonSolve(pressure,
1812 enthalpy,
1814 _tolerance,
1815 lambda,
1816 name() + "::T_from_p_h",
1819 .first;
1820 // check for nans
1821 if (std::isnan(T))
1822 mooseError("Conversion from enthalpy (h = ",
1823 enthalpy,
1824 ") and pressure (p = ",
1825 pressure,
1826 ") to temperature failed to converge.");
1827 return T;
1828 }
1829 else if (_fp)
1830 return _fp->T_from_p_h(pressure, enthalpy);
1831 else
1832 NeedTabulationOrFPError("T_from_p_h", "temperature");
1833}
1834
1835ADReal
1836TabulatedFluidProperties::T_from_p_h(const ADReal & pressure, const ADReal & enthalpy) const
1837{
1838 using std::isnan;
1840 {
1841 auto lambda =
1842 [&](ADReal pressure, ADReal current_T, ADReal & new_h, ADReal & dh_dp, ADReal & dh_dT)
1843 {
1844 h_from_p_T(pressure.value(), current_T.value(), new_h.value(), dh_dp.value(), dh_dT.value());
1845 // Reconstruct derivatives
1846 new_h.derivatives() =
1847 dh_dp.value() * pressure.derivatives() + dh_dT.value() * current_T.derivatives();
1848 };
1850 enthalpy,
1852 _tolerance,
1853 lambda,
1854 name() + "::T_from_p_h",
1857 .first;
1858 // check for nans
1859 if (isnan(T))
1860 mooseError("Conversion from enthalpy (h = ",
1861 enthalpy,
1862 ") and pressure (p = ",
1863 pressure,
1864 ") to temperature failed to converge.");
1865 return T;
1866 }
1867 else if (_fp)
1868 return _fp->T_from_p_h(pressure, enthalpy);
1869 else
1870 NeedTabulationOrFPError("T_from_p_h", "temperature");
1871}
1872
1873Real
1874TabulatedFluidProperties::s_from_h_p(Real enthalpy, Real pressure) const
1875{
1876 Real T = T_from_p_h(pressure, enthalpy);
1877 return s_from_p_T(pressure, T);
1878}
1879
1880void
1882 Real h, Real pressure, Real & s, Real & ds_dh, Real & ds_dp) const
1883{
1884 if (_fp)
1885 _fp->s_from_h_p(h, pressure, s, ds_dh, ds_dp);
1886 else
1887 TabulationNotImplementedError("s_from_h_p with derivatives");
1888}
1889
1890[[noreturn]] void
1891TabulatedFluidProperties::TabulationNotImplementedError(const std::string & desired_routine) const
1892{
1893 mooseError("TabulatedFluidProperties can only call the function '" + desired_routine +
1894 "' when the 'input_fp' parameter is provided. It is currently not implemented using "
1895 "tabulations, and this property is simply forwarded to the FluidProperties specified "
1896 "in the 'input_fp' parameter");
1897}
1898
1899[[noreturn]] void
1900TabulatedFluidProperties::NeedTabulationOrFPError(const std::string & desired_routine,
1901 const std::string & needed_property) const
1902{
1903 mooseError(
1904 "TabulatedFluidProperties can only call the function '" + desired_routine +
1905 "' when either:\n- the property '" + needed_property +
1906 "' is tabulated and listed in the 'interpolated_properties' parameter.\n- the 'input_fp' "
1907 "parameter is provided.");
1908}
1909
1910[[noreturn]] void
1911TabulatedFluidProperties::NeedTabulationError(const std::string & needed_property) const
1912{
1913 mooseError("Property '" + needed_property +
1914 "' is marked as interpolated but neither of the following methods are active:\n- "
1915 "(p,T) interpolation created from fluid properties ('create_pT_interpolations' "
1916 "parameter)\n- a (p,T) tabulation file ('fluid_property_file')\n- (v,e) interpolation "
1917 "created from fluid properties or a (p,T) tabulation ('create_ve_interpolations' "
1918 "parameter)\n- a (v,e) tabulation file ('fluid_property_ve_file' parameter)");
1919}
1920
1921void
1923{
1924 if (processor_id() == 0)
1925 {
1926 // Write out the (p, T) interpolation tables
1927 if (_file_name_out != "")
1928 {
1929 file_name = file_name.empty() ? "fluid_properties_" + name() + "_out.csv" : file_name;
1931
1932 std::ofstream file_out(file_name.c_str());
1933
1934 if (!getParam<bool>("skip_header_tabulation"))
1935 {
1936 // Write out date and fluid type
1937 time_t now = std::time(&now);
1938 file_out << "# " << (_fp ? _fp->fluidName() : "")
1939 << " properties (p,T) tabulation created by TabulatedFluidProperties on "
1940 << ctime(&now) << "\n";
1941 }
1942
1943 // Write out column names
1944 file_out << "pressure, temperature";
1945 for (std::size_t i = 0; i < _interpolated_properties.size(); ++i)
1946 if (_interpolated_properties[i] != "pressure" &&
1947 _interpolated_properties[i] != "temperature")
1948 file_out << ", " << _interpolated_properties[i];
1949 file_out << "\n";
1950
1951 // Write out the fluid property data
1952 for (unsigned int p = 0; p < _num_p; ++p)
1953 for (unsigned int t = 0; t < _num_T; ++t)
1954 {
1955 file_out << _pressure[p] << ", " << _temperature[t];
1956 for (std::size_t i = 0; i < _properties.size(); ++i)
1957 file_out << ", " << _properties[i][p * _num_T + t];
1958 file_out << "\n";
1959 }
1960
1961 file_out << std::flush;
1962 file_out.close();
1963 }
1964
1965 // Write out the (v,e) interpolation tables
1967 {
1968 const auto file_name_ve = (_file_name_ve_out == "")
1969 ? std::regex_replace(file_name, std::regex("\\.csv"), "_ve.csv")
1971 MooseUtils::checkFileWriteable(file_name_ve);
1972 std::ofstream file_out(file_name_ve.c_str());
1973
1974 // Write out date and fluid type
1975 if (!getParam<bool>("skip_header_tabulation"))
1976 {
1977 time_t now = std::time(&now);
1978 file_out << "# " << (_fp ? _fp->fluidName() : "")
1979 << " properties (v,e) tabulation created by TabulatedFluidProperties on "
1980 << ctime(&now) << "\n";
1981 }
1982
1983 // Write out column names
1984 file_out << "specific_volume, internal_energy, pressure, temperature";
1985 for (const auto i : index_range(_interpolated_properties))
1986 // Avoid writing the fixed columns twice
1987 if (_interpolated_properties[i] != "internal_energy" &&
1988 _interpolated_properties[i] != "pressure" &&
1989 _interpolated_properties[i] != "temperature")
1990 file_out << ", " << _interpolated_properties[i];
1991 file_out << "\n";
1992
1993 // Write out the fluid property data
1994 for (const auto v : make_range(_num_v))
1995 for (const auto e : make_range(_num_e))
1996 {
1997 const auto v_val = _specific_volume[v];
1998 const auto e_val = _internal_energy[e];
1999 // Use the expected source for the grid. Note that the tabulations are already created
2000 Real pressure, temperature;
2002 {
2003 pressure = _p_from_v_e_ipol->sample(v_val, e_val);
2004 temperature = _T_from_v_e_ipol->sample(v_val, e_val);
2005 }
2006 else
2007 {
2008 pressure = p_from_v_e(v_val, e_val);
2009 temperature = T_from_v_e(v_val, e_val);
2010 }
2011 file_out << v_val << ", " << e_val << ", " << pressure << ", " << temperature
2012 << (_interpolated_properties.size() ? ", " : "");
2013 for (const auto i : index_range(_interpolated_properties))
2014 {
2015 bool add_comma = true;
2016 if (i == _density_idx)
2017 file_out << 1. / v_val;
2018 else if (i == _enthalpy_idx)
2019 file_out << h_from_v_e(v_val, e_val);
2020 // Note that we could use (p,T) routine to generate this instead of (v,e)
2021 // Or could use the _properties_ve array similar to what we do for (pressure,
2022 // temperature)
2023 else if (i == _viscosity_idx)
2024 file_out << mu_from_v_e(v_val, e_val);
2025 else if (i == _k_idx)
2026 file_out << k_from_v_e(v_val, e_val);
2027 else if (i == _c_idx)
2028 file_out << c_from_v_e(v_val, e_val);
2029 else if (i == _cv_idx)
2030 file_out << cv_from_v_e(v_val, e_val);
2031 else if (i == _cp_idx)
2032 file_out << cp_from_v_e(v_val, e_val);
2033 else if (i == _entropy_idx)
2034 file_out << s_from_v_e(v_val, e_val);
2035 else
2036 add_comma = false;
2037 if (i != _interpolated_properties.size() - 1 && add_comma)
2038 file_out << ", ";
2039 }
2040
2041 file_out << "\n";
2042 }
2043
2044 file_out << std::flush;
2045 file_out.close();
2046 }
2047 }
2048}
2049
2050void
2052{
2053 mooseAssert(_fp, "We should not try to generate (p,T) tabulated data without a _fp user object");
2054 _pressure.resize(_num_p);
2055 _temperature.resize(_num_T);
2056
2057 // Generate data for all properties entered in input file
2060
2061 for (std::size_t i = 0; i < _interpolated_properties_enum.size(); ++i)
2063
2064 for (const auto i : index_range(_properties))
2065 _properties[i].resize(_num_p * _num_T);
2066
2067 // Temperature is divided equally into _num_T segments
2068 Real delta_T = (_temperature_max - _temperature_min) / static_cast<Real>(_num_T - 1);
2069
2070 for (unsigned int j = 0; j < _num_T; ++j)
2071 _temperature[j] = _temperature_min + j * delta_T;
2072
2073 // Divide the pressure into _num_p equal segments
2074 Real delta_p = (_pressure_max - _pressure_min) / static_cast<Real>(_num_p - 1);
2075
2076 for (unsigned int i = 0; i < _num_p; ++i)
2077 _pressure[i] = _pressure_min + i * delta_p;
2078
2079 // Generate the tabulated data at the pressure and temperature points
2080 for (const auto i : index_range(_properties))
2081 {
2082 if (_interpolated_properties[i] == "density")
2083 for (unsigned int p = 0; p < _num_p; ++p)
2084 for (unsigned int t = 0; t < _num_T; ++t)
2085 _properties[i][p * _num_T + t] = _fp->rho_from_p_T(_pressure[p], _temperature[t]);
2086
2087 if (_interpolated_properties[i] == "enthalpy")
2088 for (unsigned int p = 0; p < _num_p; ++p)
2089 for (unsigned int t = 0; t < _num_T; ++t)
2090 _properties[i][p * _num_T + t] = _fp->h_from_p_T(_pressure[p], _temperature[t]);
2091
2092 if (_interpolated_properties[i] == "internal_energy")
2093 for (unsigned int p = 0; p < _num_p; ++p)
2094 for (unsigned int t = 0; t < _num_T; ++t)
2095 _properties[i][p * _num_T + t] = _fp->e_from_p_T(_pressure[p], _temperature[t]);
2096
2097 if (_interpolated_properties[i] == "viscosity")
2098 for (unsigned int p = 0; p < _num_p; ++p)
2099 for (unsigned int t = 0; t < _num_T; ++t)
2100 _properties[i][p * _num_T + t] = _fp->mu_from_p_T(_pressure[p], _temperature[t]);
2101
2102 if (_interpolated_properties[i] == "k")
2103 for (unsigned int p = 0; p < _num_p; ++p)
2104 for (unsigned int t = 0; t < _num_T; ++t)
2105 _properties[i][p * _num_T + t] = _fp->k_from_p_T(_pressure[p], _temperature[t]);
2106
2107 if (_interpolated_properties[i] == "c")
2108 for (unsigned int p = 0; p < _num_p; ++p)
2109 for (unsigned int t = 0; t < _num_T; ++t)
2110 _properties[i][p * _num_T + t] = _fp->c_from_p_T(_pressure[p], _temperature[t]);
2111
2112 if (_interpolated_properties[i] == "cv")
2113 for (unsigned int p = 0; p < _num_p; ++p)
2114 for (unsigned int t = 0; t < _num_T; ++t)
2115 _properties[i][p * _num_T + t] = _fp->cv_from_p_T(_pressure[p], _temperature[t]);
2116
2117 if (_interpolated_properties[i] == "cp")
2118 for (unsigned int p = 0; p < _num_p; ++p)
2119 for (unsigned int t = 0; t < _num_T; ++t)
2120 _properties[i][p * _num_T + t] = _fp->cp_from_p_T(_pressure[p], _temperature[t]);
2121
2122 if (_interpolated_properties[i] == "entropy")
2123 for (unsigned int p = 0; p < _num_p; ++p)
2124 for (unsigned int t = 0; t < _num_T; ++t)
2125 _properties[i][p * _num_T + t] = _fp->s_from_p_T(_pressure[p], _temperature[t]);
2126 }
2127}
2128
2129void
2131{
2132 mooseAssert(_fp, "We should not try to generate (v,e) tabulated data without a _fp user object");
2133 _specific_volume.resize(_num_v);
2134 _internal_energy.resize(_num_e);
2135
2136 // Generate data for all properties entered in input file
2139
2140 // This is filled from the user input, so it does not matter than this operation is performed
2141 // for both the (p,T) and (v,e) tabulated data generation
2142 for (std::size_t i = 0; i < _interpolated_properties_enum.size(); ++i)
2144
2145 for (const auto i : index_range(_properties_ve))
2146 _properties_ve[i].resize(_num_v * _num_e);
2147
2148 // Grids in (v,e) are not read, so we either use user input or rely on (p,T) data
2150
2151 // Generate the tabulated data at the pressure and temperature points
2152 for (const auto i : index_range(_properties_ve))
2153 {
2154 if (_interpolated_properties[i] == "density")
2155 for (unsigned int v = 0; v < _num_v; ++v)
2156 for (unsigned int e = 0; e < _num_e; ++e)
2157 _properties_ve[i][v * _num_e + e] = 1. / _specific_volume[v];
2158
2159 if (_interpolated_properties[i] == "enthalpy")
2160 for (unsigned int v = 0; v < _num_v; ++v)
2161 for (unsigned int e = 0; e < _num_e; ++e)
2162 _properties_ve[i][v * _num_e + e] =
2163 _fp->h_from_v_e(_specific_volume[v], _internal_energy[e]);
2164
2165 if (_interpolated_properties[i] == "internal_energy")
2166 for (unsigned int v = 0; v < _num_v; ++v)
2167 for (unsigned int e = 0; e < _num_e; ++e)
2168 _properties_ve[i][v * _num_e + e] = _internal_energy[e];
2169
2170 if (_interpolated_properties[i] == "viscosity")
2171 for (unsigned int v = 0; v < _num_v; ++v)
2172 for (unsigned int e = 0; e < _num_e; ++e)
2173 _properties_ve[i][v * _num_e + e] =
2174 _fp->mu_from_v_e(_specific_volume[v], _internal_energy[e]);
2175
2176 if (_interpolated_properties[i] == "k")
2177 for (unsigned int v = 0; v < _num_v; ++v)
2178 for (unsigned int e = 0; e < _num_e; ++e)
2179 _properties_ve[i][v * _num_e + e] =
2180 _fp->k_from_v_e(_specific_volume[v], _internal_energy[e]);
2181
2182 if (_interpolated_properties[i] == "c")
2183 for (unsigned int v = 0; v < _num_v; ++v)
2184 for (unsigned int e = 0; e < _num_e; ++e)
2185 _properties_ve[i][v * _num_e + e] =
2186 _fp->c_from_v_e(_specific_volume[v], _internal_energy[e]);
2187
2188 if (_interpolated_properties[i] == "cv")
2189 for (unsigned int v = 0; v < _num_v; ++v)
2190 for (unsigned int e = 0; e < _num_e; ++e)
2191 _properties_ve[i][v * _num_e + e] =
2192 _fp->cv_from_v_e(_specific_volume[v], _internal_energy[e]);
2193
2194 if (_interpolated_properties[i] == "cp")
2195 for (unsigned int v = 0; v < _num_v; ++v)
2196 for (unsigned int e = 0; e < _num_e; ++e)
2197 _properties_ve[i][v * _num_e + e] =
2198 _fp->cp_from_v_e(_specific_volume[v], _internal_energy[e]);
2199
2200 if (_interpolated_properties[i] == "entropy")
2201 for (unsigned int v = 0; v < _num_v; ++v)
2202 for (unsigned int e = 0; e < _num_e; ++e)
2203 _properties_ve[i][v * _num_e + e] =
2204 _fp->s_from_v_e(_specific_volume[v], _internal_energy[e]);
2205
2206 if (_interpolated_properties[i] == "pressure")
2207 for (unsigned int v = 0; v < _num_v; ++v)
2208 for (unsigned int e = 0; e < _num_e; ++e)
2209 _properties_ve[i][v * _num_e + e] =
2210 _fp->p_from_v_e(_specific_volume[v], _internal_energy[e]);
2211
2212 if (_interpolated_properties[i] == "temperature")
2213 for (unsigned int v = 0; v < _num_v; ++v)
2214 for (unsigned int e = 0; e < _num_e; ++e)
2215 _properties_ve[i][v * _num_e + e] =
2216 _fp->T_from_v_e(_specific_volume[v], _internal_energy[e]);
2217 }
2218}
2219
2220template <typename T>
2221void
2222TabulatedFluidProperties::checkInputVariables(T & pressure, T & temperature) const
2223{
2224 using std::max, std::min;
2225
2226 if (_OOBBehavior == Ignore)
2227 return;
2228 else if (MooseUtils::absoluteFuzzyGreaterThan(_pressure_min, pressure, libMesh::TOLERANCE) ||
2229 MooseUtils::absoluteFuzzyGreaterThan(pressure, _pressure_max, libMesh::TOLERANCE))
2230 {
2231 if (_OOBBehavior == Throw)
2232 throw MooseException("Pressure " + Moose::stringify(pressure) +
2233 " is outside the range of tabulated pressure (" +
2236
2237 else
2238 {
2239 pressure = max(_pressure_min, min(pressure, _pressure_max));
2241 flagInvalidSolution("Pressure out of bounds");
2242 else if (_OOBBehavior == WarnInvalid)
2243 flagSolutionWarning("Pressure out of bounds");
2244 }
2245 }
2246
2247 if (MooseUtils::absoluteFuzzyGreaterThan(_temperature_min, temperature, libMesh::TOLERANCE) ||
2248 MooseUtils::absoluteFuzzyGreaterThan(temperature, _temperature_max, libMesh::TOLERANCE))
2249 {
2250 if (_OOBBehavior == Throw)
2251 throw MooseException("Temperature " + Moose::stringify(temperature) +
2252 " is outside the range of tabulated temperature (" +
2255 else
2256 {
2257 temperature = max(T(_temperature_min), min(temperature, T(_temperature_max)));
2259 flagInvalidSolution("Temperature out of bounds");
2260 else if (_OOBBehavior == WarnInvalid)
2261 flagSolutionWarning("Temperature out of bounds");
2262 }
2263 }
2264}
2265
2266template <typename T>
2267void
2269{
2270 using std::max, std::min;
2271
2272 if (_OOBBehavior == Ignore)
2273 return;
2274 else if (e < _e_min || e > _e_max)
2275 {
2276 if (_OOBBehavior == Throw)
2277 throw MooseException("Specific internal energy " + Moose::stringify(e) +
2278 " is outside the range of tabulated specific internal energies (" +
2280 else
2281 {
2282 e = max(_e_min, min(e, _e_max));
2284 flagInvalidSolution("Specific internal energy out of bounds");
2285 else if (_OOBBehavior == WarnInvalid)
2286 flagSolutionWarning("Specific internal energy out of bounds");
2287 }
2288 }
2289
2290 if (v < _v_min || v > _v_max)
2291 {
2292 if (_OOBBehavior == Throw)
2293 throw MooseException("Specific volume " + Moose::stringify(v) +
2294 " is outside the range of tabulated specific volumes (" +
2296 else
2297 {
2298 v = max(T(_v_min), min(v, T(_v_max)));
2300 flagInvalidSolution("Specific volume out of bounds");
2301 else if (_OOBBehavior == WarnInvalid)
2302 flagSolutionWarning("Specific volume out of bounds");
2303 }
2304 }
2305}
2306
2307void
2308TabulatedFluidProperties::checkInitialGuess(bool post_reading_tabulation) const
2309{
2310 // First condition applies when generating a tabulation
2311 // Second condition applies when using a pre-generated loaded tabulation
2312 if ((!post_reading_tabulation && _fp && (_construct_pT_from_ve || _construct_pT_from_vh)) ||
2313 (post_reading_tabulation && (_file_name_in != "" || _file_name_ve_in != "") &&
2314 (_create_direct_ve_interpolations || isParamSetByUser("T_initial_guess") ||
2315 isParamSetByUser("p_initial_guess"))))
2316 {
2317 if (_p_initial_guess < _pressure_min || _p_initial_guess > _pressure_max)
2318 mooseWarning("Pressure initial guess for (p,T), (v,e) conversions " +
2320 " is outside the range of tabulated "
2321 "pressure (" +
2323
2324 if (_T_initial_guess < _temperature_min || _T_initial_guess > _temperature_max)
2325 mooseWarning("Temperature initial guess for (p,T), (v,e) conversions " +
2327 " is outside the range of tabulated "
2328 "temperature (" +
2330 ").");
2331 }
2332}
2333
2334void
2336{
2337 std::string file_name;
2338 if (use_pT)
2339 {
2340 _console << name() + ": Reading tabulated properties from " << _file_name_in << std::endl;
2341 _csv_reader.read();
2342 file_name = _file_name_in;
2343 }
2344 else
2345 {
2346 _console << name() + ": Reading tabulated properties from " << _file_name_ve_in << std::endl;
2348 _csv_reader.read();
2349 file_name = _file_name_ve_in;
2350 }
2351
2352 const std::vector<std::string> & column_names = _csv_reader.getNames();
2353
2354 // These columns form the grid and must be present in the file
2355 std::vector<std::string> required_columns;
2356 if (use_pT)
2357 required_columns = {"pressure", "temperature"};
2358 else
2359 required_columns = {"specific_volume", "internal_energy"};
2360
2361 // Check that all required columns are present
2362 for (std::size_t i = 0; i < required_columns.size(); ++i)
2363 {
2364 if (std::find(column_names.begin(), column_names.end(), required_columns[i]) ==
2365 column_names.end())
2366 mooseError("No ",
2367 required_columns[i],
2368 " data read in ",
2369 file_name,
2370 ". A column named ",
2371 required_columns[i],
2372 " must be present");
2373 }
2374
2375 // These columns can be present in the file
2376 std::vector<std::string> property_columns = {
2377 "density", "enthalpy", "viscosity", "k", "c", "cv", "cp", "entropy"};
2378 if (use_pT)
2379 property_columns.push_back("internal_energy");
2380 else
2381 {
2382 property_columns.push_back("pressure");
2383 property_columns.push_back("temperature");
2384 }
2385
2386 // Check that any property names read from the file are present in the list of possible
2387 // properties, and if they are, add them to the list of read properties
2388 for (std::size_t i = 0; i < column_names.size(); ++i)
2389 {
2390 // Only check properties not in _required_columns
2391 if (std::find(required_columns.begin(), required_columns.end(), column_names[i]) ==
2392 required_columns.end())
2393 {
2394 if (std::find(property_columns.begin(), property_columns.end(), column_names[i]) ==
2395 property_columns.end())
2396 mooseWarning(column_names[i],
2397 " read in ",
2398 file_name,
2399 " tabulation file is not one of the properties that TabulatedFluidProperties "
2400 "understands. It will be ignored.");
2401 // We could be reading a (v,e) tabulation after having read a (p,T) tabulation, do not
2402 // insert twice
2403 // Also only allow properties specified as interpolated if user passed the parameter
2404 else if (std::find(_interpolated_properties.begin(),
2406 column_names[i]) == _interpolated_properties.end() &&
2407 (!isParamSetByUser("interpolated_properties") ||
2408 _interpolated_properties_enum.contains(column_names[i])))
2409 _interpolated_properties.push_back(column_names[i]);
2410 }
2411 }
2412
2413 std::map<std::string, unsigned int> data_index;
2414 for (std::size_t i = 0; i < column_names.size(); ++i)
2415 {
2416 auto it = std::find(column_names.begin(), column_names.end(), column_names[i]);
2417 data_index[column_names[i]] = std::distance(column_names.begin(), it);
2418 }
2419
2420 const std::vector<std::vector<Real>> & column_data = _csv_reader.getData();
2421
2422 // Extract the pressure and temperature data vectors
2423 if (use_pT)
2424 {
2425 _pressure = column_data[data_index.find("pressure")->second];
2426 _temperature = column_data[data_index.find("temperature")->second];
2427 }
2428 else
2429 {
2430 _specific_volume = column_data[data_index.find("specific_volume")->second];
2431 _internal_energy = column_data[data_index.find("internal_energy")->second];
2432 }
2433
2434 if (use_pT)
2435 checkFileTabulationGrids(_pressure, _temperature, file_name, "pressure", "temperature");
2436 else
2439 file_name,
2440 "specific volume",
2441 "specific internal energy");
2442
2443 if (use_pT)
2444 {
2445 _num_p = _pressure.size();
2446 _num_T = _temperature.size();
2447
2448 // Minimum and maximum pressure and temperature. Note that _pressure and
2449 // _temperature are sorted
2450 _pressure_min = _pressure.front();
2451 _pressure_max = _pressure.back();
2454 checkInitialGuess(true);
2455
2456 // Extract the fluid property data from the file
2457 for (std::size_t i = 0; i < _interpolated_properties.size(); ++i)
2458 _properties.push_back(column_data[data_index.find(_interpolated_properties[i])->second]);
2459 }
2460 else
2461 {
2462 _num_v = _specific_volume.size();
2463 _num_e = _internal_energy.size();
2464
2465 // Minimum and maximum specific internal energy and specific volume
2466 _v_min = _specific_volume.front();
2467 _v_max = _specific_volume.back();
2468 _e_min = _internal_energy.front();
2469 _e_max = _internal_energy.back();
2470
2471 // We cannot overwrite the tabulated data grid with a grid generated from user-input for the
2472 // purpose of creating (p,T) to (v,e) interpolations
2474 paramError("construct_pT_from_ve",
2475 "Reading a (v,e) tabulation and generating (p,T) to (v,e) interpolation tables is "
2476 "not supported at this time.");
2477
2478 // Make sure we use the tabulation bounds
2479 _e_bounds_specified = true;
2480 _v_bounds_specified = true;
2481
2482 // Extract the fluid property data from the file
2484 for (std::size_t i = 0; i < _interpolated_properties.size(); ++i)
2485 _properties_ve.push_back(column_data[data_index.find(_interpolated_properties[i])->second]);
2486
2487 // Obtain the min/max T, p and check the initial guess
2488 const auto & p_col = column_data[data_index.find("pressure")->second];
2489 _pressure_min = *std::min_element(p_col.begin(), p_col.end());
2490 _pressure_max = *std::max_element(p_col.begin(), p_col.end());
2491 const auto & T_col = column_data[data_index.find("temperature")->second];
2492 _temperature_min = *std::min_element(T_col.begin(), T_col.end());
2493 _temperature_max = *std::max_element(T_col.begin(), T_col.end());
2494 checkInitialGuess(true);
2495 }
2496}
2497
2498void
2500 std::vector<Real> & v2,
2501 const std::string & file_name,
2502 const std::string & v1_name,
2503 const std::string & v2_name)
2504{
2505 // NOTE: We kept the comments in terms of pressure & temperature for clarity
2506 // Pressure (v1) and temperature (v2) data contains duplicates due to the csv format.
2507 // First, check that pressure (v1) is monotonically increasing
2508 if (!std::is_sorted(v1.begin(), v1.end()))
2509 mooseError("The column data for ", v1_name, " is not monotonically increasing in ", file_name);
2510
2511 // The first pressure (v1) value is repeated for each temperature (v2) value. Counting the
2512 // number of repeats provides the number of temperature (v2) values
2513 auto num_v2 = std::count(v1.begin(), v1.end(), v1.front());
2514
2515 // Now remove the duplicates in the pressure (v1) vector
2516 auto last_unique = std::unique(v1.begin(), v1.end());
2517 v1.erase(last_unique, v1.end());
2518
2519 // Check that the number of rows in the csv file is equal to _num_v1 * _num_v2
2520 // v2 is currently the same size as the column_data (will get trimmed at the end)
2521 if (v2.size() != v1.size() * libMesh::cast_int<unsigned int>(num_v2))
2522 mooseError("The number of rows in ",
2523 file_name,
2524 " is not equal to the number of unique ",
2525 v1_name,
2526 " values ",
2527 v1.size(),
2528 " multiplied by the number of unique ",
2529 v2_name,
2530 " values ",
2531 num_v2);
2532
2533 // Need to make sure that the temperature (v2) values are provided in ascending order
2534 std::vector<Real> base_v2(v2.begin(), v2.begin() + num_v2);
2535 if (!std::is_sorted(base_v2.begin(), base_v2.end()))
2536 mooseError("The column data for ", v2_name, " is not monotonically increasing in ", file_name);
2537
2538 // Need to make sure that the temperature (v2) are repeated for each pressure (v1) grid point
2539 auto it_v2 = v2.begin() + num_v2;
2540 for (const auto i : make_range(v1.size() - 1))
2541 {
2542 std::vector<Real> repeated_v2(it_v2, it_v2 + num_v2);
2543 if (repeated_v2 != base_v2)
2544 mooseError(v2_name,
2545 " values for ",
2546 v1_name,
2547 " ",
2548 v1[i + 1],
2549 " are not identical to values for ",
2550 v1[0]);
2551
2552 std::advance(it_v2, num_v2);
2553 }
2554
2555 // At this point, all temperature (v2) data has been provided in ascending order
2556 // identically for each pressure (v1) value, so we can just keep the first range
2557 v2.erase(v2.begin() + num_v2, v2.end());
2558}
2559
2560void
2562{
2563 // At this point, all properties read or generated are able to be used by
2564 // TabulatedFluidProperties. Now set flags and indexes for each property in
2565 //_interpolated_properties to use in property calculations
2566 for (std::size_t i = 0; i < _interpolated_properties.size(); ++i)
2567 {
2568 if (_interpolated_properties[i] == "density")
2569 {
2570 _interpolate_density = true;
2571 _density_idx = i;
2572 }
2573 else if (_interpolated_properties[i] == "enthalpy")
2574 {
2575 _interpolate_enthalpy = true;
2576 _enthalpy_idx = i;
2577 }
2578 else if (_interpolated_properties[i] == "internal_energy")
2579 {
2582 }
2583 else if (_interpolated_properties[i] == "viscosity")
2584 {
2586 _viscosity_idx = i;
2587 }
2588 else if (_interpolated_properties[i] == "k")
2589 {
2590 _interpolate_k = true;
2591 _k_idx = i;
2592 }
2593 else if (_interpolated_properties[i] == "c")
2594 {
2595 _interpolate_c = true;
2596 _c_idx = i;
2597 }
2598 else if (_interpolated_properties[i] == "cp")
2599 {
2600 _interpolate_cp = true;
2601 _cp_idx = i;
2602 }
2603 else if (_interpolated_properties[i] == "cv")
2604 {
2605 _interpolate_cv = true;
2606 _cv_idx = i;
2607 }
2608 else if (_interpolated_properties[i] == "entropy")
2609 {
2610 _interpolate_entropy = true;
2611 _entropy_idx = i;
2612 }
2613 else if (_interpolated_properties[i] == "pressure")
2614 {
2615 _interpolate_pressure = true;
2616 _p_idx = i;
2617 }
2618 else if (_interpolated_properties[i] == "temperature")
2619 {
2621 _T_idx = i;
2622 }
2623 else
2624 mooseError("Specified property '" + _interpolated_properties[i] +
2625 "' is present in the tabulation but is not currently leveraged by the code in the "
2626 "TabulatedFluidProperties. If it is spelled correctly, then please contact a "
2627 "MOOSE or fluid properties module developer.");
2628 }
2629}
2630
2631void
2633{
2634 mooseAssert(_file_name_ve_in.empty(), "We should be reading the specific volume grid from file");
2636 {
2637 // if csv exists, get max and min values from csv file
2639 {
2640 Real rho_max =
2641 *std::max_element(_properties[_density_idx].begin(), _properties[_density_idx].end());
2642 Real rho_min =
2643 *std::min_element(_properties[_density_idx].begin(), _properties[_density_idx].end());
2644 _v_max = 1 / rho_min;
2645 _v_min = 1 / rho_max;
2646 }
2647 else if (_fp)
2648 {
2649 // extreme values of specific volume for the grid bounds
2650 Real v1 = _fp->v_from_p_T(_pressure_min, _temperature_min);
2651 Real v2 = _fp->v_from_p_T(_pressure_max, _temperature_min);
2652 Real v3 = _fp->v_from_p_T(_pressure_min, _temperature_max);
2653 Real v4 = _fp->v_from_p_T(_pressure_max, _temperature_max);
2654 _v_min = std::min({v1, v2, v3, v4});
2655 _v_max = std::max({v1, v2, v3, v4});
2656 }
2657 else
2658 mooseWarning("Unable to compute grid bounds in specific volume. Please specify the v_min/max "
2659 "parameters");
2660
2661 // Prevent changing the bounds of the grid
2662 _v_bounds_specified = true;
2663 }
2664
2665 // Create v grid for interpolation
2666 _specific_volume.resize(_num_v);
2667 if (_log_space_v)
2668 {
2669 // incrementing the exponent linearly will yield a log-spaced grid after taking the value to
2670 // the power of 10
2671 Real dv = (std::log10(_v_max) - std::log10(_v_min)) / ((Real)_num_v - 1);
2672 Real log_v_min = std::log10(_v_min);
2673 for (unsigned int j = 0; j < _num_v; ++j)
2674 _specific_volume[j] = std::pow(10, log_v_min + j * dv);
2675 }
2676 else
2677 {
2678 Real dv = (_v_max - _v_min) / ((Real)_num_v - 1);
2679 for (unsigned int j = 0; j < _num_v; ++j)
2680 _specific_volume[j] = _v_min + j * dv;
2681 }
2682}
2683
2684void
2686{
2689 {
2690 // if csv exists, get max and min values from csv file
2692 {
2693 _e_min = *std::min_element(_properties[_internal_energy_idx].begin(),
2695 _e_max = *std::max_element(_properties[_internal_energy_idx].begin(),
2697 }
2698 else if (_fp)
2699 {
2700 // extreme values of internal energy for the grid bounds
2701 Real e1 = _fp->e_from_p_T(_pressure_min, _temperature_min);
2702 Real e2 = _fp->e_from_p_T(_pressure_max, _temperature_min);
2703 Real e3 = _fp->e_from_p_T(_pressure_min, _temperature_max);
2704 Real e4 = _fp->e_from_p_T(_pressure_max, _temperature_max);
2705 _e_min = std::min({e1, e2, e3, e4});
2706 _e_max = std::max({e1, e2, e3, e4});
2707 }
2708 else
2709 mooseWarning("Unable to compute grid bounds in internal energy. Please specify the e_min/max "
2710 "parameters");
2711 }
2712
2713 // Create e grid for interpolation
2714 _internal_energy.resize(_num_e);
2715 if (_log_space_e)
2716 {
2717 // incrementing the exponent linearly will yield a log-spaced grid after taking the value to
2718 // the power of 10
2719 if (_e_min < 0)
2720 mooseError("Logarithmic grid in specific energy can only be used with a positive specific "
2721 "energy. Current minimum: " +
2722 std::to_string(_e_min));
2723 Real de = (std::log10(_e_max) - std::log10(_e_min)) / ((Real)_num_e - 1);
2724 Real log_e_min = std::log10(_e_min);
2725 for (const auto j : make_range(_num_e))
2726 _internal_energy[j] = std::pow(10, log_e_min + j * de);
2727 }
2728 else
2729 {
2730 Real de = (_e_max - _e_min) / ((Real)_num_e - 1);
2731 for (const auto j : make_range(_num_e))
2732 _internal_energy[j] = _e_min + j * de;
2733 }
2734}
2735
2736void
2738{
2739 if (_file_name_ve_in.empty())
2741 if (_fp)
2742 {
2743 // extreme values of enthalpy for the grid bounds
2744 Real h1 = _fp->h_from_p_T(_pressure_min, _temperature_min);
2745 Real h2 = _fp->h_from_p_T(_pressure_max, _temperature_min);
2746 Real h3 = _fp->h_from_p_T(_pressure_min, _temperature_max);
2747 Real h4 = _fp->h_from_p_T(_pressure_max, _temperature_max);
2748 _h_min = std::min({h1, h2, h3, h4});
2749 _h_max = std::max({h1, h2, h3, h4});
2750 }
2751 // if csv exists, get max and min values from csv file
2752 else if (_properties.size())
2753 {
2754 _h_max = *max_element(_properties[_enthalpy_idx].begin(), _properties[_enthalpy_idx].end());
2755 _h_min = *min_element(_properties[_enthalpy_idx].begin(), _properties[_enthalpy_idx].end());
2756 }
2757 else if (_properties_ve.size())
2758 {
2759 _h_max = *max_element(_properties[_enthalpy_idx].begin(), _properties[_enthalpy_idx].end());
2760 _h_min = *min_element(_properties[_enthalpy_idx].begin(), _properties[_enthalpy_idx].end());
2761 }
2762 else
2763 mooseError("Need a source to compute the enthalpy grid bounds: either a FP object, or a (p,T) "
2764 "tabulation file or a (v,e) tabulation file");
2765
2766 // Create h grid for interpolation
2767 // enthalpy & internal energy use same # grid points
2768 _enthalpy.resize(_num_e);
2769 if (_log_space_h)
2770 {
2771 // incrementing the exponent linearly will yield a log-spaced grid after taking the value to
2772 // the power of 10
2773 if (_h_min < 0)
2774 mooseError("Logarithmic grid in specific energy can only be used with a positive enthalpy. "
2775 "Current minimum: " +
2776 std::to_string(_h_min));
2777 Real dh = (std::log10(_h_max) - std::log10(_h_min)) / ((Real)_num_e - 1);
2778 Real log_h_min = std::log10(_h_min);
2779 for (const auto j : make_range(_num_e))
2780 _enthalpy[j] = std::pow(10, log_h_min + j * dh);
2781 }
2782 else
2783 {
2784 Real dh = (_h_max - _h_min) / ((Real)_num_e - 1);
2785 for (const auto j : make_range(_num_e))
2786 _enthalpy[j] = _h_min + j * dh;
2787 }
2788}
2789
2790void
2791TabulatedFluidProperties::missingVEInterpolationError(const std::string & function_name) const
2792{
2793 mooseError(function_name +
2794 ": to call this function you must:\n-add this property to the list to the list of "
2795 "'interpolated_properties'\n and then either:\n-construct (p, T) from (v, e) "
2796 "tabulations using the 'construct_pT_from_ve' parameter\n-load (v,e) interpolation "
2797 "tables using the 'fluid_property_ve_file' parameter");
2798}
2799
2800template void TabulatedFluidProperties::checkInputVariables(Real & pressure,
2801 Real & temperature) const;
2803 ADReal & temperature) const;
2804template void TabulatedFluidProperties::checkInputVariablesVE(Real & v, Real & e) const;
DualNumber< Real, DNDerivativeType, true > ADReal
const double mu
const Real p
const double rho
const double T
const double v
const std::string name
Definition Setup.h:21
void ErrorVector unsigned int
const ConsoleStream _console
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
void addDeprecatedParam(const std::string &name, const T &value, const std::string &doc_string, const std::string &deprecation_message)
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)
void deprecateParam(const std::string &old_name, const std::string &new_name, const std::string &removal_date)
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
const std::string & name() const
void paramError(const std::string &param, Args... args) const
bool isParamSetByUser(const std::string &name) const
void mooseError(Args &&... args) const
void mooseWarning(Args &&... args) const
bool isParamValid(const std::string &name) const
const std::vector< std::string > & getNames() const
const std::vector< std::vector< T > > & getData() const
void setComment(const std::string &value)
void setFileName(const std::string &new_file)
bool contains(const std::string &value) const
unsigned int size() const
Common class for single phase fluid properties.
virtual std::vector< Real > henryCoefficients() const
Henry's law coefficients for dissolution in water.
const Real _T_initial_guess
Initial guess for temperature (or temperature used to compute the initial guess)
void p_T_from_h_s(const T &h, const T &s, Real p0, Real T0, T &pressure, T &temperature, bool &conversion_succeeded) const
Determines (p,T) from (h,s) using Newton Solve in 2D Useful for conversion between different sets of ...
const bool _verbose_newton
Whether to output information about newton solves to console.
void p_T_from_v_e(const CppType &v, const CppType &e, Real p0, Real T0, CppType &p, CppType &T, bool &conversion_succeeded) const
Determines (p,T) from (v,e) using Newton Solve in 2D Useful for conversion between different sets of ...
const Real _p_initial_guess
Initial guess for pressure (or pressure used to compute the initial guess)
virtual Real vaporPressure(Real T) const
Vapor pressure.
static InputParameters validParams()
const Real _tolerance
Newton's method may be used to convert between variable sets.
virtual Real triplePointPressure() const
Triple point pressure.
virtual Real criticalDensity() const
Critical density.
virtual Real criticalPressure() const
Critical pressure.
const unsigned int _max_newton_its
Maximum number of iterations for the variable conversion newton solves.
virtual Real molarMass() const
Molar mass [kg/mol].
void v_e_from_p_T(const CppType &p, const CppType &T, CppType &v, CppType &e) const
e e e e s T T T T T rho v v T e h
virtual Real criticalTemperature() const
Critical temperature.
virtual Real triplePointTemperature() const
Triple point temperature.
virtual Real vaporTemperature(Real p) const
Vapor temperature.
e e e e s T T T T T rho v v T e p T T virtual T std::string fluidName() const
Fluid name.
FileName _file_name_out
File name of output tabulated data file.
std::unique_ptr< BidimensionalInterpolation > _T_from_v_e_ipol
Bi-dimensional interpolation of temperature from (v,e)
virtual Real v_from_p_T(Real pressure, Real temperature) const override
unsigned int _num_T
Number of temperature points in the tabulated data.
const bool _save_file
Whether to save a generated fluid properties file to disk.
bool _initial_setup_done
keeps track of whether initialSetup has been performed
const SinglePhaseFluidProperties *const _fp
SinglePhaseFluidPropertiesPT UserObject.
FileName _file_name_ve_out
File name of output (v,e) tabulated data file.
std::vector< std::string > _interpolated_properties
List of properties to be interpolated.
void checkInputVariables(T &pressure, T &temperature) const
Checks that the inputs are within the range of the tabulated data, and throws an error if they are no...
std::vector< Real > _internal_energy
Specific internal energy vector.
void readFileTabulationData(bool use_pT)
Read tabulation data from file.
Real _v_min
Minimum specific volume in tabulated data (can be user-specified)
virtual Real s_from_v_e(Real v, Real e) const override
virtual Real triplePointTemperature() const override
Triple point temperature.
std::vector< std::vector< Real > > _properties_ve
Tabulated fluid properties in (v,e) (read from file OR computed from _fp)
void computePropertyIndicesInInterpolationVectors()
Retrieves the index for each property in the vector of interpolations.
virtual Real e_from_v_h(Real v, Real h) const override
TabulatedFluidProperties(const InputParameters &parameters)
unsigned int _num_v
Number of specific volume points in the tabulated data.
Real _v_max
Maximum specific volume in tabulated data (can be user-specified)
Real _h_min
Minimum specific enthalpy in tabulated data.
FileName _file_name_ve_in
File name of input (v,e) tabulated data file.
virtual Real T_from_p_h(Real pressure, Real enthalpy) const override
void missingVEInterpolationError(const std::string &function_name) const
Standardized error message for missing interpolation.
virtual void constructInterpolation()=0
virtual Real rho_from_p_s(Real p, Real s) const override
std::vector< Real > _temperature
Temperature vector.
const bool _create_direct_ve_interpolations
Whether the object has direct (v,e) interpolations (whether created from file or from _fp)
virtual Real g_from_v_e(Real v, Real e) const override
void writeTabulatedData(std::string file_name)
Writes tabulated data to a file.
void NeedTabulationError(const std::string &needed_property) const
Utility to forward errors related to properties being requested for tabulation, but no tabulation is ...
virtual void generateTabulatedData()
Generates a table of fluid properties by looping over pressure and temperature and calculating proper...
virtual Real cp_from_p_T(Real pressure, Real temperature) const override
std::unique_ptr< BidimensionalInterpolation > _p_from_v_h_ipol
Bidimensional interpolation of pressure from (v,h)
virtual void generateVETabulatedData()
Generates a table of fluid properties by looping over specific volume and internal energy and calcula...
Real _temperature_max
Maximum temperature in tabulated data.
static InputParameters validParams()
Real _e_min
Minimum internal energy in tabulated data (can be user-specified)
virtual Real T_from_p_rho(Real pressure, Real rho) const
const bool _create_direct_pT_interpolations
Whether the object has direct (p,T) interpolations (whether created from file or from _fp)
Real _e_max
Maximum internal energy in tabulated data (can be user-specified)
virtual Real triplePointPressure() const override
Triple point pressure.
void checkFileTabulationGrids(std::vector< Real > &v1, std::vector< Real > &v2, const std::string &file_name, const std::string &v1_name, const std::string &v2_name)
Check that the tabulation grids in the file are correct (no repeats etc)
std::vector< Real > _enthalpy
Specific enthalpy vector.
virtual Real cp_from_v_e(Real v, Real e) const override
void TabulationNotImplementedError(const std::string &desired_routine) const
Utility to forward errors related to fluid properties methods not implemented.
virtual Real cv_from_v_e(Real v, Real e) const override
void createVHGridVectors()
Create (or reset) the grid vectors for the specific volume and enthalpy interpolation The order of pr...
virtual Real T_from_h_s(Real h, Real s) const
bool _log_space_v
log-space the specific volume interpolation grid axis instead of linear
unsigned int _num_p
Number of pressure points in the tabulated data.
OOBBehavior
Enum specifying all the behavior on out of bounds data options.
MooseUtils::DelimitedFileReader _csv_reader
The MOOSE delimited file reader.
FileName _file_name_in
File name of input tabulated data file.
bool _v_bounds_specified
Whether the specific volume bounds were set by the user.
std::vector< std::unique_ptr< BidimensionalInterpolation > > _property_ve_ipol
Vector of bi-dimensional interpolation of fluid properties directly in (v,e)
bool _construct_pT_from_vh
if the lookup table p(v, h) and T(v, h) should be constructed
virtual void checkInitialGuess(bool post_reading_tabulation) const
Checks initial guess for Newton Method.
MultiMooseEnum _interpolated_properties_enum
Properties to be interpolated entered in the input file.
std::unique_ptr< BidimensionalInterpolation > _p_from_v_e_ipol
Bi-dimensional interpolation of pressure from (v,e)
void createVGridVector()
Create (or reset) the grid vectors for the specific volume and internal energy interpolations The ord...
virtual Real criticalTemperature() const override
Critical temperature.
virtual Real h_from_p_T(Real p, Real T) const override
unsigned int _num_e
Number of internal energy points in tabulated data.
void NeedTabulationOrFPError(const std::string &desired_routine, const std::string &needed_property) const
Utility to forward errors related to fluid properties needing more data for their computation This sh...
std::vector< Real > _specific_volume
Specific volume vector.
virtual Real rho_from_p_T(Real pressure, Real temperature) const override
virtual void initialSetup() override
virtual Real e_from_p_rho(Real pressure, Real rho) const override
virtual Real cv_from_p_T(Real pressure, Real temperature) const override
MooseEnum _OOBBehavior
User-selected out-of-bounds behavior.
virtual Real p_from_v_e(Real v, Real e) const override
Derivatives like dc_dv & dc_de are computed using the chain rule dy/dx(p,T) = dy/dp * dp/dx + dy/dT *...
bool _log_space_e
log-space the internal energy interpolation grid axis instead of linear
void checkInputVariablesVE(T &v, T &e) const
Checks that the inputs are within the range of the tabulated data, and throws an error if they are no...
virtual Real mu_from_p_T(Real pressure, Real temperature) const override
virtual Real mu_from_v_e(Real v, Real e) const override
virtual Real k_from_p_T(Real pressure, Real temperature) const override
virtual std::vector< Real > henryCoefficients() const override
The following routines are simply forwarded to the 'fp' companion FluidProperties as they are not inc...
virtual Real T_from_p_s(Real p, Real s) const
virtual Real s_from_h_p(Real h, Real pressure) const override
virtual Real vaporTemperature(Real pressure) const override
Vapor temperature.
Real _pressure_max
Maximum pressure in tabulated data.
virtual Real vaporPressure(Real temperature) const override
Vapor pressure.
Real _h_max
Maximum specific enthalpy in tabulated data.
std::vector< std::unique_ptr< BidimensionalInterpolation > > _property_ipol
Vector of bi-dimensional interpolation of fluid properties.
virtual Real criticalDensity() const override
Critical density.
virtual Real molarMass() const override
Molar mass [kg/mol].
virtual Real T_from_v_e(Real v, Real e) const override
unsigned int _density_idx
Index of each property.
virtual Real k_from_v_e(Real v, Real e) const override
std::unique_ptr< BidimensionalInterpolation > _T_from_v_h_ipol
Bi-dimensional interpolation of temperature from (v,h)
virtual Real s_from_p_T(Real pressure, Real temperature) const override
Real _temperature_min
Minimum temperature in tabulated data.
virtual std::string fluidName() const override
Fluid name.
Real _pressure_min
Minimum pressure in tabulated data.
const bool _allow_fp_and_tabulation
Whether to allow a fp object when a tabulation is in use.
virtual Real c_from_v_e(Real v, Real e) const override
std::vector< std::vector< Real > > _properties
Tabulated fluid properties (read from file OR computed from _fp)
bool _construct_pT_from_ve
if the lookup table p(v, e) and T(v, e) should be constructed
std::vector< Real > _pressure
Pressure vector.
bool _log_space_h
log-space the enthalpy interpolation grid axis instead of linear
bool _e_bounds_specified
Whether the specific internal energy bounds were set by the user.
virtual Real e_from_p_T(Real pressure, Real temperature) const override
virtual Real c_from_p_T(Real pressure, Real temperature) const override
bool _interpolate_density
Set of flags to note whether a property is to be interpolated.
virtual Real criticalPressure() const override
Critical pressure.
processor_id_type processor_id() const
std::pair< T, T > NewtonSolve(const T &x, const T &y, const Real z_initial_guess, const Real tolerance, const Functor &y_from_x_z, const std::string &caller_name, const unsigned int max_its=100, const bool verbose=false)
NewtonSolve does a 1D Newton Solve to solve the equation y = f(x, z) for variable z.
void NewtonSolve2D(const T &f, const T &g, const Real x0, const Real y0, T &x_final, T &y_final, const Real f_tol, const Real g_tol, const Functor1 &f_from_x_y, const Functor2 &g_from_x_y, const std::string &caller_name="", const unsigned int max_its=100, bool debug=false)
NewtonSolve2D does a 2D Newton Solve to solve for the x and y such that: f = f_from_x_y(x,...
bool checkFileWriteable(const std::string &filename, bool throw_on_unwritable)
std::string stringify(const T &t)
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
const unsigned int invalid_uint
static constexpr Real TOLERANCE