Cantera  2.2.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MultiPhaseEquil.cpp
Go to the documentation of this file.
1 /**
2  * @file MultiPhaseEquil.cpp
3  */
7 
8 #include <cstdio>
9 
10 using namespace std;
11 
12 namespace Cantera
13 {
14 
15 MultiPhaseEquil::MultiPhaseEquil(MultiPhase* mix, bool start, int loglevel) : m_mix(mix)
16 {
17  // store some mixture parameters locally
18  m_nel_mix = mix->nElements();
19  m_nsp_mix = mix->nSpecies();
20  m_np = mix->nPhases();
21  m_press = mix->pressure();
22  m_temp = mix->temperature();
23 
24  size_t m, k;
25  m_force = true;
26  m_nel = 0;
27  m_nsp = 0;
28  m_eloc = 1000;
29  m_incl_species.resize(m_nsp_mix,1);
30  m_incl_element.resize(m_nel_mix,1);
31  for (m = 0; m < m_nel_mix; m++) {
32  string enm = mix->elementName(m);
33  // element 'E' or 'e' represents an electron; this
34  // requires special handling, so save its index
35  // for later use
36  if (enm == "E" || enm == "e") {
37  m_eloc = m;
38  }
39  // if an element other than electrons is not present in
40  // the mixture, then exclude it and all species containing
41  // it from the calculation. Electrons are a special case,
42  // since a species can have a negative number of 'atoms'
43  // of electrons (positive ions).
44  if (m_mix->elementMoles(m) <= 0.0) {
45  if (m != m_eloc) {
46  m_incl_element[m] = 0;
47  for (k = 0; k < m_nsp_mix; k++) {
48  if (m_mix->nAtoms(k,m) != 0.0) {
49  m_incl_species[k] = 0;
50  }
51  }
52  }
53  }
54  }
55 
56  // Now build the list of elements to be included, starting with
57  // electrons, if they are present.
58  if (m_eloc < m_nel_mix) {
59  m_element.push_back(m_eloc);
60  m_nel++;
61  }
62  // add the included elements other than electrons
63  for (m = 0; m < m_nel_mix; m++) {
64  if (m_incl_element[m] == 1 && m != m_eloc) {
65  m_nel++;
66  m_element.push_back(m);
67  }
68  }
69 
70  // include pure single-constituent phases only if their thermo
71  // data are valid for this temperature. This is necessary,
72  // since some thermo polynomial fits are done only for a
73  // limited temperature range. For example, using the NASA
74  // polynomial fits for solid ice and liquid water, if this
75  // were not done the calculation would predict solid ice to be
76  // present far above its melting point, since the thermo
77  // polynomial fits only extend to 273.15 K, and give
78  // unphysical results above this temperature, leading
79  // (incorrectly) to Gibbs free energies at high temperature
80  // lower than for liquid water.
81  size_t ip;
82  for (k = 0; k < m_nsp_mix; k++) {
83  ip = m_mix->speciesPhaseIndex(k);
84  if (!m_mix->solutionSpecies(k) &&
85  !m_mix->tempOK(ip)) {
86  m_incl_species[k] = 0;
87  if (m_mix->speciesMoles(k) > 0.0) {
88  throw CanteraError("MultiPhaseEquil",
89  "condensed-phase species"+ m_mix->speciesName(k)
90  + " is excluded since its thermo properties are \n"
91  "not valid at this temperature, but it has "
92  "non-zero moles in the initial state.");
93  }
94  }
95  }
96 
97  // Now build the list of all species to be included in the
98  // calculation.
99  for (k = 0; k < m_nsp_mix; k++) {
100  if (m_incl_species[k] ==1) {
101  m_nsp++;
102  m_species.push_back(k);
103  }
104  }
105 
106  // some work arrays for internal use
107  m_work.resize(m_nsp);
108  m_work2.resize(m_nsp);
109  m_work3.resize(m_nsp_mix);
110  m_mu.resize(m_nsp_mix);
111 
112  // number of moles of each species
113  m_moles.resize(m_nsp);
114  m_lastmoles.resize(m_nsp);
115  m_dxi.resize(nFree());
116 
117  // initialize the mole numbers to the mixture composition
118  size_t ik;
119  for (ik = 0; ik < m_nsp; ik++) {
120  m_moles[ik] = m_mix->speciesMoles(m_species[ik]);
121  }
122 
123  // Delta G / RT for each reaction
124  m_deltaG_RT.resize(nFree(), 0.0);
125 
126  m_majorsp.resize(m_nsp);
127  m_sortindex.resize(m_nsp,0);
128  m_lastsort.resize(m_nel);
129  m_solnrxn.resize(nFree());
130  m_A.resize(m_nel, m_nsp, 0.0);
131  m_N.resize(m_nsp, nFree());
132  m_order.resize(std::max(m_nsp, m_nel), 0);
133  for (k = 0; k < m_nsp; k++) {
134  m_order[k] = k;
135  }
136 
137  // if the 'start' flag is set, estimate the initial mole
138  // numbers by doing a linear Gibbs minimization. In this case,
139  // only the elemental composition of the initial mixture state
140  // matters.
141  if (start) {
142  setInitialMoles(loglevel-1);
143  }
144  computeN();
145 
146  // Take a very small step in composition space, so that no
147  // species has precisely zero moles.
148  vector_fp dxi(nFree(), 1.0e-20);
149  if (!dxi.empty()) {
150  multiply(m_N, DATA_PTR(dxi), DATA_PTR(m_work));
151  unsort(m_work);
152  }
153 
154  for (k = 0; k < m_nsp; k++) {
155  m_moles[k] += m_work[k];
156  m_lastmoles[k] = m_moles[k];
157  if (m_mix->solutionSpecies(m_species[k])) {
158  m_dsoln.push_back(1);
159  } else {
160  m_dsoln.push_back(0);
161  }
162  }
163  m_force = false;
164  updateMixMoles();
165 
166  // At this point, the instance has been created, the species
167  // to be included have been determined, and an initial
168  // composition has been selected that has all non-zero mole
169  // numbers for the included species.
170 }
171 
172 doublereal MultiPhaseEquil::equilibrate(int XY, doublereal err,
173  int maxsteps, int loglevel)
174 {
175  int i;
176  m_iter = 0;
177 
178  for (i = 0; i < maxsteps; i++) {
179  stepComposition(loglevel-1);
180  if (error() < err) {
181  break;
182  }
183  }
184  if (i >= maxsteps) {
185  throw CanteraError("MultiPhaseEquil::equilibrate",
186  "no convergence in " + int2str(maxsteps) +
187  " iterations. Error = " + fp2str(error()));
188  }
189  finish();
190  return error();
191 }
192 
193 void MultiPhaseEquil::updateMixMoles()
194 {
195  fill(m_work3.begin(), m_work3.end(), 0.0);
196  size_t k;
197  for (k = 0; k < m_nsp; k++) {
198  m_work3[m_species[k]] = m_moles[k];
199  }
200  m_mix->setMoles(DATA_PTR(m_work3));
201 }
202 
204 {
205  fill(m_work3.begin(), m_work3.end(), 0.0);
206  size_t k;
207  for (k = 0; k < m_nsp; k++) {
208  m_work3[m_species[k]] = (m_moles[k] > 0.0 ? m_moles[k] : 0.0);
209  }
210  m_mix->setMoles(DATA_PTR(m_work3));
211 }
212 
214 {
215  size_t ik, j;
216 
217  double not_mu = 1.0e12;
218 
219  m_mix->getValidChemPotentials(not_mu, DATA_PTR(m_mu), true);
220  doublereal dg_rt;
221 
222  int idir;
223  double nu;
224  double delta_xi, dxi_min = 1.0e10;
225  bool redo = true;
226  int iter = 0;
227 
228  while (redo) {
229 
230  // choose a set of components based on the current
231  // composition
232  computeN();
233  redo = false;
234  iter++;
235  if (iter > 4) {
236  break;
237  }
238 
239  // loop over all reactions
240  for (j = 0; j < nFree(); j++) {
241  dg_rt = 0.0;
242  dxi_min = 1.0e10;
243  for (ik = 0; ik < m_nsp; ik++) {
244  dg_rt += mu(ik) * m_N(ik,j);
245  }
246 
247  // fwd or rev direction
248  idir = (dg_rt < 0.0 ? 1 : -1);
249 
250  for (ik = 0; ik < m_nsp; ik++) {
251  nu = m_N(ik, j);
252 
253  // set max change in progress variable by
254  // non-negativity requirement
255  // -> Note, 0.99 factor is so that difference of 2 numbers
256  // isn't zero. This causes differences between
257  // optimized and debug versions of the code
258  if (nu*idir < 0) {
259  delta_xi = fabs(0.99*moles(ik)/nu);
260  // if a component has nearly zero moles, redo
261  // with a new set of components
262  if (!redo && delta_xi < 1.0e-10 && ik < m_nel) {
263  redo = true;
264  }
265  dxi_min = std::min(dxi_min, delta_xi);
266  }
267  }
268  // step the composition by dxi_min
269  for (ik = 0; ik < m_nsp; ik++) {
270  moles(ik) += m_N(ik, j) * idir*dxi_min;
271  }
272  }
273  // set the moles of the phase objects to match
274  updateMixMoles();
275  }
276  return 0;
277 }
278 
279 void MultiPhaseEquil::getComponents(const std::vector<size_t>& order)
280 {
281  size_t m, k, j;
282 
283  // if the input species array has the wrong size, ignore it
284  // and consider the species for components in declaration order.
285  if (order.size() != m_nsp) {
286  for (k = 0; k < m_nsp; k++) {
287  m_order[k] = k;
288  }
289  } else {
290  for (k = 0; k < m_nsp; k++) {
291  m_order[k] = order[k];
292  }
293  }
294 
295  size_t nRows = m_nel;
296  size_t nColumns = m_nsp;
297  doublereal fctr;
298 
299  // set up the atomic composition matrix
300  for (m = 0; m < nRows; m++) {
301  for (k = 0; k < nColumns; k++) {
302  m_A(m, k) = m_mix->nAtoms(m_species[m_order[k]], m_element[m]);
303  }
304  }
305 
306  // Do Gaussian elimination
307  for (m = 0; m < nRows; m++) {
308  // Check for rows that are zero
309  bool isZeroRow = true;
310  for (k = m; k < nColumns; k++) {
311  if (fabs(m_A(m,k)) > sqrt(Tiny)) {
312  isZeroRow = false;
313  break;
314  }
315  }
316  if (isZeroRow) {
317  // Find the last non-zero row
318  size_t n = nRows - 1;
319  bool foundSwapCandidate = false;
320  for (; n > m; n--) {
321  for (k = m; k < nColumns; k++) {
322  if (fabs(m_A(n,k)) > sqrt(Tiny)) {
323  foundSwapCandidate = true;
324  break;
325  }
326  }
327  if (foundSwapCandidate) {
328  break;
329  }
330  }
331  if (m != n) {
332  // Swap this row with the last non-zero row
333  for (k = 0; k < nColumns; k++) {
334  std::swap(m_A(n,k), m_A(m,k));
335  }
336  } else {
337  // All remaining rows are zero. Elimination is complete.
338  break;
339  }
340  }
341 
342  // If a pivot is zero, exchange columns. This occurs when
343  // a species has an elemental composition that is not
344  // linearly independent of the component species that have
345  // already been assigned
346  if (m < nColumns && m_A(m,m) == 0.0) {
347  // First, we need to find a good candidate for a
348  // component species to swap in for the one that has
349  // zero pivot. It must contain element m, be linearly
350  // independent of the components processed so far
351  // (m_A(m,k) != 0), and should be a major species if
352  // possible. We'll choose the species with greatest
353  // mole fraction that satisfies these criteria.
354  doublereal maxmoles = -999.0;
355  size_t kmax = 0;
356  for (k = m+1; k < nColumns; k++) {
357  if (m_A(m,k) != 0.0) {
358  if (fabs(m_moles[m_order[k]]) > maxmoles) {
359  kmax = k;
360  maxmoles = fabs(m_moles[m_order[k]]);
361  }
362  }
363  }
364 
365  // Now exchange the column with zero pivot with the
366  // column for this major species
367  for (size_t n = 0; n < nRows; n++) {
368  std::swap(m_A(n, m), m_A(n, kmax));
369  }
370 
371  // exchange the species labels on the columns
372  std::swap(m_order[m], m_order[kmax]);
373  }
374 
375  // scale row m so that the diagonal element is unity
376  fctr = 1.0/m_A(m,m);
377  for (k = 0; k < nColumns; k++) {
378  m_A(m,k) *= fctr;
379  }
380 
381  // For all rows below the diagonal, subtract A(n,m)/A(m,m)
382  // * (row m) from row n, so that A(n,m) = 0.
383  for (size_t n = m+1; n < m_nel; n++) {
384  fctr = m_A(n,m)/m_A(m,m);
385  for (k = 0; k < m_nsp; k++) {
386  m_A(n,k) -= m_A(m,k)*fctr;
387  }
388  }
389  }
390 
391  // The left m_nel columns of A are now upper-diagonal. Now
392  // reduce the m_nel columns to diagonal form by back-solving
393  for (m = std::min(nRows,nColumns)-1; m > 0; m--) {
394  for (size_t n = m-1; n != npos; n--) {
395  if (m_A(n,m) != 0.0) {
396  fctr = m_A(n,m);
397  for (k = m; k < m_nsp; k++) {
398  m_A(n,k) -= fctr*m_A(m,k);
399  }
400  }
401  }
402  }
403 
404  // create stoichiometric coefficient matrix.
405  for (size_t n = 0; n < m_nsp; n++) {
406  if (n < m_nel)
407  for (k = 0; k < nFree(); k++) {
408  m_N(n, k) = -m_A(n, k + m_nel);
409  }
410  else {
411  for (k = 0; k < nFree(); k++) {
412  m_N(n, k) = 0.0;
413  }
414  m_N(n, n - m_nel) = 1.0;
415  }
416  }
417 
418  // find reactions involving solution phase species
419  for (j = 0; j < nFree(); j++) {
420  m_solnrxn[j] = false;
421  for (k = 0; k < m_nsp; k++) {
422  if (m_N(k, j) != 0)
423  if (m_mix->solutionSpecies(m_species[m_order[k]])) {
424  m_solnrxn[j] = true;
425  }
426  }
427  }
428 }
429 
431 {
432  copy(x.begin(), x.end(), m_work2.begin());
433  size_t k;
434  for (k = 0; k < m_nsp; k++) {
435  x[m_order[k]] = m_work2[k];
436  }
437 }
438 
439 void MultiPhaseEquil::step(doublereal omega, vector_fp& deltaN,
440  int loglevel)
441 {
442  size_t k, ik;
443  if (omega < 0.0) {
444  throw CanteraError("MultiPhaseEquil::step","negative omega");
445  }
446 
447  for (ik = 0; ik < m_nel; ik++) {
448  k = m_order[ik];
449  m_lastmoles[k] = m_moles[k];
450  m_moles[k] += omega * deltaN[k];
451  }
452 
453  for (ik = m_nel; ik < m_nsp; ik++) {
454  k = m_order[ik];
455  m_lastmoles[k] = m_moles[k];
456  if (m_majorsp[k]) {
457  m_moles[k] += omega * deltaN[k];
458  } else {
459  m_moles[k] = fabs(m_moles[k])*std::min(10.0,
460  exp(-m_deltaG_RT[ik - m_nel]));
461  }
462  }
463  updateMixMoles();
464 }
465 
466 doublereal MultiPhaseEquil::stepComposition(int loglevel)
467 {
468  m_iter++;
469  size_t ik, k = 0;
470  doublereal grad0 = computeReactionSteps(m_dxi);
471 
472  // compute the mole fraction changes.
473  if (nFree()) {
474  multiply(m_N, DATA_PTR(m_dxi), DATA_PTR(m_work));
475  }
476 
477  // change to sequential form
478  unsort(m_work);
479 
480  // scale omega to keep the major species non-negative
481  doublereal FCTR = 0.99;
482  const doublereal MAJOR_THRESHOLD = 1.0e-12;
483 
484  doublereal omega = 1.0, omax, omegamax = 1.0;
485  for (ik = 0; ik < m_nsp; ik++) {
486  k = m_order[ik];
487  if (ik < m_nel) {
488  FCTR = 0.99;
489  if (m_moles[k] < MAJOR_THRESHOLD) {
490  m_force = true;
491  }
492  } else {
493  FCTR = 0.9;
494  }
495  // if species k is in a multi-species solution phase, then its
496  // mole number must remain positive, unless the entire phase
497  // goes away. First we'll determine an upper bound on omega,
498  // such that all
499  if (m_dsoln[k] == 1) {
500 
501  if ((m_moles[k] > MAJOR_THRESHOLD) || (ik < m_nel)) {
502  if (m_moles[k] < MAJOR_THRESHOLD) {
503  m_force = true;
504  }
505  omax = m_moles[k]*FCTR/(fabs(m_work[k]) + Tiny);
506  if (m_work[k] < 0.0 && omax < omegamax) {
507  omegamax = omax;
508  if (omegamax < 1.0e-5) {
509  m_force = true;
510  }
511  }
512  m_majorsp[k] = true;
513  } else {
514  m_majorsp[k] = false;
515  }
516  } else {
517  if (m_work[k] < 0.0 && m_moles[k] > 0.0) {
518  omax = -m_moles[k]/m_work[k];
519  if (omax < omegamax) {
520  omegamax = omax;
521  if (omegamax < 1.0e-5) {
522  m_force = true;
523  }
524  }
525  }
526  m_majorsp[k] = true;
527  }
528  }
529 
530  // now take a step with this scaled omega
531  step(omegamax, m_work);
532  // compute the gradient of G at this new position in the
533  // current direction. If it is positive, then we have overshot
534  // the minimum. In this case, interpolate back.
535  doublereal not_mu = 1.0e12;
536  m_mix->getValidChemPotentials(not_mu, DATA_PTR(m_mu));
537  doublereal grad1 = 0.0;
538  for (k = 0; k < m_nsp; k++) {
539  grad1 += m_work[k] * m_mu[m_species[k]];
540  }
541 
542  omega = omegamax;
543  if (grad1 > 0.0) {
544  omega *= fabs(grad0) / (grad1 + fabs(grad0));
545  for (k = 0; k < m_nsp; k++) {
546  m_moles[k] = m_lastmoles[k];
547  }
548  step(omega, m_work);
549  }
550  return omega;
551 }
552 
554 {
555  size_t j, k, ik, kc, ip;
556  doublereal stoich, nmoles, csum, term1, fctr, rfctr;
557  vector_fp nu;
558  doublereal grad = 0.0;
559 
560  dxi.resize(nFree());
561  computeN();
562  doublereal not_mu = 1.0e12;
563  m_mix->getValidChemPotentials(not_mu, DATA_PTR(m_mu));
564 
565  for (j = 0; j < nFree(); j++) {
566 
567  // get stoichiometric vector
568  getStoichVector(j, nu);
569 
570  // compute Delta G
571  doublereal dg_rt = 0.0;
572  for (k = 0; k < m_nsp; k++) {
573  dg_rt += m_mu[m_species[k]] * nu[k];
574  }
575  dg_rt /= (m_temp * GasConstant);
576 
577  m_deltaG_RT[j] = dg_rt;
578  fctr = 1.0;
579 
580  // if this is a formation reaction for a single-component phase,
581  // check whether reaction should be included
582  ik = j + m_nel;
583  k = m_order[ik];
584  if (!m_dsoln[k]) {
585  if (m_moles[k] <= 0.0 && dg_rt > 0.0) {
586  fctr = 0.0;
587  } else {
588  fctr = 0.5;
589  }
590  } else if (!m_solnrxn[j]) {
591  fctr = 1.0;
592  } else {
593 
594  // component sum
595  csum = 0.0;
596  for (k = 0; k < m_nel; k++) {
597  kc = m_order[k];
598  stoich = nu[kc];
599  nmoles = fabs(m_mix->speciesMoles(m_species[kc])) + Tiny;
600  csum += stoich*stoich*m_dsoln[kc]/nmoles;
601  }
602 
603  // noncomponent term
604  kc = m_order[j + m_nel];
605  nmoles = fabs(m_mix->speciesMoles(m_species[kc])) + Tiny;
606  term1 = m_dsoln[kc]/nmoles;
607 
608  // sum over solution phases
609  doublereal sum = 0.0, psum;
610  for (ip = 0; ip < m_np; ip++) {
611  ThermoPhase& p = m_mix->phase(ip);
612  if (p.nSpecies() > 1) {
613  psum = 0.0;
614  for (k = 0; k < m_nsp; k++) {
615  kc = m_species[k];
616  if (m_mix->speciesPhaseIndex(kc) == ip) {
617  stoich = nu[k];
618  psum += stoich * stoich;
619  }
620  }
621  sum -= psum / (fabs(m_mix->phaseMoles(ip)) + Tiny);
622  }
623  }
624  rfctr = term1 + csum + sum;
625  if (fabs(rfctr) < Tiny) {
626  fctr = 1.0;
627  } else {
628  fctr = 1.0/(term1 + csum + sum);
629  }
630  }
631  dxi[j] = -fctr*dg_rt;
632 
633  size_t m;
634  for (m = 0; m < m_nel; m++) {
635  if (m_moles[m_order[m]] <= 0.0 && (m_N(m, j)*dxi[j] < 0.0)) {
636  dxi[j] = 0.0;
637  }
638  }
639  grad += dxi[j]*dg_rt;
640 
641  }
642  return grad*GasConstant*m_temp;
643 }
644 
645 void MultiPhaseEquil::computeN()
646 {
647  // Sort the list of species by mole fraction (decreasing order)
648  std::vector<std::pair<double, size_t> > moleFractions(m_nsp);
649  for (size_t k = 0; k < m_nsp; k++) {
650  // use -Xk to generate reversed sort order
651  moleFractions[k].first = - m_mix->speciesMoles(m_species[k]);
652  moleFractions[k].second = k;
653  }
654  std::sort(moleFractions.begin(), moleFractions.end());
655  for (size_t k = 0; k < m_nsp; k++) {
656  m_sortindex[k] = moleFractions[k].second;
657  }
658 
659  bool ok;
660  for (size_t m = 0; m < m_nel; m++) {
661  size_t k = 0;
662  for (size_t ik = 0; ik < m_nsp; ik++) {
663  k = m_sortindex[ik];
664  if (m_mix->nAtoms(m_species[k],m_element[m]) != 0) {
665  break;
666  }
667  }
668  ok = false;
669  for (size_t ij = 0; ij < m_nel; ij++) {
670  if (k == m_order[ij]) {
671  ok = true;
672  }
673  }
674  if (!ok || m_force) {
675  getComponents(m_sortindex);
676  m_force = true;
677  break;
678  }
679  }
680 }
681 
682 doublereal MultiPhaseEquil::error()
683 {
684  doublereal err, maxerr = 0.0;
685 
686  // examine every reaction
687  for (size_t j = 0; j < nFree(); j++) {
688  size_t ik = j + m_nel;
689 
690  // don't require formation reactions for solution species
691  // present in trace amounts to be equilibrated
692  if (!isStoichPhase(ik) && fabs(moles(ik)) <= SmallNumber) {
693  err = 0.0;
694  }
695 
696  // for stoichiometric phase species, no error if not present and
697  // delta G for the formation reaction is positive
698  if (isStoichPhase(ik) && moles(ik) <= 0.0 &&
699  m_deltaG_RT[j] >= 0.0) {
700  err = 0.0;
701  } else {
702  err = fabs(m_deltaG_RT[j]);
703  }
704  maxerr = std::max(maxerr, err);
705  }
706  return maxerr;
707 }
708 
709 double MultiPhaseEquil::phaseMoles(size_t iph) const
710 {
711  return m_mix->phaseMoles(iph);
712 }
713 
714 void MultiPhaseEquil::reportCSV(const std::string& reportFile)
715 {
716  size_t k;
717  size_t istart;
718  size_t nSpecies;
719 
720  double vol = 0.0;
721  string sName;
722  size_t nphase = m_np;
723 
724  FILE* FP = fopen(reportFile.c_str(), "w");
725  if (!FP) {
726  throw CanteraError("MultiPhaseEquil::reportCSV", "Failure to open file");
727  }
728  double Temp = m_mix->temperature();
729  double pres = m_mix->pressure();
730  vector<double> mf(m_nsp_mix, 1.0);
731  vector<double> fe(m_nsp_mix, 0.0);
732 
733  std::vector<double> VolPM;
734  std::vector<double> activity;
735  std::vector<double> ac;
736  std::vector<double> mu;
737  std::vector<double> mu0;
738  std::vector<double> molalities;
739 
740 
741  vol = 0.0;
742  for (size_t iphase = 0; iphase < nphase; iphase++) {
743  istart = m_mix->speciesIndex(0, iphase);
744  ThermoPhase& tref = m_mix->phase(iphase);
745  nSpecies = tref.nSpecies();
746  VolPM.resize(nSpecies, 0.0);
747  tref.getMoleFractions(&mf[istart]);
748  tref.getPartialMolarVolumes(DATA_PTR(VolPM));
749 
750  double TMolesPhase = phaseMoles(iphase);
751  double VolPhaseVolumes = 0.0;
752  for (k = 0; k < nSpecies; k++) {
753  VolPhaseVolumes += VolPM[k] * mf[istart + k];
754  }
755  VolPhaseVolumes *= TMolesPhase;
756  vol += VolPhaseVolumes;
757  }
758  fprintf(FP,"--------------------- VCS_MULTIPHASE_EQUIL FINAL REPORT"
759  " -----------------------------\n");
760  fprintf(FP,"Temperature = %11.5g kelvin\n", Temp);
761  fprintf(FP,"Pressure = %11.5g Pascal\n", pres);
762  fprintf(FP,"Total Volume = %11.5g m**3\n", vol);
763 
764  for (size_t iphase = 0; iphase < nphase; iphase++) {
765  istart = m_mix->speciesIndex(0, iphase);
766 
767  ThermoPhase& tref = m_mix->phase(iphase);
768  ThermoPhase* tp = &tref;
769  tp->getMoleFractions(&mf[istart]);
770  string phaseName = tref.name();
771  double TMolesPhase = phaseMoles(iphase);
772  nSpecies = tref.nSpecies();
773  activity.resize(nSpecies, 0.0);
774  ac.resize(nSpecies, 0.0);
775 
776  mu0.resize(nSpecies, 0.0);
777  mu.resize(nSpecies, 0.0);
778  VolPM.resize(nSpecies, 0.0);
779  molalities.resize(nSpecies, 0.0);
780 
781  int actConvention = tp->activityConvention();
782  tp->getActivities(DATA_PTR(activity));
783  tp->getActivityCoefficients(DATA_PTR(ac));
784  tp->getStandardChemPotentials(DATA_PTR(mu0));
785 
786  tp->getPartialMolarVolumes(DATA_PTR(VolPM));
787  tp->getChemPotentials(DATA_PTR(mu));
788  double VolPhaseVolumes = 0.0;
789  for (k = 0; k < nSpecies; k++) {
790  VolPhaseVolumes += VolPM[k] * mf[istart + k];
791  }
792  VolPhaseVolumes *= TMolesPhase;
793  vol += VolPhaseVolumes;
794  if (actConvention == 1) {
795  MolalityVPSSTP* mTP = static_cast<MolalityVPSSTP*>(tp);
796  mTP->getMolalities(DATA_PTR(molalities));
797  tp->getChemPotentials(DATA_PTR(mu));
798 
799  if (iphase == 0) {
800  fprintf(FP," Name, Phase, PhaseMoles, Mole_Fract, "
801  "Molalities, ActCoeff, Activity,"
802  "ChemPot_SS0, ChemPot, mole_num, PMVol, Phase_Volume\n");
803 
804  fprintf(FP," , , (kmol), , "
805  ", , ,"
806  " (kJ/gmol), (kJ/gmol), (kmol), (m**3/kmol), (m**3)\n");
807  }
808  for (k = 0; k < nSpecies; k++) {
809  sName = tp->speciesName(k);
810  fprintf(FP,"%12s, %11s, %11.3e, %11.3e, %11.3e, %11.3e, %11.3e,"
811  "%11.3e, %11.3e, %11.3e, %11.3e, %11.3e\n",
812  sName.c_str(),
813  phaseName.c_str(), TMolesPhase,
814  mf[istart + k], molalities[k], ac[k], activity[k],
815  mu0[k]*1.0E-6, mu[k]*1.0E-6,
816  mf[istart + k] * TMolesPhase,
817  VolPM[k], VolPhaseVolumes);
818  }
819 
820  } else {
821  if (iphase == 0) {
822  fprintf(FP," Name, Phase, PhaseMoles, Mole_Fract, "
823  "Molalities, ActCoeff, Activity,"
824  " ChemPotSS0, ChemPot, mole_num, PMVol, Phase_Volume\n");
825 
826  fprintf(FP," , , (kmol), , "
827  ", , ,"
828  " (kJ/gmol), (kJ/gmol), (kmol), (m**3/kmol), (m**3)\n");
829  }
830  for (k = 0; k < nSpecies; k++) {
831  molalities[k] = 0.0;
832  }
833  for (k = 0; k < nSpecies; k++) {
834  sName = tp->speciesName(k);
835  fprintf(FP,"%12s, %11s, %11.3e, %11.3e, %11.3e, %11.3e, %11.3e, "
836  "%11.3e, %11.3e,% 11.3e, %11.3e, %11.3e\n",
837  sName.c_str(),
838  phaseName.c_str(), TMolesPhase,
839  mf[istart + k], molalities[k], ac[k],
840  activity[k], mu0[k]*1.0E-6, mu[k]*1.0E-6,
841  mf[istart + k] * TMolesPhase,
842  VolPM[k], VolPhaseVolumes);
843  }
844  }
845  }
846  fclose(FP);
847 }
848 
849 }
std::string int2str(const int n, const std::string &fmt)
Convert an int to a string using a format converter.
Definition: stringUtils.cpp:39
bool tempOK(size_t p) const
Return true if the phase p has valid thermo data for the current temperature.
Definition: MultiPhase.cpp:958
doublereal speciesMoles(size_t kGlob) const
Returns the moles of global species k. units = kmol.
Definition: MultiPhase.cpp:247
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:165
std::string speciesName(const size_t kGlob) const
Name of species with global index kGlob.
Definition: MultiPhase.cpp:904
size_t nFree() const
Number of degrees of freedom.
doublereal phaseMoles(const size_t n) const
Return the number of moles in phase n.
Definition: MultiPhase.cpp:938
doublereal temperature() const
Temperature [K].
Definition: MultiPhase.h:334
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...
void getMoleFractions(doublereal *const x) const
Get the species mole fraction vector.
Definition: Phase.cpp:556
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:97
size_t speciesPhaseIndex(const size_t kGlob) const
Returns the phase index of the Kth "global" species.
Definition: MultiPhase.cpp:948
A class for multiphase mixtures.
Definition: MultiPhase.h:53
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:64
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:875
void getValidChemPotentials(doublereal not_mu, doublereal *mu, bool standard=false) const
Returns a vector of Valid chemical potentials.
Definition: MultiPhase.cpp:316
std::string fp2str(const double x, const std::string &fmt)
Convert a double into a c++ string.
Definition: stringUtils.cpp:28
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.
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:99
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:909
size_t nSpecies() const
Returns the number of species in the phase.
Definition: Phase.h:265
const doublereal SmallNumber
smallest number to compare to zero.
Definition: ct_defs.h:126
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:157
const doublereal Tiny
Small number to compare differences of mole fractions against.
Definition: ct_defs.h:142
const doublereal GasConstant
Universal Gas Constant. [J/kmol/K].
Definition: ct_defs.h:64
doublereal elementMoles(size_t m) const
Total moles of global element m, summed over all phases.
Definition: MultiPhase.cpp:253
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:337
thermo_t & phase(size_t n)
Return a reference to phase n.
Definition: MultiPhase.cpp:222
doublereal pressure() const
Pressure [Pa].
Definition: MultiPhase.h:409
size_t nSpecies() const
Number of species, summed over all phases.
Definition: MultiPhase.h:124
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:230
void finish()
Clean up the composition.
void setMoles(const doublereal *n)
Sets all of the global species mole numbers.
Definition: MultiPhase.cpp:459
size_t nElements() const
Number of elements.
Definition: MultiPhase.h:98
size_t nPhases() const
Number of phases.
Definition: MultiPhase.h:446