https://mooseframework.inl.gov
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 {
27  params.addClassDescription(
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>("create_ve_interpolations",
37  false,
38  "Whether to load (from file) or create (from a fluid property object) "
39  "properties interpolations from pressure and temperature");
40 
41  // Input / output
42  params.addParam<UserObjectName>("fp", "The name of the FluidProperties UserObject");
43  params.addParam<FileName>("fluid_property_file",
44  "Name of the csv file containing the tabulated fluid property data.");
45  params.addParam<FileName>(
46  "fluid_property_ve_file",
47  "Name of the csv file containing the tabulated (v,e) fluid property data.");
48  params.addParam<FileName>("fluid_property_output_file",
49  "Name of the CSV file which can be output with the tabulation. This "
50  "file can then be read as a 'fluid_property_file'");
51  params.addParam<FileName>(
52  "fluid_property_ve_output_file",
53  "Name of the CSV file which can be output with the (v,e) tabulation. This "
54  "file can then be read as a 'fluid_property_ve_file'");
55  params.addDeprecatedParam<bool>(
56  "save_file",
57  "Whether to save the csv fluid properties file",
58  "This parameter is no longer required. Whether to save a CSV tabulation file is controlled "
59  "by specifying the 'fluid_property_output_file' parameter");
60 
61  // Data source on a per-property basis
62  MultiMooseEnum properties(
63  "density enthalpy internal_energy viscosity k c cv cp entropy pressure temperature",
64  "density enthalpy internal_energy viscosity");
65  params.addParam<MultiMooseEnum>("interpolated_properties",
66  properties,
67  "Properties to interpolate if no data file is provided");
68 
69  // (p,T) grid parameters
70  params.addRangeCheckedParam<Real>(
71  "temperature_min", 300, "temperature_min > 0", "Minimum temperature for tabulated data.");
72  params.addParam<Real>("temperature_max", 500, "Maximum temperature for tabulated data.");
73  params.addRangeCheckedParam<Real>(
74  "pressure_min", 1e5, "pressure_min > 0", "Minimum pressure for tabulated data.");
75  params.addParam<Real>("pressure_max", 50.0e6, "Maximum pressure for tabulated data.");
76  params.addRangeCheckedParam<unsigned int>(
77  "num_T", 100, "num_T > 0", "Number of points to divide temperature range.");
78  params.addRangeCheckedParam<unsigned int>(
79  "num_p", 100, "num_p > 0", "Number of points to divide pressure range.");
80 
81  // (v,e) grid parameters
82  params.addParam<Real>("e_min", "Minimum specific internal energy for tabulated data.");
83  params.addParam<Real>("e_max", "Maximum specific internal energy for tabulated data.");
84  params.addRangeCheckedParam<Real>(
85  "v_min", "v_min > 0", "Minimum specific volume for tabulated data.");
86  params.addRangeCheckedParam<Real>(
87  "v_max", "v_max > 0", "Maximum specific volume for tabulated data.");
88  params.addParam<bool>("construct_pT_from_ve",
89  false,
90  "If the lookup table (p, T) as functions of (v, e) should be constructed.");
91  params.addParam<bool>("construct_pT_from_vh",
92  false,
93  "If the lookup table (p, T) as functions of (v, h) should be constructed.");
94  params.addRangeCheckedParam<unsigned int>(
95  "num_v",
96  100,
97  "num_v > 0",
98  "Number of points to divide specific volume range for (v,e) lookups.");
99  params.addRangeCheckedParam<unsigned int>("num_e",
100  100,
101  "num_e > 0",
102  "Number of points to divide specific internal energy "
103  "range for (v,e) lookups.");
104  params.addParam<bool>(
105  "use_log_grid_v",
106  false,
107  "Option to use a base-10 logarithmically-spaced grid for specific volume instead of a "
108  "linearly-spaced grid.");
109  params.addParam<bool>(
110  "use_log_grid_e",
111  false,
112  "Option to use a base-10 logarithmically-spaced grid for specific internal energy instead "
113  "of a linearly-spaced grid.");
114  params.addParam<bool>(
115  "use_log_grid_h",
116  false,
117  "Option to use a base-10 logarithmically-spaced grid for specific enthalpy instead "
118  "of a linearly-spaced grid.");
119 
120  // Out of bounds behavior
121  params.addDeprecatedParam<bool>(
122  "error_on_out_of_bounds",
123  "Whether pressure or temperature from tabulation exceeding user-specified bounds leads to "
124  "an error.",
125  "This parameter has been replaced by the 'out_of_bounds_behavior' parameter which offers "
126  "more flexibility. The option to error is called 'throw' in that parameter.");
127  // NOTE: this enum must remain the same as OOBBehavior in the header
128  MooseEnum OOBBehavior("ignore throw declare_invalid warn_invalid set_to_closest_bound", "throw");
129  params.addParam<MooseEnum>("out_of_bounds_behavior",
130  OOBBehavior,
131  "Property evaluation behavior when evaluated outside the "
132  "user-specified or tabulation-specified bounds");
133 
134  // This is generally a bad idea. However, several properties have not been tabulated so several
135  // tests are relying on the original fp object to provide the value (for example for the
136  // vaporPressure())
137  params.addParam<bool>(
138  "allow_fp_and_tabulation", false, "Whether to allow the two sources of data concurrently");
139 
140  params.addParamNamesToGroup("fluid_property_file fluid_property_ve_file "
141  "fluid_property_output_file fluid_property_ve_output_file",
142  "Tabulation file read/write");
143  params.addParamNamesToGroup("construct_pT_from_ve construct_pT_from_vh",
144  "Variable set conversion");
145  params.addParamNamesToGroup("temperature_min temperature_max pressure_min pressure_max e_min "
146  "e_max v_min v_max error_on_out_of_bounds out_of_bounds_behavior",
147  "Tabulation and interpolation bounds");
148  params.addParamNamesToGroup(
149  "num_T num_p num_v num_e use_log_grid_v use_log_grid_e use_log_grid_h",
150  "Tabulation and interpolation discretization");
151 
152  return params;
153 }
154 
156  : SinglePhaseFluidProperties(parameters),
157  _file_name_in(isParamValid("fluid_property_file") ? getParam<FileName>("fluid_property_file")
158  : ""),
159  _file_name_ve_in(
160  isParamValid("fluid_property_ve_file") ? getParam<FileName>("fluid_property_ve_file") : ""),
161  _file_name_out(isParamValid("fluid_property_output_file")
162  ? getParam<FileName>("fluid_property_output_file")
163  : ""),
164  _file_name_ve_out(isParamValid("fluid_property_ve_output_file")
165  ? getParam<FileName>("fluid_property_ve_output_file")
166  : ""),
167  _save_file(isParamValid("save_file") ? getParam<bool>("save_file")
168  : isParamValid("fluid_property_output_file")),
169  _create_direct_pT_interpolations(getParam<bool>("create_pT_interpolations")),
170  _create_direct_ve_interpolations(getParam<bool>("create_ve_interpolations")),
171  _temperature_min(getParam<Real>("temperature_min")),
172  _temperature_max(getParam<Real>("temperature_max")),
173  _pressure_min(getParam<Real>("pressure_min")),
174  _pressure_max(getParam<Real>("pressure_max")),
175  _num_T(getParam<unsigned int>("num_T")),
176  _num_p(getParam<unsigned int>("num_p")),
177  _fp(isParamValid("fp") ? &getUserObject<SinglePhaseFluidProperties>("fp") : nullptr),
178  _allow_fp_and_tabulation(getParam<bool>("allow_fp_and_tabulation")),
179  _interpolated_properties_enum(getParam<MultiMooseEnum>("interpolated_properties")),
180  _interpolated_properties(),
181  _interpolate_density(false),
182  _interpolate_enthalpy(false),
183  _interpolate_internal_energy(false),
184  _interpolate_viscosity(false),
185  _interpolate_k(false),
186  _interpolate_c(false),
187  _interpolate_cp(false),
188  _interpolate_cv(false),
189  _interpolate_entropy(false),
190  _interpolate_pressure(false),
191  _interpolate_temperature(false),
192  _density_idx(libMesh::invalid_uint),
193  _enthalpy_idx(libMesh::invalid_uint),
194  _internal_energy_idx(libMesh::invalid_uint),
195  _viscosity_idx(libMesh::invalid_uint),
196  _k_idx(libMesh::invalid_uint),
197  _c_idx(libMesh::invalid_uint),
198  _cp_idx(libMesh::invalid_uint),
199  _cv_idx(libMesh::invalid_uint),
200  _entropy_idx(libMesh::invalid_uint),
201  _p_idx(libMesh::invalid_uint),
202  _T_idx(libMesh::invalid_uint),
203  _csv_reader(_file_name_in, &_communicator),
204  _construct_pT_from_ve(getParam<bool>("construct_pT_from_ve")),
205  _construct_pT_from_vh(getParam<bool>("construct_pT_from_vh")),
206  _initial_setup_done(false),
207  _num_v(getParam<unsigned int>("num_v")),
208  _num_e(getParam<unsigned int>("num_e")),
209  _log_space_v(getParam<bool>("use_log_grid_v")),
210  _log_space_e(getParam<bool>("use_log_grid_e")),
211  _log_space_h(getParam<bool>("use_log_grid_h")),
212  _OOBBehavior(getParam<MooseEnum>("out_of_bounds_behavior"))
213 {
214  // Check that initial guess (used in Newton Method) is within min and max values
216  // Sanity check on minimum and maximum temperatures and pressures
218  mooseError("temperature_max must be greater than temperature_min");
220  mooseError("pressure_max must be greater than pressure_min");
221 
222  // Set (v,e) bounds if specified by the user
223  if (isParamValid("e_min") && isParamValid("e_max"))
224  {
225  _e_min = getParam<Real>("e_min");
226  _e_max = getParam<Real>("e_max");
227  _e_bounds_specified = true;
228  }
229  else if (isParamValid("e_min") || isParamValid("e_max"))
230  paramError("e_min",
231  "Either both or none of the min and max values of the specific internal energy "
232  "should be specified");
233  else
234  _e_bounds_specified = false;
235  if (isParamValid("v_min") && isParamValid("v_max"))
236  {
237  _v_min = getParam<Real>("v_min");
238  _v_max = getParam<Real>("v_max");
239  _v_bounds_specified = true;
240  }
241  else if (isParamValid("v_min") || isParamValid("v_max"))
242  paramError("v_min",
243  "Either both or none of the min and max values of the specific volume "
244  "should be specified");
245  else
246  _v_bounds_specified = false;
247 
248  // Handle out of bounds behavior parameters and deprecation
249  if (isParamValid("error_on_out_of_bounds") && getParam<bool>("error_on_out_of_bounds") &&
250  _OOBBehavior != Throw)
251  paramError("out_of_bounds_behavior", "Inconsistent selection of out of bounds behavior.");
252  else if (isParamValid("error_on_out_of_bounds") && !getParam<bool>("error_on_out_of_bounds"))
254 
255  // Lines starting with # in the data file are treated as comments
256  _csv_reader.setComment("#");
257 
258  // Can only and must receive one source of data between fp and tabulations
259  if (_fp && (!_file_name_in.empty() || !_file_name_ve_in.empty()) && !_allow_fp_and_tabulation)
260  paramError("fluid_property_file",
261  "Cannot supply both a fluid properties object with 'fp' and a source tabulation "
262  "file with 'fluid_property_file', unless 'allow_fp_and_tabulation' is set to true");
263  if (!_fp && _file_name_in.empty() && _file_name_ve_in.empty())
264  paramError("fluid_property_file",
265  "Either a fluid properties object with the parameter 'fp' and a source tabulation "
266  "file with the parameter 'fluid_property_file' or 'fluid_property_ve_file' should "
267  "be provided.");
269  paramError("create_pT_interpolations", "Must create either (p,T) or (v,e) interpolations");
270 
271  // Some parameters are not used when reading a tabulation
272  if (!_fp && !_file_name_in.empty() &&
273  (isParamSetByUser("pressure_min") || isParamSetByUser("pressure_max") ||
274  isParamSetByUser("temperature_min") || isParamSetByUser("temperature_max")))
275  mooseWarning("User-specified bounds in pressure and temperature are ignored when reading a "
276  "'fluid_property_file'. The tabulation bounds are selected "
277  "from the bounds of the input tabulation.");
278  if (!_fp && !_file_name_in.empty() && (isParamSetByUser("num_p") || isParamSetByUser("num_T")))
279  mooseWarning("User-specified grid sizes in pressure and temperature are ignored when reading a "
280  "'fluid_property_file'. The tabulation bounds are selected "
281  "from the bounds of the input tabulation.");
282  if (!_fp && !_file_name_ve_in.empty() &&
283  (isParamSetByUser("v_min") || isParamSetByUser("v_max") || isParamSetByUser("e_min") ||
284  isParamSetByUser("e_max")))
285  mooseWarning(
286  "User-specified bounds in specific volume and internal energy are ignored when reading a "
287  "'fluid_property_ve_file'. The tabulation bounds are selected "
288  "from the bounds of the input tabulation.");
289  if (!_fp && !_file_name_ve_in.empty() && (isParamSetByUser("num_e") || isParamSetByUser("num_v")))
290  mooseWarning("User-specified grid sizes in specific volume and internal energy are ignored "
291  "when reading a 'fluid_property_ve_file'. The tabulation widths are read "
292  "from the input tabulation.");
293  if (!_file_name_ve_in.empty() && (_log_space_v || _log_space_e))
294  mooseWarning(
295  "User specfied logarithmic grids in specific volume and energy are ignored when reading a "
296  "'fluid_properties_ve_file'. The tabulation grid is read from the input tabulation");
297 }
298 
299 void
301 {
303  return;
304  _initial_setup_done = true;
305 
307  {
308  // If the user specified a (p, T) tabulation to read, use that
309  if (!_file_name_in.empty())
311  else
312  {
313  if (!_fp)
314  paramError(
315  "create_pT_interpolations",
316  "No FluidProperties (specified with 'fp' parameter) exists. Either specify a 'fp' or "
317  "specify a (p, T) tabulation file with the 'fluid_property_file' parameter");
318  _console << name() + ": Generating (p, T) tabulated data\n";
319  _console << std::flush;
320 
322  }
323  }
324 
326  {
327  // If the user specified a (v, e) tabulation to read, use that
328  if (!_file_name_ve_in.empty())
329  readFileTabulationData(false);
330  else
331  {
332  if (!_fp)
333  paramError(
334  "create_ve_interpolations",
335  "No FluidProperties (specified with 'fp' parameter) exists. Either specify a 'fp' or "
336  "specify a (v, e) tabulation file with the 'fluid_property_ve_file' parameter");
337  _console << name() + ": Generating (v, e) tabulated data\n";
338  _console << std::flush;
339 
341  }
342  }
343 
346 
347  // Write tabulated data to file
348  if (_save_file)
349  {
350  _console << name() + ": Writing tabulated data to " << _file_name_out << "\n";
352  }
353 }
354 
355 std::string
357 {
358  if (_fp)
359  return _fp->fluidName();
360  else
361  return "TabulationFromFile";
362 }
363 
364 Real
366 {
367  if (_fp)
368  return _fp->molarMass();
369  else
370  FluidPropertiesForwardError("molarMass");
371 }
372 
373 Real
375 {
377  {
379  return 1.0 / _property_ipol[_density_idx]->sample(pressure, temperature);
380  }
381  else
382  {
383  if (_fp)
384  return 1.0 / _fp->rho_from_p_T(pressure, temperature);
385  else
386  paramError("fp", "No fluid properties or csv data provided for density.");
387  }
388 }
389 
390 void
392  Real pressure, Real temperature, Real & v, Real & dv_dp, Real & dv_dT) const
393 {
394  Real rho = 0, drho_dp = 0, drho_dT = 0;
396  {
398  _property_ipol[_density_idx]->sampleValueAndDerivatives(
399  pressure, temperature, rho, drho_dp, drho_dT);
400  }
401  else
402  {
403  if (_fp)
404  _fp->rho_from_p_T(pressure, temperature, rho, drho_dp, drho_dT);
405  else
406  paramError("fp", "No fluid properties or csv data provided for density.");
407  }
408  // convert from rho to v
409  v = 1.0 / rho;
410  dv_dp = -drho_dp / (rho * rho);
411  dv_dT = -drho_dT / (rho * rho);
412 }
413 
414 Real
416 {
418  {
421  }
422  else
423  {
424  if (_fp)
425  return _fp->rho_from_p_T(pressure, temperature);
426  else
427  paramError("fp", "No fluid properties or csv data provided for density.");
428  }
429 }
430 
431 void
433  Real pressure, Real temperature, Real & rho, Real & drho_dp, Real & drho_dT) const
434 {
436  {
438  _property_ipol[_density_idx]->sampleValueAndDerivatives(
439  pressure, temperature, rho, drho_dp, drho_dT);
440  }
441  else
442  {
443  if (_fp)
444  _fp->rho_from_p_T(pressure, temperature, rho, drho_dp, drho_dT);
445  else
446  paramError("fp", "No fluid properties or csv data provided for density.");
447  }
448 }
449 
450 void
452  const ADReal & temperature,
453  ADReal & rho,
454  ADReal & drho_dp,
455  ADReal & drho_dT) const
456 {
458  {
461  _property_ipol[_density_idx]->sampleValueAndDerivatives(p, T, rho, drho_dp, drho_dT);
462  }
463  else
464  {
465  if (_fp)
466  _fp->rho_from_p_T(pressure, temperature, rho, drho_dp, drho_dT);
467  else
468  paramError("fp", "No fluid properties or csv data provided for density.");
469  }
470 }
471 
472 Real
474 {
475  Real T = T_from_p_s(p, s);
476  return rho_from_p_T(p, T);
477 }
478 
479 void
481  Real p, Real s, Real & rho, Real & drho_dp, Real & drho_ds) const
482 {
483  Real T, dT_dp, dT_ds;
484  T_from_p_s(p, s, T, dT_dp, dT_ds);
485  Real drho_dp_T, drho_dT;
486  rho_from_p_T(p, T, rho, drho_dp_T, drho_dT);
487  drho_dp = drho_dT * dT_dp + drho_dp_T;
488  drho_ds = drho_dT * dT_ds;
489 }
490 
491 Real
493 {
495  {
498  }
499  else
500  {
501  if (_fp)
502  return _fp->e_from_p_T(pressure, temperature);
503  else
504  paramError("fp", "No fluid properties or csv data provided for internal energy.");
505  }
506 }
507 
508 void
510  Real pressure, Real temperature, Real & e, Real & de_dp, Real & de_dT) const
511 {
513  {
515  _property_ipol[_internal_energy_idx]->sampleValueAndDerivatives(
516  pressure, temperature, e, de_dp, de_dT);
517  }
518  else
519  {
520  if (_fp)
521  _fp->e_from_p_T(pressure, temperature, e, de_dp, de_dT);
522  else
523  paramError("fp", "No fluid properties or csv data provided for internal energy.");
524  }
525 }
526 
527 Real
529 {
531  Real e = e_from_p_T(pressure, T);
532  return e;
533 }
534 
535 void
537  Real pressure, Real rho, Real & e, Real & de_dp, Real & de_drho) const
538 {
539  // get derivatives of T wrt to pressure and density
540  Real T, dT_dp, dT_drho;
541  T_from_p_rho(pressure, rho, T, dT_dp, dT_drho);
542 
543  // Get e, then derivatives of e wrt pressure and temperature
544  Real de_dp_at_const_T, de_dT;
545  e_from_p_T(pressure, T, e, de_dp_at_const_T, de_dT);
546 
547  // Get the derivatives of density wrt pressure and temperature
548  Real rho_pT, drho_dp, drho_dT;
549  rho_from_p_T(pressure, T, rho_pT, drho_dp, drho_dT);
550 
551  // derivatives of e wrt pressure and rho (what we want from e_from_p_rho)
552  de_drho = de_dT * dT_drho;
553  de_dp = de_dp_at_const_T - (de_drho * drho_dp);
554 }
555 
556 Real
558 {
559  auto lambda = [&](Real p, Real current_T, Real & new_rho, Real & drho_dp, Real & drho_dT)
560  { rho_from_p_T(p, current_T, new_rho, drho_dp, drho_dT); };
562  rho,
564  _tolerance,
565  lambda,
566  name() + "::T_from_p_rho",
568  .first;
569  // check for nans
570  if (std::isnan(T))
571  mooseError("Conversion from pressure (p = ",
572  pressure,
573  ") and density (rho = ",
574  rho,
575  ") to temperature failed to converge.");
576  return T;
577 }
578 
579 void
581  Real pressure, Real rho, Real & T, Real & dT_dp, Real & dT_drho) const
582 {
584  Real eps = 1e-8;
585  dT_dp = (T_from_p_rho(pressure * (1 + eps), rho) - T) / (eps * pressure);
586  dT_drho = (T_from_p_rho(pressure, rho * (1 + eps)) - T) / (eps * rho);
587 }
588 
589 Real
591 {
592  auto lambda = [&](Real p, Real current_T, Real & new_s, Real & ds_dp, Real & ds_dT)
593  { s_from_p_T(p, current_T, new_s, ds_dp, ds_dT); };
595  s,
597  _tolerance,
598  lambda,
599  name() + "::T_from_p_s",
601  .first;
602  // check for nans
603  if (std::isnan(T))
604  mooseError("Conversion from pressure (p = ",
605  pressure,
606  ") and entropy (s = ",
607  s,
608  ") to temperature failed to converge.");
609  return T;
610 }
611 
612 void
614  Real pressure, Real s, Real & T, Real & dT_dp, Real & dT_ds) const
615 {
616  T = T_from_p_s(pressure, s);
617  Real eps = 1e-8;
618  dT_dp = (T_from_p_s(pressure * (1 + eps), s) - T) / (eps * pressure);
619  dT_ds = (T_from_p_s(pressure, s * (1 + eps)) - T) / (eps * s);
620 }
621 
622 Real
624 {
626  {
629  }
630  else
631  {
632  if (_fp)
633  return _fp->h_from_p_T(pressure, temperature);
634  else
635  paramError("fp", "No fluid properties or csv data provided for enthalpy.");
636  }
637 }
638 
639 ADReal
641 {
642  if (_fp) // Assuming _fp can handle ADReal types
643  return _fp->h_from_p_T(pressure, temperature);
644  else
645  FluidPropertiesForwardError("h_from_p_T");
646 }
647 
648 void
650  Real pressure, Real temperature, Real & h, Real & dh_dp, Real & dh_dT) const
651 {
653  {
655  _property_ipol[_enthalpy_idx]->sampleValueAndDerivatives(
656  pressure, temperature, h, dh_dp, dh_dT);
657  }
658  else
659  {
660  if (_fp)
661  _fp->h_from_p_T(pressure, temperature, h, dh_dp, dh_dT);
662  else
663  paramError("fp", "No fluid properties or csv data provided for enthalpy.");
664  }
665 }
666 
667 Real
669 {
671  {
674  }
675  else
676  {
677  if (_fp)
678  return _fp->mu_from_p_T(pressure, temperature);
679  else
680  paramError("fp", "No fluid properties or csv data provided for viscosity.");
681  }
682 }
683 
684 void
686  Real pressure, Real temperature, Real & mu, Real & dmu_dp, Real & dmu_dT) const
687 {
689  {
691  _property_ipol[_viscosity_idx]->sampleValueAndDerivatives(
692  pressure, temperature, mu, dmu_dp, dmu_dT);
693  }
694  else
695  {
696  if (_fp)
697  _fp->mu_from_p_T(pressure, temperature, mu, dmu_dp, dmu_dT);
698  else
699  paramError("fp", "No fluid properties or csv data provided for viscosity.");
700  }
701 }
702 
703 Real
705 {
707  {
709  return _property_ipol[_c_idx]->sample(pressure, temperature);
710  }
711  else
712  {
713  if (_fp)
714  return _fp->c_from_p_T(pressure, temperature);
715  else
716  paramError("interpolated_properties", "No data to interpolate for speed of sound.");
717  }
718 }
719 
720 void
722  Real pressure, Real temperature, Real & c, Real & dc_dp, Real & dc_dT) const
723 {
725  {
727  _property_ipol[_c_idx]->sampleValueAndDerivatives(pressure, temperature, c, dc_dp, dc_dT);
728  }
729  else
730  {
731  if (_fp)
732  _fp->c_from_p_T(pressure, temperature, c, dc_dp, dc_dT);
733  else
734  paramError("interpolated_properties", "No data to interpolate for speed of sound.");
735  }
736 }
737 
738 Real
740 {
742  {
744  return _property_ipol[_cp_idx]->sample(pressure, temperature);
745  }
746  else
747  {
748  if (_fp)
749  return _fp->cp_from_p_T(pressure, temperature);
750  else
751  paramError("interpolated_properties",
752  "No data to interpolate for specific heat capacity at constant pressure.");
753  }
754 }
755 
756 void
758  Real pressure, Real temperature, Real & cp, Real & dcp_dp, Real & dcp_dT) const
759 {
761  {
763  _property_ipol[_cp_idx]->sampleValueAndDerivatives(pressure, temperature, cp, dcp_dp, dcp_dT);
764  }
765  else
766  {
767  if (_fp)
768  _fp->cp_from_p_T(pressure, temperature, cp, dcp_dp, dcp_dT);
769  else
770  paramError("interpolated_properties",
771  "No data to interpolate for specific heat capacity at constant pressure.");
772  }
773 }
774 
775 Real
777 {
779  {
781  return _property_ipol[_cv_idx]->sample(pressure, temperature);
782  }
783  else
784  {
785  if (_fp)
786  return _fp->cv_from_p_T(pressure, temperature);
787  else
788  paramError("interpolated_properties",
789  "No data to interpolate for specific heat capacity at constant volume.");
790  }
791 }
792 
793 void
795  Real pressure, Real temperature, Real & cv, Real & dcv_dp, Real & dcv_dT) const
796 {
797  if (_interpolate_cv)
798  {
800  _property_ipol[_cv_idx]->sampleValueAndDerivatives(pressure, temperature, cv, dcv_dp, dcv_dT);
801  }
802  else
803  {
804  if (_fp)
805  _fp->cv_from_p_T(pressure, temperature, cv, dcv_dp, dcv_dT);
806  else
807  paramError("interpolated_properties",
808  "No data to interpolate for specific heat capacity at constant volume.");
809  }
810 }
811 
812 Real
814 {
816  {
818  return _property_ipol[_k_idx]->sample(pressure, temperature);
819  }
820  else
821  {
822  if (_fp)
823  return _fp->k_from_p_T(pressure, temperature);
824  else
825  paramError("interpolated_properties", "No data to interpolate for thermal conductivity.");
826  }
827 }
828 
829 void
831  Real pressure, Real temperature, Real & k, Real & dk_dp, Real & dk_dT) const
832 {
834  {
836  return _property_ipol[_k_idx]->sampleValueAndDerivatives(
837  pressure, temperature, k, dk_dp, dk_dT);
838  }
839  else
840  {
841  if (_fp)
842  return _fp->k_from_p_T(pressure, temperature, k, dk_dp, dk_dT);
843  else
844  paramError("interpolated_properties", "No data to interpolate for thermal conductivity.");
845  }
846 }
847 
848 Real
850 {
852  {
855  }
856  else
857  {
858  if (_fp)
859  return _fp->s_from_p_T(pressure, temperature);
860  else
861  paramError("interpolated_properties", "No data to interpolate for entropy.");
862  }
863 }
864 
865 void
866 TabulatedFluidProperties::s_from_p_T(Real p, Real T, Real & s, Real & ds_dp, Real & ds_dT) const
867 {
869  {
871  _property_ipol[_entropy_idx]->sampleValueAndDerivatives(p, T, s, ds_dp, ds_dT);
872  }
873  else
874  {
875  if (_fp)
876  _fp->s_from_p_T(p, T, s, ds_dp, ds_dT);
877  else
878  paramError("interpolated_properties", "No data to interpolate for entropy.");
879  }
880 }
881 
882 Real
884 {
886  {
887  const Real p = _p_from_v_h_ipol->sample(v, h);
888  const Real T = _T_from_v_h_ipol->sample(v, h);
889  return e_from_p_T(p, T);
890  }
892  {
893  // Lambda computes h from v and the current_e
894  auto lambda = [&](Real v, Real current_e, Real & new_h, Real & dh_dv, Real & dh_de)
895  { h_from_v_e(v, current_e, new_h, dh_dv, dh_de); };
897  h,
898  /*e initial guess*/ h - _p_initial_guess * v,
899  _tolerance,
900  lambda,
901  name() + "::e_from_v_h",
903  .first;
904  return e;
905  }
906  else if (_fp)
907  return _fp->e_from_v_h(v, h);
908  else
909  mooseError(__PRETTY_FUNCTION__,
910  "\nNo tabulation or fluid property 'fp' object to compute value");
911 }
912 
913 void
914 TabulatedFluidProperties::e_from_v_h(Real v, Real h, Real & e, Real & de_dv, Real & de_dh) const
915 {
917  {
918  Real p = 0, dp_dv = 0, dp_dh = 0;
919  _p_from_v_h_ipol->sampleValueAndDerivatives(v, h, p, dp_dv, dp_dh);
920  Real T = 0, dT_dv = 0, dT_dh = 0;
921  _T_from_v_h_ipol->sampleValueAndDerivatives(v, h, T, dT_dv, dT_dh);
922  Real de_dp, de_dT;
923  e_from_p_T(p, T, e, de_dp, de_dT);
924  de_dv = de_dp * dp_dv + de_dT * dT_dv;
925  de_dh = de_dp * dp_dh + de_dT * dT_dh;
926  }
928  {
929  // Lambda computes h from v and the current_e
930  auto lambda = [&](Real v, Real current_e, Real & new_h, Real & dh_dv, Real & dh_de)
931  { h_from_v_e(v, current_e, new_h, dh_dv, dh_de); };
932  const auto e_data =
934  h,
935  /*e initial guess*/ h - _p_initial_guess * v,
936  _tolerance,
937  lambda,
938  name() + "::e_from_v_h",
940  e = e_data.first;
941  // Finite difference approximation
942  const auto e2 = e_from_v_h(v * (1 + TOLERANCE), h);
943  de_dv = (e2 - e) / (TOLERANCE * v);
944  de_dh = 1. / e_data.second;
945  }
946  else if (_fp)
947  _fp->e_from_v_h(v, h, e, de_dv, de_dh);
948  else
949  mooseError(__PRETTY_FUNCTION__,
950  "\nNo tabulation or fluid property 'fp' object to compute value");
951 }
952 
953 std::vector<Real>
955 {
956  if (_fp)
957  return _fp->henryCoefficients();
958  else
959  FluidPropertiesForwardError("henryCoefficients");
960 }
961 
962 Real
964 {
965  if (_fp)
966  return _fp->vaporPressure(temperature);
967  else
968  FluidPropertiesForwardError("vaporPressure");
969 }
970 
971 void
972 TabulatedFluidProperties::vaporPressure(Real temperature, Real & psat, Real & dpsat_dT) const
973 {
974  if (_fp)
975  _fp->vaporPressure(temperature, psat, dpsat_dT);
976  else
977  FluidPropertiesForwardError("vaporPressure");
978 }
979 
980 Real
982 {
983  if (_fp)
984  return _fp->vaporTemperature(pressure);
985  else
986  FluidPropertiesForwardError("vaporTemperature");
987 }
988 
989 void
990 TabulatedFluidProperties::vaporTemperature(Real pressure, Real & Tsat, Real & dTsat_dp) const
991 {
992 
993  if (_fp)
994  _fp->vaporTemperature(pressure, Tsat, dTsat_dp);
995  else
996  FluidPropertiesForwardError("vaporTemperature");
997 }
998 
999 Real
1001 {
1002 
1003  if (_fp)
1004  return _fp->triplePointPressure();
1005  else
1006  FluidPropertiesForwardError("triplePointPressure");
1007 }
1008 
1009 Real
1011 {
1012 
1013  if (_fp)
1014  return _fp->triplePointTemperature();
1015  else
1016  FluidPropertiesForwardError("triplePointTemperature");
1017 }
1018 
1019 Real
1021 {
1022 
1023  if (_fp)
1024  return _fp->criticalPressure();
1025  else
1026  FluidPropertiesForwardError("criticalPressure");
1027 }
1028 
1029 Real
1031 {
1032 
1033  if (_fp)
1034  return _fp->criticalTemperature();
1035  else
1036  FluidPropertiesForwardError("criticalTemperature");
1037 }
1038 
1039 Real
1041 {
1042  if (_fp)
1043  return _fp->criticalDensity();
1044  else
1045  FluidPropertiesForwardError("criticalDensity");
1046 }
1047 
1048 Real
1050 {
1052  missingVEInterpolationError(__PRETTY_FUNCTION__);
1054 
1056  return _property_ve_ipol[_p_idx]->sample(v, e);
1057  else if (_construct_pT_from_ve)
1058  return _p_from_v_e_ipol->sample(v, e);
1059  else if (_fp)
1060  return _fp->p_from_v_e(v, e);
1061  else
1062  mooseError(__PRETTY_FUNCTION__,
1063  "\nNo tabulation or fluid property 'fp' object to compute value");
1064 }
1065 
1066 void
1067 TabulatedFluidProperties::p_from_v_e(Real v, Real e, Real & p, Real & dp_dv, Real & dp_de) const
1068 {
1070  missingVEInterpolationError(__PRETTY_FUNCTION__);
1072 
1074  _property_ve_ipol[_p_idx]->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1075  else if (_construct_pT_from_ve)
1076  _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1077  else if (_fp)
1078  _fp->p_from_v_e(v, e, p, dp_dv, dp_de);
1079  else
1080  mooseError(__PRETTY_FUNCTION__,
1081  "\nNo tabulation or fluid property 'fp' object to compute value");
1082 }
1083 
1084 Real
1086 {
1088  missingVEInterpolationError(__PRETTY_FUNCTION__);
1090 
1092  return _property_ve_ipol[_T_idx]->sample(v, e);
1093  else if (_construct_pT_from_ve)
1094  return _T_from_v_e_ipol->sample(v, e);
1095  else if (_fp)
1096  return _fp->T_from_v_e(v, e);
1097  else
1098  mooseError(__PRETTY_FUNCTION__,
1099  "\nNo tabulation or fluid property 'fp' object to compute value");
1100 }
1101 
1102 void
1103 TabulatedFluidProperties::T_from_v_e(Real v, Real e, Real & T, Real & dT_dv, Real & dT_de) const
1104 {
1106  missingVEInterpolationError(__PRETTY_FUNCTION__);
1108 
1110  _property_ve_ipol[_T_idx]->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1111  else if (_construct_pT_from_ve)
1112  _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1113  else if (_fp)
1114  _fp->T_from_v_e(v, e, T, dT_dv, dT_de);
1115  else
1116  mooseError(__PRETTY_FUNCTION__,
1117  "\nNo tabulation or fluid property 'fp' object to compute value");
1118 }
1119 
1120 Real
1122 {
1124  missingVEInterpolationError(__PRETTY_FUNCTION__);
1126 
1128  return _property_ve_ipol[_c_idx]->sample(v, e);
1129  else if (_construct_pT_from_ve)
1130  {
1131  Real p = _p_from_v_e_ipol->sample(v, e);
1132  Real T = _T_from_v_e_ipol->sample(v, e);
1133  return c_from_p_T(p, T);
1134  }
1135  else if (_fp)
1136  return _fp->c_from_v_e(v, e);
1137  else
1138  mooseError(__PRETTY_FUNCTION__,
1139  "\nNo tabulation or fluid property 'fp' object to compute value");
1140 }
1141 
1142 void
1143 TabulatedFluidProperties::c_from_v_e(Real v, Real e, Real & c, Real & dc_dv, Real & dc_de) const
1144 {
1146  missingVEInterpolationError(__PRETTY_FUNCTION__);
1148 
1150  _property_ve_ipol[_c_idx]->sampleValueAndDerivatives(v, e, c, dc_dv, dc_de);
1151  else if (_construct_pT_from_ve)
1152  {
1153  Real p, dp_dv, dp_de;
1154  _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1155  Real T, dT_dv, dT_de;
1156  _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1157  Real dc_dp, dc_dT;
1158  c_from_p_T(p, T, c, dc_dp, dc_dT);
1159  dc_dv = dc_dp * dp_dv + dc_dT * dT_dv;
1160  dc_de = dc_dp * dp_de + dc_dT * dT_de;
1161  }
1162  else if (_fp)
1163  _fp->c_from_v_e(v, e, c, dc_dv, dc_de);
1164  else
1165  mooseError(__PRETTY_FUNCTION__,
1166  "\nNo tabulation or fluid property 'fp' object to compute value");
1167 }
1168 
1169 Real
1171 {
1173  missingVEInterpolationError(__PRETTY_FUNCTION__);
1175 
1177  return _property_ve_ipol[_cp_idx]->sample(v, e);
1178  else if (_construct_pT_from_ve)
1179  {
1180  Real p = _p_from_v_e_ipol->sample(v, e);
1181  Real T = _T_from_v_e_ipol->sample(v, e);
1182  return cp_from_p_T(p, T);
1183  }
1184  else if (_fp)
1185  return _fp->cp_from_v_e(v, e);
1186  else
1187  mooseError(__PRETTY_FUNCTION__,
1188  "\nNo tabulation or fluid property 'fp' object to compute value");
1189 }
1190 
1191 void
1192 TabulatedFluidProperties::cp_from_v_e(Real v, Real e, Real & cp, Real & dcp_dv, Real & dcp_de) const
1193 {
1195  missingVEInterpolationError(__PRETTY_FUNCTION__);
1197 
1199  _property_ve_ipol[_cp_idx]->sampleValueAndDerivatives(v, e, cp, dcp_dv, dcp_de);
1200  else if (_construct_pT_from_ve)
1201  {
1202  Real p, dp_dv, dp_de;
1203  _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1204  Real T, dT_dv, dT_de;
1205  _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1206  Real dcp_dp, dcp_dT;
1207  cp_from_p_T(p, T, cp, dcp_dp, dcp_dT);
1208  dcp_dv = dcp_dp * dp_dv + dcp_dT * dT_dv;
1209  dcp_de = dcp_dp * dp_de + dcp_dT * dT_de;
1210  }
1211  else if (_fp)
1212  _fp->cp_from_v_e(v, e, cp, dcp_dv, dcp_de);
1213  else
1214  mooseError(__PRETTY_FUNCTION__,
1215  "\nNo tabulation or fluid property 'fp' object to compute value");
1216 }
1217 
1218 Real
1220 {
1222  missingVEInterpolationError(__PRETTY_FUNCTION__);
1224 
1226  return _property_ve_ipol[_cv_idx]->sample(v, e);
1227  else if (_construct_pT_from_ve)
1228  {
1229  Real p = _p_from_v_e_ipol->sample(v, e);
1230  Real T = _T_from_v_e_ipol->sample(v, e);
1231  return cv_from_p_T(p, T);
1232  }
1233  else if (_fp)
1234  return _fp->cv_from_v_e(v, e);
1235  else
1236  mooseError(__PRETTY_FUNCTION__,
1237  "\nNo tabulation or fluid property 'fp' object to compute value");
1238 }
1239 
1240 void
1241 TabulatedFluidProperties::cv_from_v_e(Real v, Real e, Real & cv, Real & dcv_dv, Real & dcv_de) const
1242 {
1244  missingVEInterpolationError(__PRETTY_FUNCTION__);
1246 
1248  _property_ve_ipol[_cv_idx]->sampleValueAndDerivatives(v, e, cv, dcv_dv, dcv_de);
1249  else if (_construct_pT_from_ve)
1250  {
1251  Real p, dp_dv, dp_de;
1252  _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1253  Real T, dT_dv, dT_de;
1254  _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1255  Real dcv_dp, dcv_dT;
1256  cv_from_p_T(p, T, cv, dcv_dp, dcv_dT);
1257  dcv_dv = dcv_dp * dp_dv + dcv_dT * dT_dv;
1258  dcv_de = dcv_dp * dp_de + dcv_dT * dT_de;
1259  }
1260  else if (_fp)
1261  _fp->cv_from_v_e(v, e, cv, dcv_dv, dcv_de);
1262  else
1263  mooseError(__PRETTY_FUNCTION__,
1264  "\nNo tabulation or fluid property 'fp' object to compute value");
1265 }
1266 
1267 Real
1269 {
1271  missingVEInterpolationError(__PRETTY_FUNCTION__);
1273 
1275  return _property_ve_ipol[_viscosity_idx]->sample(v, e);
1276  else if (_construct_pT_from_ve)
1277  {
1278  Real p = _p_from_v_e_ipol->sample(v, e);
1279  Real T = _T_from_v_e_ipol->sample(v, e);
1280  return mu_from_p_T(p, T);
1281  }
1282  else if (_fp)
1283  return _fp->mu_from_v_e(v, e);
1284  else
1285  mooseError(__PRETTY_FUNCTION__,
1286  "\nNo tabulation or fluid property 'fp' object to compute value");
1287 }
1288 
1289 void
1290 TabulatedFluidProperties::mu_from_v_e(Real v, Real e, Real & mu, Real & dmu_dv, Real & dmu_de) const
1291 {
1293  missingVEInterpolationError(__PRETTY_FUNCTION__);
1295 
1297  _property_ve_ipol[_viscosity_idx]->sampleValueAndDerivatives(v, e, mu, dmu_dv, dmu_de);
1298  else if (_construct_pT_from_ve)
1299  {
1300  Real p, dp_dv, dp_de;
1301  _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1302  Real T, dT_dv, dT_de;
1303  _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1304  Real dmu_dp, dmu_dT;
1305  mu_from_p_T(p, T, mu, dmu_dp, dmu_dT);
1306  dmu_dv = dmu_dp * dp_dv + dmu_dT * dT_dv;
1307  dmu_de = dmu_dp * dp_de + dmu_dT * dT_de;
1308  }
1309  else if (_fp)
1310  _fp->mu_from_v_e(v, e, mu, dmu_dv, dmu_de);
1311  else
1312  mooseError(__PRETTY_FUNCTION__,
1313  "\nNo tabulation or fluid property 'fp' object to compute value");
1314 }
1315 
1316 Real
1318 {
1320  missingVEInterpolationError(__PRETTY_FUNCTION__);
1322 
1324  return _property_ve_ipol[_k_idx]->sample(v, e);
1325  else if (_construct_pT_from_ve)
1326  {
1327  Real T = _T_from_v_e_ipol->sample(v, e);
1328  Real p = _p_from_v_e_ipol->sample(v, e);
1329  return k_from_p_T(p, T);
1330  }
1331  else if (_fp)
1332  return _fp->k_from_v_e(v, e);
1333  else
1334  mooseError(__PRETTY_FUNCTION__,
1335  "\nNo tabulation or fluid property 'fp' object to compute value");
1336 }
1337 
1338 void
1339 TabulatedFluidProperties::k_from_v_e(Real v, Real e, Real & k, Real & dk_dv, Real & dk_de) const
1340 {
1342  missingVEInterpolationError(__PRETTY_FUNCTION__);
1344 
1346  _property_ve_ipol[_k_idx]->sampleValueAndDerivatives(v, e, k, dk_dv, dk_de);
1347  else if (_construct_pT_from_ve)
1348  {
1349  Real p, dp_dv, dp_de;
1350  _p_from_v_e_ipol->sampleValueAndDerivatives(v, e, p, dp_dv, dp_de);
1351  Real T, dT_dv, dT_de;
1352  _T_from_v_e_ipol->sampleValueAndDerivatives(v, e, T, dT_dv, dT_de);
1353  Real dk_dp, dk_dT;
1354  k_from_p_T(p, T, k, dk_dp, dk_dT);
1355  dk_dv = dk_dp * dp_dv + dk_dT * dT_dv;
1356  dk_de = dk_dp * dp_de + dk_dT * dT_de;
1357  }
1358  else if (_fp)
1359  _fp->k_from_v_e(v, e, k, dk_dv, dk_de);
1360  else
1361  mooseError(__PRETTY_FUNCTION__,
1362  "\nNo tabulation or fluid property 'fp' object to compute value");
1363 }
1364 
1365 Real
1367 {
1369  missingVEInterpolationError(__PRETTY_FUNCTION__);
1371 
1373  return _property_ve_ipol[_entropy_idx]->sample(v, e);
1374  else if (_construct_pT_from_ve)
1375  {
1376  Real T = _T_from_v_e_ipol->sample(v, e);
1377  Real p = _p_from_v_e_ipol->sample(v, e);
1378  return s_from_p_T(p, T);
1379  }
1380  else if (_fp)
1381  return _fp->s_from_v_e(v, e);
1382  else
1383  mooseError(__PRETTY_FUNCTION__,
1384  "\nNo tabulation or fluid property 'fp' object to compute value");
1385 }
1386 
1387 Real
1389 {
1390  if (!_construct_pT_from_ve &&
1393  missingVEInterpolationError(__PRETTY_FUNCTION__);
1395 
1396  Real h, T = 0, s;
1398  {
1399  s = _property_ve_ipol[_entropy_idx]->sample(v, e);
1400  h = _property_ve_ipol[_enthalpy_idx]->sample(v, e);
1401  T = _property_ve_ipol[_T_idx]->sample(v, e);
1402  }
1404  {
1405  Real p0 = _p_initial_guess;
1406  Real T0 = _T_initial_guess;
1407  Real p = 0;
1408  bool conversion_succeeded;
1409  p_T_from_v_e(v, e, p0, T0, p, T, conversion_succeeded);
1410  s = s_from_p_T(p, T);
1411  h = h_from_p_T(p, T);
1412  }
1413  else
1414  mooseError(__PRETTY_FUNCTION__,
1415  "\nNo tabulation or fluid property 'fp' object to compute value");
1416  return h - T * s;
1417 }
1418 
1419 Real
1421 {
1422  Real p0 = _p_initial_guess;
1423  Real T0 = _T_initial_guess;
1424  Real p, T;
1425  bool conversion_succeeded;
1426  p_T_from_h_s(h, s, p0, T0, p, T, conversion_succeeded);
1427  return T;
1428 }
1429 
1430 Real
1432 {
1433  if (_fp)
1434  return _fp->T_from_p_h(pressure, enthalpy);
1435  else
1436  {
1437  auto lambda = [&](Real pressure, Real current_T, Real & new_h, Real & dh_dp, Real & dh_dT)
1438  { h_from_p_T(pressure, current_T, new_h, dh_dp, dh_dT); };
1440  pressure, enthalpy, _T_initial_guess, _tolerance, lambda, name() + "::T_from_p_h")
1441  .first;
1442  // check for nans
1443  if (std::isnan(T))
1444  mooseError("Conversion from enthalpy (h = ",
1445  enthalpy,
1446  ") and pressure (p = ",
1447  pressure,
1448  ") to temperature failed to converge.");
1449  return T;
1450  }
1451 }
1452 
1453 ADReal
1455 {
1456  using std::isnan;
1457  if (_fp)
1458  return _fp->T_from_p_h(pressure, enthalpy);
1459  else
1460  {
1461  auto lambda =
1462  [&](ADReal pressure, ADReal current_T, ADReal & new_h, ADReal & dh_dp, ADReal & dh_dT)
1463  {
1464  h_from_p_T(pressure.value(), current_T.value(), new_h.value(), dh_dp.value(), dh_dT.value());
1465  // Reconstruct derivatives
1466  new_h.derivatives() =
1467  dh_dp.value() * pressure.derivatives() + dh_dT.value() * current_T.derivatives();
1468  };
1469  ADReal T =
1471  pressure, enthalpy, _T_initial_guess, _tolerance, lambda, name() + "::T_from_p_h")
1472  .first;
1473  // check for nans
1474  if (isnan(T))
1475  mooseError("Conversion from enthalpy (h = ",
1476  enthalpy,
1477  ") and pressure (p = ",
1478  pressure,
1479  ") to temperature failed to converge.");
1480  return T;
1481  }
1482 }
1483 
1484 Real
1486 {
1487  Real T = T_from_p_h(pressure, enthalpy);
1488  return s_from_p_T(pressure, T);
1489 }
1490 
1491 void
1493  Real h, Real pressure, Real & s, Real & ds_dh, Real & ds_dp) const
1494 {
1495 
1496  if (_fp)
1497  _fp->s_from_h_p(h, pressure, s, ds_dh, ds_dp);
1498  else
1499  mooseError("fp", "s_from_h_p derivatives not implemented.");
1500 }
1501 
1502 [[noreturn]] void
1503 TabulatedFluidProperties::FluidPropertiesForwardError(const std::string & desired_routine) const
1504 {
1505  mooseError("TabulatedFluidProperties can only call the function '" + desired_routine +
1506  "' when the 'fp' parameter is provided. It is currently not implemented using "
1507  "tabulations, and this property is simply forwarded to the FluidProperties specified "
1508  "in the 'fp' parameter");
1509 }
1510 
1511 void
1513 {
1514  file_name = file_name.empty() ? "fluid_properties_" + name() + "_out.csv" : file_name;
1515  if (processor_id() == 0)
1516  {
1517  {
1518  MooseUtils::checkFileWriteable(file_name);
1519 
1520  std::ofstream file_out(file_name.c_str());
1521 
1522  // Write out date and fluid type
1523  time_t now = std::time(&now);
1524  if (_fp)
1525  file_out << "# " << _fp->fluidName()
1526  << " properties created by TabulatedFluidProperties on " << ctime(&now) << "\n";
1527  else
1528  file_out << "# tabulated properties created by TabulatedFluidProperties on " << ctime(&now)
1529  << "\n";
1530 
1531  // Write out column names
1532  file_out << "pressure, temperature";
1533  for (std::size_t i = 0; i < _interpolated_properties.size(); ++i)
1534  file_out << ", " << _interpolated_properties[i];
1535  file_out << "\n";
1536 
1537  // Write out the fluid property data
1538  for (unsigned int p = 0; p < _num_p; ++p)
1539  for (unsigned int t = 0; t < _num_T; ++t)
1540  {
1541  file_out << _pressure[p] << ", " << _temperature[t];
1542  for (std::size_t i = 0; i < _properties.size(); ++i)
1543  file_out << ", " << _properties[i][p * _num_T + t];
1544  file_out << "\n";
1545  }
1546 
1547  file_out << std::flush;
1548  file_out.close();
1549  }
1550 
1551  // Write out the (v,e) to (p,T) conversions
1553  {
1554  const auto file_name_ve = (_file_name_ve_out == "")
1555  ? std::regex_replace(file_name, std::regex("\\.csv"), "_ve.csv")
1557  MooseUtils::checkFileWriteable(file_name_ve);
1558  std::ofstream file_out(file_name_ve.c_str());
1559 
1560  // Write out column names
1561  file_out << "specific_volume, internal_energy, pressure, temperature";
1562  for (const auto i : index_range(_properties))
1563  if (_interpolated_properties[i] != "internal_energy")
1564  file_out << ", " << _interpolated_properties[i];
1565  file_out << "\n";
1566 
1567  // Write out the fluid property data
1568  for (const auto v : make_range(_num_v))
1569  for (const auto e : make_range(_num_e))
1570  {
1571  const auto v_val = _specific_volume[v];
1572  const auto e_val = _internal_energy[e];
1573  const auto pressure = _p_from_v_e_ipol->sample(v_val, e_val);
1574  const auto temperature = _T_from_v_e_ipol->sample(v_val, e_val);
1575  file_out << v_val << ", " << e_val << ", " << pressure << ", " << temperature << ", ";
1576  for (const auto i : index_range(_properties))
1577  {
1578  bool add_comma = true;
1579  if (i == _density_idx)
1580  file_out << 1 / v_val;
1581  else if (i == _enthalpy_idx)
1582  file_out << h_from_p_T(pressure, temperature);
1583  // Note that we could use (p,T) routine to generate this instead of (v,e)
1584  // Or could use the _properties_ve array
1585  else if (i == _viscosity_idx)
1586  file_out << mu_from_v_e(v_val, e_val);
1587  else if (i == _k_idx)
1588  file_out << k_from_v_e(v_val, e_val);
1589  else if (i == _c_idx)
1590  file_out << c_from_v_e(v_val, e_val);
1591  else if (i == _cv_idx)
1592  file_out << cv_from_v_e(v_val, e_val);
1593  else if (i == _cp_idx)
1594  file_out << cp_from_v_e(v_val, e_val);
1595  else if (i == _entropy_idx)
1596  file_out << s_from_v_e(v_val, e_val);
1597  else
1598  add_comma = false;
1599  if (i != _properties.size() - 1 && add_comma)
1600  file_out << ", ";
1601  }
1602 
1603  file_out << "\n";
1604  }
1605 
1606  file_out << std::flush;
1607  file_out.close();
1608  }
1609  }
1610 }
1611 
1612 void
1614 {
1615  mooseAssert(_fp, "We should not try to generate (p,T) tabulated data without a _fp user object");
1616  _pressure.resize(_num_p);
1617  _temperature.resize(_num_T);
1618 
1619  // Generate data for all properties entered in input file
1622 
1623  for (std::size_t i = 0; i < _interpolated_properties_enum.size(); ++i)
1625 
1626  for (const auto i : index_range(_properties))
1627  _properties[i].resize(_num_p * _num_T);
1628 
1629  // Temperature is divided equally into _num_T segments
1630  Real delta_T = (_temperature_max - _temperature_min) / static_cast<Real>(_num_T - 1);
1631 
1632  for (unsigned int j = 0; j < _num_T; ++j)
1633  _temperature[j] = _temperature_min + j * delta_T;
1634 
1635  // Divide the pressure into _num_p equal segments
1636  Real delta_p = (_pressure_max - _pressure_min) / static_cast<Real>(_num_p - 1);
1637 
1638  for (unsigned int i = 0; i < _num_p; ++i)
1639  _pressure[i] = _pressure_min + i * delta_p;
1640 
1641  // Generate the tabulated data at the pressure and temperature points
1642  for (const auto i : index_range(_properties))
1643  {
1644  if (_interpolated_properties[i] == "density")
1645  for (unsigned int p = 0; p < _num_p; ++p)
1646  for (unsigned int t = 0; t < _num_T; ++t)
1647  _properties[i][p * _num_T + t] = _fp->rho_from_p_T(_pressure[p], _temperature[t]);
1648 
1649  if (_interpolated_properties[i] == "enthalpy")
1650  for (unsigned int p = 0; p < _num_p; ++p)
1651  for (unsigned int t = 0; t < _num_T; ++t)
1652  _properties[i][p * _num_T + t] = _fp->h_from_p_T(_pressure[p], _temperature[t]);
1653 
1654  if (_interpolated_properties[i] == "internal_energy")
1655  for (unsigned int p = 0; p < _num_p; ++p)
1656  for (unsigned int t = 0; t < _num_T; ++t)
1657  _properties[i][p * _num_T + t] = _fp->e_from_p_T(_pressure[p], _temperature[t]);
1658 
1659  if (_interpolated_properties[i] == "viscosity")
1660  for (unsigned int p = 0; p < _num_p; ++p)
1661  for (unsigned int t = 0; t < _num_T; ++t)
1662  _properties[i][p * _num_T + t] = _fp->mu_from_p_T(_pressure[p], _temperature[t]);
1663 
1664  if (_interpolated_properties[i] == "k")
1665  for (unsigned int p = 0; p < _num_p; ++p)
1666  for (unsigned int t = 0; t < _num_T; ++t)
1667  _properties[i][p * _num_T + t] = _fp->k_from_p_T(_pressure[p], _temperature[t]);
1668 
1669  if (_interpolated_properties[i] == "c")
1670  for (unsigned int p = 0; p < _num_p; ++p)
1671  for (unsigned int t = 0; t < _num_T; ++t)
1672  _properties[i][p * _num_T + t] = _fp->c_from_p_T(_pressure[p], _temperature[t]);
1673 
1674  if (_interpolated_properties[i] == "cv")
1675  for (unsigned int p = 0; p < _num_p; ++p)
1676  for (unsigned int t = 0; t < _num_T; ++t)
1677  _properties[i][p * _num_T + t] = _fp->cv_from_p_T(_pressure[p], _temperature[t]);
1678 
1679  if (_interpolated_properties[i] == "cp")
1680  for (unsigned int p = 0; p < _num_p; ++p)
1681  for (unsigned int t = 0; t < _num_T; ++t)
1682  _properties[i][p * _num_T + t] = _fp->cp_from_p_T(_pressure[p], _temperature[t]);
1683 
1684  if (_interpolated_properties[i] == "entropy")
1685  for (unsigned int p = 0; p < _num_p; ++p)
1686  for (unsigned int t = 0; t < _num_T; ++t)
1687  _properties[i][p * _num_T + t] = _fp->s_from_p_T(_pressure[p], _temperature[t]);
1688  }
1689 }
1690 
1691 void
1693 {
1694  mooseAssert(_fp, "We should not try to generate (v,e) tabulated data without a _fp user object");
1695  _specific_volume.resize(_num_v);
1696  _internal_energy.resize(_num_e);
1697 
1698  // Generate data for all properties entered in input file
1701 
1702  // This is filled from the user input, so it does not matter than this operation is performed
1703  // for both the (p,T) and (v,e) tabulated data generation
1704  for (std::size_t i = 0; i < _interpolated_properties_enum.size(); ++i)
1706 
1707  for (const auto i : index_range(_properties_ve))
1708  _properties_ve[i].resize(_num_v * _num_e);
1709 
1710  // Grids in (v,e) are not read, so we either use user input or rely on (p,T) data
1712 
1713  // Generate the tabulated data at the pressure and temperature points
1714  for (const auto i : index_range(_properties_ve))
1715  {
1716  if (_interpolated_properties[i] == "density")
1717  for (unsigned int v = 0; v < _num_v; ++v)
1718  for (unsigned int e = 0; e < _num_e; ++e)
1719  _properties_ve[i][v * _num_e + e] = 1. / _specific_volume[v];
1720 
1721  if (_interpolated_properties[i] == "enthalpy")
1722  for (unsigned int v = 0; v < _num_v; ++v)
1723  for (unsigned int e = 0; e < _num_e; ++e)
1724  _properties_ve[i][v * _num_e + e] =
1725  _fp->h_from_v_e(_specific_volume[v], _internal_energy[e]);
1726 
1727  if (_interpolated_properties[i] == "internal_energy")
1728  for (unsigned int v = 0; v < _num_v; ++v)
1729  for (unsigned int e = 0; e < _num_e; ++e)
1730  _properties_ve[i][v * _num_e + e] = _internal_energy[e];
1731 
1732  if (_interpolated_properties[i] == "viscosity")
1733  for (unsigned int v = 0; v < _num_v; ++v)
1734  for (unsigned int e = 0; e < _num_e; ++e)
1735  _properties_ve[i][v * _num_e + e] =
1736  _fp->mu_from_v_e(_specific_volume[v], _internal_energy[e]);
1737 
1738  if (_interpolated_properties[i] == "k")
1739  for (unsigned int v = 0; v < _num_v; ++v)
1740  for (unsigned int e = 0; e < _num_e; ++e)
1741  _properties_ve[i][v * _num_e + e] =
1742  _fp->k_from_v_e(_specific_volume[v], _internal_energy[e]);
1743 
1744  if (_interpolated_properties[i] == "c")
1745  for (unsigned int v = 0; v < _num_v; ++v)
1746  for (unsigned int e = 0; e < _num_e; ++e)
1747  _properties_ve[i][v * _num_e + e] =
1748  _fp->c_from_v_e(_specific_volume[v], _internal_energy[e]);
1749 
1750  if (_interpolated_properties[i] == "cv")
1751  for (unsigned int v = 0; v < _num_v; ++v)
1752  for (unsigned int e = 0; e < _num_e; ++e)
1753  _properties_ve[i][v * _num_e + e] =
1754  _fp->cv_from_v_e(_specific_volume[v], _internal_energy[e]);
1755 
1756  if (_interpolated_properties[i] == "cp")
1757  for (unsigned int v = 0; v < _num_v; ++v)
1758  for (unsigned int e = 0; e < _num_e; ++e)
1759  _properties_ve[i][v * _num_e + e] =
1760  _fp->cp_from_v_e(_specific_volume[v], _internal_energy[e]);
1761 
1762  if (_interpolated_properties[i] == "entropy")
1763  for (unsigned int v = 0; v < _num_v; ++v)
1764  for (unsigned int e = 0; e < _num_e; ++e)
1765  _properties_ve[i][v * _num_e + e] =
1766  _fp->s_from_v_e(_specific_volume[v], _internal_energy[e]);
1767 
1768  if (_interpolated_properties[i] == "pressure")
1769  for (unsigned int v = 0; v < _num_v; ++v)
1770  for (unsigned int e = 0; e < _num_e; ++e)
1771  _properties_ve[i][v * _num_e + e] =
1772  _fp->p_from_v_e(_specific_volume[v], _internal_energy[e]);
1773 
1774  if (_interpolated_properties[i] == "temperature")
1775  for (unsigned int v = 0; v < _num_v; ++v)
1776  for (unsigned int e = 0; e < _num_e; ++e)
1777  _properties_ve[i][v * _num_e + e] =
1778  _fp->T_from_v_e(_specific_volume[v], _internal_energy[e]);
1779  }
1780 }
1781 
1782 template <typename T>
1783 void
1785 {
1786  using std::max, std::min;
1787 
1788  if (_OOBBehavior == Ignore)
1789  return;
1790  else if (MooseUtils::absoluteFuzzyGreaterThan(_pressure_min, pressure, libMesh::TOLERANCE) ||
1791  MooseUtils::absoluteFuzzyGreaterThan(pressure, _pressure_max, libMesh::TOLERANCE))
1792  {
1793  if (_OOBBehavior == Throw)
1794  throw MooseException("Pressure " + Moose::stringify(pressure) +
1795  " is outside the range of tabulated pressure (" +
1798 
1799  else
1800  {
1803  flagInvalidSolution("Pressure out of bounds");
1804  else if (_OOBBehavior == WarnInvalid)
1805  flagSolutionWarning("Pressure out of bounds");
1806  }
1807  }
1808 
1809  if (MooseUtils::absoluteFuzzyGreaterThan(_temperature_min, temperature, libMesh::TOLERANCE) ||
1810  MooseUtils::absoluteFuzzyGreaterThan(temperature, _temperature_max, libMesh::TOLERANCE))
1811  {
1812  if (_OOBBehavior == Throw)
1813  throw MooseException("Temperature " + Moose::stringify(temperature) +
1814  " is outside the range of tabulated temperature (" +
1817  else
1818  {
1821  flagInvalidSolution("Temperature out of bounds");
1822  else if (_OOBBehavior == WarnInvalid)
1823  flagSolutionWarning("Temperature out of bounds");
1824  }
1825  }
1826 }
1827 
1828 template <typename T>
1829 void
1831 {
1832  using std::max, std::min;
1833 
1834  if (_OOBBehavior == Ignore)
1835  return;
1836  else if (e < _e_min || e > _e_max)
1837  {
1838  if (_OOBBehavior == Throw)
1839  throw MooseException("Specific internal energy " + Moose::stringify(e) +
1840  " is outside the range of tabulated specific internal energies (" +
1841  Moose::stringify(_e_min) + ", " + Moose::stringify(_e_max) + ").");
1842  else
1843  {
1844  e = max(_e_min, min(e, _e_max));
1846  flagInvalidSolution("Specific internal energy out of bounds");
1847  else if (_OOBBehavior == WarnInvalid)
1848  flagSolutionWarning("Specific internal energy out of bounds");
1849  }
1850  }
1851 
1852  if (v < _v_min || v > _v_max)
1853  {
1854  if (_OOBBehavior == Throw)
1855  throw MooseException("Specific volume " + Moose::stringify(v) +
1856  " is outside the range of tabulated specific volumes (" +
1857  Moose::stringify(_v_min) + ", " + Moose::stringify(_v_max) + ").");
1858  else
1859  {
1860  v = max(T(_v_min), min(v, T(_v_max)));
1862  flagInvalidSolution("Specific volume out of bounds");
1863  else if (_OOBBehavior == WarnInvalid)
1864  flagSolutionWarning("Specific volume out of bounds");
1865  }
1866  }
1867 }
1868 
1869 void
1871 {
1873  {
1874  if (_p_initial_guess < _pressure_min || _p_initial_guess > _pressure_max)
1875  mooseWarning("Pressure initial guess for (p,T), (v,e) conversions " +
1877  " is outside the range of tabulated "
1878  "pressure (" +
1880 
1881  if (_T_initial_guess < _temperature_min || _T_initial_guess > _temperature_max)
1882  mooseWarning("Temperature initial guess for (p,T), (v,e) conversions " +
1884  " is outside the range of tabulated "
1885  "temperature (" +
1887  ").");
1888  }
1889 }
1890 
1891 void
1893 {
1894  std::string file_name;
1895  if (use_pT)
1896  {
1897  _console << name() + ": Reading tabulated properties from " << _file_name_in << std::endl;
1898  _csv_reader.read();
1899  file_name = _file_name_in;
1900  }
1901  else
1902  {
1903  _console << name() + ": Reading tabulated properties from " << _file_name_ve_in << std::endl;
1905  _csv_reader.read();
1906  file_name = _file_name_ve_in;
1907  }
1908 
1909  const std::vector<std::string> & column_names = _csv_reader.getNames();
1910 
1911  // These columns form the grid and must be present in the file
1912  std::vector<std::string> required_columns;
1913  if (use_pT)
1914  required_columns = {"pressure", "temperature"};
1915  else
1916  required_columns = {"specific_volume", "internal_energy"};
1917 
1918  // Check that all required columns are present
1919  for (std::size_t i = 0; i < required_columns.size(); ++i)
1920  {
1921  if (std::find(column_names.begin(), column_names.end(), required_columns[i]) ==
1922  column_names.end())
1923  mooseError("No ",
1924  required_columns[i],
1925  " data read in ",
1926  file_name,
1927  ". A column named ",
1928  required_columns[i],
1929  " must be present");
1930  }
1931 
1932  // These columns can be present in the file
1933  std::vector<std::string> property_columns = {
1934  "density", "enthalpy", "viscosity", "k", "c", "cv", "cp", "entropy"};
1935  if (use_pT)
1936  property_columns.push_back("internal_energy");
1937  else
1938  {
1939  property_columns.push_back("pressure");
1940  property_columns.push_back("temperature");
1941  }
1942 
1943  // Check that any property names read from the file are present in the list of possible
1944  // properties, and if they are, add them to the list of read properties
1945  for (std::size_t i = 0; i < column_names.size(); ++i)
1946  {
1947  // Only check properties not in _required_columns
1948  if (std::find(required_columns.begin(), required_columns.end(), column_names[i]) ==
1949  required_columns.end())
1950  {
1951  if (std::find(property_columns.begin(), property_columns.end(), column_names[i]) ==
1952  property_columns.end())
1953  mooseWarning(column_names[i],
1954  " read in ",
1955  file_name,
1956  " tabulation file is not one of the properties that TabulatedFluidProperties "
1957  "understands. It will be ignored.");
1958  // We could be reading a (v,e) tabulation after having read a (p,T) tabulation, do not
1959  // insert twice
1960  else if (std::find(_interpolated_properties.begin(),
1962  column_names[i]) == _interpolated_properties.end())
1963  _interpolated_properties.push_back(column_names[i]);
1964  }
1965  }
1966 
1967  std::map<std::string, unsigned int> data_index;
1968  for (std::size_t i = 0; i < column_names.size(); ++i)
1969  {
1970  auto it = std::find(column_names.begin(), column_names.end(), column_names[i]);
1971  data_index[column_names[i]] = std::distance(column_names.begin(), it);
1972  }
1973 
1974  const std::vector<std::vector<Real>> & column_data = _csv_reader.getData();
1975 
1976  // Extract the pressure and temperature data vectors
1977  if (use_pT)
1978  {
1979  _pressure = column_data[data_index.find("pressure")->second];
1980  _temperature = column_data[data_index.find("temperature")->second];
1981  }
1982  else
1983  {
1984  _specific_volume = column_data[data_index.find("specific_volume")->second];
1985  _internal_energy = column_data[data_index.find("internal_energy")->second];
1986  }
1987 
1988  if (use_pT)
1989  checkFileTabulationGrids(_pressure, _temperature, file_name, "pressure", "temperature");
1990  else
1993  file_name,
1994  "specific volume",
1995  "specific internal energy");
1996 
1997  if (use_pT)
1998  {
1999  _num_p = _pressure.size();
2000  _num_T = _temperature.size();
2001 
2002  // Minimum and maximum pressure and temperature. Note that _pressure and
2003  // _temperature are sorted
2004  _pressure_min = _pressure.front();
2005  _pressure_max = _pressure.back();
2006  _temperature_min = _temperature.front();
2007  _temperature_max = _temperature.back();
2008 
2009  // Extract the fluid property data from the file
2010  for (std::size_t i = 0; i < _interpolated_properties.size(); ++i)
2011  _properties.push_back(column_data[data_index.find(_interpolated_properties[i])->second]);
2012  }
2013  else
2014  {
2015  _num_v = _specific_volume.size();
2016  _num_e = _internal_energy.size();
2017 
2018  // Minimum and maximum specific internal energy and specific volume
2019  _v_min = _specific_volume.front();
2020  _v_max = _specific_volume.back();
2021  _e_min = _internal_energy.front();
2022  _e_max = _internal_energy.back();
2023 
2024  // We cannot overwrite the tabulated data grid with a grid generated from user-input for the
2025  // purpose of creating (p,T) to (v,e) interpolations
2027  paramError("construct_pT_from_ve",
2028  "Reading a (v,e) tabulation and generating (p,T) to (v,e) interpolation tables is "
2029  "not supported at this time.");
2030 
2031  // Make sure we use the tabulation bounds
2032  _e_bounds_specified = true;
2033  _v_bounds_specified = true;
2034 
2035  // Extract the fluid property data from the file
2036  _properties_ve.reserve(_interpolated_properties.size());
2037  for (std::size_t i = 0; i < _interpolated_properties.size(); ++i)
2038  _properties_ve.push_back(column_data[data_index.find(_interpolated_properties[i])->second]);
2039  }
2040 }
2041 
2042 void
2044  std::vector<Real> & v2,
2045  const std::string & file_name,
2046  const std::string & v1_name,
2047  const std::string & v2_name)
2048 {
2049  // NOTE: We kept the comments in terms of pressure & temperature for clarity
2050  // Pressure (v1) and temperature (v2) data contains duplicates due to the csv format.
2051  // First, check that pressure (v1) is monotonically increasing
2052  if (!std::is_sorted(v1.begin(), v1.end()))
2053  mooseError("The column data for ", v1_name, " is not monotonically increasing in ", file_name);
2054 
2055  // The first pressure (v1) value is repeated for each temperature (v2) value. Counting the
2056  // number of repeats provides the number of temperature (v2) values
2057  auto num_v2 = std::count(v1.begin(), v1.end(), v1.front());
2058 
2059  // Now remove the duplicates in the pressure (v1) vector
2060  auto last_unique = std::unique(v1.begin(), v1.end());
2061  v1.erase(last_unique, v1.end());
2062 
2063  // Check that the number of rows in the csv file is equal to _num_v1 * _num_v2
2064  // v2 is currently the same size as the column_data (will get trimmed at the end)
2065  if (v2.size() != v1.size() * libMesh::cast_int<unsigned int>(num_v2))
2066  mooseError("The number of rows in ",
2067  file_name,
2068  " is not equal to the number of unique ",
2069  v1_name,
2070  " values ",
2071  v1.size(),
2072  " multiplied by the number of unique ",
2073  v2_name,
2074  " values ",
2075  num_v2);
2076 
2077  // Need to make sure that the temperature (v2) values are provided in ascending order
2078  std::vector<Real> base_v2(v2.begin(), v2.begin() + num_v2);
2079  if (!std::is_sorted(base_v2.begin(), base_v2.end()))
2080  mooseError("The column data for ", v2_name, " is not monotonically increasing in ", file_name);
2081 
2082  // Need to make sure that the temperature (v2) are repeated for each pressure (v1) grid point
2083  auto it_v2 = v2.begin() + num_v2;
2084  for (const auto i : make_range(v1.size() - 1))
2085  {
2086  std::vector<Real> repeated_v2(it_v2, it_v2 + num_v2);
2087  if (repeated_v2 != base_v2)
2088  mooseError(v2_name,
2089  " values for ",
2090  v1_name,
2091  " ",
2092  v1[i + 1],
2093  " are not identical to values for ",
2094  v1[0]);
2095 
2096  std::advance(it_v2, num_v2);
2097  }
2098 
2099  // At this point, all temperature (v2) data has been provided in ascending order
2100  // identically for each pressure (v1) value, so we can just keep the first range
2101  v2.erase(v2.begin() + num_v2, v2.end());
2102 }
2103 
2104 void
2106 {
2107  // At this point, all properties read or generated are able to be used by
2108  // TabulatedFluidProperties. Now set flags and indexes for each property in
2109  //_interpolated_properties to use in property calculations
2110  for (std::size_t i = 0; i < _interpolated_properties.size(); ++i)
2111  {
2112  if (_interpolated_properties[i] == "density")
2113  {
2114  _interpolate_density = true;
2115  _density_idx = i;
2116  }
2117  else if (_interpolated_properties[i] == "enthalpy")
2118  {
2119  _interpolate_enthalpy = true;
2120  _enthalpy_idx = i;
2121  }
2122  else if (_interpolated_properties[i] == "internal_energy")
2123  {
2126  }
2127  else if (_interpolated_properties[i] == "viscosity")
2128  {
2129  _interpolate_viscosity = true;
2130  _viscosity_idx = i;
2131  }
2132  else if (_interpolated_properties[i] == "k")
2133  {
2134  _interpolate_k = true;
2135  _k_idx = i;
2136  }
2137  else if (_interpolated_properties[i] == "c")
2138  {
2139  _interpolate_c = true;
2140  _c_idx = i;
2141  }
2142  else if (_interpolated_properties[i] == "cp")
2143  {
2144  _interpolate_cp = true;
2145  _cp_idx = i;
2146  }
2147  else if (_interpolated_properties[i] == "cv")
2148  {
2149  _interpolate_cv = true;
2150  _cv_idx = i;
2151  }
2152  else if (_interpolated_properties[i] == "entropy")
2153  {
2154  _interpolate_entropy = true;
2155  _entropy_idx = i;
2156  }
2157  else if (_interpolated_properties[i] == "pressure")
2158  {
2159  _interpolate_pressure = true;
2160  _p_idx = i;
2161  }
2162  else if (_interpolated_properties[i] == "temperature")
2163  {
2164  _interpolate_temperature = true;
2165  _T_idx = i;
2166  }
2167  else
2168  mooseError("Specified property '" + _interpolated_properties[i] +
2169  "' is present in the tabulation but is not currently leveraged by the code in the "
2170  "TabulatedFluidProperties. If it is spelled correctly, then please contact a "
2171  "MOOSE or fluid properties module developer.");
2172  }
2173 }
2174 
2175 void
2177 {
2178  mooseAssert(_file_name_ve_in.empty(), "We should be reading the specific volume grid from file");
2179  if (!_v_bounds_specified)
2180  {
2181  if (_fp)
2182  {
2183  // extreme values of specific volume for the grid bounds
2188  _v_min = std::min({v1, v2, v3, v4});
2189  _v_max = std::max({v1, v2, v3, v4});
2190  }
2191  // if csv exists, get max and min values from csv file
2192  else
2193  {
2194  Real rho_max =
2195  *std::max_element(_properties[_density_idx].begin(), _properties[_density_idx].end());
2196  Real rho_min =
2197  *std::min_element(_properties[_density_idx].begin(), _properties[_density_idx].end());
2198  _v_max = 1 / rho_min;
2199  _v_min = 1 / rho_max;
2200  }
2201  // Prevent changing the bounds of the grid
2202  _v_bounds_specified = true;
2203  }
2204 
2205  // Create v grid for interpolation
2206  _specific_volume.resize(_num_v);
2207  if (_log_space_v)
2208  {
2209  // incrementing the exponent linearly will yield a log-spaced grid after taking the value to
2210  // the power of 10
2211  Real dv = (std::log10(_v_max) - std::log10(_v_min)) / ((Real)_num_v - 1);
2212  Real log_v_min = std::log10(_v_min);
2213  for (unsigned int j = 0; j < _num_v; ++j)
2214  _specific_volume[j] = std::pow(10, log_v_min + j * dv);
2215  }
2216  else
2217  {
2218  Real dv = (_v_max - _v_min) / ((Real)_num_v - 1);
2219  for (unsigned int j = 0; j < _num_v; ++j)
2220  _specific_volume[j] = _v_min + j * dv;
2221  }
2222 }
2223 
2224 void
2226 {
2228  if (!_e_bounds_specified)
2229  {
2230  if (_fp)
2231  {
2232  // extreme values of internal energy for the grid bounds
2237  _e_min = std::min({e1, e2, e3, e4});
2238  _e_max = std::max({e1, e2, e3, e4});
2239  }
2240  // if csv exists, get max and min values from csv file
2241  else
2242  {
2243  _e_min = *std::min_element(_properties[_internal_energy_idx].begin(),
2245  _e_max = *std::max_element(_properties[_internal_energy_idx].begin(),
2247  }
2248  }
2249 
2250  // Create e grid for interpolation
2251  _internal_energy.resize(_num_e);
2252  if (_log_space_e)
2253  {
2254  // incrementing the exponent linearly will yield a log-spaced grid after taking the value to
2255  // the power of 10
2256  if (_e_min < 0)
2257  mooseError("Logarithmic grid in specific energy can only be used with a positive specific "
2258  "energy. Current minimum: " +
2259  std::to_string(_e_min));
2260  Real de = (std::log10(_e_max) - std::log10(_e_min)) / ((Real)_num_e - 1);
2261  Real log_e_min = std::log10(_e_min);
2262  for (const auto j : make_range(_num_e))
2263  _internal_energy[j] = std::pow(10, log_e_min + j * de);
2264  }
2265  else
2266  {
2267  Real de = (_e_max - _e_min) / ((Real)_num_e - 1);
2268  for (const auto j : make_range(_num_e))
2269  _internal_energy[j] = _e_min + j * de;
2270  }
2271 }
2272 
2273 void
2275 {
2276  if (_file_name_ve_in.empty())
2278  if (_fp)
2279  {
2280  // extreme values of enthalpy for the grid bounds
2285  _h_min = std::min({h1, h2, h3, h4});
2286  _h_max = std::max({h1, h2, h3, h4});
2287  }
2288  // if csv exists, get max and min values from csv file
2289  else if (_properties.size())
2290  {
2291  _h_max = *max_element(_properties[_enthalpy_idx].begin(), _properties[_enthalpy_idx].end());
2292  _h_min = *min_element(_properties[_enthalpy_idx].begin(), _properties[_enthalpy_idx].end());
2293  }
2294  else if (_properties_ve.size())
2295  {
2296  _h_max = *max_element(_properties[_enthalpy_idx].begin(), _properties[_enthalpy_idx].end());
2297  _h_min = *min_element(_properties[_enthalpy_idx].begin(), _properties[_enthalpy_idx].end());
2298  }
2299  else
2300  mooseError("Need a source to compute the enthalpy grid bounds: either a FP object, or a (p,T) "
2301  "tabulation file or a (v,e) tabulation file");
2302 
2303  // Create h grid for interpolation
2304  // enthalpy & internal energy use same # grid points
2305  _enthalpy.resize(_num_e);
2306  if (_log_space_h)
2307  {
2308  // incrementing the exponent linearly will yield a log-spaced grid after taking the value to
2309  // the power of 10
2310  if (_h_min < 0)
2311  mooseError("Logarithmic grid in specific energy can only be used with a positive enthalpy. "
2312  "Current minimum: " +
2313  std::to_string(_h_min));
2314  Real dh = (std::log10(_h_max) - std::log10(_h_min)) / ((Real)_num_e - 1);
2315  Real log_h_min = std::log10(_h_min);
2316  for (const auto j : make_range(_num_e))
2317  _enthalpy[j] = std::pow(10, log_h_min + j * dh);
2318  }
2319  else
2320  {
2321  Real dh = (_h_max - _h_min) / ((Real)_num_e - 1);
2322  for (const auto j : make_range(_num_e))
2323  _enthalpy[j] = _h_min + j * dh;
2324  }
2325 }
2326 
2327 void
2328 TabulatedFluidProperties::missingVEInterpolationError(const std::string & function_name) const
2329 {
2330  mooseError(function_name +
2331  ": to call this function you must:\n-add this property to the list to the list of "
2332  "'interpolated_properties'\n and then either:\n-construct (p, T) from (v, e) "
2333  "tabulations using the 'construct_pT_from_ve' parameter\n-load (v,e) interpolation "
2334  "tables using the 'fluid_property_ve_file' parameter");
2335 }
2336 
2338  Real & temperature) const;
2340  ADReal & temperature) const;
2341 template void TabulatedFluidProperties::checkInputVariablesVE(Real & v, Real & e) const;
virtual Real k_from_p_T(Real pressure, Real temperature) const override
bool _log_space_v
log-space the specific volume interpolation grid axis instead of linear
FileName _file_name_ve_in
File name of input (v,e) tabulated data file.
void readFileTabulationData(bool use_pT)
Read tabulation data from file.
virtual void initialSetup() override
virtual Real rho_from_p_s(Real p, Real s) const override
virtual Real triplePointTemperature() const override
Triple point temperature.
virtual Real v_from_p_T(Real pressure, Real temperature) const override
const bool _create_direct_ve_interpolations
Whether to create direct (v,e) interpolations.
static const std::string cv
Definition: NS.h:122
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)
void addDeprecatedParam(const std::string &name, const T &value, const std::string &doc_string, const std::string &deprecation_message)
virtual Real criticalDensity() const override
Critical density.
std::unique_ptr< BidimensionalInterpolation > _p_from_v_h_ipol
Bidimensional interpolation of pressure from (v,h)
virtual Real triplePointTemperature() const
Triple point temperature.
const unsigned int invalid_uint
void paramError(const std::string &param, Args... args) const
const bool _create_direct_pT_interpolations
Whether to create direct (p,T) interpolations.
virtual Real e_from_v_h(Real v, Real h) const override
void setComment(const std::string &value)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
const Real eps
static InputParameters validParams()
static constexpr Real TOLERANCE
Real _temperature_max
Maximum temperature in tabulated data.
Real _h_max
Maximum specific enthalpy in tabulated data.
Real _e_max
Maximum internal energy in tabulated data (can be user-specified)
virtual Real T_from_p_s(Real p, Real s) const
virtual Real T_from_v_e(Real v, Real e) const override
virtual Real molarMass() const
Molar mass [kg/mol].
virtual void checkInitialGuess() const
Checks initial guess for Newton Method.
Real _e_min
Minimum internal energy in tabulated data (can be user-specified)
Real _pressure_max
Maximum pressure in tabulated data.
bool _e_bounds_specified
Whether the specific internal energy bounds were set by the user.
unsigned int size() const
MooseUtils::DelimitedFileReader _csv_reader
The MOOSE delimited file reader.
virtual std::vector< Real > henryCoefficients() const
Henry&#39;s law coefficients for dissolution in water.
static const std::string temperature
Definition: NS.h:59
bool _log_space_e
log-space the internal energy interpolation grid axis instead of linear
virtual Real s_from_p_T(Real pressure, Real temperature) const override
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
MooseEnum _OOBBehavior
User-selected out-of-bounds behavior.
MultiMooseEnum _interpolated_properties_enum
Properties to be interpolated entered in the input file.
void computePropertyIndicesInInterpolationVectors()
Retrieves the index for each property in the vector of interpolations.
bool _v_bounds_specified
Whether the specific volume bounds were set by the user.
DualNumber< Real, DNDerivativeType, true > ADReal
std::vector< std::unique_ptr< BidimensionalInterpolation > > _property_ve_ipol
Vector of bi-dimensional interpolation of fluid properties directly in (v,e)
auto max(const L &left, const R &right)
std::pair< T, T > NewtonSolve(const T &x, const T &y, const Real z_initial_guess, const Real tolerance, const Functor &func, const std::string &caller_name, const unsigned int max_its=100)
NewtonSolve does a 1D Newton Solve to solve the equation y = f(x, z) for variable z...
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 ...
std::unique_ptr< BidimensionalInterpolation > _p_from_v_e_ipol
Bi-dimensional interpolation of pressure from (v,e)
Real _h_min
Minimum specific enthalpy in tabulated data.
virtual Real s_from_h_p(Real h, Real pressure) const override
virtual Real vaporTemperature(Real pressure) const override
Vapor temperature.
std::unique_ptr< BidimensionalInterpolation > _T_from_v_e_ipol
Bi-dimensional interpolation of temperature from (v,e)
FileName _file_name_ve_out
File name of output (v,e) tabulated data file.
std::vector< Real > _enthalpy
Specific enthalpy vector.
unsigned int _num_v
Number of specific volume points in the tabulated data.
static const std::string cp
Definition: NS.h:121
const Real _tolerance
Newton&#39;s method may be used to convert between variable sets.
bool _construct_pT_from_ve
if the lookup table p(v, e) and T(v, e) should be constructed
void setFileName(const std::string &new_file)
e e e e s T T T T T rho v v T e h
Real _temperature_min
Minimum temperature in tabulated data.
const std::string & name() const
virtual Real vaporTemperature(Real p) const
Vapor temperature.
void createVHGridVectors()
Create (or reset) the grid vectors for the specific volume and enthalpy interpolation The order of pr...
void FluidPropertiesForwardError(const std::string &desired_routine) const
bool _construct_pT_from_vh
if the lookup table p(v, h) and T(v, h) should be constructed
virtual Real molarMass() const override
Molar mass [kg/mol].
FileName _file_name_out
File name of output tabulated data file.
virtual Real h_from_p_T(Real p, Real T) const override
virtual Real e_from_p_rho(Real pressure, Real rho) const override
virtual Real vaporPressure(Real temperature) const override
Vapor pressure.
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 criticalTemperature() const
Critical temperature.
static InputParameters validParams()
virtual Real k_from_v_e(Real v, Real e) const override
static const std::string mu
Definition: NS.h:123
virtual void generateVETabulatedData()
Generates a table of fluid properties by looping over specific volume and internal energy and calcula...
virtual Real cv_from_v_e(Real v, Real e) const override
virtual Real c_from_p_T(Real pressure, Real temperature) const override
Common class for single phase fluid properties.
bool _log_space_h
log-space the enthalpy interpolation grid axis instead of linear
Real _v_min
Minimum specific volume in tabulated data (can be user-specified)
virtual Real triplePointPressure() const override
Triple point pressure.
std::vector< std::string > _interpolated_properties
List of properties to be interpolated.
virtual Real g_from_v_e(Real v, Real e) const override
std::string stringify(const T &t)
virtual Real e_from_p_T(Real pressure, Real temperature) const override
virtual Real c_from_v_e(Real v, Real e) const override
const std::vector< std::vector< T > > & getData() const
TabulatedFluidProperties(const InputParameters &parameters)
virtual Real triplePointPressure() const
Triple point pressure.
std::vector< Real > _pressure
Pressure vector.
std::vector< Real > _temperature
Temperature vector.
virtual Real rho_from_p_T(Real pressure, Real temperature) const override
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 ...
virtual Real criticalTemperature() const override
Critical temperature.
unsigned int _num_T
Number of temperature points in the tabulated data.
bool _initial_setup_done
keeps track of whether initialSetup has been performed
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...
virtual Real criticalPressure() const override
Critical pressure.
virtual std::string fluidName() const override
Fluid name.
const SinglePhaseFluidProperties *const _fp
SinglePhaseFluidPropertiesPT UserObject.
void missingVEInterpolationError(const std::string &function_name) const
Standardized error message for missing interpolation.
virtual Real s_from_v_e(Real v, Real e) const override
Real _pressure_min
Minimum pressure in tabulated data.
std::vector< std::vector< Real > > _properties
Tabulated fluid properties (read from file OR computed from _fp)
virtual Real mu_from_p_T(Real pressure, Real temperature) const override
virtual Real cp_from_p_T(Real pressure, Real temperature) const override
virtual Real criticalDensity() const
Critical density.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
unsigned int _num_p
Number of pressure points in the tabulated data.
static const std::string v
Definition: NS.h:84
virtual Real T_from_p_h(Real pressure, Real enthalpy) const override
const bool _allow_fp_and_tabulation
Whether to allow a fp object when a tabulation is in use.
virtual Real cv_from_p_T(Real pressure, Real temperature) const override
std::vector< std::vector< Real > > _properties_ve
Tabulated fluid properties in (v,e) (read from file OR computed from _fp)
const unsigned int _max_newton_its
Maximum number of iterations for the variable conversion newton solves.
Real _v_max
Maximum specific volume in tabulated data (can be user-specified)
static const std::string pressure
Definition: NS.h:56
virtual Real T_from_h_s(Real h, Real s) const
const Real _p_initial_guess
Initial guess for pressure (or pressure used to compute the initial guess)
void mooseWarning(Args &&... args) const
IntRange< T > make_range(T beg, T end)
std::vector< Real > _specific_volume
Specific volume vector.
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 *...
void mooseError(Args &&... args) const
virtual Real cp_from_v_e(Real v, Real e) const override
unsigned int _density_idx
Index of each property.
virtual Real criticalPressure() const
Critical pressure.
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.
void addClassDescription(const std::string &doc_string)
std::vector< Real > _internal_energy
Specific internal energy vector.
std::unique_ptr< BidimensionalInterpolation > _T_from_v_h_ipol
Bi-dimensional interpolation of temperature from (v,h)
bool _interpolate_density
Set of flags to note whether a property is to be interpolated.
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
bool checkFileWriteable(const std::string &filename, bool throw_on_unwritable)
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
virtual std::vector< Real > henryCoefficients() const override
The following routines are simply forwarded to the &#39;fp&#39; companion FluidProperties as they are not inc...
bool isParamValid(const std::string &name) const
const ConsoleStream _console
void createVGridVector()
Create (or reset) the grid vectors for the specific volume and internal energy interpolations The ord...
std::vector< std::unique_ptr< BidimensionalInterpolation > > _property_ipol
Vector of bi-dimensional interpolation of fluid properties.
const bool _save_file
Whether to save a generated fluid properties file to disk.
virtual Real mu_from_v_e(Real v, Real e) const override
OOBBehavior
Enum specifying all the behavior on out of bounds data options.
const std::vector< std::string > & getNames() const
processor_id_type processor_id() const
virtual Real vaporPressure(Real T) const
Vapor pressure.
auto min(const L &left, const R &right)
const Real _T_initial_guess
Initial guess for temperature (or temperature used to compute the initial guess)
virtual void generateTabulatedData()
Generates a table of fluid properties by looping over pressure and temperature and calculating proper...
bool isParamSetByUser(const std::string &name) const
MooseUnits pow(const MooseUnits &, int)
virtual void constructInterpolation()=0
static const std::string k
Definition: NS.h:130
void writeTabulatedData(std::string file_name)
Writes tabulated data to a file.
virtual Real T_from_p_rho(Real pressure, Real rho) const
void ErrorVector unsigned int
auto index_range(const T &sizable)
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
FileName _file_name_in
File name of input tabulated data file.
unsigned int _num_e
Number of internal energy points in tabulated data.