Cantera  2.0
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 equilfunctions).
5  */
7 #include "cantera/equil/MultiPhaseEquil.h"
8 
12 #include "cantera/base/global.h"
13 
14 using namespace std;
15 
16 namespace Cantera
17 {
18 
19 //====================================================================================================================
20 // Constructor.
21 MultiPhase::MultiPhase() :
22  m_np(0),
23  m_temp(0.0),
24  m_press(0.0),
25  m_nel(0),
26  m_nsp(0),
27  m_init(false),
28  m_eloc(npos),
29  m_Tmin(1.0),
30  m_Tmax(100000.0)
31 {
32 }
33 //====================================================================================================================
34 // Copy Constructor
35 /*
36  * @param right Object to be copied
37  */
39  m_np(0),
40  m_temp(0.0),
41  m_press(0.0),
42  m_nel(0),
43  m_nsp(0),
44  m_init(false),
45  m_eloc(npos),
46  m_Tmin(1.0),
47  m_Tmax(100000.0)
48 {
49  operator=(right);
50 }
51 //====================================================================================================================
52 // Destructor.
53 /*
54  * Does nothing. Class MultiPhase does not take
55  * "ownership" (i.e. responsibility for destroying) the
56  * phase objects.
57  */
59 {
60 }
61 //====================================================================================================================
62 // Assignment operator
63 /*
64  * @param right Object to be copied
65  */
67 {
68  if (&right != this) {
69  m_moles = right.m_moles;
70  // shallow copy of phase pointers
71  m_phase = right.m_phase;
72  m_atoms = right.m_atoms;
74  m_spphase = right.m_spphase;
75  m_spstart = right.m_spstart;
76  m_enames = right.m_enames;
77  m_enamemap = right.m_enamemap;
78  m_np = right.m_np;
79  m_temp = right.m_temp;
80  m_press = right.m_press;
81  m_nel = right.m_nel;
82  m_nsp = right.m_nsp;
83  m_init = right.m_init;
84  m_eloc = right.m_eloc;
85  m_temp_OK = right.m_temp_OK;
86  m_Tmin = right.m_Tmin;
87  m_Tmax = right.m_Tmax;
89  }
90  return *this;
91 }
92 //====================================================================================================================
93 void MultiPhase::
95 {
96  index_t n;
97  for (n = 0; n < mix.m_np; n++) {
98  addPhase(mix.m_phase[n], mix.m_moles[n]);
99  }
100 }
101 //====================================================================================================================
102 void MultiPhase::
103 addPhases(std::vector<ThermoPhase*>& phases, const vector_fp& phaseMoles)
104 {
105  index_t np = phases.size();
106  index_t n;
107  for (n = 0; n < np; n++) {
108  addPhase(phases[n], phaseMoles[n]);
109  }
110  init();
111 }
112 //====================================================================================================================
113 void MultiPhase::
114 addPhase(ThermoPhase* p, doublereal moles)
115 {
116  if (m_init) {
117  throw CanteraError("addPhase",
118  "phases cannot be added after init() has been called.");
119  }
120 
121  // save the pointer to the phase object
122  m_phase.push_back(p);
123 
124  // store its number of moles
125  m_moles.push_back(moles);
126  m_temp_OK.push_back(true);
127 
128  // update the number of phases and the total number of
129  // species
130  m_np = m_phase.size();
131  m_nsp += p->nSpecies();
132 
133  // determine if this phase has new elements
134  // for each new element, add an entry in the map
135  // from names to index number + 1:
136 
137  string ename;
138  // iterate over the elements in this phase
139  index_t m, nel = p->nElements();
140  for (m = 0; m < nel; m++) {
141  ename = p->elementName(m);
142 
143  // if no entry is found for this element name, then
144  // it is a new element. In this case, add the name
145  // to the list of names, increment the element count,
146  // and add an entry to the name->(index+1) map.
147  if (m_enamemap.find(ename) == m_enamemap.end()) {
148  m_enamemap[ename] = m_nel + 1;
149  m_enames.push_back(ename);
150  m_atomicNumber.push_back(p->atomicNumber(m));
151 
152  // Element 'E' (or 'e') is special. Note its location.
153  if (ename == "E" || ename == "e") {
154  m_eloc = m_nel;
155  }
156 
157  m_nel++;
158  }
159  }
160 
161  // If the mixture temperature hasn't been set, then set the
162  // temperature and pressure to the values for the phase being
163  // added.
164  if (m_temp == 0.0 && p->temperature() > 0.0) {
165  m_temp = p->temperature();
166  m_press = p->pressure();
167  }
168 
169  // If this is a solution phase, update the minimum and maximum
170  // mixture temperatures. Stoichiometric phases are excluded,
171  // since a mixture may define multiple stoichiometric phases,
172  // each of which has thermo data valid only over a limited
173  // range. For example, a mixture might be defined to contain a
174  // phase representing water ice and one representing liquid
175  // water, only one of which should be present if the mixture
176  // represents an equilibrium state.
177  if (p->nSpecies() > 1) {
178  double t = p->minTemp();
179  if (t > m_Tmin) {
180  m_Tmin = t;
181  }
182  t = p->maxTemp();
183  if (t < m_Tmax) {
184  m_Tmax = t;
185  }
186  }
187 }
188 //====================================================================================================================
189 // Process phases and build atomic composition array. This method
190 // must be called after all phases are added, before doing
191 // anything else with the mixture. After init() has been called,
192 // no more phases may be added.
194 {
195  if (m_init) {
196  return;
197  }
198  index_t ip, kp, k = 0, nsp, m;
199  size_t mlocal;
200  string sym;
201 
202  // allocate space for the atomic composition matrix
203  m_atoms.resize(m_nel, m_nsp, 0.0);
204  m_moleFractions.resize(m_nsp, 0.0);
205  m_elemAbundances.resize(m_nel, 0.0);
206 
207  // iterate over the elements
208  // -> fill in m_atoms(m,k), m_snames(k), m_spphase(k),
209  // m_sptart(ip)
210  for (m = 0; m < m_nel; m++) {
211  sym = m_enames[m];
212  k = 0;
213  // iterate over the phases
214  for (ip = 0; ip < m_np; ip++) {
215  ThermoPhase* p = m_phase[ip];
216  nsp = p->nSpecies();
217  mlocal = p->elementIndex(sym);
218  for (kp = 0; kp < nsp; kp++) {
219  if (mlocal != npos) {
220  m_atoms(m, k) = p->nAtoms(kp, mlocal);
221  }
222  if (m == 0) {
223  m_snames.push_back(p->speciesName(kp));
224  if (kp == 0) {
225  m_spstart.push_back(m_spphase.size());
226  }
227  m_spphase.push_back(ip);
228  }
229  k++;
230  }
231  }
232  }
233 
234  if (m_eloc != npos) {
235  doublereal esum;
236  for (k = 0; k < m_nsp; k++) {
237  esum = 0.0;
238  for (m = 0; m < m_nel; m++) {
239  if (m != m_eloc) {
240  esum += m_atoms(m,k) * m_atomicNumber[m];
241  }
242  }
243  //m_atoms(m_eloc, k) += esum;
244  }
245  }
246 
247  /// set the initial composition within each phase to the
248  /// mole fractions stored in the phase objects
249  m_init = true;
250 
252 
253  updatePhases();
254 }
255 
256 //====================================================================================================================
257 // Return a reference to phase n. The state of phase n is
258 // also updated to match the state stored locally in the
259 // mixture object.
261 {
262  if (!m_init) {
263  init();
264  }
265  m_phase[n]->setTemperature(m_temp);
266  m_phase[n]->setMoleFractions_NoNorm(DATA_PTR(m_moleFractions) + m_spstart[n]);
267  m_phase[n]->setPressure(m_press);
268  return *m_phase[n];
269 }
270 
271 void MultiPhase::checkPhaseIndex(size_t m) const
272 {
273  if (m >= nPhases()) {
274  throw IndexError("checkPhaseIndex", "phase", m, nPhases()-1);
275  }
276 }
277 
278 void MultiPhase::checkPhaseArraySize(size_t mm) const
279 {
280  if (nPhases() > mm) {
281  throw ArraySizeError("checkPhaseIndex", mm, nPhases());
282  }
283 }
284 
285 //====================================================================================================================
286 /// Moles of species \c k.
288 {
289  index_t ip = m_spphase[k];
290  return m_moles[ip]*m_moleFractions[k];
291 }
292 //====================================================================================================================
293 // Total moles of global element \a m, summed over all phases.
294 /*
295  * @param m Index of the global element
296  */
298 {
299  doublereal sum = 0.0, phasesum;
300  index_t i, k = 0, ik, nsp;
301  for (i = 0; i < m_np; i++) {
302  phasesum = 0.0;
303  nsp = m_phase[i]->nSpecies();
304  for (ik = 0; ik < nsp; ik++) {
305  k = speciesIndex(ik, i);
306  phasesum += m_atoms(m,k)*m_moleFractions[k];
307  }
308  sum += phasesum * m_moles[i];
309  }
310  return sum;
311 }
312 //====================================================================================================================
313 // Total charge, summed over all phases
314 doublereal MultiPhase::charge() const
315 {
316  doublereal sum = 0.0;
317  index_t i;
318  for (i = 0; i < m_np; i++) {
319  sum += phaseCharge(i);
320  }
321  return sum;
322 }
323 //====================================================================================================================
324 size_t MultiPhase::speciesIndex(std::string speciesName, std::string phaseName)
325 {
326  if (!m_init) {
327  init();
328  }
329  size_t p = phaseIndex(phaseName);
330  if (p == npos) {
331  throw CanteraError("MultiPhase::speciesIndex", "phase not found: " + phaseName);
332  }
333  size_t k = m_phase[p]->speciesIndex(speciesName);
334  if (k == npos) {
335  throw CanteraError("MultiPhase::speciesIndex", "species not found: " + speciesName);
336  }
337  return m_spstart[p] + k;
338 }
339 //====================================================================================================================
340 /// Net charge of one phase (Coulombs). The net charge is computed as
341 /// \f[ Q_p = N_p \sum_k F z_k X_k \f]
342 /// where the sum runs only over species in phase \a p.
343 /// @param p index of the phase for which the charge is desired.
344 doublereal MultiPhase::phaseCharge(index_t p) const
345 {
346  doublereal phasesum = 0.0;
347  size_t ik, k, nsp = m_phase[p]->nSpecies();
348  for (ik = 0; ik < nsp; ik++) {
349  k = speciesIndex(ik, p);
350  phasesum += m_phase[p]->charge(ik)*m_moleFractions[k];
351  }
352  return Faraday*phasesum*m_moles[p];
353 }
354 //====================================================================================================================
355 
356 /// Get the chemical potentials of all species in all phases.
357 void MultiPhase::getChemPotentials(doublereal* mu) const
358 {
359  index_t i, loc = 0;
360  updatePhases();
361  for (i = 0; i < m_np; i++) {
362  m_phase[i]->getChemPotentials(mu + loc);
363  loc += m_phase[i]->nSpecies();
364  }
365 }
366 //====================================================================================================================
367 // Get chemical potentials of species with valid thermo
368 // data. This method is designed for use in computing chemical
369 // equilibrium by Gibbs minimization. For solution phases (more
370 // than one species), this does the same thing as
371 // getChemPotentials. But for stoichiometric phases, this writes
372 // into array \a mu the user-specified value \a not_mu instead of
373 // the chemical potential if the temperature is outside the range
374 // for which the thermo data for the one species in the phase are
375 // valid. The need for this arises since many condensed phases
376 // have thermo data fit only for the temperature range for which
377 // they are stable. For example, in the NASA database, the fits
378 // for H2O(s) are only done up to 0 C, the fits for H2O(L) are
379 // only done from 0 C to 100 C, etc. Using the polynomial fits outside
380 // the range for which the fits were done can result in spurious
381 // chemical potentials, and can lead to condensed phases
382 // appearing when in fact they should be absent.
383 //
384 // By setting \a not_mu to a large positive value, it is possible
385 // to force routines which seek to minimize the Gibbs free energy
386 // of the mixture to zero out any phases outside the temperature
387 // range for which their thermo data are valid.
388 //
389 // If this method is called with \a standard set to true, then
390 // the composition-independent standard chemical potentials are
391 // returned instead of the composition-dependent chemical
392 // potentials.
393 //
394 void MultiPhase::getValidChemPotentials(doublereal not_mu,
395  doublereal* mu, bool standard) const
396 {
397  index_t i, loc = 0;
398 
399  updatePhases();
400  // iterate over the phases
401  for (i = 0; i < m_np; i++) {
402  if (tempOK(i) || m_phase[i]->nSpecies() > 1) {
403  if (!standard) {
404  m_phase[i]->getChemPotentials(mu + loc);
405  } else {
406  m_phase[i]->getStandardChemPotentials(mu + loc);
407  }
408  } else {
409  fill(mu + loc, mu + loc + m_phase[i]->nSpecies(), not_mu);
410  }
411  loc += m_phase[i]->nSpecies();
412  }
413 }
414 //====================================================================================================================
415 /// True if species \a k belongs to a solution phase.
417 {
418  if (m_phase[m_spphase[k]]->nSpecies() > 1) {
419  return true;
420  } else {
421  return false;
422  }
423 }
424 //====================================================================================================================
425 /// The Gibbs free energy of the mixture (J).
426 doublereal MultiPhase::gibbs() const
427 {
428  index_t i;
429  doublereal sum = 0.0;
430  updatePhases();
431  for (i = 0; i < m_np; i++) {
432  if (m_moles[i] > 0.0) {
433  sum += m_phase[i]->gibbs_mole() * m_moles[i];
434  }
435  }
436  return sum;
437 }
438 //====================================================================================================================
439 /// The enthalpy of the mixture (J).
440 doublereal MultiPhase::enthalpy() const
441 {
442  index_t i;
443  doublereal sum = 0.0;
444  updatePhases();
445  for (i = 0; i < m_np; i++) {
446  if (m_moles[i] > 0.0) {
447  sum += m_phase[i]->enthalpy_mole() * m_moles[i];
448  }
449  }
450  return sum;
451 }
452 //====================================================================================================================
453 /// The internal energy of the mixture (J).
454 doublereal MultiPhase::IntEnergy() const
455 {
456  index_t i;
457  doublereal sum = 0.0;
458  updatePhases();
459  for (i = 0; i < m_np; i++) {
460  if (m_moles[i] > 0.0) {
461  sum += m_phase[i]->intEnergy_mole() * m_moles[i];
462  }
463  }
464  return sum;
465 }
466 //====================================================================================================================
467 /// The entropy of the mixture (J/K).
468 doublereal MultiPhase::entropy() const
469 {
470  index_t i;
471  doublereal sum = 0.0;
472  updatePhases();
473  for (i = 0; i < m_np; i++) {
474  if (m_moles[i] > 0.0) {
475  sum += m_phase[i]->entropy_mole() * m_moles[i];
476  }
477  }
478  return sum;
479 }
480 //====================================================================================================================
481 /// The specific heat at constant pressure and composition (J/K).
482 /// Note that this does not account for changes in composition of
483 /// the mixture with temperature.
484 doublereal MultiPhase::cp() const
485 {
486  index_t i;
487  doublereal sum = 0.0;
488  updatePhases();
489  for (i = 0; i < m_np; i++) {
490  if (m_moles[i] > 0.0) {
491  sum += m_phase[i]->cp_mole() * m_moles[i];
492  }
493  }
494  return sum;
495 }
496 
497 //====================================================================================================================
498 
499 /// Set the mole fractions of phase \a n to the values in
500 /// array \a x.
501 void MultiPhase::setPhaseMoleFractions(const index_t n, const doublereal* const x)
502 {
503  if (!m_init) {
504  init();
505  }
506  ThermoPhase* p = m_phase[n];
507  p->setState_TPX(m_temp, m_press, x);
508  size_t istart = m_spstart[n];
509  for (size_t k = 0; k < p->nSpecies(); k++) {
510  m_moleFractions[istart+k] = x[k];
511  }
512 }
513 //====================================================================================================================
514 // Set the species moles using a map. The map \a xMap maps
515 // species name strings to mole numbers. Mole numbers that are
516 // less than or equal to zero will be set to zero.
518 {
519  size_t kk = nSpecies();
520  doublereal x;
521  vector_fp moles(kk, 0.0);
522  for (size_t k = 0; k < kk; k++) {
523  x = xMap[speciesName(k)];
524  if (x > 0.0) {
525  moles[k] = x;
526  }
527  }
528  setMoles(DATA_PTR(moles));
529 }
530 //====================================================================================================================
531 // Set the species moles using a string. Unspecified species are
532 // set to zero.
533 void MultiPhase::setMolesByName(const std::string& x)
534 {
535  compositionMap xx;
536 
537  // add an entry in the map for every species, with value -1.0.
538  // Function parseCompString (stringUtils.cpp) uses the names
539  // in the map to specify the allowed species.
540  for (size_t k = 0; k < nSpecies(); k++) {
541  xx[speciesName(k)] = -1.0;
542  }
543 
544  // build the composition map from the string, and then set the
545  // moles.
546  parseCompString(x, xx);
547  setMolesByName(xx);
548 }
549 //====================================================================================================================
550 // Get the mole numbers of all species in the multiphase
551 // object
552 void MultiPhase::getMoles(doublereal* molNum) const
553 {
554  /*
555  * First copy in the mole fractions
556  */
557  copy(m_moleFractions.begin(), m_moleFractions.end(), molNum);
558  index_t ik;
559  doublereal* dtmp = molNum;
560  for (index_t ip = 0; ip < m_np; ip++) {
561  doublereal phasemoles = m_moles[ip];
562  ThermoPhase* p = m_phase[ip];
563  index_t nsp = p->nSpecies();
564  for (ik = 0; ik < nsp; ik++) {
565  *(dtmp++) *= phasemoles;
566  }
567  }
568 }
569 //====================================================================================================================
570 /// Set the species moles to the values in array \a n. The state
571 /// of each phase object is also updated to have the specified
572 /// composition and the mixture temperature and pressure.
573 void MultiPhase::setMoles(const doublereal* n)
574 {
575  if (!m_init) {
576  init();
577  }
578  index_t ip, loc = 0;
579  index_t ik, k = 0, nsp;
580  doublereal phasemoles;
581  for (ip = 0; ip < m_np; ip++) {
582  ThermoPhase* p = m_phase[ip];
583  nsp = p->nSpecies();
584  phasemoles = 0.0;
585  for (ik = 0; ik < nsp; ik++) {
586  phasemoles += n[k];
587  k++;
588  }
589  m_moles[ip] = phasemoles;
590  if (nsp > 1) {
591  if (phasemoles > 0.0) {
592  p->setState_TPX(m_temp, m_press, n + loc);
594  } else {
596  }
597  } else {
598  m_moleFractions[loc] = 1.0;
599  }
600  loc += nsp;
601  }
602 }
603 //====================================================================================================================
604 void MultiPhase::addSpeciesMoles(const int indexS, const doublereal addedMoles)
605 {
606  vector_fp tmpMoles(m_nsp, 0.0);
607  getMoles(DATA_PTR(tmpMoles));
608  tmpMoles[indexS] += addedMoles;
609  if (tmpMoles[indexS] < 0.0) {
610  tmpMoles[indexS] = 0.0;
611  }
612  setMoles(DATA_PTR(tmpMoles));
613 }
614 //====================================================================================================================
615 void MultiPhase::setState_TP(const doublereal T, const doublereal Pres)
616 {
617  if (!m_init) {
618  init();
619  }
620  m_temp = T;
621  m_press = Pres;
622  updatePhases();
623 }
624 //====================================================================================================================
625 void MultiPhase::setState_TPMoles(const doublereal T, const doublereal Pres,
626  const doublereal* n)
627 {
628  m_temp = T;
629  m_press = Pres;
630  setMoles(n);
631 }
632 //====================================================================================================================
633 void MultiPhase::getElemAbundances(doublereal* elemAbundances) const
634 {
635  index_t eGlobal;
637  for (eGlobal = 0; eGlobal < m_nel; eGlobal++) {
638  elemAbundances[eGlobal] = m_elemAbundances[eGlobal];
639  }
640 }
641 //====================================================================================================================
642 // Internal routine to calculate the element abundance vector
644 {
645  index_t loc = 0;
646  index_t eGlobal;
647  index_t ik, kGlobal;
648  doublereal spMoles;
649  for (eGlobal = 0; eGlobal < m_nel; eGlobal++) {
650  m_elemAbundances[eGlobal] = 0.0;
651  }
652  for (index_t ip = 0; ip < m_np; ip++) {
653  ThermoPhase* p = m_phase[ip];
654  size_t nspPhase = p->nSpecies();
655  doublereal phasemoles = m_moles[ip];
656  for (ik = 0; ik < nspPhase; ik++) {
657  kGlobal = loc + ik;
658  spMoles = m_moleFractions[kGlobal] * phasemoles;
659  for (eGlobal = 0; eGlobal < m_nel; eGlobal++) {
660  m_elemAbundances[eGlobal] += m_atoms(eGlobal, kGlobal) * spMoles;
661  }
662  }
663  loc += nspPhase;
664  }
665 }
666 //====================================================================================================================
667 /// The total mixture volume [m^3].
668 doublereal MultiPhase::volume() const
669 {
670  int i;
671  doublereal sum = 0;
672  for (i = 0; i < int(m_np); i++) {
673  double vol = 1.0/m_phase[i]->molarDensity();
674  sum += m_moles[i] * vol;
675  }
676  return sum;
677 }
678 //====================================================================================================================
679 doublereal MultiPhase::equilibrate(int XY, doublereal err,
680  int maxsteps, int maxiter, int loglevel)
681 {
682  bool strt = false;
683  doublereal dt;
684  doublereal h0;
685  int n;
686  doublereal hnow, herr = 1.0;
687  doublereal snow, serr = 1.0, s0;
688  doublereal Tlow = -1.0, Thigh = -1.0;
689  doublereal Hlow = Undef, Hhigh = Undef, tnew;
690  doublereal dta=0.0, dtmax, cpb;
691  MultiPhaseEquil* e = 0;
692 
693  if (!m_init) {
694  init();
695  }
696  if (loglevel > 0) {
697  beginLogGroup("MultiPhase::equilibrate", loglevel);
698  }
699 
700  if (XY == TP) {
701  if (loglevel > 0) {
702  addLogEntry("problem type","fixed T,P");
703  addLogEntry("Temperature",temperature());
704  addLogEntry("Pressure", pressure());
705  }
706 
707  // create an equilibrium manager
708  e = new MultiPhaseEquil(this);
709  try {
710  e->equilibrate(XY, err, maxsteps, loglevel);
711  } catch (CanteraError& err) {
712  err.save();
713  if (loglevel > 0) {
714  endLogGroup();
715  }
716  delete e;
717  e = 0;
718  throw err;
719  }
720  goto done;
721  }
722 
723  else if (XY == HP) {
724  h0 = enthalpy();
725  Tlow = 0.5*m_Tmin; // lower bound on T
726  Thigh = 2.0*m_Tmax; // upper bound on T
727  if (loglevel > 0) {
728  addLogEntry("problem type","fixed H,P");
729  addLogEntry("H target",fp2str(h0));
730  }
731  for (n = 0; n < maxiter; n++) {
732 
733  // if 'strt' is false, the current composition will be used as
734  // the starting estimate; otherwise it will be estimated
735  // if (e) {
736  // cout << "e should be zero, but it is not!" << endl;
737  // delete e;
738  // }
739  e = new MultiPhaseEquil(this, strt);
740  // start with a loose error tolerance, but tighten it as we get
741  // close to the final temperature
742  if (loglevel > 0) {
743  beginLogGroup("iteration "+int2str(n));
744  }
745 
746  try {
747  e->equilibrate(TP, err, maxsteps, loglevel);
748  hnow = enthalpy();
749  // the equilibrium enthalpy monotonically increases with T;
750  // if the current value is below the target, the we know the
751  // current temperature is too low. Set
752  if (hnow < h0) {
753  if (m_temp > Tlow) {
754  Tlow = m_temp;
755  Hlow = hnow;
756  }
757  }
758  // the current enthalpy is greater than the target; therefore the
759  // current temperature is too high.
760  else {
761  if (m_temp < Thigh) {
762  Thigh = m_temp;
763  Hhigh = hnow;
764  }
765  }
766  if (Hlow != Undef && Hhigh != Undef) {
767  cpb = (Hhigh - Hlow)/(Thigh - Tlow);
768  dt = (h0 - hnow)/cpb;
769  dta = fabs(dt);
770  dtmax = 0.5*fabs(Thigh - Tlow);
771  if (dta > dtmax) {
772  dt *= dtmax/dta;
773  }
774  } else {
775  tnew = sqrt(Tlow*Thigh);
776  dt = tnew - m_temp;
777  //cpb = cp();
778  }
779 
780  herr = fabs((h0 - hnow)/h0);
781  if (loglevel > 0) {
783  addLogEntry("H",fp2str(hnow));
784  addLogEntry("H rel error",fp2str(herr));
785  addLogEntry("lower T bound",fp2str(Tlow));
786  addLogEntry("upper T bound",fp2str(Thigh));
787  endLogGroup(); // iteration
788  }
789 
790 
791  if (herr < err) { // || dta < 1.0e-4) {
792  if (loglevel > 0) {
793  addLogEntry("T iterations",int2str(n));
794  addLogEntry("Final T",fp2str(temperature()));
795  addLogEntry("H rel error",fp2str(herr));
796  }
797  goto done;
798  }
799  tnew = m_temp + dt;
800  if (tnew < 0.0) {
801  tnew = 0.5*m_temp;
802  }
803  //dta = fabs(tnew - m_temp);
804  setTemperature(tnew);
805 
806  // if the size of Delta T is not too large, use
807  // the current composition as the starting estimate
808  if (dta < 100.0) {
809  strt = false;
810  }
811 
812  }
813 
814  catch (CanteraError& err) {
815  err.save();
816  if (!strt) {
817  if (loglevel > 0)
818  addLogEntry("no convergence",
819  "try estimating starting composition");
820  strt = true;
821  } else {
822  tnew = 0.5*(m_temp + Thigh);
823  if (fabs(tnew - m_temp) < 1.0) {
824  tnew = m_temp + 1.0;
825  }
826  setTemperature(tnew);
827  if (loglevel > 0)
828  addLogEntry("no convergence",
829  "trying T = "+fp2str(m_temp));
830  }
831  if (loglevel > 0) {
832  endLogGroup();
833  }
834  }
835  delete e;
836  e = 0;
837  }
838  if (loglevel > 0) {
839  addLogEntry("reached max number of T iterations",int2str(maxiter));
840  endLogGroup();
841  }
842  throw CanteraError("MultiPhase::equilibrate",
843  "No convergence for T");
844  } else if (XY == SP) {
845  s0 = entropy();
846  Tlow = 1.0; // m_Tmin; // lower bound on T
847  Thigh = 1.0e6; // m_Tmax; // upper bound on T
848  if (loglevel > 0) {
849  addLogEntry("problem type","fixed S,P");
850  addLogEntry("S target",fp2str(s0));
851  addLogEntry("min T",fp2str(Tlow));
852  addLogEntry("max T",fp2str(Thigh));
853  }
854  for (n = 0; n < maxiter; n++) {
855  if (e) {
856  delete e;
857  }
858  e = new MultiPhaseEquil(this, strt);
859  if (loglevel > 0) {
860  beginLogGroup("iteration "+int2str(n));
861  }
862 
863  try {
864  e->equilibrate(TP, err, maxsteps, loglevel);
865  snow = entropy();
866  if (snow < s0) {
867  if (m_temp > Tlow) {
868  Tlow = m_temp;
869  }
870  } else {
871  if (m_temp < Thigh) {
872  Thigh = m_temp;
873  }
874  }
875  serr = fabs((s0 - snow)/s0);
876  if (loglevel > 0) {
878  addLogEntry("S",fp2str(snow));
879  addLogEntry("S rel error",fp2str(serr));
880  endLogGroup();
881  }
882  dt = (s0 - snow)*m_temp/cp();
883  dtmax = 0.5*fabs(Thigh - Tlow);
884  dtmax = (dtmax > 500.0 ? 500.0 : dtmax);
885  dta = fabs(dt);
886  if (dta > dtmax) {
887  dt *= dtmax/dta;
888  }
889  if (herr < err || dta < 1.0e-4) {
890  if (loglevel > 0) {
891  addLogEntry("T iterations",int2str(n));
892  addLogEntry("Final T",fp2str(temperature()));
893  addLogEntry("S rel error",fp2str(serr));
894  }
895  goto done;
896  }
897  tnew = m_temp + dt;
898  setTemperature(tnew);
899 
900  // if the size of Delta T is not too large, use
901  // the current composition as the starting estimate
902  if (dta < 100.0) {
903  strt = false;
904  }
905  }
906 
907  catch (CanteraError& err) {
908  err.save();
909  if (!strt) {
910  if (loglevel > 0) {
911  addLogEntry("no convergence",
912  "setting strt to True");
913  }
914  strt = true;
915  } else {
916  tnew = 0.5*(m_temp + Thigh);
917  setTemperature(tnew);
918  if (loglevel > 0) {
919  addLogEntry("no convergence",
920  "trying T = "+fp2str(m_temp));
921  }
922  }
923  if (loglevel > 0) {
924  endLogGroup();
925  }
926  }
927  delete e;
928  e = 0;
929  }
930  if (loglevel > 0) {
931  addLogEntry("reached max number of T iterations",int2str(maxiter));
932  endLogGroup();
933  }
934  throw CanteraError("MultiPhase::equilibrate",
935  "No convergence for T");
936  } else if (XY == TV) {
937  addLogEntry("problem type","fixed T, V");
938  // doublereal dt = 1.0e3;
939  doublereal v0 = volume();
940  doublereal dVdP;
941  int n;
942  bool start = true;
943  doublereal vnow, pnow, verr;
944  for (n = 0; n < maxiter; n++) {
945  pnow = pressure();
946  MultiPhaseEquil e(this, start);
947  start = false;
948  beginLogGroup("iteration "+int2str(n));
949 
950  e.equilibrate(TP, err, maxsteps, loglevel);
951  vnow = volume();
952  verr = fabs((v0 - vnow)/v0);
953  addLogEntry("P",fp2str(pressure()));
954  addLogEntry("V rel error",fp2str(verr));
955  endLogGroup();
956 
957  if (verr < err) {
958  addLogEntry("P iterations",int2str(n));
959  addLogEntry("Final P",fp2str(pressure()));
960  addLogEntry("V rel error",fp2str(verr));
961  goto done;
962  }
963  // find dV/dP
964  setPressure(pnow*1.01);
965  dVdP = (volume() - vnow)/(0.01*pnow);
966  setPressure(pnow + 0.5*(v0 - vnow)/dVdP);
967  }
968  }
969 
970  else {
971  if (loglevel > 0) {
972  endLogGroup();
973  }
974  throw CanteraError("MultiPhase::equilibrate","unknown option");
975  }
976  return -1.0;
977 done:
978  delete e;
979  e = 0;
980  if (loglevel > 0) {
981  endLogGroup();
982  }
983  return err;
984 }
985 
986 #ifdef MULTIPHASE_DEVEL
987 void importFromXML(string infile, string id)
988 {
989  XML_Node* root = get_XML_File(infile);
990  if (id == "-") {
991  id = "";
992  }
993  XML_Node* x = get_XML_Node(string("#")+id, root);
994  if (x.name() != "multiphase")
995  throw CanteraError("MultiPhase::importFromXML",
996  "Current XML_Node is not a multiphase element.");
997  vector<XML_Node*> phases;
998  x.getChildren("phase",phases);
999  int np = phases.size();
1000  int n;
1001  ThermoPhase* p;
1002  for (n = 0; n < np; n++) {
1003  XML_Node& ph = *phases[n];
1004  srcfile = infile;
1005  if (ph.hasAttrib("src")) {
1006  srcfile = ph["src"];
1007  }
1008  idstr = ph["id"];
1009  p = newPhase(srcfile, idstr);
1010  if (p) {
1011  addPhase(p, ph.value());
1012  }
1013  }
1014 }
1015 #endif
1016 //====================================================================================================================
1017 void MultiPhase::setTemperature(const doublereal T)
1018 {
1019  if (!m_init) {
1020  init();
1021  }
1022  m_temp = T;
1023  updatePhases();
1024 }
1025 
1026 void MultiPhase::checkElementIndex(size_t m) const
1027 {
1028  if (m >= m_nel) {
1029  throw IndexError("checkElementIndex", "elements", m, m_nel-1);
1030  }
1031 }
1032 
1034 {
1035  if (m_nel > mm) {
1036  throw ArraySizeError("checkElementArraySize", mm, m_nel);
1037  }
1038 }
1039 
1040 //====================================================================================================================
1041 // Name of element \a m.
1042 std::string MultiPhase::elementName(size_t m) const
1043 {
1044  return m_enames[m];
1045 }
1046 //====================================================================================================================
1047 // Index of element with name \a name.
1048 size_t MultiPhase::elementIndex(std::string name) const
1049 {
1050  for (size_t e = 0; e < m_nel; e++) {
1051  if (m_enames[e] == name) {
1052  return e;
1053  }
1054  }
1055  return npos;
1056 }
1057 
1058 void MultiPhase::checkSpeciesIndex(size_t k) const
1059 {
1060  if (k >= m_nsp) {
1061  throw IndexError("checkSpeciesIndex", "species", k, m_nsp-1);
1062  }
1063 }
1064 
1066 {
1067  if (m_nsp > kk) {
1068  throw ArraySizeError("checkSpeciesArraySize", kk, m_nsp);
1069  }
1070 }
1071 
1072 //====================================================================================================================
1073 // Name of species with global index \a k.
1074 std::string MultiPhase::speciesName(const size_t k) const
1075 {
1076  return m_snames[k];
1077 }
1078 
1079 doublereal MultiPhase::nAtoms(const size_t kGlob, const size_t mGlob) const
1080 {
1081  return m_atoms(mGlob, kGlob);
1082 }
1083 //====================================================================================================================
1084 void MultiPhase::getMoleFractions(doublereal* const x) const
1085 {
1086  std::copy(m_moleFractions.begin(), m_moleFractions.end(), x);
1087 }
1088 //====================================================================================================================
1089 std::string MultiPhase::phaseName(const index_t iph) const
1090 {
1091  const ThermoPhase* tptr = m_phase[iph];
1092  return tptr->id();
1093 }
1094 //====================================================================================================================
1095 int MultiPhase::phaseIndex(const std::string& pName) const
1096 {
1097  std::string tmp;
1098  for (int iph = 0; iph < (int) m_np; iph++) {
1099  const ThermoPhase* tptr = m_phase[iph];
1100  tmp = tptr->id();
1101  if (tmp == pName) {
1102  return iph;
1103  }
1104  }
1105  return -1;
1106 }
1107 //====================================================================================================================
1108 doublereal MultiPhase::phaseMoles(const index_t n) const
1109 {
1110  return m_moles[n];
1111 }
1112 //====================================================================================================================
1113 void MultiPhase::setPhaseMoles(const index_t n, const doublereal moles)
1114 {
1115  m_moles[n] = moles;
1116 }
1117 
1118 size_t MultiPhase::speciesPhaseIndex(const index_t kGlob) const
1119 {
1120  return m_spphase[kGlob];
1121 }
1122 //====================================================================================================================
1123 doublereal MultiPhase::moleFraction(const index_t kGlob) const
1124 {
1125  return m_moleFractions[kGlob];
1126 }
1127 //====================================================================================================================
1128 
1129 bool MultiPhase::tempOK(const index_t p) const
1130 {
1131  return m_temp_OK[p];
1132 }
1133 //====================================================================================================================
1134 /// Update the locally-stored species mole fractions.
1136 {
1138 }
1139 //====================================================================================================================
1140 /// Update the locally-stored species mole fractions.
1142 {
1143  index_t ip, loc = 0;
1144  for (ip = 0; ip < m_np; ip++) {
1145  ThermoPhase* p = m_phase[ip];
1147  loc += p->nSpecies();
1148  }
1150 }
1151 //====================================================================================================================
1152 //-------------------------------------------------------------
1153 //
1154 // protected methods
1155 //
1156 //-------------------------------------------------------------
1157 
1158 
1159 
1160 /// synchronize the phase objects with the mixture state. This
1161 /// method sets each phase to the mixture temperature and
1162 /// pressure, and sets the phase mole fractions based on the
1163 /// mixture mole numbers.
1165 {
1166  index_t p, nsp, loc = 0;
1167  for (p = 0; p < m_np; p++) {
1168  nsp = m_phase[p]->nSpecies();
1169  const doublereal* x = DATA_PTR(m_moleFractions) + loc;
1170  loc += nsp;
1171  m_phase[p]->setState_TPX(m_temp, m_press, x);
1172  m_temp_OK[p] = true;
1173  if (m_temp < m_phase[p]->minTemp()
1174  || m_temp > m_phase[p]->maxTemp()) {
1175  m_temp_OK[p] = false;
1176  }
1177  }
1178 }
1179 //====================================================================================================================
1180 }
1181