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 equilfunctions).
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 
10 #include "cantera/equil/ChemEquil.h"
11 #include "cantera/equil/MultiPhase.h"
12 #include "cantera/equil/MultiPhaseEquil.h"
13 #include "cantera/equil/vcs_MultiPhaseEquil.h"
14 #include "cantera/thermo/ThermoPhase.h"
15 #include "cantera/base/stringUtils.h"
16 #include "cantera/base/utilities.h"
17 
18 using namespace std;
19 
20 namespace Cantera
21 {
22 
MultiPhase()23 MultiPhase::MultiPhase() :
24     m_temp(298.15),
25     m_press(OneBar),
26     m_nel(0),
27     m_nsp(0),
28     m_init(false),
29     m_eloc(npos),
30     m_Tmin(1.0),
31     m_Tmax(100000.0)
32 {
33 }
34 
addPhases(MultiPhase & mix)35 void MultiPhase::addPhases(MultiPhase& mix)
36 {
37     for (size_t n = 0; n < mix.nPhases(); n++) {
38         addPhase(mix.m_phase[n], mix.m_moles[n]);
39     }
40 }
41 
addPhases(std::vector<ThermoPhase * > & phases,const vector_fp & phaseMoles)42 void MultiPhase::addPhases(std::vector<ThermoPhase*>& phases,
43                            const vector_fp& phaseMoles)
44 {
45     for (size_t n = 0; n < phases.size(); n++) {
46         addPhase(phases[n], phaseMoles[n]);
47     }
48     init();
49 }
50 
addPhase(ThermoPhase * p,doublereal moles)51 void MultiPhase::addPhase(ThermoPhase* p, doublereal moles)
52 {
53     if (m_init) {
54         throw CanteraError("MultiPhase::addPhase",
55                            "phases cannot be added after init() has been called.");
56     }
57 
58     if (!p->compatibleWithMultiPhase()) {
59         throw CanteraError("MultiPhase::addPhase", "Phase '{}'' is not "
60             "compatible with MultiPhase equilibrium solver", p->name());
61     }
62 
63     // save the pointer to the phase object
64     m_phase.push_back(p);
65 
66     // store its number of moles
67     m_moles.push_back(moles);
68     m_temp_OK.push_back(true);
69 
70     // update the total number of species
71     m_nsp += p->nSpecies();
72 
73     // determine if this phase has new elements for each new element, add an
74     // entry in the map from names to index number + 1:
75 
76     // iterate over the elements in this phase
77     for (size_t m = 0; m < p->nElements(); m++) {
78         string ename = p->elementName(m);
79 
80         // if no entry is found for this element name, then it is a new element.
81         // In this case, add the name to the list of names, increment the
82         // element count, and add an entry to the name->(index+1) map.
83         if (m_enamemap.find(ename) == m_enamemap.end()) {
84             m_enamemap[ename] = m_nel + 1;
85             m_enames.push_back(ename);
86             m_atomicNumber.push_back(p->atomicNumber(m));
87 
88             // Element 'E' (or 'e') is special. Note its location.
89             if (ename == "E" || ename == "e") {
90                 m_eloc = m_nel;
91             }
92 
93             m_nel++;
94         }
95     }
96 
97     // If the mixture temperature hasn't been set, then set the temperature and
98     // pressure to the values for the phase being added. There is no good way to
99     // do this. However, this will be overridden later.
100     if (m_temp == 298.15 && p->temperature() > 2.0E-3) {
101         m_temp = p->temperature();
102         m_press = p->pressure();
103     }
104 
105     // If this is a solution phase, update the minimum and maximum mixture
106     // temperatures. Stoichiometric phases are excluded, since a mixture may
107     // define multiple stoichiometric phases, each of which has thermo data
108     // valid only over a limited range. For example, a mixture might be defined
109     // to contain a phase representing water ice and one representing liquid
110     // water, only one of which should be present if the mixture represents an
111     // equilibrium state.
112     if (p->nSpecies() > 1) {
113         m_Tmin = std::max(p->minTemp(), m_Tmin);
114         m_Tmax = std::min(p->maxTemp(), m_Tmax);
115     }
116 }
117 
init()118 void MultiPhase::init()
119 {
120     if (m_init) {
121         return;
122     }
123 
124     // allocate space for the atomic composition matrix
125     m_atoms.resize(m_nel, m_nsp, 0.0);
126     m_moleFractions.resize(m_nsp, 0.0);
127     m_elemAbundances.resize(m_nel, 0.0);
128 
129     // iterate over the elements
130     //   -> fill in m_atoms(m,k), m_snames(k), m_spphase(k), m_spstart(ip)
131     for (size_t m = 0; m < m_nel; m++) {
132         size_t k = 0;
133         // iterate over the phases
134         for (size_t ip = 0; ip < nPhases(); ip++) {
135             ThermoPhase* p = m_phase[ip];
136             size_t nsp = p->nSpecies();
137             size_t mlocal = p->elementIndex(m_enames[m]);
138             for (size_t kp = 0; kp < nsp; kp++) {
139                 if (mlocal != npos) {
140                     m_atoms(m, k) = p->nAtoms(kp, mlocal);
141                 }
142                 if (m == 0) {
143                     m_snames.push_back(p->speciesName(kp));
144                     if (kp == 0) {
145                         m_spstart.push_back(m_spphase.size());
146                     }
147                     m_spphase.push_back(ip);
148                 }
149                 k++;
150             }
151         }
152     }
153 
154     // set the initial composition within each phase to the
155     // mole fractions stored in the phase objects
156     m_init = true;
157     uploadMoleFractionsFromPhases();
158     updatePhases();
159 }
160 
phase(size_t n)161 ThermoPhase& MultiPhase::phase(size_t n)
162 {
163     if (!m_init) {
164         init();
165     }
166     m_phase[n]->setTemperature(m_temp);
167     m_phase[n]->setMoleFractions_NoNorm(&m_moleFractions[m_spstart[n]]);
168     m_phase[n]->setPressure(m_press);
169     return *m_phase[n];
170 }
171 
checkPhaseIndex(size_t m) const172 void MultiPhase::checkPhaseIndex(size_t m) const
173 {
174     if (m >= nPhases()) {
175         throw IndexError("MultiPhase::checkPhaseIndex", "phase", m, nPhases()-1);
176     }
177 }
178 
checkPhaseArraySize(size_t mm) const179 void MultiPhase::checkPhaseArraySize(size_t mm) const
180 {
181     if (nPhases() > mm) {
182         throw ArraySizeError("MultiPhase::checkPhaseIndex", mm, nPhases());
183     }
184 }
185 
speciesMoles(size_t k) const186 doublereal MultiPhase::speciesMoles(size_t k) const
187 {
188     size_t ip = m_spphase[k];
189     return m_moles[ip]*m_moleFractions[k];
190 }
191 
elementMoles(size_t m) const192 doublereal MultiPhase::elementMoles(size_t m) const
193 {
194     doublereal sum = 0.0;
195     for (size_t i = 0; i < nPhases(); i++) {
196         double phasesum = 0.0;
197         size_t nsp = m_phase[i]->nSpecies();
198         for (size_t ik = 0; ik < nsp; ik++) {
199             size_t k = speciesIndex(ik, i);
200             phasesum += m_atoms(m,k)*m_moleFractions[k];
201         }
202         sum += phasesum * m_moles[i];
203     }
204     return sum;
205 }
206 
charge() const207 doublereal MultiPhase::charge() const
208 {
209     doublereal sum = 0.0;
210     for (size_t i = 0; i < nPhases(); i++) {
211         sum += phaseCharge(i);
212     }
213     return sum;
214 }
215 
speciesIndex(const std::string & speciesName,const std::string & phaseName)216 size_t MultiPhase::speciesIndex(const std::string& speciesName, const std::string& phaseName)
217 {
218     if (!m_init) {
219         init();
220     }
221     size_t p = phaseIndex(phaseName);
222     if (p == npos) {
223         throw CanteraError("MultiPhase::speciesIndex", "phase not found: " + phaseName);
224     }
225     size_t k = m_phase[p]->speciesIndex(speciesName);
226     if (k == npos) {
227         throw CanteraError("MultiPhase::speciesIndex", "species not found: " + speciesName);
228     }
229     return m_spstart[p] + k;
230 }
231 
phaseCharge(size_t p) const232 doublereal MultiPhase::phaseCharge(size_t p) const
233 {
234     doublereal phasesum = 0.0;
235     size_t nsp = m_phase[p]->nSpecies();
236     for (size_t ik = 0; ik < nsp; ik++) {
237         size_t k = speciesIndex(ik, p);
238         phasesum += m_phase[p]->charge(ik)*m_moleFractions[k];
239     }
240     return Faraday*phasesum*m_moles[p];
241 }
242 
getChemPotentials(doublereal * mu) const243 void MultiPhase::getChemPotentials(doublereal* mu) const
244 {
245     updatePhases();
246     size_t loc = 0;
247     for (size_t i = 0; i < nPhases(); i++) {
248         m_phase[i]->getChemPotentials(mu + loc);
249         loc += m_phase[i]->nSpecies();
250     }
251 }
252 
getValidChemPotentials(doublereal not_mu,doublereal * mu,bool standard) const253 void MultiPhase::getValidChemPotentials(doublereal not_mu,
254                                         doublereal* mu, bool standard) const
255 {
256     updatePhases();
257     // iterate over the phases
258     size_t loc = 0;
259     for (size_t i = 0; i < nPhases(); i++) {
260         if (tempOK(i) || m_phase[i]->nSpecies() > 1) {
261             if (!standard) {
262                 m_phase[i]->getChemPotentials(mu + loc);
263             } else {
264                 m_phase[i]->getStandardChemPotentials(mu + loc);
265             }
266         } else {
267             fill(mu + loc, mu + loc + m_phase[i]->nSpecies(), not_mu);
268         }
269         loc += m_phase[i]->nSpecies();
270     }
271 }
272 
solutionSpecies(size_t k) const273 bool MultiPhase::solutionSpecies(size_t k) const
274 {
275     if (m_phase[m_spphase[k]]->nSpecies() > 1) {
276         return true;
277     } else {
278         return false;
279     }
280 }
281 
gibbs() const282 doublereal MultiPhase::gibbs() const
283 {
284     doublereal sum = 0.0;
285     updatePhases();
286     for (size_t i = 0; i < nPhases(); i++) {
287         if (m_moles[i] > 0.0) {
288             sum += m_phase[i]->gibbs_mole() * m_moles[i];
289         }
290     }
291     return sum;
292 }
293 
enthalpy() const294 doublereal MultiPhase::enthalpy() const
295 {
296     doublereal sum = 0.0;
297     updatePhases();
298     for (size_t i = 0; i < nPhases(); i++) {
299         if (m_moles[i] > 0.0) {
300             sum += m_phase[i]->enthalpy_mole() * m_moles[i];
301         }
302     }
303     return sum;
304 }
305 
IntEnergy() const306 doublereal MultiPhase::IntEnergy() const
307 {
308     doublereal sum = 0.0;
309     updatePhases();
310     for (size_t i = 0; i < nPhases(); i++) {
311         if (m_moles[i] > 0.0) {
312             sum += m_phase[i]->intEnergy_mole() * m_moles[i];
313         }
314     }
315     return sum;
316 }
317 
entropy() const318 doublereal MultiPhase::entropy() const
319 {
320     doublereal sum = 0.0;
321     updatePhases();
322     for (size_t i = 0; i < nPhases(); i++) {
323         if (m_moles[i] > 0.0) {
324             sum += m_phase[i]->entropy_mole() * m_moles[i];
325         }
326     }
327     return sum;
328 }
329 
cp() const330 doublereal MultiPhase::cp() const
331 {
332     doublereal sum = 0.0;
333     updatePhases();
334     for (size_t i = 0; i < nPhases(); i++) {
335         if (m_moles[i] > 0.0) {
336             sum += m_phase[i]->cp_mole() * m_moles[i];
337         }
338     }
339     return sum;
340 }
341 
setPhaseMoleFractions(const size_t n,const doublereal * const x)342 void MultiPhase::setPhaseMoleFractions(const size_t n, const doublereal* const x)
343 {
344     if (!m_init) {
345         init();
346     }
347     ThermoPhase* p = m_phase[n];
348     p->setState_TPX(m_temp, m_press, x);
349     size_t istart = m_spstart[n];
350     for (size_t k = 0; k < p->nSpecies(); k++) {
351         m_moleFractions[istart+k] = x[k];
352     }
353 }
354 
setMolesByName(const compositionMap & xMap)355 void MultiPhase::setMolesByName(const compositionMap& xMap)
356 {
357     size_t kk = nSpecies();
358     vector_fp moles(kk, 0.0);
359     for (size_t k = 0; k < kk; k++) {
360         moles[k] = std::max(getValue(xMap, speciesName(k), 0.0), 0.0);
361     }
362     setMoles(moles.data());
363 }
364 
setMolesByName(const std::string & x)365 void MultiPhase::setMolesByName(const std::string& x)
366 {
367     // build the composition map from the string, and then set the moles.
368     compositionMap xx = parseCompString(x, m_snames);
369     setMolesByName(xx);
370 }
371 
getMoles(doublereal * molNum) const372 void MultiPhase::getMoles(doublereal* molNum) const
373 {
374     // First copy in the mole fractions
375     copy(m_moleFractions.begin(), m_moleFractions.end(), molNum);
376     doublereal* dtmp = molNum;
377     for (size_t ip = 0; ip < nPhases(); ip++) {
378         doublereal phasemoles = m_moles[ip];
379         ThermoPhase* p = m_phase[ip];
380         size_t nsp = p->nSpecies();
381         for (size_t ik = 0; ik < nsp; ik++) {
382             *(dtmp++) *= phasemoles;
383         }
384     }
385 }
386 
setMoles(const doublereal * n)387 void MultiPhase::setMoles(const doublereal* n)
388 {
389     if (!m_init) {
390         init();
391     }
392     size_t loc = 0;
393     size_t k = 0;
394     for (size_t ip = 0; ip < nPhases(); ip++) {
395         ThermoPhase* p = m_phase[ip];
396         size_t nsp = p->nSpecies();
397         double phasemoles = 0.0;
398         for (size_t ik = 0; ik < nsp; ik++) {
399             phasemoles += n[k];
400             k++;
401         }
402         m_moles[ip] = phasemoles;
403         if (nsp > 1) {
404             if (phasemoles > 0.0) {
405                 p->setState_TPX(m_temp, m_press, n + loc);
406                 p->getMoleFractions(&m_moleFractions[loc]);
407             } else {
408                 p->getMoleFractions(&m_moleFractions[loc]);
409             }
410         } else {
411             m_moleFractions[loc] = 1.0;
412         }
413         loc += nsp;
414     }
415 }
416 
addSpeciesMoles(const int indexS,const doublereal addedMoles)417 void MultiPhase::addSpeciesMoles(const int indexS, const doublereal addedMoles)
418 {
419     vector_fp tmpMoles(m_nsp, 0.0);
420     getMoles(tmpMoles.data());
421     tmpMoles[indexS] += addedMoles;
422     tmpMoles[indexS] = std::max(tmpMoles[indexS], 0.0);
423     setMoles(tmpMoles.data());
424 }
425 
setState_TP(const doublereal T,const doublereal Pres)426 void MultiPhase::setState_TP(const doublereal T, const doublereal Pres)
427 {
428     if (!m_init) {
429         init();
430     }
431     m_temp = T;
432     m_press = Pres;
433     updatePhases();
434 }
435 
setState_TPMoles(const doublereal T,const doublereal Pres,const doublereal * n)436 void MultiPhase::setState_TPMoles(const doublereal T, const doublereal Pres,
437                                   const doublereal* n)
438 {
439     m_temp = T;
440     m_press = Pres;
441     setMoles(n);
442 }
443 
getElemAbundances(doublereal * elemAbundances) const444 void MultiPhase::getElemAbundances(doublereal* elemAbundances) const
445 {
446     calcElemAbundances();
447     for (size_t eGlobal = 0; eGlobal < m_nel; eGlobal++) {
448         elemAbundances[eGlobal] = m_elemAbundances[eGlobal];
449     }
450 }
451 
calcElemAbundances() const452 void MultiPhase::calcElemAbundances() const
453 {
454     size_t loc = 0;
455     doublereal spMoles;
456     for (size_t eGlobal = 0; eGlobal < m_nel; eGlobal++) {
457         m_elemAbundances[eGlobal] = 0.0;
458     }
459     for (size_t ip = 0; ip < nPhases(); ip++) {
460         ThermoPhase* p = m_phase[ip];
461         size_t nspPhase = p->nSpecies();
462         doublereal phasemoles = m_moles[ip];
463         for (size_t ik = 0; ik < nspPhase; ik++) {
464             size_t kGlobal = loc + ik;
465             spMoles = m_moleFractions[kGlobal] * phasemoles;
466             for (size_t eGlobal = 0; eGlobal < m_nel; eGlobal++) {
467                 m_elemAbundances[eGlobal] += m_atoms(eGlobal, kGlobal) * spMoles;
468             }
469         }
470         loc += nspPhase;
471     }
472 }
473 
volume() const474 doublereal MultiPhase::volume() const
475 {
476     doublereal sum = 0;
477     for (size_t i = 0; i < nPhases(); i++) {
478         double vol = 1.0/m_phase[i]->molarDensity();
479         sum += m_moles[i] * vol;
480     }
481     return sum;
482 }
483 
equilibrate_MultiPhaseEquil(int XY,doublereal err,int maxsteps,int maxiter,int loglevel)484 double MultiPhase::equilibrate_MultiPhaseEquil(int XY, doublereal err,
485                                                int maxsteps, int maxiter,
486                                                int loglevel)
487 {
488     bool strt = false;
489     doublereal dta = 0.0;
490     if (!m_init) {
491         init();
492     }
493 
494     if (XY == TP) {
495         // create an equilibrium manager
496         MultiPhaseEquil e(this);
497         return e.equilibrate(XY, err, maxsteps, loglevel);
498     } else if (XY == HP) {
499         double h0 = enthalpy();
500         double Tlow = 0.5*m_Tmin; // lower bound on T
501         double Thigh = 2.0*m_Tmax; // upper bound on T
502         doublereal Hlow = Undef, Hhigh = Undef;
503         for (int n = 0; n < maxiter; n++) {
504             // if 'strt' is false, the current composition will be used as
505             // the starting estimate; otherwise it will be estimated
506             MultiPhaseEquil e(this, strt);
507             // start with a loose error tolerance, but tighten it as we get
508             // close to the final temperature
509 
510             try {
511                 e.equilibrate(TP, err, maxsteps, loglevel);
512                 double hnow = enthalpy();
513                 // the equilibrium enthalpy monotonically increases with T;
514                 // if the current value is below the target, the we know the
515                 // current temperature is too low. Set
516                 if (hnow < h0) {
517                     if (m_temp > Tlow) {
518                         Tlow = m_temp;
519                         Hlow = hnow;
520                     }
521                 } else {
522                     // the current enthalpy is greater than the target;
523                     // therefore the current temperature is too high.
524                     if (m_temp < Thigh) {
525                         Thigh = m_temp;
526                         Hhigh = hnow;
527                     }
528                 }
529                 double dt;
530                 if (Hlow != Undef && Hhigh != Undef) {
531                     double cpb = (Hhigh - Hlow)/(Thigh - Tlow);
532                     dt = (h0 - hnow)/cpb;
533                     dta = fabs(dt);
534                     double dtmax = 0.5*fabs(Thigh - Tlow);
535                     if (dta > dtmax) {
536                         dt *= dtmax/dta;
537                     }
538                 } else {
539                     double tnew = sqrt(Tlow*Thigh);
540                     dt = tnew - m_temp;
541                 }
542 
543                 double herr = fabs((h0 - hnow)/h0);
544 
545                 if (herr < err) {
546                     return err;
547                 }
548                 double tnew = m_temp + dt;
549                 if (tnew < 0.0) {
550                     tnew = 0.5*m_temp;
551                 }
552                 setTemperature(tnew);
553 
554                 // if the size of Delta T is not too large, use
555                 // the current composition as the starting estimate
556                 if (dta < 100.0) {
557                     strt = false;
558                 }
559 
560             } catch (CanteraError&) {
561                 if (!strt) {
562                     strt = true;
563                 } else {
564                     double tnew = 0.5*(m_temp + Thigh);
565                     if (fabs(tnew - m_temp) < 1.0) {
566                         tnew = m_temp + 1.0;
567                     }
568                     setTemperature(tnew);
569                 }
570             }
571         }
572         throw CanteraError("MultiPhase::equilibrate_MultiPhaseEquil",
573                            "No convergence for T");
574     } else if (XY == SP) {
575         double s0 = entropy();
576         double Tlow = 1.0; // lower bound on T
577         double Thigh = 1.0e6; // upper bound on T
578         for (int n = 0; n < maxiter; n++) {
579             MultiPhaseEquil e(this, strt);
580 
581             try {
582                 e.equilibrate(TP, err, maxsteps, loglevel);
583                 double snow = entropy();
584                 if (snow < s0) {
585                     Tlow = std::max(Tlow, m_temp);
586                 } else {
587                     Thigh = std::min(Thigh, m_temp);
588                 }
589                 double dt = (s0 - snow)*m_temp/cp();
590                 double dtmax = 0.5*fabs(Thigh - Tlow);
591                 dtmax = (dtmax > 500.0 ? 500.0 : dtmax);
592                 dta = fabs(dt);
593                 if (dta > dtmax) {
594                     dt *= dtmax/dta;
595                 }
596                 if (dta < 1.0e-4) {
597                     return err;
598                 }
599                 double tnew = m_temp + dt;
600                 setTemperature(tnew);
601 
602                 // if the size of Delta T is not too large, use
603                 // the current composition as the starting estimate
604                 if (dta < 100.0) {
605                     strt = false;
606                 }
607             } catch (CanteraError&) {
608                 if (!strt) {
609                     strt = true;
610                 } else {
611                     double tnew = 0.5*(m_temp + Thigh);
612                     setTemperature(tnew);
613                 }
614             }
615         }
616         throw CanteraError("MultiPhase::equilibrate_MultiPhaseEquil",
617                            "No convergence for T");
618     } else if (XY == TV) {
619         doublereal v0 = volume();
620         bool start = true;
621         for (int n = 0; n < maxiter; n++) {
622             double pnow = pressure();
623             MultiPhaseEquil e(this, start);
624             start = false;
625 
626             e.equilibrate(TP, err, maxsteps, loglevel);
627             double vnow = volume();
628             double verr = fabs((v0 - vnow)/v0);
629 
630             if (verr < err) {
631                 return err;
632             }
633             // find dV/dP
634             setPressure(pnow*1.01);
635             double dVdP = (volume() - vnow)/(0.01*pnow);
636             setPressure(pnow + 0.5*(v0 - vnow)/dVdP);
637         }
638     } else {
639         throw CanteraError("MultiPhase::equilibrate_MultiPhaseEquil",
640                            "unknown option");
641     }
642     return -1.0;
643 }
644 
equilibrate(const std::string & XY,const std::string & solver,double rtol,int max_steps,int max_iter,int estimate_equil,int log_level)645 void MultiPhase::equilibrate(const std::string& XY, const std::string& solver,
646                              double rtol, int max_steps, int max_iter,
647                              int estimate_equil, int log_level)
648 {
649     // Save the initial state so that it can be restored in case one of the
650     // solvers fails
651     vector_fp initial_moleFractions = m_moleFractions;
652     vector_fp initial_moles = m_moles;
653     double initial_T = m_temp;
654     double initial_P = m_press;
655     int ixy = _equilflag(XY.c_str());
656     if (solver == "auto" || solver == "vcs") {
657         try {
658             debuglog("Trying VCS equilibrium solver\n", log_level);
659             vcs_MultiPhaseEquil eqsolve(this, log_level-1);
660             int ret = eqsolve.equilibrate(ixy, estimate_equil, log_level-1,
661                                           rtol, max_steps);
662             if (ret) {
663                 throw CanteraError("MultiPhase::equilibrate",
664                     "VCS solver failed. Return code: {}", ret);
665             }
666             debuglog("VCS solver succeeded\n", log_level);
667             return;
668         } catch (std::exception& err) {
669             debuglog("VCS solver failed.\n", log_level);
670             debuglog(err.what(), log_level);
671             m_moleFractions = initial_moleFractions;
672             m_moles = initial_moles;
673             m_temp = initial_T;
674             m_press = initial_P;
675             updatePhases();
676             if (solver == "auto") {
677             } else {
678                 throw;
679             }
680         }
681     }
682 
683     if (solver == "auto" || solver == "gibbs") {
684         try {
685             debuglog("Trying MultiPhaseEquil (Gibbs) equilibrium solver\n",
686                      log_level);
687             equilibrate_MultiPhaseEquil(ixy, rtol, max_steps, max_iter,
688                                         log_level-1);
689             debuglog("MultiPhaseEquil solver succeeded\n", log_level);
690             return;
691         } catch (std::exception& err) {
692             debuglog("MultiPhaseEquil solver failed.\n", log_level);
693             debuglog(err.what(), log_level);
694             m_moleFractions = initial_moleFractions;
695             m_moles = initial_moles;
696             m_temp = initial_T;
697             m_press = initial_P;
698             updatePhases();
699             throw;
700         }
701     }
702 
703     if (solver != "auto") {
704         throw CanteraError("MultiPhase::equilibrate",
705             "Invalid solver specified: '" + solver + "'");
706     }
707 }
708 
setTemperature(const doublereal T)709 void MultiPhase::setTemperature(const doublereal T)
710 {
711     if (!m_init) {
712         init();
713     }
714     m_temp = T;
715     updatePhases();
716 }
717 
checkElementIndex(size_t m) const718 void MultiPhase::checkElementIndex(size_t m) const
719 {
720     if (m >= m_nel) {
721         throw IndexError("MultiPhase::checkElementIndex", "elements", m, m_nel-1);
722     }
723 }
724 
checkElementArraySize(size_t mm) const725 void MultiPhase::checkElementArraySize(size_t mm) const
726 {
727     if (m_nel > mm) {
728         throw ArraySizeError("MultiPhase::checkElementArraySize", mm, m_nel);
729     }
730 }
731 
elementName(size_t m) const732 std::string MultiPhase::elementName(size_t m) const
733 {
734     return m_enames[m];
735 }
736 
elementIndex(const std::string & name) const737 size_t MultiPhase::elementIndex(const std::string& name) const
738 {
739     for (size_t e = 0; e < m_nel; e++) {
740         if (m_enames[e] == name) {
741             return e;
742         }
743     }
744     return npos;
745 }
746 
checkSpeciesIndex(size_t k) const747 void MultiPhase::checkSpeciesIndex(size_t k) const
748 {
749     if (k >= m_nsp) {
750         throw IndexError("MultiPhase::checkSpeciesIndex", "species", k, m_nsp-1);
751     }
752 }
753 
checkSpeciesArraySize(size_t kk) const754 void MultiPhase::checkSpeciesArraySize(size_t kk) const
755 {
756     if (m_nsp > kk) {
757         throw ArraySizeError("MultiPhase::checkSpeciesArraySize", kk, m_nsp);
758     }
759 }
760 
speciesName(const size_t k) const761 std::string MultiPhase::speciesName(const size_t k) const
762 {
763     return m_snames[k];
764 }
765 
nAtoms(const size_t kGlob,const size_t mGlob) const766 doublereal MultiPhase::nAtoms(const size_t kGlob, const size_t mGlob) const
767 {
768     return m_atoms(mGlob, kGlob);
769 }
770 
getMoleFractions(doublereal * const x) const771 void MultiPhase::getMoleFractions(doublereal* const x) const
772 {
773     std::copy(m_moleFractions.begin(), m_moleFractions.end(), x);
774 }
775 
phaseName(const size_t iph) const776 std::string MultiPhase::phaseName(const size_t iph) const
777 {
778     return m_phase[iph]->name();
779 }
780 
phaseIndex(const std::string & pName) const781 int MultiPhase::phaseIndex(const std::string& pName) const
782 {
783     for (int iph = 0; iph < (int) nPhases(); iph++) {
784         if (m_phase[iph]->name() == pName) {
785             return iph;
786         }
787     }
788     return -1;
789 }
790 
phaseMoles(const size_t n) const791 doublereal MultiPhase::phaseMoles(const size_t n) const
792 {
793     return m_moles[n];
794 }
795 
setPhaseMoles(const size_t n,const doublereal moles)796 void MultiPhase::setPhaseMoles(const size_t n, const doublereal moles)
797 {
798     m_moles[n] = moles;
799 }
800 
speciesPhaseIndex(const size_t kGlob) const801 size_t MultiPhase::speciesPhaseIndex(const size_t kGlob) const
802 {
803     return m_spphase[kGlob];
804 }
805 
moleFraction(const size_t kGlob) const806 doublereal MultiPhase::moleFraction(const size_t kGlob) const
807 {
808     return m_moleFractions[kGlob];
809 }
810 
tempOK(const size_t p) const811 bool MultiPhase::tempOK(const size_t p) const
812 {
813     return m_temp_OK[p];
814 }
815 
uploadMoleFractionsFromPhases()816 void MultiPhase::uploadMoleFractionsFromPhases()
817 {
818     size_t loc = 0;
819     for (size_t ip = 0; ip < nPhases(); ip++) {
820         ThermoPhase* p = m_phase[ip];
821         p->getMoleFractions(&m_moleFractions[loc]);
822         loc += p->nSpecies();
823     }
824     calcElemAbundances();
825 }
826 
updatePhases() const827 void MultiPhase::updatePhases() const
828 {
829     size_t loc = 0;
830     for (size_t p = 0; p < nPhases(); p++) {
831         m_phase[p]->setState_TPX(m_temp, m_press, &m_moleFractions[loc]);
832         loc += m_phase[p]->nSpecies();
833         m_temp_OK[p] = true;
834         if (m_temp < m_phase[p]->minTemp() || m_temp > m_phase[p]->maxTemp()) {
835             m_temp_OK[p] = false;
836         }
837     }
838 }
839 
operator <<(std::ostream & s,MultiPhase & x)840 std::ostream& operator<<(std::ostream& s, MultiPhase& x)
841 {
842     x.updatePhases();
843     for (size_t ip = 0; ip < x.nPhases(); ip++) {
844         if (x.phase(ip).name() != "") {
845             s << "*************** " << x.phase(ip).name() << " *****************" << std::endl;
846         } else {
847             s << "*************** Phase " << ip << " *****************" << std::endl;
848         }
849         s << "Moles: " << x.phaseMoles(ip) << std::endl;
850 
851         s << x.phase(ip).report() << std::endl;
852     }
853     return s;
854 }
855 
856 }
857