Cantera  2.3.0
vcs_phaseStability.cpp
Go to the documentation of this file.
1 /**
2  * @file vcs_phaseStability.cpp
3  * Implementation class for functions associated with determining the stability of a phase
4  * (see Class \link Cantera::VCS_SOLVE VCS_SOLVE\endlink and \ref equilfunctions ).
5  */
6 
7 // This file is part of Cantera. See License.txt in the top-level directory or
8 // at http://www.cantera.org/license.txt for license and copyright information.
9 
14 
15 using namespace std;
16 
17 namespace Cantera
18 {
19 
20 bool VCS_SOLVE::vcs_popPhasePossible(const size_t iphasePop) const
21 {
22  vcs_VolPhase* Vphase = m_VolPhaseList[iphasePop];
23  AssertThrowMsg(!Vphase->exists(), "VCS_SOLVE::vcs_popPhasePossible",
24  "called for a phase that exists!");
25 
26  // Loop through all of the species in the phase. We say the phase can be
27  // popped, if there is one species in the phase that can be popped. This
28  // does not mean that the phase will be popped or that it leads to a lower
29  // Gibbs free energy.
30  for (size_t k = 0; k < Vphase->nSpecies(); k++) {
31  size_t kspec = Vphase->spGlobalIndexVCS(k);
32  AssertThrowMsg(m_molNumSpecies_old[kspec] <= 0.0,
33  "VCS_SOLVE::vcs_popPhasePossible",
34  "we shouldn't be here {}: {} > 0.0", kspec,
35  m_molNumSpecies_old[kspec]);
36  size_t irxn = kspec - m_numComponents;
37  if (kspec >= m_numComponents) {
38  bool iPopPossible = true;
39 
40  // Note one case is if the component is a member of the popping
41  // phase. This component will be zeroed and the logic here will
42  // negate the current species from causing a positive if this
43  // component is consumed.
44  for (size_t j = 0; j < m_numComponents; ++j) {
45  if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
46  double stoicC = m_stoichCoeffRxnMatrix(j,irxn);
47  if (stoicC != 0.0) {
48  double negChangeComp = - stoicC;
49  if (negChangeComp > 0.0) {
50  // If there is no component to give, then the
51  // species can't be created
52  if (m_molNumSpecies_old[j] <= VCS_DELETE_ELEMENTABS_CUTOFF*0.5) {
53  iPopPossible = false;
54  }
55  }
56  }
57  }
58  }
59  // We are here when the species can be popped because all its needed
60  // components have positive mole numbers
61  if (iPopPossible) {
62  return true;
63  }
64  } else {
65  // We are here when the species, k, in the phase is a component. Its
66  // mole number is zero. We loop through the regular reaction looking
67  // for a reaction that can pop the component.
68  for (size_t jrxn = 0; jrxn < m_numRxnRdc; jrxn++) {
69  bool foundJrxn = false;
70  // First, if the component is a product of the reaction
71  if (m_stoichCoeffRxnMatrix(kspec,jrxn) > 0.0) {
72  foundJrxn = true;
73  // We can do the reaction if all other reactant components
74  // have positive mole fractions
75  for (size_t kcomp = 0; kcomp < m_numComponents; kcomp++) {
76  if (m_stoichCoeffRxnMatrix(kcomp,jrxn) < 0.0 && m_molNumSpecies_old[kcomp] <= VCS_DELETE_ELEMENTABS_CUTOFF*0.5) {
77  foundJrxn = false;
78  }
79  }
80  if (foundJrxn) {
81  return true;
82  }
83  } else if (m_stoichCoeffRxnMatrix(kspec,jrxn) < 0.0) {
84  // Second we are here if the component is a reactant in the
85  // reaction, and the reaction goes backwards.
86  foundJrxn = true;
87  size_t jspec = jrxn + m_numComponents;
88  if (m_molNumSpecies_old[jspec] <= VCS_DELETE_ELEMENTABS_CUTOFF*0.5) {
89  foundJrxn = false;
90  continue;
91  }
92  // We can do the backwards reaction if all of the product
93  // components species are positive
94  for (size_t kcomp = 0; kcomp < m_numComponents; kcomp++) {
95  if (m_stoichCoeffRxnMatrix(kcomp,jrxn) > 0.0 && m_molNumSpecies_old[kcomp] <= VCS_DELETE_ELEMENTABS_CUTOFF*0.5) {
96  foundJrxn = false;
97  }
98  }
99  if (foundJrxn) {
100  return true;
101  }
102  }
103  }
104  }
105  }
106  return false;
107 }
108 
109 int VCS_SOLVE::vcs_phasePopDeterminePossibleList()
110 {
111  warn_deprecated("VCS_SOLVE::vcs_phasePopDeterminePossibleList",
112  "Unused. To be removed after Cantera 2.3.");
113  int nfound = 0;
114  phasePopProblemLists_.clear();
115 
116  // This is a vector over each component. For zeroed components it lists the
117  // phases, which are currently zeroed, which have a species with a positive
118  // stoichiometric value wrt the component. Therefore, we could pop the
119  // component species and pop that phase at the same time if we considered no
120  // other factors than keeping the component mole number positive.
121  //
122  // It does not count species with positive stoichiometric values if that
123  // species already has a positive mole number. The phase is already popped.
124  std::vector< std::vector<size_t> > zeroedComponentLinkedPhasePops(m_numComponents);
125 
126  // The logic below calculates zeroedComponentLinkedPhasePops
127  for (size_t j = 0; j < m_numComponents; j++) {
128  if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS && m_molNumSpecies_old[j] <= 0.0) {
129  std::vector<size_t> &jList = zeroedComponentLinkedPhasePops[j];
130  size_t iph = m_phaseID[j];
131  jList.push_back(iph);
132  for (size_t irxn = 0; irxn < m_numRxnTot; irxn++) {
133  size_t kspec = irxn + m_numComponents;
134  iph = m_phaseID[kspec];
135  vcs_VolPhase* Vphase = m_VolPhaseList[iph];
136  int existence = Vphase->exists();
137  if (existence < 0 && m_stoichCoeffRxnMatrix(j,irxn) > 0.0 &&
138  std::find(jList.begin(), jList.end(), iph) != jList.end()) {
139  jList.push_back(iph);
140  }
141  }
142  }
143  }
144 
145  // This is a vector over each zeroed phase For zeroed phases, it lists the
146  // components, which are currently zeroed, which have a species with a
147  // negative stoichiometric value wrt one or more species in the phase. Cut
148  // out components which have a pos stoichiometric value with another species
149  // in the phase.
150  std::vector< std::vector<size_t> > zeroedPhaseLinkedZeroComponents(m_numPhases);
151 
152  // The logic below calculates zeroedPhaseLinkedZeroComponents
153  for (size_t iph = 0; iph < m_numPhases; iph++) {
154  std::vector<size_t> &iphList = zeroedPhaseLinkedZeroComponents[iph];
155  iphList.clear();
156  vcs_VolPhase* Vphase = m_VolPhaseList[iph];
157  if (Vphase->exists() < 0) {
158  size_t nsp = Vphase->nSpecies();
159  for (size_t k = 0; k < nsp; k++) {
160  size_t kspec = Vphase->spGlobalIndexVCS(k);
161  size_t irxn = kspec - m_numComponents;
162  for (size_t j = 0; j < m_numComponents; j++) {
163  if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS && m_molNumSpecies_old[j] <= 0.0 && m_stoichCoeffRxnMatrix(j,irxn) < 0.0) {
164  bool foundPos = false;
165  for (size_t kk = 0; kk < nsp; kk++) {
166  size_t kkspec = Vphase->spGlobalIndexVCS(kk);
167  if (kkspec >= m_numComponents) {
168  size_t iirxn = kkspec - m_numComponents;
169  if (m_stoichCoeffRxnMatrix(j,iirxn) > 0.0) {
170  foundPos = true;
171  }
172  }
173  }
174  if (!foundPos && std::find(iphList.begin(), iphList.end(), j) != iphList.end()) {
175  iphList.push_back(j);
176  }
177  }
178  }
179  }
180  }
181  }
182 
183  // Now fill in the phasePopProblemLists_ list.
184  for (size_t iph = 0; iph < m_numPhases; iph++) {
185  vcs_VolPhase* Vphase = m_VolPhaseList[iph];
186  if (Vphase->exists() < 0) {
187  std::vector<size_t> &iphList = zeroedPhaseLinkedZeroComponents[iph];
188  std::vector<size_t> popProblem(0);
189  popProblem.push_back(iph);
190  for (size_t i = 0; i < iphList.size(); i++) {
191  size_t j = iphList[i];
192  std::vector<size_t> &jList = zeroedComponentLinkedPhasePops[j];
193  for (size_t jjl = 0; jjl < jList.size(); jjl++) {
194  size_t jph = jList[jjl];
195  if (std::find(popProblem.begin(), popProblem.end(), jph) != popProblem.end()) {
196  popProblem.push_back(jph);
197  }
198  }
199  }
200  phasePopProblemLists_.push_back(popProblem);
201  }
202  }
203  return nfound;
204 }
205 
206 size_t VCS_SOLVE::vcs_popPhaseID(std::vector<size_t> & phasePopPhaseIDs)
207 {
208  size_t iphasePop = npos;
209  doublereal FephaseMax = -1.0E30;
210  doublereal Fephase = -1.0E30;
211 
212  char anote[128];
213  if (m_debug_print_lvl >= 2) {
214  plogf(" --- vcs_popPhaseID() called\n");
215  plogf(" --- Phase Status F_e MoleNum\n");
216  plogf(" --------------------------------------------------------------------------\n");
217  }
218  for (size_t iph = 0; iph < m_numPhases; iph++) {
219  vcs_VolPhase* Vphase = m_VolPhaseList[iph];
220  int existence = Vphase->exists();
221  strcpy(anote, "");
222  if (existence > 0) {
223  if (m_debug_print_lvl >= 2) {
224  plogf(" --- %18s %5d NA %11.3e\n",
225  Vphase->PhaseName, existence, m_tPhaseMoles_old[iph]);
226  }
227  } else {
228  if (Vphase->m_singleSpecies) {
229  // Single Phase Stability Resolution
230  size_t kspec = Vphase->spGlobalIndexVCS(0);
231  size_t irxn = kspec - m_numComponents;
232  if (irxn > m_deltaGRxn_old.size()) {
233  throw CanteraError("VCS_SOLVE::vcs_popPhaseID",
234  "Index out of bounds due to logic error.");
235  }
236  doublereal deltaGRxn = m_deltaGRxn_old[irxn];
237  Fephase = exp(-deltaGRxn) - 1.0;
238  if (Fephase > 0.0) {
239  strcpy(anote," (ready to be birthed)");
240  if (Fephase > FephaseMax) {
241  iphasePop = iph;
242  FephaseMax = Fephase;
243  strcpy(anote," (chosen to be birthed)");
244  }
245  }
246  if (Fephase < 0.0) {
247  strcpy(anote," (not stable)");
248  AssertThrowMsg(m_tPhaseMoles_old[iph] <= 0.0,
249  "VCS_SOLVE::vcs_popPhaseID", "shouldn't be here");
250  }
251 
252  if (m_debug_print_lvl >= 2) {
253  plogf(" --- %18s %5d %10.3g %10.3g %s\n",
254  Vphase->PhaseName, existence, Fephase,
255  m_tPhaseMoles_old[iph], anote);
256  }
257  } else {
258  // MultiSpecies Phase Stability Resolution
259  if (vcs_popPhasePossible(iph)) {
260  Fephase = vcs_phaseStabilityTest(iph);
261  if (Fephase > 0.0) {
262  if (Fephase > FephaseMax) {
263  iphasePop = iph;
264  FephaseMax = Fephase;
265  }
266  } else {
267  FephaseMax = std::max(FephaseMax, Fephase);
268  }
269  if (m_debug_print_lvl >= 2) {
270  plogf(" --- %18s %5d %11.3g %11.3g\n",
271  Vphase->PhaseName, existence, Fephase,
272  m_tPhaseMoles_old[iph]);
273  }
274  } else {
275  if (m_debug_print_lvl >= 2) {
276  plogf(" --- %18s %5d blocked %11.3g\n",
277  Vphase->PhaseName,
278  existence, m_tPhaseMoles_old[iph]);
279  }
280  }
281  }
282  }
283  }
284  phasePopPhaseIDs.resize(0);
285  if (iphasePop != npos) {
286  phasePopPhaseIDs.push_back(iphasePop);
287  }
288 
289  // Insert logic here to figure out if phase pops are linked together. Only
290  // do one linked pop at a time.
291  if (m_debug_print_lvl >= 2) {
292  plogf(" ---------------------------------------------------------------------\n");
293  }
294  return iphasePop;
295 }
296 
297 int VCS_SOLVE::vcs_popPhaseRxnStepSizes(const size_t iphasePop)
298 {
299  vcs_VolPhase* Vphase = m_VolPhaseList[iphasePop];
300  // Identify the first species in the phase
301  size_t kspec = Vphase->spGlobalIndexVCS(0);
302  // Identify the formation reaction for that species
303  size_t irxn = kspec - m_numComponents;
304  std::vector<size_t> creationGlobalRxnNumbers;
305 
306  // Calculate the initial moles of the phase being born.
307  // Here we set it to 10x of the value which would cause the phase to be
308  // zeroed out within the algorithm. We may later adjust the value.
309  doublereal tPhaseMoles = 10. * m_totalMolNum * VCS_DELETE_PHASE_CUTOFF;
310 
311  AssertThrowMsg(!Vphase->exists(), "VCS_SOLVE::vcs_popPhaseRxnStepSizes",
312  "called for a phase that exists!");
313  if (m_debug_print_lvl >= 2) {
314  plogf(" --- vcs_popPhaseRxnStepSizes() called to pop phase %s %d into existence\n",
315  Vphase->PhaseName, iphasePop);
316  }
317  // Section for a single-species phase
318  if (Vphase->m_singleSpecies) {
319  double s = 0.0;
320  for (size_t j = 0; j < m_numComponents; ++j) {
321  if (!m_SSPhase[j] && m_molNumSpecies_old[j] > 0.0) {
322  s += pow(m_stoichCoeffRxnMatrix(j,irxn), 2) / m_molNumSpecies_old[j];
323  }
324  }
325  for (size_t j = 0; j < m_numPhases; j++) {
326  Vphase = m_VolPhaseList[j];
327  if (! Vphase->m_singleSpecies && m_tPhaseMoles_old[j] > 0.0) {
328  s -= pow(m_deltaMolNumPhase(j,irxn), 2) / m_tPhaseMoles_old[j];
329  }
330  }
331  if (s != 0.0) {
332  double s_old = s;
333  s = vcs_Hessian_diag_adj(irxn, s_old);
334  m_deltaMolNumSpecies[kspec] = -m_deltaGRxn_new[irxn] / s;
335  } else {
336  // Ok, s is equal to zero. We can not apply a sophisticated theory
337  // to birth the phase. Just pick a small delta and go with it.
338  m_deltaMolNumSpecies[kspec] = tPhaseMoles;
339  }
340 
341  // section to do damping of the m_deltaMolNumSpecies[]
342  for (size_t j = 0; j < m_numComponents; ++j) {
343  double stoicC = m_stoichCoeffRxnMatrix(j,irxn);
344  if (stoicC != 0.0 && m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
345  double negChangeComp = - stoicC * m_deltaMolNumSpecies[kspec];
346  if (negChangeComp > m_molNumSpecies_old[j]) {
347  if (m_molNumSpecies_old[j] > 0.0) {
348  m_deltaMolNumSpecies[kspec] = - 0.5 * m_molNumSpecies_old[j] / stoicC;
349  } else {
350  m_deltaMolNumSpecies[kspec] = 0.0;
351  }
352  }
353  }
354  }
355  // Implement a damping term that limits m_deltaMolNumSpecies to the size
356  // of the mole number
357  if (-m_deltaMolNumSpecies[kspec] > m_molNumSpecies_old[kspec]) {
358  m_deltaMolNumSpecies[kspec] = -m_molNumSpecies_old[kspec];
359  }
360  } else {
361  vector_fp fracDelta(Vphase->nSpecies());
362  vector_fp X_est(Vphase->nSpecies());
363  fracDelta = Vphase->creationMoleNumbers(creationGlobalRxnNumbers);
364 
365  double sumFrac = 0.0;
366  for (size_t k = 0; k < Vphase->nSpecies(); k++) {
367  sumFrac += fracDelta[k];
368  }
369  for (size_t k = 0; k < Vphase->nSpecies(); k++) {
370  X_est[k] = fracDelta[k] / sumFrac;
371  }
372 
373  doublereal deltaMolNumPhase = tPhaseMoles;
374  doublereal damp = 1.0;
375  m_deltaGRxn_tmp = m_molNumSpecies_old;
376  double* molNumSpecies_tmp = m_deltaGRxn_tmp.data();
377 
378  for (size_t k = 0; k < Vphase->nSpecies(); k++) {
379  kspec = Vphase->spGlobalIndexVCS(k);
380  double delmol = deltaMolNumPhase * X_est[k];
381  if (kspec >= m_numComponents) {
382  irxn = kspec - m_numComponents;
383  if (irxn > m_stoichCoeffRxnMatrix.nColumns()) {
384  throw CanteraError("VCS_SOLVE::vcs_popPhaseRxnStepSizes",
385  "Index out of bounds due to logic error.");
386  }
387  for (size_t j = 0; j < m_numComponents; ++j) {
388  double stoicC = m_stoichCoeffRxnMatrix(j,irxn);
389  if (stoicC != 0.0 && m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
390  molNumSpecies_tmp[j] += stoicC * delmol;
391  }
392  }
393  }
394  }
395 
396  doublereal ratioComp = 0.0;
397  for (size_t j = 0; j < m_numComponents; ++j) {
398  double deltaJ = m_molNumSpecies_old[j] - molNumSpecies_tmp[j];
399  if (molNumSpecies_tmp[j] < 0.0) {
400  ratioComp = 1.0;
401  if (deltaJ > 0.0) {
402  double delta0 = m_molNumSpecies_old[j];
403  damp = std::min(damp, delta0 / deltaJ * 0.9);
404  }
405  } else {
406  if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
407  size_t jph = m_phaseID[j];
408  if ((jph != iphasePop) && (!m_SSPhase[j])) {
409  double fdeltaJ = fabs(deltaJ);
410  if (m_molNumSpecies_old[j] > 0.0) {
411  ratioComp = std::max(ratioComp, fdeltaJ/ m_molNumSpecies_old[j]);
412  }
413  }
414  }
415  }
416  }
417 
418  // We may have greatly underestimated the deltaMoles for the phase pop
419  // Here we create a damp > 1 to account for this possibility. We adjust
420  // upwards to make sure that a component in an existing multispecies
421  // phase is modified by a factor of 1/1000.
422  if (ratioComp > 1.0E-30 && ratioComp < 0.001) {
423  damp = 0.001 / ratioComp;
424  }
425  if (damp <= 1.0E-6) {
426  return 3;
427  }
428 
429  for (size_t k = 0; k < Vphase->nSpecies(); k++) {
430  kspec = Vphase->spGlobalIndexVCS(k);
431  if (kspec < m_numComponents) {
432  m_speciesStatus[kspec] = VCS_SPECIES_COMPONENT;
433  } else {
434  m_deltaMolNumSpecies[kspec] = deltaMolNumPhase * X_est[k] * damp;
435  if (X_est[k] > 1.0E-3) {
436  m_speciesStatus[kspec] = VCS_SPECIES_MAJOR;
437  } else {
438  m_speciesStatus[kspec] = VCS_SPECIES_MINOR;
439  }
440  }
441  }
442  }
443  return 0;
444 }
445 
446 double VCS_SOLVE::vcs_phaseStabilityTest(const size_t iph)
447 {
448  // We will use the _new state calc here
449  vcs_VolPhase* Vphase = m_VolPhaseList[iph];
450  const size_t nsp = Vphase->nSpecies();
451  int minNumberIterations = 3;
452  if (nsp <= 1) {
453  minNumberIterations = 1;
454  }
455 
456  // We will do a full Newton calculation later, but for now, ...
457  bool doSuccessiveSubstitution = true;
458  double funcPhaseStability;
459  vector_fp X_est(nsp, 0.0);
460  vector_fp delFrac(nsp, 0.0);
461  vector_fp E_phi(nsp, 0.0);
462  vector_fp fracDelta_old(nsp, 0.0);
463  vector_fp fracDelta_raw(nsp, 0.0);
464  vector<size_t> creationGlobalRxnNumbers(nsp, npos);
465  m_deltaGRxn_Deficient = m_deltaGRxn_old;
466  vector_fp feSpecies_Deficient = m_feSpecies_old;
467 
468  // get the activity coefficients
469  Vphase->sendToVCS_ActCoeff(VCS_STATECALC_OLD, &m_actCoeffSpecies_new[0]);
470 
471  // Get the stored estimate for the composition of the phase if
472  // it gets created
473  vector_fp fracDelta_new = Vphase->creationMoleNumbers(creationGlobalRxnNumbers);
474 
475  std::vector<size_t> componentList;
476  for (size_t k = 0; k < nsp; k++) {
477  size_t kspec = Vphase->spGlobalIndexVCS(k);
478  if (kspec < m_numComponents) {
479  componentList.push_back(k);
480  }
481  }
482 
483  double normUpdate = 0.1 * vcs_l2norm(fracDelta_new);
484  double damp = 1.0E-2;
485 
486  if (doSuccessiveSubstitution) {
487  int KP = 0;
488  if (m_debug_print_lvl >= 2) {
489  plogf(" --- vcs_phaseStabilityTest() called\n");
490  plogf(" --- Its X_old[%2d] FracDel_old[%2d] deltaF[%2d] FracDel_new[%2d]"
491  " normUpdate damp FuncPhaseStability\n", KP, KP, KP, KP);
492  plogf(" --------------------------------------------------------------"
493  "--------------------------------------------------------\n");
494  } else if (m_debug_print_lvl == 1) {
495  plogf(" --- vcs_phaseStabilityTest() called for phase %d\n", iph);
496  }
497 
498  for (size_t k = 0; k < nsp; k++) {
499  if (fracDelta_new[k] < 1.0E-13) {
500  fracDelta_new[k] =1.0E-13;
501  }
502  }
503  bool converged = false;
504  double dirProd = 0.0;
505  for (int its = 0; its < 200 && (!converged); its++) {
506  double dampOld = damp;
507  double normUpdateOld = normUpdate;
508  fracDelta_old = fracDelta_new;
509  double dirProdOld = dirProd;
510 
511  // Given a set of fracDelta's, we calculate the fracDelta's
512  // for the component species, if any
513  for (size_t i = 0; i < componentList.size(); i++) {
514  size_t kc = componentList[i];
515  size_t kc_spec = Vphase->spGlobalIndexVCS(kc);
516  fracDelta_old[kc] = 0.0;
517  for (size_t k = 0; k < nsp; k++) {
518  size_t kspec = Vphase->spGlobalIndexVCS(k);
519  if (kspec >= m_numComponents) {
520  size_t irxn = kspec - m_numComponents;
521  fracDelta_old[kc] += m_stoichCoeffRxnMatrix(kc_spec,irxn) * fracDelta_old[k];
522  }
523  }
524  }
525 
526  // Now, calculate the predicted mole fractions, X_est[k]
527  double sumFrac = 0.0;
528  for (size_t k = 0; k < nsp; k++) {
529  sumFrac += fracDelta_old[k];
530  }
531  // Necessary because this can be identically zero. -> we need to fix
532  // this algorithm!
533  if (sumFrac <= 0.0) {
534  sumFrac = 1.0;
535  }
536  double sum_Xcomp = 0.0;
537  for (size_t k = 0; k < nsp; k++) {
538  X_est[k] = fracDelta_old[k] / sumFrac;
539  if (Vphase->spGlobalIndexVCS(k) < m_numComponents) {
540  sum_Xcomp += X_est[k];
541  }
542  }
543 
544  // Feed the newly formed estimate of the mole fractions back into the
545  // ThermoPhase object
546  Vphase->setMoleFractionsState(0.0, &X_est[0], VCS_STATECALC_PHASESTABILITY);
547 
548  // get the activity coefficients
549  Vphase->sendToVCS_ActCoeff(VCS_STATECALC_OLD, &m_actCoeffSpecies_new[0]);
550 
551  // First calculate altered chemical potentials for component species
552  // belonging to this phase.
553  for (size_t i = 0; i < componentList.size(); i++) {
554  size_t kc = componentList[i];
555  size_t kc_spec = Vphase->spGlobalIndexVCS(kc);
556  if (X_est[kc] > VCS_DELETE_MINORSPECIES_CUTOFF) {
557  feSpecies_Deficient[kc_spec] = m_feSpecies_old[kc_spec]
558  + log(m_actCoeffSpecies_new[kc_spec] * X_est[kc]);
559  } else {
560  feSpecies_Deficient[kc_spec] = m_feSpecies_old[kc_spec]
561  + log(m_actCoeffSpecies_new[kc_spec] * VCS_DELETE_MINORSPECIES_CUTOFF);
562  }
563  }
564 
565  for (size_t i = 0; i < componentList.size(); i++) {
566  size_t kc_spec = Vphase->spGlobalIndexVCS(componentList[i]);
567  for (size_t k = 0; k < Vphase->nSpecies(); k++) {
568  size_t kspec = Vphase->spGlobalIndexVCS(k);
569  if (kspec >= m_numComponents) {
570  size_t irxn = kspec - m_numComponents;
571  if (i == 0) {
572  m_deltaGRxn_Deficient[irxn] = m_deltaGRxn_old[irxn];
573  }
574  if (m_stoichCoeffRxnMatrix(kc_spec,irxn) != 0.0) {
575  m_deltaGRxn_Deficient[irxn] +=
576  m_stoichCoeffRxnMatrix(kc_spec,irxn) * (feSpecies_Deficient[kc_spec]- m_feSpecies_old[kc_spec]);
577  }
578  }
579  }
580  }
581 
582  // Calculate the E_phi's
583  double sum = 0.0;
584  funcPhaseStability = sum_Xcomp - 1.0;
585  for (size_t k = 0; k < nsp; k++) {
586  size_t kspec = Vphase->spGlobalIndexVCS(k);
587  if (kspec >= m_numComponents) {
588  size_t irxn = kspec - m_numComponents;
589  double deltaGRxn = clip(m_deltaGRxn_Deficient[irxn], -50.0, 50.0);
590  E_phi[k] = std::exp(-deltaGRxn) / m_actCoeffSpecies_new[kspec];
591  sum += E_phi[k];
592  funcPhaseStability += E_phi[k];
593  } else {
594  E_phi[k] = 0.0;
595  }
596  }
597 
598  // Calculate the raw estimate of the new fracs
599  for (size_t k = 0; k < nsp; k++) {
600  size_t kspec = Vphase->spGlobalIndexVCS(k);
601  double b = E_phi[k] / sum * (1.0 - sum_Xcomp);
602  if (kspec >= m_numComponents) {
603  fracDelta_raw[k] = b;
604  }
605  }
606 
607  // Given a set of fracDelta's, we calculate the fracDelta's
608  // for the component species, if any
609  for (size_t i = 0; i < componentList.size(); i++) {
610  size_t kc = componentList[i];
611  size_t kc_spec = Vphase->spGlobalIndexVCS(kc);
612  fracDelta_raw[kc] = 0.0;
613  for (size_t k = 0; k < nsp; k++) {
614  size_t kspec = Vphase->spGlobalIndexVCS(k);
615  if (kspec >= m_numComponents) {
616  size_t irxn = kspec - m_numComponents;
617  fracDelta_raw[kc] += m_stoichCoeffRxnMatrix(kc_spec,irxn) * fracDelta_raw[k];
618  }
619  }
620  }
621 
622  // Now possibly dampen the estimate.
623  doublereal sumADel = 0.0;
624  for (size_t k = 0; k < nsp; k++) {
625  delFrac[k] = fracDelta_raw[k] - fracDelta_old[k];
626  sumADel += fabs(delFrac[k]);
627  }
628  normUpdate = vcs_l2norm(delFrac);
629 
630  dirProd = 0.0;
631  for (size_t k = 0; k < nsp; k++) {
632  dirProd += fracDelta_old[k] * delFrac[k];
633  }
634  bool crossedSign = false;
635  if (dirProd * dirProdOld < 0.0) {
636  crossedSign = true;
637  }
638 
639  damp = 0.5;
640  if (dampOld < 0.25) {
641  damp = 2.0 * dampOld;
642  }
643  if (crossedSign) {
644  if (normUpdate *1.5 > normUpdateOld) {
645  damp = 0.5 * dampOld;
646  } else if (normUpdate *2.0 > normUpdateOld) {
647  damp = 0.8 * dampOld;
648  }
649  } else {
650  if (normUpdate > normUpdateOld * 2.0) {
651  damp = 0.6 * dampOld;
652  } else if (normUpdate > normUpdateOld * 1.2) {
653  damp = 0.9 * dampOld;
654  }
655  }
656 
657  for (size_t k = 0; k < nsp; k++) {
658  if (fabs(damp * delFrac[k]) > 0.3*fabs(fracDelta_old[k])) {
659  damp = std::max(0.3*fabs(fracDelta_old[k]) / fabs(delFrac[k]),
660  1.0E-8/fabs(delFrac[k]));
661  }
662  if (delFrac[k] < 0.0 && 2.0 * damp * (-delFrac[k]) > fracDelta_old[k]) {
663  damp = fracDelta_old[k] / (2.0 * -delFrac[k]);
664  }
665  if (delFrac[k] > 0.0 && 2.0 * damp * delFrac[k] > fracDelta_old[k]) {
666  damp = fracDelta_old[k] / (2.0 * delFrac[k]);
667  }
668  }
669  damp = std::max(damp, 0.000001);
670  for (size_t k = 0; k < nsp; k++) {
671  fracDelta_new[k] = fracDelta_old[k] + damp * delFrac[k];
672  }
673 
674  if (m_debug_print_lvl >= 2) {
675  plogf(" --- %3d %12g %12g %12g %12g %12g %12g %12g\n", its, X_est[KP], fracDelta_old[KP],
676  delFrac[KP], fracDelta_new[KP], normUpdate, damp, funcPhaseStability);
677  }
678 
679  if (normUpdate < 1.0E-5 * damp) {
680  converged = true;
681  if (its < minNumberIterations) {
682  converged = false;
683  }
684  }
685  }
686 
687  if (converged) {
688  // Save the final optimized stated back into the VolPhase object for later use
689  Vphase->setMoleFractionsState(0.0, &X_est[0], VCS_STATECALC_PHASESTABILITY);
690 
691  // Save fracDelta for later use to initialize the problem better
692  // @TODO creationGlobalRxnNumbers needs to be calculated here and stored.
693  Vphase->setCreationMoleNumbers(&fracDelta_new[0], creationGlobalRxnNumbers);
694  }
695  } else {
696  throw CanteraError("VCS_SOLVE::vcs_phaseStabilityTest", "not done yet");
697  }
698  if (m_debug_print_lvl >= 2) {
699  plogf(" ------------------------------------------------------------"
700  "-------------------------------------------------------------\n");
701  } else if (m_debug_print_lvl == 1) {
702  if (funcPhaseStability > 0.0) {
703  plogf(" --- phase %d with func = %g is to be born\n", iph, funcPhaseStability);
704  } else {
705  plogf(" --- phase %d with func = %g stays dead\n", iph, funcPhaseStability);
706  }
707  }
708  return funcPhaseStability;
709 }
710 
711 }
void setCreationMoleNumbers(const double *const n_k, const std::vector< size_t > &creationGlobalRxnNumbers)
Sets the creationMoleNum&#39;s within the phase object.
bool m_singleSpecies
If true, this phase consists of a single species.
Definition: vcs_VolPhase.h:566
void setMoleFractionsState(const double molNum, const double *const moleFracVec, const int vcsStateStatus)
Set the moles and/or mole fractions within the phase.
#define VCS_SPECIES_MINOR
Species is a major species.
Definition: vcs_defs.h:129
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:165
#define VCS_DELETE_ELEMENTABS_CUTOFF
Cutoff moles below which a phase or species which comprises the bulk of an element&#39;s total concentrat...
Definition: vcs_defs.h:79
T clip(const T &value, const T &lower, const T &upper)
Clip value such that lower <= value <= upper.
Definition: global.h:295
void warn_deprecated(const std::string &method, const std::string &extra)
Print a warning indicating that method is deprecated.
Definition: global.cpp:54
size_t spGlobalIndexVCS(const size_t spIndex) const
Return the Global VCS index of the kth species in the phase.
STL namespace.
const vector_fp & creationMoleNumbers(std::vector< size_t > &creationGlobalRxnNumbers) const
Return a const reference to the creationMoleNumbers stored in the object.
std::string PhaseName
String name for the phase.
Definition: vcs_VolPhase.h:653
#define VCS_STATECALC_PHASESTABILITY
State Calculation based on tentative mole numbers for a phase which is currently zeroed, but is being evaluated for whether it should pop back into existence.
Definition: vcs_defs.h:328
Header file for the internal object that holds the vcs equilibrium problem (see Class VCS_SOLVE and E...
Header for the object representing each phase within vcs.
#define VCS_ELEM_TYPE_ABSPOS
Normal element constraint consisting of positive coefficients for the formula matrix.
Definition: vcs_defs.h:248
#define VCS_DELETE_MINORSPECIES_CUTOFF
Cutoff relative mole number value, below which species are deleted from the equilibrium problem...
Definition: vcs_defs.h:53
#define VCS_DELETE_PHASE_CUTOFF
Cutoff relative moles below which a phase is deleted from the equilibrium problem.
Definition: vcs_defs.h:65
size_t nSpecies() const
Return the number of species in the phase.
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:65
void sendToVCS_ActCoeff(const int stateCalc, double *const AC)
Fill in an activity coefficients vector within a VCS_SOLVE object.
#define VCS_SPECIES_MAJOR
Species is a major species.
Definition: vcs_defs.h:122
Phase information and Phase calculations for vcs.
Definition: vcs_VolPhase.h:81
#define AssertThrowMsg(expr, procedure,...)
Assertion must be true or an error is thrown.
Definition: ctexceptions.h:270
double vcs_l2norm(const vector_fp &vec)
determine the l2 norm of a vector of doubles
Definition: vcs_util.cpp:21
#define VCS_STATECALC_OLD
State Calculation based on the old or base mole numbers.
Definition: vcs_defs.h:320
int exists() const
int indicating whether the phase exists or not
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
Contains declarations for string manipulation functions within Cantera.
#define plogf
define this Cantera function to replace printf
Definition: vcs_internal.h:18
#define VCS_SPECIES_COMPONENT
Species is a component which can be nonzero.
Definition: vcs_defs.h:115
Namespace for the Cantera kernel.
Definition: application.cpp:29
Definitions for the classes that are thrown when Cantera experiences an error condition (also contain...