Cantera  2.0
SurfPhase.cpp
Go to the documentation of this file.
1 /**
2  * @file SurfPhase.cpp
3  * Definitions for a simple thermodynamic model of a surface phase
4  * derived from ThermoPhase, assuming an ideal solution model
5  * (see \ref thermoprops and class
6  * \link Cantera::SurfPhase SurfPhase\endlink).
7  */
8 // Copyright 2002 California Institute of Technology
9 
14 
15 using namespace ctml;
16 using namespace std;
17 
18 ///////////////////////////////////////////////////////////
19 //
20 // class SurfPhase methods
21 //
22 ///////////////////////////////////////////////////////////
23 
24 namespace Cantera
25 {
26 
27 SurfPhase::SurfPhase(doublereal n0):
28  ThermoPhase(),
29  m_n0(n0),
30  m_logn0(0.0),
31  m_tmin(0.0),
32  m_tmax(0.0),
33  m_press(OneAtm),
34  m_tlast(0.0)
35 {
36  if (n0 > 0.0) {
37  m_logn0 = log(n0);
38  }
39  setNDim(2);
40 }
41 
42 SurfPhase::SurfPhase(std::string infile, std::string id) :
43  ThermoPhase(),
44  m_n0(0.0),
45  m_logn0(0.0),
46  m_tmin(0.0),
47  m_tmax(0.0),
48  m_press(OneAtm),
49  m_tlast(0.0)
50 {
51  XML_Node* root = get_XML_File(infile);
52  if (id == "-") {
53  id = "";
54  }
55  XML_Node* xphase = get_XML_NameID("phase", std::string("#")+id, root);
56  if (!xphase) {
57  throw CanteraError("SurfPhase::SurfPhase",
58  "Couldn't find phase name in file:" + id);
59  }
60  // Check the model name to ensure we have compatibility
61  const XML_Node& th = xphase->child("thermo");
62  string model = th["model"];
63  if (model != "Surface" && model != "Edge") {
64  throw CanteraError("SurfPhase::SurfPhase",
65  "thermo model attribute must be Surface or Edge");
66  }
67  importPhase(*xphase, this);
68 }
69 
70 
72  ThermoPhase(),
73  m_n0(0.0),
74  m_logn0(0.0),
75  m_tmin(0.0),
76  m_tmax(0.0),
77  m_press(OneAtm),
78  m_tlast(0.0)
79 {
80  const XML_Node& th = xmlphase.child("thermo");
81  string model = th["model"];
82  if (model != "Surface" && model != "Edge") {
83  throw CanteraError("SurfPhase::SurfPhase",
84  "thermo model attribute must be Surface or Edge");
85  }
86  importPhase(xmlphase, this);
87 }
88 
89 // Copy Constructor
90 /*
91  * Copy constructor for the object. Constructed
92  * object will be a clone of this object, but will
93  * also own all of its data.
94  * This is a wrapper around the assignment operator
95  *
96  * @param right Object to be copied.
97  */
99  m_n0(right.m_n0),
100  m_logn0(right.m_logn0),
101  m_tmin(right.m_tmin),
102  m_tmax(right.m_tmax),
103  m_press(right.m_press),
104  m_tlast(right.m_tlast)
105 {
106  *this = operator=(right);
107 }
108 
109 // Assignment operator
110 /*
111  * Assignment operator for the object. Constructed
112  * object will be a clone of this object, but will
113  * also own all of its data.
114  *
115  * @param right Object to be copied.
116  */
118 operator=(const SurfPhase& right)
119 {
120  if (&right != this) {
121  ThermoPhase::operator=(right);
122  m_n0 = right.m_n0;
123  m_logn0 = right.m_logn0;
124  m_tmin = right.m_tmin;
125  m_tmax = right.m_tmax;
126  m_press = right.m_press;
127  m_tlast = right.m_tlast;
128  m_h0 = right.m_h0;
129  m_s0 = right.m_s0;
130  m_cp0 = right.m_cp0;
131  m_mu0 = right.m_mu0;
132  m_work = right.m_work;
133  m_pe = right.m_pe;
134  m_logsize = right.m_logsize;
135  }
136  return *this;
137 }
138 
139 // Duplicator from the %ThermoPhase parent class
140 /*
141  * Given a pointer to a %ThermoPhase object, this function will
142  * duplicate the %ThermoPhase object and all underlying structures.
143  * This is basically a wrapper around the copy constructor.
144  *
145  * @return returns a pointer to a %ThermoPhase
146  */
148 {
149  SurfPhase* igp = new SurfPhase(*this);
150  return (ThermoPhase*) igp;
151 }
152 
153 doublereal SurfPhase::
155 {
156  if (m_n0 <= 0.0) {
157  return 0.0;
158  }
159  _updateThermo();
160  return mean_X(DATA_PTR(m_h0));
161 }
162 
164 {
165 }
166 
167 /*
168  * For a surface phase, the pressure is not a relevant
169  * thermodynamic variable, and so the Enthalpy is equal to the
170  * internal energy.
171  */
172 doublereal SurfPhase::
174 {
175  return enthalpy_mole();
176 }
177 
178 /*
179  * Get the array of partial molar enthalpies of the species
180  * units = J / kmol
181  */
182 void SurfPhase::getPartialMolarEnthalpies(doublereal* hbar) const
183 {
184  getEnthalpy_RT(hbar);
185  doublereal rt = GasConstant * temperature();
186  for (size_t k = 0; k < m_kk; k++) {
187  hbar[k] *= rt;
188  }
189 }
190 
191 // Returns an array of partial molar entropies of the species in the
192 // solution. Units: J/kmol/K.
193 /*
194  * @param sbar Output vector of species partial molar entropies.
195  * Length = m_kk. units are J/kmol/K.
196  */
197 void SurfPhase::getPartialMolarEntropies(doublereal* sbar) const
198 {
199  getEntropy_R(sbar);
200  for (size_t k = 0; k < m_kk; k++) {
201  sbar[k] *= GasConstant;
202  }
203 }
204 
205 // Returns an array of partial molar heat capacities of the species in the
206 // solution. Units: J/kmol/K.
207 /*
208  * @param sbar Output vector of species partial molar entropies.
209  * Length = m_kk. units are J/kmol/K.
210  */
211 void SurfPhase::getPartialMolarCp(doublereal* cpbar) const
212 {
213  getCp_R(cpbar);
214  for (size_t k = 0; k < m_kk; k++) {
215  cpbar[k] *= GasConstant;
216  }
217 }
218 
219 // HKM 9/1/11 The partial molar volumes returned here are really partial molar areas.
220 // Partial molar volumes for this phase should actually be equal to zero.
221 void SurfPhase::getPartialMolarVolumes(doublereal* vbar) const
222 {
223  getStandardVolumes(vbar);
224 }
225 
226 void SurfPhase::getStandardChemPotentials(doublereal* mu0) const
227 {
228  _updateThermo();
229  copy(m_mu0.begin(), m_mu0.end(), mu0);
230 }
231 
232 void SurfPhase::getChemPotentials(doublereal* mu) const
233 {
234  _updateThermo();
235  copy(m_mu0.begin(), m_mu0.end(), mu);
237  for (size_t k = 0; k < m_kk; k++) {
238  mu[k] += GasConstant * temperature() *
239  (log(m_work[k]) - logStandardConc(k));
240  }
241 }
242 
243 void SurfPhase::getActivityConcentrations(doublereal* c) const
244 {
246 }
247 
248 doublereal SurfPhase::standardConcentration(size_t k) const
249 {
250  return m_n0/size(k);
251 }
252 
253 doublereal SurfPhase::logStandardConc(size_t k) const
254 {
255  return m_logn0 - m_logsize[k];
256 }
257 
258 /// The only parameter that can be set is the site density.
259 void SurfPhase::setParameters(int n, doublereal* const c)
260 {
261  if (n != 1) {
262  throw CanteraError("SurfPhase::setParameters",
263  "Bad value for number of parameter");
264  }
265  m_n0 = c[0];
266  if (m_n0 <= 0.0) {
267  throw CanteraError("SurfPhase::setParameters",
268  "Bad value for parameter");
269  }
270  m_logn0 = log(m_n0);
271 }
272 
273 void SurfPhase::getGibbs_RT(doublereal* grt) const
274 {
275  _updateThermo();
276  double rrt = 1.0/(GasConstant*temperature());
277  scale(m_mu0.begin(), m_mu0.end(), grt, rrt);
278 }
279 
280 void SurfPhase::
281 getEnthalpy_RT(doublereal* hrt) const
282 {
283  _updateThermo();
284  double rrt = 1.0/(GasConstant*temperature());
285  scale(m_h0.begin(), m_h0.end(), hrt, rrt);
286 }
287 
288 void SurfPhase::getEntropy_R(doublereal* sr) const
289 {
290  _updateThermo();
291  double rr = 1.0/GasConstant;
292  scale(m_s0.begin(), m_s0.end(), sr, rr);
293 }
294 
295 void SurfPhase::getCp_R(doublereal* cpr) const
296 {
297  _updateThermo();
298  double rr = 1.0/GasConstant;
299  scale(m_cp0.begin(), m_cp0.end(), cpr, rr);
300 }
301 
302 void SurfPhase::getStandardVolumes(doublereal* vol) const
303 {
304  _updateThermo();
305  for (size_t k = 0; k < m_kk; k++) {
306  vol[k] = 1.0/standardConcentration(k);
307  }
308 }
309 
310 void SurfPhase::getGibbs_RT_ref(doublereal* grt) const
311 {
312  getGibbs_RT(grt);
313 }
314 
315 void SurfPhase::getEnthalpy_RT_ref(doublereal* hrt) const
316 {
317  getEnthalpy_RT(hrt);
318 }
319 
320 void SurfPhase::getEntropy_R_ref(doublereal* sr) const
321 {
322  getEntropy_R(sr);
323 }
324 
325 void SurfPhase::getCp_R_ref(doublereal* cprt) const
326 {
327  getCp_R(cprt);
328 }
329 
331 {
332  if (m_kk == 0) {
333  throw CanteraError("SurfPhase::initThermo",
334  "Number of species is equal to zero");
335  }
336  m_h0.resize(m_kk);
337  m_s0.resize(m_kk);
338  m_cp0.resize(m_kk);
339  m_mu0.resize(m_kk);
340  m_work.resize(m_kk);
341  m_pe.resize(m_kk, 0.0);
342  vector_fp cov(m_kk, 0.0);
343  cov[0] = 1.0;
344  setCoverages(DATA_PTR(cov));
345  m_logsize.resize(m_kk);
346  for (size_t k = 0; k < m_kk; k++) {
347  m_logsize[k] = log(size(k));
348  }
349 }
350 
351 void SurfPhase::setPotentialEnergy(int k, doublereal pe)
352 {
353  m_pe[k] = pe;
354  _updateThermo(true);
355 }
356 
357 void SurfPhase::setSiteDensity(doublereal n0)
358 {
359  doublereal x = n0;
360  setParameters(1, &x);
361 }
362 
363 //void SurfPhase::
364 //setElectricPotential(doublereal V) {
365 // for (int k = 0; k < m_kk; k++) {
366 // m_pe[k] = charge(k)*Faraday*V;
367 // }
368 // _updateThermo(true);
369 //}
370 
371 
372 /**
373  * Set the coverage fractions to a specified
374  * state. This routine converts to concentrations
375  * in kmol/m2, using m_n0, the surface site density,
376  * and size(k), which is defined to be the number of
377  * surface sites occupied by the kth molecule.
378  * It then calls Phase::setConcentrations to set the
379  * internal concentration in the object.
380  */
381 void SurfPhase::
382 setCoverages(const doublereal* theta)
383 {
384  double sum = 0.0;
385  for (size_t k = 0; k < m_kk; k++) {
386  sum += theta[k];
387  }
388  if (sum <= 0.0) {
389  for (size_t k = 0; k < m_kk; k++) {
390  cout << "theta(" << k << ") = " << theta[k] << endl;
391  }
392  throw CanteraError("SurfPhase::setCoverages",
393  "Sum of Coverage fractions is zero or negative");
394  }
395  for (size_t k = 0; k < m_kk; k++) {
396  m_work[k] = m_n0*theta[k]/(sum*size(k));
397  }
398  /*
399  * Call the Phase:: class function
400  * setConcentrations.
401  */
403 }
404 
405 void SurfPhase::
406 setCoveragesNoNorm(const doublereal* theta)
407 {
408  for (size_t k = 0; k < m_kk; k++) {
409  m_work[k] = m_n0*theta[k]/(size(k));
410  }
411  /*
412  * Call the Phase:: class function
413  * setConcentrations.
414  */
416 }
417 
418 void SurfPhase::
419 getCoverages(doublereal* theta) const
420 {
421  getConcentrations(theta);
422  for (size_t k = 0; k < m_kk; k++) {
423  theta[k] *= size(k)/m_n0;
424  }
425 }
426 
427 void SurfPhase::
428 setCoveragesByName(std::string cov)
429 {
430  size_t kk = nSpecies();
431  compositionMap cc;
432  for (size_t k = 0; k < kk; k++) {
433  cc[speciesName(k)] = -1.0;
434  }
435  parseCompString(cov, cc);
436  doublereal c;
437  vector_fp cv(kk, 0.0);
438  bool ifound = false;
439  for (size_t k = 0; k < kk; k++) {
440  c = cc[speciesName(k)];
441  if (c > 0.0) {
442  ifound = true;
443  cv[k] = c;
444  }
445  }
446  if (!ifound) {
447  throw CanteraError("SurfPhase::setCoveragesByName",
448  "Input coverages are all zero or negative");
449  }
450  setCoverages(DATA_PTR(cv));
451 }
452 
453 
454 void SurfPhase::
455 _updateThermo(bool force) const
456 {
457  doublereal tnow = temperature();
458  if (m_tlast != tnow || force) {
460  DATA_PTR(m_s0));
461  m_tlast = tnow;
462  doublereal rt = GasConstant * tnow;
463  for (size_t k = 0; k < m_kk; k++) {
464  m_h0[k] *= rt;
465  m_s0[k] *= GasConstant;
466  m_cp0[k] *= GasConstant;
467  m_mu0[k] = m_h0[k] - tnow*m_s0[k];
468  }
469  m_tlast = tnow;
470  }
471 }
472 
473 void SurfPhase::
475 {
476  eosdata._require("model","Surface");
477  doublereal n = getFloat(eosdata, "site_density", "toSI");
478  if (n <= 0.0)
479  throw CanteraError("SurfPhase::setParametersFromXML",
480  "missing or negative site density");
481  m_n0 = n;
482  m_logn0 = log(m_n0);
483 }
484 
485 
487 {
488 
489  double t;
490  if (getOptionalFloat(state, "temperature", t, "temperature")) {
491  setTemperature(t);
492  }
493 
494  if (state.hasChild("coverages")) {
495  string comp = getChildValue(state,"coverages");
496  setCoveragesByName(comp);
497  }
498 }
499 
500 // Default constructor
501 EdgePhase::EdgePhase(doublereal n0) : SurfPhase(n0)
502 {
503  setNDim(1);
504 }
505 
506 // Copy Constructor
507 /*
508  * @param right Object to be copied
509  */
511  SurfPhase(right.m_n0)
512 {
513  setNDim(1);
514  *this = operator=(right);
515 }
516 
517 // Assignment Operator
518 /*
519  * @param right Object to be copied
520  */
522 {
523  if (&right != this) {
524  SurfPhase::operator=(right);
525  setNDim(1);
526  }
527  return *this;
528 }
529 
530 // Duplicator from the %ThermoPhase parent class
531 /*
532  * Given a pointer to a %ThermoPhase object, this function will
533  * duplicate the %ThermoPhase object and all underlying structures.
534  * This is basically a wrapper around the copy constructor.
535  *
536  * @return returns a pointer to a %ThermoPhase
537  */
539 {
540  EdgePhase* igp = new EdgePhase(*this);
541  return (ThermoPhase*) igp;
542 }
543 
544 void EdgePhase::
546 {
547  eosdata._require("model","Edge");
548  doublereal n = getFloat(eosdata, "site_density", "toSI");
549  if (n <= 0.0)
550  throw CanteraError("EdgePhase::setParametersFromXML",
551  "missing or negative site density");
552  m_n0 = n;
553  m_logn0 = log(m_n0);
554 }
555 
556 
557 }