Cantera  4.0.0a2
Loading...
Searching...
No Matches
MultiPhase.cpp
Go to the documentation of this file.
1/**
2 * @file MultiPhase.cpp
3 * Definitions for the @link Cantera::MultiPhase MultiPhase@endlink
4 * object that is used to set up multiphase equilibrium problems (see @ref equilGroup).
5 */
6
7// This file is part of Cantera. See License.txt in the top-level directory or
8// at https://cantera.org/license.txt for license and copyright information.
9
17
18using namespace std;
19
20namespace Cantera
21{
22
23MultiPhase::~MultiPhase()
24{
25 for (size_t i = 0; i < m_phase.size(); i++) {
26 m_phase[i]->removeSpeciesLock();
27 }
28}
29
30void MultiPhase::addPhase(shared_ptr<ThermoPhase> p, double moles)
31{
32 if (m_init) {
33 throw CanteraError("MultiPhase::addPhase",
34 "phases cannot be added after init() has been called.");
35 }
36
37 if (!p->compatibleWithMultiPhase()) {
38 throw CanteraError("MultiPhase::addPhase", "Phase '{}'' is not "
39 "compatible with MultiPhase equilibrium solver", p->name());
40 }
41
42 p->addSpeciesLock(); // lock species list
43 m_phase.push_back(p); // save the pointer to the phase object
44
45 // store its number of moles
46 m_moles.push_back(moles);
47 m_temp_OK.push_back(true);
48
49 // update the total number of species
50 m_nsp += p->nSpecies();
51
52 // determine if this phase has new elements for each new element, add an
53 // entry in the map from names to index number + 1:
54
55 // iterate over the elements in this phase
56 for (size_t m = 0; m < p->nElements(); m++) {
57 string ename = p->elementName(m);
58
59 // if no entry is found for this element name, then it is a new element.
60 // In this case, add the name to the list of names, increment the
61 // element count, and add an entry to the name->(index+1) map.
62 if (m_enamemap.find(ename) == m_enamemap.end()) {
63 m_enamemap[ename] = m_nel + 1;
64 m_enames.push_back(ename);
65 m_atomicNumber.push_back(p->atomicNumber(m));
66
67 // Element 'E' (or 'e') is special. Note its location.
68 if (ename == "E" || ename == "e") {
69 m_eloc = m_nel;
70 }
71
72 m_nel++;
73 }
74 }
75
76 // If the mixture temperature hasn't been set, then set the temperature and
77 // pressure to the values for the phase being added. There is no good way to
78 // do this. However, this will be overridden later.
79 if (m_temp == 298.15 && p->temperature() > 2.0E-3) {
80 m_temp = p->temperature();
81 m_press = p->pressure();
82 }
83
84 // If this is a solution phase, update the minimum and maximum mixture
85 // temperatures. Stoichiometric phases are excluded, since a mixture may
86 // define multiple stoichiometric phases, each of which has thermo data
87 // valid only over a limited range. For example, a mixture might be defined
88 // to contain a phase representing water ice and one representing liquid
89 // water, only one of which should be present if the mixture represents an
90 // equilibrium state.
91 if (p->nSpecies() > 1) {
92 m_Tmin = std::max(p->minTemp(), m_Tmin);
93 m_Tmax = std::min(p->maxTemp(), m_Tmax);
94 }
95}
96
98{
99 if (m_init) {
100 return;
101 }
102
103 // allocate space for the atomic composition matrix
104 m_atoms.resize(m_nel, m_nsp, 0.0);
105 m_moleFractions.resize(m_nsp, 0.0);
106 m_elemAbundances.resize(m_nel, 0.0);
107
108 // iterate over the elements
109 // -> fill in m_atoms(m,k), m_snames(k), m_spphase(k), m_spstart(ip)
110 for (size_t m = 0; m < m_nel; m++) {
111 size_t k = 0;
112 // iterate over the phases
113 for (size_t ip = 0; ip < nPhases(); ip++) {
114 auto& p = m_phase[ip];
115 size_t nsp = p->nSpecies();
116 size_t mlocal = p->elementIndex(m_enames[m], false);
117 for (size_t kp = 0; kp < nsp; kp++) {
118 if (mlocal != npos) {
119 m_atoms(m, k) = p->nAtoms(kp, mlocal);
120 }
121 if (m == 0) {
122 m_snames.push_back(p->speciesName(kp));
123 if (kp == 0) {
124 m_spstart.push_back(m_spphase.size());
125 }
126 m_spphase.push_back(ip);
127 }
128 k++;
129 }
130 }
131 }
132
133 // set the initial composition within each phase to the
134 // mole fractions stored in the phase objects
135 m_init = true;
137 updatePhases();
138}
139
141{
142 if (!m_init) {
143 init();
144 }
145 m_phase[n]->setTemperature(m_temp);
146 m_phase[n]->setMoleFractions_NoNorm(
147 span<const double>(&m_moleFractions[m_spstart[n]], m_phase[n]->nSpecies()));
148 m_phase[n]->setPressure(m_press);
149 return *m_phase[n];
150}
151
152double MultiPhase::speciesMoles(size_t k) const
153{
154 size_t ip = m_spphase[k];
155 return m_moles[ip]*m_moleFractions[k];
156}
157
158double MultiPhase::elementMoles(size_t m) const
159{
160 double sum = 0.0;
161 for (size_t i = 0; i < nPhases(); i++) {
162 double phasesum = 0.0;
163 size_t nsp = m_phase[i]->nSpecies();
164 for (size_t ik = 0; ik < nsp; ik++) {
165 size_t k = speciesIndex(ik, i);
166 phasesum += m_atoms(m,k)*m_moleFractions[k];
167 }
168 sum += phasesum * m_moles[i];
169 }
170 return sum;
171}
172
173double MultiPhase::charge() const
174{
175 double sum = 0.0;
176 for (size_t i = 0; i < nPhases(); i++) {
177 sum += phaseCharge(i);
178 }
179 return sum;
180}
181
182size_t MultiPhase::speciesIndex(const string& speciesName, const string& phaseName)
183{
184 if (!m_init) {
185 init();
186 }
187 size_t p = phaseIndex(phaseName, true);
188 size_t k = m_phase[p]->speciesIndex(speciesName, true);
189 return m_spstart[p] + k;
190}
191
192double MultiPhase::phaseCharge(size_t p) const
193{
194 double phasesum = 0.0;
195 size_t nsp = m_phase[p]->nSpecies();
196 for (size_t ik = 0; ik < nsp; ik++) {
197 size_t k = speciesIndex(ik, p);
198 phasesum += m_phase[p]->charge(ik)*m_moleFractions[k];
199 }
200 return Faraday*phasesum*m_moles[p];
201}
202
203void MultiPhase::getChemPotentials(span<double> mu) const
204{
205 updatePhases();
206 size_t loc = 0;
207 for (size_t i = 0; i < nPhases(); i++) {
208 size_t nsp = m_phase[i]->nSpecies();
209 m_phase[i]->getChemPotentials(mu.subspan(loc, nsp));
210 loc += nsp;
211 }
212}
213
214void MultiPhase::getValidChemPotentials(double not_mu, span<double> mu,
215 bool standard) const
216{
217 updatePhases();
218 // iterate over the phases
219 size_t loc = 0;
220 for (size_t i = 0; i < nPhases(); i++) {
221 if (tempOK(i) || m_phase[i]->nSpecies() > 1) {
222 if (!standard) {
223 m_phase[i]->getChemPotentials(mu.subspan(loc, m_phase[i]->nSpecies()));
224 } else {
225 m_phase[i]->getStandardChemPotentials(
226 mu.subspan(loc, m_phase[i]->nSpecies()));
227 }
228 } else {
229 fill(mu.begin() + loc, mu.begin() + loc + m_phase[i]->nSpecies(), not_mu);
230 }
231 loc += m_phase[i]->nSpecies();
232 }
233}
234
235bool MultiPhase::solutionSpecies(size_t k) const
236{
237 if (m_phase[m_spphase[k]]->nSpecies() > 1) {
238 return true;
239 } else {
240 return false;
241 }
242}
243
244double MultiPhase::gibbs() const
245{
246 double sum = 0.0;
247 updatePhases();
248 for (size_t i = 0; i < nPhases(); i++) {
249 if (m_moles[i] > 0.0) {
250 sum += m_phase[i]->gibbs_mole() * m_moles[i];
251 }
252 }
253 return sum;
254}
255
257{
258 double sum = 0.0;
259 updatePhases();
260 for (size_t i = 0; i < nPhases(); i++) {
261 if (m_moles[i] > 0.0) {
262 sum += m_phase[i]->enthalpy_mole() * m_moles[i];
263 }
264 }
265 return sum;
266}
267
269{
270 double sum = 0.0;
271 updatePhases();
272 for (size_t i = 0; i < nPhases(); i++) {
273 if (m_moles[i] > 0.0) {
274 sum += m_phase[i]->intEnergy_mole() * m_moles[i];
275 }
276 }
277 return sum;
278}
279
281{
282 double sum = 0.0;
283 updatePhases();
284 for (size_t i = 0; i < nPhases(); i++) {
285 if (m_moles[i] > 0.0) {
286 sum += m_phase[i]->entropy_mole() * m_moles[i];
287 }
288 }
289 return sum;
290}
291
292double MultiPhase::cp() const
293{
294 double sum = 0.0;
295 updatePhases();
296 for (size_t i = 0; i < nPhases(); i++) {
297 if (m_moles[i] > 0.0) {
298 sum += m_phase[i]->cp_mole() * m_moles[i];
299 }
300 }
301 return sum;
302}
303
304void MultiPhase::setPhaseMoleFractions(const size_t n, span<const double> x)
305{
306 if (!m_init) {
307 init();
308 }
309 auto& p = m_phase[n];
310 p->setState_TPX(m_temp, m_press, x.subspan(0, p->nSpecies()));
311 size_t istart = m_spstart[n];
312 for (size_t k = 0; k < p->nSpecies(); k++) {
313 m_moleFractions[istart+k] = x[k];
314 }
315}
316
318{
319 size_t kk = nSpecies();
320 vector<double> moles(kk, 0.0);
321 for (size_t k = 0; k < kk; k++) {
322 moles[k] = std::max(getValue(xMap, speciesName(k), 0.0), 0.0);
323 }
324 setMoles(moles);
325}
326
327void MultiPhase::setMolesByName(const string& x)
328{
329 // build the composition map from the string, and then set the moles.
331 setMolesByName(xx);
332}
333
334void MultiPhase::getMoles(span<double> molNum) const
335{
336 // First copy in the mole fractions
337 copy(m_moleFractions.begin(), m_moleFractions.end(), molNum.begin());
338 double* dtmp = molNum.data();
339 for (size_t ip = 0; ip < nPhases(); ip++) {
340 double phasemoles = m_moles[ip];
341 size_t nsp = m_phase[ip]->nSpecies();
342 for (size_t ik = 0; ik < nsp; ik++) {
343 *(dtmp++) *= phasemoles;
344 }
345 }
346}
347
348void MultiPhase::setMoles(span<const double> n)
349{
350 checkArraySize("MultiPhase::setMoles", n.size(), nSpecies());
351 if (!m_init) {
352 init();
353 }
354 size_t loc = 0;
355 size_t k = 0;
356 for (size_t ip = 0; ip < nPhases(); ip++) {
357 auto& p = m_phase[ip];
358 size_t nsp = p->nSpecies();
359 double phasemoles = 0.0;
360 for (size_t ik = 0; ik < nsp; ik++) {
361 phasemoles += n[k];
362 k++;
363 }
364 m_moles[ip] = phasemoles;
365 if (nsp > 1) {
366 if (phasemoles > SmallNumber) {
367 p->setState_TPX(m_temp, m_press, n.subspan(loc, nsp));
368 p->getMoleFractions(span<double>(&m_moleFractions[loc], nsp));
369 } else {
370 p->getMoleFractions(span<double>(&m_moleFractions[loc], nsp));
371 }
372 } else {
373 m_moleFractions[loc] = 1.0;
374 }
375 loc += nsp;
376 }
377}
378
379void MultiPhase::getElemAbundances(span<double> elemAbundances) const
380{
382 for (size_t eGlobal = 0; eGlobal < m_nel; eGlobal++) {
383 elemAbundances[eGlobal] = m_elemAbundances[eGlobal];
384 }
385}
386
388{
389 size_t loc = 0;
390 double spMoles;
391 for (size_t eGlobal = 0; eGlobal < m_nel; eGlobal++) {
392 m_elemAbundances[eGlobal] = 0.0;
393 }
394 for (size_t ip = 0; ip < nPhases(); ip++) {
395 size_t nspPhase = m_phase[ip]->nSpecies();
396 double phasemoles = m_moles[ip];
397 for (size_t ik = 0; ik < nspPhase; ik++) {
398 size_t kGlobal = loc + ik;
399 spMoles = m_moleFractions[kGlobal] * phasemoles;
400 for (size_t eGlobal = 0; eGlobal < m_nel; eGlobal++) {
401 m_elemAbundances[eGlobal] += m_atoms(eGlobal, kGlobal) * spMoles;
402 }
403 }
404 loc += nspPhase;
405 }
406}
407
408double MultiPhase::volume() const
409{
410 double sum = 0;
411 for (size_t i = 0; i < nPhases(); i++) {
412 double vol = 1.0/m_phase[i]->molarDensity();
413 sum += m_moles[i] * vol;
414 }
415 return sum;
416}
417
418namespace {
419
420struct EquilPhaseGuard
421{
422 explicit EquilPhaseGuard(MultiPhase& mix) : m_mix(mix)
423 {
424 for (size_t ip = 0; ip < m_mix.nPhases(); ++ip) {
425 m_mix.phase(ip).beginEquilibrate();
426 }
427 }
428
429 ~EquilPhaseGuard()
430 {
431 for (size_t ip = 0; ip < m_mix.nPhases(); ++ip) {
432 m_mix.phase(ip).endEquilibrate();
433 }
434 }
435
436private:
437 MultiPhase& m_mix;
438};
439
440} // namespace
441
442double MultiPhase::equilibrate_MultiPhaseEquil(int XY, double err, int maxsteps,
443 int maxiter, int loglevel)
444{
445 bool strt = false;
446 double dta = 0.0;
447 if (!m_init) {
448 init();
449 }
450
451 if (XY == TP) {
452 // create an equilibrium manager
453 MultiPhaseEquil e(this);
454 return e.equilibrate(XY, err, maxsteps, loglevel);
455 } else if (XY == HP) {
456 double h0 = enthalpy();
457 double Tlow = 0.5*m_Tmin; // lower bound on T
458 double Thigh = 2.0*m_Tmax; // upper bound on T
459 double Hlow = Undef, Hhigh = Undef;
460 for (int n = 0; n < maxiter; n++) {
461 try {
462 // if 'strt' is false, the current composition will be used as the
463 // starting estimate; otherwise it will be estimated. Construction can
464 // fail if the temperature guess puts a phase outside its valid
465 // temperature range while it still has moles in the current
466 // composition; the catch block below recovers by recomputing the
467 // initial composition (strt=true).
468 MultiPhaseEquil e(this, strt);
469 // start with a loose error tolerance, but tighten it as we get close to
470 // the final temperature
471 e.equilibrate(TP, err, maxsteps, loglevel);
472 double hnow = enthalpy();
473 // the equilibrium enthalpy monotonically increases with T;
474 // if the current value is below the target, the we know the
475 // current temperature is too low. Set
476 if (hnow < h0) {
477 if (m_temp > Tlow) {
478 Tlow = m_temp;
479 Hlow = hnow;
480 }
481 } else {
482 // the current enthalpy is greater than the target;
483 // therefore the current temperature is too high.
484 if (m_temp < Thigh) {
485 Thigh = m_temp;
486 Hhigh = hnow;
487 }
488 }
489 double dt;
490 if (Hlow != Undef && Hhigh != Undef) {
491 double cpb = (Hhigh - Hlow)/(Thigh - Tlow);
492 dt = (h0 - hnow)/cpb;
493 dta = fabs(dt);
494 double dtmax = 0.5*fabs(Thigh - Tlow);
495 if (dta > dtmax) {
496 dt *= dtmax/dta;
497 }
498 } else {
499 double tnew = sqrt(Tlow*Thigh);
500 dt = tnew - m_temp;
501 }
502
503 double herr = fabs((h0 - hnow)/h0);
504
505 if (herr < err) {
506 return err;
507 }
508 double tnew = m_temp + dt;
509 if (tnew < 0.0) {
510 tnew = 0.5*m_temp;
511 }
512 setTemperature(tnew);
513
514 // if the size of Delta T is not too large, use
515 // the current composition as the starting estimate
516 if (dta < 100.0) {
517 strt = false;
518 }
519
520 } catch (CanteraError&) {
521 if (!strt) {
522 strt = true;
523 } else {
524 double tnew = 0.5*(m_temp + Thigh);
525 if (fabs(tnew - m_temp) < 1.0) {
526 tnew = m_temp + 1.0;
527 }
528 setTemperature(tnew);
529 }
530 }
531 }
532 throw CanteraError("MultiPhase::equilibrate_MultiPhaseEquil",
533 "No convergence for T");
534 } else if (XY == SP) {
535 double s0 = entropy();
536 double Tlow = 1.0; // lower bound on T
537 double Thigh = 1.0e6; // upper bound on T
538 for (int n = 0; n < maxiter; n++) {
539 try {
540 // Construction (inside the try so the catch below can recover) can fail
541 // if the temperature guess puts a condensed phase outside its valid
542 // range while it still has moles in the current composition; recovery
543 // recomputes the initial composition (strt=true).
544 MultiPhaseEquil e(this, strt);
545 e.equilibrate(TP, err, maxsteps, loglevel);
546 double snow = entropy();
547 if (snow < s0) {
548 Tlow = std::max(Tlow, m_temp);
549 } else {
550 Thigh = std::min(Thigh, m_temp);
551 }
552 double dt = (s0 - snow)*m_temp/cp();
553 double dtmax = 0.5*fabs(Thigh - Tlow);
554 dtmax = (dtmax > 500.0 ? 500.0 : dtmax);
555 dta = fabs(dt);
556 if (dta > dtmax) {
557 dt *= dtmax/dta;
558 }
559 if (dta < 1.0e-4) {
560 return err;
561 }
562 double tnew = m_temp + dt;
563 setTemperature(tnew);
564
565 // if the size of Delta T is not too large, use
566 // the current composition as the starting estimate
567 if (dta < 100.0) {
568 strt = false;
569 }
570 } catch (CanteraError&) {
571 if (!strt) {
572 strt = true;
573 } else {
574 double tnew = 0.5*(m_temp + Thigh);
575 setTemperature(tnew);
576 }
577 }
578 }
579 throw CanteraError("MultiPhase::equilibrate_MultiPhaseEquil",
580 "No convergence for T");
581 } else if (XY == TV) {
582 double v0 = volume();
583 bool start = true;
584 for (int n = 0; n < maxiter; n++) {
585 double pnow = pressure();
586 MultiPhaseEquil e(this, start);
587 start = false;
588
589 e.equilibrate(TP, err, maxsteps, loglevel);
590 double vnow = volume();
591 double verr = fabs((v0 - vnow)/v0);
592
593 if (verr < err) {
594 return err;
595 }
596 // find dV/dP
597 setPressure(pnow*1.01);
598 double dVdP = (volume() - vnow)/(0.01*pnow);
599 setPressure(pnow + 0.5*(v0 - vnow)/dVdP);
600 }
601 } else {
602 throw CanteraError("MultiPhase::equilibrate_MultiPhaseEquil",
603 "unknown option");
604 }
605 return -1.0;
606}
607
608void MultiPhase::equilibrate(const string& XY, const string& solver,
609 double rtol, int max_steps, int max_iter,
610 int estimate_equil, int log_level)
611{
612 // Save the initial state so that it can be restored in case one of the
613 // solvers fails
614 vector<double> initial_moleFractions = m_moleFractions;
615 vector<double> initial_moles = m_moles;
616 double initial_T = m_temp;
617 double initial_P = m_press;
618 int ixy = _equilflag(XY.c_str());
619 EquilPhaseGuard phase_guard(*this);
620 if (solver == "auto" || solver == "vcs") {
621 try {
622 debuglog("Trying VCS equilibrium solver\n", log_level);
623 vcs_MultiPhaseEquil eqsolve(this, log_level-1);
624 int ret = eqsolve.equilibrate(ixy, estimate_equil, log_level-1,
625 rtol, max_steps);
626 if (ret) {
627 throw CanteraError("MultiPhase::equilibrate",
628 "VCS solver failed. Return code: {}", ret);
629 }
630 debuglog("VCS solver succeeded\n", log_level);
631 return;
632 } catch (std::exception& err) {
633 debuglog("VCS solver failed.\n", log_level);
634 debuglog(err.what(), log_level);
635 m_moleFractions = initial_moleFractions;
636 m_moles = initial_moles;
637 m_temp = initial_T;
638 m_press = initial_P;
639 updatePhases();
640 if (solver == "auto") {
641 } else {
642 throw;
643 }
644 }
645 }
646
647 if (solver == "auto" || solver == "gibbs") {
648 try {
649 debuglog("Trying MultiPhaseEquil (Gibbs) equilibrium solver\n",
650 log_level);
651 equilibrate_MultiPhaseEquil(ixy, rtol, max_steps, max_iter,
652 log_level-1);
653 debuglog("MultiPhaseEquil solver succeeded\n", log_level);
654 return;
655 } catch (std::exception& err) {
656 debuglog("MultiPhaseEquil solver failed.\n", log_level);
657 debuglog(err.what(), log_level);
658 m_moleFractions = initial_moleFractions;
659 m_moles = initial_moles;
660 m_temp = initial_T;
661 m_press = initial_P;
662 updatePhases();
663 throw;
664 }
665 }
666
667 if (solver != "auto") {
668 throw CanteraError("MultiPhase::equilibrate",
669 "Invalid solver specified: '" + solver + "'");
670 }
671}
672
673void MultiPhase::setTemperature(const double T)
674{
675 if (!m_init) {
676 init();
677 }
678 m_temp = T;
679 updatePhases();
680}
681
682size_t MultiPhase::checkElementIndex(size_t m) const
683{
684 if (m < m_nel) {
685 return m;
686 }
687 throw IndexError("MultiPhase::checkElementIndex", "elements", m, m_nel);
688}
689
690string MultiPhase::elementName(size_t m) const
691{
692 if (m < m_nel) {
693 return m_enames[m];
694 }
695 throw IndexError("MultiPhase::elementName", "element", m, m_nel);
696}
697
698size_t MultiPhase::elementIndex(const string& name, bool raise) const
699{
700 for (size_t e = 0; e < m_nel; e++) {
701 if (m_enames[e] == name) {
702 return e;
703 }
704 }
705 if (!raise) {
706 return npos;
707 }
708 throw CanteraError("MultiPhase::elementIndex", "Element '{}' not found", name);
709}
710
711string MultiPhase::speciesName(size_t k) const
712{
713 if (k < m_nsp) {
714 return m_snames[k];
715 }
716 throw IndexError("MultiPhase::speciesName", "species", k, m_nsp);
717}
718
719double MultiPhase::nAtoms(const size_t kGlob, const size_t mGlob) const
720{
721 return m_atoms(mGlob, kGlob);
722}
723
724void MultiPhase::getMoleFractions(span<double> x) const
725{
726 checkArraySize("MultiPhase::getMoleFractions", x.size(), nSpecies());
727 std::copy(m_moleFractions.begin(), m_moleFractions.end(), x.begin());
728}
729
730string MultiPhase::phaseName(size_t iph) const
731{
732 if (iph < m_phase.size()) {
733 return m_phase[iph]->name();
734 }
735 throw IndexError("MultiPhase::phaseName", "phase", iph, m_phase.size());
736}
737
738size_t MultiPhase::phaseIndex(const string& pName, bool raise) const
739{
740 for (int iph = 0; iph < (int) nPhases(); iph++) {
741 if (m_phase[iph]->name() == pName) {
742 return iph;
743 }
744 }
745 if (!raise) {
746 return npos;
747 }
748 throw CanteraError("MultiPhase::phaseIndex", "Phase '{}' not found", pName);
749}
750
751double MultiPhase::phaseMoles(const size_t n) const
752{
753 return m_moles[n];
754}
755
756void MultiPhase::setPhaseMoles(const size_t n, const double moles)
757{
758 m_moles[n] = moles;
759}
760
761size_t MultiPhase::speciesPhaseIndex(const size_t kGlob) const
762{
763 return m_spphase[kGlob];
764}
765
766double MultiPhase::moleFraction(const size_t kGlob) const
767{
768 return m_moleFractions[kGlob];
769}
770
771bool MultiPhase::tempOK(const size_t p) const
772{
773 return m_temp_OK[p];
774}
775
777{
778 size_t loc = 0;
779 for (size_t ip = 0; ip < nPhases(); ip++) {
780 auto& p = m_phase[ip];
781 p->getMoleFractions(span<double>(m_moleFractions.data() + loc, p->nSpecies()));
782 loc += p->nSpecies();
783 }
785}
786
788{
789 size_t loc = 0;
790 for (size_t p = 0; p < nPhases(); p++) {
791 size_t nsp = m_phase[p]->nSpecies();
792 span<const double> X(m_moleFractions.data() + loc, nsp);
793 m_phase[p]->setState_TPX(m_temp, m_press, X);
794 loc += nsp;
795 m_temp_OK[p] = true;
796 if (m_temp < m_phase[p]->minTemp() || m_temp > m_phase[p]->maxTemp()) {
797 m_temp_OK[p] = false;
798 }
799 }
800}
801
802std::ostream& operator<<(std::ostream& s, MultiPhase& x)
803{
804 x.updatePhases();
805 for (size_t ip = 0; ip < x.nPhases(); ip++) {
806 if (x.phase(ip).name() != "") {
807 s << "*************** " << x.phase(ip).name() << " *****************" << std::endl;
808 } else {
809 s << "*************** Phase " << ip << " *****************" << std::endl;
810 }
811 s << "Moles: " << x.phaseMoles(ip) << std::endl;
812
813 s << x.phase(ip).report() << std::endl;
814 }
815 return s;
816}
817
818}
Chemical equilibrium.
Headers for the MultiPhase object that is used to set up multiphase equilibrium problems (see Chemica...
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
Base class for exceptions thrown by Cantera classes.
void resize(size_t n, size_t m, double v=0.0) override
Resize the matrix.
An array index is out of range.
Multiphase chemical equilibrium solver.
A class for multiphase mixtures.
Definition MultiPhase.h:62
void getMoleFractions(span< double > x) const
Returns the global Species mole fractions.
void init()
Process phases and build atomic composition array.
size_t speciesIndex(size_t k, size_t p) const
Return the global index of the species belonging to phase number p with local index k within the phas...
Definition MultiPhase.h:206
bool solutionSpecies(size_t kGlob) const
Return true is species kGlob is a species in a multicomponent solution phase.
void getMoles(span< double > molNum) const
Get the mole numbers of all species in the multiphase object.
double nAtoms(const size_t kGlob, const size_t mGlob) const
Returns the Number of atoms of global element mGlob in global species kGlob.
void setMolesByName(const Composition &xMap)
Set the number of moles of species in the mixture.
DenseMatrix m_atoms
Global Stoichiometric Coefficient array.
Definition MultiPhase.h:536
double gibbs() const
The Gibbs function of the mixture [J].
void setMoles(span< const double > n)
Sets all of the global species mole numbers.
size_t m_nel
Number of distinct elements in all of the phases.
Definition MultiPhase.h:581
double speciesMoles(size_t kGlob) const
Returns the moles of global species k. units = kmol.
double m_temp
Current value of the temperature (kelvin)
Definition MultiPhase.h:575
void calcElemAbundances() const
Calculate the element abundance vector.
size_t nSpecies() const
Number of species, summed over all phases.
Definition MultiPhase.h:116
size_t checkElementIndex(size_t m) const
Check that the specified element index is in range.
double enthalpy() const
The enthalpy of the mixture [J].
double pressure() const
Pressure [Pa].
Definition MultiPhase.h:352
vector< size_t > m_spstart
Vector of ints containing of first species index in the global list of species for each phase.
Definition MultiPhase.h:555
vector< size_t > m_spphase
Mapping between the global species number and the phase ID.
Definition MultiPhase.h:547
double minTemp() const
Minimum temperature for which all solution phases have valid thermo data.
Definition MultiPhase.h:226
vector< double > m_moleFractions
Locally stored vector of mole fractions of all species comprising the MultiPhase object.
Definition MultiPhase.h:540
size_t phaseIndex(const string &pName, bool raise=true) const
Returns the index, given the phase name.
void getElemAbundances(span< double > elemAbundances) const
Retrieves a vector of element abundances.
vector< double > m_elemAbundances
Vector of element abundances.
Definition MultiPhase.h:613
void setPhaseMoleFractions(const size_t n, span< const double > x)
Set the Mole fractions of the nth phase.
double equilibrate_MultiPhaseEquil(int XY, double err, int maxsteps, int maxiter, int loglevel)
Set the mixture to a state of chemical equilibrium using the MultiPhaseEquil solver.
vector< bool > m_temp_OK
Vector of bools indicating whether temperatures are ok for phases.
Definition MultiPhase.h:598
double phaseCharge(size_t p) const
Charge (Coulombs) of phase with index p.
size_t nPhases() const
Number of phases.
Definition MultiPhase.h:389
size_t m_eloc
Global ID of the element corresponding to the electronic charge.
Definition MultiPhase.h:591
double entropy() const
The entropy of the mixture [J/K].
double moleFraction(const size_t kGlob) const
Returns the mole fraction of global species k.
void getChemPotentials(span< double > mu) const
Returns a vector of Chemical potentials.
string speciesName(size_t kGlob) const
Name of species with global index kGlob.
vector< shared_ptr< ThermoPhase > > m_phase
Vector of phase objects.
Definition MultiPhase.h:528
double m_press
Current value of the pressure (Pa)
Definition MultiPhase.h:578
bool tempOK(size_t p) const
Return true if the phase p has valid thermo data for the current temperature.
map< string, size_t > m_enamemap
Returns the global element index, given the element string name.
Definition MultiPhase.h:572
size_t speciesPhaseIndex(const size_t kGlob) const
Returns the phase index of the Kth "global" species.
void getValidChemPotentials(double not_mu, span< double > mu, bool standard=false) const
Returns a vector of Valid chemical potentials.
void setPressure(double P)
Set the pressure [Pa].
Definition MultiPhase.h:367
vector< string > m_enames
String names of the global elements.
Definition MultiPhase.h:559
void addPhase(shared_ptr< ThermoPhase > p, double moles)
Add a phase to the mixture.
double phaseMoles(const size_t n) const
Return the number of moles in phase n.
size_t m_nsp
Number of distinct species in all of the phases.
Definition MultiPhase.h:584
vector< int > m_atomicNumber
Atomic number of each global element.
Definition MultiPhase.h:562
double volume() const
The total mixture volume [m^3].
void uploadMoleFractionsFromPhases()
Update the locally-stored composition within this object to match the current compositions of the pha...
bool m_init
True if the init() routine has been called, and the MultiPhase frozen.
Definition MultiPhase.h:587
vector< string > m_snames
Vector of species names in the problem.
Definition MultiPhase.h:566
double m_Tmin
Minimum temperature for which thermo parameterizations are valid.
Definition MultiPhase.h:602
void updatePhases() const
Set the states of the phase objects to the locally-stored state within this MultiPhase object.
double charge() const
Total charge summed over all phases (Coulombs).
double cp() const
Heat capacity at constant pressure [J/K].
size_t elementIndex(const string &name, bool raise=true) const
Returns the index of the element with name name.
void setTemperature(const double T)
Set the temperature [K].
ThermoPhase & phase(size_t n)
Return a reference to phase n.
double maxTemp() const
Maximum temperature for which all solution phases have valid thermo data.
Definition MultiPhase.h:233
double IntEnergy() const
The internal energy of the mixture [J].
string elementName(size_t m) const
Returns the name of the global element m.
vector< double > m_moles
Vector of the number of moles in each phase.
Definition MultiPhase.h:525
void setPhaseMoles(const size_t n, const double moles)
Set the number of moles of phase with index n.
double elementMoles(size_t m) const
Total moles of global element m, summed over all phases.
string phaseName(size_t iph) const
Returns the name of the n'th phase.
double m_Tmax
Minimum temperature for which thermo parameterizations are valid.
Definition MultiPhase.h:606
string name() const
Return the name of the phase.
Definition Phase.cpp:20
Base class for a phase with thermodynamic properties.
virtual string report(bool show_thermo=true, double threshold=-1e-14) const
returns a summary of the state of the phase as a string
Cantera's Interface to the Multiphase chemical equilibrium solver.
int equilibrate(int XY, int estimateEquil=0, int printLvl=0, double err=1.0e-6, int maxsteps=VCS_MAXSTEPS, int loglevel=-99)
Equilibrate the solution using the current element abundances stored in the MultiPhase object.
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 MultiPhase object.
void debuglog(const string &msg, int loglevel)
Write a message to the log only if loglevel > 0.
Definition global.h:154
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
const double Undef
Fairly random number to be used to initialize variables against to see if they are subsequently defin...
Definition ct_defs.h:167
const double SmallNumber
smallest number to compare to zero.
Definition ct_defs.h:161
int _equilflag(const char *xy)
map property strings to integers
Definition ChemEquil.cpp:20
std::ostream & operator<<(std::ostream &s, const Array2D &m)
Output the current contents of the Array2D object.
Definition Array.cpp:101
const U & getValue(const map< T, U > &m, const T &key, const U &default_val)
Const accessor for a value in a map.
Definition utilities.h:223
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...
Interface class for the vcsnonlinear solver.