Cantera  2.1.2
MultiPhaseEquil.cpp
Go to the documentation of this file.
1 /**
2  * @file MultiPhaseEquil.cpp
3  */
7 #include "cantera/base/global.h"
9 
10 #include <cstdio>
11 
12 using namespace std;
13 
14 namespace Cantera
15 {
16 
17 #if defined(WITH_HTML_LOGS)
18 
19 //! Used to print reaction equations. Given a stoichiometric coefficient 'nu'
20 //! and a chemical symbol 'sym', return a string for this species in the
21 //! reaction.
22 //! @param first if this is false, then a " + " string will be added to the
23 //! beginning of the string.
24 //! @param nu Stoichiometric coefficient. May be positive or negative. The
25 //! absolute value will be used in the string.
26 //! @param sym Species chemical symbol.
27 static string coeffString(bool first, doublereal nu, string sym)
28 {
29  if (nu == 0.0) {
30  return "";
31  }
32  string strt = " + ";
33  if (first) {
34  strt = "";
35  }
36  if (nu == 1.0 || nu == -1.0) {
37  return strt + sym;
38  }
39  string s = fp2str(fabs(nu));
40  return strt + s + " " + sym;
41 }
42 #endif
43 
44 MultiPhaseEquil::MultiPhaseEquil(MultiPhase* mix, bool start, int loglevel) : m_mix(mix)
45 {
46  // the multi-phase mixture
47  // m_mix = mix;
48 
49  // store some mixture parameters locally
50  m_nel_mix = mix->nElements();
51  m_nsp_mix = mix->nSpecies();
52  m_np = mix->nPhases();
53  m_press = mix->pressure();
54  m_temp = mix->temperature();
55 
56  size_t m, k;
57  m_force = true;
58  m_nel = 0;
59  m_nsp = 0;
60  m_eloc = 1000;
61  m_incl_species.resize(m_nsp_mix,1);
62  m_incl_element.resize(m_nel_mix,1);
63  for (m = 0; m < m_nel_mix; m++) {
64  string enm = mix->elementName(m);
65  // element 'E' or 'e' represents an electron; this
66  // requires special handling, so save its index
67  // for later use
68  if (enm == "E" || enm == "e") {
69  m_eloc = m;
70  }
71  // if an element other than electrons is not present in
72  // the mixture, then exclude it and all species containing
73  // it from the calculation. Electrons are a special case,
74  // since a species can have a negative number of 'atoms'
75  // of electrons (positive ions).
76  if (m_mix->elementMoles(m) <= 0.0) {
77  if (m != m_eloc) {
78  m_incl_element[m] = 0;
79  for (k = 0; k < m_nsp_mix; k++) {
80  if (m_mix->nAtoms(k,m) != 0.0) {
81  m_incl_species[k] = 0;
82  }
83  }
84  }
85  }
86  }
87 
88  // Now build the list of elements to be included, starting with
89  // electrons, if they are present.
90  if (m_eloc < m_nel_mix) {
91  m_element.push_back(m_eloc);
92  m_nel++;
93  }
94  // add the included elements other than electrons
95  for (m = 0; m < m_nel_mix; m++) {
96  if (m_incl_element[m] == 1 && m != m_eloc) {
97  m_nel++;
98  m_element.push_back(m);
99  }
100  }
101 
102  // include pure single-constituent phases only if their thermo
103  // data are valid for this temperature. This is necessary,
104  // since some thermo polynomial fits are done only for a
105  // limited temperature range. For example, using the NASA
106  // polynomial fits for solid ice and liquid water, if this
107  // were not done the calculation would predict solid ice to be
108  // present far above its melting point, since the thermo
109  // polynomial fits only extend to 273.15 K, and give
110  // unphysical results above this temperature, leading
111  // (incorrectly) to Gibbs free energies at high temperature
112  // lower than for liquid water.
113  size_t ip;
114  for (k = 0; k < m_nsp_mix; k++) {
115  ip = m_mix->speciesPhaseIndex(k);
116  if (!m_mix->solutionSpecies(k) &&
117  !m_mix->tempOK(ip)) {
118  m_incl_species[k] = 0;
119  if (m_mix->speciesMoles(k) > 0.0) {
120  throw CanteraError("MultiPhaseEquil",
121  "condensed-phase species"+ m_mix->speciesName(k)
122  + " is excluded since its thermo properties are \n"
123  "not valid at this temperature, but it has "
124  "non-zero moles in the initial state.");
125  }
126  }
127  }
128 
129  // Now build the list of all species to be included in the
130  // calculation.
131  for (k = 0; k < m_nsp_mix; k++) {
132  if (m_incl_species[k] ==1) {
133  m_nsp++;
134  m_species.push_back(k);
135  }
136  }
137 
138  // some work arrays for internal use
139  m_work.resize(m_nsp);
140  m_work2.resize(m_nsp);
141  m_work3.resize(m_nsp_mix);
142  m_mu.resize(m_nsp_mix);
143 
144  // number of moles of each species
145  m_moles.resize(m_nsp);
146  m_lastmoles.resize(m_nsp);
147  m_dxi.resize(nFree());
148 
149  // initialize the mole numbers to the mixture composition
150  size_t ik;
151  for (ik = 0; ik < m_nsp; ik++) {
152  m_moles[ik] = m_mix->speciesMoles(m_species[ik]);
153  }
154 
155  // Delta G / RT for each reaction
156  m_deltaG_RT.resize(nFree(), 0.0);
157 
158  m_majorsp.resize(m_nsp);
159  m_sortindex.resize(m_nsp,0);
160  m_lastsort.resize(m_nel);
161  m_solnrxn.resize(nFree());
162  m_A.resize(m_nel, m_nsp, 0.0);
163  m_N.resize(m_nsp, nFree());
164  m_order.resize(std::max(m_nsp, m_nel), 0);
165  for (k = 0; k < m_nsp; k++) {
166  m_order[k] = k;
167  }
168 
169  // if the 'start' flag is set, estimate the initial mole
170  // numbers by doing a linear Gibbs minimization. In this case,
171  // only the elemental composition of the initial mixture state
172  // matters.
173  if (start) {
174  setInitialMoles(loglevel-1);
175  }
176  computeN();
177 
178  // Take a very small step in composition space, so that no
179  // species has precisely zero moles.
180  vector_fp dxi(nFree(), 1.0e-20);
181  if (!dxi.empty()) {
182  multiply(m_N, DATA_PTR(dxi), DATA_PTR(m_work));
183  unsort(m_work);
184  }
185 
186  for (k = 0; k < m_nsp; k++) {
187  m_moles[k] += m_work[k];
188  m_lastmoles[k] = m_moles[k];
189  if (m_mix->solutionSpecies(m_species[k])) {
190  m_dsoln.push_back(1);
191  } else {
192  m_dsoln.push_back(0);
193  }
194  }
195  m_force = false;
196  updateMixMoles();
197 
198  // At this point, the instance has been created, the species
199  // to be included have been determined, and an initial
200  // composition has been selected that has all non-zero mole
201  // numbers for the included species.
202 }
203 
204 doublereal MultiPhaseEquil::equilibrate(int XY, doublereal err,
205  int maxsteps, int loglevel)
206 {
207  int i;
208  m_iter = 0;
209  string iterstr;
210  if (loglevel > 0) {
211  beginLogGroup("MultiPhaseEquil::equilibrate", loglevel);
212  }
213 
214  for (i = 0; i < maxsteps; i++) {
215  if (loglevel > 0) {
216  iterstr = "iteration "+int2str(i);
217  beginLogGroup(iterstr);
218  }
219  stepComposition(loglevel-1);
220  if (loglevel > 0) {
221  addLogEntry("error",fp2str(error()));
222  endLogGroup(iterstr);
223  }
224  if (error() < err) {
225  break;
226  }
227  }
228  if (i >= maxsteps) {
229  if (loglevel > 0) {
230  addLogEntry("Error","no convergence in "+int2str(maxsteps)
231  +" iterations");
232  endLogGroup("MultiPhaseEquil::equilibrate");
233  }
234  throw CanteraError("MultiPhaseEquil::equilibrate",
235  "no convergence in " + int2str(maxsteps) +
236  " iterations. Error = " + fp2str(error()));
237  }
238  if (loglevel > 0) {
239  addLogEntry("iterations",int2str(iterations()));
240  addLogEntry("error tolerance",fp2str(err));
241  addLogEntry("error",fp2str(error()));
242  endLogGroup("MultiPhaseEquil::equilibrate");
243  }
244  finish();
245  return error();
246 }
247 
248 void MultiPhaseEquil::updateMixMoles()
249 {
250  fill(m_work3.begin(), m_work3.end(), 0.0);
251  size_t k;
252  for (k = 0; k < m_nsp; k++) {
253  m_work3[m_species[k]] = m_moles[k];
254  }
255  m_mix->setMoles(DATA_PTR(m_work3));
256 }
257 
259 {
260  fill(m_work3.begin(), m_work3.end(), 0.0);
261  size_t k;
262  for (k = 0; k < m_nsp; k++) {
263  m_work3[m_species[k]] = (m_moles[k] > 0.0 ? m_moles[k] : 0.0);
264  }
265  m_mix->setMoles(DATA_PTR(m_work3));
266 }
267 
269 {
270  size_t ik, j;
271 
272  double not_mu = 1.0e12;
273  if (loglevel > 0) {
274  beginLogGroup("MultiPhaseEquil::setInitialMoles");
275  }
276 
277  m_mix->getValidChemPotentials(not_mu, DATA_PTR(m_mu), true);
278  doublereal dg_rt;
279 
280  int idir;
281  double nu;
282  double delta_xi, dxi_min = 1.0e10;
283  bool redo = true;
284  int iter = 0;
285 
286  while (redo) {
287 
288  // choose a set of components based on the current
289  // composition
290  computeN();
291  if (loglevel > 0) {
292  addLogEntry("iteration",iter);
293  }
294  redo = false;
295  iter++;
296  if (iter > 4) {
297  break;
298  }
299 
300  // loop over all reactions
301  for (j = 0; j < nFree(); j++) {
302  dg_rt = 0.0;
303  dxi_min = 1.0e10;
304  for (ik = 0; ik < m_nsp; ik++) {
305  dg_rt += mu(ik) * m_N(ik,j);
306  }
307 
308  // fwd or rev direction
309  idir = (dg_rt < 0.0 ? 1 : -1);
310 
311  for (ik = 0; ik < m_nsp; ik++) {
312  nu = m_N(ik, j);
313 
314  // set max change in progress variable by
315  // non-negativity requirement
316  // -> Note, 0.99 factor is so that difference of 2 numbers
317  // isn't zero. This causes differences between
318  // optimized and debug versions of the code
319  if (nu*idir < 0) {
320  delta_xi = fabs(0.99*moles(ik)/nu);
321  // if a component has nearly zero moles, redo
322  // with a new set of components
323  if (!redo && delta_xi < 1.0e-10 && ik < m_nel) {
324  if (loglevel > 0) {
325  addLogEntry("component too small",speciesName(ik));
326  }
327  redo = true;
328  }
329  if (delta_xi < dxi_min) {
330  dxi_min = delta_xi;
331  }
332  }
333  }
334  // step the composition by dxi_min
335  for (ik = 0; ik < m_nsp; ik++) {
336  moles(ik) += m_N(ik, j) * idir*dxi_min;
337  }
338  }
339  // set the moles of the phase objects to match
340  updateMixMoles();
341  }
342  for (ik = 0; ik < m_nsp; ik++)
343  if (moles(ik) != 0.0) {
344  addLogEntry(speciesName(ik), moles(ik));
345  }
346  if (loglevel > 0) {
347  endLogGroup("MultiPhaseEquil::setInitialMoles");
348  }
349  return 0;
350 }
351 
352 void MultiPhaseEquil::getComponents(const std::vector<size_t>& order)
353 {
354  size_t m, k, j;
355 
356  // if the input species array has the wrong size, ignore it
357  // and consider the species for components in declaration order.
358  if (order.size() != m_nsp) {
359  for (k = 0; k < m_nsp; k++) {
360  m_order[k] = k;
361  }
362  } else {
363  for (k = 0; k < m_nsp; k++) {
364  m_order[k] = order[k];
365  }
366  }
367 
368  size_t nRows = m_nel;
369  size_t nColumns = m_nsp;
370  doublereal fctr;
371 
372  // set up the atomic composition matrix
373  for (m = 0; m < nRows; m++) {
374  for (k = 0; k < nColumns; k++) {
375  m_A(m, k) = m_mix->nAtoms(m_species[m_order[k]], m_element[m]);
376  }
377  }
378 
379  // Do Gaussian elimination
380  for (m = 0; m < nRows; m++) {
381  // Check for rows that are zero
382  bool isZeroRow = true;
383  for (k = m; k < nColumns; k++) {
384  if (fabs(m_A(m,k)) > sqrt(Tiny)) {
385  isZeroRow = false;
386  break;
387  }
388  }
389  if (isZeroRow) {
390  // Find the last non-zero row
391  size_t n = nRows - 1;
392  bool foundSwapCandidate = false;
393  for (; n > m; n--) {
394  for (k = m; k < nColumns; k++) {
395  if (fabs(m_A(n,k)) > sqrt(Tiny)) {
396  foundSwapCandidate = true;
397  break;
398  }
399  }
400  if (foundSwapCandidate) {
401  break;
402  }
403  }
404  if (m != n) {
405  // Swap this row with the last non-zero row
406  for (k = 0; k < nColumns; k++) {
407  std::swap(m_A(n,k), m_A(m,k));
408  }
409  } else {
410  // All remaining rows are zero. Elimination is complete.
411  break;
412  }
413  }
414 
415  // If a pivot is zero, exchange columns. This occurs when
416  // a species has an elemental composition that is not
417  // linearly independent of the component species that have
418  // already been assigned
419  if (m < nColumns && m_A(m,m) == 0.0) {
420  // First, we need to find a good candidate for a
421  // component species to swap in for the one that has
422  // zero pivot. It must contain element m, be linearly
423  // independent of the components processed so far
424  // (m_A(m,k) != 0), and should be a major species if
425  // possible. We'll choose the species with greatest
426  // mole fraction that satisfies these criteria.
427  doublereal maxmoles = -999.0;
428  size_t kmax = 0;
429  for (k = m+1; k < nColumns; k++) {
430  if (m_A(m,k) != 0.0) {
431  if (fabs(m_moles[m_order[k]]) > maxmoles) {
432  kmax = k;
433  maxmoles = fabs(m_moles[m_order[k]]);
434  }
435  }
436  }
437 
438  // Now exchange the column with zero pivot with the
439  // column for this major species
440  for (size_t n = 0; n < nRows; n++) {
441  std::swap(m_A(n, m), m_A(n, kmax));
442  }
443 
444  // exchange the species labels on the columns
445  std::swap(m_order[m], m_order[kmax]);
446  }
447 
448  // scale row m so that the diagonal element is unity
449  fctr = 1.0/m_A(m,m);
450  for (k = 0; k < nColumns; k++) {
451  m_A(m,k) *= fctr;
452  }
453 
454  // For all rows below the diagonal, subtract A(n,m)/A(m,m)
455  // * (row m) from row n, so that A(n,m) = 0.
456  for (size_t n = m+1; n < m_nel; n++) {
457  fctr = m_A(n,m)/m_A(m,m);
458  for (k = 0; k < m_nsp; k++) {
459  m_A(n,k) -= m_A(m,k)*fctr;
460  }
461  }
462  }
463 
464  // The left m_nel columns of A are now upper-diagonal. Now
465  // reduce the m_nel columns to diagonal form by back-solving
466  for (m = std::min(nRows,nColumns)-1; m > 0; m--) {
467  for (size_t n = m-1; n != npos; n--) {
468  if (m_A(n,m) != 0.0) {
469  fctr = m_A(n,m);
470  for (k = m; k < m_nsp; k++) {
471  m_A(n,k) -= fctr*m_A(m,k);
472  }
473  }
474  }
475  }
476 
477  // create stoichiometric coefficient matrix.
478  for (size_t n = 0; n < m_nsp; n++) {
479  if (n < m_nel)
480  for (k = 0; k < nFree(); k++) {
481  m_N(n, k) = -m_A(n, k + m_nel);
482  }
483  else {
484  for (k = 0; k < nFree(); k++) {
485  m_N(n, k) = 0.0;
486  }
487  m_N(n, n - m_nel) = 1.0;
488  }
489  }
490 
491  // find reactions involving solution phase species
492  for (j = 0; j < nFree(); j++) {
493  m_solnrxn[j] = false;
494  for (k = 0; k < m_nsp; k++) {
495  if (m_N(k, j) != 0)
496  if (m_mix->solutionSpecies(m_species[m_order[k]])) {
497  m_solnrxn[j] = true;
498  }
499  }
500  }
501 }
502 
504 {
505  copy(x.begin(), x.end(), m_work2.begin());
506  size_t k;
507  for (k = 0; k < m_nsp; k++) {
508  x[m_order[k]] = m_work2[k];
509  }
510 }
511 
512 #if defined(WITH_HTML_LOGS)
513 void MultiPhaseEquil::printInfo(int loglevel)
514 {
515  size_t m, ik, k;
516  if (loglevel > 0) {
517  beginLogGroup("info");
518  beginLogGroup("components");
519  }
520  for (m = 0; m < m_nel; m++) {
521  ik = m_order[m];
522  k = m_species[ik];
523  if (loglevel > 0) {
524  addLogEntry(m_mix->speciesName(k), fp2str(m_moles[ik]));
525  }
526  }
527  if (loglevel > 0) {
528  endLogGroup("components");
529  beginLogGroup("non-components");
530  }
531  for (m = m_nel; m < m_nsp; m++) {
532  ik = m_order[m];
533  k = m_species[ik];
534  if (loglevel > 0) {
535  addLogEntry(m_mix->speciesName(k), fp2str(m_moles[ik]));
536  }
537  }
538  if (loglevel > 0) {
539  endLogGroup("non-components");
540  addLogEntry("Error",fp2str(error()));
541  beginLogGroup("Delta G / RT");
542  }
543  for (k = 0; k < nFree(); k++) {
544  if (loglevel > 0) {
545  addLogEntry(reactionString(k), fp2str(m_deltaG_RT[k]));
546  }
547  }
548  if (loglevel > 0) {
549  endLogGroup("Delta G / RT");
550  endLogGroup("info");
551  }
552 }
553 
555 {
556  string sr = "", sp = "";
557  size_t i, k;
558  bool rstrt = true;
559  bool pstrt = true;
560  doublereal nu;
561  for (i = 0; i < m_nsp; i++) {
562  nu = m_N(i, j);
563  k = m_species[m_order[i]];
564  if (nu < 0.0) {
565  sr += coeffString(rstrt, nu, m_mix->speciesName(k));
566  rstrt = false;
567  }
568  if (nu > 0.0) {
569  sp += coeffString(pstrt, nu, m_mix->speciesName(k));
570  pstrt = false;
571  }
572  }
573  return sr + " <=> " + sp;
574 }
575 #endif
576 
577 void MultiPhaseEquil::step(doublereal omega, vector_fp& deltaN,
578  int loglevel)
579 {
580  size_t k, ik;
581  if (loglevel > 0) {
582  beginLogGroup("MultiPhaseEquil::step");
583  }
584  if (omega < 0.0) {
585  throw CanteraError("step","negative omega");
586  }
587 
588  for (ik = 0; ik < m_nel; ik++) {
589  k = m_order[ik];
590  m_lastmoles[k] = m_moles[k];
591  if (loglevel > 0) {
592  addLogEntry("component "+m_mix->speciesName(m_species[k])+" moles",
593  m_moles[k]);
594  addLogEntry("component "+m_mix->speciesName(m_species[k])+" step",
595  omega*deltaN[k]);
596  }
597  m_moles[k] += omega * deltaN[k];
598  }
599 
600  for (ik = m_nel; ik < m_nsp; ik++) {
601  k = m_order[ik];
602  m_lastmoles[k] = m_moles[k];
603  if (m_majorsp[k]) {
604  m_moles[k] += omega * deltaN[k];
605  } else {
606  m_moles[k] = fabs(m_moles[k])*std::min(10.0,
607  exp(-m_deltaG_RT[ik - m_nel]));
608  }
609  }
610  updateMixMoles();
611  if (loglevel > 0) {
612  endLogGroup("MultiPhaseEquil::step");
613  }
614 }
615 
616 doublereal MultiPhaseEquil::
617 stepComposition(int loglevel)
618 {
619  if (loglevel > 0) {
620  beginLogGroup("MultiPhaseEquil::stepComposition");
621  }
622 
623  m_iter++;
624  size_t ik, k = 0;
625  doublereal grad0 = computeReactionSteps(m_dxi);
626 
627  // compute the mole fraction changes.
628  if (nFree()) {
629  multiply(m_N, DATA_PTR(m_dxi), DATA_PTR(m_work));
630  }
631 
632  // change to sequential form
633  unsort(m_work);
634 
635  // scale omega to keep the major species non-negative
636  doublereal FCTR = 0.99;
637  const doublereal MAJOR_THRESHOLD = 1.0e-12;
638 
639  doublereal omega = 1.0, omax, omegamax = 1.0;
640  for (ik = 0; ik < m_nsp; ik++) {
641  k = m_order[ik];
642  if (ik < m_nel) {
643  FCTR = 0.99;
644  if (m_moles[k] < MAJOR_THRESHOLD) {
645  m_force = true;
646  }
647  } else {
648  FCTR = 0.9;
649  }
650  // if species k is in a multi-species solution phase, then its
651  // mole number must remain positive, unless the entire phase
652  // goes away. First we'll determine an upper bound on omega,
653  // such that all
654  if (m_dsoln[k] == 1) {
655 
656  if ((m_moles[k] > MAJOR_THRESHOLD) || (ik < m_nel)) {
657  if (m_moles[k] < MAJOR_THRESHOLD) {
658  m_force = true;
659  }
660  omax = m_moles[k]*FCTR/(fabs(m_work[k]) + Tiny);
661  if (m_work[k] < 0.0 && omax < omegamax) {
662  omegamax = omax;
663  if (omegamax < 1.0e-5) {
664  m_force = true;
665  }
666  }
667  m_majorsp[k] = true;
668  } else {
669  m_majorsp[k] = false;
670  }
671  } else {
672  if (m_work[k] < 0.0 && m_moles[k] > 0.0) {
673  omax = -m_moles[k]/m_work[k];
674  if (omax < omegamax) {
675  omegamax = omax; //*1.000001;
676  if (omegamax < 1.0e-5) {
677  m_force = true;
678  }
679  }
680  }
681  if (m_moles[k] < -Tiny) {
682  if (loglevel > 0)
683  addLogEntry("Negative moles for "
684  +m_mix->speciesName(m_species[k]), fp2str(m_moles[k]));
685  }
686  m_majorsp[k] = true;
687  }
688  }
689 
690  // now take a step with this scaled omega
691  if (loglevel > 0) {
692  addLogEntry("Stepping by ", fp2str(omegamax));
693  }
694  step(omegamax, m_work);
695  // compute the gradient of G at this new position in the
696  // current direction. If it is positive, then we have overshot
697  // the minimum. In this case, interpolate back.
698  doublereal not_mu = 1.0e12;
699  m_mix->getValidChemPotentials(not_mu, DATA_PTR(m_mu));
700  doublereal grad1 = 0.0;
701  for (k = 0; k < m_nsp; k++) {
702  grad1 += m_work[k] * m_mu[m_species[k]];
703  }
704 
705  omega = omegamax;
706  if (grad1 > 0.0) {
707  omega *= fabs(grad0) / (grad1 + fabs(grad0));
708  for (k = 0; k < m_nsp; k++) {
709  m_moles[k] = m_lastmoles[k];
710  }
711  if (loglevel > 0) {
712  addLogEntry("Stepped over minimum. Take smaller step ", fp2str(omega));
713  }
714  step(omega, m_work);
715  }
716  printInfo(loglevel);
717  if (loglevel > 0) {
718  endLogGroup("MultiPhaseEquil::stepComposition");
719  }
720  return omega;
721 }
722 
724 {
725  size_t j, k, ik, kc, ip;
726  doublereal stoich, nmoles, csum, term1, fctr, rfctr;
727  vector_fp nu;
728  doublereal grad = 0.0;
729 
730  dxi.resize(nFree());
731  computeN();
732  doublereal not_mu = 1.0e12;
733  m_mix->getValidChemPotentials(not_mu, DATA_PTR(m_mu));
734 
735  for (j = 0; j < nFree(); j++) {
736 
737  // get stoichiometric vector
738  getStoichVector(j, nu);
739 
740  // compute Delta G
741  doublereal dg_rt = 0.0;
742  for (k = 0; k < m_nsp; k++) {
743  dg_rt += m_mu[m_species[k]] * nu[k];
744  }
745  dg_rt /= (m_temp * GasConstant);
746 
747  m_deltaG_RT[j] = dg_rt;
748  fctr = 1.0;
749 
750  // if this is a formation reaction for a single-component phase,
751  // check whether reaction should be included
752  ik = j + m_nel;
753  k = m_order[ik];
754  if (!m_dsoln[k]) {
755  if (m_moles[k] <= 0.0 && dg_rt > 0.0) {
756  fctr = 0.0;
757  } else {
758  fctr = 0.5;
759  }
760  } else if (!m_solnrxn[j]) {
761  fctr = 1.0;
762  } else {
763 
764  // component sum
765  csum = 0.0;
766  for (k = 0; k < m_nel; k++) {
767  kc = m_order[k];
768  stoich = nu[kc];
769  nmoles = fabs(m_mix->speciesMoles(m_species[kc])) + Tiny;
770  csum += stoich*stoich*m_dsoln[kc]/nmoles;
771  }
772 
773  // noncomponent term
774  kc = m_order[j + m_nel];
775  nmoles = fabs(m_mix->speciesMoles(m_species[kc])) + Tiny;
776  term1 = m_dsoln[kc]/nmoles;
777 
778  // sum over solution phases
779  doublereal sum = 0.0, psum;
780  for (ip = 0; ip < m_np; ip++) {
781  ThermoPhase& p = m_mix->phase(ip);
782  if (p.nSpecies() > 1) {
783  psum = 0.0;
784  for (k = 0; k < m_nsp; k++) {
785  kc = m_species[k];
786  if (m_mix->speciesPhaseIndex(kc) == ip) {
787  // bug fixed 7/12/06 DGG
788  stoich = nu[k]; // nu[kc];
789  psum += stoich * stoich;
790  }
791  }
792  sum -= psum / (fabs(m_mix->phaseMoles(ip)) + Tiny);
793  }
794  }
795  rfctr = term1 + csum + sum;
796  if (fabs(rfctr) < Tiny) {
797  fctr = 1.0;
798  } else {
799  fctr = 1.0/(term1 + csum + sum);
800  }
801  }
802  dxi[j] = -fctr*dg_rt;
803 
804  size_t m;
805  for (m = 0; m < m_nel; m++) {
806  if (m_moles[m_order[m]] <= 0.0 && (m_N(m, j)*dxi[j] < 0.0)) {
807  dxi[j] = 0.0;
808  }
809  }
810  grad += dxi[j]*dg_rt;
811 
812  }
813  return grad*GasConstant*m_temp;
814 }
815 
816 void MultiPhaseEquil::computeN()
817 {
818  // Sort the list of species by mole fraction (decreasing order)
819  std::vector<std::pair<double, size_t> > moleFractions(m_nsp);
820  for (size_t k = 0; k < m_nsp; k++) {
821  // use -Xk to generate reversed sort order
822  moleFractions[k].first = - m_mix->speciesMoles(m_species[k]);
823  moleFractions[k].second = k;
824  }
825  std::sort(moleFractions.begin(), moleFractions.end());
826  for (size_t k = 0; k < m_nsp; k++) {
827  m_sortindex[k] = moleFractions[k].second;
828  }
829 
830  bool ok;
831  for (size_t m = 0; m < m_nel; m++) {
832  size_t k = 0;
833  for (size_t ik = 0; ik < m_nsp; ik++) {
834  k = m_sortindex[ik];
835  if (m_mix->nAtoms(m_species[k],m_element[m]) != 0) {
836  break;
837  }
838  }
839  ok = false;
840  for (size_t ij = 0; ij < m_nel; ij++) {
841  if (k == m_order[ij]) {
842  ok = true;
843  }
844  }
845  if (!ok || m_force) {
846  getComponents(m_sortindex);
847  m_force = true;
848  break;
849  }
850  }
851 }
852 
853 doublereal MultiPhaseEquil::error()
854 {
855  doublereal err, maxerr = 0.0;
856 
857  // examine every reaction
858  for (size_t j = 0; j < nFree(); j++) {
859  size_t ik = j + m_nel;
860 
861  // don't require formation reactions for solution species
862  // present in trace amounts to be equilibrated
863  if (!isStoichPhase(ik) && fabs(moles(ik)) <= SmallNumber) {
864  err = 0.0;
865  }
866 
867  // for stoichiometric phase species, no error if not present and
868  // delta G for the formation reaction is positive
869  if (isStoichPhase(ik) && moles(ik) <= 0.0 &&
870  m_deltaG_RT[j] >= 0.0) {
871  err = 0.0;
872  } else {
873  err = fabs(m_deltaG_RT[j]);
874  }
875  if (err > maxerr) {
876  maxerr = err;
877  }
878  }
879  return maxerr;
880 }
881 
882 double MultiPhaseEquil::phaseMoles(size_t iph) const
883 {
884  return m_mix->phaseMoles(iph);
885 }
886 
887 void MultiPhaseEquil::reportCSV(const std::string& reportFile)
888 {
889  size_t k;
890  size_t istart;
891  size_t nSpecies;
892 
893  double vol = 0.0;
894  string sName;
895  size_t nphase = m_np;
896 
897  FILE* FP = fopen(reportFile.c_str(), "w");
898  if (!FP) {
899  printf("Failure to open file\n");
900  exit(EXIT_FAILURE);
901  }
902  double Temp = m_mix->temperature();
903  double pres = m_mix->pressure();
904  vector<double> mf(m_nsp_mix, 1.0);
905  vector<double> fe(m_nsp_mix, 0.0);
906 
907  std::vector<double> VolPM;
908  std::vector<double> activity;
909  std::vector<double> ac;
910  std::vector<double> mu;
911  std::vector<double> mu0;
912  std::vector<double> molalities;
913 
914 
915  vol = 0.0;
916  for (size_t iphase = 0; iphase < nphase; iphase++) {
917  istart = m_mix->speciesIndex(0, iphase);
918  ThermoPhase& tref = m_mix->phase(iphase);
919  nSpecies = tref.nSpecies();
920  VolPM.resize(nSpecies, 0.0);
921  tref.getMoleFractions(&mf[istart]);
922  tref.getPartialMolarVolumes(DATA_PTR(VolPM));
923  //vcs_VolPhase *volP = m_vprob->VPhaseList[iphase];
924 
925  double TMolesPhase = phaseMoles(iphase);
926  double VolPhaseVolumes = 0.0;
927  for (k = 0; k < nSpecies; k++) {
928  VolPhaseVolumes += VolPM[k] * mf[istart + k];
929  }
930  VolPhaseVolumes *= TMolesPhase;
931  vol += VolPhaseVolumes;
932  }
933  fprintf(FP,"--------------------- VCS_MULTIPHASE_EQUIL FINAL REPORT"
934  " -----------------------------\n");
935  fprintf(FP,"Temperature = %11.5g kelvin\n", Temp);
936  fprintf(FP,"Pressure = %11.5g Pascal\n", pres);
937  fprintf(FP,"Total Volume = %11.5g m**3\n", vol);
938  // fprintf(FP,"Number Basis optimizations = %d\n", m_vprob->m_NumBasisOptimizations);
939  // fprintf(FP,"Number VCS iterations = %d\n", m_vprob->m_Iterations);
940 
941  for (size_t iphase = 0; iphase < nphase; iphase++) {
942  istart = m_mix->speciesIndex(0, iphase);
943 
944  ThermoPhase& tref = m_mix->phase(iphase);
945  ThermoPhase* tp = &tref;
946  tp->getMoleFractions(&mf[istart]);
947  string phaseName = tref.name();
948  // vcs_VolPhase *volP = m_vprob->VPhaseList[iphase];
949  double TMolesPhase = phaseMoles(iphase);
950  //AssertTrace(TMolesPhase == m_mix->phaseMoles(iphase));
951  nSpecies = tref.nSpecies();
952  activity.resize(nSpecies, 0.0);
953  ac.resize(nSpecies, 0.0);
954 
955  mu0.resize(nSpecies, 0.0);
956  mu.resize(nSpecies, 0.0);
957  VolPM.resize(nSpecies, 0.0);
958  molalities.resize(nSpecies, 0.0);
959 
960  int actConvention = tp->activityConvention();
961  tp->getActivities(DATA_PTR(activity));
962  tp->getActivityCoefficients(DATA_PTR(ac));
963  tp->getStandardChemPotentials(DATA_PTR(mu0));
964 
965  tp->getPartialMolarVolumes(DATA_PTR(VolPM));
966  tp->getChemPotentials(DATA_PTR(mu));
967  double VolPhaseVolumes = 0.0;
968  for (k = 0; k < nSpecies; k++) {
969  VolPhaseVolumes += VolPM[k] * mf[istart + k];
970  }
971  VolPhaseVolumes *= TMolesPhase;
972  vol += VolPhaseVolumes;
973  if (actConvention == 1) {
974  MolalityVPSSTP* mTP = static_cast<MolalityVPSSTP*>(tp);
975  mTP->getMolalities(DATA_PTR(molalities));
976  tp->getChemPotentials(DATA_PTR(mu));
977 
978  if (iphase == 0) {
979  fprintf(FP," Name, Phase, PhaseMoles, Mole_Fract, "
980  "Molalities, ActCoeff, Activity,"
981  "ChemPot_SS0, ChemPot, mole_num, PMVol, Phase_Volume\n");
982 
983  fprintf(FP," , , (kmol), , "
984  ", , ,"
985  " (kJ/gmol), (kJ/gmol), (kmol), (m**3/kmol), (m**3)\n");
986  }
987  for (k = 0; k < nSpecies; k++) {
988  sName = tp->speciesName(k);
989  fprintf(FP,"%12s, %11s, %11.3e, %11.3e, %11.3e, %11.3e, %11.3e,"
990  "%11.3e, %11.3e, %11.3e, %11.3e, %11.3e\n",
991  sName.c_str(),
992  phaseName.c_str(), TMolesPhase,
993  mf[istart + k], molalities[k], ac[k], activity[k],
994  mu0[k]*1.0E-6, mu[k]*1.0E-6,
995  mf[istart + k] * TMolesPhase,
996  VolPM[k], VolPhaseVolumes);
997  }
998 
999  } else {
1000  if (iphase == 0) {
1001  fprintf(FP," Name, Phase, PhaseMoles, Mole_Fract, "
1002  "Molalities, ActCoeff, Activity,"
1003  " ChemPotSS0, ChemPot, mole_num, PMVol, Phase_Volume\n");
1004 
1005  fprintf(FP," , , (kmol), , "
1006  ", , ,"
1007  " (kJ/gmol), (kJ/gmol), (kmol), (m**3/kmol), (m**3)\n");
1008  }
1009  for (k = 0; k < nSpecies; k++) {
1010  molalities[k] = 0.0;
1011  }
1012  for (k = 0; k < nSpecies; k++) {
1013  sName = tp->speciesName(k);
1014  fprintf(FP,"%12s, %11s, %11.3e, %11.3e, %11.3e, %11.3e, %11.3e, "
1015  "%11.3e, %11.3e,% 11.3e, %11.3e, %11.3e\n",
1016  sName.c_str(),
1017  phaseName.c_str(), TMolesPhase,
1018  mf[istart + k], molalities[k], ac[k],
1019  activity[k], mu0[k]*1.0E-6, mu[k]*1.0E-6,
1020  mf[istart + k] * TMolesPhase,
1021  VolPM[k], VolPhaseVolumes);
1022  }
1023  }
1024 #ifdef DEBUG_MODE
1025  /*
1026  * Check consistency: These should be equal
1027  */
1028  tp->getChemPotentials(&(fe[istart]));
1029  for (k = 0; k < nSpecies; k++) {
1030  //if (!vcs_doubleEqual(fe[istart+k], mu[k])) {
1031  // fprintf(FP,"ERROR: incompatibility!\n");
1032  // fclose(FP);
1033  // printf("ERROR: incompatibility!\n");
1034  // exit(EXIT_FAILURE);
1035  // }
1036  }
1037 #endif
1038 
1039  }
1040  fclose(FP);
1041 }
1042 
1043 }
std::string int2str(const int n, const std::string &fmt)
Convert an int to a string using a format converter.
Definition: stringUtils.cpp:40
bool tempOK(size_t p) const
Return true if the phase p has valid thermo data for the current temperature.
void beginLogGroup(const std::string &title, int loglevel)
Create a new group for log messages.
Definition: global.cpp:90
doublereal speciesMoles(size_t kGlob) const
Returns the moles of global species k. units = kmol.
Definition: MultiPhase.cpp:261
doublereal computeReactionSteps(vector_fp &dxi)
Compute the change in extent of reaction for each reaction.
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:173
std::string speciesName(const size_t kGlob) const
Name of species with global index kGlob.
Definition: MultiPhase.cpp:969
size_t nFree() const
Number of degrees of freedom.
doublereal phaseMoles(const size_t n) const
Return the number of moles in phase n.
doublereal temperature() const
Temperature [K].
Definition: MultiPhase.h:335
void getComponents(const std::vector< size_t > &order)
This method finds a set of component species and a complete set of formation reactions for the non-co...
This file contains definitions for utility functions and text for modules, inputfiles, logs, textlogs, HTML_logs (see Input File Handling, Diagnostic Output, Writing messages to the screen and Writing HTML Logfiles).
void getMoleFractions(doublereal *const x) const
Get the species mole fraction vector.
Definition: Phase.cpp:519
Headers for the MultiPhase object that is used to set up multiphase equilibrium problems (see Equilib...
Header for intermediate ThermoPhase object for phases which employ molality based activity coefficien...
int setInitialMoles(int loglevel=0)
Estimate the initial mole numbers.
Base class for a phase with thermodynamic properties.
Definition: ThermoPhase.h:101
size_t speciesPhaseIndex(const size_t kGlob) const
Returns the phase index of the Kth "global" species.
A class for multiphase mixtures.
Definition: MultiPhase.h:54
doublereal stepComposition(int loglevel=0)
Take one step in composition, given the gradient of G at the starting point, and a vector of reaction...
void resize(size_t n, size_t m, doublereal v=0.0)
Resize the matrix.
Definition: DenseMatrix.cpp:71
void unsort(vector_fp &x)
Re-arrange a vector of species properties in sorted form (components first) into unsorted, sequential form.
std::string elementName(size_t m) const
Returns the name of the global element m.
Definition: MultiPhase.cpp:940
void getValidChemPotentials(doublereal not_mu, doublereal *mu, bool standard=false) const
Returns a vector of Valid chemical potentials.
Definition: MultiPhase.cpp:330
std::string fp2str(const double x, const std::string &fmt)
Convert a double into a c++ string.
Definition: stringUtils.cpp:29
void multiply(const DenseMatrix &A, const double *const b, double *const prod)
Multiply A*b and return the result in prod. Uses BLAS routine DGEMV.
std::string reactionString(size_t j)
Return a string specifying the jth reaction.
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:68
doublereal nAtoms(const size_t kGlob, const size_t mGlob) const
Returns the Number of atoms of global element mGlob in global species kGlob.
Definition: MultiPhase.cpp:974
void endLogGroup(const std::string &title)
Close the current group of log messages.
Definition: global.cpp:115
size_t nSpecies() const
Returns the number of species in the phase.
Definition: Phase.h:252
void addLogEntry(const std::string &tag, const std::string &value)
Add an entry to an HTML log file.
Definition: global.cpp:95
const doublereal SmallNumber
smallest number to compare to zero.
Definition: ct_defs.h:139
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
Definition: ct_defs.h:165
static string coeffString(bool first, doublereal nu, string sym)
Used to print reaction equations.
const doublereal Tiny
Small number to compare differences of mole fractions against.
Definition: ct_defs.h:155
const doublereal GasConstant
Universal Gas Constant. [J/kmol/K].
Definition: ct_defs.h:66
doublereal elementMoles(size_t m) const
Total moles of global element m, summed over all phases.
Definition: MultiPhase.cpp:267
Contains declarations for string manipulation functions within Cantera.
#define DATA_PTR(vec)
Creates a pointer to the start of the raw data for a vector.
Definition: ct_defs.h:36
bool solutionSpecies(size_t kGlob) const
Return true is species kGlob is a species in a multicomponent solution phase.
Definition: MultiPhase.cpp:351
thermo_t & phase(size_t n)
Return a reference to phase n.
Definition: MultiPhase.cpp:236
doublereal pressure() const
Pressure [Pa].
Definition: MultiPhase.h:378
size_t nSpecies() const
Number of species, summed over all phases.
Definition: MultiPhase.h:125
size_t speciesIndex(size_t k, size_t p) const
Return the global index of the species belonging to phase number p with local index k within the phas...
Definition: MultiPhase.h:231
void finish()
Clean up the composition.
void setMoles(const doublereal *n)
Sets all of the global species mole numbers.
Definition: MultiPhase.cpp:477
size_t nElements() const
Number of elements.
Definition: MultiPhase.h:99
size_t nPhases() const
Number of phases.
Definition: MultiPhase.h:415