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 // Relative steps, made absolute at zero so that they do not vanish
557 const Real dp = (pressure == 0 ? 1.0 : std::abs(pressure)) * 1e-8;
558 const Real dT = temperature * 1e-8;
559 drho_dp = (rho_from_p_T(pressure + dp, temperature) - rho) / dp;
560 drho_dT = (rho_from_p_T(pressure, temperature + dT) - rho) / dT;
561 }
562 else
563 {
564 if (_fp)
565 _fp->rho_from_p_T(pressure, temperature, rho, drho_dp, drho_dT);
566 else
567 NeedTabulationOrFPError("rho_from_p_T with derivatives", "density");
568 }
569}
570
571void
573 const ADReal & temperature,
574 ADReal & rho,
575 ADReal & drho_dp,
576 ADReal & drho_dT) const
577{
579 {
580 ADReal p = pressure, T = temperature;
582 _property_ipol[_density_idx]->sampleValueAndDerivatives(p, T, rho, drho_dp, drho_dT);
583 }
584 else
585 {
586 if (_fp)
587 _fp->rho_from_p_T(pressure, temperature, rho, drho_dp, drho_dT);
588 else
589 NeedTabulationOrFPError("AD rho_from_p_T with derivatives", "density");
590 }
591}
592
593Real
595{
596 Real T = T_from_p_s(p, s);
597 return rho_from_p_T(p, T);
598}
599
600void
602 Real p, Real s, Real & rho, Real & drho_dp, Real & drho_ds) const
603{
604 Real T, dT_dp, dT_ds;
605 T_from_p_s(p, s, T, dT_dp, dT_ds);
606 Real drho_dp_T, drho_dT;
607 rho_from_p_T(p, T, rho, drho_dp_T, drho_dT);
608 drho_dp = drho_dT * dT_dp + drho_dp_T;
609 drho_ds = drho_dT * dT_ds;
610}
611
612Real
613TabulatedFluidProperties::e_from_p_T(Real pressure, Real temperature) const
614{
616 {
617 checkInputVariables(pressure, temperature);
619 return _property_ipol[_internal_energy_idx]->sample(pressure, temperature);
620 else
621 NeedTabulationError("internal_energy");
622 }
624 {
625 checkInputVariables(pressure, temperature);
626 const Real rho = rho_from_p_T(pressure, temperature);
627 auto lambda = [&](Real v, Real current_e, Real & new_T, Real & dT_dv, Real & dT_de)
628 { T_from_v_e(v, current_e, new_T, dT_dv, dT_de); };
629 const auto pair = FluidPropertiesUtils::NewtonSolve(1. / rho,
630 temperature,
631 /*initial guess*/ (_e_min + _e_max) / 2,
633 lambda,
634 name() + "::e_from_p_T",
637 return pair.first;
638 }
639 else
640 {
641 if (_fp)
642 return _fp->e_from_p_T(pressure, temperature);
643 else
644 NeedTabulationOrFPError("e_from_p_T", "internal_energy");
645 }
646}
647
648ADReal
649TabulatedFluidProperties::e_from_p_T(const ADReal & pressure, const ADReal & temperature) const
650{
652 {
653 ADReal pressure_nc = pressure, temperature_nc = temperature;
654 checkInputVariables(pressure_nc, temperature_nc);
656 return _property_ipol[_internal_energy_idx]->sample(pressure_nc, temperature_nc);
657 else
658 NeedTabulationError("internal_energy");
659 }
661 {
662 ADReal pressure_nc = pressure, temperature_nc = temperature;
663 checkInputVariables(pressure_nc, temperature_nc);
664 const ADReal rho = rho_from_p_T(pressure_nc, temperature_nc);
665 auto lambda = [&](ADReal v, ADReal current_e, ADReal & new_T, ADReal & dT_dv, ADReal & dT_de)
666 { T_from_v_e(v, current_e, new_T, dT_dv, dT_de); };
667 const auto pair = FluidPropertiesUtils::NewtonSolve(1. / rho,
668 temperature_nc,
669 /*initial guess*/ (_e_min + _e_max) / 2,
671 lambda,
672 name() + "::e_from_p_T",
675 return pair.first;
676 }
677 else
678 {
679 if (_fp)
680 return _fp->e_from_p_T(pressure, temperature);
681 else
682 NeedTabulationOrFPError("AD e_from_p_T", "internal_energy");
683 }
684}
685
686void
688 Real pressure, Real temperature, Real & e, Real & de_dp, Real & de_dT) const
689{
691 {
692 checkInputVariables(pressure, temperature);
694 _property_ipol[_internal_energy_idx]->sampleValueAndDerivatives(
695 pressure, temperature, e, de_dp, de_dT);
696 else
697 NeedTabulationError("internal_energy");
698 }
700 {
701 checkInputVariables(pressure, temperature);
702 // use finite differencing stencil
703 e = e_from_p_T(pressure, temperature);
704 // Relative steps, made absolute at zero so that they do not vanish
705 const Real dp = (pressure == 0 ? 1.0 : std::abs(pressure)) * 1e-8;
706 const Real dT = temperature * 1e-8;
707 de_dp = (e_from_p_T(pressure + dp, temperature) - e) / dp;
708 de_dT = (e_from_p_T(pressure, temperature + dT) - e) / dT;
709 }
710 else
711 {
712 if (_fp)
713 _fp->e_from_p_T(pressure, temperature, e, de_dp, de_dT);
714 else
715 NeedTabulationOrFPError("e_from_p_T with derivatives", "internal_energy");
716 }
717}
718
719ADReal
721{
722 ADReal e;
723 // Use v,e data, we already have v from rho
725 {
726 auto lambda = [&](ADReal v, ADReal current_e, ADReal & new_p, ADReal & dp_dv, ADReal & dp_de)
727 { p_from_v_e(v, current_e, new_p, dp_dv, dp_de); };
728 const auto pair = FluidPropertiesUtils::NewtonSolve(1. / rho,
729 pressure,
730 /*initial guess*/ (_e_min + _e_max) / 2,
732 lambda,
733 name() + "::e_from_p_rho",
736 e = pair.first;
737 }
738 // May use rho_from_p_T with derivatives in a Newton solve
739 else
740 {
741 const auto T = T_from_p_rho(pressure, rho);
742 e = e_from_p_T(pressure, T);
743 }
744 return e;
745}
746
747Real
749{
750 Real e;
751 // Use v,e data, we already have v from rho
753 {
754 Real T_dummy = _T_initial_guess;
755 checkInputVariables(pressure, T_dummy);
756 auto lambda = [&](Real v, Real current_e, Real & new_p, Real & dp_dv, Real & dp_de)
757 { p_from_v_e(v, current_e, new_p, dp_dv, dp_de); };
758 const auto pair = FluidPropertiesUtils::NewtonSolve(1. / rho,
759 pressure,
760 /*initial guess*/ (_e_min + _e_max) / 2,
762 lambda,
763 name() + "::e_from_p_rho",
766 e = pair.first;
767 }
768 // May use rho_from_p_T with derivatives in a Newton solve
769 else
770 {
771 Real T = T_from_p_rho(pressure, rho);
772 e = e_from_p_T(pressure, T);
773 }
774 return e;
775}
776
777void
779 Real pressure, Real rho, Real & e, Real & de_dp, Real & de_drho) const
780{
781 // get derivatives of T wrt to pressure and density
782 Real T, dT_dp, dT_drho;
783 T_from_p_rho(pressure, rho, T, dT_dp, dT_drho);
784
785 // Get e, then derivatives of e wrt pressure and temperature
786 Real de_dp_at_const_T, de_dT;
787 e_from_p_T(pressure, T, e, de_dp_at_const_T, de_dT);
788
789 // Get the derivatives of density wrt pressure and temperature
790 Real rho_pT, drho_dp, drho_dT;
791 rho_from_p_T(pressure, T, rho_pT, drho_dp, drho_dT);
792
793 // derivatives of e wrt pressure and rho (what we want from e_from_p_rho)
794 de_drho = de_dT * dT_drho;
795 de_dp = de_dp_at_const_T - (de_drho * drho_dp);
796}
797
798Real
800{
801 Real T = _T_initial_guess;
803 {
804 checkInputVariables(pressure, T);
805 auto lambda = [&](Real p, Real current_T, Real & new_rho, Real & drho_dp, Real & drho_dT)
806 { rho_from_p_T(p, current_T, new_rho, drho_dp, drho_dT); };
808 rho,
811 lambda,
812 name() + "::T_from_p_rho",
815 .first;
816 }
818 T = T_from_v_e(1. / rho, e_from_p_rho(pressure, rho));
819 else
820 NeedTabulationOrFPError("T_from_p_rho", "temperature");
821 // check for nans
822 if (std::isnan(T))
823 mooseError("Conversion from pressure (p = ",
824 pressure,
825 ") and density (rho = ",
826 rho,
827 ") to temperature failed to converge.");
828 return T;
829}
830
831ADReal
833{
835 {
836 auto lambda =
837 [&](ADReal p, ADReal current_T, ADReal & new_rho, ADReal & drho_dp, ADReal & drho_dT)
838 { rho_from_p_T(p, current_T, new_rho, drho_dp, drho_dT); };
840 rho,
843 lambda,
844 name() + "::T_from_p_rho",
847 .first;
848 // check for nans
849 if (std::isnan(T.value()))
850 mooseError("Conversion from pressure (p = ",
851 pressure,
852 ") and density (rho = ",
853 rho,
854 ") to temperature failed to converge.");
855 return T;
856 }
858 return T_from_v_e(1. / rho, e_from_p_rho(pressure, rho));
859 else
860 NeedTabulationOrFPError("AD T_from_p_rho", "temperature");
861}
862
863void
865 Real pressure, Real rho, Real & T, Real & dT_dp, Real & dT_drho) const
866{
867 T = T_from_p_rho(pressure, rho);
868 // Implicit differentiation of rho(p, T) = rho
869 Real rho_pT, drho_dp, drho_dT;
870 rho_from_p_T(pressure, T, rho_pT, drho_dp, drho_dT);
871 dT_dp = -drho_dp / drho_dT;
872 dT_drho = 1.0 / drho_dT;
873}
874
875Real
876TabulatedFluidProperties::T_from_p_s(Real pressure, Real s) const
877{
878 auto lambda = [&](Real p, Real current_T, Real & new_s, Real & ds_dp, Real & ds_dT)
879 { s_from_p_T(p, current_T, new_s, ds_dp, ds_dT); };
880 Real T = FluidPropertiesUtils::NewtonSolve(pressure,
881 s,
884 lambda,
885 name() + "::T_from_p_s",
888 .first;
889 // check for nans
890 if (std::isnan(T))
891 mooseError("Conversion from pressure (p = ",
892 pressure,
893 ") and entropy (s = ",
894 s,
895 ") to temperature failed to converge.");
896 return T;
897}
898
899void
901 Real pressure, Real s, Real & T, Real & dT_dp, Real & dT_ds) const
902{
903 T = T_from_p_s(pressure, s);
904 // Implicit differentiation of s(p, T) = s
905 Real s_pT, ds_dp, ds_dT;
906 s_from_p_T(pressure, T, s_pT, ds_dp, ds_dT);
907 dT_dp = -ds_dp / ds_dT;
908 dT_ds = 1.0 / ds_dT;
909}
910
911Real
912TabulatedFluidProperties::h_from_p_T(Real pressure, Real temperature) const
913{
915 {
916 checkInputVariables(pressure, temperature);
918 return _property_ipol[_enthalpy_idx]->sample(pressure, temperature);
920 {
921 Real v, e;
922 SinglePhaseFluidProperties::v_e_from_p_T(pressure, temperature, v, e);
923 return _property_ve_ipol[_enthalpy_idx]->sample(v, e);
924 }
925 else
926 NeedTabulationError("enthalpy");
927 }
928 else
929 {
930 if (_fp)
931 return _fp->h_from_p_T(pressure, temperature);
932 else
933 NeedTabulationOrFPError("h_from_p_T", "enthalpy");
934 }
935}
936
937ADReal
938TabulatedFluidProperties::h_from_p_T(const ADReal & pressure, const ADReal & temperature) const
939{
941 {
942 ADReal pressure_nc = pressure, temperature_nc = temperature;
943 checkInputVariables(pressure_nc, temperature_nc);
945 return _property_ipol[_enthalpy_idx]->sample(pressure_nc, temperature_nc);
947 {
948 ADReal v, e;
949 SinglePhaseFluidProperties::v_e_from_p_T(pressure_nc, temperature_nc, v, e);
950 return _property_ve_ipol[_enthalpy_idx]->sample(v, e);
951 }
952 else
953 NeedTabulationError("enthalpy");
954 }
955 else if (_fp) // Assuming _fp can handle ADReal types
956 return _fp->h_from_p_T(pressure, temperature);
957 else
958 NeedTabulationOrFPError("AD h_from_p_T", "enthalpy");
959}
960
961void
963 Real pressure, Real temperature, Real & h, Real & dh_dp, Real & dh_dT) const
964{
966 {
967 checkInputVariables(pressure, temperature);
969 _property_ipol[_enthalpy_idx]->sampleValueAndDerivatives(
970 pressure, temperature, h, dh_dp, dh_dT);
972 {
973 Real v, e, dv_dp, dv_dT, de_dp, de_dT;
975 pressure, temperature, v, dv_dp, dv_dT, e, de_dp, de_dT);
976 Real dh_dv, dh_de;
977 _property_ve_ipol[_enthalpy_idx]->sampleValueAndDerivatives(v, e, h, dh_dv, dh_de);
978 dh_dp = dh_dv * dv_dp + dh_de * de_dp;
979 dh_dT = dh_dv * dv_dT + dh_de * de_dT;
980 }
981 else
982 NeedTabulationError("enthalpy");
983 }
984 else
985 {
986 if (_fp)
987 _fp->h_from_p_T(pressure, temperature, h, dh_dp, dh_dT);
988 else
989 NeedTabulationOrFPError("h_from_p_T with derivatives", "enthalpy");
990 }
991}
992
993Real
994TabulatedFluidProperties::mu_from_p_T(Real pressure, Real temperature) const
995{
997 {
998 checkInputVariables(pressure, temperature);
999 return _property_ipol[_viscosity_idx]->sample(pressure, temperature);
1000 }
1001 else
1002 {
1003 if (_fp)
1004 return _fp->mu_from_p_T(pressure, temperature);
1005 else
1006 NeedTabulationOrFPError("mu_from_p_T", "viscosity");
1007 }
1008}
1009
1010void
1012 Real pressure, Real temperature, Real & mu, Real & dmu_dp, Real & dmu_dT) const
1013{
1015 {
1016 checkInputVariables(pressure, temperature);
1017 _property_ipol[_viscosity_idx]->sampleValueAndDerivatives(
1018 pressure, temperature, mu, dmu_dp, dmu_dT);
1019 }
1020 else
1021 {
1022 if (_fp)
1023 _fp->mu_from_p_T(pressure, temperature, mu, dmu_dp, dmu_dT);
1024 else
1025 NeedTabulationOrFPError("mu_from_p_T with derivatives", "viscosity");
1026 }
1027}
1028
1029Real
1030TabulatedFluidProperties::c_from_p_T(Real pressure, Real temperature) const
1031{
1033 {
1034 checkInputVariables(pressure, temperature);
1035 return _property_ipol[_c_idx]->sample(pressure, temperature);
1036 }
1037 else
1038 {
1039 if (_fp)
1040 return _fp->c_from_p_T(pressure, temperature);
1041 else
1042 NeedTabulationOrFPError("c_from_p_T", "c");
1043 }
1044}
1045
1046void
1048 Real pressure, Real temperature, Real & c, Real & dc_dp, Real & dc_dT) const
1049{
1051 {
1052 checkInputVariables(pressure, temperature);
1053 _property_ipol[_c_idx]->sampleValueAndDerivatives(pressure, temperature, c, dc_dp, dc_dT);
1054 }
1055 else
1056 {
1057 if (_fp)
1058 _fp->c_from_p_T(pressure, temperature, c, dc_dp, dc_dT);
1059 else
1060 NeedTabulationOrFPError("c_from_p_T with derivatives", "c");
1061 }
1062}
1063
1064Real
1065TabulatedFluidProperties::cp_from_p_T(Real pressure, Real temperature) const
1066{
1068 {
1069 checkInputVariables(pressure, temperature);
1070 return _property_ipol[_cp_idx]->sample(pressure, temperature);
1071 }
1072 else
1073 {
1074 if (_fp)
1075 return _fp->cp_from_p_T(pressure, temperature);
1076 else
1077 NeedTabulationOrFPError("cp_from_p_T", "c");
1078 }
1079}
1080
1081void
1083 Real pressure, Real temperature, Real & cp, Real & dcp_dp, Real & dcp_dT) const
1084{
1086 {
1087 checkInputVariables(pressure, temperature);
1088 _property_ipol[_cp_idx]->sampleValueAndDerivatives(pressure, temperature, cp, dcp_dp, dcp_dT);
1089 }
1090 else
1091 {
1092 if (_fp)
1093 _fp->cp_from_p_T(pressure, temperature, cp, dcp_dp, dcp_dT);
1094 else
1095 NeedTabulationOrFPError("cp_from_p_T with derivatives", "cp");
1096 }
1097}
1098
1099Real
1100TabulatedFluidProperties::cv_from_p_T(Real pressure, Real temperature) const
1101{
1103 {
1104 checkInputVariables(pressure, temperature);
1105 return _property_ipol[_cv_idx]->sample(pressure, temperature);
1106 }
1107 else
1108 {
1109 if (_fp)
1110 return _fp->cv_from_p_T(pressure, temperature);
1111 else
1112 NeedTabulationOrFPError("cv_from_p_T", "cv");
1113 }
1114}
1115
1116void
1118 Real pressure, Real temperature, Real & cv, Real & dcv_dp, Real & dcv_dT) const
1119{
1120 if (_interpolate_cv)
1121 {
1122 checkInputVariables(pressure, temperature);
1123 _property_ipol[_cv_idx]->sampleValueAndDerivatives(pressure, temperature, cv, dcv_dp, dcv_dT);
1124 }
1125 else
1126 {
1127 if (_fp)
1128 _fp->cv_from_p_T(pressure, temperature, cv, dcv_dp, dcv_dT);
1129 else
1130 NeedTabulationOrFPError("cv_from_p_T with derivatives", "cv");
1131 }
1132}
1133
1134Real
1135TabulatedFluidProperties::k_from_p_T(Real pressure, Real temperature) const
1136{
1138 {
1139 checkInputVariables(pressure, temperature);
1140 return _property_ipol[_k_idx]->sample(pressure, temperature);
1141 }
1142 else
1143 {
1144 if (_fp)
1145 return _fp->k_from_p_T(pressure, temperature);
1146 else
1147 NeedTabulationOrFPError("k_from_p_T", "k");
1148 }
1149}
1150
1151void
1153 Real pressure, Real temperature, Real & k, Real & dk_dp, Real & dk_dT) const
1154{
1156 {
1157 checkInputVariables(pressure, temperature);
1158 return _property_ipol[_k_idx]->sampleValueAndDerivatives(
1159 pressure, temperature, k, dk_dp, dk_dT);
1160 }
1161 else
1162 {
1163 if (_fp)
1164 return _fp->k_from_p_T(pressure, temperature, k, dk_dp, dk_dT);
1165 else
1166 NeedTabulationOrFPError("k_from_p_T with derivatives", "k");
1167 }
1168}
1169
1170Real
1171TabulatedFluidProperties::s_from_p_T(Real pressure, Real temperature) const
1172{
1174 {
1175 checkInputVariables(pressure, temperature);
1176 return _property_ipol[_entropy_idx]->sample(pressure, temperature);
1177 }
1178 else
1179 {
1180 if (_fp)
1181 return _fp->s_from_p_T(pressure, temperature);
1182 else
1183 NeedTabulationOrFPError("s_from_p_T", "entropy");
1184 }
1185}
1186
1187void
1188TabulatedFluidProperties::s_from_p_T(Real p, Real T, Real & s, Real & ds_dp, Real & ds_dT) const
1189{
1191 {
1194 _property_ipol[_entropy_idx]->sampleValueAndDerivatives(p, T, s, ds_dp, ds_dT);
1196 {
1197 Real v, e, dv_dp, dv_dT, de_dp, de_dT;
1198 SinglePhaseFluidProperties::v_e_from_p_T(p, T, v, dv_dp, dv_dT, e, de_dp, de_dT);
1199 Real ds_dv, ds_de;
1200 _property_ve_ipol[_entropy_idx]->sampleValueAndDerivatives(v, e, s, ds_dv, ds_de);
1201 ds_dp = ds_dv * dv_dp + ds_de * de_dp;
1202 ds_dT = ds_dv * dv_dT + ds_de * de_dT;
1203 }
1204 else
1205 NeedTabulationError("entropy");
1206 }
1207 else
1208 {
1209 if (_fp)
1210 _fp->s_from_p_T(p, T, s, ds_dp, ds_dT);
1211 else
1212 NeedTabulationOrFPError("s_from_p_T with derivatives", "entropy");
1213 }
1214}
1215
1216Real
1218{
1220 {
1221 const Real p = _p_from_v_h_ipol->sample(v, h);
1222 const Real T = _T_from_v_h_ipol->sample(v, h);
1223 return e_from_p_T(p, T);
1224 }
1226 {
1227 // Lambda computes h from v and the current_e
1228 auto lambda = [&](Real v, Real current_e, Real & new_h, Real & dh_dv, Real & dh_de)
1229 { h_from_v_e(v, current_e, new_h, dh_dv, dh_de); };
1231 h,
1232 /*e initial guess*/ h - _p_initial_guess * v,
1233 _tolerance,
1234 lambda,
1235 name() + "::e_from_v_h",
1238 .first;
1239 return e;
1240 }
1241 else if (_fp)
1242 return _fp->e_from_v_h(v, h);
1243 else
1244 NeedTabulationOrFPError("e_from_v_h", "internal_energy");
1245}
1246
1247void
1248TabulatedFluidProperties::e_from_v_h(Real v, Real h, Real & e, Real & de_dv, Real & de_dh) const
1249{
1251 {
1252 Real p = 0, dp_dv = 0, dp_dh = 0;
1253 _p_from_v_h_ipol->sampleValueAndDerivatives(v, h, p, dp_dv, dp_dh);
1254 Real T = 0, dT_dv = 0, dT_dh = 0;
1255 _T_from_v_h_ipol->sampleValueAndDerivatives(v, h, T, dT_dv, dT_dh);
1256 Real de_dp, de_dT;
1257 e_from_p_T(p, T, e, de_dp, de_dT);
1258 de_dv = de_dp * dp_dv + de_dT * dT_dv;
1259 de_dh = de_dp * dp_dh + de_dT * dT_dh;
1260 }
1262 {
1263 // Lambda computes h from v and the current_e
1264 auto lambda = [&](Real v, Real current_e, Real & new_h, Real & dh_dv, Real & dh_de)
1265 { h_from_v_e(v, current_e, new_h, dh_dv, dh_de); };
1266 const auto e_data =
1268 h,
1269 /*e initial guess*/ h - _p_initial_guess * v,
1270 _tolerance,
1271 lambda,
1272 name() + "::e_from_v_h",
1275 e = e_data.first;
1276 // Finite difference approximation
1277 const auto e2 = e_from_v_h(v * (1 + TOLERANCE), h);
1278 de_dv = (e2 - e) / (TOLERANCE * v);
1279 de_dh = 1. / e_data.second;
1280 }
1281 else if (_fp)
1282 _fp->e_from_v_h(v, h, e, de_dv, de_dh);
1283 else
1284 NeedTabulationOrFPError("e_from_v_h", "internal_energy");
1285}
1286
1287std::vector<Real>
1289{
1290 if (_fp)
1291 return _fp->henryCoefficients();
1292 else
1293 TabulationNotImplementedError("henryCoefficients");
1294}
1295
1296Real
1298{
1299 if (_fp)
1300 return _fp->vaporPressure(temperature);
1301 else
1302 TabulationNotImplementedError("vaporPressure");
1303}
1304
1305void
1306TabulatedFluidProperties::vaporPressure(Real temperature, Real & psat, Real & dpsat_dT) const
1307{
1308 if (_fp)
1309 _fp->vaporPressure(temperature, psat, dpsat_dT);
1310 else
1311 TabulationNotImplementedError("vaporPressure");
1312}
1313
1314Real
1316{
1317 if (_fp)
1318 return _fp->vaporTemperature(pressure);
1319 else
1320 TabulationNotImplementedError("vaporTemperature");
1321}
1322
1323void
1324TabulatedFluidProperties::vaporTemperature(Real pressure, Real & Tsat, Real & dTsat_dp) const
1325{
1326
1327 if (_fp)
1328 _fp->vaporTemperature(pressure, Tsat, dTsat_dp);
1329 else
1330 TabulationNotImplementedError("vaporTemperature");
1331}
1332
1333Real
1335{
1336
1337 if (_fp)
1338 return _fp->triplePointPressure();
1339 else
1340 TabulationNotImplementedError("triplePointPressure");
1341}
1342
1343Real
1345{
1346
1347 if (_fp)
1348 return _fp->triplePointTemperature();
1349 else
1350 TabulationNotImplementedError("triplePointTemperature");
1351}
1352
1353Real
1355{
1356
1357 if (_fp)
1358 return _fp->criticalPressure();
1359 else
1360 TabulationNotImplementedError("criticalPressure");
1361}
1362
1363Real
1365{
1366
1367 if (_fp)
1368 return _fp->criticalTemperature();
1369 else
1370 TabulationNotImplementedError("criticalTemperature");
1371}
1372
1373Real
1375{
1376 if (_fp)
1377 return _fp->criticalDensity();
1378 else
1379 TabulationNotImplementedError("criticalDensity");
1380}
1381
1382Real
1384{
1386 missingVEInterpolationError(__PRETTY_FUNCTION__);
1388
1390 return _property_ve_ipol[_p_idx]->sample(v, e);
1391 else if (_construct_pT_from_ve)
1392 return _p_from_v_e_ipol->sample(v, e);
1393 else if (_fp)
1394 return _fp->p_from_v_e(v, e);
1395 else
1396 NeedTabulationOrFPError("p_from_v_e", "pressure");
1397}
1398
1399void
1400TabulatedFluidProperties::p_from_v_e(Real v, Real e, Real & p, Real & dp_dv, Real & dp_de) const
1401{
1403 missingVEInterpolationError(__PRETTY_FUNCTION__);
1405
1407 _property_ve_ipol[_p_idx]->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1408 else if (_construct_pT_from_ve)
1409 _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1410 else if (_fp)
1411 _fp->p_from_v_e(v, e, p, dp_dv, dp_de);
1412 else
1413 NeedTabulationOrFPError("p_from_v_e with derivatives", "pressure");
1414}
1415
1416void
1418 const ADReal & v, const ADReal & e, ADReal & p, ADReal & dp_dv, ADReal & dp_de) const
1419{
1421 missingVEInterpolationError(__PRETTY_FUNCTION__);
1422 ADReal vc = v, ec = e;
1423 checkInputVariablesVE(vc, ec);
1424
1426 _property_ve_ipol[_p_idx]->sampleValueAndDerivatives(vc, ec, p, dp_dv, dp_de);
1427 else if (_construct_pT_from_ve)
1428 _p_from_v_e_ipol->sampleValueAndDerivatives(vc, ec, p, dp_dv, dp_de);
1429 else if (_fp)
1430 _fp->p_from_v_e(vc, ec, p, dp_dv, dp_de);
1431 else
1432 NeedTabulationOrFPError("AD p_from_v_e with derivatives", "pressure");
1433}
1434
1435Real
1437{
1439 missingVEInterpolationError(__PRETTY_FUNCTION__);
1441
1443 return _property_ve_ipol[_T_idx]->sample(v, e);
1444 else if (_construct_pT_from_ve)
1445 return _T_from_v_e_ipol->sample(v, e);
1446 else if (_fp)
1447 return _fp->T_from_v_e(v, e);
1448 else
1449 NeedTabulationOrFPError("T_from_v_e", "temperature");
1450}
1451
1452void
1453TabulatedFluidProperties::T_from_v_e(Real v, Real e, Real & T, Real & dT_dv, Real & dT_de) const
1454{
1456 missingVEInterpolationError(__PRETTY_FUNCTION__);
1458
1460 _property_ve_ipol[_T_idx]->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1461 else if (_construct_pT_from_ve)
1462 _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1463 else if (_fp)
1464 _fp->T_from_v_e(v, e, T, dT_dv, dT_de);
1465 else
1466 NeedTabulationOrFPError("T_from_v_e with derivatives", "temperature");
1467}
1468
1469void
1471 const ADReal & v, const ADReal & e, ADReal & T, ADReal & dT_dv, ADReal & dT_de) const
1472{
1474 missingVEInterpolationError(__PRETTY_FUNCTION__);
1475 ADReal vc = v, ec = e;
1476 checkInputVariablesVE(vc, ec);
1477
1479 _property_ve_ipol[_T_idx]->sampleValueAndDerivatives(vc, ec, T, dT_dv, dT_de);
1480 else if (_construct_pT_from_ve)
1481 _T_from_v_e_ipol->sampleValueAndDerivatives(vc, ec, T, dT_dv, dT_de);
1482 else if (_fp)
1483 _fp->T_from_v_e(vc, ec, T, dT_dv, dT_de);
1484 else
1485 NeedTabulationOrFPError("AD T_from_v_e with derivatives", "temperature");
1486}
1487
1488Real
1490{
1492 missingVEInterpolationError(__PRETTY_FUNCTION__);
1494
1496 return _property_ve_ipol[_c_idx]->sample(v, e);
1497 else if (_construct_pT_from_ve)
1498 {
1499 Real p = _p_from_v_e_ipol->sample(v, e);
1500 Real T = _T_from_v_e_ipol->sample(v, e);
1501 return c_from_p_T(p, T);
1502 }
1503 else if (_fp)
1504 return _fp->c_from_v_e(v, e);
1505 else
1506 NeedTabulationOrFPError("c_from_v_e", "c");
1507}
1508
1509void
1510TabulatedFluidProperties::c_from_v_e(Real v, Real e, Real & c, Real & dc_dv, Real & dc_de) const
1511{
1513 missingVEInterpolationError(__PRETTY_FUNCTION__);
1515
1517 _property_ve_ipol[_c_idx]->sampleValueAndDerivatives(v, e, c, dc_dv, dc_de);
1518 else if (_construct_pT_from_ve)
1519 {
1520 Real p, dp_dv, dp_de;
1521 _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1522 Real T, dT_dv, dT_de;
1523 _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1524 Real dc_dp, dc_dT;
1525 c_from_p_T(p, T, c, dc_dp, dc_dT);
1526 dc_dv = dc_dp * dp_dv + dc_dT * dT_dv;
1527 dc_de = dc_dp * dp_de + dc_dT * dT_de;
1528 }
1529 else if (_fp)
1530 _fp->c_from_v_e(v, e, c, dc_dv, dc_de);
1531 else
1532 NeedTabulationOrFPError("c_from_v_e with derivatives", "c");
1533}
1534
1535Real
1537{
1539 missingVEInterpolationError(__PRETTY_FUNCTION__);
1541
1543 return _property_ve_ipol[_cp_idx]->sample(v, e);
1544 else if (_construct_pT_from_ve)
1545 {
1546 Real p = _p_from_v_e_ipol->sample(v, e);
1547 Real T = _T_from_v_e_ipol->sample(v, e);
1548 return cp_from_p_T(p, T);
1549 }
1550 else if (_fp)
1551 return _fp->cp_from_v_e(v, e);
1552 else
1553 NeedTabulationOrFPError("cp_from_v_e", "cp");
1554}
1555
1556void
1557TabulatedFluidProperties::cp_from_v_e(Real v, Real e, Real & cp, Real & dcp_dv, Real & dcp_de) const
1558{
1560 missingVEInterpolationError(__PRETTY_FUNCTION__);
1562
1564 _property_ve_ipol[_cp_idx]->sampleValueAndDerivatives(v, e, cp, dcp_dv, dcp_de);
1565 else if (_construct_pT_from_ve)
1566 {
1567 Real p, dp_dv, dp_de;
1568 _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1569 Real T, dT_dv, dT_de;
1570 _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1571 Real dcp_dp, dcp_dT;
1572 cp_from_p_T(p, T, cp, dcp_dp, dcp_dT);
1573 dcp_dv = dcp_dp * dp_dv + dcp_dT * dT_dv;
1574 dcp_de = dcp_dp * dp_de + dcp_dT * dT_de;
1575 }
1576 else if (_fp)
1577 _fp->cp_from_v_e(v, e, cp, dcp_dv, dcp_de);
1578 else
1579 NeedTabulationOrFPError("cp_from_v_e with derivatives", "cp");
1580}
1581
1582Real
1584{
1586 missingVEInterpolationError(__PRETTY_FUNCTION__);
1588
1590 return _property_ve_ipol[_cv_idx]->sample(v, e);
1591 else if (_construct_pT_from_ve)
1592 {
1593 Real p = _p_from_v_e_ipol->sample(v, e);
1594 Real T = _T_from_v_e_ipol->sample(v, e);
1595 return cv_from_p_T(p, T);
1596 }
1597 else if (_fp)
1598 return _fp->cv_from_v_e(v, e);
1599 else
1600 NeedTabulationOrFPError("cv_from_v_e", "cv");
1601}
1602
1603void
1604TabulatedFluidProperties::cv_from_v_e(Real v, Real e, Real & cv, Real & dcv_dv, Real & dcv_de) const
1605{
1607 missingVEInterpolationError(__PRETTY_FUNCTION__);
1609
1611 _property_ve_ipol[_cv_idx]->sampleValueAndDerivatives(v, e, cv, dcv_dv, dcv_de);
1612 else if (_construct_pT_from_ve)
1613 {
1614 Real p, dp_dv, dp_de;
1615 _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1616 Real T, dT_dv, dT_de;
1617 _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1618 Real dcv_dp, dcv_dT;
1619 cv_from_p_T(p, T, cv, dcv_dp, dcv_dT);
1620 dcv_dv = dcv_dp * dp_dv + dcv_dT * dT_dv;
1621 dcv_de = dcv_dp * dp_de + dcv_dT * dT_de;
1622 }
1623 else if (_fp)
1624 _fp->cv_from_v_e(v, e, cv, dcv_dv, dcv_de);
1625 else
1626 NeedTabulationOrFPError("cv_from_v_e with derivatives", "cv");
1627}
1628
1629Real
1631{
1633 missingVEInterpolationError(__PRETTY_FUNCTION__);
1635
1637 return _property_ve_ipol[_viscosity_idx]->sample(v, e);
1638 else if (_construct_pT_from_ve)
1639 {
1640 Real p = _p_from_v_e_ipol->sample(v, e);
1641 Real T = _T_from_v_e_ipol->sample(v, e);
1642 return mu_from_p_T(p, T);
1643 }
1644 else if (_fp)
1645 return _fp->mu_from_v_e(v, e);
1646 else
1647 NeedTabulationOrFPError("mu_from_v_e", "viscosity");
1648}
1649
1650void
1651TabulatedFluidProperties::mu_from_v_e(Real v, Real e, Real & mu, Real & dmu_dv, Real & dmu_de) const
1652{
1654 missingVEInterpolationError(__PRETTY_FUNCTION__);
1656
1658 _property_ve_ipol[_viscosity_idx]->sampleValueAndDerivatives(v, e, mu, dmu_dv, dmu_de);
1659 else if (_construct_pT_from_ve)
1660 {
1661 Real p, dp_dv, dp_de;
1662 _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1663 Real T, dT_dv, dT_de;
1664 _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1665 Real dmu_dp, dmu_dT;
1666 mu_from_p_T(p, T, mu, dmu_dp, dmu_dT);
1667 dmu_dv = dmu_dp * dp_dv + dmu_dT * dT_dv;
1668 dmu_de = dmu_dp * dp_de + dmu_dT * dT_de;
1669 }
1670 else if (_fp)
1671 _fp->mu_from_v_e(v, e, mu, dmu_dv, dmu_de);
1672 else
1673 NeedTabulationOrFPError("mu_from_v_e with derivatives", "viscosity");
1674}
1675
1676Real
1678{
1680 missingVEInterpolationError(__PRETTY_FUNCTION__);
1682
1684 return _property_ve_ipol[_k_idx]->sample(v, e);
1685 else if (_construct_pT_from_ve)
1686 {
1687 Real T = _T_from_v_e_ipol->sample(v, e);
1688 Real p = _p_from_v_e_ipol->sample(v, e);
1689 return k_from_p_T(p, T);
1690 }
1691 else if (_fp)
1692 return _fp->k_from_v_e(v, e);
1693 else
1694 NeedTabulationOrFPError("k_from_v_e", "k");
1695}
1696
1697void
1698TabulatedFluidProperties::k_from_v_e(Real v, Real e, Real & k, Real & dk_dv, Real & dk_de) const
1699{
1701 missingVEInterpolationError(__PRETTY_FUNCTION__);
1703
1705 _property_ve_ipol[_k_idx]->sampleValueAndDerivatives(v, e, k, dk_dv, dk_de);
1706 else if (_construct_pT_from_ve)
1707 {
1708 Real p, dp_dv, dp_de;
1709 _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1710 Real T, dT_dv, dT_de;
1711 _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1712 Real dk_dp, dk_dT;
1713 k_from_p_T(p, T, k, dk_dp, dk_dT);
1714 dk_dv = dk_dp * dp_dv + dk_dT * dT_dv;
1715 dk_de = dk_dp * dp_de + dk_dT * dT_de;
1716 }
1717 else if (_fp)
1718 _fp->k_from_v_e(v, e, k, dk_dv, dk_de);
1719 else
1720 NeedTabulationOrFPError("k_from_v_e with derivatives", "k");
1721}
1722
1723Real
1725{
1727 missingVEInterpolationError(__PRETTY_FUNCTION__);
1729
1731 return _property_ve_ipol[_entropy_idx]->sample(v, e);
1732 else if (_construct_pT_from_ve)
1733 {
1734 Real T = _T_from_v_e_ipol->sample(v, e);
1735 Real p = _p_from_v_e_ipol->sample(v, e);
1736 return s_from_p_T(p, T);
1737 }
1738 else if (_fp)
1739 return _fp->s_from_v_e(v, e);
1740 else
1741 NeedTabulationOrFPError("s_from_v_e", "entropy");
1742}
1743
1744void
1745TabulatedFluidProperties::s_from_v_e(Real v, Real e, Real & s, Real & ds_dv, Real & ds_de) const
1746{
1748 missingVEInterpolationError(__PRETTY_FUNCTION__);
1750
1752 _property_ve_ipol[_entropy_idx]->sampleValueAndDerivatives(v, e, s, ds_dv, ds_de);
1753 else if (_construct_pT_from_ve)
1754 {
1755 Real p, T, dT_dv, dT_de, dp_dv, dp_de;
1756 _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1757 _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1758 Real ds_dp, ds_dT;
1759 s_from_p_T(p, T, s, ds_dp, ds_dT);
1760 ds_dv = ds_dp * dp_dv + ds_dT * dT_dv;
1761 ds_de = ds_dp * dp_de + ds_dT * dT_de;
1762 }
1763 else if (_fp)
1764 _fp->s_from_v_e(v, e, s, ds_dv, ds_de);
1765 else
1766 NeedTabulationOrFPError("s_from_v_e", "entropy");
1767}
1768
1769Real
1771{
1772 if (!_construct_pT_from_ve &&
1775 missingVEInterpolationError(__PRETTY_FUNCTION__);
1777
1778 Real h, T = 0, s;
1779 const bool have_direct_ve_tables =
1782 if (have_direct_ve_tables)
1783 {
1784 s = _property_ve_ipol[_entropy_idx]->sample(v, e);
1785 h = _property_ve_ipol[_enthalpy_idx]->sample(v, e);
1786 T = _property_ve_ipol[_T_idx]->sample(v, e);
1787 }
1789 {
1790 Real p0 = _p_initial_guess;
1791 Real T0 = _T_initial_guess;
1792 Real p = 0;
1793 bool conversion_succeeded;
1794 p_T_from_v_e(v, e, p0, T0, p, T, conversion_succeeded);
1795 s = s_from_p_T(p, T);
1796 h = h_from_p_T(p, T);
1797 }
1798 else
1799 mooseError(__PRETTY_FUNCTION__,
1800 "\nNo tabulation or fluid property 'input_fp' object to compute value");
1801 return h - T * s;
1802}
1803
1804Real
1806{
1807 Real p0 = _p_initial_guess;
1808 Real T0 = _T_initial_guess;
1809 Real p, T;
1810 bool conversion_succeeded;
1811 p_T_from_h_s(h, s, p0, T0, p, T, conversion_succeeded);
1812 return T;
1813}
1814
1815Real
1816TabulatedFluidProperties::T_from_p_h(Real pressure, Real enthalpy) const
1817{
1819 {
1820 auto lambda = [&](Real pressure, Real current_T, Real & new_h, Real & dh_dp, Real & dh_dT)
1821 { h_from_p_T(pressure, current_T, new_h, dh_dp, dh_dT); };
1822 Real T = FluidPropertiesUtils::NewtonSolve(pressure,
1823 enthalpy,
1825 _tolerance,
1826 lambda,
1827 name() + "::T_from_p_h",
1830 .first;
1831 // check for nans
1832 if (std::isnan(T))
1833 mooseError("Conversion from enthalpy (h = ",
1834 enthalpy,
1835 ") and pressure (p = ",
1836 pressure,
1837 ") to temperature failed to converge.");
1838 return T;
1839 }
1840 else if (_fp)
1841 return _fp->T_from_p_h(pressure, enthalpy);
1842 else
1843 NeedTabulationOrFPError("T_from_p_h", "temperature");
1844}
1845
1846ADReal
1847TabulatedFluidProperties::T_from_p_h(const ADReal & pressure, const ADReal & enthalpy) const
1848{
1849 using std::isnan;
1851 {
1852 auto lambda =
1853 [&](ADReal pressure, ADReal current_T, ADReal & new_h, ADReal & dh_dp, ADReal & dh_dT)
1854 {
1855 h_from_p_T(pressure.value(), current_T.value(), new_h.value(), dh_dp.value(), dh_dT.value());
1856 // Reconstruct derivatives
1857 new_h.derivatives() =
1858 dh_dp.value() * pressure.derivatives() + dh_dT.value() * current_T.derivatives();
1859 };
1861 enthalpy,
1863 _tolerance,
1864 lambda,
1865 name() + "::T_from_p_h",
1868 .first;
1869 // check for nans
1870 if (isnan(T))
1871 mooseError("Conversion from enthalpy (h = ",
1872 enthalpy,
1873 ") and pressure (p = ",
1874 pressure,
1875 ") to temperature failed to converge.");
1876 return T;
1877 }
1878 else if (_fp)
1879 return _fp->T_from_p_h(pressure, enthalpy);
1880 else
1881 NeedTabulationOrFPError("T_from_p_h", "temperature");
1882}
1883
1884Real
1885TabulatedFluidProperties::s_from_h_p(Real enthalpy, Real pressure) const
1886{
1887 Real T = T_from_p_h(pressure, enthalpy);
1888 return s_from_p_T(pressure, T);
1889}
1890
1891void
1893 Real h, Real pressure, Real & s, Real & ds_dh, Real & ds_dp) const
1894{
1895 if (_fp)
1896 _fp->s_from_h_p(h, pressure, s, ds_dh, ds_dp);
1897 else
1898 TabulationNotImplementedError("s_from_h_p with derivatives");
1899}
1900
1901[[noreturn]] void
1902TabulatedFluidProperties::TabulationNotImplementedError(const std::string & desired_routine) const
1903{
1904 mooseError("TabulatedFluidProperties can only call the function '" + desired_routine +
1905 "' when the 'input_fp' parameter is provided. It is currently not implemented using "
1906 "tabulations, and this property is simply forwarded to the FluidProperties specified "
1907 "in the 'input_fp' parameter");
1908}
1909
1910[[noreturn]] void
1911TabulatedFluidProperties::NeedTabulationOrFPError(const std::string & desired_routine,
1912 const std::string & needed_property) const
1913{
1914 mooseError(
1915 "TabulatedFluidProperties can only call the function '" + desired_routine +
1916 "' when either:\n- the property '" + needed_property +
1917 "' is tabulated and listed in the 'interpolated_properties' parameter.\n- the 'input_fp' "
1918 "parameter is provided.");
1919}
1920
1921[[noreturn]] void
1922TabulatedFluidProperties::NeedTabulationError(const std::string & needed_property) const
1923{
1924 mooseError("Property '" + needed_property +
1925 "' is marked as interpolated but neither of the following methods are active:\n- "
1926 "(p,T) interpolation created from fluid properties ('create_pT_interpolations' "
1927 "parameter)\n- a (p,T) tabulation file ('fluid_property_file')\n- (v,e) interpolation "
1928 "created from fluid properties or a (p,T) tabulation ('create_ve_interpolations' "
1929 "parameter)\n- a (v,e) tabulation file ('fluid_property_ve_file' parameter)");
1930}
1931
1932void
1934{
1935 if (processor_id() == 0)
1936 {
1937 // Write out the (p, T) interpolation tables
1938 if (_file_name_out != "")
1939 {
1940 file_name = file_name.empty() ? "fluid_properties_" + name() + "_out.csv" : file_name;
1942
1943 std::ofstream file_out(file_name.c_str());
1944
1945 if (!getParam<bool>("skip_header_tabulation"))
1946 {
1947 // Write out date and fluid type
1948 time_t now = std::time(&now);
1949 file_out << "# " << (_fp ? _fp->fluidName() : "")
1950 << " properties (p,T) tabulation created by TabulatedFluidProperties on "
1951 << ctime(&now) << "\n";
1952 }
1953
1954 // Write out column names
1955 file_out << "pressure, temperature";
1956 for (std::size_t i = 0; i < _interpolated_properties.size(); ++i)
1957 if (_interpolated_properties[i] != "pressure" &&
1958 _interpolated_properties[i] != "temperature")
1959 file_out << ", " << _interpolated_properties[i];
1960 file_out << "\n";
1961
1962 // Write out the fluid property data
1963 for (unsigned int p = 0; p < _num_p; ++p)
1964 for (unsigned int t = 0; t < _num_T; ++t)
1965 {
1966 file_out << _pressure[p] << ", " << _temperature[t];
1967 for (std::size_t i = 0; i < _properties.size(); ++i)
1968 file_out << ", " << _properties[i][p * _num_T + t];
1969 file_out << "\n";
1970 }
1971
1972 file_out << std::flush;
1973 file_out.close();
1974 }
1975
1976 // Write out the (v,e) interpolation tables
1978 {
1979 const auto file_name_ve = (_file_name_ve_out == "")
1980 ? std::regex_replace(file_name, std::regex("\\.csv"), "_ve.csv")
1982 MooseUtils::checkFileWriteable(file_name_ve);
1983 std::ofstream file_out(file_name_ve.c_str());
1984
1985 // Write out date and fluid type
1986 if (!getParam<bool>("skip_header_tabulation"))
1987 {
1988 time_t now = std::time(&now);
1989 file_out << "# " << (_fp ? _fp->fluidName() : "")
1990 << " properties (v,e) tabulation created by TabulatedFluidProperties on "
1991 << ctime(&now) << "\n";
1992 }
1993
1994 // Write out column names
1995 file_out << "specific_volume, internal_energy, pressure, temperature";
1996 for (const auto i : index_range(_interpolated_properties))
1997 // Avoid writing the fixed columns twice
1998 if (_interpolated_properties[i] != "internal_energy" &&
1999 _interpolated_properties[i] != "pressure" &&
2000 _interpolated_properties[i] != "temperature")
2001 file_out << ", " << _interpolated_properties[i];
2002 file_out << "\n";
2003
2004 // Write out the fluid property data
2005 for (const auto v : make_range(_num_v))
2006 for (const auto e : make_range(_num_e))
2007 {
2008 const auto v_val = _specific_volume[v];
2009 const auto e_val = _internal_energy[e];
2010 // Use the expected source for the grid. Note that the tabulations are already created
2011 Real pressure, temperature;
2013 {
2014 pressure = _p_from_v_e_ipol->sample(v_val, e_val);
2015 temperature = _T_from_v_e_ipol->sample(v_val, e_val);
2016 }
2017 else
2018 {
2019 pressure = p_from_v_e(v_val, e_val);
2020 temperature = T_from_v_e(v_val, e_val);
2021 }
2022 file_out << v_val << ", " << e_val << ", " << pressure << ", " << temperature
2023 << (_interpolated_properties.size() ? ", " : "");
2024 for (const auto i : index_range(_interpolated_properties))
2025 {
2026 bool add_comma = true;
2027 if (i == _density_idx)
2028 file_out << 1. / v_val;
2029 else if (i == _enthalpy_idx)
2030 file_out << h_from_v_e(v_val, e_val);
2031 // Note that we could use (p,T) routine to generate this instead of (v,e)
2032 // Or could use the _properties_ve array similar to what we do for (pressure,
2033 // temperature)
2034 else if (i == _viscosity_idx)
2035 file_out << mu_from_v_e(v_val, e_val);
2036 else if (i == _k_idx)
2037 file_out << k_from_v_e(v_val, e_val);
2038 else if (i == _c_idx)
2039 file_out << c_from_v_e(v_val, e_val);
2040 else if (i == _cv_idx)
2041 file_out << cv_from_v_e(v_val, e_val);
2042 else if (i == _cp_idx)
2043 file_out << cp_from_v_e(v_val, e_val);
2044 else if (i == _entropy_idx)
2045 file_out << s_from_v_e(v_val, e_val);
2046 else
2047 add_comma = false;
2048 if (i != _interpolated_properties.size() - 1 && add_comma)
2049 file_out << ", ";
2050 }
2051
2052 file_out << "\n";
2053 }
2054
2055 file_out << std::flush;
2056 file_out.close();
2057 }
2058 }
2059}
2060
2061void
2063{
2064 mooseAssert(_fp, "We should not try to generate (p,T) tabulated data without a _fp user object");
2065 _pressure.resize(_num_p);
2066 _temperature.resize(_num_T);
2067
2068 // Generate data for all properties entered in input file
2071
2072 for (std::size_t i = 0; i < _interpolated_properties_enum.size(); ++i)
2074
2075 for (const auto i : index_range(_properties))
2076 _properties[i].resize(_num_p * _num_T);
2077
2078 // Temperature is divided equally into _num_T segments
2079 Real delta_T = (_temperature_max - _temperature_min) / static_cast<Real>(_num_T - 1);
2080
2081 for (unsigned int j = 0; j < _num_T; ++j)
2082 _temperature[j] = _temperature_min + j * delta_T;
2083
2084 // Divide the pressure into _num_p equal segments
2085 Real delta_p = (_pressure_max - _pressure_min) / static_cast<Real>(_num_p - 1);
2086
2087 for (unsigned int i = 0; i < _num_p; ++i)
2088 _pressure[i] = _pressure_min + i * delta_p;
2089
2090 // Generate the tabulated data at the pressure and temperature points
2091 for (const auto i : index_range(_properties))
2092 {
2093 if (_interpolated_properties[i] == "density")
2094 for (unsigned int p = 0; p < _num_p; ++p)
2095 for (unsigned int t = 0; t < _num_T; ++t)
2096 _properties[i][p * _num_T + t] = _fp->rho_from_p_T(_pressure[p], _temperature[t]);
2097
2098 if (_interpolated_properties[i] == "enthalpy")
2099 for (unsigned int p = 0; p < _num_p; ++p)
2100 for (unsigned int t = 0; t < _num_T; ++t)
2101 _properties[i][p * _num_T + t] = _fp->h_from_p_T(_pressure[p], _temperature[t]);
2102
2103 if (_interpolated_properties[i] == "internal_energy")
2104 for (unsigned int p = 0; p < _num_p; ++p)
2105 for (unsigned int t = 0; t < _num_T; ++t)
2106 _properties[i][p * _num_T + t] = _fp->e_from_p_T(_pressure[p], _temperature[t]);
2107
2108 if (_interpolated_properties[i] == "viscosity")
2109 for (unsigned int p = 0; p < _num_p; ++p)
2110 for (unsigned int t = 0; t < _num_T; ++t)
2111 _properties[i][p * _num_T + t] = _fp->mu_from_p_T(_pressure[p], _temperature[t]);
2112
2113 if (_interpolated_properties[i] == "k")
2114 for (unsigned int p = 0; p < _num_p; ++p)
2115 for (unsigned int t = 0; t < _num_T; ++t)
2116 _properties[i][p * _num_T + t] = _fp->k_from_p_T(_pressure[p], _temperature[t]);
2117
2118 if (_interpolated_properties[i] == "c")
2119 for (unsigned int p = 0; p < _num_p; ++p)
2120 for (unsigned int t = 0; t < _num_T; ++t)
2121 _properties[i][p * _num_T + t] = _fp->c_from_p_T(_pressure[p], _temperature[t]);
2122
2123 if (_interpolated_properties[i] == "cv")
2124 for (unsigned int p = 0; p < _num_p; ++p)
2125 for (unsigned int t = 0; t < _num_T; ++t)
2126 _properties[i][p * _num_T + t] = _fp->cv_from_p_T(_pressure[p], _temperature[t]);
2127
2128 if (_interpolated_properties[i] == "cp")
2129 for (unsigned int p = 0; p < _num_p; ++p)
2130 for (unsigned int t = 0; t < _num_T; ++t)
2131 _properties[i][p * _num_T + t] = _fp->cp_from_p_T(_pressure[p], _temperature[t]);
2132
2133 if (_interpolated_properties[i] == "entropy")
2134 for (unsigned int p = 0; p < _num_p; ++p)
2135 for (unsigned int t = 0; t < _num_T; ++t)
2136 _properties[i][p * _num_T + t] = _fp->s_from_p_T(_pressure[p], _temperature[t]);
2137 }
2138}
2139
2140void
2142{
2143 mooseAssert(_fp, "We should not try to generate (v,e) tabulated data without a _fp user object");
2144 _specific_volume.resize(_num_v);
2145 _internal_energy.resize(_num_e);
2146
2147 // Generate data for all properties entered in input file
2150
2151 // This is filled from the user input, so it does not matter than this operation is performed
2152 // for both the (p,T) and (v,e) tabulated data generation
2153 for (std::size_t i = 0; i < _interpolated_properties_enum.size(); ++i)
2155
2156 for (const auto i : index_range(_properties_ve))
2157 _properties_ve[i].resize(_num_v * _num_e);
2158
2159 // Grids in (v,e) are not read, so we either use user input or rely on (p,T) data
2161
2162 // Generate the tabulated data at the pressure and temperature points
2163 for (const auto i : index_range(_properties_ve))
2164 {
2165 if (_interpolated_properties[i] == "density")
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] = 1. / _specific_volume[v];
2169
2170 if (_interpolated_properties[i] == "enthalpy")
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->h_from_v_e(_specific_volume[v], _internal_energy[e]);
2175
2176 if (_interpolated_properties[i] == "internal_energy")
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] = _internal_energy[e];
2180
2181 if (_interpolated_properties[i] == "viscosity")
2182 for (unsigned int v = 0; v < _num_v; ++v)
2183 for (unsigned int e = 0; e < _num_e; ++e)
2184 _properties_ve[i][v * _num_e + e] =
2185 _fp->mu_from_v_e(_specific_volume[v], _internal_energy[e]);
2186
2187 if (_interpolated_properties[i] == "k")
2188 for (unsigned int v = 0; v < _num_v; ++v)
2189 for (unsigned int e = 0; e < _num_e; ++e)
2190 _properties_ve[i][v * _num_e + e] =
2191 _fp->k_from_v_e(_specific_volume[v], _internal_energy[e]);
2192
2193 if (_interpolated_properties[i] == "c")
2194 for (unsigned int v = 0; v < _num_v; ++v)
2195 for (unsigned int e = 0; e < _num_e; ++e)
2196 _properties_ve[i][v * _num_e + e] =
2197 _fp->c_from_v_e(_specific_volume[v], _internal_energy[e]);
2198
2199 if (_interpolated_properties[i] == "cv")
2200 for (unsigned int v = 0; v < _num_v; ++v)
2201 for (unsigned int e = 0; e < _num_e; ++e)
2202 _properties_ve[i][v * _num_e + e] =
2203 _fp->cv_from_v_e(_specific_volume[v], _internal_energy[e]);
2204
2205 if (_interpolated_properties[i] == "cp")
2206 for (unsigned int v = 0; v < _num_v; ++v)
2207 for (unsigned int e = 0; e < _num_e; ++e)
2208 _properties_ve[i][v * _num_e + e] =
2209 _fp->cp_from_v_e(_specific_volume[v], _internal_energy[e]);
2210
2211 if (_interpolated_properties[i] == "entropy")
2212 for (unsigned int v = 0; v < _num_v; ++v)
2213 for (unsigned int e = 0; e < _num_e; ++e)
2214 _properties_ve[i][v * _num_e + e] =
2215 _fp->s_from_v_e(_specific_volume[v], _internal_energy[e]);
2216
2217 if (_interpolated_properties[i] == "pressure")
2218 for (unsigned int v = 0; v < _num_v; ++v)
2219 for (unsigned int e = 0; e < _num_e; ++e)
2220 _properties_ve[i][v * _num_e + e] =
2221 _fp->p_from_v_e(_specific_volume[v], _internal_energy[e]);
2222
2223 if (_interpolated_properties[i] == "temperature")
2224 for (unsigned int v = 0; v < _num_v; ++v)
2225 for (unsigned int e = 0; e < _num_e; ++e)
2226 _properties_ve[i][v * _num_e + e] =
2227 _fp->T_from_v_e(_specific_volume[v], _internal_energy[e]);
2228 }
2229}
2230
2231template <typename T>
2232void
2233TabulatedFluidProperties::checkInputVariables(T & pressure, T & temperature) const
2234{
2235 using std::max, std::min;
2236
2237 if (_OOBBehavior == Ignore)
2238 return;
2239 else if (MooseUtils::absoluteFuzzyGreaterThan(_pressure_min, pressure, libMesh::TOLERANCE) ||
2240 MooseUtils::absoluteFuzzyGreaterThan(pressure, _pressure_max, libMesh::TOLERANCE))
2241 {
2242 if (_OOBBehavior == Throw)
2243 throw MooseException("Pressure " + Moose::stringify(pressure) +
2244 " is outside the range of tabulated pressure (" +
2247
2248 else
2249 {
2250 pressure = max(_pressure_min, min(pressure, _pressure_max));
2252 flagInvalidSolution("Pressure out of bounds");
2253 else if (_OOBBehavior == WarnInvalid)
2254 flagSolutionWarning("Pressure out of bounds");
2255 }
2256 }
2257
2258 if (MooseUtils::absoluteFuzzyGreaterThan(_temperature_min, temperature, libMesh::TOLERANCE) ||
2259 MooseUtils::absoluteFuzzyGreaterThan(temperature, _temperature_max, libMesh::TOLERANCE))
2260 {
2261 if (_OOBBehavior == Throw)
2262 throw MooseException("Temperature " + Moose::stringify(temperature) +
2263 " is outside the range of tabulated temperature (" +
2266 else
2267 {
2268 temperature = max(T(_temperature_min), min(temperature, T(_temperature_max)));
2270 flagInvalidSolution("Temperature out of bounds");
2271 else if (_OOBBehavior == WarnInvalid)
2272 flagSolutionWarning("Temperature out of bounds");
2273 }
2274 }
2275}
2276
2277template <typename T>
2278void
2280{
2281 using std::max, std::min;
2282
2283 if (_OOBBehavior == Ignore)
2284 return;
2285 else if (e < _e_min || e > _e_max)
2286 {
2287 if (_OOBBehavior == Throw)
2288 throw MooseException("Specific internal energy " + Moose::stringify(e) +
2289 " is outside the range of tabulated specific internal energies (" +
2291 else
2292 {
2293 e = max(_e_min, min(e, _e_max));
2295 flagInvalidSolution("Specific internal energy out of bounds");
2296 else if (_OOBBehavior == WarnInvalid)
2297 flagSolutionWarning("Specific internal energy out of bounds");
2298 }
2299 }
2300
2301 if (v < _v_min || v > _v_max)
2302 {
2303 if (_OOBBehavior == Throw)
2304 throw MooseException("Specific volume " + Moose::stringify(v) +
2305 " is outside the range of tabulated specific volumes (" +
2307 else
2308 {
2309 v = max(T(_v_min), min(v, T(_v_max)));
2311 flagInvalidSolution("Specific volume out of bounds");
2312 else if (_OOBBehavior == WarnInvalid)
2313 flagSolutionWarning("Specific volume out of bounds");
2314 }
2315 }
2316}
2317
2318void
2319TabulatedFluidProperties::checkInitialGuess(bool post_reading_tabulation) const
2320{
2321 // First condition applies when generating a tabulation
2322 // Second condition applies when using a pre-generated loaded tabulation
2323 if ((!post_reading_tabulation && _fp && (_construct_pT_from_ve || _construct_pT_from_vh)) ||
2324 (post_reading_tabulation && (_file_name_in != "" || _file_name_ve_in != "") &&
2325 (_create_direct_ve_interpolations || isParamSetByUser("T_initial_guess") ||
2326 isParamSetByUser("p_initial_guess"))))
2327 {
2328 if (_p_initial_guess < _pressure_min || _p_initial_guess > _pressure_max)
2329 mooseWarning("Pressure initial guess for (p,T), (v,e) conversions " +
2331 " is outside the range of tabulated "
2332 "pressure (" +
2334
2335 if (_T_initial_guess < _temperature_min || _T_initial_guess > _temperature_max)
2336 mooseWarning("Temperature initial guess for (p,T), (v,e) conversions " +
2338 " is outside the range of tabulated "
2339 "temperature (" +
2341 ").");
2342 }
2343}
2344
2345void
2347{
2348 std::string file_name;
2349 if (use_pT)
2350 {
2351 _console << name() + ": Reading tabulated properties from " << _file_name_in << std::endl;
2352 _csv_reader.read();
2353 file_name = _file_name_in;
2354 }
2355 else
2356 {
2357 _console << name() + ": Reading tabulated properties from " << _file_name_ve_in << std::endl;
2359 _csv_reader.read();
2360 file_name = _file_name_ve_in;
2361 }
2362
2363 const std::vector<std::string> & column_names = _csv_reader.getNames();
2364
2365 // These columns form the grid and must be present in the file
2366 std::vector<std::string> required_columns;
2367 if (use_pT)
2368 required_columns = {"pressure", "temperature"};
2369 else
2370 required_columns = {"specific_volume", "internal_energy"};
2371
2372 // Check that all required columns are present
2373 for (std::size_t i = 0; i < required_columns.size(); ++i)
2374 {
2375 if (std::find(column_names.begin(), column_names.end(), required_columns[i]) ==
2376 column_names.end())
2377 mooseError("No ",
2378 required_columns[i],
2379 " data read in ",
2380 file_name,
2381 ". A column named ",
2382 required_columns[i],
2383 " must be present");
2384 }
2385
2386 // These columns can be present in the file
2387 std::vector<std::string> property_columns = {
2388 "density", "enthalpy", "viscosity", "k", "c", "cv", "cp", "entropy"};
2389 if (use_pT)
2390 property_columns.push_back("internal_energy");
2391 else
2392 {
2393 property_columns.push_back("pressure");
2394 property_columns.push_back("temperature");
2395 }
2396
2397 // Check that any property names read from the file are present in the list of possible
2398 // properties, and if they are, add them to the list of read properties
2399 for (std::size_t i = 0; i < column_names.size(); ++i)
2400 {
2401 // Only check properties not in _required_columns
2402 if (std::find(required_columns.begin(), required_columns.end(), column_names[i]) ==
2403 required_columns.end())
2404 {
2405 if (std::find(property_columns.begin(), property_columns.end(), column_names[i]) ==
2406 property_columns.end())
2407 mooseWarning(column_names[i],
2408 " read in ",
2409 file_name,
2410 " tabulation file is not one of the properties that TabulatedFluidProperties "
2411 "understands. It will be ignored.");
2412 // We could be reading a (v,e) tabulation after having read a (p,T) tabulation, do not
2413 // insert twice
2414 // Also only allow properties specified as interpolated if user passed the parameter
2415 else if (std::find(_interpolated_properties.begin(),
2417 column_names[i]) == _interpolated_properties.end() &&
2418 (!isParamSetByUser("interpolated_properties") ||
2419 _interpolated_properties_enum.contains(column_names[i])))
2420 _interpolated_properties.push_back(column_names[i]);
2421 }
2422 }
2423
2424 std::map<std::string, unsigned int> data_index;
2425 for (std::size_t i = 0; i < column_names.size(); ++i)
2426 {
2427 auto it = std::find(column_names.begin(), column_names.end(), column_names[i]);
2428 data_index[column_names[i]] = std::distance(column_names.begin(), it);
2429 }
2430
2431 const std::vector<std::vector<Real>> & column_data = _csv_reader.getData();
2432
2433 // Extract the pressure and temperature data vectors
2434 if (use_pT)
2435 {
2436 _pressure = column_data[data_index.find("pressure")->second];
2437 _temperature = column_data[data_index.find("temperature")->second];
2438 }
2439 else
2440 {
2441 _specific_volume = column_data[data_index.find("specific_volume")->second];
2442 _internal_energy = column_data[data_index.find("internal_energy")->second];
2443 }
2444
2445 if (use_pT)
2446 checkFileTabulationGrids(_pressure, _temperature, file_name, "pressure", "temperature");
2447 else
2450 file_name,
2451 "specific volume",
2452 "specific internal energy");
2453
2454 if (use_pT)
2455 {
2456 _num_p = _pressure.size();
2457 _num_T = _temperature.size();
2458
2459 // Minimum and maximum pressure and temperature. Note that _pressure and
2460 // _temperature are sorted
2461 _pressure_min = _pressure.front();
2462 _pressure_max = _pressure.back();
2465 checkInitialGuess(true);
2466
2467 // Extract the fluid property data from the file
2468 for (std::size_t i = 0; i < _interpolated_properties.size(); ++i)
2469 _properties.push_back(column_data[data_index.find(_interpolated_properties[i])->second]);
2470 }
2471 else
2472 {
2473 _num_v = _specific_volume.size();
2474 _num_e = _internal_energy.size();
2475
2476 // Minimum and maximum specific internal energy and specific volume
2477 _v_min = _specific_volume.front();
2478 _v_max = _specific_volume.back();
2479 _e_min = _internal_energy.front();
2480 _e_max = _internal_energy.back();
2481
2482 // We cannot overwrite the tabulated data grid with a grid generated from user-input for the
2483 // purpose of creating (p,T) to (v,e) interpolations
2485 paramError("construct_pT_from_ve",
2486 "Reading a (v,e) tabulation and generating (p,T) to (v,e) interpolation tables is "
2487 "not supported at this time.");
2488
2489 // Make sure we use the tabulation bounds
2490 _e_bounds_specified = true;
2491 _v_bounds_specified = true;
2492
2493 // Extract the fluid property data from the file
2495 for (std::size_t i = 0; i < _interpolated_properties.size(); ++i)
2496 _properties_ve.push_back(column_data[data_index.find(_interpolated_properties[i])->second]);
2497
2498 // Obtain the min/max T, p and check the initial guess
2499 const auto & p_col = column_data[data_index.find("pressure")->second];
2500 _pressure_min = *std::min_element(p_col.begin(), p_col.end());
2501 _pressure_max = *std::max_element(p_col.begin(), p_col.end());
2502 const auto & T_col = column_data[data_index.find("temperature")->second];
2503 _temperature_min = *std::min_element(T_col.begin(), T_col.end());
2504 _temperature_max = *std::max_element(T_col.begin(), T_col.end());
2505 checkInitialGuess(true);
2506 }
2507}
2508
2509void
2511 std::vector<Real> & v2,
2512 const std::string & file_name,
2513 const std::string & v1_name,
2514 const std::string & v2_name)
2515{
2516 // NOTE: We kept the comments in terms of pressure & temperature for clarity
2517 // Pressure (v1) and temperature (v2) data contains duplicates due to the csv format.
2518 // First, check that pressure (v1) is monotonically increasing
2519 if (!std::is_sorted(v1.begin(), v1.end()))
2520 mooseError("The column data for ", v1_name, " is not monotonically increasing in ", file_name);
2521
2522 // The first pressure (v1) value is repeated for each temperature (v2) value. Counting the
2523 // number of repeats provides the number of temperature (v2) values
2524 auto num_v2 = std::count(v1.begin(), v1.end(), v1.front());
2525
2526 // Now remove the duplicates in the pressure (v1) vector
2527 auto last_unique = std::unique(v1.begin(), v1.end());
2528 v1.erase(last_unique, v1.end());
2529
2530 // Check that the number of rows in the csv file is equal to _num_v1 * _num_v2
2531 // v2 is currently the same size as the column_data (will get trimmed at the end)
2532 if (v2.size() != v1.size() * libMesh::cast_int<unsigned int>(num_v2))
2533 mooseError("The number of rows in ",
2534 file_name,
2535 " is not equal to the number of unique ",
2536 v1_name,
2537 " values ",
2538 v1.size(),
2539 " multiplied by the number of unique ",
2540 v2_name,
2541 " values ",
2542 num_v2);
2543
2544 // Need to make sure that the temperature (v2) values are provided in ascending order
2545 std::vector<Real> base_v2(v2.begin(), v2.begin() + num_v2);
2546 if (!std::is_sorted(base_v2.begin(), base_v2.end()))
2547 mooseError("The column data for ", v2_name, " is not monotonically increasing in ", file_name);
2548
2549 // Need to make sure that the temperature (v2) are repeated for each pressure (v1) grid point
2550 auto it_v2 = v2.begin() + num_v2;
2551 for (const auto i : make_range(v1.size() - 1))
2552 {
2553 std::vector<Real> repeated_v2(it_v2, it_v2 + num_v2);
2554 if (repeated_v2 != base_v2)
2555 mooseError(v2_name,
2556 " values for ",
2557 v1_name,
2558 " ",
2559 v1[i + 1],
2560 " are not identical to values for ",
2561 v1[0]);
2562
2563 std::advance(it_v2, num_v2);
2564 }
2565
2566 // At this point, all temperature (v2) data has been provided in ascending order
2567 // identically for each pressure (v1) value, so we can just keep the first range
2568 v2.erase(v2.begin() + num_v2, v2.end());
2569}
2570
2571void
2573{
2574 // At this point, all properties read or generated are able to be used by
2575 // TabulatedFluidProperties. Now set flags and indexes for each property in
2576 //_interpolated_properties to use in property calculations
2577 for (std::size_t i = 0; i < _interpolated_properties.size(); ++i)
2578 {
2579 if (_interpolated_properties[i] == "density")
2580 {
2581 _interpolate_density = true;
2582 _density_idx = i;
2583 }
2584 else if (_interpolated_properties[i] == "enthalpy")
2585 {
2586 _interpolate_enthalpy = true;
2587 _enthalpy_idx = i;
2588 }
2589 else if (_interpolated_properties[i] == "internal_energy")
2590 {
2593 }
2594 else if (_interpolated_properties[i] == "viscosity")
2595 {
2597 _viscosity_idx = i;
2598 }
2599 else if (_interpolated_properties[i] == "k")
2600 {
2601 _interpolate_k = true;
2602 _k_idx = i;
2603 }
2604 else if (_interpolated_properties[i] == "c")
2605 {
2606 _interpolate_c = true;
2607 _c_idx = i;
2608 }
2609 else if (_interpolated_properties[i] == "cp")
2610 {
2611 _interpolate_cp = true;
2612 _cp_idx = i;
2613 }
2614 else if (_interpolated_properties[i] == "cv")
2615 {
2616 _interpolate_cv = true;
2617 _cv_idx = i;
2618 }
2619 else if (_interpolated_properties[i] == "entropy")
2620 {
2621 _interpolate_entropy = true;
2622 _entropy_idx = i;
2623 }
2624 else if (_interpolated_properties[i] == "pressure")
2625 {
2626 _interpolate_pressure = true;
2627 _p_idx = i;
2628 }
2629 else if (_interpolated_properties[i] == "temperature")
2630 {
2632 _T_idx = i;
2633 }
2634 else
2635 mooseError("Specified property '" + _interpolated_properties[i] +
2636 "' is present in the tabulation but is not currently leveraged by the code in the "
2637 "TabulatedFluidProperties. If it is spelled correctly, then please contact a "
2638 "MOOSE or fluid properties module developer.");
2639 }
2640}
2641
2642void
2644{
2645 mooseAssert(_file_name_ve_in.empty(), "We should be reading the specific volume grid from file");
2647 {
2648 // if csv exists, get max and min values from csv file
2650 {
2651 Real rho_max =
2652 *std::max_element(_properties[_density_idx].begin(), _properties[_density_idx].end());
2653 Real rho_min =
2654 *std::min_element(_properties[_density_idx].begin(), _properties[_density_idx].end());
2655 _v_max = 1 / rho_min;
2656 _v_min = 1 / rho_max;
2657 }
2658 else if (_fp)
2659 {
2660 // extreme values of specific volume for the grid bounds
2661 Real v1 = _fp->v_from_p_T(_pressure_min, _temperature_min);
2662 Real v2 = _fp->v_from_p_T(_pressure_max, _temperature_min);
2663 Real v3 = _fp->v_from_p_T(_pressure_min, _temperature_max);
2664 Real v4 = _fp->v_from_p_T(_pressure_max, _temperature_max);
2665 _v_min = std::min({v1, v2, v3, v4});
2666 _v_max = std::max({v1, v2, v3, v4});
2667 }
2668 else
2669 mooseWarning("Unable to compute grid bounds in specific volume. Please specify the v_min/max "
2670 "parameters");
2671
2672 // Prevent changing the bounds of the grid
2673 _v_bounds_specified = true;
2674 }
2675
2676 // Create v grid for interpolation
2677 _specific_volume.resize(_num_v);
2678 if (_log_space_v)
2679 {
2680 // incrementing the exponent linearly will yield a log-spaced grid after taking the value to
2681 // the power of 10
2682 Real dv = (std::log10(_v_max) - std::log10(_v_min)) / ((Real)_num_v - 1);
2683 Real log_v_min = std::log10(_v_min);
2684 for (unsigned int j = 0; j < _num_v; ++j)
2685 _specific_volume[j] = std::pow(10, log_v_min + j * dv);
2686 }
2687 else
2688 {
2689 Real dv = (_v_max - _v_min) / ((Real)_num_v - 1);
2690 for (unsigned int j = 0; j < _num_v; ++j)
2691 _specific_volume[j] = _v_min + j * dv;
2692 }
2693}
2694
2695void
2697{
2700 {
2701 // if csv exists, get max and min values from csv file
2703 {
2704 _e_min = *std::min_element(_properties[_internal_energy_idx].begin(),
2706 _e_max = *std::max_element(_properties[_internal_energy_idx].begin(),
2708 }
2709 else if (_fp)
2710 {
2711 // extreme values of internal energy for the grid bounds
2712 Real e1 = _fp->e_from_p_T(_pressure_min, _temperature_min);
2713 Real e2 = _fp->e_from_p_T(_pressure_max, _temperature_min);
2714 Real e3 = _fp->e_from_p_T(_pressure_min, _temperature_max);
2715 Real e4 = _fp->e_from_p_T(_pressure_max, _temperature_max);
2716 _e_min = std::min({e1, e2, e3, e4});
2717 _e_max = std::max({e1, e2, e3, e4});
2718 }
2719 else
2720 mooseWarning("Unable to compute grid bounds in internal energy. Please specify the e_min/max "
2721 "parameters");
2722 }
2723
2724 // Create e grid for interpolation
2725 _internal_energy.resize(_num_e);
2726 if (_log_space_e)
2727 {
2728 // incrementing the exponent linearly will yield a log-spaced grid after taking the value to
2729 // the power of 10
2730 if (_e_min < 0)
2731 mooseError("Logarithmic grid in specific energy can only be used with a positive specific "
2732 "energy. Current minimum: " +
2733 std::to_string(_e_min));
2734 Real de = (std::log10(_e_max) - std::log10(_e_min)) / ((Real)_num_e - 1);
2735 Real log_e_min = std::log10(_e_min);
2736 for (const auto j : make_range(_num_e))
2737 _internal_energy[j] = std::pow(10, log_e_min + j * de);
2738 }
2739 else
2740 {
2741 Real de = (_e_max - _e_min) / ((Real)_num_e - 1);
2742 for (const auto j : make_range(_num_e))
2743 _internal_energy[j] = _e_min + j * de;
2744 }
2745}
2746
2747void
2749{
2750 if (_file_name_ve_in.empty())
2752 if (_fp)
2753 {
2754 // extreme values of enthalpy for the grid bounds
2755 Real h1 = _fp->h_from_p_T(_pressure_min, _temperature_min);
2756 Real h2 = _fp->h_from_p_T(_pressure_max, _temperature_min);
2757 Real h3 = _fp->h_from_p_T(_pressure_min, _temperature_max);
2758 Real h4 = _fp->h_from_p_T(_pressure_max, _temperature_max);
2759 _h_min = std::min({h1, h2, h3, h4});
2760 _h_max = std::max({h1, h2, h3, h4});
2761 }
2762 // if csv exists, get max and min values from csv file
2763 else if (_properties.size())
2764 {
2765 _h_max = *max_element(_properties[_enthalpy_idx].begin(), _properties[_enthalpy_idx].end());
2766 _h_min = *min_element(_properties[_enthalpy_idx].begin(), _properties[_enthalpy_idx].end());
2767 }
2768 else if (_properties_ve.size())
2769 {
2770 _h_max = *max_element(_properties[_enthalpy_idx].begin(), _properties[_enthalpy_idx].end());
2771 _h_min = *min_element(_properties[_enthalpy_idx].begin(), _properties[_enthalpy_idx].end());
2772 }
2773 else
2774 mooseError("Need a source to compute the enthalpy grid bounds: either a FP object, or a (p,T) "
2775 "tabulation file or a (v,e) tabulation file");
2776
2777 // Create h grid for interpolation
2778 // enthalpy & internal energy use same # grid points
2779 _enthalpy.resize(_num_e);
2780 if (_log_space_h)
2781 {
2782 // incrementing the exponent linearly will yield a log-spaced grid after taking the value to
2783 // the power of 10
2784 if (_h_min < 0)
2785 mooseError("Logarithmic grid in specific energy can only be used with a positive enthalpy. "
2786 "Current minimum: " +
2787 std::to_string(_h_min));
2788 Real dh = (std::log10(_h_max) - std::log10(_h_min)) / ((Real)_num_e - 1);
2789 Real log_h_min = std::log10(_h_min);
2790 for (const auto j : make_range(_num_e))
2791 _enthalpy[j] = std::pow(10, log_h_min + j * dh);
2792 }
2793 else
2794 {
2795 Real dh = (_h_max - _h_min) / ((Real)_num_e - 1);
2796 for (const auto j : make_range(_num_e))
2797 _enthalpy[j] = _h_min + j * dh;
2798 }
2799}
2800
2801void
2802TabulatedFluidProperties::missingVEInterpolationError(const std::string & function_name) const
2803{
2804 mooseError(function_name +
2805 ": to call this function you must:\n-add this property to the list to the list of "
2806 "'interpolated_properties'\n and then either:\n-construct (p, T) from (v, e) "
2807 "tabulations using the 'construct_pT_from_ve' parameter\n-load (v,e) interpolation "
2808 "tables using the 'fluid_property_ve_file' parameter");
2809}
2810
2811template void TabulatedFluidProperties::checkInputVariables(Real & pressure,
2812 Real & temperature) const;
2814 ADReal & temperature) const;
2815template 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