Cantera  2.0
Phase.cpp
Go to the documentation of this file.
1 /**
2  * @file Phase.cpp
3  * Definition file for class Phase.
4  */
5 
6 // Copyright 2001 California Institute of Technology
7 
8 #include "cantera/thermo/Phase.h"
12 
13 using namespace std;
14 
15 namespace Cantera
16 {
17 
18 Phase::Phase() :
19  m_kk(0),
20  m_ndim(3),
21  m_xml(new XML_Node("phase")),
22  m_id("<phase>"),
23  m_name(""),
24  m_temp(0.0),
25  m_dens(0.001),
26  m_mmw(0.0),
27  m_stateNum(-1),
28  m_speciesFrozen(false),
29  m_elementsFrozen(false),
30  m_mm(0),
31  m_elem_type(0)
32 {
33 }
34 
35 Phase::Phase(const Phase& right) :
36  m_kk(0),
37  m_ndim(3),
38  m_xml(0),
39  m_id("<phase>"),
40  m_name(""),
41  m_temp(0.0),
42  m_dens(0.001),
43  m_mmw(0.0),
44  m_stateNum(-1),
45  m_speciesFrozen(false) ,
46  m_elementsFrozen(false),
47  m_mm(0),
48  m_elem_type(0)
49 {
50  // Use the assignment operator to do the actual copying
51  *this = operator=(right);
52 }
53 
55 {
56  // Check for self assignment.
57  if (this == &right) {
58  return *this;
59  }
60 
61  // Handle our own data
62  m_kk = right.m_kk;
63  m_ndim = right.m_ndim;
64  m_temp = right.m_temp;
65  m_dens = right.m_dens;
66  m_mmw = right.m_mmw;
67  m_ym = right.m_ym;
68  m_y = right.m_y;
69  m_molwts = right.m_molwts;
70  m_rmolwts = right.m_rmolwts;
71  m_stateNum = -1;
72 
78 
79  m_mm = right.m_mm;
84  m_entropy298 = right.m_entropy298;
85  m_elem_type = right.m_elem_type;
86  /*
87  * This is a little complicated. -> Because we delete m_xml
88  * in the destructor, we own m_xml completely, and we need
89  * to have our own individual copies of the XML data tree
90  * in each object
91  */
92  if (m_xml) {
93  delete m_xml;
94  m_xml = 0;
95  }
96  if (right.m_xml) {
97  m_xml = new XML_Node();
98  (right.m_xml)->copy(m_xml);
99  }
100  m_id = right.m_id;
101  m_name = right.m_name;
102 
103  return *this;
104 }
105 
106 // Destructor.
108 {
109  if (m_xml) {
110  delete m_xml;
111  m_xml = 0;
112  }
113 }
114 
115 inline void Phase::stateMFChangeCalc(bool forcerChange)
116 {
117  // Right now we assume that the mole fractions have changed every time
118  // the function is called
119  m_stateNum++;
120  if (m_stateNum > 1000000) {
121  m_stateNum = -10000000;
122  }
123 }
124 
126 {
127  return *m_xml;
128 }
129 
130 std::string Phase::id() const
131 {
132  return m_id;
133 }
134 
135 void Phase::setID(std::string id)
136 {
137  m_id = id;
138 }
139 
140 std::string Phase::name() const
141 {
142  return m_name;
143 }
144 
145 void Phase::setName(std::string nm)
146 {
147  m_name = nm;
148 }
149 
150 size_t Phase::nElements() const
151 {
152  return m_mm;
153 }
154 
155 void Phase::checkElementIndex(size_t m) const
156 {
157  if (m >= m_mm) {
158  throw IndexError("checkElementIndex", "elements", m, m_mm-1);
159  }
160 }
161 
162 void Phase::checkElementArraySize(size_t mm) const
163 {
164  if (m_mm > mm) {
165  throw ArraySizeError("checkElementArraySize", mm, m_mm);
166  }
167 }
168 
169 string Phase::elementName(size_t m) const
170 {
172  return m_elementNames[m];
173 }
174 
175 size_t Phase::elementIndex(std::string name) const
176 {
177  for (size_t i = 0; i < m_mm; i++) {
178  if (m_elementNames[i] == name) {
179  return i;
180  }
181  }
182  return npos;
183 }
184 
185 const vector<string>& Phase::elementNames() const
186 {
187  return m_elementNames;
188 }
189 
190 doublereal Phase::atomicWeight(size_t m) const
191 {
192  return m_atomicWeights[m];
193 }
194 
195 doublereal Phase::entropyElement298(size_t m) const
196 {
198  "Elements::entropy298",
199  "Entropy at 298 K of element is unknown");
200  AssertTrace(m < m_mm);
201  return (m_entropy298[m]);
202 }
203 
205 {
206  return m_atomicWeights;
207 }
208 
209 int Phase::atomicNumber(size_t m) const
210 {
211  return m_atomicNumbers[m];
212 }
213 
214 int Phase::elementType(size_t m) const
215 {
216  return m_elem_type[m];
217 }
218 
219 int Phase::changeElementType(int m, int elem_type)
220 {
221  int old = m_elem_type[m];
222  m_elem_type[m] = elem_type;
223  return old;
224 }
225 
226 doublereal Phase::nAtoms(size_t k, size_t m) const
227 {
230  return m_speciesComp[m_mm * k + m];
231 }
232 
233 void Phase::getAtoms(size_t k, double* atomArray) const
234 {
235  for (size_t m = 0; m < m_mm; m++) {
236  atomArray[m] = (double) m_speciesComp[m_mm * k + m];
237  }
238 }
239 
240 size_t Phase::speciesIndex(std::string nameStr) const
241 {
242  std::string pn;
243  std::string sn = parseSpeciesName(nameStr, pn);
244  if (pn == "" || pn == m_name || pn == m_id) {
245  vector<string>::const_iterator it = m_speciesNames.begin();
246  for (size_t k = 0; k < m_kk; k++) {
247  if (*it == sn) {
248  return k;
249  }
250  ++it;
251  }
252  return npos;
253  }
254  return npos;
255 }
256 
257 string Phase::speciesName(size_t k) const
258 {
260  return m_speciesNames[k];
261 }
262 
263 const vector<string>& Phase::speciesNames() const
264 {
265  return m_speciesNames;
266 }
267 
268 void Phase::checkSpeciesIndex(size_t k) const
269 {
270  if (k >= m_kk) {
271  throw IndexError("checkSpeciesIndex", "species", k, m_kk-1);
272  }
273 }
274 
275 void Phase::checkSpeciesArraySize(size_t kk) const
276 {
277  if (m_kk > kk) {
278  throw ArraySizeError("checkSpeciesArraySize", kk, m_kk);
279  }
280 }
281 
282 std::string Phase::speciesSPName(int k) const
283 {
284  std::string sn = speciesName(k);
285  return(m_name + ":" + sn);
286 }
287 
288 void Phase::saveState(vector_fp& state) const
289 {
290  state.resize(nSpecies() + 2);
291  saveState(state.size(),&(state[0]));
292 }
293 void Phase::saveState(size_t lenstate, doublereal* state) const
294 {
295  state[0] = temperature();
296  state[1] = density();
297  getMassFractions(state + 2);
298 }
299 
300 void Phase::restoreState(const vector_fp& state)
301 {
302  restoreState(state.size(),&state[0]);
303 }
304 
305 void Phase::restoreState(size_t lenstate, const doublereal* state)
306 {
307  if (lenstate >= nSpecies() + 2) {
308  setMassFractions_NoNorm(state + 2);
309  setTemperature(state[0]);
310  setDensity(state[1]);
311  } else {
312  throw ArraySizeError("Phase::restoreState",
313  lenstate,nSpecies()+2);
314  }
315 }
316 
317 void Phase::setMoleFractions(const doublereal* const x)
318 {
319  // Use m_y as a temporary work vector for the non-negative mole fractions
320  doublereal norm = 0.0;
321  /*
322  * sum is calculated below as the unnormalized molecular weight
323  */
324  doublereal sum = 0;
325  for (size_t k = 0; k < m_kk; k++) {
326  double xk = std::max(x[k], 0.0); // Ignore negative mole fractions
327  m_y[k] = xk;
328  norm += xk;
329  sum += m_molwts[k] * xk;
330  }
331  /*
332  * Set m_ym_ to the normalized mole fractions divided by the normalized mean molecular weight:
333  * m_ym_k = X_k / (sum_k X_k M_k)
334  */
335  transform(m_y.begin(), m_y.end(), m_ym.begin(), timesConstant<double>(1.0/sum));
336  /*
337  * Now set m_y to the normalized mass fractions
338  * m_y = X_k M_k / (sum_k X_k M_k)
339  */
340  transform(m_ym.begin(), m_ym.begin() + m_kk, m_molwts.begin(), m_y.begin(), multiplies<double>());
341  /*
342  * Calculate the normalized molecular weight
343  */
344  m_mmw = sum/norm;
345 
346  // Call a routine to determine whether state has changed.
348 }
349 
350 void Phase::setMoleFractions_NoNorm(const doublereal* const x)
351 {
352  m_mmw = dot(x, x + m_kk, m_molwts.begin());
353  doublereal rmmw = 1.0/m_mmw;
354  transform(x, x + m_kk, m_ym.begin(), timesConstant<double>(rmmw));
355  transform(m_ym.begin(), m_ym.begin() + m_kk, m_molwts.begin(),
356  m_y.begin(), multiplies<double>());
357 
358  // Call a routine to determine whether state has changed.
360 }
361 
363 {
364  size_t kk = nSpecies();
365  doublereal x;
366  vector_fp mf(kk, 0.0);
367  for (size_t k = 0; k < kk; k++) {
368  x = xMap[speciesName(k)];
369  if (x > 0.0) {
370  mf[k] = x;
371  }
372  }
373  setMoleFractions(&mf[0]);
374 }
375 
376 void Phase::setMoleFractionsByName(const std::string& x)
377 {
378  size_t kk = nSpecies();
379  compositionMap xx;
380  for (size_t k = 0; k < kk; k++) {
381  xx[speciesName(k)] = -1.0;
382  }
383  parseCompString(x, xx);
385 }
386 
387 void Phase::setMassFractions(const doublereal* const y)
388 {
389  for (size_t k = 0; k < m_kk; k++) {
390  m_y[k] = std::max(y[k], 0.0); // Ignore negative mass fractions
391  }
392  doublereal norm = accumulate(m_y.begin(), m_y.end(), 0.0);
393  scale(m_y.begin(), m_y.end(), m_y.begin(), 1.0/norm);
394 
395  transform(m_y.begin(), m_y.end(), m_rmolwts.begin(),
396  m_ym.begin(), multiplies<double>());
397  m_mmw = 1.0 / accumulate(m_ym.begin(), m_ym.end(), 0.0);
398 
399  // Call a routine to determine whether state has changed.
401 }
402 
403 void Phase::setMassFractions_NoNorm(const doublereal* const y)
404 {
405  doublereal sum = 0.0;
406  copy(y, y + m_kk, m_y.begin());
407  transform(m_y.begin(), m_y.end(), m_rmolwts.begin(), m_ym.begin(),
408  multiplies<double>());
409  sum = accumulate(m_ym.begin(), m_ym.end(), 0.0);
410  m_mmw = 1.0/sum;
411 
412  // Call a routine to determine whether state has changed.
414 }
415 
417 {
418  size_t kk = nSpecies();
419  doublereal y;
420  vector_fp mf(kk, 0.0);
421  for (size_t k = 0; k < kk; k++) {
422  y = yMap[speciesName(k)];
423  if (y > 0.0) {
424  mf[k] = y;
425  }
426  }
427  setMassFractions(&mf[0]);
428 }
429 
430 void Phase::setMassFractionsByName(const std::string& y)
431 {
432  size_t kk = nSpecies();
433  compositionMap yy;
434  for (size_t k = 0; k < kk; k++) {
435  yy[speciesName(k)] = -1.0;
436  }
437  parseCompString(y, yy);
439 }
440 
441 void Phase::setState_TRX(doublereal t, doublereal dens, const doublereal* x)
442 {
443  setMoleFractions(x);
444  setTemperature(t);
445  setDensity(dens);
446 }
447 
448 void Phase::setState_TNX(doublereal t, doublereal n, const doublereal* x)
449 {
450  setMoleFractions(x);
451  setTemperature(t);
452  setMolarDensity(n);
453 }
454 
455 void Phase::setState_TRX(doublereal t, doublereal dens, compositionMap& x)
456 {
458  setTemperature(t);
459  setDensity(dens);
460 }
461 
462 void Phase::setState_TRY(doublereal t, doublereal dens, const doublereal* y)
463 {
464  setMassFractions(y);
465  setTemperature(t);
466  setDensity(dens);
467 }
468 
469 void Phase::setState_TRY(doublereal t, doublereal dens, compositionMap& y)
470 {
472  setTemperature(t);
473  setDensity(dens);
474 }
475 
476 void Phase::setState_TR(doublereal t, doublereal rho)
477 {
478  setTemperature(t);
479  setDensity(rho);
480 }
481 
482 void Phase::setState_TX(doublereal t, doublereal* x)
483 {
484  setTemperature(t);
485  setMoleFractions(x);
486 }
487 
488 void Phase::setState_TY(doublereal t, doublereal* y)
489 {
490  setTemperature(t);
491  setMassFractions(y);
492 }
493 
494 void Phase::setState_RX(doublereal rho, doublereal* x)
495 {
496  setMoleFractions(x);
497  setDensity(rho);
498 }
499 
500 void Phase::setState_RY(doublereal rho, doublereal* y)
501 {
502  setMassFractions(y);
503  setDensity(rho);
504 }
505 
506 doublereal Phase::molecularWeight(size_t k) const
507 {
509  return m_molwts[k];
510 }
511 
513 {
514  const vector_fp& mw = molecularWeights();
515  if (weights.size() < mw.size()) {
516  weights.resize(mw.size());
517  }
518  copy(mw.begin(), mw.end(), weights.begin());
519 }
520 
521 void Phase::getMolecularWeights(int iwt, doublereal* weights) const
522 {
523  const vector_fp& mw = molecularWeights();
524  copy(mw.begin(), mw.end(), weights);
525 }
526 
527 void Phase::getMolecularWeights(doublereal* weights) const
528 {
529  const vector_fp& mw = molecularWeights();
530  copy(mw.begin(), mw.end(), weights);
531 }
532 
534 {
535  return m_molwts;
536 }
537 
539 {
540  x.clear();
541  size_t kk = nSpecies();
542  for (size_t k = 0; k < kk; k++) {
543  x[speciesName(k)] = Phase::moleFraction(k);
544  }
545 }
546 
547 void Phase::getMoleFractions(doublereal* const x) const
548 {
549  scale(m_ym.begin(), m_ym.end(), x, m_mmw);
550 }
551 
552 doublereal Phase::moleFraction(size_t k) const
553 {
555  return m_ym[k] * m_mmw;
556 }
557 
558 doublereal Phase::moleFraction(std::string nameSpec) const
559 {
560  size_t iloc = speciesIndex(nameSpec);
561  if (iloc != npos) {
562  return moleFraction(iloc);
563  } else {
564  return 0.0;
565  }
566 }
567 
568 const doublereal* Phase::moleFractdivMMW() const
569 {
570  return &m_ym[0];
571 }
572 
573 doublereal Phase::massFraction(size_t k) const
574 {
576  return m_y[k];
577 }
578 
579 doublereal Phase::massFraction(std::string nameSpec) const
580 {
581  size_t iloc = speciesIndex(nameSpec);
582  if (iloc != npos) {
583  return massFractions()[iloc];
584  } else {
585  return 0.0;
586  }
587 }
588 
589 void Phase::getMassFractions(doublereal* const y) const
590 {
591  copy(m_y.begin(), m_y.end(), y);
592 }
593 
594 doublereal Phase::concentration(const size_t k) const
595 {
597  return m_y[k] * m_dens * m_rmolwts[k] ;
598 }
599 
600 void Phase::getConcentrations(doublereal* const c) const
601 {
602  scale(m_ym.begin(), m_ym.end(), c, m_dens);
603 }
604 
605 void Phase::setConcentrations(const doublereal* const conc)
606 {
607  // Use m_y as temporary storage for non-negative concentrations
608  doublereal sum = 0.0, norm = 0.0;
609  for (size_t k = 0; k != m_kk; ++k) {
610  double ck = std::max(conc[k], 0.0); // Ignore negative concentrations
611  m_y[k] = ck;
612  sum += ck * m_molwts[k];
613  norm += ck;
614  }
615  m_mmw = sum/norm;
616  setDensity(sum);
617  doublereal rsum = 1.0/sum;
618  for (size_t k = 0; k != m_kk; ++k) {
619  m_ym[k] = m_y[k] * rsum;
620  m_y[k] = m_ym[k] * m_molwts[k]; // m_y is now the mass fraction
621  }
622 
623  // Call a routine to determine whether state has changed.
625 }
626 
627 doublereal Phase::molarDensity() const
628 {
629  return density()/meanMolecularWeight();
630 }
631 
632 void Phase::setMolarDensity(const doublereal molarDensity)
633 {
634  m_dens = molarDensity*meanMolecularWeight();
635 }
636 
637 doublereal Phase::molarVolume() const
638 {
639  return 1.0/molarDensity();
640 }
641 
642 doublereal Phase::charge(size_t k) const
643 {
644  return m_speciesCharge[k];
645 }
646 
647 doublereal Phase::chargeDensity() const
648 {
649  size_t kk = nSpecies();
650  doublereal cdens = 0.0;
651  for (size_t k = 0; k < kk; k++) {
652  cdens += charge(k)*moleFraction(k);
653  }
654  cdens *= Faraday;
655  return cdens;
656 }
657 
658 doublereal Phase::mean_X(const doublereal* const Q) const
659 {
660  return m_mmw*std::inner_product(m_ym.begin(), m_ym.end(), Q, 0.0);
661 }
662 
663 doublereal Phase::mean_Y(const doublereal* const Q) const
664 {
665  return dot(m_y.begin(), m_y.end(), Q);
666 }
667 
668 doublereal Phase::sum_xlogx() const
669 {
670  return m_mmw* Cantera::sum_xlogx(m_ym.begin(), m_ym.end()) + log(m_mmw);
671 }
672 
673 doublereal Phase::sum_xlogQ(doublereal* Q) const
674 {
675  return m_mmw * Cantera::sum_xlogQ(m_ym.begin(), m_ym.end(), Q);
676 }
677 
678 void Phase::addElement(const std::string& symbol, doublereal weight)
679 {
680  if (weight == -12345.0) {
681  weight = LookupWtElements(symbol);
682  if (weight < 0.0) {
683  throw ElementsFrozen("addElement");
684  }
685  }
686  if (m_elementsFrozen) {
687  throw ElementsFrozen("addElement");
688  return;
689  }
690  m_atomicWeights.push_back(weight);
691  m_elementNames.push_back(symbol);
692  if (symbol == "E") {
694  } else {
695  m_elem_type.push_back(CT_ELEM_TYPE_ABSPOS);
696  }
697 
698  m_mm++;
699 }
700 
702 {
703  doublereal weight = atof(e["atomicWt"].c_str());
704  string symbol = e["name"];
705  addElement(symbol, weight);
706 }
707 
708 void Phase::addUniqueElement(const std::string& symbol, doublereal weight,
709  int atomicNumber, doublereal entropy298,
710  int elem_type)
711 {
712  if (weight == -12345.0) {
713  weight = LookupWtElements(symbol);
714  if (weight < 0.0) {
715  throw ElementsFrozen("addElement");
716  }
717  }
718  /*
719  * First decide if this element has been previously added
720  * by conducting a string search. If it unique, add it to
721  * the list.
722  */
723  int ifound = 0;
724  int i = 0;
725  for (vector<string>::const_iterator it = m_elementNames.begin();
726  it < m_elementNames.end(); ++it, ++i) {
727  if (*it == symbol) {
728  ifound = 1;
729  break;
730  }
731  }
732  if (!ifound) {
733  if (m_elementsFrozen) {
734  throw ElementsFrozen("addElement");
735  return;
736  }
737  m_atomicWeights.push_back(weight);
738  m_elementNames.push_back(symbol);
739  m_atomicNumbers.push_back(atomicNumber);
740  m_entropy298.push_back(entropy298);
741  if (symbol == "E") {
743  } else {
744  m_elem_type.push_back(elem_type);
745  }
746  m_mm++;
747  } else {
748  if (m_atomicWeights[i] != weight) {
749  throw CanteraError("AddUniqueElement",
750  "Duplicate Elements (" + symbol + ") have different weights");
751  }
752  }
753 }
754 
756 {
757  doublereal weight = 0.0;
758  if (e.hasAttrib("atomicWt")) {
759  weight = atof(stripws(e["atomicWt"]).c_str());
760  }
761  int anum = 0;
762  if (e.hasAttrib("atomicNumber")) {
763  anum = atoi(stripws(e["atomicNumber"]).c_str());
764  }
765  string symbol = e["name"];
766  doublereal entropy298 = ENTROPY298_UNKNOWN;
767  if (e.hasChild("entropy298")) {
768  XML_Node& e298Node = e.child("entropy298");
769  if (e298Node.hasAttrib("value")) {
770  entropy298 = atofCheck(stripws(e298Node["value"]).c_str());
771  }
772  }
773  if (weight != 0.0) {
774  addUniqueElement(symbol, weight, anum, entropy298);
775  } else {
776  addUniqueElement(symbol);
777  }
778 }
779 
781 {
782  // get the declared element names
783  if (! phase.hasChild("elementArray")) {
784  throw CanteraError("Elements::addElementsFromXML",
785  "phase xml node doesn't have \"elementArray\" XML Node");
786  }
787  XML_Node& elements = phase.child("elementArray");
788  vector<string> enames;
789  ctml::getStringArray(elements, enames);
790 
791  // // element database defaults to elements.xml
792  string element_database = "elements.xml";
793  if (elements.hasAttrib("datasrc")) {
794  element_database = elements["datasrc"];
795  }
796 
797  XML_Node* doc = get_XML_File(element_database);
798  XML_Node* dbe = &doc->child("ctml/elementData");
799 
800  XML_Node& root = phase.root();
801  XML_Node* local_db = 0;
802  if (root.hasChild("ctml")) {
803  if (root.child("ctml").hasChild("elementData")) {
804  local_db = &root.child("ctml/elementData");
805  }
806  }
807 
808  int nel = static_cast<int>(enames.size());
809  int i;
810  string enm;
811  XML_Node* e = 0;
812  for (i = 0; i < nel; i++) {
813  e = 0;
814  if (local_db) {
815  //writelog("looking in local database.");
816  e = local_db->findByAttr("name",enames[i]);
817  //if (!e) writelog(enames[i]+" not found.");
818  }
819  if (!e) {
820  e = dbe->findByAttr("name",enames[i]);
821  }
822  if (e) {
823  addUniqueElement(*e);
824  } else {
825  throw CanteraError("addElementsFromXML","no data for element "
826  +enames[i]);
827  }
828  }
829 }
830 
832 {
833  m_elementsFrozen = true;
834 }
835 
837 {
838  return m_elementsFrozen;
839 }
840 
841 size_t Phase::addUniqueElementAfterFreeze(const std::string& symbol,
842  doublereal weight, int atomicNumber,
843  doublereal entropy298, int elem_type)
844 {
845  size_t ii = elementIndex(symbol);
846  if (ii != npos) {
847  return ii;
848  }
849  // Check to see that the element isn't really in the list
850  m_elementsFrozen = false;
851  addUniqueElement(symbol, weight, atomicNumber, entropy298, elem_type);
852  m_elementsFrozen = true;
853  ii = elementIndex(symbol);
854  if (ii != m_mm-1) {
855  throw CanteraError("Phase::addElementAfterFreeze()", "confused");
856  }
857  if (m_kk > 0) {
859  m_speciesComp.resize(m_kk*m_mm, 0.0);
860  for (size_t k = 0; k < m_kk; k++) {
861  size_t m_old = m_mm - 1;
862  for (size_t m = 0; m < m_old; m++) {
863  m_speciesComp[k * m_mm + m] = old[k * (m_old) + m];
864  }
865  m_speciesComp[k * (m_mm) + (m_mm-1)] = 0.0;
866  }
867  }
868  return ii;
869 }
870 
871 void Phase::addSpecies(const std::string& name, const doublereal* comp,
872  doublereal charge, doublereal size)
873 {
874  freezeElements();
875  m_speciesNames.push_back(name);
876  m_speciesCharge.push_back(charge);
877  m_speciesSize.push_back(size);
878  size_t ne = nElements();
879  // Create a changeable copy of the element composition. We now change
880  // the charge potentially
881  vector_fp compNew(ne);
882  for (size_t m = 0; m < ne; m++) {
883  compNew[m] = comp[m];
884  }
885  double wt = 0.0;
886  const vector_fp& aw = atomicWeights();
887  if (charge != 0.0) {
888  size_t eindex = elementIndex("E");
889  if (eindex != npos) {
890  doublereal ecomp = compNew[eindex];
891  if (fabs(charge + ecomp) > 0.001) {
892  if (ecomp != 0.0) {
893  throw CanteraError("Phase::addSpecies",
894  "Input charge and element E compositions differ "
895  "for species " + name);
896  } else {
897  // Just fix up the element E composition based on the input
898  // species charge
899  compNew[eindex] = -charge;
900  }
901  }
902  } else {
903  addUniqueElementAfterFreeze("E", 0.000545, 0, 0.0,
905  ne = nElements();
906  eindex = elementIndex("E");
907  compNew.resize(ne);
908  compNew[ne - 1] = - charge;
909  }
910  }
911  for (size_t m = 0; m < ne; m++) {
912  m_speciesComp.push_back(compNew[m]);
913  wt += compNew[m] * aw[m];
914  }
915  m_molwts.push_back(wt);
916  m_kk++;
917 }
918 
919 void Phase::addUniqueSpecies(const std::string& name, const doublereal* comp,
920  doublereal charge, doublereal size)
921 {
922  vector<string>::const_iterator it = m_speciesNames.begin();
923  for (size_t k = 0; k < m_kk; k++) {
924  if (*it == name) {
925  // We have found a match. At this point we could do some
926  // compatibility checks. However, let's just return for the moment
927  // without specifying any error.
928  for (size_t i = 0; i < m_mm; i++) {
929  if (comp[i] != m_speciesComp[m_kk * m_mm + i]) {
930  throw CanteraError("addUniqueSpecies",
931  "Duplicate species have different "
932  "compositions: " + *it);
933  }
934  }
935  if (charge != m_speciesCharge[m_kk]) {
936  throw CanteraError("addUniqueSpecies",
937  "Duplicate species have different "
938  "charges: " + *it);
939  }
940  if (size != m_speciesSize[m_kk]) {
941  throw CanteraError("addUniqueSpecies",
942  "Duplicate species have different "
943  "sizes: " + *it);
944  }
945  return;
946  }
947  ++it;
948  }
949  addSpecies(name, comp, charge, size);
950 }
951 
953 {
954  m_speciesFrozen = true;
956 }
957 
958 void Phase::init(const vector_fp& mw)
959 {
960  m_kk = mw.size();
961  m_rmolwts.resize(m_kk);
962  m_y.resize(m_kk, 0.0);
963  m_ym.resize(m_kk, 0.0);
964  copy(mw.begin(), mw.end(), m_molwts.begin());
965  for (size_t k = 0; k < m_kk; k++) {
966  if (m_molwts[k] < 0.0) {
967  throw CanteraError("Phase::init",
968  "negative molecular weight for species number "
969  + int2str(k));
970  }
971 
972  // Some surface phases may define species representing empty sites
973  // that have zero molecular weight. Give them a very small molecular
974  // weight to avoid dividing by zero.
975  if (m_molwts[k] < Tiny) {
976  m_molwts[k] = Tiny;
977  }
978  m_rmolwts[k] = 1.0/m_molwts[k];
979  }
980 
981  // Now that we have resized the State object, let's fill it with a valid
982  // mass fraction vector that sums to one. The Phase object should never
983  // have a mass fraction vector that doesn't sum to one. We will assume that
984  // species 0 has a mass fraction of 1.0 and mass fraction of all other
985  // species is 0.0.
986  m_y[0] = 1.0;
987  m_ym[0] = m_y[0] * m_rmolwts[0];
988  m_mmw = 1.0 / m_ym[0];
989 }
990 
991 bool Phase::ready() const
992 {
993  return (m_kk > 0 && m_elementsFrozen && m_speciesFrozen);
994 }
995 
996 } // namespace Cantera