16 #include "libmesh/string_to_enum.h" 20 #ifdef THERMOCHIMICA_ENABLED 21 #include "Thermochimica-cxx.h" 22 #include "checkUnits.h" 38 "Sets up the thermodynamic model and variables for the " 39 "thermochemistry solve using Thermochimica.");
42 params.
addParam<std::vector<std::string>>(
43 "elements", {
"ALL"},
"List of chemical elements (or ALL)");
44 params.
addCoupledVar(
"temperature",
"Name of temperature variable");
45 params.
addCoupledVar(
"pressure",
"Name of pressure variable");
46 MooseEnum reinit_type(
"none time nodal",
"nodal");
48 "reinitialization_type", reinit_type,
"Reinitialization scheme to use with Thermochimica");
49 params.
addParam<FileName>(
"initial_values",
"The CSV file name with initial conditions.");
50 params.
addParam<FileName>(
"thermofile",
"Thermodynamics model file");
57 "mole_fraction atom_fraction atoms moles gram-atoms mass_fraction kilograms grams pounds");
62 "execute_on", exec_enum,
"When to execute the ThermochimicaData UO");
63 params.
addParam<
bool>(
"is_fv",
false,
"Should the variables set up by action be of FV type");
65 params.
addParam<std::vector<std::string>>(
"output_phases", {},
"List of phases to be output");
66 params.
addParam<std::vector<std::string>>(
67 "output_species", {},
"List species for which concentration in the phases is needed");
68 MooseEnum mUnit_op(
"moles mole_fraction");
70 "output_species_unit", mUnit_op,
"Mass unit for output species: mole_fractions or moles");
71 params.
addParam<std::vector<std::string>>(
72 "output_element_potentials",
74 "List of chemical elements for which chemical potentials are requested");
75 params.
addParam<std::vector<std::string>>(
76 "output_vapor_pressures",
78 "List of gas phase species for which vapor pressures are requested");
79 params.
addParam<std::vector<std::string>>(
80 "output_element_phases",
82 "List of elements whose molar amounts in specific phases are requested");
84 "uo_name",
"Thermochimica",
"Name of the ThermochimicaDataUserObject.");
96 if (action.size() == 1)
102 "The temperature unit must be specified for Thermochimica objects to be constructed");
106 "The pressure unit must be specified for Thermochimica objects to be constructed");
110 "The mass unit must be specified for Thermochimica objects to be constructed");
114 "Temperature variable must be specified for this object to be constructed");
119 "output_species_unit",
120 "Output mass unit must be specified for Thermochimica user object to be constructed");
124 #ifdef THERMOCHIMICA_ENABLED 128 const auto thermo_file = getParam<FileName>(
"thermofile");
130 if (thermo_file.length() > 1024)
132 "Path exceeds Thermochimica's maximal permissible length of 1024 with ",
133 thermo_file.length(),
137 Thermochimica::setThermoFilename(thermo_file);
140 Thermochimica::parseThermoFile();
142 const auto idbg = Thermochimica::checkInfoThermo();
144 paramError(
"thermofile",
"Thermochimica data file cannot be parsed. ", idbg);
149 Thermochimica::checkTemperature(tunit);
150 Thermochimica::setUnitTemperature(tunit);
151 int idbg = Thermochimica::checkInfoThermo();
153 paramError(
"tunit",
"Cannot set temperature unit in Thermochimica", idbg);
156 Thermochimica::checkPressure(punit);
157 Thermochimica::setUnitPressure(punit);
158 idbg = Thermochimica::checkInfoThermo();
160 paramError(
"punit",
"Cannot set pressure unit in Thermochimica", idbg);
163 std::replace(munit.begin(), munit.end(),
'_',
' ');
164 Thermochimica::checkMass(munit);
165 Thermochimica::setUnitMass(munit);
166 idbg = Thermochimica::checkInfoThermo();
168 paramError(
"munit",
"Cannot set mass unit in Thermochimica", idbg);
170 _elements = getParam<std::vector<std::string>>(
"elements");
173 _elements.resize(Thermochimica::getNumberElementsDatabase());
174 _elements = Thermochimica::getElementsDatabase();
175 mooseInfo(
"Thermochimica elements: 'ALL' specified in input file. Using: ",
180 std::vector<std::string> db_elements(Thermochimica::getNumberElementsDatabase());
181 db_elements = Thermochimica::getElementsDatabase();
183 if (std::find(db_elements.begin(), db_elements.end(),
_elements[i]) == db_elements.end())
194 Thermochimica::setTemperaturePressure(1000.0, 1.0);
195 Thermochimica::setElementMass(0, 0.0);
198 Thermochimica::setElementMass(Thermochimica::atomicNumber(
_elements[i]), 1.0);
200 Thermochimica::setup();
204 _phases = getParam<std::vector<std::string>>(
"output_phases");
207 auto [soln_phases, stoich_phases] = Thermochimica::getNumberPhasesSystem();
208 _phases.resize(soln_phases + stoich_phases);
209 _phases = Thermochimica::getPhaseNamesSystem();
210 mooseInfo(
"ChemicalCompositionAction phases: 'ALL' specified in input file. Using: ",
215 auto db_phases = Thermochimica::getPhaseNamesSystem();
217 if (std::find(db_phases.begin(), db_phases.end(),
_phases[i]) == db_phases.end())
218 paramError(
"output_phases",
"Phase '",
_phases[i],
"' was not found in the simulation.");
224 auto species = getParam<std::vector<std::string>>(
"output_species");
225 auto db_phases = Thermochimica::getPhaseNamesSystem();
226 auto n_db_species = Thermochimica::getNumberSpeciesSystem();
227 auto db_species = Thermochimica::getSpeciesSystem();
229 if (Thermochimica::isPhaseMQM(i))
231 auto [pairs, quads, idbg] =
232 Thermochimica::getMqmqaNumberPairsQuads(Thermochimica::getPhaseNamesSystem()[i]);
233 n_db_species[i] = pairs;
236 if (species.size() == 1 && species[0] ==
"ALL")
238 if (!n_db_species.empty())
239 species.resize(n_db_species.back());
241 mooseInfo(
"ChemicalCompositionAction species: 'ALL' specified in input file. Thermochimica " 242 "returned no possible species.");
246 for (
const auto i :
make_range(db_species.size()))
249 species.push_back(db_phases[i] +
":" + db_species[i][
j]);
252 mooseInfo(
"ChemicalCompositionAction species: 'ALL' specified in input file. Using: ",
259 std::vector<std::string> tokens;
261 if (tokens.size() == 1)
262 paramError(
"output_species",
"No ':' separator found in variable '", species[i],
"'");
264 auto phase_index = std::find(db_phases.begin(), db_phases.end(), tokens[0]);
265 if (phase_index == db_phases.end())
269 "' of output species '",
271 "' not found in the simulation.");
272 auto sp = db_species[std::distance(db_phases.begin(), phase_index)];
273 if (std::find(sp.begin(), sp.end(), tokens[1]) == sp.end())
275 "output_species",
"Species '", tokens[1],
"' was not found in the simulation.");
282 auto element_potentials = getParam<std::vector<std::string>>(
"output_element_potentials");
283 if (element_potentials.size() == 1 && element_potentials[0] ==
"ALL")
287 element_potentials.resize(
_elements.size());
289 element_potentials[i] =
"mu:" +
_elements[i];
291 "ChemicalCompositionAction element potentials: 'ALL' specified in input file. Using: ",
297 for (
const auto i :
index_range(element_potentials))
299 std::vector<std::string> tokens;
301 if (tokens.size() == 1)
303 "No ':' separator found in variable '",
304 element_potentials[i],
310 "' was not found in the simulation.");
318 auto vapor_pressures = getParam<std::vector<std::string>>(
"output_vapor_pressures");
319 if (!Thermochimica::isPhaseGas(0))
321 "No gas phase found in the simulation. Cannot output vapor pressures.");
322 if (vapor_pressures.size() == 1 && vapor_pressures[0] ==
"ALL")
324 vapor_pressures.resize(Thermochimica::getNumberSpeciesSystem()[0]);
326 auto db_gas_species = Thermochimica::getSpeciesInPhase(0);
327 auto gas_name = Thermochimica::getPhaseNamesSystem()[0];
330 vapor_pressures[i] =
"vp:" + gas_name +
':' + db_gas_species[i];
333 mooseInfo(
"ChemicalCompositionAction vapor pressures: 'ALL' specified in input file. Using: ",
338 auto db_gas_species = Thermochimica::getSpeciesInPhase(0);
342 std::vector<std::string> tokens;
344 if (tokens.size() == 1)
346 "No ':' separator found in variable '",
349 if (tokens[1] != Thermochimica::getPhaseNamesSystem()[0])
353 "' of vapor species '",
355 "' is not a gas phase. Cannot calculate vapor pressure.");
356 if (std::find(db_gas_species.begin(), db_gas_species.end(), tokens[2]) ==
357 db_gas_species.end())
361 "' was not found in the gas phase of simulation.");
369 auto element_phases = getParam<std::vector<std::string>>(
"output_element_phases");
370 auto db_phases = Thermochimica::getPhaseNamesSystem();
371 if (element_phases.size() == 1 && element_phases[0] ==
"ALL")
373 element_phases.resize(
_elements.size() * db_phases.size());
383 "ChemicalCompositionAction elements in phase: 'ALL' specified in input file. Using: ",
391 std::vector<std::string> tokens;
393 if (tokens.size() == 1)
395 "No ':' separator found in variable '",
398 if (std::find(db_phases.begin(), db_phases.end(), tokens[1]) == db_phases.end())
404 "' not found in the simulation.");
409 "' was not found in the simulation.");
415 Thermochimica::resetThermoAll();
423 #ifdef THERMOCHIMICA_ENABLED 430 FEType(Utility::string_to_enum<Order>(
_problem->mesh().hasSecondOrderElements() ?
"SECOND" 432 Utility::string_to_enum<libMesh::FEFamily>(
"LAGRANGE")),
433 getParam<bool>(
"is_fv"),
451 _problem->addAuxVariable(aux_var_type,
471 const std::string class_name =
"ConstantIC";
473 params.
set<VariableName>(
"variable") = it.first;
474 params.set<
Real>(
"value") = it.second;
475 _problem->addInitialCondition(class_name, it.first +
"_ic", params);
484 std::string uo_name = getParam<std::string>(
"uo_name");
490 getParam<bool>(
"is_fv") ?
"ThermochimicaElementData" :
"ThermochimicaNodalData";
496 std::back_inserter(uo_params.set<std::vector<VariableName>>(
"elements")));
501 std::back_inserter(uo_params.set<std::vector<VariableName>>(
"output_phases")));
505 std::vector<std::string> species;
508 uo_params.
set<std::vector<VariableName>>(
"output_species")
509 .insert(uo_params.set<std::vector<VariableName>>(
"output_species").end(),
516 std::vector<std::string> element_potentials;
518 element_potentials.push_back(
"mu:" +
token);
519 uo_params.set<std::vector<VariableName>>(
"output_element_potentials")
520 .insert(uo_params.set<std::vector<VariableName>>(
"output_element_potentials").end(),
521 element_potentials.begin(),
522 element_potentials.end());
527 std::vector<std::string> vapor_pressures;
530 uo_params.set<std::vector<VariableName>>(
"output_vapor_pressures")
531 .insert(uo_params.set<std::vector<VariableName>>(
"output_vapor_pressures").end(),
532 vapor_pressures.begin(),
533 vapor_pressures.end());
538 std::vector<std::string> element_phases;
541 uo_params.set<std::vector<VariableName>>(
"output_element_phases")
542 .insert(uo_params.set<std::vector<VariableName>>(
"output_element_phases").end(),
543 element_phases.begin(),
544 element_phases.end());
546 uo_params.set<std::vector<VariableName>>(
"temperature") =
547 getParam<std::vector<VariableName>>(
"temperature");
551 uo_params.set<FileName>(
"thermofile") = getParam<FileName>(
"thermofile");
553 uo_params.set<
MooseEnum>(
"reinit_type") = getParam<MooseEnum>(
"reinitialization_type");
555 uo_params.set<
MooseEnum>(
"output_species_unit") = getParam<MooseEnum>(
"output_species_unit");
559 _problem->addUserObject(uo_type, uo_name, uo_params);
568 const auto & filename = getParam<FileName>(
"initial_values");
569 std::ifstream file(filename.c_str());
571 paramError(
"initial_values",
"Error opening file '", filename,
"'.");
574 std::vector<std::string> items;
577 std::getline(file, line);
578 while (std::getline(file, line))
583 if (items.size() != 2)
584 paramError(
"initial_value",
"Unexpected line in CSV file: ", line);
Store common ChemicalComposition action parameters.
ChemicalCompositionAction(const InputParameters ¶ms)
std::vector< std::pair< std::string, std::string > > _tokenized_phase_elements
registerMooseAction("ChemicalReactionsApp", ChemicalCompositionAction, "add_variable")
void tokenize(const std::string &str, std::vector< T > &elements, unsigned int min_len=1, const std::string &delims="/")
std::vector< std::string > _elements
Element names.
InputParameterWarehouse & getInputParameterWarehouse()
void mooseInfo(Args &&... args) const
InputParameters getValidParams(const std::string &name) const
std::vector< std::pair< std::string, std::string > > _tokenized_species
Tokenized versions of the output variables to avoid redoing tokenization.
const ExecFlagType EXEC_TIMESTEP_END
MooseObjectName uniqueActionName() const
static InputParameters validParams()
ExecFlagEnum getDefaultExecFlagEnum()
bool isParamValid(const std::string &name) const
std::vector< std::string > _phases
List of phases tracked by Thermochimica.
static InputParameters validParams()
const T & getParam(const std::string &name) const
const std::string & _current_task
static std::string variableType(const libMesh::FEType &fe_type, const bool is_fv=false, const bool is_array=false)
void paramError(const std::string ¶m, Args... args) const
std::string stringify(const T &t)
void checkLibraryAvailability(MooseObject &self)
Check if thermochimica is available and throw an error if it is not.
std::map< std::string, Real > _initial_conditions
Initial conditions for each element: [element name] => initial condition value.
std::vector< unsigned int > _element_ids
Atomic numbers of the selected elements.
bool isParamSetByUser(const std::string &nm) const
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::vector< std::pair< std::string, std::string > > _tokenized_vapor_species
IntRange< T > make_range(T beg, T end)
std::shared_ptr< FEProblemBase > & _problem
const InputParameters & parameters() const
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
The ChemicalCompositionAction sets up user objects, aux kernels, and aux variables for a thermochemis...
std::vector< std::string > _tokenized_element_potentials
void addClassDescription(InputParameters ¶ms, const std::string &desc)
Add the supplied class description if thermochimica is available, otherwise add a warning message...
std::vector< const T *> getActions()
auto index_range(const T &sizable)
static InputParameters validParams()
const ExecFlagType EXEC_INITIAL