Cantera  2.0
LatticeSolidPhase.cpp
1 /**
2  * @file LatticeSolidPhase.h
3  * Definitions for a simple thermodynamics model of a bulk solid phase
4  * derived from %ThermoPhase,
5  * assuming an ideal solution model based on a lattice of solid atoms
6  * (see \ref thermoprops and class \link Cantera::LatticeSolidPhase LatticeSolidPhase\endlink).
7  */
8 
9 #include "cantera/base/ct_defs.h"
10 #include "cantera/thermo/mix_defs.h"
17 
18 #include <string>
19 
20 using namespace std;
21 //======================================================================================================================
22 namespace Cantera
23 {
24 
25 //====================================================================================================================
26 // Base empty constructor
27 LatticeSolidPhase::LatticeSolidPhase() :
28  m_mm(0),
29  m_tlast(0.0),
30  m_press(-1.0),
31  m_molar_density(0.0),
32  m_nlattice(0),
33  m_lattice(0),
34  m_x(0),
35  theta_(0),
36  tmpV_(0)
37 {
38 }
39 //====================================================================================================================
40 // Copy Constructor
41 /*
42  * @param right Object to be copied
43  */
45  m_mm(0),
46  m_tlast(0.0),
47  m_press(-1.0),
48  m_molar_density(0.0),
49  m_nlattice(0),
50  m_lattice(0),
51  m_x(0),
52  theta_(0),
53  tmpV_(0)
54 {
55  *this = operator=(right);
56 }
57 //====================================================================================================================
58 // Assignment operator
59 /*
60  * @param right Object to be copied
61  */
64 {
65  if (&right != this) {
67  m_mm = right.m_mm;
68  m_tlast = right.m_tlast;
69  m_press = right.m_press;
71  m_nlattice = right.m_nlattice;
72  deepStdVectorPointerCopy<LatticePhase>(right.m_lattice, m_lattice);
73  m_x = right.m_x;
74  theta_ = right.theta_;
75  tmpV_ = right.tmpV_;
76  }
77  return *this;
78 }
79 //====================================================================================================================
80 // Destructor
82 {
83  // We own the sublattices. So we have to delete the sublattices
84  for (size_t n = 0; n < m_nlattice; n++) {
85  delete m_lattice[n];
86  m_lattice[n] = 0;
87  }
88 }
89 //====================================================================================================================
90 // Duplication function
91 /*
92  * This virtual function is used to create a duplicate of the
93  * current phase. It's used to duplicate the phase when given
94  * a ThermoPhase pointer to the phase.
95  *
96  * @return It returns a %ThermoPhase pointer.
97  */
99 {
100  LatticeSolidPhase* igp = new LatticeSolidPhase(*this);
101  return (ThermoPhase*) igp;
102 }
103 
104 //====================================================================================================================
105 // Minimum temperature for which the thermodynamic data for the species
106 // or phase are valid.
107 /*
108  * If no argument is supplied, the
109  * value returned will be the lowest temperature at which the
110  * data for \e all species are valid. Otherwise, the value
111  * will be only for species \a k. This function is a wrapper
112  * that calls the species thermo minTemp function.
113  *
114  * @param k index of the species. Default is -1, which will return the max of the min value
115  * over all species.
116  */
117 doublereal LatticeSolidPhase::minTemp(size_t k) const
118 {
119  if (k != npos) {
120  for (size_t n = 0; n < m_nlattice; n++) {
121  if (lkstart_[n+1] < k) {
122  double ml = (m_lattice[n])->minTemp(k-lkstart_[n]);
123  return ml;
124  }
125  }
126  }
127  doublereal mm = 1.0E300;
128  for (size_t n = 0; n < m_nlattice; n++) {
129  double ml = (m_lattice[n])->minTemp();
130  mm = std::min(mm, ml);
131  }
132  return mm;
133 }
134 //====================================================================================================================
135 // Maximum temperature for which the thermodynamic data for the species
136 // or phase are valid.
137 /*
138  * If no argument is supplied, the
139  * value returned will be the lowest temperature at which the
140  * data for \e all species are valid. Otherwise, the value
141  * will be only for species \a k. This function is a wrapper
142  * that calls the species thermo minTemp function.
143  *
144  * @param k index of the species. Default is -1, which will return the max of the min value
145  * over all species.
146  */
147 doublereal LatticeSolidPhase::maxTemp(size_t k) const
148 {
149  if (k != npos) {
150  for (size_t n = 0; n < m_nlattice; n++) {
151  if (lkstart_[n+1] < k) {
152  double ml = (m_lattice[n])->maxTemp(k - lkstart_[n]);
153  return ml;
154  }
155  }
156  }
157  doublereal mm = -1.0E300;
158  for (size_t n = 0; n < m_nlattice; n++) {
159  double ml = (m_lattice[n])->maxTemp();
160  mm = std::max(mm, ml);
161  }
162  return mm;
163 }
164 //====================================================================================================================
165 /*
166  * Returns the reference pressure in Pa. This function is a wrapper
167  * that calls the species thermo refPressure function.
168  */
170 {
171  return m_lattice[0]->refPressure();
172 }
173 //====================================================================================================================
174 doublereal LatticeSolidPhase::
176 {
177  _updateThermo();
178  doublereal sum = 0.0;
179  for (size_t n = 0; n < m_nlattice; n++) {
180  sum += theta_[n] * m_lattice[n]->enthalpy_mole();
181  }
182  return sum;
183 }
184 //====================================================================================================================
186 {
187  _updateThermo();
188  doublereal sum = 0.0;
189  for (size_t n = 0; n < m_nlattice; n++) {
190  sum += theta_[n] * m_lattice[n]->intEnergy_mole();
191  }
192  return sum;
193 }
194 //====================================================================================================================
196 {
197  _updateThermo();
198  doublereal sum = 0.0;
199  for (size_t n = 0; n < m_nlattice; n++) {
200  sum += theta_[n] * m_lattice[n]->entropy_mole();
201  }
202  return sum;
203 }
204 //====================================================================================================================
206 {
207  _updateThermo();
208  doublereal sum = 0.0;
209  for (size_t n = 0; n < m_nlattice; n++) {
210  sum += theta_[n] * m_lattice[n]->gibbs_mole();
211  }
212  return sum;
213 }
214 //====================================================================================================================
215 doublereal LatticeSolidPhase::cp_mole() const
216 {
217  _updateThermo();
218  doublereal sum = 0.0;
219  for (size_t n = 0; n < m_nlattice; n++) {
220  sum += theta_[n] * m_lattice[n]->cp_mole();
221  }
222  return sum;
223 }
224 //====================================================================================================================
226 {
227  _updateThermo();
228  size_t strt = 0;
229  for (size_t n = 0; n < m_nlattice; n++) {
230  m_lattice[n]->getMoleFractions(c+strt);
231  strt += m_lattice[n]->nSpecies();
232  }
233 }
234 //====================================================================================================================
236 {
237  for (size_t k = 0; k < m_kk; k++) {
238  ac[k] = 1.0;
239  }
240 }
241 //====================================================================================================================
243 {
244  return 1.0;
245 }
246 //====================================================================================================================
247 doublereal LatticeSolidPhase::logStandardConc(size_t k) const
248 {
249  return 0.0;
250 }
251 
252 //====================================================================================================================
253 // Set the pressure at constant temperature. Units: Pa.
254 /*
255  *
256  * @param p Pressure (units - Pa)
257  */
259 {
260  m_press = p;
261  for (size_t n = 0; n < m_nlattice; n++) {
262  m_lattice[n]->setPressure(m_press);
263  }
264  calcDensity();
265 }
266 //====================================================================================================================
267 // Calculate the density of the solid mixture
268 /*
269  * The formula for this is
270  *
271  * \f[
272  * \rho = \sum_n{ \rho_n \theta_n }
273  * \f]
274  *
275  * where \f$ \rho_n \f$ is the density of the nth sublattice
276  *
277  * Note this is a nonvirtual function.
278  */
280 {
281  double sum = 0.0;
282  for (size_t n = 0; n < m_nlattice; n++) {
283  sum += theta_[n] * m_lattice[n]->density();
284  }
285  Phase::setDensity(sum);
286  return sum;
287 }
288 //====================================================================================================================
289 // Set the mole fractions to the specified values, and then
290 // normalize them so that they sum to 1.0 for each of the subphases
291 /*
292  * On input, the mole fraction vector is assumed to sum to one for each of the sublattices. The sublattices
293  * are updated with this mole fraction vector. The mole fractions are also stored within this object, after
294  * they are normalized to one by dividing by the number of sublattices.
295  *
296  * @param x Input vector of mole fractions. There is no restriction
297  * on the sum of the mole fraction vector. Internally,
298  * this object will pass portions of this vector to the sublattices which assume that the portions
299  * individually sum to one.
300  * Length is m_kk.
301  */
302 void LatticeSolidPhase::setMoleFractions(const doublereal* const x)
303 {
304  size_t nsp, strt = 0;
305  for (size_t n = 0; n < m_nlattice; n++) {
306  nsp = m_lattice[n]->nSpecies();
307  m_lattice[n]->setMoleFractions(x + strt);
308  strt += nsp;
309  }
310  for (size_t k = 0; k < strt; k++) {
311  m_x[k] = x[k] / m_nlattice;
312  }
314  calcDensity();
315 }
316 //====================================================================================================================
317 // Get the species mole fraction vector.
318 /*
319  * On output the mole fraction vector will sum to one for each of the subphases which make up this phase.
320  *
321  * @param x On return, x contains the mole fractions. Must have a
322  * length greater than or equal to the number of species.
323  */
324 void LatticeSolidPhase::getMoleFractions(doublereal* const x) const
325 {
326  size_t nsp, strt = 0;
327  // the ifdef block should be the way we calculate this.!!!!!
329  doublereal sum;
330  for (size_t n = 0; n < m_nlattice; n++) {
331  nsp = m_lattice[n]->nSpecies();
332  sum = 0.0;
333  for (size_t k = 0; k < nsp; k++) {
334  sum += (x + strt)[k];
335  }
336  for (size_t k = 0; k < nsp; k++) {
337  (x + strt)[k] /= sum;
338  }
339  /*
340  * At this point we can check against the mole fraction vector of the underlying LatticePhase objects and
341  * get the same answer.
342  */
343 #ifdef DEBUG_MODE
344  m_lattice[n]->getMoleFractions(&(m_x[strt]));
345  for (size_t k = 0; k < nsp; k++) {
346  if (fabs((x + strt)[k] - m_x[strt+k]) > 1.0E-14) {
347  throw CanteraError("LatticeSolidPhase::getMoleFractions()",
348  "internal error");
349  }
350  }
351 #endif
352  strt += nsp;
353  }
354 }
355 //====================================================================================================================
356 // Get the species chemical potentials. Units: J/kmol.
357 /*
358  * This function returns a vector of chemical potentials of the
359  * species in solution at the current temperature, pressure
360  * and mole fraction of the solution.
361  *
362  * This returns the underlying lattice chemical potentials
363  *
364  * @param mu Output vector of species chemical
365  * potentials. Length: m_kk. Units: J/kmol
366  */
367 void LatticeSolidPhase::getChemPotentials(doublereal* mu) const
368 {
369  _updateThermo();
370  size_t strt = 0;
371  for (size_t n = 0; n < m_nlattice; n++) {
372  size_t nlsp = m_lattice[n]->nSpecies();
373  m_lattice[n]->getChemPotentials(mu+strt);
374  strt += nlsp;
375  }
376 }
377 //====================================================================================================================
379 {
380  _updateThermo();
381  size_t strt = 0;
382  for (size_t n = 0; n < m_nlattice; n++) {
383  size_t nlsp = m_lattice[n]->nSpecies();
384  m_lattice[n]->getPartialMolarEnthalpies(hbar + strt);
385  strt += nlsp;
386  }
387 }
388 //====================================================================================================================
390 {
391  _updateThermo();
392  size_t strt = 0;
393  for (size_t n = 0; n < m_nlattice; n++) {
394  size_t nlsp = m_lattice[n]->nSpecies();
395  m_lattice[n]->getPartialMolarEntropies(sbar + strt);
396  strt += nlsp;
397  }
398 }
399 //====================================================================================================================
400 void LatticeSolidPhase::getPartialMolarCp(doublereal* cpbar) const
401 {
402  _updateThermo();
403  size_t strt = 0;
404  for (size_t n = 0; n < m_nlattice; n++) {
405  size_t nlsp = m_lattice[n]->nSpecies();
406  m_lattice[n]->getPartialMolarCp(cpbar + strt);
407  strt += nlsp;
408  }
409 }
410 //====================================================================================================================
411 void LatticeSolidPhase::getPartialMolarVolumes(doublereal* vbar) const
412 {
413  _updateThermo();
414  size_t strt = 0;
415  for (size_t n = 0; n < m_nlattice; n++) {
416  size_t nlsp = m_lattice[n]->nSpecies();
417  m_lattice[n]->getPartialMolarVolumes(vbar + strt);
418  strt += nlsp;
419  }
420 }
421 //====================================================================================================================
422 // Get the array of standard state chemical potentials at unit activity for the species
423 // at their standard states at the current <I>T</I> and <I>P</I> of the solution.
424 /*
425  * These are the standard state chemical potentials \f$ \mu^0_k(T,P)
426  * \f$. The values are evaluated at the current
427  * temperature and pressure of the solution.
428  *
429  * This returns the underlying lattice standard chemical potentials, as the units are kmol-1 of
430  * the sublattice species.
431  *
432  * @param mu0 Output vector of chemical potentials.
433  * Length: m_kk. Units: J/kmol
434  */
436 {
437  _updateThermo();
438  size_t strt = 0;
439  for (size_t n = 0; n < m_nlattice; n++) {
440  m_lattice[n]->getStandardChemPotentials(mu0+strt);
441  strt += m_lattice[n]->nSpecies();
442  }
443 }
444 //====================================================================================================================
445 void LatticeSolidPhase::getGibbs_RT_ref(doublereal* grt) const
446 {
447  _updateThermo();
448  for (size_t n = 0; n < m_nlattice; n++) {
449  m_lattice[n]->getGibbs_RT_ref(grt + lkstart_[n]);
450  }
451 }
452 //====================================================================================================================
453 void LatticeSolidPhase::getGibbs_ref(doublereal* g) const
454 {
455  getGibbs_RT_ref(g);
456  for (size_t k = 0; k < m_kk; k++) {
457  g[k] *= GasConstant * temperature();
458  }
459 }
460 //====================================================================================================================
461 // Add in species from Slave phases
462 /*
463  * This hook is used for cSS_CONVENTION_SLAVE phases
464  *
465  * @param phaseNode XML_Node for the current phase
466  */
468 {
469  size_t kk = 0;
470  size_t kstart = 0;
472  SpeciesThermo* spthermo_ptr = new GeneralSpeciesThermo();
473  setSpeciesThermo(spthermo_ptr);
474  m_speciesData.clear();
475 
476  XML_Node& eosdata = phaseNode->child("thermo");
477  XML_Node& la = eosdata.child("LatticeArray");
478  std::vector<XML_Node*> lattices;
479  la.getChildren("phase",lattices);
480  for (size_t n = 0; n < m_nlattice; n++) {
481  LatticePhase* lp = m_lattice[n];
482  XML_Node* phaseNode_ptr = lattices[n];
483  size_t nsp = lp->nSpecies();
484  vector<doublereal> constArr(lp->nElements());
485  const vector_fp& aws = lp->atomicWeights();
486  for (size_t es = 0; es < lp->nElements(); es++) {
487  string esName = lp->elementName(es);
488  double wt = aws[es];
489  int an = lp->atomicNumber(es);
490  int e298 = lp->entropyElement298(es); //! @todo Why is this an int instead of a double?
491  int et = lp->elementType(es);
492  addUniqueElementAfterFreeze(esName, wt, an, e298, et);
493  }
494  const std::vector<const XML_Node*> & spNode = lp->speciesData();
495  kstart = kk;
496 
497 
498  for (size_t k = 0; k < nsp; k++) {
499  std::string sname = lp->speciesName(k);
500  std::map<std::string, double> comp;
501  lp->getAtoms(k, DATA_PTR(constArr));
502  size_t nel = nElements();
503  vector_fp ecomp(nel, 0.0);
504  for (size_t m = 0; m < lp->nElements(); m++) {
505  if (constArr[m] != 0.0) {
506  std::string oldEname = lp->elementName(m);
507  size_t newIndex = elementIndex(oldEname);
508  if (newIndex == npos) {
509  throw CanteraError("LatticeSolidPhase::installSlavePhases", "confused");
510  }
511  ecomp[newIndex] = constArr[m];
512  }
513  }
514  double chrg = lp->charge(k);
515  double sz = lp->size(k);
516  addUniqueSpecies(sname, &ecomp[0], chrg, sz);
517  spFactory->installThermoForSpecies(kk, *(spNode[k]), this, *m_spthermo, phaseNode_ptr);
518 
519  m_speciesData.push_back(new XML_Node(*(spNode[k])));
520  kk++;
521  }
522  /*
523  * Add in the lattice stoichiometry constraint
524  */
525  if (n > 0) {
526  string econ = "LC_";
527  econ += int2str(n);
528  econ += "_" + id();
529  size_t m = addUniqueElementAfterFreeze(econ, 0.0, 0, 0.0, CT_ELEM_TYPE_LATTICERATIO);
530  m_mm = nElements();
531  LatticePhase* lp0 = m_lattice[0];
532  size_t nsp0 = lp0->nSpecies();
533  for (size_t k = 0; k < nsp0; k++) {
534  m_speciesComp[k * m_mm + m] = -theta_[0];
535  }
536  for (size_t k = 0; k < nsp; k++) {
537  size_t ks = kstart + k;
538  m_speciesComp[ks * m_mm + m] = theta_[n];
539  }
540  }
541  }
542 }
543 
544 //====================================================================================================================
545 // Initialize the ThermoPhase object after all species have been set up
546 /*
547  * @internal Initialize.
548  *
549  * This method is provided to allow subclasses to perform any initialization required after all
550  * species have been added. For example, it might be used to
551  * resize internal work arrays that must have an entry for
552  * each species. The base class implementation does nothing,
553  * and subclasses that do not require initialization do not
554  * need to overload this method. When importing a CTML phase
555  * description, this method is called from ThermoPhase::initThermoXML(),
556  * which is called from importPhase(), just prior to returning from function importPhase().
557  *
558  * @see importCTML.cpp
559  */
561 {
562  m_kk = nSpecies();
563  m_mm = nElements();
564  initLengths();
565  size_t nsp, loc = 0;
566  for (size_t n = 0; n < m_nlattice; n++) {
567  nsp = m_lattice[n]->nSpecies();
568  lkstart_[n] = loc;
569  for (size_t k = 0; k < nsp; k++) {
570  m_x[loc] =m_lattice[n]->moleFraction(k) / (double) m_nlattice;
571  loc++;
572  }
573  lkstart_[n+1] = loc;
574  }
577 }
578 //====================================================================================================================
579 // Initialize vectors that depend on the number of species and sublattices
580 /*
581  *
582  */
584 {
585  theta_.resize(m_nlattice,0);
586  lkstart_.resize(m_nlattice+1);
587  m_x.resize(m_kk, 0.0);
588  tmpV_.resize(m_kk, 0.0);
589 }
590 //====================================================================================================================
592 {
593  doublereal tnow = temperature();
594  // if (fabs(molarDensity() - m_molar_density)/m_molar_density > 0.0001) {
595  // throw CanteraError("_updateThermo","molar density changed from "
596  // +fp2str(m_molar_density)+" to "+fp2str(molarDensity()));
597  //}
598  if (m_tlast != tnow) {
600  size_t strt = 0;
601  for (size_t n = 0; n < m_nlattice; n++) {
602  m_lattice[n]->setTemperature(tnow);
603  m_lattice[n]->setMoleFractions(DATA_PTR(m_x) + strt);
604  m_lattice[n]->setPressure(m_press);
605  strt += m_lattice[n]->nSpecies();
606  }
607  m_tlast = tnow;
608  }
609 }
610 //====================================================================================================================
612 {
613  m_lattice[nn]->setMoleFractionsByName(x);
614  size_t loc = 0;
615  doublereal ndens;
616  for (size_t n = 0; n < m_nlattice; n++) {
617  size_t nsp = m_lattice[n]->nSpecies();
618  ndens = m_lattice[n]->molarDensity();
619  for (size_t k = 0; k < nsp; k++) {
620  m_x[loc] = ndens * m_lattice[n]->moleFraction(k);
621  loc++;
622  }
623  }
625 }
626 //====================================================================================================================
627 
628 
629 //====================================================================================================================
630 // Set the parameters from the XML file
631 /*!
632  * Currently, this is the spot that we read in all of the sublattice phases.
633  * The SetParametersFromXML() call is carried out at
634  */
636 {
637  eosdata._require("model","LatticeSolid");
638  XML_Node& la = eosdata.child("LatticeArray");
639  std::vector<XML_Node*> lattices;
640  la.getChildren("phase",lattices);
641  size_t nl = lattices.size();
642  m_nlattice = nl;
643  for (size_t n = 0; n < nl; n++) {
644  XML_Node& i = *lattices[n];
645  m_lattice.push_back((LatticePhase*)newPhase(i));
646  }
647  std::vector<string> pnam;
648  std::vector<string> pval;
649  XML_Node& ls = eosdata.child("LatticeStoichiometry");
650  int np = ctml::getPairs(ls, pnam, pval);
651  theta_.resize(nl);
652  for (int i = 0; i < np; i++) {
653  double val = fpValueCheck(pval[i]);
654  bool found = false;
655  for (size_t j = 0; j < nl; j++) {
656  ThermoPhase& tp = *(m_lattice[j]);
657  string idj = tp.id();
658  if (idj == pnam[i]) {
659  theta_[j] = val;
660  found = true;
661  break;
662  }
663  }
664  if (!found) {
665  throw CanteraError("", "not found");
666  }
667  }
668 
669 }
670 //====================================================================================================================
671 // Return a changeable reference to the calculation manager
672 // for species reference-state thermodynamic properties
673 /*
674  *
675  * @param k Speices id. The default is -1, meaning return the default
676  *
677  * @internal
678  */
680 {
681  return *m_spthermo;
682  /*
683  int kk;
684  if (k >= 0) {
685  for (int n = 0; n < m_nlattice; n++) {
686  if (lkstart_[n+1] < k) {
687  kk = k - lkstart_[n];
688  return m_lattice[n]->speciesThermo(kk);
689  }
690  }
691  }
692  return m_lattice[0]->speciesThermo(-1);
693  */
694 }
695 //====================================================================================================================
696 
697 #ifdef H298MODIFY_CAPABILITY
698 
699 //! Modify the value of the 298 K Heat of Formation of one species in the phase (J kmol-1)
700 /*!
701  * The 298K heat of formation is defined as the enthalpy change to create the standard state
702  * of the species from its constituent elements in their standard states at 298 K and 1 bar.
703  *
704  * @param k Species k
705  * @param Hf298New Specify the new value of the Heat of Formation at 298K and 1 bar
706  */
707 void LatticeSolidPhase::modifyOneHf298SS(const int k, const doublereal Hf298New)
708 {
709  for (int n = 0; n < m_nlattice; n++) {
710  if (lkstart_[n+1] < k) {
711  int kk = k-lkstart_[n];
712  SpeciesThermo& l_spthermo = m_lattice[n]->speciesThermo();
713  l_spthermo.modifyOneHf298(kk, Hf298New);
714  }
715  }
716  m_tlast += 0.0001234;
717  _updateThermo();
718 }
719 #endif
720 //====================================================================================================================
721 
722 doublereal LatticeSolidPhase::err(std::string msg) const
723 {
724  throw CanteraError("LatticeSolidPhase","Unimplemented " + msg);
725  return 0.0;
726 }
727 
728 } // End namespace Cantera
729 //======================================================================================================================