Cantera  4.0.0a1
Loading...
Searching...
No Matches
Phase.cpp
Go to the documentation of this file.
1/**
2 * @file Phase.cpp
3 * Definition file for class Phase.
4 */
5
6// This file is part of Cantera. See License.txt in the top-level directory or
7// at https://cantera.org/license.txt for license and copyright information.
8
14
15using namespace std;
16
17namespace Cantera
18{
19
20string Phase::name() const
21{
22 return m_name;
23}
24
25void Phase::setName(const string& name)
26{
27 m_name = name;
28}
29
30size_t Phase::nElements() const
31{
32 return m_mm;
33}
34
35size_t Phase::checkElementIndex(size_t m) const
36{
37 if (m < m_mm) {
38 return m;
39 }
40 throw IndexError("Phase::checkElementIndex", "elements", m, m_mm);
41}
42
43string Phase::elementName(size_t m) const
44{
45 if (m < m_mm) {
46 return m_elementNames[m];
47 }
48 throw IndexError("Phase::elementName", "element", m, m_mm);
49}
50
51size_t Phase::elementIndex(const string& elementName, bool raise) const
52{
53 for (size_t i = 0; i < m_mm; i++) {
54 if (m_elementNames[i] == elementName) {
55 return i;
56 }
57 }
58 if (!raise) {
59 return npos;
60 }
61 throw CanteraError("Phase::elementIndex", "Element '{}' not found", elementName);
62}
63
64const vector<string>& Phase::elementNames() const
65{
66 return m_elementNames;
67}
68
69double Phase::atomicWeight(size_t m) const
70{
71 return m_atomicWeights[m];
72}
73
74double Phase::entropyElement298(size_t m) const
75{
77}
78
79span<const double> Phase::atomicWeights() const
80{
81 return m_atomicWeights;
82}
83
84int Phase::atomicNumber(size_t m) const
85{
86 return m_atomicNumbers[m];
87}
88
89int Phase::elementType(size_t m) const
90{
91 return m_elem_type[m];
92}
93
94int Phase::changeElementType(int m, int elem_type)
95{
96 int old = m_elem_type[m];
97 m_elem_type[m] = elem_type;
98 return old;
99}
100
101double Phase::nAtoms(size_t k, size_t m) const
102{
105 return m_speciesComp[m_mm * k + m];
106}
107
108size_t Phase::findSpeciesLower(const string& name) const
109{
110 string nLower = toLowerCopy(name);
111 size_t loc = npos;
112 auto it = m_speciesLower.find(nLower);
113 if (it == m_speciesLower.end()) {
114 return npos;
115 } else {
116 loc = it->second;
117 if (loc == npos) {
118 throw CanteraError("Phase::findSpeciesLower",
119 "Lowercase species name '{}' is not unique. "
120 "Set Phase::caseSensitiveSpecies to true to "
121 "enforce case sensitive species names", nLower);
122 }
123 }
124 return loc;
125}
126
127size_t Phase::speciesIndex(const string& name, bool raise) const
128{
129 size_t loc = npos;
130 auto it = m_speciesIndices.find(name);
131 if (it != m_speciesIndices.end()) {
132 return it->second;
133 } else if (!m_caseSensitiveSpecies) {
134 loc = findSpeciesLower(name);
135 }
136 if (loc==npos && raise) {
137 throw CanteraError("Phase::speciesIndex", "Species '{}' not found", name);
138 }
139
140 return loc;
141}
142
143string Phase::speciesName(size_t k) const
144{
145 if (k < m_kk) {
146 return m_speciesNames[k];
147 }
148 throw IndexError("Phase::speciesName", "species", k, m_kk);
149}
150
151const vector<string>& Phase::speciesNames() const
152{
153 return m_speciesNames;
154}
155
156size_t Phase::checkSpeciesIndex(size_t k) const
157{
158 if (k < m_kk) {
159 return k;
160 }
161 throw IndexError("Phase::checkSpeciesIndex", "species", k, m_kk);
162}
163
164map<string, size_t> Phase::nativeState() const
165{
166 if (isPure()) {
167 if (isCompressible()) {
168 return { {"T", 0}, {"D", 1} };
169 } else {
170 return { {"T", 0}, {"P", 1} };
171 }
172 } else {
173 if (isCompressible()) {
174 return { {"T", 0}, {"D", 1}, {"Y", 2} };
175 } else {
176 return { {"T", 0}, {"P", 1}, {"Y", 2} };
177 }
178 }
179}
180
181string Phase::nativeMode() const
182{
183 map<size_t, string> states; // reverse lookup
184 for (auto& [name, value] : nativeState()) {
185 states[value] = name;
186 }
187 string out;
188 for (auto& [value, name] : states) {
189 out += name;
190 }
191 return out;
192}
193
194vector<string> Phase::fullStates() const
195{
196 if (isPure()) {
197 if (isCompressible()) {
198 return {"TD", "TP", "UV", "DP", "HP", "SP", "SV"};
199 } else {
200 return {"TP", "HP", "SP"};
201 }
202 } else {
203 if (isCompressible()) {
204 return {"TDX", "TDY", "TPX", "TPY", "UVX", "UVY", "DPX", "DPY",
205 "HPX", "HPY", "SPX", "SPY", "SVX", "SVY"};
206 } else {
207 return {"TPX", "TPY", "HPX", "HPY", "SPX", "SPY"};
208 }
209 }
210}
211
212vector<string> Phase::partialStates() const
213{
214 if (isPure()) {
215 return {};
216 } else {
217 if (isCompressible()) {
218 return {"TD", "TP", "UV", "DP", "HP", "SP", "SV"};
219 } else {
220 return {"TP", "HP", "SP"};
221 }
222 }
223}
224
225void Phase::savePartialState(span<double> state) const
226{
227 checkArraySize("Phase::savePartialState", state.size(), partialStateSize());
228 auto native = nativeState();
229 state[native.at("T")] = temperature();
230 if (isCompressible()) {
231 state[native.at("D")] = density();
232 } else {
233 state[native.at("P")] = pressure();
234 }
235}
236
237void Phase::restorePartialState(span<const double> state)
238{
239 checkArraySize("Phase::restorePartialState", state.size(), partialStateSize());
240 auto native = nativeState();
241 setTemperature(state[native.at("T")]);
242 if (isCompressible()) {
243 setDensity(state[native.at("D")]);
244 } else {
245 setPressure(state[native.at("P")]);
246 }
247}
248
249size_t Phase::stateSize() const {
250 if (isPure()) {
251 return partialStateSize();
252 } else {
253 return partialStateSize() + nSpecies();
254 }
255}
256
257void Phase::saveState(span<double> state) const
258{
259 checkArraySize("Phase::saveState", state.size(), stateSize());
260 savePartialState(state);
261 auto native = nativeState();
262 if (native.count("X")) {
263 getMoleFractions(state.subspan(native["X"], nSpecies()));
264 } else if (native.count("Y")) {
265 getMassFractions(state.subspan(native["Y"], nSpecies()));
266 }
267}
268
269void Phase::restoreState(span<const double> state)
270{
271 checkArraySize("Phase::restoreState", state.size(), stateSize());
272 restorePartialState(state);
273 auto native = nativeState();
274
275 if (native.count("X")) {
276 setMoleFractions_NoNorm(state.subspan(native["X"], nSpecies()));
277 } else if (native.count("Y")) {
278 setMassFractions_NoNorm(state.subspan(native["Y"], nSpecies()));
279 }
281}
282
283void Phase::setMoleFractions(span<const double> x)
284{
285 checkArraySize("Phase::setMoleFractions", x.size(), m_kk);
286 // Use m_y as a temporary work vector for the non-negative mole fractions
287 double norm = 0.0;
288 // sum is calculated below as the unnormalized molecular weight
289 double sum = 0;
290 for (size_t k = 0; k < m_kk; k++) {
291 double xk = std::max(x[k], 0.0); // Ignore negative mole fractions
292 m_y[k] = xk;
293 norm += xk;
294 sum += m_molwts[k] * xk;
295 }
296
297 // Set m_ym to the normalized mole fractions divided by the normalized mean
298 // molecular weight:
299 // m_ym_k = X_k / (sum_k X_k M_k)
300 const double invSum = 1.0/sum;
301 if (!std::isfinite(invSum) && m_kk != 0) {
302 throw CanteraError("Phase::setMoleFractions",
303 "The sum of the species mole fractions is zero, too small to normalize,"
304 " or not-a-number.");
305 }
306 for (size_t k=0; k < m_kk; k++) {
307 m_ym[k] = m_y[k]*invSum;
308 }
309
310 // Now set m_y to the normalized mass fractions:
311 // m_y = X_k M_k / (sum_k X_k M_k)
312 for (size_t k=0; k < m_kk; k++) {
313 m_y[k] = m_ym[k] * m_molwts[k];
314 }
315
316 // Calculate the normalized molecular weight
317 m_mmw = sum/norm;
319}
320
321void Phase::setMoleFractions_NoNorm(span<const double> x)
322{
323 checkArraySize("Phase::setMoleFractions_NoNorm", x.size(), m_kk);
324 m_mmw = dot(x.begin(), x.begin() + m_kk, m_molwts.begin());
325 scale(x.begin(), x.begin() + m_kk, m_ym.begin(), 1.0/m_mmw);
326 transform(m_ym.begin(), m_ym.begin() + m_kk, m_molwts.begin(),
327 m_y.begin(), multiplies<double>());
329}
330
332{
333 vector<double> mf = getCompositionFromMap(xMap);
335}
336
337void Phase::setMoleFractionsByName(const string& x)
338{
340}
341
342void Phase::setMassFractions(span<const double> y)
343{
344 checkArraySize("Phase::setMassFractions", y.size(), m_kk);
345 for (size_t k = 0; k < m_kk; k++) {
346 m_y[k] = std::max(y[k], 0.0); // Ignore negative mass fractions
347 }
348 double invSum = 1.0 / accumulate(m_y.begin(), m_y.end(), 0.0);
349 if (!std::isfinite(invSum) && m_kk != 0) {
350 throw CanteraError("Phase::setMassFractions",
351 "The sum of the species mass fractions is zero, too small to normalize,"
352 " or not-a-number.");
353 }
354 scale(m_y.begin(), m_y.end(), m_y.begin(), invSum);
355
356 transform(m_y.begin(), m_y.end(), m_rmolwts.begin(),
357 m_ym.begin(), multiplies<double>());
358 m_mmw = 1.0 / accumulate(m_ym.begin(), m_ym.end(), 0.0);
360}
361
362void Phase::setMassFractions_NoNorm(span<const double> y)
363{
364 checkArraySize("Phase::setMassFractions_NoNorm", y.size(), m_kk);
365 double sum = 0.0;
366 copy(y.begin(), y.begin() + m_kk, m_y.begin());
367 transform(m_y.begin(), m_y.end(), m_rmolwts.begin(), m_ym.begin(),
368 multiplies<double>());
369 sum = accumulate(m_ym.begin(), m_ym.end(), 0.0);
370 m_mmw = 1.0/sum;
372}
373
375{
376 vector<double> mf = getCompositionFromMap(yMap);
378}
379
380void Phase::setMassFractionsByName(const string& y)
381{
383}
384
385void Phase::setState_TD(double t, double rho)
386{
387 vector<double> state(partialStateSize());
388 savePartialState(state);
389 try {
391 setDensity(rho);
392 } catch (std::exception&) {
393 restorePartialState(state);
394 throw;
395 }
396}
397
398double Phase::molecularWeight(size_t k) const
399{
401 return m_molwts[k];
402}
403
404void Phase::getMolecularWeights(span<double> weights) const
405{
406 auto mw = molecularWeights();
407 checkArraySize("Phase::getMolecularWeights", weights.size(), mw.size());
408 copy(mw.begin(), mw.end(), weights.begin());
409}
410
411span<const double> Phase::molecularWeights() const
412{
413 return m_molwts;
414}
415
416span<const double> Phase::inverseMolecularWeights() const
417{
418 return m_rmolwts;
419}
420
421void Phase::getCharges(span<double> charges) const
422{
423 checkArraySize("Phase::getCharges", charges.size(), m_speciesCharge.size());
424 copy(m_speciesCharge.begin(), m_speciesCharge.end(), charges.begin());
425}
426
428{
429 Composition comp;
430 for (size_t k = 0; k < m_kk; k++) {
431 double x = moleFraction(k);
432 if (x > threshold) {
433 comp[speciesName(k)] = x;
434 }
435 }
436 return comp;
437}
438
440{
441 Composition comp;
442 for (size_t k = 0; k < m_kk; k++) {
443 double x = massFraction(k);
444 if (x > threshold) {
445 comp[speciesName(k)] = x;
446 }
447 }
448 return comp;
449}
450
451void Phase::getMoleFractions(span<double> x) const
452{
453 checkArraySize("Phase::getMoleFractions", x.size(), m_kk);
454 scale(m_ym.begin(), m_ym.end(), x.begin(), m_mmw);
455}
456
457double Phase::moleFraction(size_t k) const
458{
460 return m_ym[k] * m_mmw;
461}
462
463double Phase::moleFraction(const string& nameSpec) const
464{
465 size_t iloc = speciesIndex(nameSpec, false);
466 if (iloc != npos) {
467 return moleFraction(iloc);
468 } else {
469 return 0.0;
470 }
471}
472
473double Phase::massFraction(size_t k) const
474{
476 return m_y[k];
477}
478
479double Phase::massFraction(const string& nameSpec) const
480{
481 size_t iloc = speciesIndex(nameSpec, false);
482 if (iloc != npos) {
483 return massFractions()[iloc];
484 } else {
485 return 0.0;
486 }
487}
488
489void Phase::getMassFractions(span<double> y) const
490{
491 checkArraySize("Phase::getMassFractions", y.size(), m_kk);
492 copy(m_y.begin(), m_y.end(), y.begin());
493}
494
495double Phase::concentration(const size_t k) const
496{
498 return m_y[k] * m_dens * m_rmolwts[k];
499}
500
501void Phase::getConcentrations(span<double> c) const
502{
503 checkArraySize("Phase::getConcentrations", c.size(), m_kk);
504 scale(m_ym.begin(), m_ym.end(), c.begin(), m_dens);
505}
506
507void Phase::setConcentrations(span<const double> conc)
508{
509 assertCompressible("setConcentrations");
510 checkArraySize("Phase::setConcentrations", conc.size(), m_kk);
511
512 // Use m_y as temporary storage for non-negative concentrations
513 double sum = 0.0, norm = 0.0;
514 for (size_t k = 0; k != m_kk; ++k) {
515 double ck = std::max(conc[k], 0.0); // Ignore negative concentrations
516 m_y[k] = ck;
517 sum += ck * m_molwts[k];
518 norm += ck;
519 }
520 m_mmw = sum/norm;
521 setDensity(sum);
522 double rsum = 1.0/sum;
523 for (size_t k = 0; k != m_kk; ++k) {
524 m_ym[k] = m_y[k] * rsum;
525 m_y[k] = m_ym[k] * m_molwts[k]; // m_y is now the mass fraction
526 }
528}
529
530void Phase::setConcentrationsNoNorm(span<const double> conc)
531{
532 assertCompressible("setConcentrationsNoNorm");
533 checkArraySize("Phase::setConcentrationsNoNorm", conc.size(), m_kk);
534
535 double sum = 0.0, norm = 0.0;
536 for (size_t k = 0; k != m_kk; ++k) {
537 sum += conc[k] * m_molwts[k];
538 norm += conc[k];
539 }
540 m_mmw = sum/norm;
541 setDensity(sum);
542 double rsum = 1.0/sum;
543 for (size_t k = 0; k != m_kk; ++k) {
544 m_ym[k] = conc[k] * rsum;
545 m_y[k] = m_ym[k] * m_molwts[k];
546 }
548}
549
550void Phase::setMolesNoTruncate(span<const double> N)
551{
552 checkArraySize("Phase::setMolesNoTruncate", N.size(), m_kk);
553 // get total moles
554 copy(N.begin(), N.begin() + m_kk, m_ym.begin());
555 double totalMoles = accumulate(m_ym.begin(), m_ym.end(), 0.0);
556 // get total mass
557 copy(N.begin(), N.begin() + m_kk, m_y.begin());
558 transform(m_y.begin(), m_y.end(), m_molwts.begin(), m_y.begin(), multiplies<double>());
559 double totalMass = accumulate(m_y.begin(), m_y.end(), 0.0);
560 // mean molecular weight
561 m_mmw = totalMass/totalMoles;
562 // mass fractions
563 scale(m_y.begin(), m_y.end(), m_y.begin(), 1/totalMass);
564 // moles fractions/m_mmw
565 scale(m_ym.begin(), m_ym.end(), m_ym.begin(), 1/(m_mmw * totalMoles));
566 // composition has changed
568}
569
570double Phase::elementalMassFraction(const size_t m) const
571{
572 double Z_m = 0.0;
573 for (size_t k = 0; k != m_kk; ++k) {
574 Z_m += nAtoms(k, m) * atomicWeight(m) / molecularWeight(k)
575 * massFraction(k);
576 }
577 return Z_m;
578}
579
580double Phase::elementalMoleFraction(const size_t m) const
581{
582 double denom = 0;
583 for (size_t k = 0; k < m_kk; k++) {
584 double atoms = 0;
585 for (size_t j = 0; j < nElements(); j++) {
586 atoms += nAtoms(k, j);
587 }
588 denom += atoms * moleFraction(k);
589 }
590 double numerator = 0.0;
591 for (size_t k = 0; k != m_kk; ++k) {
592 numerator += nAtoms(k, m) * moleFraction(k);
593 }
594 return numerator / denom;
595}
596
598{
599 return density()/meanMolecularWeight();
600}
601
602double Phase::molarVolume() const
603{
604 return 1.0/molarDensity();
605}
606
607void Phase::setDensity(const double density_)
608{
609 assertCompressible("setDensity");
610 if (density_ > 0.0) {
611 m_dens = density_;
612 } else {
613 throw CanteraError("Phase::setDensity",
614 "density must be positive. density = {}", density_);
615 }
616}
617
618void Phase::assignDensity(const double density_)
619{
620 if (density_ > 0.0) {
621 m_dens = density_;
622 } else {
623 throw CanteraError("Phase::assignDensity",
624 "density must be positive. density = {}", density_);
625 }
626}
627
629{
630 double cdens = 0.0;
631 for (size_t k = 0; k < m_kk; k++) {
632 cdens += charge(k)*moleFraction(k);
633 }
634 return cdens * Faraday;
635}
636
637double Phase::mean_X(span<const double> Q) const
638{
639 checkArraySize("Phase::mean_X", Q.size(), m_kk);
640 return m_mmw*std::inner_product(m_ym.begin(), m_ym.end(), Q.begin(), 0.0);
641}
642
643double Phase::sum_xlogx() const
644{
645 double sumxlogx = 0;
646 for (size_t k = 0; k < m_kk; k++) {
647 sumxlogx += m_ym[k] * std::log(std::max(m_ym[k], SmallNumber));
648 }
649 return m_mmw * sumxlogx + std::log(m_mmw);
650}
651
652size_t Phase::addElement(const string& symbol, double weight, int atomic_number,
653 double entropy298, int elem_type)
654{
655 // Look up the atomic weight if not given
656 if (weight == 0.0) {
657 try {
658 weight = getElementWeight(symbol);
659 } catch (CanteraError&) {
660 // assume this is just a custom element with zero atomic weight
661 }
662 } else if (weight == -12345.0) {
663 weight = getElementWeight(symbol);
664 }
665
666 // Try to look up the standard entropy if not given. Fail silently.
667 if (entropy298 == ENTROPY298_UNKNOWN) {
668 try {
669 const static AnyMap db = AnyMap::fromYamlFile(
670 "element-standard-entropies.yaml");
671 const AnyMap& elem = db["elements"].getMapWhere("symbol", symbol);
672 entropy298 = elem.convert("entropy298", "J/kmol/K", ENTROPY298_UNKNOWN);
673 } catch (CanteraError&) {
674 }
675 }
676
677 // Check for duplicates
678 auto iter = find(m_elementNames.begin(), m_elementNames.end(), symbol);
679 if (iter != m_elementNames.end()) {
680 size_t m = iter - m_elementNames.begin();
681 if (m_atomicWeights[m] != weight) {
682 throw CanteraError("Phase::addElement",
683 "Duplicate elements ({}) have different weights", symbol);
684 } else {
685 // Ignore attempt to add duplicate element with the same weight
686 return m;
687 }
688 }
689
690 // Add the new element
691 m_atomicWeights.push_back(weight);
692 m_elementNames.push_back(symbol);
693 m_atomicNumbers.push_back(atomic_number);
694 m_entropy298.push_back(entropy298);
695 if (symbol == "E") {
697 } else {
698 m_elem_type.push_back(elem_type);
699 }
700 m_mm++;
701
702 // Update species compositions
703 if (m_kk) {
704 vector<double> old(m_speciesComp);
705 m_speciesComp.resize(m_kk*m_mm, 0.0);
706 for (size_t k = 0; k < m_kk; k++) {
707 size_t m_old = m_mm - 1;
708 for (size_t m = 0; m < m_old; m++) {
709 m_speciesComp[k * m_mm + m] = old[k * (m_old) + m];
710 }
711 m_speciesComp[k * (m_mm) + (m_mm-1)] = 0.0;
712 }
713 }
714
715 return m_mm-1;
716}
717
718vector<AnyMap> Phase::elementDefinitions() const
719{
720 vector<AnyMap> definitions;
721 definitions.reserve(nElements());
722 const static AnyMap db = AnyMap::fromYamlFile("element-standard-entropies.yaml");
723 const static auto standardEntropies = db["elements"].asMap("symbol");
724 const auto& weights = elementWeights();
725
726 for (size_t m = 0; m < nElements(); m++) {
727 const string& symbol = m_elementNames[m];
728 bool exportDefinition = false;
729 AnyMap element;
730 element["symbol"] = symbol;
731
732 auto weightIter = weights.find(symbol);
733 if (weightIter == weights.end()) {
734 exportDefinition = true;
735 element["atomic-weight"] = m_atomicWeights[m];
736 } else if (m_atomicWeights[m] != weightIter->second) {
737 exportDefinition = true;
738 element["atomic-weight"] = m_atomicWeights[m];
739 }
740
741 if (m_atomicNumbers[m] != 0) {
742 exportDefinition = true;
743 element["atomic-number"] = m_atomicNumbers[m];
744 }
745
747 auto entropyIter = standardEntropies.find(symbol);
748 if (entropyIter == standardEntropies.end()
749 || !entropyIter->second->hasKey("entropy298"))
750 {
751 exportDefinition = true;
752 element["entropy298"].setQuantity(m_entropy298[m], "J/kmol/K");
753 } else {
754 double defaultEntropy =
755 entropyIter->second->convert("entropy298", "J/kmol/K");
756 if (m_entropy298[m] != defaultEntropy) {
757 exportDefinition = true;
758 element["entropy298"].setQuantity(m_entropy298[m], "J/kmol/K");
759 }
760 }
761 }
762
763 if (exportDefinition) {
764 definitions.push_back(std::move(element));
765 }
766 }
767 return definitions;
768}
769
770bool Phase::addSpecies(shared_ptr<Species> spec)
771{
772 if (m_nSpeciesLocks) {
773 throw CanteraError("Phase::addSpecies",
774 "Cannot add species to ThermoPhase '{}' because it is being "
775 "used by another object,\nsuch as a Reactor, Domain1D (flame), "
776 "SolutionArray, or MultiPhase (Mixture) object.", m_name);
777 }
778
779 if (m_species.find(spec->name) != m_species.end()) {
780 throw CanteraError("Phase::addSpecies",
781 "Phase '{}' already contains a species named '{}'.",
782 m_name, spec->name);
783 }
784
785 vector<double> comp(nElements());
786 for (const auto& [eName, stoich] : spec->composition) {
787 size_t m = elementIndex(eName, false);
788 if (m == npos) { // Element doesn't exist in this phase
790 case UndefElement::ignore:
791 return false;
792
793 case UndefElement::add:
794 addElement(eName);
795 comp.resize(nElements());
796 m = elementIndex(eName, true);
797 break;
798
799 case UndefElement::error:
800 default:
801 throw CanteraError("Phase::addSpecies",
802 "Species '{}' contains an undefined element '{}'.",
803 spec->name, eName);
804 }
805 }
806 comp[m] = stoich;
807 }
808
809 size_t ne = nElements();
810 auto aw = atomicWeights();
811 if (spec->charge != 0.0) {
812 size_t eindex = elementIndex("E", false);
813 if (eindex != npos) {
814 double ecomp = comp[eindex];
815 if (fabs(spec->charge + ecomp) > 0.001) {
816 if (ecomp != 0.0) {
817 throw CanteraError("Phase::addSpecies",
818 "Input charge and element E compositions differ "
819 "for species " + spec->name);
820 } else {
821 // Just fix up the element E composition based on the input
822 // species charge
823 comp[eindex] = -spec->charge;
824 }
825 }
826 } else {
827 addElement("E", 0.000545, 0, 0.0, CT_ELEM_TYPE_ELECTRONCHARGE);
828 ne = nElements();
829 eindex = elementIndex("E", true);
830 comp.resize(ne);
831 comp[ne - 1] = - spec->charge;
832 }
833 }
834
835 double wt = 0.0;
836 for (size_t m = 0; m < ne; m++) {
837 wt += comp[m] * aw[m];
838 }
839
840 // Some surface phases may define species representing empty sites
841 // that have zero molecular weight. Give them a very small molecular
842 // weight to avoid dividing by zero.
843 wt = std::max(wt, Tiny);
844
845 spec->setMolecularWeight(wt);
846
847 m_molwts.push_back(wt);
848 m_rmolwts.push_back(1.0/wt);
849
850 for (size_t m = 0; m < ne; m++) {
851 m_speciesComp.push_back(comp[m]);
852 }
853
854 m_speciesNames.push_back(spec->name);
855 m_species[spec->name] = spec;
856 m_speciesIndices[spec->name] = m_kk;
857 m_speciesCharge.push_back(spec->charge);
858
859 string nLower = toLowerCopy(spec->name);
860 if (m_speciesLower.find(nLower) == m_speciesLower.end()) {
861 m_speciesLower[nLower] = m_kk;
862 } else {
863 m_speciesLower[nLower] = npos;
864 }
865 m_kk++;
866
867 // Ensure that the Phase has a valid mass fraction vector that sums to
868 // one. We will assume that species 0 has a mass fraction of 1.0 and mass
869 // fraction of all other species is 0.0.
870 if (m_kk == 1) {
871 m_y.push_back(1.0);
872 m_ym.push_back(m_rmolwts[0]);
873 m_mmw = 1.0 / m_ym[0];
874 } else {
875 m_y.push_back(0.0);
876 m_ym.push_back(0.0);
877 }
878 m_workS.push_back(0.0);
880 return true;
881}
882
883void Phase::modifySpecies(size_t k, shared_ptr<Species> spec)
884{
885 if (speciesName(k) != spec->name) {
886 throw CanteraError("Phase::modifySpecies",
887 "New species name '{}' does not match existing name '{}'",
888 spec->name, speciesName(k));
889 }
890 const shared_ptr<Species>& old = m_species[spec->name];
891 if (spec->composition != old->composition) {
892 throw CanteraError("Phase::modifySpecies",
893 "New composition for '{}' does not match existing composition",
894 spec->name);
895 }
896 m_species[spec->name] = spec;
898}
899
900void Phase::addSpeciesAlias(const string& name, const string& alias)
901{
902 if (speciesIndex(alias, false) != npos) {
903 throw CanteraError("Phase::addSpeciesAlias",
904 "Invalid alias '{}': species already exists", alias);
905 }
906 size_t k = speciesIndex(name, false);
907 if (k != npos) {
908 m_speciesIndices[alias] = k;
909 } else {
910 throw CanteraError("Phase::addSpeciesAlias",
911 "Unable to add alias '{}' "
912 "(original species '{}' not found).", alias, name);
913 }
914}
915
917{
918 if (!m_nSpeciesLocks) {
919 throw CanteraError("Phase::removeSpeciesLock",
920 "ThermoPhase '{}' has no current species locks.", m_name);
921 }
923}
924
925vector<string> Phase::findIsomers(const Composition& compMap) const
926{
927 vector<string> isomerNames;
928
929 for (const auto& [name, species] : m_species) {
930 if (species->composition == compMap) {
931 isomerNames.emplace_back(name);
932 }
933 }
934
935 return isomerNames;
936}
937
938vector<string> Phase::findIsomers(const string& comp) const
939{
940 return findIsomers(parseCompString(comp));
941}
942
943shared_ptr<Species> Phase::species(const string& name) const
944{
945 size_t k = speciesIndex(name, true);
946 return m_species.at(speciesName(k));
947}
948
949shared_ptr<Species> Phase::species(size_t k) const
950{
952 return m_species.at(m_speciesNames[k]);
953}
954
956 m_undefinedElementBehavior = UndefElement::ignore;
957}
958
960 m_undefinedElementBehavior = UndefElement::add;
961}
962
964 m_undefinedElementBehavior = UndefElement::error;
965}
966
967bool Phase::ready() const
968{
969 return (m_kk > 0);
970}
971
973 m_stateNum++;
974 m_cache.clear();
975}
976
977void Phase::setMolecularWeight(const int k, const double mw)
978{
979 m_molwts[k] = mw;
980 m_rmolwts[k] = 1.0/mw;
981
982 transform(m_y.begin(), m_y.end(), m_rmolwts.begin(), m_ym.begin(),
983 multiplies<double>());
984 m_mmw = 1.0 / accumulate(m_ym.begin(), m_ym.end(), 0.0);
985}
986
988 m_stateNum++;
989}
990
991vector<double> Phase::getCompositionFromMap(const Composition& comp) const
992{
993 vector<double> X(m_kk);
994 for (const auto& [name, value] : comp) {
995 size_t loc = speciesIndex(name, true);
996 X[loc] = value;
997 }
998 return X;
999}
1000
1001void Phase::massFractionsToMoleFractions(span<const double> Y, span<double> X) const
1002{
1003 checkArraySize("Phase::massFractionsToMoleFractions", Y.size(), m_kk);
1004 checkArraySize("Phase::massFractionsToMoleFractions", X.size(), m_kk);
1005 double rmmw = 0.0;
1006 for (size_t k = 0; k != m_kk; ++k) {
1007 rmmw += Y[k]/m_molwts[k];
1008 }
1009 if (rmmw == 0.0) {
1010 throw CanteraError("Phase::massFractionsToMoleFractions",
1011 "no input composition given");
1012 }
1013 for (size_t k = 0; k != m_kk; ++k) {
1014 X[k] = Y[k]/(rmmw*m_molwts[k]);
1015 }
1016}
1017
1018void Phase::moleFractionsToMassFractions(span<const double> X, span<double> Y) const
1019{
1020 checkArraySize("Phase::moleFractionsToMassFractions", X.size(), m_kk);
1021 checkArraySize("Phase::moleFractionsToMassFractions", Y.size(), m_kk);
1022 double mmw = dot(m_molwts.begin(), m_molwts.end(), X.begin());
1023 if (mmw == 0.0) {
1024 throw CanteraError("Phase::moleFractionsToMassFractions",
1025 "no input composition given");
1026 }
1027 double rmmw = 1.0/mmw;
1028 for (size_t k = 0; k != m_kk; ++k) {
1029 Y[k] = X[k]*m_molwts[k]*rmmw;
1030 }
1031}
1032
1033} // namespace Cantera
#define CT_ELEM_TYPE_ELECTRONCHARGE
This refers to conservation of electrons.
Definition Elements.h:41
#define ENTROPY298_UNKNOWN
Number indicating we don't know the entropy of the element in its most stable state at 298....
Definition Elements.h:85
Header file for class Phase.
Declaration for class Cantera::Species.
Headers for the factory class that can create known ThermoPhase objects (see Thermodynamic Properties...
A map of string keys to values whose type can vary at runtime.
Definition AnyMap.h:431
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
static AnyMap fromYamlFile(const string &name, const string &parent_name="")
Create an AnyMap from a YAML file.
Definition AnyMap.cpp:1841
Base class for exceptions thrown by Cantera classes.
An array index is out of range.
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
span< const double > inverseMolecularWeights() const
Return a const reference to the internal vector of molecular weights.
Definition Phase.cpp:416
virtual vector< string > partialStates() const
Return a vector of settable partial property sets within a phase.
Definition Phase.cpp:212
map< string, size_t > m_speciesLower
Map of lower-case species names to indices.
Definition Phase.h:955
double massFraction(size_t k) const
Return the mass fraction of a single species.
Definition Phase.cpp:473
virtual double molarDensity() const
Molar density (kmol/m^3).
Definition Phase.cpp:597
void assignDensity(const double density_)
Set the internally stored constant density (kg/m^3) of the phase.
Definition Phase.cpp:618
virtual bool addSpecies(shared_ptr< Species > spec)
Add a Species to this Phase.
Definition Phase.cpp:770
int changeElementType(int m, int elem_type)
Change the element type of the mth constraint Reassigns an element type.
Definition Phase.cpp:94
virtual vector< string > fullStates() const
Return a vector containing full states defining a phase.
Definition Phase.cpp:194
void assertCompressible(const string &setter) const
Ensure that phase is compressible.
Definition Phase.h:842
vector< double > m_workS
Vector of size m_kk, used as a temporary holding area.
Definition Phase.h:906
vector< double > m_speciesComp
Atomic composition of the species.
Definition Phase.h:891
ValueCache m_cache
Cached for saved calculations within each ThermoPhase.
Definition Phase.h:862
size_t m_nSpeciesLocks
Reference counter preventing species addition.
Definition Phase.h:897
vector< string > m_speciesNames
Vector of the species names.
Definition Phase.h:949
size_t nSpecies() const
Returns the number of species in the phase.
Definition Phase.h:247
size_t checkElementIndex(size_t m) const
Check that the specified element index is in range.
Definition Phase.cpp:35
bool m_caseSensitiveSpecies
Flag determining whether case sensitive species names are enforced.
Definition Phase.h:903
vector< string > m_elementNames
element names
Definition Phase.h:960
void ignoreUndefinedElements()
Set behavior when adding a species containing undefined elements to just skip the species.
Definition Phase.cpp:955
UndefElement::behavior m_undefinedElementBehavior
Flag determining behavior when adding species with an undefined element.
Definition Phase.h:900
virtual map< string, size_t > nativeState() const
Return a map of properties defining the native state of a substance.
Definition Phase.cpp:164
double chargeDensity() const
Charge density [C/m^3].
Definition Phase.cpp:628
void addUndefinedElements()
Set behavior when adding a species containing undefined elements to add those elements to the phase.
Definition Phase.cpp:959
vector< int > m_atomicNumbers
element atomic numbers
Definition Phase.h:959
size_t m_kk
Number of species in the phase.
Definition Phase.h:882
int atomicNumber(size_t m) const
Atomic number of element m.
Definition Phase.cpp:84
virtual void modifySpecies(size_t k, shared_ptr< Species > spec)
Modify the thermodynamic data associated with a species.
Definition Phase.cpp:883
virtual void setMolesNoTruncate(span< const double > N)
Set the state of the object with moles in [kmol].
Definition Phase.cpp:550
double m_mmw
mean molecular weight of the mixture (kg kmol-1)
Definition Phase.h:927
double elementalMoleFraction(const size_t m) const
Elemental mole fraction of element m.
Definition Phase.cpp:580
size_t speciesIndex(const string &name, bool raise=true) const
Returns the index of a species named 'name' within the Phase object.
Definition Phase.cpp:127
void massFractionsToMoleFractions(span< const double > Y, span< double > X) const
Converts a mixture composition from mole fractions to mass fractions.
Definition Phase.cpp:1001
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
vector< double > m_rmolwts
inverse of species molecular weights (kmol kg-1)
Definition Phase.h:942
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
virtual bool isCompressible() const
Return whether phase represents a compressible substance.
Definition Phase.h:271
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 removeSpeciesLock()
Decrement species lock counter.
Definition Phase.cpp:916
Composition getMoleFractionsByName(double threshold=0.0) const
Get the mole fractions by name.
Definition Phase.cpp:427
virtual double concentration(const size_t k) const
Concentration of species k.
Definition Phase.cpp:495
double atomicWeight(size_t m) const
Atomic weight of element m.
Definition Phase.cpp:69
void setMassFractionsByName(const Composition &yMap)
Set the species mass fractions by name.
Definition Phase.cpp:374
int elementType(size_t m) const
Return the element constraint type Possible types include:
Definition Phase.cpp:89
virtual void setConcentrationsNoNorm(span< const double > conc)
Set the concentrations without ignoring negative concentrations.
Definition Phase.cpp:530
string speciesName(size_t k) const
Name of the species with index k.
Definition Phase.cpp:143
void getMolecularWeights(span< double > weights) const
Copy the vector of molecular weights into array weights.
Definition Phase.cpp:404
void moleFractionsToMassFractions(span< const double > X, span< double > Y) const
Converts a mixture composition from mass fractions to mole fractions.
Definition Phase.cpp:1018
map< string, size_t > m_speciesIndices
Map of species names to indices.
Definition Phase.h:952
virtual void setDensity(const double density_)
Set the internally stored density (kg/m^3) of the phase.
Definition Phase.cpp:607
Composition getMassFractionsByName(double threshold=0.0) const
Get the mass fractions by name.
Definition Phase.cpp:439
virtual size_t stateSize() const
Return size of vector defining internal state of the phase.
Definition Phase.cpp:249
string nativeMode() const
Return string acronym representing the native state of a Phase.
Definition Phase.cpp:181
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
map< string, shared_ptr< Species > > m_species
Map of Species objects.
Definition Phase.h:895
size_t findSpeciesLower(const string &nameStr) const
Find lowercase species name in m_speciesIndices when case sensitive species names are not enforced an...
Definition Phase.cpp:108
vector< double > m_molwts
species molecular weights (kg kmol-1)
Definition Phase.h:940
virtual vector< string > findIsomers(const Composition &compMap) const
Return a vector with isomers names matching a given composition map.
Definition Phase.cpp:925
virtual bool isPure() const
Return whether phase represents a pure (single species) substance.
Definition Phase.h:261
vector< double > m_y
Mass fractions of the species.
Definition Phase.h:938
void setMoleFractionsByName(const Composition &xMap)
Set the species mole fractions by name.
Definition Phase.cpp:331
vector< int > m_elem_type
Vector of element types.
Definition Phase.h:961
double sum_xlogx() const
Evaluate .
Definition Phase.cpp:643
vector< AnyMap > elementDefinitions() const
Return explicit element definitions needed to reconstruct this phase.
Definition Phase.cpp:718
double mean_X(span< const double > Q) const
Evaluate the mole-fraction-weighted mean of an array Q.
Definition Phase.cpp:637
string m_name
Name of the phase.
Definition Phase.h:917
double moleFraction(size_t k) const
Return the mole fraction of a single species.
Definition Phase.cpp:457
double m_dens
Density (kg m-3).
Definition Phase.h:925
virtual void setConcentrations(span< const double > conc)
Set the concentrations to the specified values within the phase.
Definition Phase.cpp:507
const vector< string > & elementNames() const
Return a read-only reference to the vector of element names.
Definition Phase.cpp:64
virtual void getConcentrations(span< double > c) const
Get the species concentrations (kmol/m^3).
Definition Phase.cpp:501
virtual double density() const
Density (kg/m^3).
Definition Phase.h:611
virtual void compositionChanged()
Apply changes to the state which are needed after the composition changes.
Definition Phase.cpp:987
vector< double > m_atomicWeights
element atomic weights (kg kmol-1)
Definition Phase.h:958
double nAtoms(size_t k, size_t m) const
Number of atoms of element m in species k.
Definition Phase.cpp:101
void addSpeciesAlias(const string &name, const string &alias)
Add a species alias (that is, a user-defined alternative species name).
Definition Phase.cpp:900
span< const double > atomicWeights() const
Return a read-only reference to the vector of atomic weights.
Definition Phase.cpp:79
size_t checkSpeciesIndex(size_t k) const
Check that the specified species index is in range.
Definition Phase.cpp:156
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
void setMolecularWeight(const int k, const double mw)
Set the molecular weight of a single species to a given value.
Definition Phase.cpp:977
vector< double > m_entropy298
Entropy at 298.15 K and 1 bar of stable state pure elements (J kmol-1)
Definition Phase.h:964
void getCharges(span< double > charges) const
Copy the vector of species charges into array charges.
Definition Phase.cpp:421
virtual void setMoleFractions_NoNorm(span< const double > x)
Set the mole fractions to the specified values without normalizing.
Definition Phase.cpp:321
vector< double > m_ym
m_ym[k] = mole fraction of species k divided by the mean molecular weight of mixture.
Definition Phase.h:931
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
virtual bool ready() const
Returns a bool indicating whether the object is ready for use.
Definition Phase.cpp:967
double molecularWeight(size_t k) const
Molecular weight of species k.
Definition Phase.cpp:398
double elementalMassFraction(const size_t m) const
Elemental mass fraction of element m.
Definition Phase.cpp:570
shared_ptr< Species > species(const string &name) const
Return the Species object for the named species.
Definition Phase.cpp:943
virtual double molarVolume() const
Molar volume (m^3/kmol).
Definition Phase.cpp:602
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
int m_stateNum
State Change variable.
Definition Phase.h:946
void throwUndefinedElements()
Set the behavior when adding a species containing undefined elements to throw an exception.
Definition Phase.cpp:963
void setName(const string &nm)
Sets the string name for the phase.
Definition Phase.cpp:25
size_t m_mm
Number of elements.
Definition Phase.h:957
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
double entropyElement298(size_t m) const
Entropy of the element in its standard state at 298 K and 1 bar.
Definition Phase.cpp:74
vector< double > m_speciesCharge
Vector of species charges.
Definition Phase.h:893
size_t addElement(const string &symbol, double weight=-12345.0, int atomicNumber=0, double entropy298=ENTROPY298_UNKNOWN, int elem_type=CT_ELEM_TYPE_ABSPOS)
Add an element.
Definition Phase.cpp:652
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
void clear()
Clear all cached values.
string toLowerCopy(const string &input)
Convert to lower case.
Composition parseCompString(const string &ss, const vector< string > &names)
Parse a composition string into a map consisting of individual key:composition pairs.
double dot(InputIter x_begin, InputIter x_end, InputIter2 y_begin)
Function that calculates a templated inner product.
Definition utilities.h:96
void scale(InputIter begin, InputIter end, OutputIter out, S scale_factor)
Multiply elements of an array by a scale factor.
Definition utilities.h:118
const double Faraday
Faraday constant [C/kmol].
Definition ct_defs.h:134
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
double getElementWeight(const string &ename)
Get the atomic weight of an element.
Definition Elements.cpp:251
const double Tiny
Small number to compare differences of mole fractions against.
Definition ct_defs.h:176
const double SmallNumber
smallest number to compare to zero.
Definition ct_defs.h:161
const map< string, double > & elementWeights()
Get a map with the element and isotope symbols and names as keys and weights as values.
Definition Elements.cpp:246
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.
Various templated functions that carry out common vector and polynomial operations (see Templated Arr...