Cantera  4.0.0a1
Loading...
Searching...
No Matches
ThermoPhase.cpp
Go to the documentation of this file.
1/**
2 * @file ThermoPhase.cpp
3 * Definition file for class ThermoPhase, the base class for phases with
4 * thermodynamic properties
5 * (see class @link Cantera::ThermoPhase ThermoPhase@endlink).
6 */
7
8// This file is part of Cantera. See License.txt in the top-level directory or
9// at https://cantera.org/license.txt for license and copyright information.
10
18#include "cantera/base/global.h"
19
20#include <iomanip>
21#include <fstream>
22#include <numeric>
23
24using namespace std;
25
26namespace Cantera
27{
28
29shared_ptr<ThermoPhase> ThermoPhase::clone() const
30{
31 AnyMap phase = parameters();
33 auto elementDefs = elementDefinitions();
34 if (!elementDefs.empty()) {
35 root["elements"] = std::move(elementDefs);
36 }
37 vector<AnyMap> speciesDefs;
38 speciesDefs.reserve(nSpecies());
39 for (const auto& name : speciesNames()) {
40 speciesDefs.emplace_back(species(name)->parameters(this));
41 }
42 root["species"] = std::move(speciesDefs);
43 phase.applyUnits();
44 root.applyUnits();
45 return newThermo(phase, root);
46}
47
48namespace {
49
50struct PhaseEquilGuard
51{
52 explicit PhaseEquilGuard(ThermoPhase& phase) : m_phase(phase)
53 {
54 m_phase.beginEquilibrate();
55 }
56
57 ~PhaseEquilGuard()
58 {
59 m_phase.endEquilibrate();
60 }
61
62private:
63 ThermoPhase& m_phase;
64};
65
66void checkTemperatureLimits(const string& method, const ThermoPhase& phase)
67{
68 if (phase.temperature() > phase.maxTemp() + 1.0
69 || phase.temperature() < phase.minTemp() - 1.0) {
70 if (phase.temperatureLimitsEnforced()) {
71 throw CanteraError(method,
72 "Temperature ({} K) outside valid range of {} K to {} K",
73 phase.temperature(), phase.minTemp(), phase.maxTemp());
74 }
75 warn_user(method,
76 "Temperature ({} K) outside valid range of {} K to {} K",
77 phase.temperature(), phase.minTemp(), phase.maxTemp());
78 }
79}
80
81} // namespace
82
84 if (k != npos) {
86 } else {
87 for (size_t k = 0; k < nSpecies(); k++) {
89 }
90 }
92}
93
95{
97}
98
100{
101 return m_ssConvention;
102}
103
105{
106 // kmol/m^3 for bulk phases, kmol/m^2 for surface phases, etc.
107 return Units(1.0, 0, -static_cast<double>(nDim()), 0, 0, 0, 1);
108}
109
110double ThermoPhase::logStandardConc(size_t k) const
111{
112 return log(standardConcentration(k));
113}
114
115void ThermoPhase::getActivities(span<double> a) const
116{
118 for (size_t k = 0; k < nSpecies(); k++) {
119 a[k] /= standardConcentration(k);
120 }
121}
122
123void ThermoPhase::getLnActivityCoefficients(span<double> lnac) const
124{
126 for (size_t k = 0; k < m_kk; k++) {
127 lnac[k] = std::log(lnac[k]);
128 }
129}
130
131void ThermoPhase::getElectrochemPotentials(span<double> mu) const
132{
134 double ve = Faraday * electricPotential();
135 for (size_t k = 0; k < m_kk; k++) {
136 mu[k] += ve*charge(k);
137 }
138}
139
141{
142 vector<double> vbar(m_kk);
145 double pi_t = internalPressure();
146 for (size_t k = 0; k < m_kk; k++) {
147 ubar[k] -= pi_t * vbar[k];
148 }
149}
150
151void ThermoPhase::setState_TPX(double t, double p, span<const double> x)
152{
154 setState_TP(t,p);
155}
156
157void ThermoPhase::setState_TPX(double t, double p, const Composition& x)
158{
160 setState_TP(t,p);
161}
162
163void ThermoPhase::setState_TPX(double t, double p, const string& x)
164{
166 setState_TP(t,p);
167}
168
169void ThermoPhase::setState_TPY(double t, double p, span<const double> y)
170{
172 setState_TP(t,p);
173}
174
175void ThermoPhase::setState_TPY(double t, double p, const Composition& y)
176{
178 setState_TP(t,p);
179}
180
181void ThermoPhase::setState_TPY(double t, double p, const string& y)
182{
184 setState_TP(t,p);
185}
186
187void ThermoPhase::setState_TP(double t, double p)
188{
189 vector<double> state(partialStateSize());
190 savePartialState(state);
191 try {
193 setPressure(p);
194 } catch (std::exception&) {
195 restorePartialState(state);
196 throw;
197 }
198}
199
200void ThermoPhase::setState_HP(double Htarget, double p, double rtol)
201{
202 vector<double> state(partialStateSize());
203 savePartialState(state);
204 try {
205 setState_HPorUV(Htarget, p, rtol, false);
206 } catch (std::exception&) {
207 restorePartialState(state);
208 throw;
209 }
210}
211
212void ThermoPhase::setState_UV(double u, double v, double rtol)
213{
214 assertCompressible("setState_UV");
215 vector<double> state(partialStateSize());
216 savePartialState(state);
217 try {
218 setState_HPorUV(u, v, rtol, true);
219 } catch (std::exception&) {
220 restorePartialState(state);
221 throw;
222 }
223}
224
225void ThermoPhase::setState(const AnyMap& input_state)
226{
227 AnyMap state = input_state;
228
229 // Remap allowable synonyms
230 if (state.hasKey("mass-fractions")) {
231 state["Y"] = state["mass-fractions"];
232 state.erase("mass-fractions");
233 }
234 if (state.hasKey("mole-fractions")) {
235 state["X"] = state["mole-fractions"];
236 state.erase("mole-fractions");
237 }
238 if (state.hasKey("temperature")) {
239 state["T"] = state["temperature"];
240 }
241 if (state.hasKey("pressure")) {
242 state["P"] = state["pressure"];
243 }
244 if (state.hasKey("enthalpy")) {
245 state["H"] = state["enthalpy"];
246 }
247 if (state.hasKey("int-energy")) {
248 state["U"] = state["int-energy"];
249 }
250 if (state.hasKey("internal-energy")) {
251 state["U"] = state["internal-energy"];
252 }
253 if (state.hasKey("specific-volume")) {
254 state["V"] = state["specific-volume"];
255 }
256 if (state.hasKey("entropy")) {
257 state["S"] = state["entropy"];
258 }
259 if (state.hasKey("density")) {
260 state["D"] = state["density"];
261 }
262 if (state.hasKey("vapor-fraction")) {
263 state["Q"] = state["vapor-fraction"];
264 }
265
266 // Set composition
267 if (state.hasKey("X")) {
268 if (state["X"].is<string>()) {
269 setMoleFractionsByName(state["X"].asString());
270 } else {
271 setMoleFractionsByName(state["X"].asMap<double>());
272 }
273 state.erase("X");
274 } else if (state.hasKey("Y")) {
275 if (state["Y"].is<string>()) {
276 setMassFractionsByName(state["Y"].asString());
277 } else {
278 setMassFractionsByName(state["Y"].asMap<double>());
279 }
280 state.erase("Y");
281 }
282 // set thermodynamic state using whichever property set is found
283 if (state.size() == 0) {
284 setState_TP(298.15, OneAtm);
285 } else if (state.hasKey("T") && state.hasKey("P")) {
286 double T = state.convert("T", "K");
287 double P = state.convert("P", "Pa");
288 if (state.hasKey("Q")) {
289 setState_TPQ(T, P, state["Q"].asDouble());
290 } else {
291 setState_TP(T, P);
292 }
293 } else if (state.hasKey("T") && state.hasKey("D")) {
294 setState_TD(state.convert("T", "K"), state.convert("D", "kg/m^3"));
295 } else if (state.hasKey("T") && state.hasKey("V")) {
296 setState_TV(state.convert("T", "K"), state.convert("V", "m^3/kg"));
297 } else if (state.hasKey("H") && state.hasKey("P")) {
298 setState_HP(state.convert("H", "J/kg"), state.convert("P", "Pa"));
299 } else if (state.hasKey("U") && state.hasKey("V")) {
300 setState_UV(state.convert("U", "J/kg"), state.convert("V", "m^3/kg"));
301 } else if (state.hasKey("S") && state.hasKey("P")) {
302 setState_SP(state.convert("S", "J/kg/K"), state.convert("P", "Pa"));
303 } else if (state.hasKey("S") && state.hasKey("V")) {
304 setState_SV(state.convert("S", "J/kg/K"), state.convert("V", "m^3/kg"));
305 } else if (state.hasKey("S") && state.hasKey("T")) {
306 setState_ST(state.convert("S", "J/kg/K"), state.convert("T", "K"));
307 } else if (state.hasKey("P") && state.hasKey("V")) {
308 setState_PV(state.convert("P", "Pa"), state.convert("V", "m^3/kg"));
309 } else if (state.hasKey("U") && state.hasKey("P")) {
310 setState_UP(state.convert("U", "J/kg"), state.convert("P", "Pa"));
311 } else if (state.hasKey("V") && state.hasKey("H")) {
312 setState_VH(state.convert("V", "m^3/kg"), state.convert("H", "J/kg"));
313 } else if (state.hasKey("T") && state.hasKey("H")) {
314 setState_TH(state.convert("T", "K"), state.convert("H", "J/kg"));
315 } else if (state.hasKey("S") && state.hasKey("H")) {
316 setState_SH(state.convert("S", "J/kg/K"), state.convert("H", "J/kg"));
317 } else if (state.hasKey("D") && state.hasKey("P")) {
318 setState_DP(state.convert("D", "kg/m^3"), state.convert("P", "Pa"));
319 } else if (state.hasKey("P") && state.hasKey("Q")) {
320 setState_Psat(state.convert("P", "Pa"), state["Q"].asDouble());
321 } else if (state.hasKey("T") && state.hasKey("Q")) {
322 setState_Tsat(state.convert("T", "K"), state["Q"].asDouble());
323 } else if (state.hasKey("T")) {
324 setState_TP(state.convert("T", "K"), OneAtm);
325 } else if (state.hasKey("P")) {
326 setState_TP(298.15, state.convert("P", "Pa"));
327 } else {
328 throw CanteraError("ThermoPhase::setState",
329 "'state' did not specify a recognized set of properties.\n"
330 "Keys provided were: {}", input_state.keys_str());
331 }
332}
333
334void ThermoPhase::setState_conditional_TP(double t, double p, bool set_p)
335{
337 if (set_p) {
338 setPressure(p);
339 }
340}
341
342void ThermoPhase::setState_HPorUV(double Htarget, double p,
343 double rtol, bool doUV)
344{
345 double dt;
346 double v = 0.0;
347
348 // Assign the specific volume or pressure and make sure it's positive
349 if (doUV) {
350 v = p;
351 if (v < 1.0E-300) {
352 throw CanteraError("ThermoPhase::setState_HPorUV (UV)",
353 "Input specific volume is too small or negative. v = {}", v);
354 }
355 setDensity(1.0/v);
356 } else {
357 if (p < 1.0E-300) {
358 throw CanteraError("ThermoPhase::setState_HPorUV (HP)",
359 "Input pressure is too small or negative. p = {}", p);
360 }
361 setPressure(p);
362 }
363 double Tmax = temperatureLimitsEnforced() ? maxTemp() + 0.1 :
364 std::max(maxTemp() + 1000.0, 10.0 * maxTemp());
365 double Tmin = temperatureLimitsEnforced() ? minTemp() - 0.1
366 : clip(minTemp(), SmallNumber, 100.0);
367 if (Tmax <= Tmin) {
368 Tmax = Tmin + 20.0;
369 }
370
371 // Make sure we are within the temperature bounds at the start
372 // of the iteration
373 double Tnew = temperature();
374 double Tinit = Tnew;
375 if (Tnew > Tmax) {
376 Tnew = Tmax - 1.0;
377 } else if (Tnew < Tmin) {
378 Tnew = Tmin + 1.0;
379 }
380 if (Tnew != Tinit) {
381 setState_conditional_TP(Tnew, p, !doUV);
382 }
383
384 double Hnew = (doUV) ? intEnergy_mass() : enthalpy_mass();
385 double Cpnew = (doUV) ? cv_mass() : cp_mass();
386 double Htop = Hnew;
387 double Ttop = Tnew;
388 double Hbot = Hnew;
389 double Tbot = Tnew;
390
391 bool ignoreBounds = false;
392 // Unstable phases are those for which cp < 0.0. These are possible for
393 // cases where we have passed the spinodal curve.
394 bool unstablePhase = false;
395 // Counter indicating the last temperature point where the
396 // phase was unstable
397 double Tunstable = -1.0;
398 bool unstablePhaseNew = false;
399
400 // Newton iteration
401 for (int n = 0; n < 500; n++) {
402 double Told = Tnew;
403 double Hold = Hnew;
404 double cpd = Cpnew;
405 if (cpd < 0.0) {
406 unstablePhase = true;
407 Tunstable = Tnew;
408 }
409 // limit step size to 100 K
410 dt = clip((Htarget - Hold)/cpd, -100.0, 100.0);
411
412 // Calculate the new T
413 Tnew = Told + dt;
414
415 // Limit the step size so that we are convergent This is the step that
416 // makes it different from a Newton's algorithm
417 if ((dt > 0.0 && unstablePhase) || (dt <= 0.0 && !unstablePhase)) {
418 if (Hbot < Htarget && Tnew < (0.75 * Tbot + 0.25 * Told)) {
419 dt = 0.75 * (Tbot - Told);
420 Tnew = Told + dt;
421 }
422 } else if (Htop > Htarget && Tnew > (0.75 * Ttop + 0.25 * Told)) {
423 dt = 0.75 * (Ttop - Told);
424 Tnew = Told + dt;
425 }
426
427 // Check Max and Min values
428 if (Tnew > Tmax && !ignoreBounds) {
429 setState_conditional_TP(Tmax, p, !doUV);
430 double Hmax = (doUV) ? intEnergy_mass() : enthalpy_mass();
431 if (Hmax >= Htarget) {
432 if (Htop < Htarget) {
433 Ttop = Tmax;
434 Htop = Hmax;
435 }
436 Tnew = Tmax;
437 dt = Tnew - Told;
438 } else {
440 throw CanteraError("ThermoPhase::setState_HPorUV",
441 "Target {} = {} cannot be reached within the temperature "
442 "bounds of {} K to {} K.",
443 doUV ? "internal energy" : "enthalpy", Htarget, Tmin, Tmax);
444 }
445 Tnew = Tmax + 1.0;
446 ignoreBounds = true;
447 }
448 }
449 if (Tnew < Tmin && !ignoreBounds) {
450 setState_conditional_TP(Tmin, p, !doUV);
451 double Hmin = (doUV) ? intEnergy_mass() : enthalpy_mass();
452 if (Hmin <= Htarget) {
453 if (Hbot > Htarget) {
454 Tbot = Tmin;
455 Hbot = Hmin;
456 }
457 Tnew = Tmin;
458 dt = Tnew - Told;
459 } else {
461 throw CanteraError("ThermoPhase::setState_HPorUV",
462 "Target {} = {} cannot be reached within the temperature "
463 "bounds of {} K to {} K.",
464 doUV ? "internal energy" : "enthalpy", Htarget, Tmin, Tmax);
465 }
466 Tnew = Tmin - 1.0;
467 ignoreBounds = true;
468 }
469 }
470
471 // Try to keep phase within its region of stability
472 // -> Could do a lot better if I calculate the
473 // spinodal value of H.
474 for (int its = 0; its < 10; its++) {
475 Tnew = Told + dt;
476 if (Tnew < Told / 3.0) {
477 Tnew = Told / 3.0;
478 dt = -2.0 * Told / 3.0;
479 }
480 setState_conditional_TP(Tnew, p, !doUV);
481 if (doUV) {
482 Hnew = intEnergy_mass();
483 Cpnew = cv_mass();
484 } else {
485 Hnew = enthalpy_mass();
486 Cpnew = cp_mass();
487 }
488 if (Cpnew < 0.0) {
489 unstablePhaseNew = true;
490 Tunstable = Tnew;
491 } else {
492 unstablePhaseNew = false;
493 break;
494 }
495 if (unstablePhase == false && unstablePhaseNew == true) {
496 dt *= 0.25;
497 }
498 }
499
500 if (Hnew == Htarget) {
501 checkTemperatureLimits(
502 doUV ? "ThermoPhase::setState_UV" : "ThermoPhase::setState_HP",
503 *this);
504 return;
505 } else if (Hnew > Htarget && (Htop < Htarget || Hnew < Htop)) {
506 Htop = Hnew;
507 Ttop = Tnew;
508 } else if (Hnew < Htarget && (Hbot > Htarget || Hnew > Hbot)) {
509 Hbot = Hnew;
510 Tbot = Tnew;
511 }
512 // Convergence in H
513 double Herr = Htarget - Hnew;
514 double acpd = std::max(fabs(cpd), 1.0E-5);
515 double denom = std::max(fabs(Htarget), acpd * Tnew);
516 double HConvErr = fabs((Herr)/denom);
517 if (HConvErr < rtol || fabs(dt/Tnew) < rtol) {
518 checkTemperatureLimits(
519 doUV ? "ThermoPhase::setState_UV" : "ThermoPhase::setState_HP",
520 *this);
521 return;
522 }
523 }
524 // We are here when there hasn't been convergence
525
526 // Formulate a detailed error message, since questions seem to arise often
527 // about the lack of convergence.
528 string ErrString = "No convergence in 500 iterations\n";
529 if (doUV) {
530 ErrString += fmt::format(
531 "\tTarget Internal Energy = {}\n"
532 "\tCurrent Specific Volume = {}\n"
533 "\tStarting Temperature = {}\n"
534 "\tCurrent Temperature = {}\n"
535 "\tCurrent Internal Energy = {}\n"
536 "\tCurrent Delta T = {}\n",
537 Htarget, v, Tinit, Tnew, Hnew, dt);
538 } else {
539 ErrString += fmt::format(
540 "\tTarget Enthalpy = {}\n"
541 "\tCurrent Pressure = {}\n"
542 "\tStarting Temperature = {}\n"
543 "\tCurrent Temperature = {}\n"
544 "\tCurrent Enthalpy = {}\n"
545 "\tCurrent Delta T = {}\n",
546 Htarget, p, Tinit, Tnew, Hnew, dt);
547 }
548 if (unstablePhase) {
549 ErrString += fmt::format(
550 "\t - The phase became unstable (Cp < 0) T_unstable_last = {}\n",
551 Tunstable);
552 }
553 if (doUV) {
554 throw CanteraError("ThermoPhase::setState_HPorUV (UV)", ErrString);
555 } else {
556 throw CanteraError("ThermoPhase::setState_HPorUV (HP)", ErrString);
557 }
558}
559
560void ThermoPhase::setState_SP(double Starget, double p, double rtol)
561{
562 vector<double> state(partialStateSize());
563 savePartialState(state);
564 try {
565 setState_SPorSV(Starget, p, rtol, false);
566 } catch (std::exception&) {
567 restorePartialState(state);
568 throw;
569 }
570}
571
572void ThermoPhase::setState_SV(double Starget, double v, double rtol)
573{
574 assertCompressible("setState_SV");
575 vector<double> state(partialStateSize());
576 savePartialState(state);
577 try {
578 setState_SPorSV(Starget, v, rtol, true);
579 } catch (std::exception&) {
580 restorePartialState(state);
581 throw;
582 }
583}
584
585void ThermoPhase::setState_SPorSV(double Starget, double p,
586 double rtol, bool doSV)
587{
588 double v = 0.0;
589 double dt;
590 if (doSV) {
591 v = p;
592 if (v < 1.0E-300) {
593 throw CanteraError("ThermoPhase::setState_SPorSV (SV)",
594 "Input specific volume is too small or negative. v = {}", v);
595 }
596 setDensity(1.0/v);
597 } else {
598 if (p < 1.0E-300) {
599 throw CanteraError("ThermoPhase::setState_SPorSV (SP)",
600 "Input pressure is too small or negative. p = {}", p);
601 }
602 setPressure(p);
603 }
604 double Tmax = temperatureLimitsEnforced() ? maxTemp() + 0.1 :
605 std::max(maxTemp() + 1000.0, 10.0 * maxTemp());
606 double Tmin = temperatureLimitsEnforced() ? minTemp() - 0.1
607 : clip(minTemp(), SmallNumber, 100.0);
608 if (Tmax <= Tmin) {
609 Tmax = Tmin + 20.0;
610 }
611
612 // Make sure we are within the temperature bounds at the start
613 // of the iteration
614 double Tnew = temperature();
615 double Tinit = Tnew;
616 if (Tnew > Tmax) {
617 Tnew = Tmax - 1.0;
618 } else if (Tnew < Tmin) {
619 Tnew = Tmin + 1.0;
620 }
621 if (Tnew != Tinit) {
622 setState_conditional_TP(Tnew, p, !doSV);
623 }
624
625 double Snew = entropy_mass();
626 double Cpnew = (doSV) ? cv_mass() : cp_mass();
627 double Stop = Snew;
628 double Ttop = Tnew;
629 double Sbot = Snew;
630 double Tbot = Tnew;
631
632 bool ignoreBounds = false;
633 // Unstable phases are those for which Cp < 0.0. These are possible for
634 // cases where we have passed the spinodal curve.
635 bool unstablePhase = false;
636 double Tunstable = -1.0;
637 bool unstablePhaseNew = false;
638
639 // Newton iteration
640 for (int n = 0; n < 500; n++) {
641 double Told = Tnew;
642 double Sold = Snew;
643 double cpd = Cpnew;
644 if (cpd < 0.0) {
645 unstablePhase = true;
646 Tunstable = Tnew;
647 }
648 // limit step size to 100 K
649 dt = clip((Starget - Sold)*Told/cpd, -100.0, 100.0);
650 Tnew = Told + dt;
651
652 // Limit the step size so that we are convergent
653 if ((dt > 0.0 && unstablePhase) || (dt <= 0.0 && !unstablePhase)) {
654 if (Sbot < Starget && Tnew < Tbot) {
655 dt = 0.75 * (Tbot - Told);
656 Tnew = Told + dt;
657 }
658 } else if (Stop > Starget && Tnew > Ttop) {
659 dt = 0.75 * (Ttop - Told);
660 Tnew = Told + dt;
661 }
662
663 // Check Max and Min values
664 if (Tnew > Tmax && !ignoreBounds) {
665 setState_conditional_TP(Tmax, p, !doSV);
666 double Smax = entropy_mass();
667 if (Smax >= Starget) {
668 if (Stop < Starget) {
669 Ttop = Tmax;
670 Stop = Smax;
671 }
672 Tnew = Tmax;
673 dt = Tnew - Told;
674 } else {
676 throw CanteraError("ThermoPhase::setState_SPorSV",
677 "Target entropy = {} cannot be reached within the temperature "
678 "bounds of {} K to {} K.", Starget, Tmin, Tmax);
679 }
680 Tnew = Tmax + 1.0;
681 ignoreBounds = true;
682 }
683 } else if (Tnew < Tmin && !ignoreBounds) {
684 setState_conditional_TP(Tmin, p, !doSV);
685 double Smin = entropy_mass();
686 if (Smin <= Starget) {
687 if (Sbot > Starget) {
688 Tbot = Tmin;
689 Sbot = Smin;
690 }
691 Tnew = Tmin;
692 dt = Tnew - Told;
693 } else {
695 throw CanteraError("ThermoPhase::setState_SPorSV",
696 "Target entropy = {} cannot be reached within the temperature "
697 "bounds of {} K to {} K.", Starget, Tmin, Tmax);
698 }
699 Tnew = Tmin - 1.0;
700 ignoreBounds = true;
701 }
702 }
703
704 // Try to keep phase within its region of stability
705 // -> Could do a lot better if I calculate the
706 // spinodal value of H.
707 for (int its = 0; its < 10; its++) {
708 Tnew = Told + dt;
709 setState_conditional_TP(Tnew, p, !doSV);
710 Cpnew = (doSV) ? cv_mass() : cp_mass();
711 Snew = entropy_mass();
712 if (Cpnew < 0.0) {
713 unstablePhaseNew = true;
714 Tunstable = Tnew;
715 } else {
716 unstablePhaseNew = false;
717 break;
718 }
719 if (unstablePhase == false && unstablePhaseNew == true) {
720 dt *= 0.25;
721 }
722 }
723
724 if (Snew == Starget) {
725 checkTemperatureLimits(
726 doSV ? "ThermoPhase::setState_SV" : "ThermoPhase::setState_SP",
727 *this);
728 return;
729 } else if (Snew > Starget && (Stop < Starget || Snew < Stop)) {
730 Stop = Snew;
731 Ttop = Tnew;
732 } else if (Snew < Starget && (Sbot > Starget || Snew > Sbot)) {
733 Sbot = Snew;
734 Tbot = Tnew;
735 }
736 // Convergence in S
737 double Serr = Starget - Snew;
738 double acpd = std::max(fabs(cpd), 1.0E-5);
739 double denom = std::max(fabs(Starget), acpd * Tnew);
740 double SConvErr = fabs((Serr * Tnew)/denom);
741 if (SConvErr < rtol || fabs(dt/Tnew) < rtol) {
742 checkTemperatureLimits(
743 doSV ? "ThermoPhase::setState_SV" : "ThermoPhase::setState_SP",
744 *this);
745 return;
746 }
747 }
748 // We are here when there hasn't been convergence
749
750 // Formulate a detailed error message, since questions seem to arise often
751 // about the lack of convergence.
752 string ErrString = "No convergence in 500 iterations\n";
753 if (doSV) {
754 ErrString += fmt::format(
755 "\tTarget Entropy = {}\n"
756 "\tCurrent Specific Volume = {}\n"
757 "\tStarting Temperature = {}\n"
758 "\tCurrent Temperature = {}\n"
759 "\tCurrent Entropy = {}\n"
760 "\tCurrent Delta T = {}\n",
761 Starget, v, Tinit, Tnew, Snew, dt);
762 } else {
763 ErrString += fmt::format(
764 "\tTarget Entropy = {}\n"
765 "\tCurrent Pressure = {}\n"
766 "\tStarting Temperature = {}\n"
767 "\tCurrent Temperature = {}\n"
768 "\tCurrent Entropy = {}\n"
769 "\tCurrent Delta T = {}\n",
770 Starget, p, Tinit, Tnew, Snew, dt);
771 }
772 if (unstablePhase) {
773 ErrString += fmt::format("\t - The phase became unstable (Cp < 0) T_unstable_last = {}\n",
774 Tunstable);
775 }
776 if (doSV) {
777 throw CanteraError("ThermoPhase::setState_SPorSV (SV)", ErrString);
778 } else {
779 throw CanteraError("ThermoPhase::setState_SPorSV (SP)", ErrString);
780 }
781}
782
783double ThermoPhase::o2Required(span<const double> y) const
784{
785 checkArraySize("ThermoPhase::o2Required", y.size(), m_kk);
786 // indices of fuel elements
787 size_t iC = elementIndex("C", false);
788 size_t iS = elementIndex("S", false);
789 size_t iH = elementIndex("H", false);
790
791 double o2req = 0.0;
792 double sum = 0.0;
793 for (size_t k = 0; k != m_kk; ++k) {
794 sum += y[k];
795 double x = y[k] / molecularWeights()[k];
796 if (iC != npos) {
797 o2req += x * nAtoms(k, iC);
798 }
799 if (iS != npos) {
800 o2req += x * nAtoms(k, iS);
801 }
802 if (iH != npos) {
803 o2req += x * 0.25 * nAtoms(k, iH);
804 }
805 }
806 if (sum == 0.0) {
807 throw CanteraError("ThermoPhase::o2Required",
808 "No composition specified");
809 }
810 return o2req/sum;
811}
812
813double ThermoPhase::o2Present(span<const double> y) const
814{
815 checkArraySize("ThermoPhase::o2Present", y.size(), m_kk);
816 size_t iO = elementIndex("O", true);
817 double o2pres = 0.0;
818 double sum = 0.0;
819 for (size_t k = 0; k != m_kk; ++k) {
820 sum += y[k];
821 o2pres += y[k] / molecularWeights()[k] * nAtoms(k, iO);
822 }
823 if (sum == 0.0) {
824 throw CanteraError("ThermoPhase::o2Present",
825 "No composition specified");
826 }
827 return 0.5 * o2pres / sum;
828}
829
831 const Composition& oxComp,
832 ThermoBasis basis) const
833{
834 vector<double> fuel(getCompositionFromMap(fuelComp));
835 vector<double> ox(getCompositionFromMap(oxComp));
836 return stoichAirFuelRatio(fuel, ox, basis);
837}
838
839double ThermoPhase::stoichAirFuelRatio(const string& fuelComp, const string& oxComp,
840 ThermoBasis basis) const
841{
842 return stoichAirFuelRatio(
843 parseCompString(fuelComp.find(":") != string::npos ? fuelComp : fuelComp+":1.0"),
844 parseCompString(oxComp.find(":") != string::npos ? oxComp : oxComp+":1.0"),
845 basis);
846}
847
848double ThermoPhase::stoichAirFuelRatio(span<const double> fuelComp,
849 span<const double> oxComp,
850 ThermoBasis basis) const
851{
852 vector<double> fuel, ox;
853 if (basis == ThermoBasis::molar) { // convert input compositions to mass fractions
854 fuel.resize(m_kk);
855 ox.resize(m_kk);
856 moleFractionsToMassFractions(fuelComp, fuel);
858 fuelComp = fuel;
859 oxComp = ox;
860 }
861
862 double o2_required_fuel = o2Required(fuelComp) - o2Present(fuelComp);
863 double o2_required_ox = o2Required(oxComp) - o2Present(oxComp);
864
865 if (o2_required_fuel < 0.0 || o2_required_ox > 0.0) {
866 throw CanteraError("ThermoPhase::stoichAirFuelRatio",
867 "Fuel composition contains too much oxygen or "
868 "oxidizer contains not enough oxygen. "
869 "Fuel and oxidizer composition mixed up?");
870 }
871
872 if (o2_required_ox == 0.0) {
873 return std::numeric_limits<double>::infinity();
874 }
875
876 return o2_required_fuel / (-o2_required_ox);
877}
878
879void ThermoPhase::setEquivalenceRatio(double phi, span<const double> fuelComp,
880 span<const double> oxComp, ThermoBasis basis)
881{
882 if (phi < 0.0) {
883 throw CanteraError("ThermoPhase::setEquivalenceRatio",
884 "Equivalence ratio phi must be >= 0");
885 }
886
887 double p = pressure();
888
889 vector<double> fuel, ox;
890 if (basis == ThermoBasis::molar) { // convert input compositions to mass fractions
891 fuel.resize(m_kk);
892 ox.resize(m_kk);
893 moleFractionsToMassFractions(fuelComp, fuel);
895 fuelComp = fuel;
896 oxComp = ox;
897 }
898
899 double AFR_st = stoichAirFuelRatio(fuelComp, oxComp, ThermoBasis::mass);
900 double sum_f = std::accumulate(fuelComp.begin(), fuelComp.end(), 0.0);
901 double sum_o = std::accumulate(oxComp.begin(), oxComp.end(), 0.0);
902
903 vector<double> y(m_kk);
904 for (size_t k = 0; k != m_kk; ++k) {
905 y[k] = phi * fuelComp[k]/sum_f + AFR_st * oxComp[k]/sum_o;
906 }
907
909 setPressure(p);
910}
911
912void ThermoPhase::setEquivalenceRatio(double phi, const string& fuelComp,
913 const string& oxComp, ThermoBasis basis)
914{
916 parseCompString(fuelComp.find(":") != string::npos ? fuelComp : fuelComp+":1.0"),
917 parseCompString(oxComp.find(":") != string::npos ? oxComp : oxComp+":1.0"),
918 basis);
919}
920
921void ThermoPhase::setEquivalenceRatio(double phi, const Composition& fuelComp,
922 const Composition& oxComp, ThermoBasis basis)
923{
924 vector<double> fuel = getCompositionFromMap(fuelComp);
925 vector<double> ox = getCompositionFromMap(oxComp);
926 setEquivalenceRatio(phi, fuel, ox, basis);
927}
928
930{
931 span<const double> y = massFractions();
932 double o2_required = o2Required(y);
933 double o2_present = o2Present(y);
934
935 if (o2_present == 0.0) { // pure fuel
936 return std::numeric_limits<double>::infinity();
937 }
938
939 return o2_required / o2_present;
940}
941
943 const Composition& oxComp,
944 ThermoBasis basis) const
945{
946 vector<double> fuel(getCompositionFromMap(fuelComp));
947 vector<double> ox(getCompositionFromMap(oxComp));
948 return equivalenceRatio(fuel, ox, basis);
949}
950
951double ThermoPhase::equivalenceRatio(const string& fuelComp, const string& oxComp,
952 ThermoBasis basis) const
953{
954 return equivalenceRatio(
955 parseCompString(fuelComp.find(":") != string::npos ? fuelComp : fuelComp+":1.0"),
956 parseCompString(oxComp.find(":") != string::npos ? oxComp : oxComp+":1.0"),
957 basis);
958}
959
960double ThermoPhase::equivalenceRatio(span<const double> fuelComp,
961 span<const double> oxComp, ThermoBasis basis) const
962{
963 double Z = mixtureFraction(fuelComp, oxComp, basis);
964
965 if (Z == 0.0) {
966 return 0.0; // pure oxidizer
967 }
968
969 if (Z == 1.0) {
970 return std::numeric_limits<double>::infinity(); // pure fuel
971 }
972
973 vector<double> fuel, ox;
974 if (basis == ThermoBasis::molar) { // convert input compositions to mass fractions
975 fuel.resize(m_kk);
976 ox.resize(m_kk);
977 moleFractionsToMassFractions(fuelComp, fuel);
979 fuelComp = fuel;
980 oxComp = ox;
981 }
982
983 double AFR_st = stoichAirFuelRatio(fuelComp, oxComp, ThermoBasis::mass);
984
985 return std::max(Z / (1.0 - Z) * AFR_st, 0.0);
986}
987
988void ThermoPhase::setMixtureFraction(double mixFrac, const Composition& fuelComp,
989 const Composition& oxComp, ThermoBasis basis)
990{
991 vector<double> fuel(getCompositionFromMap(fuelComp));
992 vector<double> ox(getCompositionFromMap(oxComp));
993 setMixtureFraction(mixFrac, fuel, ox, basis);
994}
995
996void ThermoPhase::setMixtureFraction(double mixFrac, const string& fuelComp,
997 const string& oxComp, ThermoBasis basis)
998{
999 setMixtureFraction(mixFrac,
1000 parseCompString(fuelComp.find(":") != string::npos ? fuelComp : fuelComp+":1.0"),
1001 parseCompString(oxComp.find(":") != string::npos ? oxComp : oxComp+":1.0"),
1002 basis);
1003}
1004
1005void ThermoPhase::setMixtureFraction(double mixFrac, span<const double> fuelComp,
1006 span<const double> oxComp, ThermoBasis basis)
1007{
1008 checkArraySize("ThermoPhase::setMixtureFraction", fuelComp.size(), m_kk);
1009 checkArraySize("ThermoPhase::setMixtureFraction", oxComp.size(), m_kk);
1010 if (mixFrac < 0.0 || mixFrac > 1.0) {
1011 throw CanteraError("ThermoPhase::setMixtureFraction",
1012 "Mixture fraction must be between 0 and 1");
1013 }
1014
1015 vector<double> fuel, ox;
1016 if (basis == ThermoBasis::molar) { // convert input compositions to mass fractions
1017 fuel.resize(m_kk);
1018 ox.resize(m_kk);
1019 moleFractionsToMassFractions(fuelComp, fuel);
1020 moleFractionsToMassFractions(oxComp, ox);
1021 fuelComp = fuel;
1022 oxComp = ox;
1023 }
1024
1025 double sum_yf = std::accumulate(fuelComp.begin(), fuelComp.end(), 0.0);
1026 double sum_yo = std::accumulate(oxComp.begin(), oxComp.end(), 0.0);
1027
1028 if (sum_yf == 0.0 || sum_yo == 0.0) {
1029 throw CanteraError("ThermoPhase::setMixtureFraction",
1030 "No fuel and/or oxidizer composition specified");
1031 }
1032
1033 double p = pressure();
1034
1035 vector<double> y(m_kk);
1036
1037 for (size_t k = 0; k != m_kk; ++k) {
1038 y[k] = mixFrac * fuelComp[k]/sum_yf + (1.0-mixFrac) * oxComp[k]/sum_yo;
1039 }
1040
1042 setPressure(p);
1043}
1044
1046 const Composition& oxComp,
1047 ThermoBasis basis,
1048 const string& element) const
1049{
1050 vector<double> fuel(getCompositionFromMap(fuelComp));
1051 vector<double> ox(getCompositionFromMap(oxComp));
1052 return mixtureFraction(fuel, ox, basis, element);
1053}
1054
1055double ThermoPhase::mixtureFraction(const string& fuelComp, const string& oxComp,
1056 ThermoBasis basis, const string& element) const
1057{
1058 return mixtureFraction(
1059 parseCompString(fuelComp.find(":") != string::npos ? fuelComp : fuelComp+":1.0"),
1060 parseCompString(oxComp.find(":") != string::npos ? oxComp : oxComp+":1.0"),
1061 basis, element);
1062}
1063
1064double ThermoPhase::mixtureFraction(span<const double> fuelComp,
1065 span<const double> oxComp,
1066 ThermoBasis basis, const string& element) const
1067{
1068 checkArraySize("ThermoPhase::mixtureFraction", fuelComp.size(), m_kk);
1069 checkArraySize("ThermoPhase::mixtureFraction", oxComp.size(), m_kk);
1070 vector<double> fuel, ox;
1071 if (basis == ThermoBasis::molar) { // convert input compositions to mass fractions
1072 fuel.resize(m_kk);
1073 ox.resize(m_kk);
1074 moleFractionsToMassFractions(fuelComp, fuel);
1075 moleFractionsToMassFractions(oxComp, ox);
1076 fuelComp = fuel;
1077 oxComp = ox;
1078 }
1079
1080 if (element == "Bilger") // compute the mixture fraction based on the Bilger mixture fraction
1081 {
1082 double o2_required_fuel = o2Required(fuelComp) - o2Present(fuelComp);
1083 double o2_required_ox = o2Required(oxComp) - o2Present(oxComp);
1084 span<const double> y = massFractions();
1085 double o2_required_mix = o2Required(y) - o2Present(y);
1086
1087 if (o2_required_fuel < 0.0 || o2_required_ox > 0.0) {
1088 throw CanteraError("ThermoPhase::mixtureFraction",
1089 "Fuel composition contains too much oxygen or "
1090 "oxidizer contains not enough oxygen. "
1091 "Fuel and oxidizer composition mixed up?");
1092 }
1093
1094 double denominator = o2_required_fuel - o2_required_ox;
1095
1096 if (denominator == 0.0) {
1097 throw CanteraError("ThermoPhase::mixtureFraction",
1098 "Fuel and oxidizer have the same composition");
1099 }
1100
1101 double Z = (o2_required_mix - o2_required_ox) / denominator;
1102
1103 return std::min(std::max(Z, 0.0), 1.0);
1104 } else {
1105 // compute the mixture fraction from a single element
1106 double sum_yf = std::accumulate(fuelComp.begin(), fuelComp.end(), 0.0);
1107 double sum_yo = std::accumulate(oxComp.begin(), oxComp.end(), 0.0);
1108
1109 if (sum_yf == 0.0 || sum_yo == 0.0) {
1110 throw CanteraError("ThermoPhase::mixtureFraction",
1111 "No fuel and/or oxidizer composition specified");
1112 }
1113
1114 auto elementalFraction = [this](size_t m, span<const double> y) {
1115 double Z_m = 0.0;
1116 for (size_t k = 0; k != m_kk; ++k) {
1117 Z_m += y[k] / molecularWeight(k) * nAtoms(k, m);
1118 }
1119 return Z_m;
1120 };
1121
1122 size_t m = elementIndex(element, true);
1123 double Z_m_fuel = elementalFraction(m, fuelComp)/sum_yf;
1124 double Z_m_ox = elementalFraction(m, oxComp)/sum_yo;
1125 double Z_m_mix = elementalFraction(m, massFractions());
1126
1127 if (Z_m_fuel == Z_m_ox) {
1128 throw CanteraError("ThermoPhase::mixtureFraction",
1129 "Fuel and oxidizer have the same composition for element {}",
1130 element);
1131 }
1132 double Z = (Z_m_mix - Z_m_ox) / (Z_m_fuel - Z_m_ox);
1133 return std::min(std::max(Z, 0.0), 1.0);
1134 }
1135}
1136
1138{
1139 return m_spthermo;
1140}
1141
1143{
1144 return m_spthermo;
1145}
1146
1147
1148void ThermoPhase::initThermoFile(const string& inputFile, const string& id)
1149{
1150 if (inputFile.empty()) {
1151 // No input file specified - nothing to set up
1152 return;
1153 }
1154 size_t dot = inputFile.find_last_of(".");
1155 string extension;
1156 if (dot != npos) {
1157 extension = inputFile.substr(dot+1);
1158 }
1159 if (extension == "xml" || extension == "cti") {
1160 throw CanteraError("ThermoPhase::initThermoFile",
1161 "The CTI and XML formats are no longer supported.");
1162 }
1163
1164 AnyMap root = AnyMap::fromYamlFile(inputFile);
1165 auto& phase = root["phases"].getMapWhere("name", id);
1166 setupPhase(*this, phase, root);
1167}
1168
1170{
1171 // Check to see that all of the species thermo objects have been initialized
1172 if (!m_spthermo.ready(m_kk)) {
1173 throw CanteraError("ThermoPhase::initThermo()",
1174 "Missing species thermo data");
1175 }
1176}
1177
1178void ThermoPhase::setState_TPQ(double T, double P, double Q)
1179{
1180 if (T > critTemperature()) {
1181 if (P > critPressure() || Q == 1) {
1182 setState_TP(T, P);
1183 return;
1184 } else {
1185 throw CanteraError("ThermoPhase::setState_TPQ",
1186 "Temperature ({}), pressure ({}) and vapor fraction ({}) "
1187 "are inconsistent, above the critical temperature.",
1188 T, P, Q);
1189 }
1190 }
1191
1192 double Psat = satPressure(T);
1193 if (std::abs(Psat / P - 1) < 1e-6) {
1194 setState_Tsat(T, Q);
1195 } else if ((Q == 0 && P >= Psat) || (Q == 1 && P <= Psat)) {
1196 setState_TP(T, P);
1197 } else {
1198 throw CanteraError("ThermoPhase::setState_TPQ",
1199 "Temperature ({}), pressure ({}) and vapor fraction ({}) "
1200 "are inconsistent.\nPsat at this T: {}\n"
1201 "Consider specifying the state using two fully independent "
1202 "properties (for example, temperature and density)",
1203 T, P, Q, Psat);
1204 }
1205}
1206
1207bool ThermoPhase::addSpecies(shared_ptr<Species> spec)
1208{
1209 if (!spec->thermo) {
1210 throw CanteraError("ThermoPhase::addSpecies",
1211 "Species {} has no thermo data", spec->name);
1212 }
1213 bool added = Phase::addSpecies(spec);
1214 if (added) {
1215 spec->thermo->validate(spec->name);
1216 m_spthermo.install_STIT(m_kk-1, spec->thermo);
1217 }
1218 return added;
1219}
1220
1221void ThermoPhase::modifySpecies(size_t k, shared_ptr<Species> spec)
1222{
1223 if (!spec->thermo) {
1224 throw CanteraError("ThermoPhase::modifySpecies",
1225 "Species {} has no thermo data", spec->name);
1226 }
1227 Phase::modifySpecies(k, spec);
1228 if (speciesName(k) != spec->name) {
1229 throw CanteraError("ThermoPhase::modifySpecies",
1230 "New species '{}' does not match existing species '{}' at index {}",
1231 spec->name, speciesName(k), k);
1232 }
1233 spec->thermo->validate(spec->name);
1234 m_spthermo.modifySpecies(k, spec->thermo);
1235}
1236
1237void ThermoPhase::setParameters(const AnyMap& phaseNode, const AnyMap& rootNode)
1238{
1239 m_input = phaseNode;
1240}
1241
1242AnyMap ThermoPhase::parameters(bool withInput) const
1243{
1244 AnyMap out;
1245 getParameters(out);
1246 if (withInput) {
1247 out.update(m_input);
1248 }
1249 return out;
1250}
1251
1253{
1254 phaseNode["name"] = name();
1255 phaseNode["thermo"] = ThermoFactory::factory()->canonicalize(type());
1256 vector<string> elementNames;
1257 for (size_t i = 0; i < nElements(); i++) {
1258 elementNames.push_back(elementName(i));
1259 }
1260 phaseNode["elements"] = elementNames;
1261 phaseNode["species"] = speciesNames();
1262
1263 AnyMap state;
1264 auto stateVars = nativeState();
1265 if (stateVars.count("T")) {
1266 state["T"].setQuantity(temperature(), "K");
1267 }
1268
1269 if (stateVars.count("D")) {
1270 state["density"].setQuantity(density(), "kg/m^3");
1271 } else if (stateVars.count("P")) {
1272 state["P"].setQuantity(pressure(), "Pa");
1273 }
1274
1275 if (stateVars.count("Y")) {
1276 map<string, double> Y;
1277 for (size_t k = 0; k < m_kk; k++) {
1278 double Yk = massFraction(k);
1279 if (Yk > 0) {
1280 Y[speciesName(k)] = Yk;
1281 }
1282 }
1283 state["Y"] = Y;
1284 state["Y"].setFlowStyle();
1285 } else if (stateVars.count("X")) {
1286 map<string, double> X;
1287 for (size_t k = 0; k < m_kk; k++) {
1288 double Xk = moleFraction(k);
1289 if (Xk > 0) {
1290 X[speciesName(k)] = Xk;
1291 }
1292 }
1293 state["X"] = X;
1294 state["X"].setFlowStyle();
1295 }
1296
1297 phaseNode["state"] = std::move(state);
1298
1299 static bool reg = AnyMap::addOrderingRules("Phase", {{"tail", "state"}});
1300 if (reg) {
1301 phaseNode["__type__"] = "Phase";
1302 }
1303}
1304
1306{
1307 return m_input;
1308}
1309
1311{
1312 return m_input;
1313}
1314
1317 m_tlast += 0.1234;
1318}
1319
1320void ThermoPhase::equilibrate(const string& XY, const string& solver,
1321 double rtol, int max_steps, int max_iter,
1322 int estimate_equil, int log_level)
1323{
1324 if (solver == "auto" || solver == "element_potential") {
1325 vector<double> initial_state(stateSize());
1326 saveState(initial_state);
1327 debuglog("Trying ChemEquil solver\n", log_level);
1328 try {
1329 PhaseEquilGuard guard(*this);
1330 ChemEquil E;
1331 E.options.maxIterations = max_steps;
1332 E.options.relTolerance = rtol;
1334 int ret = E.equilibrate(*this, XY.c_str(), log_level-1);
1335 if (ret < 0) {
1336 throw CanteraError("ThermoPhase::equilibrate",
1337 "ChemEquil solver failed. Return code: {}", ret);
1338 }
1339 debuglog("ChemEquil solver succeeded\n", log_level);
1340 return;
1341 } catch (std::exception& err) {
1342 debuglog("ChemEquil solver failed.\n", log_level);
1343 debuglog(err.what(), log_level);
1344 restoreState(initial_state);
1345 if (solver == "auto") {
1346 } else {
1347 throw;
1348 }
1349 }
1350 }
1351
1352 if (solver == "auto" || solver == "vcs" || solver == "gibbs") {
1353 MultiPhase M;
1354 // Create a non-owning shared_ptr, since this ThermoPhase object is guaranteed
1355 // to outlive the MultiPhase object.
1356 M.addPhase(shared_ptr<ThermoPhase>(this, [](ThermoPhase*) {}), 1.0);
1357 M.init();
1358 M.equilibrate(XY, solver, rtol, max_steps, max_iter,
1359 estimate_equil, log_level);
1360 return;
1361 }
1362
1363 if (solver != "auto") {
1364 throw CanteraError("ThermoPhase::equilibrate",
1365 "Invalid solver specified: '{}'", solver);
1366 }
1367}
1368
1369void ThermoPhase::getdlnActCoeffdlnN(const size_t ld, span<double> dlnActCoeffdlnN)
1370{
1371 checkArraySize("ThermoPhase::getdlnActCoeffdlnN", dlnActCoeffdlnN.size(), ld*m_kk);
1372 for (size_t m = 0; m < m_kk; m++) {
1373 for (size_t k = 0; k < m_kk; k++) {
1374 dlnActCoeffdlnN[ld * k + m] = 0.0;
1375 }
1376 }
1377 return;
1378}
1379
1380void ThermoPhase::getdlnActCoeffdlnN_numderiv(const size_t ld,
1381 span<double> dlnActCoeffdlnN)
1382{
1383 checkArraySize("ThermoPhase::getdlnActCoeffdlnN_numderiv",
1384 dlnActCoeffdlnN.size(), ld*m_kk);
1385 double deltaMoles_j = 0.0;
1386 double pres = pressure();
1387
1388 // Evaluate the current base activity coefficients if necessary
1389 vector<double> ActCoeff_Base(m_kk);
1390 getActivityCoefficients(ActCoeff_Base);
1391 vector<double> Xmol_Base(m_kk);
1392 getMoleFractions(Xmol_Base);
1393
1394 // Make copies of ActCoeff and Xmol_ for use in taking differences
1395 vector<double> ActCoeff(m_kk);
1396 vector<double> Xmol(m_kk);
1397 double v_totalMoles = 1.0;
1398 double TMoles_base = v_totalMoles;
1399
1400 // Loop over the columns species to be deltad
1401 for (size_t j = 0; j < m_kk; j++) {
1402 // Calculate a value for the delta moles of species j
1403 // -> Note Xmol_[] and Tmoles are always positive or zero quantities.
1404 // -> experience has shown that you always need to make the deltas
1405 // greater than needed to change the other mole fractions in order
1406 // to capture some effects.
1407 double moles_j_base = v_totalMoles * Xmol_Base[j];
1408 deltaMoles_j = 1.0E-7 * moles_j_base + v_totalMoles * 1.0E-13 + 1.0E-150;
1409
1410 // Now, update the total moles in the phase and all of the mole
1411 // fractions based on this.
1412 v_totalMoles = TMoles_base + deltaMoles_j;
1413 for (size_t k = 0; k < m_kk; k++) {
1414 Xmol[k] = Xmol_Base[k] * TMoles_base / v_totalMoles;
1415 }
1416 Xmol[j] = (moles_j_base + deltaMoles_j) / v_totalMoles;
1417
1418 // Go get new values for the activity coefficients.
1419 setMoleFractions(Xmol);
1420 setPressure(pres);
1421 getActivityCoefficients(ActCoeff);
1422
1423 // Calculate the column of the matrix
1424 span<double> lnActCoeffCol = dlnActCoeffdlnN.subspan(ld * j, m_kk);
1425 for (size_t k = 0; k < m_kk; k++) {
1426 lnActCoeffCol[k] = (2*moles_j_base + deltaMoles_j) *(ActCoeff[k] - ActCoeff_Base[k]) /
1427 ((ActCoeff[k] + ActCoeff_Base[k]) * deltaMoles_j);
1428 }
1429 // Revert to the base case Xmol_, v_totalMoles
1430 v_totalMoles = TMoles_base;
1431 Xmol = Xmol_Base;
1432 }
1433 setMoleFractions(Xmol_Base);
1434 setPressure(pres);
1435}
1436
1437string ThermoPhase::report(bool show_thermo, double threshold) const
1438{
1439 if (type() == "none") {
1440 throw NotImplementedError("ThermoPhase::report",
1441 "Not implemented for thermo model 'none'");
1442 }
1443
1444 fmt::memory_buffer b;
1445 // This is the width of the first column of names in the report.
1446 int name_width = 18;
1447
1448 string blank_leader = fmt::format("{:{}}", "", name_width);
1449
1450 string string_property = fmt::format("{{:>{}}} {{}}\n", name_width);
1451
1452 string one_property = fmt::format("{{:>{}}} {{:<.5g}} {{}}\n", name_width);
1453
1454 constexpr auto two_prop_header = "{} {:^15} {:^15}\n";
1455 string kg_kmol_header = fmt::format(
1456 two_prop_header, blank_leader, "1 kg", "1 kmol"
1457 );
1458 string Y_X_header = fmt::format(
1459 two_prop_header, blank_leader, "mass frac. Y", "mole frac. X"
1460 );
1461 string two_prop_sep = fmt::format(
1462 "{} {:-^15} {:-^15}\n", blank_leader, "", ""
1463 );
1464 string two_property = fmt::format(
1465 "{{:>{}}} {{:15.5g}} {{:15.5g}} {{}}\n", name_width
1466 );
1467
1468 string three_prop_header = fmt::format(
1469 "{} {:^15} {:^15} {:^15}\n", blank_leader, "mass frac. Y",
1470 "mole frac. X", "chem. pot. / RT"
1471 );
1472 string three_prop_sep = fmt::format(
1473 "{} {:-^15} {:-^15} {:-^15}\n", blank_leader, "", "", ""
1474 );
1475 string three_property = fmt::format(
1476 "{{:>{}}} {{:15.5g}} {{:15.5g}} {{:15.5g}}\n", name_width
1477 );
1478
1479 try {
1480 if (name() != "") {
1481 fmt_append(b, "\n {}:\n", name());
1482 }
1483 fmt_append(b, "\n");
1484 fmt_append(b, one_property, "temperature", temperature(), "K");
1485 fmt_append(b, one_property, "pressure", pressure(), "Pa");
1486 fmt_append(b, one_property, "density", density(), "kg/m^3");
1487 fmt_append(b, one_property,
1488 "mean mol. weight", meanMolecularWeight(), "kg/kmol");
1489
1490 double phi = electricPotential();
1491 if (phi != 0.0) {
1492 fmt_append(b, one_property, "potential", phi, "V");
1493 }
1494
1495 fmt_append(b, string_property, "phase of matter", phaseOfMatter());
1496
1497 if (show_thermo) {
1498 fmt_append(b, "\n");
1499 fmt_append(b, kg_kmol_header);
1500 fmt_append(b, two_prop_sep);
1501 fmt_append(b, two_property,
1502 "enthalpy", enthalpy_mass(), enthalpy_mole(), "J");
1503 fmt_append(b, two_property,
1504 "internal energy", intEnergy_mass(), intEnergy_mole(), "J");
1505 fmt_append(b, two_property,
1506 "entropy", entropy_mass(), entropy_mole(), "J/K");
1507 fmt_append(b, two_property,
1508 "Gibbs function", gibbs_mass(), gibbs_mole(), "J");
1509 fmt_append(b, two_property,
1510 "heat capacity c_p", cp_mass(), cp_mole(), "J/K");
1511 try {
1512 fmt_append(b, two_property,
1513 "heat capacity c_v", cv_mass(), cv_mole(), "J/K");
1514 } catch (NotImplementedError&) {
1515 fmt_append(b, string_property,
1516 "heat capacity c_v", "<not implemented>");
1517 }
1518 }
1519
1520 vector<double> x(m_kk);
1521 vector<double> y(m_kk);
1522 vector<double> mu(m_kk);
1526 int nMinor = 0;
1527 double xMinor = 0.0;
1528 double yMinor = 0.0;
1529 fmt_append(b, "\n");
1530 if (show_thermo) {
1531 fmt_append(b, three_prop_header);
1532 fmt_append(b, three_prop_sep);
1533 for (size_t k = 0; k < m_kk; k++) {
1534 if (abs(x[k]) >= threshold) {
1535 if (abs(x[k]) > SmallNumber) {
1536 fmt_append(b, three_property,
1537 speciesName(k), y[k], x[k], mu[k]/RT());
1538 } else {
1539 fmt_append(b, two_property, speciesName(k), y[k], x[k], "");
1540 }
1541 } else {
1542 nMinor++;
1543 xMinor += x[k];
1544 yMinor += y[k];
1545 }
1546 }
1547 } else {
1548 fmt_append(b, Y_X_header);
1549 fmt_append(b, two_prop_sep);
1550 for (size_t k = 0; k < m_kk; k++) {
1551 if (abs(x[k]) >= threshold) {
1552 fmt_append(b, two_property, speciesName(k), y[k], x[k], "");
1553 } else {
1554 nMinor++;
1555 xMinor += x[k];
1556 yMinor += y[k];
1557 }
1558 }
1559 }
1560 if (nMinor) {
1561 string minor = fmt::format("[{:+5d} minor]", nMinor);
1562 fmt_append(b, two_property, minor, yMinor, xMinor, "");
1563 }
1564 } catch (CanteraError& err) {
1565 return to_string(b) + err.what();
1566 }
1567 return to_string(b);
1568}
1569
1570}
Chemical equilibrium.
Headers for the MultiPhase object that is used to set up multiphase equilibrium problems (see Chemica...
Pure Virtual Base class for individual species reference state thermodynamic managers and text for th...
Declaration for class Cantera::Species.
Headers for the factory class that can create known ThermoPhase objects (see Thermodynamic Properties...
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
A map of string keys to values whose type can vary at runtime.
Definition AnyMap.h:431
size_t size() const
Returns the number of elements in this map.
Definition AnyMap.cpp:1728
bool hasKey(const string &key) const
Returns true if the map contains an item named key.
Definition AnyMap.cpp:1477
void applyUnits()
Use the supplied UnitSystem to set the default units, and recursively process overrides from nodes na...
Definition AnyMap.cpp:1761
double convert(const string &key, const string &units) const
Convert the item stored by the given key to the units specified in units.
Definition AnyMap.cpp:1595
void setFlowStyle(bool flow=true)
Use "flow" style when outputting this AnyMap to YAML.
Definition AnyMap.cpp:1794
void erase(const string &key)
Erase the value held by key.
Definition AnyMap.cpp:1483
static AnyMap fromYamlFile(const string &name, const string &parent_name="")
Create an AnyMap from a YAML file.
Definition AnyMap.cpp:1841
void update(const AnyMap &other, bool keepExisting=true)
Add items from other to this AnyMap.
Definition AnyMap.cpp:1493
static bool addOrderingRules(const string &objectType, const vector< vector< string > > &specs)
Add global rules for setting the order of elements when outputting AnyMap objects to YAML.
Definition AnyMap.cpp:1798
string keys_str() const
Return a string listing the keys in this AnyMap, for use in error messages, for example.
Definition AnyMap.cpp:1507
Base class for exceptions thrown by Cantera classes.
const char * what() const override
Get a description of the error.
Class ChemEquil implements a chemical equilibrium solver for single-phase solutions.
Definition ChemEquil.h:71
int equilibrate(ThermoPhase &s, const char *XY, int loglevel=0)
Equilibrate a phase, holding the elemental composition fixed at the initial value found within the Th...
EquilOpt options
Options controlling how the calculation is carried out.
Definition ChemEquil.h:119
bool enforceTemperatureLimits
Enforce temperature validity limits during equilibrium solver iterations.
Definition ChemEquil.h:42
double relTolerance
Relative tolerance.
Definition ChemEquil.h:29
int maxIterations
Maximum number of iterations.
Definition ChemEquil.h:31
string canonicalize(const string &name)
Get the canonical name registered for a type.
Definition FactoryBase.h:94
A class for multiphase mixtures.
Definition MultiPhase.h:62
void init()
Process phases and build atomic composition array.
void addPhase(shared_ptr< ThermoPhase > p, double moles)
Add a phase to the mixture.
A species thermodynamic property manager for a phase.
bool ready(size_t nSpecies)
Check if data for all species (0 through nSpecies-1) has been installed.
virtual void install_STIT(size_t index, shared_ptr< SpeciesThermoInterpType > stit)
Install a new species thermodynamic property parameterization for one species.
virtual void modifySpecies(size_t index, shared_ptr< SpeciesThermoInterpType > spec)
Modify the species thermodynamic property parameterization for a species.
virtual void resetHf298(const size_t k)
Restore the original heat of formation of one or more species.
An error indicating that an unimplemented function has been called.
void getMoleFractions(span< double > x) const
Get the species mole fraction vector.
Definition Phase.cpp:451
void getMassFractions(span< double > y) const
Get the species mass fractions.
Definition Phase.cpp:489
double massFraction(size_t k) const
Return the mass fraction of a single species.
Definition Phase.cpp:473
virtual bool addSpecies(shared_ptr< Species > spec)
Add a Species to this Phase.
Definition Phase.cpp:770
void assertCompressible(const string &setter) const
Ensure that phase is compressible.
Definition Phase.h:842
size_t nSpecies() const
Returns the number of species in the phase.
Definition Phase.h:247
virtual map< string, size_t > nativeState() const
Return a map of properties defining the native state of a substance.
Definition Phase.cpp:164
size_t m_kk
Number of species in the phase.
Definition Phase.h:882
virtual void modifySpecies(size_t k, shared_ptr< Species > spec)
Modify the thermodynamic data associated with a species.
Definition Phase.cpp:883
size_t nDim() const
Returns the number of spatial dimensions (1, 2, or 3)
Definition Phase.h:570
span< const double > molecularWeights() const
Return a const reference to the internal vector of molecular weights.
Definition Phase.cpp:411
void setState_TD(double t, double rho)
Set the internally stored temperature (K) and density (kg/m^3)
Definition Phase.cpp:385
double temperature() const
Temperature (K).
Definition Phase.h:586
virtual void setPressure(double p)
Set the internally stored pressure (Pa) at constant temperature and composition.
Definition Phase.h:640
double meanMolecularWeight() const
The mean molecular weight. Units: (kg/kmol)
Definition Phase.h:677
virtual void restorePartialState(span< const double > state)
Set the internal thermodynamic state of the phase, excluding composition.
Definition Phase.cpp:237
span< const double > massFractions() const
Return a view of the mass fraction array.
Definition Phase.h:466
void setMassFractionsByName(const Composition &yMap)
Set the species mass fractions by name.
Definition Phase.cpp:374
string speciesName(size_t k) const
Name of the species with index k.
Definition Phase.cpp:143
void moleFractionsToMassFractions(span< const double > X, span< double > Y) const
Converts a mixture composition from mass fractions to mole fractions.
Definition Phase.cpp:1018
virtual void setDensity(const double density_)
Set the internally stored density (kg/m^3) of the phase.
Definition Phase.cpp:607
virtual size_t stateSize() const
Return size of vector defining internal state of the phase.
Definition Phase.cpp:249
vector< double > getCompositionFromMap(const Composition &comp) const
Converts a Composition to a vector with entries for each species Species that are not specified are s...
Definition Phase.cpp:991
void setMoleFractionsByName(const Composition &xMap)
Set the species mole fractions by name.
Definition Phase.cpp:331
vector< AnyMap > elementDefinitions() const
Return explicit element definitions needed to reconstruct this phase.
Definition Phase.cpp:718
double moleFraction(size_t k) const
Return the mole fraction of a single species.
Definition Phase.cpp:457
const vector< string > & elementNames() const
Return a read-only reference to the vector of element names.
Definition Phase.cpp:64
virtual double density() const
Density (kg/m^3).
Definition Phase.h:611
double nAtoms(size_t k, size_t m) const
Number of atoms of element m in species k.
Definition Phase.cpp:101
virtual void setTemperature(double temp)
Set the internally stored temperature of the phase (K).
Definition Phase.h:647
size_t nElements() const
Number of elements.
Definition Phase.cpp:30
virtual void setMoleFractions(span< const double > x)
Set the mole fractions to the specified values.
Definition Phase.cpp:283
const vector< string > & speciesNames() const
Return a const reference to the vector of species names.
Definition Phase.cpp:151
size_t elementIndex(const string &name, bool raise=true) const
Return the index of element named 'name'.
Definition Phase.cpp:51
virtual void setMassFractions(span< const double > y)
Set the mass fractions to the specified values and normalize them.
Definition Phase.cpp:342
double molecularWeight(size_t k) const
Molecular weight of species k.
Definition Phase.cpp:398
shared_ptr< Species > species(const string &name) const
Return the Species object for the named species.
Definition Phase.cpp:943
virtual void invalidateCache()
Invalidate any cached values which are normally updated only when a change in state is detected.
Definition Phase.cpp:972
virtual void setMassFractions_NoNorm(span< const double > y)
Set the mass fractions to the specified values without normalizing.
Definition Phase.cpp:362
virtual size_t partialStateSize() const
Get the size of the partial state vector of the phase.
Definition Phase.h:318
virtual void savePartialState(span< double > state) const
Save the current thermodynamic state of the phase, excluding composition.
Definition Phase.cpp:225
virtual void restoreState(span< const double > state)
Restore the state of the phase from a previously saved state vector.
Definition Phase.cpp:269
virtual double pressure() const
Return the thermodynamic pressure (Pa).
Definition Phase.h:604
string elementName(size_t m) const
Name of the element with index m.
Definition Phase.cpp:43
double charge(size_t k) const
Dimensionless electrical charge of a single molecule of species k The charge is normalized by the the...
Definition Phase.h:562
string name() const
Return the name of the phase.
Definition Phase.cpp:20
virtual void saveState(span< double > state) const
Write to array 'state' the current internal state.
Definition Phase.cpp:257
static ThermoFactory * factory()
Static function that creates a static instance of the factory.
Base class for a phase with thermodynamic properties.
int m_ssConvention
Contains the standard state convention.
virtual double critTemperature() const
Critical temperature (K).
virtual void setState_HP(double h, double p, double tol=1e-9)
Set the internally stored specific enthalpy (J/kg) and pressure (Pa) of the phase.
virtual void getPartialMolarVolumes(span< double > vbar) const
Return an array of partial molar volumes for the species in the mixture.
double electricPotential() const
Returns the electric potential of this phase (V).
virtual void setState_UV(double u, double v, double tol=1e-9)
Set the specific internal energy (J/kg) and specific volume (m^3/kg).
virtual double cp_mole() const
Molar heat capacity at constant pressure and composition [J/kmol/K].
double equivalenceRatio() const
Compute the equivalence ratio for the current mixture from available oxygen and required oxygen.
virtual void setParameters(const AnyMap &phaseNode, const AnyMap &rootNode=AnyMap())
Set equation of state parameters from an AnyMap phase description.
virtual void getParameters(AnyMap &phaseNode) const
Store the parameters of a ThermoPhase object such that an identical one could be reconstructed using ...
virtual double enthalpy_mole() const
Molar enthalpy. Units: J/kmol.
virtual void setState_TP(double t, double p)
Set the temperature (K) and pressure (Pa)
virtual double standardConcentration(size_t k=0) const
Return the standard concentration for the kth species.
virtual void setState_TV(double t, double v, double tol=1e-9)
Set the temperature (K) and specific volume (m^3/kg).
virtual double logStandardConc(size_t k=0) const
Natural logarithm of the standard concentration of the kth species.
virtual void setState_PV(double p, double v, double tol=1e-9)
Set the pressure (Pa) and specific volume (m^3/kg).
virtual void setState(const AnyMap &state)
Set the state using an AnyMap containing any combination of properties supported by the thermodynamic...
virtual double minTemp(size_t k=npos) const
Minimum temperature for which the thermodynamic data for the species or phase are valid.
void setState_SPorSV(double s, double p, double tol=1e-9, bool doSV=false)
Carry out work in SP and SV calculations.
double RT() const
Return the Gas Constant multiplied by the current temperature.
double o2Required(span< const double > y) const
Helper function for computing the amount of oxygen required for complete oxidation.
virtual double critPressure() const
Critical pressure (Pa).
double m_tlast
last value of the temperature processed by reference state
virtual void setState_ST(double s, double t, double tol=1e-9)
Set the specific entropy (J/kg/K) and temperature (K).
virtual void getActivities(span< double > a) const
Get the array of non-dimensional activities at the current solution temperature, pressure,...
virtual void getdlnActCoeffdlnN(const size_t ld, span< double > dlnActCoeffdlnN)
Get the array of derivatives of the log activity coefficients with respect to the log of the species ...
void setState_HPorUV(double h, double p, double tol=1e-9, bool doUV=false)
Carry out work in HP and UV calculations.
double gibbs_mass() const
Specific Gibbs function. Units: J/kg.
string type() const override
String indicating the thermodynamic model implemented.
AnyMap parameters(bool withInput=true) const
Returns the parameters of a ThermoPhase object such that an identical one could be reconstructed usin...
virtual string report(bool show_thermo=true, double threshold=-1e-14) const
returns a summary of the state of the phase as a string
virtual void getActivityConcentrations(span< double > c) const
This method returns an array of generalized concentrations.
virtual double maxTemp(size_t k=npos) const
Maximum temperature for which the thermodynamic data for the species are valid.
double o2Present(span< const double > y) const
Helper function for computing the amount of oxygen available in the current mixture.
virtual void setState_TPY(double t, double p, span< const double > y)
Set the internally stored temperature (K), pressure (Pa), and mass fractions of the phase.
virtual void setState_TPX(double t, double p, span< const double > x)
Set the temperature (K), pressure (Pa), and mole fractions.
virtual string phaseOfMatter() const
String indicating the mechanical phase of the matter in this Phase.
virtual void setState_Tsat(double t, double x)
Set the state to a saturated system at a particular temperature.
virtual double entropy_mole() const
Molar entropy. Units: J/kmol/K.
double cv_mass() const
Specific heat at constant volume and composition [J/kg/K].
virtual void getLnActivityCoefficients(span< double > lnac) const
Get the array of non-dimensional molar-based ln activity coefficients at the current solution tempera...
virtual int activityConvention() const
This method returns the convention used in specification of the activities, of which there are curren...
virtual void initThermo()
Initialize the ThermoPhase object after all species have been set up.
double entropy_mass() const
Specific entropy. Units: J/kg/K.
void getElectrochemPotentials(span< double > mu) const
Get the species electrochemical potentials.
virtual MultiSpeciesThermo & speciesThermo(int k=-1)
Return a changeable reference to the calculation manager for species reference-state thermodynamic pr...
virtual void setState_UP(double u, double p, double tol=1e-9)
Set the specific internal energy (J/kg) and pressure (Pa).
void initThermoFile(const string &inputFile, const string &id)
Initialize a ThermoPhase object using an input file.
shared_ptr< Solution > root() const
Get the Solution object containing this ThermoPhase object and linked Kinetics and Transport objects.
virtual void setState_SP(double s, double p, double tol=1e-9)
Set the specific entropy (J/kg/K) and pressure (Pa).
virtual int standardStateConvention() const
This method returns the convention used in specification of the standard state, of which there are cu...
void modifySpecies(size_t k, shared_ptr< Species > spec) override
Modify the thermodynamic data associated with a species.
virtual void setState_SH(double s, double h, double tol=1e-9)
Set the specific entropy (J/kg/K) and the specific enthalpy (J/kg)
virtual void getPartialMolarIntEnergies(span< double > ubar) const
Return an array of partial molar internal energies for the species in the mixture.
void invalidateCache() override
Invalidate any cached values which are normally updated only when a change in state is detected.
double stoichAirFuelRatio(span< const double > fuelComp, span< const double > oxComp, ThermoBasis basis=ThermoBasis::molar) const
Compute the stoichiometric air to fuel ratio (kg oxidizer / kg fuel) given fuel and oxidizer composit...
virtual double internalPressure() const
Return the internal pressure [Pa].
virtual void resetHf298(const size_t k=npos)
Restore the original heat of formation of one or more species.
double cp_mass() const
Specific heat at constant pressure and composition [J/kg/K].
virtual void setState_TH(double t, double h, double tol=1e-9)
Set the temperature (K) and the specific enthalpy (J/kg)
double intEnergy_mass() const
Specific internal energy. Units: J/kg.
virtual void getPartialMolarIntEnergies_TV(span< double > utilde) const
Return an array of partial molar internal energies at constant temperature and volume [J/kmol].
virtual Units standardConcentrationUnits() const
Returns the units of the "standard concentration" for this phase.
bool temperatureLimitsEnforced() const
Returns true if temperature limits are enforced for this phase.
void setMixtureFraction(double mixFrac, span< const double > fuelComp, span< const double > oxComp, ThermoBasis basis=ThermoBasis::molar)
Set the mixture composition according to the mixture fraction = kg fuel / (kg oxidizer + kg fuel)
double mixtureFraction(span< const double > fuelComp, span< const double > oxComp, ThermoBasis basis=ThermoBasis::molar, const string &element="Bilger") const
Compute the mixture fraction = kg fuel / (kg oxidizer + kg fuel) for the current mixture given fuel a...
void setEquivalenceRatio(double phi, span< const double > fuelComp, span< const double > oxComp, ThermoBasis basis=ThermoBasis::molar)
Set the mixture composition according to the equivalence ratio.
virtual double cv_mole() const
Molar heat capacity at constant volume and composition [J/kmol/K].
MultiSpeciesThermo m_spthermo
Pointer to the calculation manager for species reference-state thermodynamic properties.
virtual double satPressure(double t)
Return the saturation pressure given the temperature.
virtual void getChemPotentials(span< double > mu) const
Get the species chemical potentials. Units: J/kmol.
bool addSpecies(shared_ptr< Species > spec) override
Add a Species to this Phase.
AnyMap m_input
Data supplied via setParameters.
virtual double intEnergy_mole() const
Molar internal energy. Units: J/kmol.
virtual void setState_DP(double rho, double p)
Set the density (kg/m**3) and pressure (Pa) at constant composition.
void setState_TPQ(double T, double P, double Q)
Set the temperature, pressure, and vapor fraction (quality).
virtual void setState_VH(double v, double h, double tol=1e-9)
Set the specific volume (m^3/kg) and the specific enthalpy (J/kg)
virtual double gibbs_mole() const
Molar Gibbs function. Units: J/kmol.
virtual void setState_SV(double s, double v, double tol=1e-9)
Set the specific entropy (J/kg/K) and specific volume (m^3/kg).
const AnyMap & input() const
Access input data associated with the phase description.
virtual void setState_Psat(double p, double x)
Set the state to a saturated system at a particular pressure.
void setState_conditional_TP(double t, double p, bool set_p)
Helper function used by setState_HPorUV and setState_SPorSV.
virtual void getActivityCoefficients(span< double > ac) const
Get the array of non-dimensional molar-based activity coefficients at the current solution temperatur...
shared_ptr< ThermoPhase > clone() const
Create a new ThermoPhase object using the same species definitions, thermodynamic parameters,...
double enthalpy_mass() const
Specific enthalpy. Units: J/kg.
A representation of the units associated with a dimensional quantity.
Definition Units.h:35
This file contains definitions for utility functions and text for modules, inputfiles and logging,...
Composition parseCompString(const string &ss, const vector< string > &names)
Parse a composition string into a map consisting of individual key:composition pairs.
void equilibrate(const string &XY, const string &solver="auto", double rtol=1e-9, int max_steps=50000, int max_iter=100, int estimate_equil=0, int log_level=0)
Equilibrate a ThermoPhase object.
void debuglog(const string &msg, int loglevel)
Write a message to the log only if loglevel > 0.
Definition global.h:154
double dot(InputIter x_begin, InputIter x_end, InputIter2 y_begin)
Function that calculates a templated inner product.
Definition utilities.h:96
T clip(const T &value, const T &lower, const T &upper)
Clip value such that lower <= value <= upper.
Definition global.h:326
const double Faraday
Faraday constant [C/kmol].
Definition ct_defs.h:134
const double OneAtm
One atmosphere [Pa].
Definition ct_defs.h:99
shared_ptr< ThermoPhase > newThermo(const AnyMap &phaseNode, const AnyMap &rootNode)
Create a new ThermoPhase object and initialize it.
void setupPhase(ThermoPhase &thermo, const AnyMap &phaseNode, const AnyMap &rootNode)
Initialize a ThermoPhase object.
void warn_user(const string &method, const string &msg, const Args &... args)
Print a user warning raised from method as CanteraWarning.
Definition global.h:263
Namespace for the Cantera kernel.
Definition AnyMap.cpp:595
const size_t npos
index returned by functions to indicate "no position"
Definition ct_defs.h:183
const int cAC_CONVENTION_MOLAR
Standard state uses the molar convention.
const double SmallNumber
smallest number to compare to zero.
Definition ct_defs.h:161
ThermoBasis
Differentiate between mole fractions and mass fractions for input mixture composition.
map< string, double > Composition
Map from string names to doubles.
Definition ct_defs.h:180
void checkArraySize(const char *procedure, size_t available, size_t required)
Wrapper for throwing ArraySizeError.
Contains declarations for string manipulation functions within Cantera.