Cantera  2.1.2
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 VCSnonideal::VCS_SOLVE VCS_SOLVE\endlink and \ref equilfunctions ).
5  */
10 
11 #include <cstdio>
12 #include <algorithm>
13 
14 using namespace std;
15 
16 namespace VCSnonideal
17 {
18 
19 bool VCS_SOLVE::vcs_popPhasePossible(const size_t iphasePop) const
20 {
21  vcs_VolPhase* Vphase = m_VolPhaseList[iphasePop];
22 
23 #ifdef DEBUG_MODE
24  int existence = Vphase->exists();
25  if (existence > 0) {
26  printf("ERROR vcs_popPhasePossible called for a phase that exists!");
27  std::exit(-1);
28  }
29 #endif
30 
31  /*
32  * Loop through all of the species in the phase. We say the phase
33  * can be popped, if there is one species in the phase that can be
34  * popped.
35  */
36  for (size_t k = 0; k < Vphase->nSpecies(); k++) {
37  size_t kspec = Vphase->spGlobalIndexVCS(k);
38 #ifdef DEBUG_MODE
39  if (m_molNumSpecies_old[kspec] > 0.0) {
40  printf("ERROR vcs_popPhasePossible we shouldn't be here %lu %g > 0.0",
41  kspec, m_molNumSpecies_old[kspec]);
42  exit(-1);
43  }
44 #endif
45  size_t irxn = kspec - m_numComponents;
46  if (kspec >= m_numComponents) {
47  bool iPopPossible = true;
48  for (size_t j = 0; j < m_numComponents; ++j) {
49  if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
50  double stoicC = m_stoichCoeffRxnMatrix[irxn][j];
51  if (stoicC != 0.0) {
52  double negChangeComp = - stoicC * 1.0;
53  if (negChangeComp > 0.0) {
54  // TODO: We may have to come up with a tolerance here
55  if (m_molNumSpecies_old[j] <= VCS_DELETE_ELEMENTABS_CUTOFF*0.5) {
56  iPopPossible = false;
57  }
58  }
59  }
60  }
61  }
62  if (iPopPossible) {
63  return true;
64  }
65  } else {
66  /*
67  * We are here when the species in the phase is a component. Its mole number is zero.
68  * We loop through the regular reaction looking for a reaction that can pop the
69  * component.
70  */
71  //printf("WE are here at new logic - CHECK\n");
72  for (size_t jrxn = 0; jrxn < m_numRxnRdc; jrxn++) {
73  bool foundJrxn = false;
74  // First, if the component is a product of the reaction
75  if (m_stoichCoeffRxnMatrix[jrxn][kspec] > 0.0) {
76  foundJrxn = true;
77  for (size_t kcomp = 0; kcomp < m_numComponents; kcomp++) {
78  if (m_stoichCoeffRxnMatrix[jrxn][kcomp] < 0.0) {
79  if (m_molNumSpecies_old[kcomp] <= VCS_DELETE_ELEMENTABS_CUTOFF*0.5) {
80  foundJrxn = false;
81  }
82  }
83  }
84  if (foundJrxn) {
85  //printf("We have found a component phase pop! CHECK1 \n");
86  return true;
87  }
88  }
89  // Second we are here if the component is a reactant in the reaction, and the reaction goes backwards.
90  else if (m_stoichCoeffRxnMatrix[jrxn][kspec] < 0.0) {
91  foundJrxn = true;
92  size_t jspec = jrxn + m_numComponents;
93  if (m_molNumSpecies_old[jspec] <= VCS_DELETE_ELEMENTABS_CUTOFF*0.5) {
94  foundJrxn = false;
95  continue;
96  }
97  for (size_t kcomp = 0; kcomp < m_numComponents; kcomp++) {
98  if (m_stoichCoeffRxnMatrix[jrxn][kcomp] > 0.0) {
99  if (m_molNumSpecies_old[kcomp] <= VCS_DELETE_ELEMENTABS_CUTOFF*0.5) {
100  foundJrxn = false;
101  }
102  }
103  }
104  if (foundJrxn) {
105  //printf("We have found a component phase pop! CHECK2 \n");
106  return true;
107  }
108  }
109  }
110  }
111  }
112  return false;
113 }
114 
115 int VCS_SOLVE::vcs_phasePopDeterminePossibleList()
116 {
117  int nfound = 0;
118  vcs_VolPhase* Vphase = 0;
119  double stoicC;
120  double molComp;
121  std::vector<int> linkedPhases;
122  phasePopProblemLists_.clear();
123 
124  /*
125  * This is a vector over each component.
126  * For zeroed components it lists the phases, which are currently zeroed,
127  * which have a species with a positive stoichiometric value wrt the component.
128  * Therefore, we could pop the component species and pop that phase at the same time
129  * if we considered no other factors than keeping the component mole number positve.
130  *
131  * It does not count species with positive stoichiometric values if that species
132  * already has a positive mole number. The phase is already popped.
133  */
134  std::vector< std::vector<size_t> > zeroedComponentLinkedPhasePops(m_numComponents);
135  /*
136  * The logic below calculates zeroedComponentLinkedPhasePops
137  */
138  for (size_t j = 0; j < m_numComponents; j++) {
139  if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
140  molComp = m_molNumSpecies_old[j];
141  if (molComp <= 0.0) {
142  std::vector<size_t> &jList = zeroedComponentLinkedPhasePops[j];
143  size_t iph = m_phaseID[j];
144  jList.push_back(iph);
145  for (size_t irxn = 0; irxn < m_numRxnTot; irxn++) {
146  size_t kspec = irxn + m_numComponents;
147  iph = m_phaseID[kspec];
148  Vphase = m_VolPhaseList[iph];
149  int existence = Vphase->exists();
150  if (existence < 0) {
151  stoicC = m_stoichCoeffRxnMatrix[irxn][j];
152  if (stoicC > 0.0) {
153  if (std::find(jList.begin(), jList.end(), iph) != jList.end()) {
154  jList.push_back(iph);
155  }
156  }
157  }
158  }
159  }
160  }
161  }
162  /*
163  * This is a vector over each zeroed phase
164  * For zeroed phases, it lists the components, which are currently zeroed,
165  * which have a species with a negative stoichiometric value wrt one or more species in the phase.
166  * Cut out components which have a pos stoichiometric value with another species in the phase.
167  */
168  std::vector< std::vector<size_t> > zeroedPhaseLinkedZeroComponents(m_numPhases);
169  /*
170  * The logic below calculates zeroedPhaseLinkedZeroComponents
171  */
172  for (size_t iph = 0; iph < m_numPhases; iph++) {
173  std::vector<size_t> &iphList = zeroedPhaseLinkedZeroComponents[iph];
174  iphList.clear();
175  Vphase = m_VolPhaseList[iph];
176  int existence = Vphase->exists();
177  if (existence < 0) {
178 
179  linkedPhases.clear();
180  size_t nsp = Vphase->nSpecies();
181  for (size_t k = 0; k < nsp; k++) {
182  size_t kspec = Vphase->spGlobalIndexVCS(k);
183  size_t irxn = kspec - m_numComponents;
184 
185  for (size_t j = 0; j < m_numComponents; j++) {
186  if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
187  molComp = m_molNumSpecies_old[j];
188  if (molComp <= 0.0) {
189  stoicC = m_stoichCoeffRxnMatrix[irxn][j];
190  if (stoicC < 0.0) {
191  bool foundPos = false;
192  for (size_t kk = 0; kk < nsp; kk++) {
193  size_t kkspec = Vphase->spGlobalIndexVCS(kk);
194  if (kkspec >= m_numComponents) {
195  size_t iirxn = kkspec - m_numComponents;
196  if (m_stoichCoeffRxnMatrix[iirxn][j] > 0.0) {
197  foundPos = true;
198  }
199  }
200  }
201  if (!foundPos) {
202  if (std::find(iphList.begin(), iphList.end(), j) != iphList.end()) {
203  iphList.push_back(j);
204  }
205  }
206  }
207  }
208  }
209  }
210  }
211  }
212  }
213 
214  /*
215  * Now fill in the phasePopProblemLists_ list.
216  *
217  */
218  for (size_t iph = 0; iph < m_numPhases; iph++) {
219  Vphase = m_VolPhaseList[iph];
220  int existence = Vphase->exists();
221  if (existence < 0) {
222  std::vector<size_t> &iphList = zeroedPhaseLinkedZeroComponents[iph];
223  std::vector<size_t> popProblem(0);
224  popProblem.push_back(iph);
225  for (size_t i = 0; i < iphList.size(); i++) {
226  size_t j = iphList[i];
227  std::vector<size_t> &jList = zeroedComponentLinkedPhasePops[j];
228  for (size_t jjl = 0; jjl < jList.size(); jjl++) {
229  size_t jph = jList[jjl];
230  if (std::find(popProblem.begin(), popProblem.end(), jph) != popProblem.end()) {
231  popProblem.push_back(jph);
232  }
233  }
234  }
235  phasePopProblemLists_.push_back(popProblem);
236  }
237  }
238 
239  return nfound;
240 }
241 
242 size_t VCS_SOLVE::vcs_popPhaseID(std::vector<size_t> & phasePopPhaseIDs)
243 {
244  size_t iphasePop = npos;
245  size_t irxn, kspec;
246  doublereal FephaseMax = -1.0E30;
247  doublereal Fephase = -1.0E30;
248  vcs_VolPhase* Vphase = 0;
249 
250 
251 #ifdef DEBUG_MODE
252  char anote[128];
253  if (m_debug_print_lvl >= 2) {
254  plogf(" --- vcs_popPhaseID() called\n");
255  plogf(" --- Phase Status F_e MoleNum\n");
256  plogf(" --------------------------------------------------------------------------\n");
257  }
258 #endif
259  for (size_t iph = 0; iph < m_numPhases; iph++) {
260  Vphase = m_VolPhaseList[iph];
261  int existence = Vphase->exists();
262 #ifdef DEBUG_MODE
263  strcpy(anote, "");
264 #endif
265  if (existence > 0) {
266 
267 #ifdef DEBUG_MODE
268  if (m_debug_print_lvl >= 2) {
269  plogf(" --- %18s %5d NA %11.3e\n",
270  Vphase->PhaseName.c_str(),
271  existence,
272  m_tPhaseMoles_old[iph]);
273  }
274 #endif
275  } else {
276  if (Vphase->m_singleSpecies) {
277  /***********************************************************************
278  *
279  * Single Phase Stability Resolution
280  *
281  ***********************************************************************/
282  kspec = Vphase->spGlobalIndexVCS(0);
283  irxn = kspec - m_numComponents;
284  doublereal deltaGRxn = m_deltaGRxn_old[irxn];
285  Fephase = exp(-deltaGRxn) - 1.0;
286  if (Fephase > 0.0) {
287 #ifdef DEBUG_MODE
288  strcpy(anote," (ready to be birthed)");
289 #endif
290  if (Fephase > FephaseMax) {
291  iphasePop = iph;
292  FephaseMax = Fephase;
293 #ifdef DEBUG_MODE
294  strcpy(anote," (chosen to be birthed)");
295 #endif
296  }
297  }
298 #ifdef DEBUG_MODE
299  if (Fephase < 0.0) {
300  strcpy(anote," (not stable)");
301  if (m_tPhaseMoles_old[iph] > 0.0) {
302  printf("shouldn't be here\n");
303  exit(-1);
304  }
305  }
306 #endif
307 
308 #ifdef DEBUG_MODE
309  if (m_debug_print_lvl >= 2) {
310  plogf(" --- %18s %5d %10.3g %10.3g %s\n",
311  Vphase->PhaseName.c_str(),
312  existence, Fephase,
313  m_tPhaseMoles_old[iph], anote);
314  }
315 #endif
316 
317  } else {
318  /***********************************************************************
319  *
320  * MultiSpecies Phase Stability Resolution
321  *
322  ***********************************************************************/
323  if (vcs_popPhasePossible(iph)) {
324  Fephase = vcs_phaseStabilityTest(iph);
325  if (Fephase > 0.0) {
326  if (Fephase > FephaseMax) {
327  iphasePop = iph;
328  FephaseMax = Fephase;
329  }
330  } else {
331  if (Fephase > FephaseMax) {
332  FephaseMax = Fephase;
333  }
334  }
335 #ifdef DEBUG_MODE
336  if (m_debug_print_lvl >= 2) {
337  plogf(" --- %18s %5d %11.3g %11.3g\n",
338  Vphase->PhaseName.c_str(),
339  existence, Fephase,
340  m_tPhaseMoles_old[iph]);
341  }
342 #endif
343  } else {
344 #ifdef DEBUG_MODE
345  if (m_debug_print_lvl >= 2) {
346  plogf(" --- %18s %5d blocked %11.3g\n",
347  Vphase->PhaseName.c_str(),
348  existence, m_tPhaseMoles_old[iph]);
349  }
350 #endif
351  }
352  }
353  }
354  }
355  phasePopPhaseIDs.resize(0);
356  if (iphasePop != npos) {
357  phasePopPhaseIDs.push_back(iphasePop);
358  }
359 
360  /*
361  * Insert logic here to figure out if phase pops are linked together. Only do one linked
362  * pop at a time.
363  */
364 
365 #ifdef DEBUG_MODE
366  if (m_debug_print_lvl >= 2) {
367  plogf(" ---------------------------------------------------------------------\n");
368  }
369 #endif
370  return iphasePop;
371 }
372 
373 int VCS_SOLVE::vcs_popPhaseRxnStepSizes(const size_t iphasePop)
374 {
375  vcs_VolPhase* Vphase = m_VolPhaseList[iphasePop];
376  // Identify the first species in the phase
377  size_t kspec = Vphase->spGlobalIndexVCS(0);
378  // Identify the formation reaction for that species
379  size_t irxn = kspec - m_numComponents;
380  std::vector<size_t> creationGlobalRxnNumbers;
381 
382  doublereal s;
383  // Calculate the initial moles of the phase being born.
384  // Here we set it to 10x of the value which would cause the phase to be
385  // zeroed out within the algorithm. We may later adjust the value.
386  doublereal tPhaseMoles = 10. * m_totalMolNum * VCS_DELETE_PHASE_CUTOFF;
387 
388 
389 #ifdef DEBUG_MODE
390  int existence = Vphase->exists();
391  if (existence > 0) {
392  printf("ERROR vcs_popPhaseRxnStepSizes called for a phase that exists!");
393  exit(-1);
394  }
395  char anote[256];
396  if (m_debug_print_lvl >= 2) {
397  plogf(" --- vcs_popPhaseRxnStepSizes() called to pop phase %s %d into existence\n",
398  Vphase->PhaseName.c_str(), iphasePop);
399  }
400 #endif
401  // Section for a single-species phase
402  if (Vphase->m_singleSpecies) {
403  s = 0.0;
404  double* dnPhase_irxn = m_deltaMolNumPhase[irxn];
405  for (size_t j = 0; j < m_numComponents; ++j) {
406  if (!m_SSPhase[j]) {
407  if (m_molNumSpecies_old[j] > 0.0) {
408  s += SQUARE(m_stoichCoeffRxnMatrix[irxn][j]) / m_molNumSpecies_old[j];
409  }
410  }
411  }
412  for (size_t j = 0; j < m_numPhases; j++) {
413  Vphase = m_VolPhaseList[j];
414  if (! Vphase->m_singleSpecies) {
415  if (m_tPhaseMoles_old[j] > 0.0) {
416  s -= SQUARE(dnPhase_irxn[j]) / m_tPhaseMoles_old[j];
417  }
418  }
419  }
420  if (s != 0.0) {
421  double s_old = s;
422  s = vcs_Hessian_diag_adj(irxn, s_old);
423 #ifdef DEBUG_MODE
424  if (s_old != s) {
425  sprintf(anote, "Normal calc: diag adjusted from %g "
426  "to %g due to act coeff", s_old, s);
427  }
428 #endif
429  m_deltaMolNumSpecies[kspec] = -m_deltaGRxn_new[irxn] / s;
430  } else {
431  // Ok, s is equal to zero. We can not apply a sophisticated theory
432  // to birth the phase. Just pick a small delta and go with it.
433  m_deltaMolNumSpecies[kspec] = tPhaseMoles;
434  }
435 
436  /*
437  * section to do damping of the m_deltaMolNumSpecies[]
438  */
439  for (size_t j = 0; j < m_numComponents; ++j) {
440  double stoicC = m_stoichCoeffRxnMatrix[irxn][j];
441  if (stoicC != 0.0) {
442  if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
443  double negChangeComp = - stoicC * m_deltaMolNumSpecies[kspec];
444  if (negChangeComp > m_molNumSpecies_old[j]) {
445  if (m_molNumSpecies_old[j] > 0.0) {
446 #ifdef DEBUG_MODE
447  sprintf(anote, "Delta damped from %g "
448  "to %g due to component %lu (%10s) going neg", m_deltaMolNumSpecies[kspec],
449  -m_molNumSpecies_old[j]/stoicC, j, m_speciesName[j].c_str());
450 #endif
451  m_deltaMolNumSpecies[kspec] = - 0.5 * m_molNumSpecies_old[j] / stoicC;
452  } else {
453 #ifdef DEBUG_MODE
454  sprintf(anote, "Delta damped from %g "
455  "to %g due to component %lu (%10s) zero", m_deltaMolNumSpecies[kspec],
456  -m_molNumSpecies_old[j]/stoicC, j, m_speciesName[j].c_str());
457 #endif
458  m_deltaMolNumSpecies[kspec] = 0.0;
459  }
460  }
461  }
462  }
463  }
464  // Implement a damping term that limits m_deltaMolNumSpecies to the size of the mole number
465  if (-m_deltaMolNumSpecies[kspec] > m_molNumSpecies_old[kspec]) {
466 #ifdef DEBUG_MODE
467  sprintf(anote, "Delta damped from %g "
468  "to %g due to %s going negative", m_deltaMolNumSpecies[kspec],
469  -m_molNumSpecies_old[kspec], m_speciesName[kspec].c_str());
470 #endif
471  m_deltaMolNumSpecies[kspec] = -m_molNumSpecies_old[kspec];
472  }
473 
474 
475  } else {
476  vector<doublereal> fracDelta(Vphase->nSpecies());
477  vector<doublereal> X_est(Vphase->nSpecies());
478  fracDelta = Vphase->creationMoleNumbers(creationGlobalRxnNumbers);
479 
480  double sumFrac = 0.0;
481  for (size_t k = 0; k < Vphase->nSpecies(); k++) {
482  sumFrac += fracDelta[k];
483  }
484  for (size_t k = 0; k < Vphase->nSpecies(); k++) {
485  X_est[k] = fracDelta[k] / sumFrac;
486  }
487 
488  doublereal deltaMolNumPhase = tPhaseMoles;
489  doublereal damp = 1.0;
490  m_deltaGRxn_tmp = m_molNumSpecies_old;
491  double* molNumSpecies_tmp = DATA_PTR(m_deltaGRxn_tmp);
492 
493 
494  for (size_t k = 0; k < Vphase->nSpecies(); k++) {
495  kspec = Vphase->spGlobalIndexVCS(k);
496  double delmol = deltaMolNumPhase * X_est[k];
497  if (kspec >= m_numComponents) {
498  irxn = kspec - m_numComponents;
499  for (size_t j = 0; j < m_numComponents; ++j) {
500  double stoicC = m_stoichCoeffRxnMatrix[irxn][j];
501  if (stoicC != 0.0) {
502  if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
503  molNumSpecies_tmp[j] += stoicC * delmol;
504  }
505  }
506  }
507  }
508  }
509 
510  doublereal ratioComp = 0.0;
511  for (size_t j = 0; j < m_numComponents; ++j) {
512  double deltaJ = m_molNumSpecies_old[j] - molNumSpecies_tmp[j];
513  if (molNumSpecies_tmp[j] < 0.0) {
514  ratioComp = 1.0;
515  if (deltaJ > 0.0) {
516  double delta0 = m_molNumSpecies_old[j];
517  double dampj = delta0 / deltaJ * 0.9;
518  if (dampj < damp) {
519  damp = dampj;
520  }
521  }
522  } else {
523  if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
524  size_t jph = m_phaseID[j];
525  if ((jph != iphasePop) && (!m_SSPhase[j])) {
526  double fdeltaJ = fabs(deltaJ);
527  if (m_molNumSpecies_old[j] > 0.0) {
528  ratioComp = std::max(ratioComp, fdeltaJ/ m_molNumSpecies_old[j]);
529  }
530  }
531  }
532  }
533  }
534 
535  // We may have greatly underestimated the deltaMoles for the phase pop
536  // Here we create a damp > 1 to account for this possibility.
537  // We adjust upwards to make sure that a component in an existing multispecies
538  // phase is modified by a factor of 1/1000.
539  if (ratioComp > 1.0E-30) {
540  if (ratioComp < 0.001) {
541  damp = 0.001 / ratioComp;
542  }
543  }
544 
545 
546  if (damp <= 1.0E-6) {
547  return 3;
548  }
549 
550  for (size_t k = 0; k < Vphase->nSpecies(); k++) {
551  kspec = Vphase->spGlobalIndexVCS(k);
552  if (kspec < m_numComponents) {
553  m_speciesStatus[kspec] = VCS_SPECIES_COMPONENT;
554  } else {
555  m_deltaMolNumSpecies[kspec] = deltaMolNumPhase * X_est[k] * damp;
556  if (X_est[k] > 1.0E-3) {
557  m_speciesStatus[kspec] = VCS_SPECIES_MAJOR;
558  } else {
559  m_speciesStatus[kspec] = VCS_SPECIES_MINOR;
560  }
561  }
562  }
563 
564  }
565 
566  return 0;
567 }
568 
569 double VCS_SOLVE::vcs_phaseStabilityTest(const size_t iph)
570 {
571  /*
572  * We will use the _new state calc here
573  */
574  size_t kspec, irxn, k, i, kc, kc_spec;
575  vcs_VolPhase* Vphase = m_VolPhaseList[iph];
576  const size_t nsp = Vphase->nSpecies();
577  doublereal deltaGRxn;
578  int minNumberIterations = 3;
579  if (nsp <= 1) {
580  minNumberIterations = 1;
581  }
582 
583  // We will do a full newton calculation later, but for now, ...
584  bool doSuccessiveSubstitution = true;
585  double funcPhaseStability;
586  vector<doublereal> X_est(nsp, 0.0);
587  vector<doublereal> delFrac(nsp, 0.0);
588  vector<doublereal> E_phi(nsp, 0.0);
589  vector<doublereal> fracDelta_new(nsp, 0.0);
590  vector<doublereal> fracDelta_old(nsp, 0.0);
591  vector<doublereal> fracDelta_raw(nsp, 0.0);
592  vector<size_t> creationGlobalRxnNumbers(nsp, npos);
593  vcs_dcopy(VCS_DATA_PTR(m_deltaGRxn_Deficient), VCS_DATA_PTR(m_deltaGRxn_old), m_numRxnRdc);
594 
595  vector<doublereal> m_feSpecies_Deficient(m_numComponents, 0.0);
596  doublereal damp = 1.0;
597  doublereal dampOld = 1.0;
598  doublereal normUpdate = 1.0;
599  doublereal normUpdateOld = 1.0;
600  doublereal sum = 0.0;
601  doublereal dirProd = 0.0;
602  doublereal dirProdOld = 0.0;
603 
604  // get the activity coefficients
605  Vphase->sendToVCS_ActCoeff(VCS_STATECALC_OLD, VCS_DATA_PTR(m_actCoeffSpecies_new));
606 
607  // Get the stored estimate for the composition of the phase if
608  // it gets created
609  fracDelta_new = Vphase->creationMoleNumbers(creationGlobalRxnNumbers);
610 
611 
612  std::vector<size_t> componentList;
613 
614  for (k = 0; k < nsp; k++) {
615  kspec = Vphase->spGlobalIndexVCS(k);
616  if (kspec < m_numComponents) {
617  componentList.push_back(k);
618  }
619  }
620 
621  for (k = 0; k < m_numComponents; k++) {
622  m_feSpecies_Deficient[k] = m_feSpecies_old[k];
623  }
624  normUpdate = 0.1 * vcs_l2norm(fracDelta_new);
625  damp = 1.0E-2;
626 
627  if (doSuccessiveSubstitution) {
628 
629 #ifdef DEBUG_MODE
630  int KP = 0;
631  if (m_debug_print_lvl >= 2) {
632  plogf(" --- vcs_phaseStabilityTest() called\n");
633  plogf(" --- Its X_old[%2d] FracDel_old[%2d] deltaF[%2d] FracDel_new[%2d]"
634  " normUpdate damp FuncPhaseStability\n", KP, KP, KP, KP);
635  plogf(" --------------------------------------------------------------"
636  "--------------------------------------------------------\n");
637  } else if (m_debug_print_lvl == 1) {
638  plogf(" --- vcs_phaseStabilityTest() called for phase %d\n", iph);
639  }
640 #endif
641 
642  for (k = 0; k < nsp; k++) {
643  if (fracDelta_new[k] < 1.0E-13) {
644  fracDelta_new[k] = 1.0E-13;
645  }
646  }
647  bool converged = false;
648  for (int its = 0; its < 200 && (!converged); its++) {
649 
650  dampOld = damp;
651  normUpdateOld = normUpdate;
652  fracDelta_old = fracDelta_new;
653  dirProdOld = dirProd;
654 
655  // Given a set of fracDelta's, we calculate the fracDelta's
656  // for the component species, if any
657  for (i = 0; i < componentList.size(); i++) {
658  kc = componentList[i];
659  kc_spec = Vphase->spGlobalIndexVCS(kc);
660  fracDelta_old[kc] = 0.0;
661  for (k = 0; k < nsp; k++) {
662  kspec = Vphase->spGlobalIndexVCS(k);
663  if (kspec >= m_numComponents) {
664  irxn = kspec - m_numComponents;
665  fracDelta_old[kc] += m_stoichCoeffRxnMatrix[irxn][kc_spec] * fracDelta_old[k];
666  }
667  }
668  }
669 
670  // Now, calculate the predicted mole fractions, X_est[k]
671  double sumFrac = 0.0;
672  for (k = 0; k < nsp; k++) {
673  sumFrac += fracDelta_old[k];
674  }
675  // Necessary because this can be identically zero. -> we need to fix this algorithm!
676  if (sumFrac <= 0.0) {
677  sumFrac = 1.0;
678  }
679  double sum_Xcomp = 0.0;
680  for (k = 0; k < nsp; k++) {
681  X_est[k] = fracDelta_old[k] / sumFrac;
682  kc_spec = Vphase->spGlobalIndexVCS(k);
683  if (kc_spec < m_numComponents) {
684  sum_Xcomp += X_est[k];
685  }
686  }
687 
688 
689  /*
690  * Feed the newly formed estimate of the mole fractions back into the
691  * ThermoPhase object
692  */
694 
695  /*
696  * get the activity coefficients
697  */
698  Vphase->sendToVCS_ActCoeff(VCS_STATECALC_OLD, VCS_DATA_PTR(m_actCoeffSpecies_new));
699 
700  /*
701  * First calculate altered chemical potentials for component species
702  * belonging to this phase.
703  */
704  for (i = 0; i < componentList.size(); i++) {
705  kc = componentList[i];
706  kc_spec = Vphase->spGlobalIndexVCS(kc);
707  if (X_est[kc] > VCS_DELETE_MINORSPECIES_CUTOFF) {
708  m_feSpecies_Deficient[kc_spec] = m_feSpecies_old[kc_spec]
709  + log(m_actCoeffSpecies_new[kc_spec] * X_est[kc]);
710  } else {
711  m_feSpecies_Deficient[kc_spec] = m_feSpecies_old[kc_spec]
712  + log(m_actCoeffSpecies_new[kc_spec] * VCS_DELETE_MINORSPECIES_CUTOFF);
713  }
714  }
715 
716  for (i = 0; i < componentList.size(); i++) {
717  kc = componentList[i];
718  kc_spec = Vphase->spGlobalIndexVCS(kc);
719 
720  for (k = 0; k < Vphase->nSpecies(); k++) {
721  kspec = Vphase->spGlobalIndexVCS(k);
722  if (kspec >= m_numComponents) {
723  irxn = kspec - m_numComponents;
724  if (i == 0) {
725  m_deltaGRxn_Deficient[irxn] = m_deltaGRxn_old[irxn];
726  }
727  double* dtmp_ptr = m_stoichCoeffRxnMatrix[irxn];
728  if (dtmp_ptr[kc_spec] != 0.0) {
729  m_deltaGRxn_Deficient[irxn] +=
730  dtmp_ptr[kc_spec] * (m_feSpecies_Deficient[kc_spec]- m_feSpecies_old[kc_spec]);
731  }
732  }
733 
734  }
735  }
736 
737  /*
738  * Calculate the E_phi's
739  */
740  sum = 0.0;
741  funcPhaseStability = sum_Xcomp - 1.0;
742  for (k = 0; k < nsp; k++) {
743  kspec = Vphase->spGlobalIndexVCS(k);
744  if (kspec >= m_numComponents) {
745  irxn = kspec - m_numComponents;
746  deltaGRxn = m_deltaGRxn_Deficient[irxn];
747  if (deltaGRxn > 50.0) {
748  deltaGRxn = 50.0;
749  }
750  if (deltaGRxn < -50.0) {
751  deltaGRxn = -50.0;
752  }
753  E_phi[k] = std::exp(-deltaGRxn) / m_actCoeffSpecies_new[kspec];
754  sum += E_phi[k];
755  funcPhaseStability += E_phi[k];
756  } else {
757  E_phi[k] = 0.0;
758  }
759  }
760 
761  /*
762  * Calculate the raw estimate of the new fracs
763  */
764  for (k = 0; k < nsp; k++) {
765  kspec = Vphase->spGlobalIndexVCS(k);
766  double b = E_phi[k] / sum * (1.0 - sum_Xcomp);
767  if (kspec >= m_numComponents) {
768  irxn = kspec - m_numComponents;
769  fracDelta_raw[k] = b;
770  }
771  }
772 
773 
774  // Given a set of fracDelta's, we calculate the fracDelta's
775  // for the component species, if any
776  for (i = 0; i < componentList.size(); i++) {
777  kc = componentList[i];
778  kc_spec = Vphase->spGlobalIndexVCS(kc);
779  fracDelta_raw[kc] = 0.0;
780  for (k = 0; k < nsp; k++) {
781  kspec = Vphase->spGlobalIndexVCS(k);
782  if (kspec >= m_numComponents) {
783  irxn = kspec - m_numComponents;
784  fracDelta_raw[kc] += m_stoichCoeffRxnMatrix[irxn][kc_spec] * fracDelta_raw[k];
785  }
786  }
787  }
788 
789 
790 
791  /*
792  * Now possibly dampen the estimate.
793  */
794  doublereal sumADel = 0.0;
795  for (k = 0; k < nsp; k++) {
796  delFrac[k] = fracDelta_raw[k] - fracDelta_old[k];
797  sumADel += fabs(delFrac[k]);
798  }
799  normUpdate = vcs_l2norm(delFrac);
800 
801  dirProd = 0.0;
802  for (k = 0; k < nsp; k++) {
803  dirProd += fracDelta_old[k] * delFrac[k];
804  }
805  bool crossedSign = false;
806  if (dirProd * dirProdOld < 0.0) {
807  crossedSign = true;
808  }
809 
810 
811  damp = 0.5;
812  if (dampOld < 0.25) {
813  damp = 2.0 * dampOld;
814  }
815  if (crossedSign) {
816  if (normUpdate *1.5 > normUpdateOld) {
817  damp = 0.5 * dampOld;
818  } else if (normUpdate *2.0 > normUpdateOld) {
819  damp = 0.8 * dampOld;
820  }
821  } else {
822  if (normUpdate > normUpdateOld * 2.0) {
823  damp = 0.6 * dampOld;
824  } else if (normUpdate > normUpdateOld * 1.2) {
825  damp = 0.9 * dampOld;
826  }
827  }
828 
829  for (k = 0; k < nsp; k++) {
830  if (fabs(damp * delFrac[k]) > 0.3*fabs(fracDelta_old[k])) {
831  damp = std::max(0.3*fabs(fracDelta_old[k]) / fabs(delFrac[k]),
832  1.0E-8/fabs(delFrac[k]));
833  }
834  if (delFrac[k] < 0.0) {
835  if (2.0 * damp * (-delFrac[k]) > fracDelta_old[k]) {
836  damp = fracDelta_old[k] / (2.0 * (-delFrac[k]));
837  }
838  }
839  if (delFrac[k] > 0.0) {
840  if (2.0 * damp * delFrac[k] > fracDelta_old[k]) {
841  damp = fracDelta_old[k] / (2.0 * delFrac[k]);
842  }
843  }
844  }
845  if (damp < 0.000001) {
846  damp = 0.000001;
847  }
848 
849  for (k = 0; k < nsp; k++) {
850  fracDelta_new[k] = fracDelta_old[k] + damp * (delFrac[k]);
851  }
852 
853 #ifdef DEBUG_MODE
854  if (m_debug_print_lvl >= 2) {
855  plogf(" --- %3d %12g %12g %12g %12g %12g %12g %12g\n", its, X_est[KP], fracDelta_old[KP],
856  delFrac[KP], fracDelta_new[KP], normUpdate, damp, funcPhaseStability);
857  }
858 #endif
859 
860  if (normUpdate < 1.0E-5 * damp) {
861  converged = true;
862  if (its < minNumberIterations) {
863  converged = false;
864  }
865  }
866 
867  }
868 
869  if (converged) {
870  /*
871  * Save the final optimized stated back into the VolPhase object for later use
872  */
874  /*
875  * Save fracDelta for later use to initialize the problem better
876  * @TODO creationGlobalRxnNumbers needs to be calculated here and storred.
877  */
878  Vphase->setCreationMoleNumbers(VCS_DATA_PTR(fracDelta_new), creationGlobalRxnNumbers);
879  }
880 
881 
882  } else {
883  printf("not done yet\n");
884  exit(-1);
885  }
886 #ifdef DEBUG_MODE
887  if (m_debug_print_lvl >= 2) {
888  plogf(" ------------------------------------------------------------"
889  "-------------------------------------------------------------\n");
890  } else if (m_debug_print_lvl == 1) {
891  if (funcPhaseStability > 0.0) {
892  plogf(" --- phase %d with func = %g is to be born\n", iph, funcPhaseStability);
893  } else {
894  plogf(" --- phase %d with func = %g stays dead\n", iph, funcPhaseStability);
895  }
896  }
897 #endif
898  return funcPhaseStability;
899 }
900 
901 }
#define VCS_SPECIES_MINOR
Species is a major species.
Definition: vcs_defs.h:151
std::string PhaseName
String name for the phase.
Definition: vcs_VolPhase.h:707
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:173
#define VCS_DELETE_ELEMENTABS_CUTOFF
Cutoff moles below which a phase or species which comprises the bulk of an element's total concentrat...
Definition: vcs_defs.h:100
#define VCS_DATA_PTR(vvv)
Points to the data in a std::vector<> object.
Definition: vcs_internal.h:24
#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:389
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:306
Phase information and Phase calculations for vcs.
Definition: vcs_VolPhase.h:97
#define VCS_DELETE_MINORSPECIES_CUTOFF
Cutoff relative mole number value, below which species are deleted from the equilibrium problem...
Definition: vcs_defs.h:73
Internal declarations for the VCSnonideal package.
void setCreationMoleNumbers(const double *const n_k, const std::vector< size_t > &creationGlobalRxnNumbers)
Sets the creationMoleNum's within the phase object.
const std::vector< double > & creationMoleNumbers(std::vector< size_t > &creationGlobalRxnNumbers) const
Return a const reference to the creationMoleNumbers stored in the object.
#define VCS_DELETE_PHASE_CUTOFF
Cutoff relative moles below which a phase is deleted from the equilibrium problem.
Definition: vcs_defs.h:85
size_t spGlobalIndexVCS(const size_t spIndex) const
Return the Global VCS index of the kth species in the phase.
#define VCS_SPECIES_MAJOR
Species is a major species.
Definition: vcs_defs.h:144
double vcs_l2norm(const std::vector< double > vec)
determine the l2 norm of a vector of doubles
Definition: vcs_util.cpp:69
size_t nSpecies() const
Return the number of species in the phase.
bool m_singleSpecies
If true, this phase consists of a single species.
Definition: vcs_VolPhase.h:595
#define VCS_STATECALC_OLD
State Calculation based on the old or base mole numbers.
Definition: vcs_defs.h:381
void sendToVCS_ActCoeff(const int stateCalc, double *const AC)
Fill in an activity coefficients vector within a VCS_SOLVE object.
#define DATA_PTR(vec)
Creates a pointer to the start of the raw data for a vector.
Definition: ct_defs.h:36
int exists() const
int indicating whether the phase exists or not
#define plogf
define this Cantera function to replace printf
Definition: vcs_internal.h:30
#define VCS_SPECIES_COMPONENT
Species is a component which can be nonzero.
Definition: vcs_defs.h:137
void setMoleFractionsState(const double molNum, const double *const moleFracVec, const int vcsStateStatus)
Set the moles and/or mole fractions within the phase.