Cantera  2.3.0
vcs_solve_TP.cpp
Go to the documentation of this file.
1 /**
2  * @file vcs_solve_TP.cpp Implementation file that contains the
3  * main algorithm for finding an equilibrium
4  */
5 
6 // This file is part of Cantera. See License.txt in the top-level directory or
7 // at http://www.cantera.org/license.txt for license and copyright information.
8 
12 #include "cantera/base/clockWC.h"
15 
16 #include <cstdio>
17 
18 using namespace std;
19 namespace {
20 enum stages {MAIN, EQUILIB_CHECK, ELEM_ABUND_CHECK,
21  RECHECK_DELETED, RETURN_A, RETURN_B};
22 }
23 
24 namespace Cantera
25 {
26 
27 void VCS_SOLVE::checkDelta1(double* const dsLocal,
28  double* const delTPhMoles, size_t kspec)
29 {
30  vector_fp dchange(m_numPhases, 0.0);
31  for (size_t k = 0; k < kspec; k++) {
32  if (m_speciesUnknownType[k] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
33  size_t iph = m_phaseID[k];
34  dchange[iph] += dsLocal[k];
35  }
36  }
37  for (size_t iphase = 0; iphase < m_numPhases; iphase++) {
38  double denom = max(m_totalMolNum, 1.0E-4);
39  if (!vcs_doubleEqual(dchange[iphase]/denom, delTPhMoles[iphase]/denom)) {
40  throw CanteraError("VCS_SOLVE::checkDelta1",
41  "we have found a problem");
42  }
43  }
44 }
45 
46 int VCS_SOLVE::vcs_solve_TP(int print_lvl, int printDetails, int maxit)
47 {
48  int stage = MAIN;
49  bool allMinorZeroedSpecies = false;
50  size_t it1 = 0;
51  size_t iti;
52  int rangeErrorFound = 0;
53  bool giveUpOnElemAbund = false;
54  int finalElemAbundAttempts = 0;
55  bool uptodate_minors = true;
56  int forceComponentCalc = 1;
57 
58  char ANOTE[128];
59  // Set the debug print lvl to the same as the print lvl.
60  m_debug_print_lvl = printDetails;
61  if (printDetails > 0 && print_lvl == 0) {
62  print_lvl = 1;
63  }
64  // Initialize and set up all counters
65  vcs_counters_init(0);
66  clockWC ticktock;
67 
68  // temporary space for usage in this routine and in subroutines
69  m_sm.assign(m_numElemConstraints*m_numElemConstraints, 0.0);
70  m_ss.assign(m_numElemConstraints, 0.0);
71  m_sa.assign(m_numElemConstraints, 0.0);
72  m_aw.assign(m_numSpeciesTot, 0.0);
73  m_wx.assign(m_numElemConstraints, 0.0);
74 
75  int solveFail = false;
76 
77  // Evaluate the elemental composition
78  vcs_elab();
79 
80  // Printout the initial conditions for problem
81  if (print_lvl != 0) {
82  plogf("VCS CALCULATION METHOD\n\n ");
83  plogf("%s\n", m_title);
84  plogf("\n\n%5d SPECIES\n%5d ELEMENTS\n", m_numSpeciesTot, m_numElemConstraints);
85  plogf("%5d COMPONENTS\n", m_numComponents);
86  plogf("%5d PHASES\n", m_numPhases);
87  plogf(" PRESSURE%22.8g %3s\n", m_pressurePA, "Pa ");
88  plogf(" TEMPERATURE%19.3f K\n", m_temperature);
89  vcs_VolPhase* Vphase = m_VolPhaseList[0];
90  if (Vphase->nSpecies() > 0) {
91  plogf(" PHASE1 INERTS%17.3f\n", TPhInertMoles[0]);
92  }
93  if (m_numPhases > 1) {
94  plogf(" PHASE2 INERTS%17.3f\n", TPhInertMoles[1]);
95  }
96  plogf("\n ELEMENTAL ABUNDANCES CORRECT");
97  plogf(" FROM ESTIMATE Type\n\n");
98  for (size_t i = 0; i < m_numElemConstraints; ++i) {
99  writeline(' ', 26, false);
100  plogf("%-2.2s", m_elementName[i]);
101  plogf("%20.12E%20.12E %3d\n", m_elemAbundancesGoal[i], m_elemAbundances[i],
102  m_elType[i]);
103  }
104  if (m_doEstimateEquil < 0) {
105  plogf("\n MODIFIED LINEAR PROGRAMMING ESTIMATE OF EQUILIBRIUM - forced\n");
106  } else if (m_doEstimateEquil > 0) {
107  plogf("\n MODIFIED LINEAR PROGRAMMING ESTIMATE OF EQUILIBRIUM - where necessary\n");
108  }
109  if (m_doEstimateEquil == 0) {
110  plogf("\n USER ESTIMATE OF EQUILIBRIUM\n");
111  }
112  plogf(" Stan. Chem. Pot. in J/kmol\n");
113  plogf("\n SPECIES FORMULA VECTOR ");
114  writeline(' ', 41, false);
115  plogf(" STAN_CHEM_POT EQUILIBRIUM_EST. Species_Type\n\n");
116  writeline(' ', 20, false);
117  for (size_t i = 0; i < m_numElemConstraints; ++i) {
118  plogf("%-4.4s ", m_elementName[i]);
119  }
120  plogf(" PhaseID\n");
121  double RT = GasConstant * m_temperature;
122  for (size_t i = 0; i < m_numSpeciesTot; ++i) {
123  plogf(" %-18.18s", m_speciesName[i]);
124  for (size_t j = 0; j < m_numElemConstraints; ++j) {
125  plogf("% -7.3g ", m_formulaMatrix(i,j));
126  }
127  plogf(" %3d ", m_phaseID[i]);
128  writeline(' ', std::max(55-int(m_numElemConstraints)*8, 0), false);
129  plogf("%12.5E %12.5E", RT * m_SSfeSpecies[i], m_molNumSpecies_old[i]);
130  if (m_speciesUnknownType[i] == VCS_SPECIES_TYPE_MOLNUM) {
131  plogf(" Mol_Num");
132  } else if (m_speciesUnknownType[i] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
133  plogf(" Voltage");
134  } else {
135  plogf(" Unknown");
136  }
137  plogendl();
138  }
139  }
140 
141  for (size_t i = 0; i < m_numSpeciesTot; ++i) {
142  if (m_molNumSpecies_old[i] < 0.0) {
143  plogf("On Input species %-12s has a negative MF, setting it small",
144  m_speciesName[i]);
145  plogendl();
146  size_t iph = m_phaseID[i];
147  double tmp = m_tPhaseMoles_old[iph] * VCS_RELDELETE_SPECIES_CUTOFF * 10;
148  tmp = std::max(tmp, VCS_DELETE_MINORSPECIES_CUTOFF*10.);
149  m_molNumSpecies_old[i] = tmp;
150  }
151  }
152 
153  // Evaluate the total moles of species in the problem
154  vcs_tmoles();
155 
156  // Evaluate all chemical potentials at the old mole numbers at the outset of
157  // the calculation.
158  vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
159  vcs_dfe(VCS_STATECALC_OLD, 0, 0, m_numSpeciesRdc);
160 
161  bool lec;
162  while (true) {
163  if (stage == MAIN) {
164  // DETERMINE BASIS SPECIES, EVALUATE STOICHIOMETRY
165  if (forceComponentCalc) {
166  int retn = solve_tp_component_calc(allMinorZeroedSpecies);
167  if (retn != VCS_SUCCESS) {
168  return retn;
169  }
170  it1 = 1;
171  forceComponentCalc = 0;
172  iti = 0;
173  }
174  // Check on too many iterations. If we have too many iterations,
175  // Clean up and exit code even though we haven't converged.
176  // -> we have run out of iterations!
177  if (m_VCount->Its > maxit) {
178  return -1;
179  }
180  solve_tp_inner(iti, it1, uptodate_minors, allMinorZeroedSpecies,
181  forceComponentCalc, stage, printDetails > 0, ANOTE);
182  lec = false;
183  } else if (stage == EQUILIB_CHECK) {
184  // EQUILIBRIUM CHECK FOR MAJOR SPECIES
185  solve_tp_equilib_check(allMinorZeroedSpecies, uptodate_minors,
186  giveUpOnElemAbund, solveFail, iti, it1,
187  maxit, stage, lec);
188  } else if (stage == ELEM_ABUND_CHECK) {
189  // CORRECT ELEMENTAL ABUNDANCES
190  solve_tp_elem_abund_check(iti, stage, lec, giveUpOnElemAbund,
191  finalElemAbundAttempts, rangeErrorFound);
192  } else if (stage == RECHECK_DELETED) {
193  // RECHECK DELETED SPECIES
194  //
195  // We are here for two reasons. One is if we have achieved
196  // convergence, but some species have been eliminated from the
197  // problem because they were in multispecies phases and their mole
198  // fractions drifted less than VCS_DELETE_SPECIES_CUTOFF. The other
199  // reason why we are here is because all of the non-component
200  // species in the problem have been eliminated for one reason or
201  // another.
202  size_t npb = vcs_recheck_deleted();
203 
204  // If we haven't found any species that needed adding we are done.
205  if (npb <= 0) {
206  stage = RETURN_B;
207  } else {
208  // If we have found something to add, recalculate everything for
209  // minor species and go back to do a full iteration
210  vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
211  vcs_dfe(VCS_STATECALC_OLD, 1, 0, m_numSpeciesRdc);
212  vcs_deltag(0, false, VCS_STATECALC_OLD);
213  iti = 0;
214  stage = MAIN;
215  }
216  } else if (stage == RETURN_A) {
217  // CLEANUP AND RETURN BLOCK
218  size_t npb = vcs_recheck_deleted();
219 
220  // If we haven't found any species that needed adding we are done.
221  if (npb > 0) {
222  // If we have found something to add, recalculate everything for
223  // minor species and go back to do a full iteration
224  vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
225  vcs_dfe(VCS_STATECALC_OLD, 1, 0, m_numSpeciesRdc);
226  vcs_deltag(0, false, VCS_STATECALC_OLD);
227  iti = 0;
228  stage = MAIN;
229  } else {
230  stage = RETURN_B;
231  }
232  } else if (stage == RETURN_B) {
233  // Add back deleted species in non-zeroed phases. Estimate their
234  // mole numbers.
235  size_t npb = vcs_add_all_deleted();
236  if (npb > 0) {
237  iti = 0;
238  if (m_debug_print_lvl >= 1) {
239  plogf(" --- add_all_deleted(): some rxns not converged. RETURNING TO LOOP!");
240  plogendl();
241  }
242  stage = MAIN;
243  } else {
244  break;
245  }
246  }
247  }
248 
249  // Make sure the volume phase objects hold the same state and information as
250  // the vcs object. This also update the Cantera objects with this
251  // information.
252  vcs_updateVP(VCS_STATECALC_OLD);
253 
254  // Evaluate the final mole fractions storing them in wt[]
255  m_molNumSpecies_new.assign(m_molNumSpecies_new.size(), 0.0);
256  for (size_t kspec = 0; kspec < m_numSpeciesTot; ++kspec) {
257  if (m_SSPhase[kspec]) {
258  m_molNumSpecies_new[kspec] = 1.0;
259  } else {
260  size_t iph = m_phaseID[kspec];
261  if (m_tPhaseMoles_old[iph] != 0.0) {
262  m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] / m_tPhaseMoles_old[iph];
263  } else {
264  // For MultiSpecies phases that are zeroed out, return the mole
265  // fraction vector from the VolPhase object. This contains the
266  // mole fraction that would be true if the phase just pops into
267  // existence.
268  size_t i = m_speciesLocalPhaseIndex[kspec];
269  m_molNumSpecies_new[kspec] = m_VolPhaseList[iph]->molefraction(i);
270  }
271  }
272  }
273  // Return an error code if a Range Space Error is thought to have occurred.
274  if (rangeErrorFound) {
275  solveFail = 1;
276  }
277 
278  // Calculate counters
279  double tsecond = ticktock.secondsWC();
280  m_VCount->Time_vcs_TP = tsecond;
281  m_VCount->T_Time_vcs_TP += m_VCount->Time_vcs_TP;
282  m_VCount->T_Calls_vcs_TP++;
283  m_VCount->T_Its += m_VCount->Its;
284  m_VCount->T_Basis_Opts += m_VCount->Basis_Opts;
285  m_VCount->T_Time_basopt += m_VCount->Time_basopt;
286 
287  // Return a Flag indicating whether convergence occurred
288  return solveFail;
289 }
290 
291 int VCS_SOLVE::solve_tp_component_calc(bool& allMinorZeroedSpecies)
292 {
293  double test = -1.0e-10;
294  bool usedZeroedSpecies;
295  int retn = vcs_basopt(false, &m_aw[0], &m_sa[0], &m_sm[0], &m_ss[0],
296  test, &usedZeroedSpecies);
297  if (retn != VCS_SUCCESS) {
298  return retn;
299  }
300 
301  // Update the phase objects with the contents of the soln vector
302  vcs_updateVP(VCS_STATECALC_OLD);
303  vcs_deltag(0, false, VCS_STATECALC_OLD);
304 
305  // EVALUATE INITIAL SPECIES STATUS VECTOR
306  allMinorZeroedSpecies = vcs_evaluate_speciesType();
307 
308  // EVALUATE THE ELELEMT ABUNDANCE CHECK
309  if (! vcs_elabcheck(0)) {
310  if (m_debug_print_lvl >= 2) {
311  plogf(" --- Element Abundance check failed");
312  plogendl();
313  }
314  vcs_elcorr(&m_sm[0], &m_wx[0]);
315  vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
316  vcs_dfe(VCS_STATECALC_OLD, 0, 0, m_numSpeciesRdc);
317  // Update the phase objects with the contents of the soln vector
318  vcs_updateVP(VCS_STATECALC_OLD);
319  vcs_deltag(0, false, VCS_STATECALC_OLD);
320  } else if (m_debug_print_lvl >= 2) {
321  plogf(" --- Element Abundance check passed");
322  plogendl();
323  }
324  return retn;
325 }
326 
327 void VCS_SOLVE::solve_tp_inner(size_t& iti, size_t& it1,
328  bool& uptodate_minors,
329  bool& allMinorZeroedSpecies,
330  int& forceComponentCalc,
331  int& stage, bool printDetails, char* ANOTE)
332 {
333  if (iti == 0) {
334  // SET INITIAL VALUES FOR ITERATION
335  // EVALUATE REACTION ADJUSTMENTS
336  //
337  // Evaluate the minor non-component species chemical potentials and
338  // delta G for their formation reactions We have already evaluated the
339  // major non-components
340  if (!uptodate_minors) {
341  vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
342  vcs_dfe(VCS_STATECALC_OLD, 1, 0, m_numSpeciesRdc);
343  vcs_deltag(1, false, VCS_STATECALC_OLD);
344  }
345  uptodate_minors = true;
346  } else {
347  uptodate_minors = false;
348  }
349 
350  if (printDetails) {
351  plogf("\n");
352  writeline('=', 110);
353  plogf(" Iteration = %3d, Iterations since last evaluation of "
354  "optimal basis = %3d",
355  m_VCount->Its, it1 - 1);
356  if (iti == 0) {
357  plogf(" (all species)\n");
358  } else {
359  plogf(" (only major species)\n");
360  }
361  }
362 
363  // Calculate the total moles in each phase -> old solution
364  // -> Needed for numerical stability when phases disappear.
365  // -> the phase moles tend to drift off without this step.
366  check_tmoles();
367  vcs_tmoles();
368  // COPY OLD into NEW and ZERO VECTORS
369  // Copy the old solution into the new solution as an initial guess
370  m_feSpecies_new = m_feSpecies_old;
371  m_actCoeffSpecies_new = m_actCoeffSpecies_old;
372  m_deltaGRxn_new = m_deltaGRxn_old;
373  m_deltaGRxn_Deficient = m_deltaGRxn_old;
374  m_tPhaseMoles_new = m_tPhaseMoles_old;
375 
376  // Zero out the entire vector of updates. We sometimes would query these
377  // values below, and we want to be sure that no information is left from
378  // previous iterations.
379  m_deltaMolNumSpecies.assign(m_deltaMolNumSpecies.size(), 0.0);
380 
381  // DETERMINE IF DEAD PHASES POP INTO EXISTENCE
382  //
383  // First step is a major branch in the algorithm. We first determine if a
384  // phase pops into existence.
385  std::vector<size_t> phasePopPhaseIDs(0);
386  size_t iphasePop = vcs_popPhaseID(phasePopPhaseIDs);
387  if (iphasePop != npos) {
388  int soldel = vcs_popPhaseRxnStepSizes(iphasePop);
389  if (soldel == 3) {
390  iphasePop = npos;
391  if (m_debug_print_lvl >= 2) {
392  plogf(" --- vcs_popPhaseRxnStepSizes() was called but stoich "
393  "prevented phase %d popping\n");
394  }
395  }
396  }
397 
398  // DETERMINE THE REACTION STEP SIZES FOR MAIN STEP AND IF PHASES DIE
399  //
400  // Don't do this step if there is a phase pop
401  size_t iphaseDelete = npos;
402  size_t kspec;
403  if (iphasePop == npos) {
404  // Figure out the new reaction step sizes for the major species (do
405  // minor species in the future too)
406  kspec = npos;
407  iphaseDelete = vcs_RxnStepSizes(forceComponentCalc, kspec);
408  } else if (m_debug_print_lvl >= 2) {
409  plogf(" --- vcs_RxnStepSizes not called because alternative"
410  "phase creation delta was used instead\n");
411  }
412  size_t doPhaseDeleteKspec = npos;
413  size_t doPhaseDeleteIph = npos;
414 
415  // Zero out the net change in moles of multispecies phases
416  m_deltaPhaseMoles.assign(m_deltaPhaseMoles.size(), 0.0);
417 
418  // MAIN LOOP IN CALCULATION: LOOP OVER IRXN TO DETERMINE STEP SIZE
419  //
420  // Loop through all of the reactions, irxn, pertaining to the formation
421  // reaction for species kspec in canonical form.
422  //
423  // At the end of this loop, we will have a new estimate for the mole numbers
424  // for all species consistent with an extent of reaction for all
425  // noncomponent species formation reactions. We will have also ensured that
426  // all predicted non-component mole numbers are greater than zero.
427  //
428  // Old_Solution New_Solution Description
429  // -----------------------------------------------------------------------------
430  // m_molNumSpecies_old[kspec] m_molNumSpecies_new[kspec] Species Mole Numbers
431  // m_deltaMolNumSpecies[kspec] Delta in the Species Mole Numbers
432  if (iphaseDelete != npos) {
433  if (m_debug_print_lvl >= 2) {
434  plogf(" --- Main Loop Treatment -> Circumvented due to Phase Deletion ");
435  plogendl();
436  }
437  for (size_t k = 0; k < m_numSpeciesTot; k++) {
438  m_molNumSpecies_new[k] = m_molNumSpecies_old[k] + m_deltaMolNumSpecies[k];
439  size_t iph = m_phaseID[k];
440  m_tPhaseMoles_new[iph] += m_deltaMolNumSpecies[k];
441  }
442  if (kspec >= m_numComponents) {
443  if (m_molNumSpecies_new[m_numSpeciesTot] != 0.0) {
444  throw CanteraError("VCS_SOLVE::solve_tp_inner",
445  "we shouldn't be here!");
446  }
447  if (m_SSPhase[kspec] == 1) {
448  m_speciesStatus[kspec] = VCS_SPECIES_ZEROEDSS;
449  } else {
450  throw CanteraError("VCS_SOLVE::solve_tp_inner",
451  "we shouldn't be here!");
452  }
453  ++m_numRxnMinorZeroed;
454  allMinorZeroedSpecies = (m_numRxnMinorZeroed == m_numRxnRdc);
455  }
456 
457  // Set the flags indicating the mole numbers in the vcs_VolPhase objects
458  // are out of date.
459  vcs_setFlagsVolPhases(false, VCS_STATECALC_NEW);
460 
461  // Calculate the new chemical potentials using the tentative solution
462  // values. We only calculate a subset of these, because we have only
463  // updated a subset of the W().
464  vcs_dfe(VCS_STATECALC_NEW, 0, 0, m_numSpeciesTot);
465 
466  // Evaluate DeltaG for all components if ITI=0, and for major components
467  // only if ITI NE 0
468  vcs_deltag(0, false, VCS_STATECALC_NEW);
469  } else {
470  if (m_debug_print_lvl >= 2) {
471  plogf(" --- Main Loop Treatment of each non-component species ");
472  if (iti == 0) {
473  plogf("- Full Calculation:\n");
474  } else {
475  plogf("- Major Components Calculation:\n");
476  }
477  plogf(" --- Species IC ");
478  plogf(" KMoles Tent_KMoles Rxn_Adj | Comment \n");
479  }
480  for (size_t irxn = 0; irxn < m_numRxnRdc; irxn++) {
481  size_t kspec = m_indexRxnToSpecies[irxn];
482  double* sc_irxn = m_stoichCoeffRxnMatrix.ptrColumn(irxn);
483  size_t iph = m_phaseID[kspec];
484  vcs_VolPhase* Vphase = m_VolPhaseList[iph];
485  ANOTE[0] = '\0';
486  double dx;
487 
488  if (iphasePop != npos) {
489  if (iph == iphasePop) {
490  dx = m_deltaMolNumSpecies[kspec];
491  m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + m_deltaMolNumSpecies[kspec];
492  sprintf(ANOTE, "Phase pop");
493  } else {
494  dx = 0.0;
495  m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec];
496  }
497  } else if (m_speciesStatus[kspec] == VCS_SPECIES_INTERFACIALVOLTAGE) {
498  // VOLTAGE SPECIES
499  bool soldel_ret;
500  dx = vcs_minor_alt_calc(kspec, irxn, &soldel_ret, ANOTE);
501  m_deltaMolNumSpecies[kspec] = dx;
502  } else if (m_speciesStatus[kspec] < VCS_SPECIES_MINOR) {
503  // ZEROED OUT SPECIES
504  bool resurrect = (m_deltaMolNumSpecies[kspec] > 0.0);
505  if (m_debug_print_lvl >= 3) {
506  plogf(" --- %s currently zeroed (SpStatus=%-2d):",
507  m_speciesName[kspec], m_speciesStatus[kspec]);
508  plogf("%3d DG = %11.4E WT = %11.4E W = %11.4E DS = %11.4E\n",
509  irxn, m_deltaGRxn_new[irxn], m_molNumSpecies_new[kspec],
510  m_molNumSpecies_old[kspec], m_deltaMolNumSpecies[kspec]);
511  }
512  if (m_deltaGRxn_new[irxn] >= 0.0 || !resurrect) {
513  m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec];
514  m_deltaMolNumSpecies[kspec] = 0.0;
515  resurrect = false;
516  sprintf(ANOTE, "Species stays zeroed: DG = %11.4E", m_deltaGRxn_new[irxn]);
517  if (m_deltaGRxn_new[irxn] < 0.0) {
518  if (m_speciesStatus[kspec] == VCS_SPECIES_STOICHZERO) {
519  sprintf(ANOTE, "Species stays zeroed even though dg neg due to "
520  "STOICH/PHASEPOP constraint: DG = %11.4E",
521  m_deltaGRxn_new[irxn]);
522  } else {
523  sprintf(ANOTE, "Species stays zeroed even though dg neg: DG = %11.4E, ds zeroed",
524  m_deltaGRxn_new[irxn]);
525  }
526  }
527  } else {
528  for (size_t j = 0; j < m_numElemConstraints; ++j) {
529  int elType = m_elType[j];
530  if (elType == VCS_ELEM_TYPE_ABSPOS) {
531  double atomComp = m_formulaMatrix(kspec,j);
532  if (atomComp > 0.0) {
533  double maxPermissible = m_elemAbundancesGoal[j] / atomComp;
534  if (maxPermissible < VCS_DELETE_MINORSPECIES_CUTOFF) {
535  sprintf(ANOTE, "Species stays zeroed even though dG "
536  "neg, because of %s elemAbund",
537  m_elementName[j].c_str());
538  resurrect = false;
539  break;
540  }
541  }
542  }
543  }
544  }
545 
546  // Resurrect the species
547  if (resurrect) {
548  if (Vphase->exists() == VCS_PHASE_EXIST_NO) {
549  if (m_debug_print_lvl >= 2) {
550  plogf(" --- Zeroed species changed to major: ");
551  plogf("%-12s\n", m_speciesName[kspec]);
552  }
553  m_speciesStatus[kspec] = VCS_SPECIES_MAJOR;
554  allMinorZeroedSpecies = false;
555  } else {
556  if (m_debug_print_lvl >= 2) {
557  plogf(" --- Zeroed species changed to minor: ");
558  plogf("%-12s\n", m_speciesName[kspec]);
559  }
560  m_speciesStatus[kspec] = VCS_SPECIES_MINOR;
561  }
562  if (m_deltaMolNumSpecies[kspec] > 0.0) {
563  dx = m_deltaMolNumSpecies[kspec] * 0.01;
564  m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + dx;
565  } else {
566  m_molNumSpecies_new[kspec] = m_totalMolNum * VCS_DELETE_PHASE_CUTOFF * 10.;
567  dx = m_molNumSpecies_new[kspec] - m_molNumSpecies_old[kspec];
568  }
569  m_deltaMolNumSpecies[kspec] = dx;
570  sprintf(ANOTE, "Born:IC=-1 to IC=1:DG=%11.4E", m_deltaGRxn_new[irxn]);
571  } else {
572  m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec];
573  m_deltaMolNumSpecies[kspec] = 0.0;
574  dx = 0.0;
575  }
576  } else if (m_speciesStatus[kspec] == VCS_SPECIES_MINOR) {
577  // MINOR SPECIES
578  //
579  // Unless ITI isn't equal to zero we zero out changes to minor
580  // species.
581  if (iti != 0) {
582  m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec];
583  m_deltaMolNumSpecies[kspec] = 0.0;
584  dx = 0.0;
585  sprintf(ANOTE,"minor species not considered");
586  if (m_debug_print_lvl >= 2) {
587  plogf(" --- ");
588  plogf("%-12s", m_speciesName[kspec]);
589  plogf("%3d% 11.4E %11.4E %11.4E | %s",
590  m_speciesStatus[kspec], m_molNumSpecies_old[kspec], m_molNumSpecies_new[kspec],
591  m_deltaMolNumSpecies[kspec], ANOTE);
592  plogendl();
593  }
594  continue;
595  }
596 
597  // Minor species alternative calculation
598  //
599  // This is based upon the following approximation:
600  // The mole fraction changes due to these reactions don't affect
601  // the mole numbers of the component species. Therefore the
602  // following approximation is valid for an ideal solution
603  // 0 = DG(I) + log(WT(I)/W(I))
604  // (DG contains the contribution from FF(I) + log(W(I)/TL) )
605  // Thus,
606  // WT(I) = W(I) EXP(-DG(I))
607  // If soldel is true on return, then we branch to the section
608  // that deletes a species from the current set of active species.
609  bool soldel_ret;
610  dx = vcs_minor_alt_calc(kspec, irxn, &soldel_ret, ANOTE);
611  m_deltaMolNumSpecies[kspec] = dx;
612  m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + dx;
613  if (soldel_ret) {
614  // DELETE MINOR SPECIES LESS THAN VCS_DELETE_SPECIES_CUTOFF
615  // MOLE NUMBER
616  if (m_debug_print_lvl >= 2) {
617  plogf(" --- Delete minor species in multispec phase: %-12s",
618  m_speciesName[kspec]);
619  plogendl();
620  }
621  m_deltaMolNumSpecies[kspec] = 0.0;
622 
623  // Delete species, kspec. The alternate return is for the
624  // case where all species become deleted. Then, we need to
625  // branch to the code where we reevaluate the deletion of
626  // all species.
627  size_t lnospec = vcs_delete_species(kspec);
628  if (lnospec) {
629  stage = RECHECK_DELETED;
630  break;
631  }
632 
633  // Go back to consider the next species in the list. Note,
634  // however, that the next species in the list is now in slot
635  // l. In deleting the previous species L, We have exchanged
636  // slot MR with slot l, and then have decremented MR.
637  // Therefore, we will decrement the species counter, here.
638  --irxn;
639  continue;
640  }
641  } else {
642  // MAJOR SPECIES
643  sprintf(ANOTE, "Normal Major Calc");
644 
645  // Check for superconvergence of the formation reaction. Do
646  // nothing if it is superconverged. Skip to the end of the irxn
647  // loop if it is superconverged.
648  if (fabs(m_deltaGRxn_new[irxn]) <= m_tolmaj2) {
649  m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec];
650  m_deltaMolNumSpecies[kspec] = 0.0;
651  dx = 0.0;
652  sprintf(ANOTE, "major species is converged");
653  if (m_debug_print_lvl >= 2) {
654  plogf(" --- ");
655  plogf("%-12s", m_speciesName[kspec]);
656  plogf(" %3d %11.4E %11.4E %11.4E | %s",
657  m_speciesStatus[kspec], m_molNumSpecies_old[kspec], m_molNumSpecies_new[kspec],
658  m_deltaMolNumSpecies[kspec], ANOTE);
659  plogendl();
660  }
661  continue;
662  }
663 
664  // Set the initial step size, dx, equal to the value produced by
665  // the routine, vcs_RxnStepSize().
666  //
667  // Note the multiplication logic is to make sure that dg[]
668  // didn't change sign due to w[] changing in the middle of the
669  // iteration. (it can if a single species phase goes out of
670  // existence).
671  if ((m_deltaGRxn_new[irxn] * m_deltaMolNumSpecies[kspec]) <= 0.0) {
672  dx = m_deltaMolNumSpecies[kspec];
673  } else {
674  dx = 0.0;
675  m_deltaMolNumSpecies[kspec] = 0.0;
676  sprintf(ANOTE, "dx set to 0, DG flipped sign due to "
677  "changed initial point");
678  }
679 
680  //Form a tentative value of the new species moles
681  m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + dx;
682 
683  // Check for non-positive mole fraction of major species. If we
684  // find one, we branch to a section below. Then, depending upon
685  // the outcome, we branch to sections below, or we restart the
686  // entire iteration.
687  if (m_molNumSpecies_new[kspec] <= 0.0) {
688  sprintf(ANOTE, "initial nonpos kmoles= %11.3E",
689  m_molNumSpecies_new[kspec]);
690  // NON-POSITIVE MOLES OF MAJOR SPECIES
691  //
692  // We are here when a tentative value of a mole fraction
693  // created by a tentative value of M_DELTAMOLNUMSPECIES(*)
694  // is negative. We branch from here depending upon whether
695  // this species is in a single species phase or in a
696  // multispecies phase.
697  if (!m_SSPhase[kspec]) {
698  // Section for multispecies phases:
699  // - Cut reaction adjustment for positive kmoles of
700  // major species in multispecies phases. Decrease
701  // its concentration by a factor of 10.
702  dx = -0.9 * m_molNumSpecies_old[kspec];
703  m_deltaMolNumSpecies[kspec] = dx;
704  m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + dx;
705  } else {
706  // Section for single species phases:
707  // Calculate a dx that will wipe out the
708  // moles in the phase.
709  dx = -m_molNumSpecies_old[kspec];
710 
711  // Calculate an update that doesn't create a negative
712  // mole number for a component species. Actually,
713  // restrict this a little more so that the component
714  // values can only be reduced by two 99%,
715  for (size_t j = 0; j < m_numComponents; ++j) {
716  if (sc_irxn[j] != 0.0) {
717  m_wx[j] = m_molNumSpecies_old[j] + sc_irxn[j] * dx;
718  if (m_wx[j] <= m_molNumSpecies_old[j] * 0.01 - 1.0E-150) {
719  dx = std::max(dx, m_molNumSpecies_old[j] * -0.99 / sc_irxn[j]);
720  }
721  } else {
722  m_wx[j] = m_molNumSpecies_old[j];
723  }
724  }
725  m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + dx;
726  if (m_molNumSpecies_new[kspec] > 0.0) {
727  m_deltaMolNumSpecies[kspec] = dx;
728  sprintf(ANOTE,
729  "zeroing SS phase created a neg component species "
730  "-> reducing step size instead");
731  } else {
732  // We are going to zero the single species phase.
733  // Set the existence flag
734  iph = m_phaseID[kspec];
735  Vphase = m_VolPhaseList[iph];
736  sprintf(ANOTE, "zeroing out SS phase: ");
737 
738  // Change the base mole numbers for the iteration.
739  // We need to do this here, because we have decided
740  // to eliminate the phase in this special section
741  // outside the main loop.
742  m_molNumSpecies_new[kspec] = 0.0;
743  doPhaseDeleteIph = iph;
744 
745  doPhaseDeleteKspec = kspec;
746  if (m_debug_print_lvl >= 2 && m_speciesStatus[kspec] >= 0) {
747  plogf(" --- SS species changed to zeroedss: ");
748  plogf("%-12s", m_speciesName[kspec]);
749  plogendl();
750  }
751  m_speciesStatus[kspec] = VCS_SPECIES_ZEROEDSS;
752  ++m_numRxnMinorZeroed;
753  allMinorZeroedSpecies = (m_numRxnMinorZeroed == m_numRxnRdc);
754 
755  for (size_t kk = 0; kk < m_numSpeciesTot; kk++) {
756  m_deltaMolNumSpecies[kk] = 0.0;
757  m_molNumSpecies_new[kk] = m_molNumSpecies_old[kk];
758  }
759  m_deltaMolNumSpecies[kspec] = dx;
760  m_molNumSpecies_new[kspec] = 0.0;
761 
762  for (size_t k = 0; k < m_numComponents; ++k) {
763  m_deltaMolNumSpecies[k] = 0.0;
764  }
765  for (iph = 0; iph < m_numPhases; iph++) {
766  m_deltaPhaseMoles[iph] = 0.0;
767  }
768  }
769  }
770  }
771  } // End of Loop on ic[irxn] -> the type of species
772 
773  // CALCULATE KMOLE NUMBER CHANGE FOR THE COMPONENT BASIS
774  if (dx != 0.0 && (m_speciesUnknownType[kspec] !=
776 
777  // Change the amount of the component compounds according to the
778  // reaction delta that we just computed. This should keep the
779  // amount of material constant.
780  AssertThrowMsg(fabs(m_deltaMolNumSpecies[kspec] -dx) <
781  1.0E-14*(fabs(m_deltaMolNumSpecies[kspec]) + fabs(dx) + 1.0E-32),
782  "VCS_SOLVE::solve_tp_inner",
783  "ds[kspec] = {}, dx = {}, kspec = {}\nwe have a problem!",
784  m_deltaMolNumSpecies[kspec], dx, kspec);
785  for (size_t k = 0; k < m_numComponents; ++k) {
786  m_deltaMolNumSpecies[k] += sc_irxn[k] * dx;
787  }
788 
789  // Calculate the tentative change in the total number of moles
790  // in all of the phases
791  for (iph = 0; iph < m_numPhases; iph++) {
792  m_deltaPhaseMoles[iph] += dx * m_deltaMolNumPhase(iph,irxn);
793  }
794  }
795 
796  checkDelta1(&m_deltaMolNumSpecies[0],
797  &m_deltaPhaseMoles[0], kspec+1);
798 
799  // Branch point for returning
800  if (m_debug_print_lvl >= 2) {
801  m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + m_deltaMolNumSpecies[kspec];
802  plogf(" --- ");
803  plogf("%-12.12s", m_speciesName[kspec]);
804  plogf("%3d %11.4E %11.4E %11.4E | %s",
805  m_speciesStatus[kspec], m_molNumSpecies_old[kspec],
806  m_molNumSpecies_new[kspec],
807  m_deltaMolNumSpecies[kspec], ANOTE);
808  plogendl();
809  }
810 
811  if (doPhaseDeleteIph != npos) {
812  if (m_debug_print_lvl >= 2) {
813  plogf(" --- ");
814  plogf("%-12.12s Main Loop Special Case deleting phase with species: ",
815  m_speciesName[doPhaseDeleteKspec]);
816  plogendl();
817  }
818  break;
819  }
820  } // END OF MAIN LOOP OVER FORMATION REACTIONS
821  if (m_debug_print_lvl >= 2) {
822  for (size_t k = 0; k < m_numComponents; k++) {
823  plogf(" --- ");
824  plogf("%-12.12s", m_speciesName[k]);
825  plogf(" c %11.4E %11.4E %11.4E |\n",
826  m_molNumSpecies_old[k],
827  m_molNumSpecies_old[k]+m_deltaMolNumSpecies[k], m_deltaMolNumSpecies[k]);
828  }
829  plogf(" ");
830  writeline('-', 80);
831  plogf(" --- Finished Main Loop");
832  plogendl();
833  }
834 
835  // LIMIT REDUCTION OF BASIS SPECIES TO 99%
836  //
837  // We have a tentative m_deltaMolNumSpecies[]. Now apply other criteria
838  // to limit its magnitude.
839  double par = 0.5;
840  size_t ll;
841  for (size_t k = 0; k < m_numComponents; ++k) {
842  if (m_molNumSpecies_old[k] > 0.0) {
843  double xx = -m_deltaMolNumSpecies[k] / m_molNumSpecies_old[k];
844  if (par < xx) {
845  par = xx;
846  ll = k;
847  }
848  } else if (m_deltaMolNumSpecies[k] < 0.0) {
849  // If we are here, we then do a step which violates element
850  // conservation.
851  size_t iph = m_phaseID[k];
852  m_deltaPhaseMoles[iph] -= m_deltaMolNumSpecies[k];
853  m_deltaMolNumSpecies[k] = 0.0;
854  }
855  }
856  par = 1.0 / par;
857  if (par <= 1.01 && par > 0.0) {
858  // Reduce the size of the step by the multiplicative factor, par
859  par *= 0.99;
860  if (m_debug_print_lvl >= 2) {
861  plogf(" --- Reduction in step size due to component ");
862  plogf("%s", m_speciesName[ll]);
863  plogf(" going negative = %11.3E", par);
864  plogendl();
865  }
866  for (size_t i = 0; i < m_numSpeciesTot; ++i) {
867  m_deltaMolNumSpecies[i] *= par;
868  }
869  for (size_t iph = 0; iph < m_numPhases; iph++) {
870  m_deltaPhaseMoles[iph] *= par;
871  }
872  } else {
873  par = 1.0;
874  }
875  checkDelta1(&m_deltaMolNumSpecies[0],
876  &m_deltaPhaseMoles[0], m_numSpeciesTot);
877 
878  // Now adjust the wt[kspec]'s so that the reflect the decrease in the
879  // overall length of m_deltaMolNumSpecies[kspec] just calculated. At the
880  // end of this section wt[], m_deltaMolNumSpecies[], tPhMoles, and
881  // tPhMoles1 should all be consistent with a new estimate of the state
882  // of the system.
883  for (size_t kspec = 0; kspec < m_numSpeciesTot; ++kspec) {
884  m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + m_deltaMolNumSpecies[kspec];
885  if (m_molNumSpecies_new[kspec] < 0.0 && (m_speciesUnknownType[kspec]
887  throw CanteraError("VCS_SOLVE::solve_tp_inner",
888  "vcs_solve_TP: ERROR on step change wt[{}:{}]: {} < 0.0",
889  kspec, m_speciesName[kspec], m_molNumSpecies_new[kspec]);
890  }
891  }
892 
893  // Calculate the tentative total mole numbers for each phase
894  for (size_t iph = 0; iph < m_numPhases; iph++) {
895  m_tPhaseMoles_new[iph] = m_tPhaseMoles_old[iph] + m_deltaPhaseMoles[iph];
896  }
897 
898  // Set the flags indicating the mole numbers in the vcs_VolPhase objects
899  // are out of date.
900  vcs_setFlagsVolPhases(false, VCS_STATECALC_NEW);
901 
902  // Calculate the new chemical potentials using the tentative solution
903  // values. We only calculate a subset of these, because we have only
904  // updated a subset of the W().
905  vcs_dfe(VCS_STATECALC_NEW, 0, 0, m_numSpeciesTot);
906 
907  // Evaluate DeltaG for all components if ITI=0, and for major components
908  // only if ITI NE 0
909  vcs_deltag(0, false, VCS_STATECALC_NEW);
910 
911  // CONVERGENCE FORCER SECTION
912  if (printDetails) {
913  plogf(" --- Total Old Dimensionless Gibbs Free Energy = %20.13E\n",
914  vcs_Total_Gibbs(&m_molNumSpecies_old[0], &m_feSpecies_old[0],
915  &m_tPhaseMoles_old[0]));
916  plogf(" --- Total tentative Dimensionless Gibbs Free Energy = %20.13E",
917  vcs_Total_Gibbs(&m_molNumSpecies_new[0], &m_feSpecies_new[0],
918  &m_tPhaseMoles_new[0]));
919  plogendl();
920  }
921 
922  bool forced = vcs_globStepDamp();
923 
924  // Print out the changes to the solution that FORCER produced
925  if (printDetails && forced) {
926  plogf(" -----------------------------------------------------\n");
927  plogf(" --- FORCER SUBROUTINE changed the solution:\n");
928  plogf(" --- SPECIES Status INIT MOLES TENT_MOLES");
929  plogf(" FINAL KMOLES INIT_DEL_G/RT TENT_DEL_G/RT FINAL_DELTA_G/RT\n");
930  for (size_t i = 0; i < m_numComponents; ++i) {
931  plogf(" --- %-12.12s", m_speciesName[i]);
932  plogf(" %14.6E %14.6E %14.6E\n", m_molNumSpecies_old[i],
933  m_molNumSpecies_old[i] + m_deltaMolNumSpecies[i], m_molNumSpecies_new[i]);
934  }
935  for (size_t kspec = m_numComponents; kspec < m_numSpeciesRdc; ++kspec) {
936  size_t irxn = kspec - m_numComponents;
937  plogf(" --- %-12.12s", m_speciesName[kspec]);
938  plogf(" %2d %14.6E%14.6E%14.6E%14.6E%14.6E%14.6E\n", m_speciesStatus[kspec],
939  m_molNumSpecies_old[kspec],
940  m_molNumSpecies_old[kspec]+m_deltaMolNumSpecies[kspec],
941  m_molNumSpecies_new[kspec], m_deltaGRxn_old[irxn],
942  m_deltaGRxn_tmp[irxn], m_deltaGRxn_new[irxn]);
943  }
944  writeline(' ', 26, false);
945  plogf("Norms of Delta G():%14.6E%14.6E\n",
946  l2normdg(&m_deltaGRxn_old[0]),
947  l2normdg(&m_deltaGRxn_new[0]));
948  plogf(" Total kmoles of gas = %15.7E\n", m_tPhaseMoles_old[0]);
949  if ((m_numPhases > 1) && (!m_VolPhaseList[1]->m_singleSpecies)) {
950  plogf(" Total kmoles of liquid = %15.7E\n", m_tPhaseMoles_old[1]);
951  } else {
952  plogf(" Total kmoles of liquid = %15.7E\n", 0.0);
953  }
954  plogf(" Total New Dimensionless Gibbs Free Energy = %20.13E\n",
955  vcs_Total_Gibbs(&m_molNumSpecies_new[0], &m_feSpecies_new[0],
956  &m_tPhaseMoles_new[0]));
957  plogf(" -----------------------------------------------------");
958  plogendl();
959  }
960 
961  }
962 
963  // ITERATION SUMMARY PRINTOUT SECTION
964  if (printDetails) {
965  plogf(" ");
966  writeline('-', 103);
967  plogf(" --- Summary of the Update ");
968  if (iti == 0) {
969  plogf(" (all species):");
970  } else {
971  plogf(" (only major species):");
972  }
973  if (m_totalMoleScale != 1.0) {
974  plogf(" (Total Mole Scale = %g)", m_totalMoleScale);
975  }
976  plogf("\n");
977  plogf(" --- Species Status Initial_KMoles Final_KMoles Initial_Mu/RT");
978  plogf(" Mu/RT Init_Del_G/RT Delta_G/RT\n");
979  for (size_t i = 0; i < m_numComponents; ++i) {
980  plogf(" --- %-12.12s", m_speciesName[i]);
981  plogf(" ");
982  plogf("%14.6E%14.6E%14.6E%14.6E\n", m_molNumSpecies_old[i],
983  m_molNumSpecies_new[i], m_feSpecies_old[i], m_feSpecies_new[i]);
984  }
985  for (size_t i = m_numComponents; i < m_numSpeciesRdc; ++i) {
986  size_t l1 = i - m_numComponents;
987  plogf(" --- %-12.12s", m_speciesName[i]);
988  plogf(" %2d %14.6E%14.6E%14.6E%14.6E%14.6E%14.6E\n",
989  m_speciesStatus[i], m_molNumSpecies_old[i],
990  m_molNumSpecies_new[i], m_feSpecies_old[i], m_feSpecies_new[i],
991  m_deltaGRxn_old[l1], m_deltaGRxn_new[l1]);
992  }
993  for (size_t kspec = m_numSpeciesRdc; kspec < m_numSpeciesTot; ++kspec) {
994  size_t l1 = kspec - m_numComponents;
995  plogf(" --- %-12.12s", m_speciesName[kspec]);
996  plogf(" %2d %14.6E%14.6E%14.6E%14.6E%14.6E%14.6E\n",
997  m_speciesStatus[kspec], m_molNumSpecies_old[kspec],
998  m_molNumSpecies_new[kspec], m_feSpecies_old[kspec], m_feSpecies_new[kspec],
999  m_deltaGRxn_old[l1], m_deltaGRxn_new[l1]);
1000  }
1001  plogf(" ---");
1002  writeline(' ', 56, false);
1003  plogf("Norms of Delta G():%14.6E%14.6E",
1004  l2normdg(&m_deltaGRxn_old[0]),
1005  l2normdg(&m_deltaGRxn_new[0]));
1006  plogendl();
1007 
1008  plogf(" --- Phase_Name KMoles(after update)\n");
1009  plogf(" --- ");
1010  writeline('-', 50);
1011  for (size_t iph = 0; iph < m_numPhases; iph++) {
1012  vcs_VolPhase* Vphase = m_VolPhaseList[iph];
1013  plogf(" --- %18s = %15.7E\n", Vphase->PhaseName, m_tPhaseMoles_new[iph]);
1014  }
1015  plogf(" ");
1016  writeline('-', 103);
1017  plogf(" --- Total Old Dimensionless Gibbs Free Energy = %20.13E\n",
1018  vcs_Total_Gibbs(&m_molNumSpecies_old[0], &m_feSpecies_old[0],
1019  &m_tPhaseMoles_old[0]));
1020  plogf(" --- Total New Dimensionless Gibbs Free Energy = %20.13E",
1021  vcs_Total_Gibbs(&m_molNumSpecies_new[0], &m_feSpecies_new[0],
1022  &m_tPhaseMoles_new[0]));
1023  plogendl();
1024  if (m_VCount->Its > 550) {
1025  plogf(" --- Troublesome solve");
1026  plogendl();
1027  }
1028  }
1029 
1030  // RESET VALUES AT END OF ITERATION
1031  // UPDATE MOLE NUMBERS
1032  //
1033  // If the solution wasn't changed in the forcer routine, then copy the
1034  // tentative mole numbers and Phase moles into the actual mole numbers and
1035  // phase moles. We will consider this current step to be completed.
1036  //
1037  // Accept the step. -> the tentative solution now becomes the real solution.
1038  // If FORCED is true, then we have already done this inside the FORCED loop.
1039  vcs_updateMolNumVolPhases(VCS_STATECALC_NEW);
1040  m_tPhaseMoles_old = m_tPhaseMoles_new;
1041  m_molNumSpecies_old = m_molNumSpecies_new;
1042  m_actCoeffSpecies_old = m_actCoeffSpecies_new;
1043  m_deltaGRxn_old = m_deltaGRxn_new;
1044  m_feSpecies_old = m_feSpecies_new;
1045 
1046  vcs_setFlagsVolPhases(true, VCS_STATECALC_OLD);
1047 
1048  // Increment the iteration counters
1049  ++m_VCount->Its;
1050  ++it1;
1051  if (m_debug_print_lvl >= 2) {
1052  plogf(" --- Increment counter increased, step is accepted: %4d",
1053  m_VCount->Its);
1054  plogendl();
1055  }
1056 
1057  // HANDLE DELETION OF MULTISPECIES PHASES
1058  //
1059  // We delete multiphases, when the total moles in the multiphase is reduced
1060  // below a relative threshold. Set microscopic multispecies phases with
1061  // total relative number of moles less than VCS_DELETE_PHASE_CUTOFF to
1062  // absolute zero.
1063  bool justDeletedMultiPhase = false;
1064  for (size_t iph = 0; iph < m_numPhases; iph++) {
1065  if (!m_VolPhaseList[iph]->m_singleSpecies && m_tPhaseMoles_old[iph] != 0.0 &&
1066  m_tPhaseMoles_old[iph]/m_totalMolNum <= VCS_DELETE_PHASE_CUTOFF) {
1067  if (m_debug_print_lvl >= 1) {
1068  plogf(" --- Setting microscopic phase %d to zero", iph);
1069  plogendl();
1070  }
1071  justDeletedMultiPhase = true;
1072  vcs_delete_multiphase(iph);
1073  }
1074  }
1075 
1076  // If we have deleted a multispecies phase because the equilibrium moles
1077  // decreased, then we will update all the component basis calculation, and
1078  // therefore all of the thermo functions just to be safe.
1079  if (justDeletedMultiPhase) {
1080  bool usedZeroedSpecies;
1081  double test = -1.0e-10;
1082  int retn = vcs_basopt(false, &m_aw[0], &m_sa[0], &m_sm[0], &m_ss[0],
1083  test, &usedZeroedSpecies);
1084  if (retn != VCS_SUCCESS) {
1085  throw CanteraError("VCS_SOLVE::solve_tp_inner",
1086  "BASOPT returned with an error condition");
1087  }
1088  vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
1089  vcs_dfe(VCS_STATECALC_OLD, 0, 0, m_numSpeciesRdc);
1090  vcs_deltag(0, true, VCS_STATECALC_OLD);
1091  iti = 0;
1092  return;
1093  }
1094 
1095  // CHECK FOR ELEMENT ABUNDANCE
1096  if (m_debug_print_lvl >= 2) {
1097  plogf(" --- Normal element abundance check");
1098  }
1099  vcs_elab();
1100  if (! vcs_elabcheck(0)) {
1101  if (m_debug_print_lvl >= 2) {
1102  plogf(" - failed -> redoing element abundances.");
1103  plogendl();
1104  }
1105  vcs_elcorr(&m_sm[0], &m_wx[0]);
1106  vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
1107  vcs_dfe(VCS_STATECALC_OLD, 0, 0, m_numSpeciesRdc);
1108  vcs_deltag(0, true, VCS_STATECALC_OLD);
1109  uptodate_minors = true;
1110  } else if (m_debug_print_lvl >= 2) {
1111  plogf(" - passed");
1112  plogendl();
1113  }
1114 
1115  // CHECK FOR OPTIMUM BASIS
1116  for (size_t i = 0; i < m_numRxnRdc; ++i) {
1117  size_t k = m_indexRxnToSpecies[i];
1118  if (m_speciesUnknownType[k] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
1119  continue;
1120  }
1121  for (size_t j = 0; j < m_numComponents; ++j) {
1122  bool doSwap = false;
1123  if (m_SSPhase[j]) {
1124  doSwap = (m_molNumSpecies_old[k] * m_spSize[k]) >
1125  (m_molNumSpecies_old[j] * m_spSize[j] * 1.01);
1126  if (!m_SSPhase[k] && doSwap) {
1127  doSwap = m_molNumSpecies_old[k] > (m_molNumSpecies_old[j] * 1.01);
1128  }
1129  } else {
1130  if (m_SSPhase[k]) {
1131  doSwap = (m_molNumSpecies_old[k] * m_spSize[k]) >
1132  (m_molNumSpecies_old[j] * m_spSize[j] * 1.01);
1133  if (!doSwap) {
1134  doSwap = m_molNumSpecies_old[k] > (m_molNumSpecies_old[j] * 1.01);
1135  }
1136  } else {
1137  doSwap = (m_molNumSpecies_old[k] * m_spSize[k]) >
1138  (m_molNumSpecies_old[j] * m_spSize[j] * 1.01);
1139  }
1140  }
1141  if (doSwap && m_stoichCoeffRxnMatrix(j,i) != 0.0) {
1142  if (m_debug_print_lvl >= 2) {
1143  plogf(" --- Get a new basis because ");
1144  plogf("%s", m_speciesName[k]);
1145  plogf(" is better than comp ");
1146  plogf("%s", m_speciesName[j]);
1147  plogf(" and share nonzero stoic: %-9.1f",
1148  m_stoichCoeffRxnMatrix(j,i));
1149  plogendl();
1150  }
1151  forceComponentCalc = 1;
1152  return;
1153  }
1154  }
1155  }
1156  if (m_debug_print_lvl >= 2) {
1157  plogf(" --- Check for an optimum basis passed");
1158  plogendl();
1159  }
1160  stage = EQUILIB_CHECK;
1161 
1162  // RE-EVALUATE MAJOR-MINOR VECTOR IF NECESSARY
1163  //
1164  // Skip this section if we haven't done a full calculation. Go right to the
1165  // check equilibrium section
1166  if (iti != 0) {
1167  return;
1168  }
1169  if (m_debug_print_lvl >= 2) {
1170  plogf(" --- Reevaluate major-minor status of noncomponents:\n");
1171  }
1172  m_numRxnMinorZeroed = 0;
1173  for (size_t irxn = 0; irxn < m_numRxnRdc; irxn++) {
1174  size_t kspec = m_indexRxnToSpecies[irxn];
1175  int speciesType = vcs_species_type(kspec);
1176  if (speciesType < VCS_SPECIES_MINOR) {
1177  if (m_debug_print_lvl >= 2 && m_speciesStatus[kspec] >= VCS_SPECIES_MINOR) {
1178  plogf(" --- major/minor species is now zeroed out: %s\n",
1179  m_speciesName[kspec]);
1180  }
1181  ++m_numRxnMinorZeroed;
1182  } else if (speciesType == VCS_SPECIES_MINOR) {
1183  if (m_debug_print_lvl >= 2 && m_speciesStatus[kspec] != VCS_SPECIES_MINOR) {
1184  if (m_speciesStatus[kspec] == VCS_SPECIES_MAJOR) {
1185  plogf(" --- Noncomponent turned from major to minor: ");
1186  } else if (kspec < m_numComponents) {
1187  plogf(" --- Component turned into a minor species: ");
1188  } else {
1189  plogf(" --- Zeroed Species turned into a "
1190  "minor species: ");
1191  }
1192  plogf("%s\n", m_speciesName[kspec]);
1193  }
1194  ++m_numRxnMinorZeroed;
1195  } else if (speciesType == VCS_SPECIES_MAJOR) {
1196  if (m_speciesStatus[kspec] != VCS_SPECIES_MAJOR) {
1197  if (m_debug_print_lvl >= 2) {
1198  if (m_speciesStatus[kspec] == VCS_SPECIES_MINOR) {
1199  plogf(" --- Noncomponent turned from minor to major: ");
1200  } else if (kspec < m_numComponents) {
1201  plogf(" --- Component turned into a major: ");
1202  } else {
1203  plogf(" --- Noncomponent turned from zeroed to major: ");
1204  }
1205  plogf("%s\n", m_speciesName[kspec]);
1206  }
1207  m_speciesStatus[kspec] = VCS_SPECIES_MAJOR;
1208  }
1209  }
1210  m_speciesStatus[kspec] = speciesType;
1211  }
1212 
1213  // This logical variable indicates whether all current non-component species
1214  // are minor or nonexistent
1215  allMinorZeroedSpecies = (m_numRxnMinorZeroed == m_numRxnRdc);
1216 }
1217 
1218 void VCS_SOLVE::solve_tp_equilib_check(bool& allMinorZeroedSpecies,
1219  bool& uptodate_minors,
1220  bool& giveUpOnElemAbund, int& solveFail,
1221  size_t& iti, size_t& it1, int maxit,
1222  int& stage, bool& lec)
1223 {
1224  if (! allMinorZeroedSpecies) {
1225  if (m_debug_print_lvl >= 2) {
1226  plogf(" --- Equilibrium check for major species: ");
1227  }
1228  for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
1229  size_t kspec = irxn + m_numComponents;
1230  if (m_speciesStatus[kspec] == VCS_SPECIES_MAJOR && (fabs(m_deltaGRxn_new[irxn]) > m_tolmaj)) {
1231  if (m_VCount->Its >= maxit) {
1232  solveFail = -1;
1233 
1234  // Clean up and exit code even though we haven't converged.
1235  // -> we have run out of iterations!
1236  stage = RETURN_A;
1237  } else {
1238  if (m_debug_print_lvl >= 2) {
1239  plogf("%s failed\n", m_speciesName[m_indexRxnToSpecies[irxn]]);
1240  }
1241  // Convergence amongst major species has not been achieved.
1242  // Go back and do another iteration with variable ITI
1243  iti = ((it1/4) *4) - it1;
1244  stage = MAIN;
1245  }
1246  return;
1247  }
1248  }
1249  if (m_debug_print_lvl >= 2) {
1250  plogf(" MAJOR SPECIES CONVERGENCE achieved");
1251  plogendl();
1252  }
1253  } else if (m_debug_print_lvl >= 2) {
1254  plogf(" MAJOR SPECIES CONVERGENCE achieved "
1255  "(because there are no major species)");
1256  plogendl();
1257  }
1258  // Convergence amongst major species has been achieved
1259 
1260  // EQUILIBRIUM CHECK FOR MINOR SPECIES
1261  if (m_numRxnMinorZeroed != 0) {
1262  // Calculate the chemical potential and reaction DeltaG for minor
1263  // species, if needed.
1264  if (iti != 0) {
1265  vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
1266  vcs_dfe(VCS_STATECALC_OLD, 1, 0, m_numSpeciesRdc);
1267  vcs_deltag(1, false, VCS_STATECALC_OLD);
1268  uptodate_minors = true;
1269  }
1270  if (m_debug_print_lvl >= 2) {
1271  plogf(" --- Equilibrium check for minor species: ");
1272  }
1273  for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
1274  size_t kspec = irxn + m_numComponents;
1275  if (m_speciesStatus[kspec] == VCS_SPECIES_MINOR && (fabs(m_deltaGRxn_new[irxn]) > m_tolmin)) {
1276  if (m_VCount->Its >= maxit) {
1277  solveFail = -1;
1278  // Clean up and exit code. -> Even though we have not
1279  // converged, we have run out of iterations !
1280  stage = RETURN_A;
1281  return;
1282  }
1283  if (m_debug_print_lvl >= 2) {
1284  plogf("%s failed\n", m_speciesName[m_indexRxnToSpecies[irxn]]);
1285  }
1286 
1287  // Set iti to zero to force a full calculation, and go back to
1288  // the main loop to do another iteration.
1289  iti = 0;
1290  stage = MAIN;
1291  return;
1292  }
1293  }
1294  if (m_debug_print_lvl >= 2) {
1295  plogf(" CONVERGENCE achieved\n");
1296  }
1297  }
1298 
1299  // FINAL ELEMENTAL ABUNDANCE CHECK
1300  // Recalculate the element abundance vector again
1301  vcs_updateVP(VCS_STATECALC_OLD);
1302  vcs_elab();
1303 
1304  // LEC is only true when we are near the end game
1305  if (lec) {
1306  if (!giveUpOnElemAbund) {
1307  if (m_debug_print_lvl >= 2) {
1308  plogf(" --- Check the Full Element Abundances: ");
1309  }
1310 
1311  // Final element abundance check: If we fail then we need to go back
1312  // and correct the element abundances, and then go do a major step
1313  if (! vcs_elabcheck(1)) {
1314  if (m_debug_print_lvl >= 2) {
1315  if (! vcs_elabcheck(0)) {
1316  plogf(" failed\n");
1317  } else {
1318  plogf(" passed for NC but failed for NE: RANGE ERROR\n");
1319  }
1320  }
1321  // delete?
1322  stage = ELEM_ABUND_CHECK;
1323  return;
1324  }
1325  if (m_debug_print_lvl >= 2) {
1326  plogf(" passed\n");
1327  }
1328  }
1329 
1330  // If we have deleted a species then we need to recheck the the deleted
1331  // species, before exiting
1332  if (m_numSpeciesRdc != m_numSpeciesTot) {
1333  stage = RECHECK_DELETED;
1334  return;
1335  }
1336  // Final checks are passed -> go check out
1337  stage = RETURN_A;
1338  }
1339  lec = true;
1340 }
1341 
1342 void VCS_SOLVE::solve_tp_elem_abund_check(size_t& iti, int& stage, bool& lec,
1343  bool& giveUpOnElemAbund,
1344  int& finalElemAbundAttempts,
1345  int& rangeErrorFound)
1346 {
1347 
1348  // HKM - Put in an element abundance check. The element abundances were
1349  // being corrected even if they were perfectly OK to start with. This is
1350  // actually an expensive operation, so I took it out. Also vcs_dfe() doesn't
1351  // need to be called if no changes were made.
1352  rangeErrorFound = 0;
1353  if (! vcs_elabcheck(1)) {
1354  bool ncBefore = vcs_elabcheck(0);
1355  vcs_elcorr(&m_sm[0], &m_wx[0]);
1356  bool ncAfter = vcs_elabcheck(0);
1357  bool neAfter = vcs_elabcheck(1);
1358 
1359  // Go back to evaluate the total moles of gas and liquid.
1360  vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
1361  vcs_dfe(VCS_STATECALC_OLD, 0, 0, m_numSpeciesRdc);
1362  vcs_deltag(0, false, VCS_STATECALC_OLD);
1363  if (!ncBefore) {
1364  if (ncAfter) {
1365  // We have breathed new life into the old problem. Now the
1366  // element abundances up to NC agree. Go back and restart the
1367  // main loop calculation, resetting the end conditions.
1368  lec = false;
1369  iti = 0;
1370  stage = MAIN;
1371  } else {
1372  // We are still hosed
1373  if (finalElemAbundAttempts >= 3) {
1374  giveUpOnElemAbund = true;
1375  stage = EQUILIB_CHECK;
1376  } else {
1377  finalElemAbundAttempts++;
1378  lec = false;
1379  iti = 0;
1380  stage = MAIN;
1381  }
1382  }
1383  return;
1384  } else if (ncAfter) {
1385  if (!neAfter) {
1386  // Probably an unrecoverable range error
1387  if (m_debug_print_lvl >= 2) {
1388  plogf(" --- vcs_solve_tp: RANGE SPACE ERROR ENCOUNTERED\n");
1389  plogf(" --- vcs_solve_tp: - Giving up on NE Element Abundance satisfaction \n");
1390  plogf(" --- vcs_solve_tp: - However, NC Element Abundance criteria is satisfied \n");
1391  plogf(" --- vcs_solve_tp: - Returning the calculated equilibrium condition ");
1392  plogendl();
1393  }
1394  rangeErrorFound = 1;
1395  giveUpOnElemAbund = true;
1396  }
1397 
1398  // Recovery of end element abundances -> go do equilibrium check
1399  // again and then check out.
1400  stage = EQUILIB_CHECK;
1401  return;
1402  }
1403  }
1404  // Calculate delta g's
1405  vcs_deltag(0, false, VCS_STATECALC_OLD);
1406  // Go back to equilibrium check as a prep to eventually checking out
1407  stage = EQUILIB_CHECK;
1408 }
1409 
1410 double VCS_SOLVE::vcs_minor_alt_calc(size_t kspec, size_t irxn, bool* do_delete,
1411  char* ANOTE) const
1412 {
1413  double w_kspec = m_molNumSpecies_old[kspec];
1414  double dg_irxn = m_deltaGRxn_old[irxn];
1415  size_t iph = m_phaseID[kspec];
1416 
1417  *do_delete = false;
1418  if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
1419  if (w_kspec <= 0.0) {
1421  }
1422  dg_irxn = std::max(dg_irxn, -200.0);
1423  if (ANOTE) {
1424  sprintf(ANOTE,"minor species alternative calc");
1425  }
1426  if (dg_irxn >= 23.0) {
1427  double molNum_kspec_new = w_kspec * 1.0e-10;
1428  if (w_kspec < VCS_DELETE_MINORSPECIES_CUTOFF) {
1429  // delete the species from the current list of active species,
1430  // because its concentration has gotten too small.
1431  *do_delete = true;
1432  return - w_kspec;
1433  }
1434  return molNum_kspec_new - w_kspec;
1435  } else {
1436  if (fabs(dg_irxn) <= m_tolmin2) {
1437  return 0.0;
1438  }
1439  }
1440 
1441  // get the diagonal of the activity coefficient Jacobian
1442  double s = m_np_dLnActCoeffdMolNum(kspec,kspec) / m_tPhaseMoles_old[iph];
1443 
1444  // We fit it to a power law approximation of the activity coefficient
1445  //
1446  // gamma = gamma_0 * ( x / x0)**a
1447  //
1448  // where a is forced to be a little bit greater than -1.
1449  // We do this so that the resulting expression is always nonnegative
1450  // We then solve the resulting calculation:
1451  //
1452  // gamma * x = gamma_0 * x0 exp (-deltaG/RT);
1453  double a = clip(w_kspec * s, -1.0+1e-8, 100.0);
1454  double tmp = clip(-dg_irxn / (1.0 + a), -200.0, 200.0);
1455  double wTrial = w_kspec * exp(tmp);
1456  double molNum_kspec_new = wTrial;
1457 
1458  if (wTrial > 100. * w_kspec) {
1459  double molNumMax = 0.0001 * m_tPhaseMoles_old[iph];
1460  if (molNumMax < 100. * w_kspec) {
1461  molNumMax = 100. * w_kspec;
1462  }
1463  if (wTrial > molNumMax) {
1464  molNum_kspec_new = molNumMax;
1465  } else {
1466  molNum_kspec_new = wTrial;
1467  }
1468  } else if (1.0E10 * wTrial < w_kspec) {
1469  molNum_kspec_new= 1.0E-10 * w_kspec;
1470  } else {
1471  molNum_kspec_new = wTrial;
1472  }
1473 
1474  if ((molNum_kspec_new) < VCS_DELETE_MINORSPECIES_CUTOFF) {
1475  // delete the species from the current list of active species,
1476  // because its concentration has gotten too small.
1477  *do_delete = true;
1478  return - w_kspec;
1479  }
1480  return molNum_kspec_new - w_kspec;
1481 
1482  } else {
1483  // Voltage calculation
1484  // Need to check the sign -> This is good for electrons
1485  double dx = m_deltaGRxn_old[irxn]/ m_Faraday_dim;
1486  if (ANOTE) {
1487  sprintf(ANOTE,"voltage species alternative calc");
1488  }
1489  return dx;
1490  }
1491 }
1492 
1493 int VCS_SOLVE::delta_species(const size_t kspec, double* const delta_ptr)
1494 {
1495  size_t irxn = kspec - m_numComponents;
1496  int retn = 1;
1497  AssertThrowMsg(kspec >= m_numComponents, "VCS_SOLVE::delta_species",
1498  "delete_species() ERROR: called for a component {}", kspec);
1499  if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
1500  // Attempt the given dx. If it doesn't work, try to see if a smaller one
1501  // would work,
1502  double dx = *delta_ptr;
1503  double* sc_irxn = m_stoichCoeffRxnMatrix.ptrColumn(irxn);
1504  for (size_t j = 0; j < m_numComponents; ++j) {
1505  if (m_molNumSpecies_old[j] > 0.0) {
1506  double tmp = sc_irxn[j] * dx;
1507  if (-tmp > m_molNumSpecies_old[j]) {
1508  retn = 0;
1509  dx = std::min(dx, - m_molNumSpecies_old[j] / sc_irxn[j]);
1510  }
1511  }
1512 
1513  // If the component has a zero concentration and is a reactant
1514  // in the formation reaction, then dx == 0.0, and we just return.
1515  if (m_molNumSpecies_old[j] <= 0.0 && sc_irxn[j] < 0.0) {
1516  *delta_ptr = 0.0;
1517  return 0;
1518  }
1519  }
1520 
1521  // ok, we found a positive dx. implement it.
1522  *delta_ptr = dx;
1523  m_molNumSpecies_old[kspec] += dx;
1524  size_t iph = m_phaseID[kspec];
1525  m_tPhaseMoles_old[iph] += dx;
1526  vcs_setFlagsVolPhase(iph, false, VCS_STATECALC_OLD);
1527 
1528  for (size_t j = 0; j < m_numComponents; ++j) {
1529  double tmp = sc_irxn[j] * dx;
1530  if (tmp != 0.0) {
1531  iph = m_phaseID[j];
1532  m_molNumSpecies_old[j] += tmp;
1533  m_tPhaseMoles_old[iph] += tmp;
1534  vcs_setFlagsVolPhase(iph, false, VCS_STATECALC_OLD);
1535  m_molNumSpecies_old[j] = std::max(m_molNumSpecies_old[j], 0.0);
1536  }
1537  }
1538  }
1539  return retn;
1540 }
1541 
1542 int VCS_SOLVE::vcs_zero_species(const size_t kspec)
1543 {
1544  int retn = 1;
1545 
1546  // Calculate a delta that will eliminate the species.
1547  if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
1548  double dx = -m_molNumSpecies_old[kspec];
1549  if (dx != 0.0) {
1550  retn = delta_species(kspec, &dx);
1551  if (!retn && m_debug_print_lvl >= 1) {
1552  plogf("vcs_zero_species: Couldn't zero the species %d, "
1553  "did delta of %g. orig conc of %g",
1554  kspec, dx, m_molNumSpecies_old[kspec] + dx);
1555  plogendl();
1556  }
1557  }
1558  }
1559  return retn;
1560 }
1561 
1562 int VCS_SOLVE::vcs_delete_species(const size_t kspec)
1563 {
1564  const size_t klast = m_numSpeciesRdc - 1;
1565  const size_t iph = m_phaseID[kspec];
1566  vcs_VolPhase* const Vphase = m_VolPhaseList[iph];
1567  const size_t irxn = kspec - m_numComponents;
1568 
1569  // Zero the concentration of the species.
1570  // -> This zeroes w[kspec] and modifies m_tPhaseMoles_old[]
1571  const int retn = vcs_zero_species(kspec);
1572  if (!retn) {
1573  throw CanteraError("VCS_SOLVE::vcs_delete_species",
1574  "Failed to delete a species!");
1575  }
1576 
1577  // Decrement the minor species counter if the current species is a minor
1578  // species
1579  if (m_speciesStatus[kspec] != VCS_SPECIES_MAJOR) {
1580  --m_numRxnMinorZeroed;
1581  }
1582  m_speciesStatus[kspec] = VCS_SPECIES_DELETED;
1583  m_deltaGRxn_new[irxn] = 0.0;
1584  m_deltaGRxn_old[irxn] = 0.0;
1585  m_feSpecies_new[kspec] = 0.0;
1586  m_feSpecies_old[kspec] = 0.0;
1587  m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec];
1588 
1589  // Rearrange the data if the current species isn't the last active species.
1590  if (kspec != klast) {
1591  vcs_switch_pos(true, klast, kspec);
1592  }
1593 
1594  // Adjust the total moles in a phase downwards.
1595  Vphase->setMolesFromVCSCheck(VCS_STATECALC_OLD, &m_molNumSpecies_old[0],
1596  &m_tPhaseMoles_old[0]);
1597 
1598  // Adjust the current number of active species and reactions counters
1599  --m_numRxnRdc;
1600  --m_numSpeciesRdc;
1601 
1602  // Check to see whether we have just annihilated a multispecies phase. If it
1603  // is extinct, call the delete_multiphase() function.
1604  if (! m_SSPhase[klast] && Vphase->exists() != VCS_PHASE_EXIST_ALWAYS) {
1605  bool stillExists = false;
1606  for (size_t k = 0; k < m_numSpeciesRdc; k++) {
1607  if (m_speciesUnknownType[k] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE &&
1608  m_phaseID[k] == iph && m_molNumSpecies_old[k] > 0.0) {
1609  stillExists = true;
1610  break;
1611  }
1612  }
1613  if (!stillExists) {
1614  vcs_delete_multiphase(iph);
1615  }
1616  }
1617 
1618  // When the total number of noncomponent species is zero, we have to signal
1619  // the calling code
1620  return (m_numRxnRdc == 0);
1621 }
1622 
1623 void VCS_SOLVE::vcs_reinsert_deleted(size_t kspec)
1624 {
1625  size_t iph = m_phaseID[kspec];
1626  if (m_debug_print_lvl >= 2) {
1627  plogf(" --- Add back a deleted species: %-12s\n", m_speciesName[kspec]);
1628  }
1629 
1630  // Set the species back to minor species status
1631  // this adjusts m_molNumSpecies_old[] and m_tPhaseMoles_old[]
1632  // HKM -> make this a relative mole number!
1633  double dx = m_tPhaseMoles_old[iph] * VCS_RELDELETE_SPECIES_CUTOFF * 10.;
1634  delta_species(kspec, &dx);
1635  m_speciesStatus[kspec] = VCS_SPECIES_MINOR;
1636 
1637  if (m_SSPhase[kspec]) {
1638  m_speciesStatus[kspec] = VCS_SPECIES_MAJOR;
1639  --m_numRxnMinorZeroed;
1640  }
1641 
1642  vcs_VolPhase* Vphase = m_VolPhaseList[iph];
1644  &m_molNumSpecies_old[0],
1645  &m_tPhaseMoles_old[0]);
1646 
1647  // We may have popped a multispecies phase back into existence. If we did,
1648  // we have to check the other species in that phase. Take care of the
1649  // m_speciesStatus[] flag. The value of m_speciesStatus[] must change from
1650  // VCS_SPECIES_ZEROEDPHASE to VCS_SPECIES_ZEROEDMS for those other species.
1651  if (! m_SSPhase[kspec]) {
1652  if (Vphase->exists() == VCS_PHASE_EXIST_NO) {
1654  for (size_t k = 0; k < m_numSpeciesTot; k++) {
1655  if (m_phaseID[k] == iph && m_speciesStatus[k] != VCS_SPECIES_DELETED) {
1656  m_speciesStatus[k] = VCS_SPECIES_MINOR;
1657  }
1658  }
1659  }
1660  } else {
1662  }
1663 
1664  ++m_numRxnRdc;
1665  ++m_numSpeciesRdc;
1666  ++m_numRxnMinorZeroed;
1667 
1668  if (kspec != (m_numSpeciesRdc - 1)) {
1669  // Rearrange both the species and the non-component global data
1670  vcs_switch_pos(true, (m_numSpeciesRdc - 1), kspec);
1671  }
1672 }
1673 
1674 bool VCS_SOLVE::vcs_delete_multiphase(const size_t iph)
1675 {
1676  vcs_VolPhase* Vphase = m_VolPhaseList[iph];
1677  bool successful = true;
1678 
1679  // set the phase existence flag to dead
1680  Vphase->setTotalMoles(0.0);
1681  if (m_debug_print_lvl >= 2) {
1682  plogf(" --- delete_multiphase %d, %s\n", iph, Vphase->PhaseName);
1683  }
1684 
1685  // Loop over all of the species in the phase.
1686  for (size_t kspec = m_numComponents; kspec < m_numSpeciesRdc; ++kspec) {
1687  if (m_phaseID[kspec] == iph) {
1688  if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
1689  // calculate an extent of rxn, dx, that zeroes out the species.
1690  double dx = - m_molNumSpecies_old[kspec];
1691  double dxTent = dx;
1692 
1693  int retn = delta_species(kspec, &dxTent);
1694  if (retn != 1) {
1695  successful = false;
1696  if (m_debug_print_lvl >= 2) {
1697  plogf(" --- delete_multiphase %d, %s ERROR problems deleting species %s\n",
1698  iph, Vphase->PhaseName, m_speciesName[kspec]);
1699  plogf(" --- delta attempted: %g achieved: %g "
1700  " Zeroing it manually\n", dx, dxTent);
1701  }
1702  m_molNumSpecies_old[kspec] = 0.0;
1703  m_molNumSpecies_new[kspec] = 0.0;
1704  m_deltaMolNumSpecies[kspec] = 0.0;
1705  // recover the total phase moles.
1706  vcs_tmoles();
1707  } else {
1708  // Set the mole number of that species to zero.
1709  m_molNumSpecies_old[kspec] = 0.0;
1710  m_molNumSpecies_new[kspec] = 0.0;
1711  m_deltaMolNumSpecies[kspec] = 0.0;
1712  }
1713 
1714  // Change the status flag of the species to that of an zeroed
1715  // phase
1716  m_speciesStatus[kspec] = VCS_SPECIES_ZEROEDMS;
1717  }
1718  }
1719  }
1720 
1721  double dxPerm = 0.0, dxPerm2 = 0.0;
1722  for (size_t kcomp = 0; kcomp < m_numComponents; ++kcomp) {
1723  if (m_phaseID[kcomp] == iph) {
1724  if (m_debug_print_lvl >= 2) {
1725  plogf(" --- delete_multiphase One of the species is a component %d - %s with mole number %g\n",
1726  kcomp, m_speciesName[kcomp], m_molNumSpecies_old[kcomp]);
1727  }
1728  if (m_molNumSpecies_old[kcomp] != 0.0) {
1729  for (size_t kspec = m_numComponents; kspec < m_numSpeciesRdc; ++kspec) {
1730  size_t irxn = kspec - m_numComponents;
1731  if (m_phaseID[kspec] != iph) {
1732  if (m_stoichCoeffRxnMatrix(kcomp,irxn) != 0.0) {
1733  double dxWant = -m_molNumSpecies_old[kcomp] / m_stoichCoeffRxnMatrix(kcomp,irxn);
1734  if (dxWant + m_molNumSpecies_old[kspec] < 0.0) {
1735  dxPerm = -m_molNumSpecies_old[kspec];
1736  }
1737  for (size_t jcomp = 0; kcomp < m_numComponents; ++kcomp) {
1738  if (jcomp != kcomp) {
1739  if (m_phaseID[jcomp] == iph) {
1740  dxPerm = 0.0;
1741  } else {
1742  double dj = dxWant * m_stoichCoeffRxnMatrix(jcomp,irxn);
1743  if (dj + m_molNumSpecies_old[kcomp] < 0.0) {
1744  dxPerm2 = -m_molNumSpecies_old[kcomp] / m_stoichCoeffRxnMatrix(jcomp,irxn);
1745  }
1746  if (fabs(dxPerm2) < fabs(dxPerm)) {
1747  dxPerm = dxPerm2;
1748  }
1749  }
1750  }
1751  }
1752  }
1753  if (dxPerm != 0.0) {
1754  delta_species(kspec, &dxPerm);
1755  }
1756  }
1757  }
1758 
1759  }
1760  if (m_molNumSpecies_old[kcomp] != 0.0) {
1761  if (m_debug_print_lvl >= 2) {
1762  plogf(" --- delete_multiphase One of the species is a component %d - %s still with mole number %g\n",
1763  kcomp, m_speciesName[kcomp], m_molNumSpecies_old[kcomp]);
1764  plogf(" --- zeroing it \n");
1765  }
1766  m_molNumSpecies_old[kcomp] = 0.0;
1767  }
1768  m_speciesStatus[kcomp] = VCS_SPECIES_ZEROEDMS;
1769  }
1770  }
1771 
1772  // Loop over all of the inactive species in the phase: Right now we
1773  // reinstate all species in a deleted multiphase. We may only want to
1774  // reinstate the "major ones" in the future. Note, species in phases with
1775  // zero mole numbers are still considered active. Whether the phase pops
1776  // back into existence or not is checked as part of the main iteration loop.
1777  for (size_t kspec = m_numSpeciesRdc; kspec < m_numSpeciesTot; ++kspec) {
1778  if (m_phaseID[kspec] == iph) {
1779  m_molNumSpecies_old[kspec] = 0.0;
1780  m_molNumSpecies_new[kspec] = 0.0;
1781  m_deltaMolNumSpecies[kspec] = 0.0;
1782  m_speciesStatus[kspec] = VCS_SPECIES_ZEROEDMS;
1783 
1784  ++m_numRxnRdc;
1785  ++m_numSpeciesRdc;
1786  if (m_debug_print_lvl >= 2) {
1787  plogf(" --- Make %s", m_speciesName[kspec]);
1788  plogf(" an active but zeroed species because its phase "
1789  "was zeroed\n");
1790  }
1791  if (kspec != (m_numSpeciesRdc - 1)) {
1792  // Rearrange both the species and the non-component global data
1793  vcs_switch_pos(true, (m_numSpeciesRdc - 1), kspec);
1794  }
1795  }
1796  }
1797 
1798  // Zero out the total moles counters for the phase
1799  m_tPhaseMoles_old[iph] = 0.0;
1800  m_tPhaseMoles_new[iph] = 0.0;
1801  m_deltaPhaseMoles[iph] = 0.0;
1802 
1803  // Upload the state to the VP object
1804  Vphase->setTotalMoles(0.0);
1805  return successful;
1806 }
1807 
1808 int VCS_SOLVE::vcs_recheck_deleted()
1809 {
1810  vector_fp& xtcutoff = m_TmpPhase;
1811  if (m_debug_print_lvl >= 2) {
1812  plogf(" --- Start rechecking deleted species in multispec phases\n");
1813  }
1814  if (m_numSpeciesRdc == m_numSpeciesTot) {
1815  return 0;
1816  }
1817 
1818  // Use the standard chemical potentials for the chemical potentials of
1819  // deleted species. Then, calculate Delta G for for formation reactions.
1820  // Note: fe[] here includes everything except for the ln(x[i]) term
1821  for (size_t kspec = m_numSpeciesRdc; kspec < m_numSpeciesTot; ++kspec) {
1822  size_t iph = m_phaseID[kspec];
1823  m_feSpecies_new[kspec] = (m_SSfeSpecies[kspec] + log(m_actCoeffSpecies_old[kspec])
1824  - m_lnMnaughtSpecies[kspec]
1825  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iph]);
1826  }
1827 
1828  // Recalculate the DeltaG's of the formation reactions for the deleted
1829  // species in the mechanism
1830  vcs_deltag(0, true, VCS_STATECALC_NEW);
1831 
1832  for (size_t iph = 0; iph < m_numPhases; iph++) {
1833  if (m_tPhaseMoles_old[iph] > 0.0) {
1834  xtcutoff[iph] = log(1.0 / VCS_RELDELETE_SPECIES_CUTOFF);
1835  } else {
1836  xtcutoff[iph] = 0.0;
1837  }
1838  }
1839 
1840  // We are checking the equation:
1841  //
1842  // sum_u = sum_j_comp [ sigma_i_j * u_j ]
1843  // = u_i_O + log((AC_i * W_i)/m_tPhaseMoles_old)
1844  //
1845  // by first evaluating:
1846  //
1847  // DG_i_O = u_i_O - sum_u.
1848  //
1849  // Then, if TL is zero, the phase pops into existence if DG_i_O < 0. Also,
1850  // if the phase exists, then we check to see if the species can have a mole
1851  // number larger than VCS_DELETE_SPECIES_CUTOFF (default value = 1.0E-32).
1852  //
1853  // HKM: This seems to be an inconsistency in the algorithm here that needs
1854  // correcting. The requirement above may bypass some multiphases which
1855  // should exist. The real requirement for the phase to exist is:
1856  //
1857  // sum_i_in_phase [ exp(-DG_i_O) ] >= 1.0
1858  //
1859  // Thus, we need to amend the code. Also nonideal solutions will tend to
1860  // complicate matters severely also.
1861  int npb = 0;
1862  for (size_t irxn = m_numRxnRdc; irxn < m_numRxnTot; ++irxn) {
1863  size_t kspec = m_indexRxnToSpecies[irxn];
1864  size_t iph = m_phaseID[kspec];
1865  if (m_tPhaseMoles_old[iph] == 0.0) {
1866  if (m_deltaGRxn_new[irxn] < 0.0) {
1867  vcs_reinsert_deleted(kspec);
1868  npb++;
1869  } else {
1870  m_molNumSpecies_old[kspec] = 0.0;
1871  }
1872  } else if (m_tPhaseMoles_old[iph] > 0.0) {
1873  if (m_deltaGRxn_new[irxn] < xtcutoff[iph]) {
1874  vcs_reinsert_deleted(kspec);
1875  npb++;
1876  }
1877  }
1878  }
1879  return npb;
1880 }
1881 
1882 bool VCS_SOLVE::recheck_deleted_phase(const int iphase)
1883 {
1884  warn_deprecated("VCS_SOLVE::recheck_deleted_phase",
1885  "Unused. To be removed after Cantera 2.3.");
1886  // Check first to see if the phase is in fact deleted
1887  const vcs_VolPhase* Vphase = m_VolPhaseList[iphase];
1888  if (Vphase->exists() != VCS_PHASE_EXIST_NO) {
1889  return false;
1890  }
1891  if (Vphase->exists() == VCS_PHASE_EXIST_ZEROEDPHASE) {
1892  return false;
1893  }
1894  if (Vphase->m_singleSpecies) {
1895  size_t kspec = Vphase->spGlobalIndexVCS(0);
1896  size_t irxn = kspec + m_numComponents;
1897  if (m_deltaGRxn_old[irxn] < 0.0) {
1898  return true;
1899  }
1900  return false;
1901  }
1902 
1903  double phaseDG = 1.0;
1904  for (size_t kk = 0; kk < Vphase->nSpecies(); kk++) {
1905  size_t kspec = Vphase->spGlobalIndexVCS(kk);
1906  size_t irxn = kspec + m_numComponents;
1907  m_deltaGRxn_old[irxn] = clip(m_deltaGRxn_old[irxn], -50.0, 50.0);
1908  phaseDG -= exp(-m_deltaGRxn_old[irxn]);
1909  }
1910 
1911  if (phaseDG < 0.0) {
1912  return true;
1913  }
1914  return false;
1915 }
1916 
1917 size_t VCS_SOLVE::vcs_add_all_deleted()
1918 {
1919  if (m_numSpeciesRdc == m_numSpeciesTot) {
1920  return 0;
1921  }
1922 
1923  // Use the standard chemical potentials for the chemical potentials of
1924  // deleted species. Then, calculate Delta G for for formation reactions. We
1925  // are relying here on a old saved value of m_actCoeffSpecies_old[kspec]
1926  // being sufficiently good. Note, we will recalculate everything at the end
1927  // of the routine.
1928  m_molNumSpecies_new = m_molNumSpecies_old;
1929  for (int cits = 0; cits < 3; cits++) {
1930  for (size_t kspec = m_numSpeciesRdc; kspec < m_numSpeciesTot; ++kspec) {
1931  size_t iph = m_phaseID[kspec];
1932  vcs_VolPhase* Vphase = m_VolPhaseList[iph];
1933  if (m_molNumSpecies_new[kspec] == 0.0) {
1934  m_molNumSpecies_new[kspec] = VCS_DELETE_MINORSPECIES_CUTOFF * 1.0E-10;
1935  }
1936  if (!Vphase->m_singleSpecies) {
1937  Vphase->sendToVCS_ActCoeff(VCS_STATECALC_NEW, &m_actCoeffSpecies_new[0]);
1938  }
1939  m_feSpecies_new[kspec] = (m_SSfeSpecies[kspec] + log(m_actCoeffSpecies_new[kspec]) - m_lnMnaughtSpecies[kspec]
1940  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iph]);
1941  }
1942 
1943  // Recalculate the DeltaG's of the formation reactions for the deleted
1944  // species in the mechanism
1945  vcs_deltag(0, true, VCS_STATECALC_NEW);
1946  for (size_t irxn = m_numRxnRdc; irxn < m_numRxnTot; ++irxn) {
1947  size_t kspec = m_indexRxnToSpecies[irxn];
1948  size_t iph = m_phaseID[kspec];
1949  if (m_tPhaseMoles_old[iph] > 0.0) {
1950  double maxDG = std::min(m_deltaGRxn_new[irxn], 690.0);
1951  double dx = m_tPhaseMoles_old[iph] * exp(- maxDG);
1952  m_molNumSpecies_new[kspec] = dx;
1953  }
1954  }
1955  }
1956  for (size_t irxn = m_numRxnRdc; irxn < m_numRxnTot; ++irxn) {
1957  size_t kspec = m_indexRxnToSpecies[irxn];
1958  size_t iph = m_phaseID[kspec];
1959  if (m_tPhaseMoles_old[iph] > 0.0) {
1960  double dx = m_molNumSpecies_new[kspec];
1961  size_t retn = delta_species(kspec, &dx);
1962  if (retn == 0) {
1963  if (m_debug_print_lvl) {
1964  plogf(" --- add_deleted(): delta_species() failed for species %s (%d) with mol number %g\n",
1965  m_speciesName[kspec], kspec, dx);
1966  }
1967  if (dx > 1.0E-50) {
1968  dx = 1.0E-50;
1969  retn = delta_species(kspec, &dx);
1970  if (retn == 0 && m_debug_print_lvl) {
1971  plogf(" --- add_deleted(): delta_species() failed for species %s (%d) with mol number %g\n",
1972  m_speciesName[kspec], kspec, dx);
1973  }
1974  }
1975  }
1976  if (m_debug_print_lvl >= 2) {
1977  if (retn != 0) {
1978  plogf(" --- add_deleted(): species %s added back in with mol number %g",
1979  m_speciesName[kspec], dx);
1980  plogendl();
1981  } else {
1982  plogf(" --- add_deleted(): species %s failed to be added back in");
1983  plogendl();
1984  }
1985  }
1986  }
1987  }
1988  vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
1989  vcs_dfe(VCS_STATECALC_OLD, 0, 0, m_numSpeciesTot);
1990  vcs_deltag(0, true, VCS_STATECALC_OLD);
1991 
1992  size_t retn = 0;
1993  for (size_t irxn = m_numRxnRdc; irxn < m_numRxnTot; ++irxn) {
1994  size_t kspec = m_indexRxnToSpecies[irxn];
1995  size_t iph = m_phaseID[kspec];
1996  if (m_tPhaseMoles_old[iph] > 0.0 && fabs(m_deltaGRxn_old[irxn]) > m_tolmin) {
1997  if (((m_molNumSpecies_old[kspec] * exp(-m_deltaGRxn_old[irxn])) >
1999  (m_molNumSpecies_old[kspec] > VCS_DELETE_MINORSPECIES_CUTOFF)) {
2000  retn++;
2001  if (m_debug_print_lvl >= 2) {
2002  plogf(" --- add_deleted(): species %s with mol number %g not converged: DG = %g",
2003  m_speciesName[kspec], m_molNumSpecies_old[kspec],
2004  m_deltaGRxn_old[irxn]);
2005  plogendl();
2006  }
2007  }
2008  }
2009  }
2010  return retn;
2011 }
2012 
2013 bool VCS_SOLVE::vcs_globStepDamp()
2014 {
2015  double* dptr = &m_deltaGRxn_new[0];
2016 
2017  // CALCULATE SLOPE AT END OF THE STEP
2018  double s2 = 0.0;
2019  for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
2020  size_t kspec = irxn + m_numComponents;
2021  if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
2022  s2 += dptr[irxn] * m_deltaMolNumSpecies[kspec];
2023  }
2024  }
2025 
2026  // CALCULATE ORIGINAL SLOPE
2027  double s1 = 0.0;
2028  dptr = &m_deltaGRxn_old[0];
2029  for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
2030  size_t kspec = irxn + m_numComponents;
2031  if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
2032  s1 += dptr[irxn] * m_deltaMolNumSpecies[kspec];
2033  }
2034  }
2035 
2036  if (m_debug_print_lvl >= 2) {
2037  plogf(" --- subroutine FORCE: Beginning Slope = %g\n", s1);
2038  plogf(" --- subroutine FORCE: End Slope = %g\n", s2);
2039  }
2040 
2041  if (s1 > 0.0) {
2042  if (m_debug_print_lvl >= 2) {
2043  plogf(" --- subroutine FORCE produced no adjustments,");
2044  if (s1 < 1.0E-40) {
2045  plogf(" s1 positive but really small");
2046  } else {
2047  plogf(" failed s1 test");
2048  }
2049  plogendl();
2050  }
2051  return false;
2052  }
2053 
2054  if (s2 <= 0.0) {
2055  if (m_debug_print_lvl >= 2) {
2056  plogf(" --- subroutine FORCE produced no adjustments, s2 < 0");
2057  plogendl();
2058  }
2059  return false;
2060  }
2061 
2062  // FIT PARABOLA
2063  double al = 1.0;
2064  if (fabs(s1 -s2) > 1.0E-200) {
2065  al = s1 / (s1 - s2);
2066  }
2067  if (al >= 0.95 || al < 0.0) {
2068  if (m_debug_print_lvl >= 2) {
2069  plogf(" --- subroutine FORCE produced no adjustments (al = %g)\n", al);
2070  }
2071  return false;
2072  }
2073  if (m_debug_print_lvl >= 2) {
2074  plogf(" --- subroutine FORCE produced a damping factor = %g\n", al);
2075  }
2076 
2077  // ADJUST MOLE NUMBERS, CHEM. POT
2078  if (m_debug_print_lvl >= 2) {
2079  m_deltaGRxn_tmp = m_deltaGRxn_new;
2080  }
2081 
2082  dptr = &m_molNumSpecies_new[0];
2083  for (size_t kspec = 0; kspec < m_numSpeciesRdc; ++kspec) {
2084  m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] +
2085  al * m_deltaMolNumSpecies[kspec];
2086  }
2087  for (size_t iph = 0; iph < m_numPhases; iph++) {
2088  m_tPhaseMoles_new[iph] = m_tPhaseMoles_old[iph] + al * m_deltaPhaseMoles[iph];
2089  }
2090  vcs_updateVP(VCS_STATECALC_NEW);
2091 
2092  if (m_debug_print_lvl >= 2) {
2093  plogf(" --- subroutine FORCE adjusted the mole "
2094  "numbers, AL = %10.3f\n", al);
2095  }
2096 
2097  // Because we changed the mole numbers, we need to calculate the chemical
2098  // potentials again. If a major-only step is being carried out, then we
2099  // don't need to update the minor noncomponents.
2100  vcs_setFlagsVolPhases(false, VCS_STATECALC_NEW);
2101  vcs_dfe(VCS_STATECALC_NEW, 0, 0, m_numSpeciesRdc);
2102 
2103  // Evaluate DeltaG for all components if ITI=0, and for major components
2104  // only if ITI NE 0
2105  vcs_deltag(0, false, VCS_STATECALC_NEW);
2106 
2107  dptr = &m_deltaGRxn_new[0];
2108  s2 = 0.0;
2109  for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
2110  size_t kspec = irxn + m_numComponents;
2111  if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
2112  s2 += dptr[irxn] * m_deltaMolNumSpecies[kspec];
2113  }
2114  }
2115 
2116  if (m_debug_print_lvl >= 2) {
2117  plogf(" --- subroutine FORCE: Adj End Slope = %g", s2);
2118  plogendl();
2119  }
2120  return true;
2121 }
2122 
2123 int VCS_SOLVE::vcs_basopt(const bool doJustComponents, double aw[], double sa[], double sm[],
2124  double ss[], double test, bool* const usedZeroedSpecies)
2125 {
2126  size_t k;
2127  size_t juse = npos;
2128  size_t jlose = npos;
2129  DenseMatrix C;
2130  clockWC tickTock;
2131  if (m_debug_print_lvl >= 2) {
2132  plogf(" ");
2133  for (size_t i=0; i<77; i++) {
2134  plogf("-");
2135  }
2136  plogf("\n");
2137  plogf(" --- Subroutine BASOPT called to ");
2138  if (doJustComponents) {
2139  plogf("calculate the number of components\n");
2140  } else {
2141  plogf("reevaluate the components\n");
2142  }
2143  if (m_debug_print_lvl >= 2) {
2144  plogf("\n");
2145  plogf(" --- Formula Matrix used in BASOPT calculation\n");
2146  plogf(" --- Active | ");
2147  for (size_t j = 0; j < m_numElemConstraints; j++) {
2148  plogf(" %1d ", m_elementActive[j]);
2149  }
2150  plogf("\n");
2151  plogf(" --- Species | ");
2152  for (size_t j = 0; j < m_numElemConstraints; j++) {
2153  writelog(" {:>8.8s}", m_elementName[j]);
2154  }
2155  plogf("\n");
2156  for (k = 0; k < m_numSpeciesTot; k++) {
2157  writelog(" --- {:>11.11s} | ", m_speciesName[k]);
2158  for (size_t j = 0; j < m_numElemConstraints; j++) {
2159  plogf(" %8.2g", m_formulaMatrix(k,j));
2160  }
2161  plogf("\n");
2162  }
2163  plogendl();
2164  }
2165  }
2166 
2167  // Calculate the maximum value of the number of components possible. It's
2168  // equal to the minimum of the number of elements and the number of total
2169  // species.
2170  size_t ncTrial = std::min(m_numElemConstraints, m_numSpeciesTot);
2171  m_numComponents = ncTrial;
2172  *usedZeroedSpecies = false;
2173  vector_int ipiv(ncTrial);
2174 
2175  // Use a temporary work array for the mole numbers, aw[]
2176  std::copy(m_molNumSpecies_old.begin(),
2177  m_molNumSpecies_old.begin() + m_numSpeciesTot, aw);
2178 
2179  // Take out the Voltage unknowns from consideration
2180  for (k = 0; k < m_numSpeciesTot; k++) {
2181  if (m_speciesUnknownType[k] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
2182  aw[k] = test;
2183  }
2184  }
2185 
2186  size_t jr = 0;
2187 
2188  // Top of a loop of some sort based on the index JR. JR is the current
2189  // number of component species found.
2190  while (jr < ncTrial) {
2191  // Top of another loop point based on finding a linearly independent
2192  // species
2193  while (true) {
2194  // Search the remaining part of the mole fraction vector, AW, for
2195  // the largest remaining species. Return its identity in K. The
2196  // first search criteria is always the largest positive magnitude of
2197  // the mole number.
2198  k = vcs_basisOptMax(aw, jr, m_numSpeciesTot);
2199 
2200  // The fun really starts when you have run out of species that have
2201  // a significant concentration. It becomes extremely important to
2202  // make a good choice of which species you want to pick to fill out
2203  // the basis. Basically, you don't want to use species with elements
2204  // abundances which aren't pegged to zero. This means that those
2205  // modes will never be allowed to grow. You want to have the best
2206  // chance that the component will grow positively.
2207  //
2208  // Suppose you start with CH4, N2, as the only species with nonzero
2209  // compositions. You have the following abundances:
2210  //
2211  // Abundances:
2212  // ----------------
2213  // C 2.0
2214  // N 2.0
2215  // H 4.0
2216  // O 0.0
2217  //
2218  // For example, Make the following choice:
2219  //
2220  // CH4 N2 O choose -> OH
2221  // or
2222  // CH4 N2 O choose -> H2
2223  //
2224  // OH and H2 both fill out the basis. They will pass the algorithm.
2225  // However, choosing OH as the next species will create a situation
2226  // where H2 can not grow in concentration. This happened in
2227  // practice, btw. The reason is that the formation reaction for H2
2228  // will cause one of the component species to go negative.
2229  //
2230  // The basic idea here is to pick a simple species whose mole number
2231  // can grow according to the element compositions. Candidates are
2232  // still filtered according to their linear independence.
2233  //
2234  // Note, if there is electronic charge and the electron species, you
2235  // should probably pick the electron as a component, if it linearly
2236  // independent. The algorithm below will do this automagically.
2237  if ((aw[k] != test) && aw[k] < VCS_DELETE_MINORSPECIES_CUTOFF) {
2238  *usedZeroedSpecies = true;
2239  double maxConcPossKspec = 0.0;
2240  double maxConcPoss = 0.0;
2241  size_t kfound = npos;
2242  int minNonZeroes = 100000;
2243  int nonZeroesKspec = 0;
2244  for (size_t kspec = ncTrial; kspec < m_numSpeciesTot; kspec++) {
2245  if (aw[kspec] >= 0.0 && m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
2246  maxConcPossKspec = 1.0E10;
2247  nonZeroesKspec = 0;
2248  for (size_t j = 0; j < m_numElemConstraints; ++j) {
2249  if (m_elementActive[j] && m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
2250  double nu = m_formulaMatrix(kspec,j);
2251  if (nu != 0.0) {
2252  nonZeroesKspec++;
2253  maxConcPossKspec = std::min(m_elemAbundancesGoal[j] / nu, maxConcPossKspec);
2254  }
2255  }
2256  }
2257  if ((maxConcPossKspec >= maxConcPoss) || (maxConcPossKspec > 1.0E-5)) {
2258  if (nonZeroesKspec <= minNonZeroes) {
2259  if (kfound == npos || nonZeroesKspec < minNonZeroes) {
2260  kfound = kspec;
2261  } else {
2262  // ok we are sitting pretty equal here
2263  // decide on the raw ss Gibbs energy
2264  if (m_SSfeSpecies[kspec] <= m_SSfeSpecies[kfound]) {
2265  kfound = kspec;
2266  }
2267  }
2268  }
2269  minNonZeroes = std::min(minNonZeroes, nonZeroesKspec);
2270  maxConcPoss = std::max(maxConcPoss, maxConcPossKspec);
2271  }
2272  }
2273  }
2274  if (kfound == npos) {
2275  double gmin = 0.0;
2276  kfound = k;
2277  for (size_t kspec = ncTrial; kspec < m_numSpeciesTot; kspec++) {
2278  if (aw[kspec] >= 0.0) {
2279  size_t irxn = kspec - ncTrial;
2280  if (m_deltaGRxn_new[irxn] < gmin) {
2281  gmin = m_deltaGRxn_new[irxn];
2282  kfound = kspec;
2283  }
2284  }
2285  }
2286  }
2287  k = kfound;
2288  }
2289 
2290  if (aw[k] == test) {
2291  m_numComponents = jr;
2292  ncTrial = m_numComponents;
2293  size_t numPreDeleted = m_numRxnTot - m_numRxnRdc;
2294  if (numPreDeleted != (m_numSpeciesTot - m_numSpeciesRdc)) {
2295  throw CanteraError("VCS_SOLVE::vcs_basopt", "we shouldn't be here");
2296  }
2297  m_numRxnTot = m_numSpeciesTot - ncTrial;
2298  m_numRxnRdc = m_numRxnTot - numPreDeleted;
2299  m_numSpeciesRdc = m_numSpeciesTot - numPreDeleted;
2300  for (size_t i = 0; i < m_numSpeciesTot; ++i) {
2301  m_indexRxnToSpecies[i] = ncTrial + i;
2302  }
2303  if (m_debug_print_lvl >= 2) {
2304  plogf(" --- Total number of components found = %3d (ne = %d)\n ",
2305  ncTrial, m_numElemConstraints);
2306  }
2307  goto L_END_LOOP;
2308  }
2309 
2310  // Assign a small negative number to the component that we have just
2311  // found, in order to take it out of further consideration.
2312  aw[k] = test;
2313 
2314  // CHECK LINEAR INDEPENDENCE WITH PREVIOUS SPECIES
2315  //
2316  // Modified Gram-Schmidt Method, p. 202 Dalquist
2317  // QR factorization of a matrix without row pivoting.
2318  size_t jl = jr;
2319  for (size_t j = 0; j < m_numElemConstraints; ++j) {
2320  sm[j + jr*m_numElemConstraints] = m_formulaMatrix(k,j);
2321  }
2322  if (jl > 0) {
2323  // Compute the coefficients of JA column of the the upper
2324  // triangular R matrix, SS(J) = R_J_JR this is slightly
2325  // different than Dalquist) R_JA_JA = 1
2326  for (size_t j = 0; j < jl; ++j) {
2327  ss[j] = 0.0;
2328  for (size_t i = 0; i < m_numElemConstraints; ++i) {
2329  ss[j] += sm[i + jr*m_numElemConstraints] * sm[i + j*m_numElemConstraints];
2330  }
2331  ss[j] /= sa[j];
2332  }
2333  // Now make the new column, (*,JR), orthogonal to the previous
2334  // columns
2335  for (size_t j = 0; j < jl; ++j) {
2336  for (size_t i = 0; i < m_numElemConstraints; ++i) {
2337  sm[i + jr*m_numElemConstraints] -= ss[j] * sm[i + j*m_numElemConstraints];
2338  }
2339  }
2340  }
2341 
2342  // Find the new length of the new column in Q. It will be used in
2343  // the denominator in future row calcs.
2344  sa[jr] = 0.0;
2345  for (size_t ml = 0; ml < m_numElemConstraints; ++ml) {
2346  sa[jr] += pow(sm[ml + jr*m_numElemConstraints], 2);
2347  }
2348 
2349  // IF NORM OF NEW ROW .LT. 1E-3 REJECT
2350  if (sa[jr] > 1.0e-6) {
2351  break;
2352  }
2353  }
2354 
2355  // REARRANGE THE DATA
2356  if (jr != k) {
2357  if (m_debug_print_lvl >= 2) {
2358  plogf(" --- %-12.12s", m_speciesName[k]);
2359  if (m_speciesUnknownType[k] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
2360  plogf("(Volts = %9.2g)", m_molNumSpecies_old[k]);
2361  } else {
2362  plogf("(%9.2g)", m_molNumSpecies_old[k]);
2363  }
2364  plogf(" replaces %-12.12s", m_speciesName[jr]);
2365  if (m_speciesUnknownType[jr] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
2366  plogf("(Volts = %9.2g)", m_molNumSpecies_old[jr]);
2367  } else {
2368  plogf("(%9.2g)", m_molNumSpecies_old[jr]);
2369  }
2370  plogf(" as component %3d\n", jr);
2371  }
2372  vcs_switch_pos(false, jr, k);
2373  std::swap(aw[jr], aw[k]);
2374  } else if (m_debug_print_lvl >= 2) {
2375  plogf(" --- %-12.12s", m_speciesName[k]);
2376  if (m_speciesUnknownType[k] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
2377  plogf("(Volts = %9.2g) remains ", m_molNumSpecies_old[k]);
2378  } else {
2379  plogf("(%9.2g) remains ", m_molNumSpecies_old[k]);
2380  }
2381  plogf(" as component %3d\n", jr);
2382  }
2383 // entry point from up above
2384 L_END_LOOP:
2385  ;
2386  // If we haven't found enough components, go back and find some more.
2387  jr++;
2388  }
2389 
2390  if (doJustComponents) {
2391  goto L_CLEANUP;
2392  }
2393 
2394  // EVALUATE THE STOICHIOMETRY
2395  //
2396  // Formulate the matrix problem for the stoichiometric
2397  // coefficients. CX + B = 0
2398  //
2399  // C will be an nc x nc matrix made up of the formula vectors for the
2400  // components. n RHS's will be solved for. Thus, B is an nc x n matrix.
2401  //
2402  // BIG PROBLEM 1/21/99:
2403  //
2404  // This algorithm makes the assumption that the first nc rows of the formula
2405  // matrix aren't rank deficient. However, this might not be the case. For
2406  // example, assume that the first element in m_formulaMatrix[] is argon.
2407  // Assume that no species in the matrix problem actually includes argon.
2408  // Then, the first row in sm[], below will be identically zero. bleh.
2409  //
2410  // What needs to be done is to perform a rearrangement of the ELEMENTS ->
2411  // i.e. rearrange, m_formulaMatrix, sp, and m_elemAbundancesGoal, such that
2412  // the first nc elements form in combination with the nc components create
2413  // an invertible sm[]. not a small project, but very doable.
2414  //
2415  // An alternative would be to turn the matrix problem below into an ne x nc
2416  // problem, and do QR elimination instead of Gauss-Jordan elimination. Note
2417  // the rearrangement of elements need only be done once in the problem. It's
2418  // actually very similar to the top of this program with ne being the
2419  // species and nc being the elements!!
2420  C.resize(ncTrial, ncTrial);
2421  for (size_t j = 0; j < ncTrial; ++j) {
2422  for (size_t i = 0; i < ncTrial; ++i) {
2423  C(i, j) = m_formulaMatrix(j,i);
2424  }
2425  }
2426  for (size_t i = 0; i < m_numRxnTot; ++i) {
2427  k = m_indexRxnToSpecies[i];
2428  for (size_t j = 0; j < ncTrial; ++j) {
2429  m_stoichCoeffRxnMatrix(j,i) = - m_formulaMatrix(k,j);
2430  }
2431  }
2432  // Solve the linear system to calculate the reaction matrix,
2433  // m_stoichCoeffRxnMatrix.
2434  solve(C, m_stoichCoeffRxnMatrix.ptrColumn(0), m_numRxnTot, m_numElemConstraints);
2435 
2436  // NOW, if we have interfacial voltage unknowns, what we did was just wrong
2437  // -> hopefully it didn't blow up. Redo the problem. Search for inactive E
2438  juse = npos;
2439  jlose = npos;
2440  for (size_t j = 0; j < m_numElemConstraints; j++) {
2441  if (!m_elementActive[j] && !strcmp(m_elementName[j].c_str(), "E")) {
2442  juse = j;
2443  }
2444  }
2445  for (size_t j = 0; j < m_numElemConstraints; j++) {
2446  if (m_elementActive[j] && !strncmp((m_elementName[j]).c_str(), "cn_", 3)) {
2447  jlose = j;
2448  }
2449  }
2450  for (k = 0; k < m_numSpeciesTot; k++) {
2451  if (m_speciesUnknownType[k] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
2452  for (size_t j = 0; j < ncTrial; ++j) {
2453  for (size_t i = 0; i < ncTrial; ++i) {
2454  if (i == jlose) {
2455  C(i, j) = m_formulaMatrix(j,juse);
2456  } else {
2457  C(i, j) = m_formulaMatrix(j,i);
2458  }
2459  }
2460  }
2461  for (size_t i = 0; i < m_numRxnTot; ++i) {
2462  k = m_indexRxnToSpecies[i];
2463  for (size_t j = 0; j < ncTrial; ++j) {
2464  if (j == jlose) {
2465  aw[j] = - m_formulaMatrix(k,juse);
2466  } else {
2467  aw[j] = - m_formulaMatrix(k,j);
2468  }
2469  }
2470  }
2471 
2472  solve(C, aw, 1, m_numElemConstraints);
2473  size_t i = k - ncTrial;
2474  for (size_t j = 0; j < ncTrial; j++) {
2475  m_stoichCoeffRxnMatrix(j,i) = aw[j];
2476  }
2477  }
2478  }
2479 
2480  // Calculate the szTmp array for each formation reaction
2481  for (size_t i = 0; i < m_numRxnTot; i++) {
2482  double szTmp = 0.0;
2483  for (size_t j = 0; j < ncTrial; j++) {
2484  szTmp += fabs(m_stoichCoeffRxnMatrix(j,i));
2485  }
2486  m_scSize[i] = szTmp;
2487  }
2488 
2489  if (m_debug_print_lvl >= 2) {
2490  plogf(" --- Components:");
2491  for (size_t j = 0; j < ncTrial; j++) {
2492  plogf(" %3d", j);
2493  }
2494  plogf("\n --- Components Moles:");
2495  for (size_t j = 0; j < ncTrial; j++) {
2496  if (m_speciesUnknownType[j] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
2497  plogf(" % -10.3E", 0.0);
2498  } else {
2499  plogf(" % -10.3E", m_molNumSpecies_old[j]);
2500  }
2501  }
2502  plogf("\n --- NonComponent| Moles |");
2503  for (size_t j = 0; j < ncTrial; j++) {
2504  plogf(" %10.10s", m_speciesName[j]);
2505  }
2506  plogf("\n");
2507  for (size_t i = 0; i < m_numRxnTot; i++) {
2508  plogf(" --- %3d ", m_indexRxnToSpecies[i]);
2509  plogf("%-10.10s", m_speciesName[m_indexRxnToSpecies[i]]);
2510  if (m_speciesUnknownType[m_indexRxnToSpecies[i]] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
2511  plogf("|% -10.3E|", 0.0);
2512  } else {
2513  plogf("|% -10.3E|", m_molNumSpecies_old[m_indexRxnToSpecies[i]]);
2514  }
2515  for (size_t j = 0; j < ncTrial; j++) {
2516  plogf(" %+7.3f", m_stoichCoeffRxnMatrix(j,i));
2517  }
2518  plogf("\n");
2519  }
2520 
2521  // Manual check on the satisfaction of the reaction matrix's ability to
2522  // conserve elements
2523  double sumMax = -1.0;
2524  size_t iMax = npos;
2525  size_t jMax = npos;
2526  for (size_t i = 0; i < m_numRxnTot; ++i) {
2527  k = m_indexRxnToSpecies[i];
2528  double sum;
2529  for (size_t j = 0; j < ncTrial; ++j) {
2530  if (j == jlose) {
2531  sum = m_formulaMatrix(k,juse);
2532  for (size_t n = 0; n < ncTrial; n++) {
2533  double numElements = m_formulaMatrix(n,juse);
2534  double coeff = m_stoichCoeffRxnMatrix(n,i);
2535  sum += coeff * numElements;
2536  }
2537  } else {
2538  sum = m_formulaMatrix(k,j);
2539  for (size_t n = 0; n < ncTrial; n++) {
2540  double numElements = m_formulaMatrix(n,j);
2541  double coeff = m_stoichCoeffRxnMatrix(n,i);
2542  sum += coeff * numElements;
2543  }
2544  }
2545  if (fabs(sum) > sumMax) {
2546  sumMax = fabs(sum);
2547  iMax = i;
2548  jMax = j;
2549  if (j == jlose) {
2550  jMax = juse;
2551  }
2552  }
2553  if (fabs(sum) > 1.0E-6) {
2554  throw CanteraError("VCS_SOLVE::vcs_basopt", "we have a prob");
2555  }
2556  }
2557  }
2558  plogf(" --- largest error in Stoich coeff = %g at rxn = %d ", sumMax, iMax);
2559  plogf("%-10.10s", m_speciesName[m_indexRxnToSpecies[iMax]]);
2560  plogf(" element = %d ", jMax);
2561  plogf("%-5.5s", m_elementName[jMax]);
2562  plogf("\n");
2563  plogf(" ");
2564  for (size_t i=0; i<77; i++) {
2565  plogf("-");
2566  }
2567  plogf("\n");
2568  }
2569 
2570  // EVALUATE DELTA N VALUES
2571  //
2572  // Evaluate the change in gas and liquid total moles due to reaction
2573  // vectors, DNG and DNL.
2574 
2575  // Zero out the change of Phase Moles array
2576  m_deltaMolNumPhase.zero();
2577  m_phaseParticipation.zero();
2578 
2579  // Loop over each reaction, creating the change in Phase Moles array,
2580  // m_deltaMolNumPhase(iphase,irxn), and the phase participation array,
2581  // PhaseParticipation[irxn][iphase]
2582  for (size_t irxn = 0; irxn < m_numRxnTot; ++irxn) {
2583  double* scrxn_ptr = m_stoichCoeffRxnMatrix.ptrColumn(irxn);
2584  size_t kspec = m_indexRxnToSpecies[irxn];
2585  size_t iph = m_phaseID[kspec];
2586  m_deltaMolNumPhase(iph,irxn) = 1.0;
2587  m_phaseParticipation(iph,irxn)++;
2588  for (size_t j = 0; j < ncTrial; ++j) {
2589  iph = m_phaseID[j];
2590  if (fabs(scrxn_ptr[j]) <= 1.0e-6) {
2591  scrxn_ptr[j] = 0.0;
2592  } else {
2593  m_deltaMolNumPhase(iph,irxn) += scrxn_ptr[j];
2594  m_phaseParticipation(iph,irxn)++;
2595  }
2596  }
2597  }
2598 
2599 L_CLEANUP:
2600  ;
2601  double tsecond = tickTock.secondsWC();
2602  m_VCount->Time_basopt += tsecond;
2603  m_VCount->Basis_Opts++;
2604  return VCS_SUCCESS;
2605 }
2606 
2607 size_t VCS_SOLVE::vcs_basisOptMax(const double* const molNum, const size_t j,
2608  const size_t n)
2609 {
2610  // The factors of 1.01 and 1.001 are placed in this routine for a purpose.
2611  // The purpose is to ensure that roundoff errors don't influence major
2612  // decisions. This means that the optimized and non-optimized versions of
2613  // the code remain close to each other.
2614  //
2615  // (we try to avoid the logic: a = b
2616  // if (a > b) { do this }
2617  // else { do something else }
2618  // because roundoff error makes a difference in the inequality evaluation)
2619  //
2620  // Mole numbers are frequently equal to each other in equilibrium problems
2621  // due to constraints. Swaps are only done if there are a 1% difference in
2622  // the mole numbers. Of course this logic isn't foolproof.
2623  size_t largest = j;
2624  double big = molNum[j] * m_spSize[j] * 1.01;
2625  if (m_spSize[j] <= 0.0) {
2626  throw CanteraError("VCS_SOLVE::vcs_basisOptMax",
2627  "spSize is nonpositive");
2628  }
2629  for (size_t i = j + 1; i < n; ++i) {
2630  if (m_spSize[i] <= 0.0) {
2631  throw CanteraError("VCS_SOLVE::vcs_basisOptMax",
2632  "spSize is nonpositive");
2633  }
2634  bool doSwap = false;
2635  if (m_SSPhase[j]) {
2636  doSwap = (molNum[i] * m_spSize[i]) > big;
2637  if (!m_SSPhase[i] && doSwap) {
2638  doSwap = molNum[i] > (molNum[largest] * 1.001);
2639  }
2640  } else {
2641  if (m_SSPhase[i]) {
2642  doSwap = (molNum[i] * m_spSize[i]) > big;
2643  if (!doSwap) {
2644  doSwap = molNum[i] > (molNum[largest] * 1.001);
2645  }
2646  } else {
2647  doSwap = (molNum[i] * m_spSize[i]) > big;
2648  }
2649  }
2650  if (doSwap) {
2651  largest = i;
2652  big = molNum[i] * m_spSize[i] * 1.01;
2653  }
2654  }
2655  return largest;
2656 }
2657 
2658 int VCS_SOLVE::vcs_species_type(const size_t kspec) const
2659 {
2660  // Treat special cases first
2661  if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
2663  }
2664 
2665  size_t iph = m_phaseID[kspec];
2666  int irxn = int(kspec) - int(m_numComponents);
2667  int phaseExist = m_VolPhaseList[iph]->exists();
2668 
2669  // Treat zeroed out species first
2670  if (m_molNumSpecies_old[kspec] <= 0.0) {
2671  if (m_tPhaseMoles_old[iph] <= 0.0 && !m_SSPhase[kspec]) {
2672  return VCS_SPECIES_ZEROEDMS;
2673  }
2674 
2675  // see if the species has an element which is so low that species will
2676  // always be zero
2677  for (size_t j = 0; j < m_numElemConstraints; ++j) {
2678  if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
2679  double atomComp = m_formulaMatrix(kspec,j);
2680  if (atomComp > 0.0) {
2681  double maxPermissible = m_elemAbundancesGoal[j] / atomComp;
2682  if (maxPermissible < VCS_DELETE_MINORSPECIES_CUTOFF) {
2683  if (m_debug_print_lvl >= 2) {
2684  plogf(" --- %s can not be nonzero because"
2685  " needed element %s is zero\n",
2686  m_speciesName[kspec], m_elementName[j]);
2687  }
2688  if (m_SSPhase[kspec]) {
2689  return VCS_SPECIES_ZEROEDSS;
2690  } else {
2691  return VCS_SPECIES_STOICHZERO;
2692  }
2693  }
2694  }
2695  }
2696  }
2697 
2698  // The Gibbs free energy for this species is such that it will pop back
2699  // into existence. An exception to this is if a needed regular element
2700  // is also zeroed out. Then, don't pop the phase or the species back
2701  // into existence.
2702  if (irxn >= 0) {
2703  for (size_t j = 0; j < m_numComponents; ++j) {
2704  double stoicC = m_stoichCoeffRxnMatrix(j,irxn);
2705  if (stoicC != 0.0) {
2706  double negChangeComp = - stoicC;
2707  if (negChangeComp > 0.0) {
2708  if (m_molNumSpecies_old[j] < 1.0E-60) {
2709  if (m_debug_print_lvl >= 2) {
2710  plogf(" --- %s is prevented from popping into existence because"
2711  " a needed component to be consumed, %s, has a zero mole number\n",
2712  m_speciesName[kspec], m_speciesName[j]);
2713  }
2714  if (m_SSPhase[kspec]) {
2715  return VCS_SPECIES_ZEROEDSS;
2716  } else {
2717  return VCS_SPECIES_STOICHZERO;
2718  }
2719  }
2720  } else if (negChangeComp < 0.0) {
2721  if (m_VolPhaseList[m_phaseID[j]]->exists() <= 0) {
2722  if (m_debug_print_lvl >= 2) {
2723  plogf(" --- %s is prevented from popping into existence because"
2724  " a needed component %s is in a zeroed-phase that would be "
2725  "popped into existence at the same time\n",
2726  m_speciesName[kspec], m_speciesName[j]);
2727  }
2728  if (m_SSPhase[kspec]) {
2729  return VCS_SPECIES_ZEROEDSS;
2730  } else {
2731  return VCS_SPECIES_STOICHZERO;
2732  }
2733  }
2734  }
2735  }
2736  }
2737  }
2738 
2739  if (irxn >= 0 && m_deltaGRxn_old[irxn] >= 0.0) {
2740  // We are here when the species is or should remain zeroed out
2741  if (m_SSPhase[kspec]) {
2742  return VCS_SPECIES_ZEROEDSS;
2743  } else {
2744  if (phaseExist >= VCS_PHASE_EXIST_YES) {
2746  } else if (phaseExist == VCS_PHASE_EXIST_ZEROEDPHASE) {
2747  return VCS_SPECIES_ZEROEDPHASE;
2748  } else {
2749  return VCS_SPECIES_ZEROEDMS;
2750  }
2751  }
2752  }
2753 
2754  // If the current phase already exists,
2755  if (m_tPhaseMoles_old[iph] > 0.0) {
2756  if (m_SSPhase[kspec]) {
2757  return VCS_SPECIES_MAJOR;
2758  } else {
2760  }
2761  }
2762 
2763  // The Gibbs free energy for this species is such that it will pop back
2764  // into existence. Set it to a major species in anticipation. Note, if
2765  // we had an estimate for the emerging mole fraction of the species in
2766  // the phase, we could do better here.
2767  if (m_tPhaseMoles_old[iph] <= 0.0) {
2768  if (m_SSPhase[kspec]) {
2769  return VCS_SPECIES_MAJOR;
2770  } else {
2771  return VCS_SPECIES_ZEROEDMS;
2772  }
2773  }
2774  }
2775 
2776  // Treat species with non-zero mole numbers next
2777 
2778  // Always treat species in single species phases as majors if the phase
2779  // exists.
2780  if (m_SSPhase[kspec]) {
2781  return VCS_SPECIES_MAJOR;
2782  }
2783 
2784  // Check to see whether the current species is a major component of its
2785  // phase. If it is, it is a major component. This is consistent with the
2786  // above rule about single species phases. A major component i.e., a species
2787  // with a high mole fraction) in any phase is always treated as a major
2788  // species
2789  if (m_molNumSpecies_old[kspec] > (m_tPhaseMoles_old[iph] * 0.001)) {
2790  return VCS_SPECIES_MAJOR;
2791  }
2792 
2793  // Main check in the loop: Check to see if there is a component with a mole
2794  // number that is within a factor of 100 of the current species. If there is
2795  // and that component is not part of a single species phase and shares a
2796  // non-zero stoichiometric coefficient, then the current species is a major
2797  // species.
2798  if (irxn < 0) {
2799  return VCS_SPECIES_MAJOR;
2800  } else {
2801  double szAdj = m_scSize[irxn] * std::sqrt((double)m_numRxnTot);
2802  for (size_t k = 0; k < m_numComponents; ++k) {
2803  if (!m_SSPhase[k] && m_stoichCoeffRxnMatrix(k,irxn) != 0.0 && m_molNumSpecies_old[kspec] * szAdj >= m_molNumSpecies_old[k] * 0.01) {
2804  return VCS_SPECIES_MAJOR;
2805  }
2806  }
2807  }
2808  return VCS_SPECIES_MINOR;
2809 }
2810 
2811 void VCS_SOLVE::vcs_chemPotPhase(const int stateCalc,
2812  const size_t iph, const double* const molNum,
2813  double* const ac, double* const mu_i,
2814  const bool do_deleted)
2815 {
2816  warn_deprecated("VCS_SOLVE::vcs_chemPotPhase",
2817  "Unused. To be removed after Cantera 2.3.");
2818  vcs_VolPhase* Vphase = m_VolPhaseList[iph];
2819  size_t nkk = Vphase->nSpecies();
2820  double tMoles = TPhInertMoles[iph];
2821  for (size_t k = 0; k < nkk; k++) {
2822  size_t kspec = Vphase->spGlobalIndexVCS(k);
2823  tMoles += molNum[kspec];
2824  }
2825  double tlogMoles = 0.0;
2826  if (tMoles > 0.0) {
2827  tlogMoles = log(tMoles);
2828  }
2829 
2830  Vphase->setMolesFromVCS(stateCalc, molNum);
2831  Vphase->sendToVCS_ActCoeff(stateCalc, ac);
2832 
2833  double phi = Vphase->electricPotential();
2834  double Faraday_phi = m_Faraday_dim * phi;
2835 
2836  for (size_t k = 0; k < nkk; k++) {
2837  size_t kspec = Vphase->spGlobalIndexVCS(k);
2838  if (kspec >= m_numComponents) {
2839  if (!do_deleted &&
2840  (m_speciesStatus[kspec] == VCS_SPECIES_DELETED)) {
2841  continue;
2842  }
2843  }
2844  if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
2845  AssertThrowMsg(molNum[kspec] == phi, "VCS_SOLVE::vcs_chemPotPhase",
2846  "We have an inconsistency!");
2847  AssertThrowMsg(m_chargeSpecies[kspec] == -1.0, "VCS_SOLVE::vcs_chemPotPhase",
2848  "We have an unexpected situation!");
2849  mu_i[kspec] = m_SSfeSpecies[kspec] + m_chargeSpecies[kspec] * Faraday_phi;
2850  } else {
2851  if (m_SSPhase[kspec]) {
2852  mu_i[kspec] = m_SSfeSpecies[kspec] + m_chargeSpecies[kspec] * Faraday_phi;
2853  } else if (molNum[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) {
2854  mu_i[kspec] = m_SSfeSpecies[kspec] + log(ac[kspec] * VCS_DELETE_MINORSPECIES_CUTOFF)
2855  - tlogMoles - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * Faraday_phi;
2856  } else {
2857  mu_i[kspec] = m_SSfeSpecies[kspec] + log(ac[kspec] * molNum[kspec])
2858  - tlogMoles - m_lnMnaughtSpecies[kspec] + m_chargeSpecies[kspec] * Faraday_phi;
2859  }
2860  }
2861  }
2862 }
2863 
2864 void VCS_SOLVE::vcs_dfe(const int stateCalc,
2865  const int ll, const size_t lbot, const size_t ltop)
2866 {
2867  double* tPhMoles_ptr=0;
2868  double* actCoeff_ptr=0;
2869  double* feSpecies=0;
2870  double* molNum=0;
2871  if (stateCalc == VCS_STATECALC_OLD) {
2872  feSpecies = &m_feSpecies_old[0];
2873  tPhMoles_ptr = &m_tPhaseMoles_old[0];
2874  actCoeff_ptr = &m_actCoeffSpecies_old[0];
2875  molNum = &m_molNumSpecies_old[0];
2876  } else if (stateCalc == VCS_STATECALC_NEW) {
2877  feSpecies = &m_feSpecies_new[0];
2878  tPhMoles_ptr = &m_tPhaseMoles_new[0];
2879  actCoeff_ptr = &m_actCoeffSpecies_new[0];
2880  molNum = &m_molNumSpecies_new[0];
2881  } else {
2882  throw CanteraError("VCS_SOLVE::vcs_dfe",
2883  "Subroutine vcs_dfe called with bad stateCalc value: {}", stateCalc);
2884  }
2885 
2886  AssertThrowMsg(m_unitsState != VCS_DIMENSIONAL_G, "VCS_SOLVE::vcs_dfe",
2887  "called with wrong units state");
2888 
2889  if (m_debug_print_lvl >= 2) {
2890  if (ll == 0) {
2891  if (lbot != 0) {
2892  plogf(" --- Subroutine vcs_dfe called for one species: ");
2893  plogf("%-12.12s", m_speciesName[lbot]);
2894  } else {
2895  plogf(" --- Subroutine vcs_dfe called for all species");
2896  }
2897  } else if (ll > 0) {
2898  plogf(" --- Subroutine vcs_dfe called for components and minors");
2899  } else {
2900  plogf(" --- Subroutine vcs_dfe called for components and majors");
2901  }
2902  if (stateCalc == VCS_STATECALC_NEW) {
2903  plogf(" using tentative solution");
2904  }
2905  plogendl();
2906  }
2907 
2908  double* tlogMoles = &m_TmpPhase[0];
2909 
2910  // Might as well recalculate the phase mole vector and compare to the stored
2911  // one. They should be correct.
2912  double* tPhInertMoles = &TPhInertMoles[0];
2913  for (size_t iph = 0; iph < m_numPhases; iph++) {
2914  tlogMoles[iph] = tPhInertMoles[iph];
2915 
2916  }
2917  for (size_t kspec = 0; kspec < m_numSpeciesTot; kspec++) {
2918  if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
2919  size_t iph = m_phaseID[kspec];
2920  tlogMoles[iph] += molNum[kspec];
2921  }
2922  }
2923  for (size_t iph = 0; iph < m_numPhases; iph++) {
2924  AssertThrowMsg(vcs_doubleEqual(tlogMoles[iph], tPhMoles_ptr[iph]),
2925  "VCS_SOLVE::vcs_dfe",
2926  "phase Moles may be off, iph = {}, {} {}",
2927  iph, tlogMoles[iph], tPhMoles_ptr[iph]);
2928  }
2929  m_TmpPhase.assign(m_TmpPhase.size(), 0.0);
2930  for (size_t iph = 0; iph < m_numPhases; iph++) {
2931  if (tPhMoles_ptr[iph] > 0.0) {
2932  tlogMoles[iph] = log(tPhMoles_ptr[iph]);
2933  }
2934  }
2935 
2936  size_t l1, l2;
2937  if (ll != 0) {
2938  l1 = lbot;
2939  l2 = m_numComponents;
2940  } else {
2941  l1 = lbot;
2942  l2 = ltop;
2943  }
2944 
2945  // Calculate activity coefficients for all phases that are not current. Here
2946  // we also trigger an update check for each VolPhase to see if its mole
2947  // numbers are current with vcs
2948  for (size_t iphase = 0; iphase < m_numPhases; iphase++) {
2949  vcs_VolPhase* Vphase = m_VolPhaseList[iphase];
2950  Vphase->updateFromVCS_MoleNumbers(stateCalc);
2951  if (!Vphase->m_singleSpecies) {
2952  Vphase->sendToVCS_ActCoeff(stateCalc, &actCoeff_ptr[0]);
2953  }
2954  m_phasePhi[iphase] = Vphase->electricPotential();
2955  }
2956 
2957  // ALL SPECIES, OR COMPONENTS
2958  //
2959  // Do all of the species when LL = 0. Then we are done for the routine When
2960  // LL ne 0., just do the initial components. We will then finish up below
2961  // with loops over either the major noncomponent species or the minor
2962  // noncomponent species.
2963  for (size_t kspec = l1; kspec < l2; ++kspec) {
2964  size_t iphase = m_phaseID[kspec];
2965  if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
2966  AssertThrowMsg(molNum[kspec] == m_phasePhi[iphase], "VCS_SOLVE::vcs_dfe",
2967  "We have an inconsistency!");
2968  AssertThrowMsg(m_chargeSpecies[kspec] == -1.0, "VCS_SOLVE::vcs_dfe",
2969  "We have an unexpected situation!");
2970  feSpecies[kspec] = m_SSfeSpecies[kspec]
2971  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
2972  } else {
2973  if (m_SSPhase[kspec]) {
2974  feSpecies[kspec] = m_SSfeSpecies[kspec]
2975  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
2976  } else if ((m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDMS) ||
2977  (m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDPHASE)) {
2978  feSpecies[kspec] = m_SSfeSpecies[kspec] - m_lnMnaughtSpecies[kspec]
2979  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
2980  } else {
2981  if (molNum[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) {
2982  size_t iph = m_phaseID[kspec];
2983  if (tPhMoles_ptr[iph] > 0.0) {
2984  feSpecies[kspec] = m_SSfeSpecies[kspec]
2985  + log(actCoeff_ptr[kspec] * VCS_DELETE_MINORSPECIES_CUTOFF)
2986  - tlogMoles[m_phaseID[kspec]] - m_lnMnaughtSpecies[kspec]
2987  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
2988  } else {
2989  feSpecies[kspec] = m_SSfeSpecies[kspec] - m_lnMnaughtSpecies[kspec]
2990  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
2991  }
2992  } else {
2993  feSpecies[kspec] = m_SSfeSpecies[kspec]
2994  + log(actCoeff_ptr[kspec] * molNum[kspec])
2995  - tlogMoles[m_phaseID[kspec]] - m_lnMnaughtSpecies[kspec]
2996  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
2997  }
2998  }
2999  }
3000  }
3001 
3002  if (ll < 0) {
3003  // MAJORS ONLY
3004  for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
3005  size_t kspec = m_indexRxnToSpecies[irxn];
3006  if (m_speciesStatus[kspec] != VCS_SPECIES_MINOR) {
3007  size_t iphase = m_phaseID[kspec];
3008  if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
3009  AssertThrowMsg(molNum[kspec] == m_phasePhi[iphase], "VCS_SOLVE::vcs_dfe",
3010  "We have an inconsistency!");
3011  AssertThrowMsg(m_chargeSpecies[kspec] == -1.0, "VCS_SOLVE::vcs_dfe",
3012  "We have an unexpected situation!");
3013  feSpecies[kspec] = m_SSfeSpecies[kspec]
3014  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
3015  } else {
3016  if (m_SSPhase[kspec]) {
3017  feSpecies[kspec] = m_SSfeSpecies[kspec]
3018  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
3019  } else if ((m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDMS) ||
3020  (m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDPHASE)) {
3021  feSpecies[kspec] = m_SSfeSpecies[kspec] - m_lnMnaughtSpecies[kspec]
3022  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
3023  } else {
3024  if (molNum[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) {
3025  size_t iph = m_phaseID[kspec];
3026  if (tPhMoles_ptr[iph] > 0.0) {
3027  feSpecies[kspec] = m_SSfeSpecies[kspec]
3028  + log(actCoeff_ptr[kspec] * VCS_DELETE_MINORSPECIES_CUTOFF)
3029  - tlogMoles[m_phaseID[kspec]] - m_lnMnaughtSpecies[kspec]
3030  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
3031  } else {
3032  feSpecies[kspec] = m_SSfeSpecies[kspec] - m_lnMnaughtSpecies[kspec]
3033  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
3034  }
3035  } else {
3036  feSpecies[kspec] = m_SSfeSpecies[kspec]
3037  + log(actCoeff_ptr[kspec] * molNum[kspec])
3038  - tlogMoles[m_phaseID[kspec]] - m_lnMnaughtSpecies[kspec]
3039  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
3040  }
3041  }
3042  }
3043  }
3044  }
3045  } else if (ll > 0) {
3046  // MINORS ONLY
3047  for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
3048  size_t kspec = m_indexRxnToSpecies[irxn];
3049  if (m_speciesStatus[kspec] == VCS_SPECIES_MINOR) {
3050  size_t iphase = m_phaseID[kspec];
3051  if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
3052  AssertThrowMsg(molNum[kspec] == m_phasePhi[iphase], "VCS_SOLVE::vcs_dfe",
3053  "We have an inconsistency!");
3054  AssertThrowMsg(m_chargeSpecies[kspec] == -1.0, "VCS_SOLVE::vcs_dfe",
3055  "We have an unexpected situation!");
3056  feSpecies[kspec] = m_SSfeSpecies[kspec]
3057  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
3058  } else {
3059  if (m_SSPhase[kspec]) {
3060  feSpecies[kspec] = m_SSfeSpecies[kspec]
3061  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
3062  } else if ((m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDMS) ||
3063  (m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDPHASE)) {
3064  feSpecies[kspec] = m_SSfeSpecies[kspec] - m_lnMnaughtSpecies[kspec]
3065  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
3066  } else {
3067  if (molNum[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) {
3068  size_t iph = m_phaseID[kspec];
3069  if (tPhMoles_ptr[iph] > 0.0) {
3070  feSpecies[kspec] = m_SSfeSpecies[kspec]
3071  + log(actCoeff_ptr[kspec] * VCS_DELETE_MINORSPECIES_CUTOFF)
3072  - tlogMoles[m_phaseID[kspec]] - m_lnMnaughtSpecies[kspec]
3073  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
3074  } else {
3075  feSpecies[kspec] = m_SSfeSpecies[kspec] - m_lnMnaughtSpecies[kspec]
3076  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
3077  }
3078  } else {
3079  feSpecies[kspec] = m_SSfeSpecies[kspec]
3080  + log(actCoeff_ptr[kspec] * molNum[kspec])
3081  - tlogMoles[m_phaseID[kspec]] - m_lnMnaughtSpecies[kspec]
3082  + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
3083  }
3084  }
3085  }
3086  }
3087  }
3088  }
3089 }
3090 
3091 void VCS_SOLVE::vcs_printSpeciesChemPot(const int stateCalc) const
3092 {
3093  warn_deprecated("VCS_SOLVE::vcs_printSpeciesChemPot",
3094  "Unused. To be removed after Cantera 2.3.");
3095  double mfValue = 1.0;
3096  bool zeroedPhase = false;
3097 
3098  const double* molNum = &m_molNumSpecies_old[0];
3099  const double* actCoeff_ptr = &m_actCoeffSpecies_old[0];
3100  if (stateCalc == VCS_STATECALC_NEW) {
3101  actCoeff_ptr = &m_actCoeffSpecies_new[0];
3102  molNum = &m_molNumSpecies_new[0];
3103  }
3104 
3105  double* tMoles = &m_TmpPhase[0];
3106  const double* tPhInertMoles = &TPhInertMoles[0];
3107  for (size_t iph = 0; iph < m_numPhases; iph++) {
3108  tMoles[iph] = tPhInertMoles[iph];
3109  }
3110  for (size_t kspec = 0; kspec < m_numSpeciesTot; kspec++) {
3111  if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
3112  size_t iph = m_phaseID[kspec];
3113  tMoles[iph] += molNum[kspec];
3114  }
3115  }
3116 
3117  double RT = m_temperature * GasConstant;
3118  writelog(" --- CHEMICAL POT TABLE (J/kmol) Name PhID MolFR ChemoSS "
3119  " logMF Gamma Elect extra ElectrChem\n");
3120  writelog(" ");
3121  writeline('-', 132);
3122 
3123  for (size_t kspec = 0; kspec < m_numSpeciesTot; ++kspec) {
3124  mfValue = 1.0;
3125  size_t iphase = m_phaseID[kspec];
3126  const vcs_VolPhase* Vphase = m_VolPhaseList[iphase];
3127  if ((m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDMS) ||
3128  (m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDPHASE) ||
3129  (m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDSS)) {
3130  zeroedPhase = true;
3131  } else {
3132  zeroedPhase = false;
3133  }
3134  if (tMoles[iphase] > 0.0) {
3135  if (molNum[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) {
3136  mfValue = VCS_DELETE_MINORSPECIES_CUTOFF / tMoles[iphase];
3137  } else {
3138  mfValue = molNum[kspec]/tMoles[iphase];
3139  }
3140  } else {
3141  size_t klocal = m_speciesLocalPhaseIndex[kspec];
3142  mfValue = Vphase->moleFraction(klocal);
3143  }
3144  double volts = Vphase->electricPotential();
3145  double elect = m_chargeSpecies[kspec] * m_Faraday_dim * volts;
3146  double comb = - m_lnMnaughtSpecies[kspec];
3147  double total = (m_SSfeSpecies[kspec] + log(mfValue) + elect + log(actCoeff_ptr[kspec]) + comb);
3148 
3149  if (zeroedPhase) {
3150  writelog(" --- ** zp *** ");
3151  } else {
3152  writelog(" --- ");
3153  }
3154  writelogf("%-24.24s", m_speciesName[kspec]);
3155  writelogf(" %3d", iphase);
3156  writelogf(" % -12.4e", mfValue);
3157  writelogf(" % -12.4e", m_SSfeSpecies[kspec] * RT);
3158  writelogf(" % -12.4e", log(mfValue) * RT);
3159  writelogf(" % -12.4e", log(actCoeff_ptr[kspec]) * RT);
3160  writelogf(" % -12.4e", elect * RT);
3161  writelogf(" % -12.4e", comb * RT);
3162  writelogf(" % -12.4e\n", total *RT);
3163  }
3164  writelog(" ");
3165  writeline('-', 132);
3166 }
3167 
3168 void VCS_SOLVE::prneav() const
3169 {
3170  warn_deprecated("VCS_SOLVE::prneav",
3171  "Unused. To be removed after Cantera 2.3.");
3172  vector_fp eav(m_numElemConstraints, 0.0);
3173 
3174  for (size_t j = 0; j < m_numElemConstraints; ++j) {
3175  for (size_t i = 0; i < m_numSpeciesTot; ++i) {
3176  if (m_speciesUnknownType[i] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
3177  eav[j] += m_formulaMatrix(i,j) * m_molNumSpecies_old[i];
3178  }
3179  }
3180  }
3181  bool kerr = false;
3182  plogf("--------------------------------------------------");
3183  plogf("ELEMENT ABUNDANCE VECTOR:\n");
3184  plogf(" Element Now Orignal Deviation Type\n");
3185  for (size_t j = 0; j < m_numElemConstraints; ++j) {
3186  plogf(" ");
3187  plogf("%-2.2s", m_elementName[j]);
3188  plogf(" = %15.6E %15.6E %15.6E %3d\n",
3189  eav[j], m_elemAbundancesGoal[j], eav[j] - m_elemAbundancesGoal[j], m_elType[j]);
3190  if (m_elemAbundancesGoal[j] != 0.) {
3191  if (fabs(eav[j] - m_elemAbundancesGoal[j]) > m_elemAbundancesGoal[j] * 5.0e-9) {
3192  kerr = true;
3193  }
3194  } else {
3195  if (fabs(eav[j]) > 1.0e-10) {
3196  kerr = true;
3197  }
3198  }
3199  }
3200  if (kerr) {
3201  plogf("Element abundance check failure\n");
3202  }
3203  plogf("--------------------------------------------------");
3204  plogendl();
3205 }
3206 
3207 double VCS_SOLVE::l2normdg(double dgLocal[]) const
3208 {
3209  if (m_numRxnRdc <= 0) {
3210  return 0.0;
3211  }
3212  double tmp = 0;
3213  for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
3214  size_t kspec = irxn + m_numComponents;
3215  if (m_speciesStatus[kspec] == VCS_SPECIES_MAJOR || m_speciesStatus[kspec] == VCS_SPECIES_MINOR ||
3216  dgLocal[irxn] < 0.0) {
3217  if (m_speciesStatus[kspec] != VCS_SPECIES_ZEROEDMS) {
3218  tmp += dgLocal[irxn] * dgLocal[irxn];
3219  }
3220  }
3221  }
3222  return std::sqrt(tmp / m_numRxnRdc);
3223 }
3224 
3225 double VCS_SOLVE::vcs_tmoles()
3226 {
3227  for (size_t i = 0; i < m_numPhases; i++) {
3228  m_tPhaseMoles_old[i] = TPhInertMoles[i];
3229  }
3230  for (size_t i = 0; i < m_numSpeciesTot; i++) {
3231  if (m_speciesUnknownType[i] == VCS_SPECIES_TYPE_MOLNUM) {
3232  m_tPhaseMoles_old[m_phaseID[i]] += m_molNumSpecies_old[i];
3233  }
3234  }
3235  double sum = 0.0;
3236  for (size_t i = 0; i < m_numPhases; i++) {
3237  sum += m_tPhaseMoles_old[i];
3238  vcs_VolPhase* Vphase = m_VolPhaseList[i];
3239  if (m_tPhaseMoles_old[i] == 0.0) {
3240  Vphase->setTotalMoles(0.0);
3241  } else {
3242  Vphase->setTotalMoles(m_tPhaseMoles_old[i]);
3243  }
3244  }
3245  m_totalMolNum = sum;
3246  return m_totalMolNum;
3247 }
3248 
3249 void VCS_SOLVE::check_tmoles() const
3250 {
3251  double sum = 0.0;
3252  for (size_t i = 0; i < m_numPhases; i++) {
3253  double m_tPhaseMoles_old_a = TPhInertMoles[i];
3254 
3255  for (size_t k = 0; k < m_numSpeciesTot; k++) {
3256  if (m_speciesUnknownType[k] == VCS_SPECIES_TYPE_MOLNUM && m_phaseID[k] == i) {
3257  m_tPhaseMoles_old_a += m_molNumSpecies_old[k];
3258  }
3259  }
3260  sum += m_tPhaseMoles_old_a;
3261 
3262  double denom = m_tPhaseMoles_old[i]+ m_tPhaseMoles_old_a + 1.0E-19;
3263  if (!vcs_doubleEqual(m_tPhaseMoles_old[i]/denom, m_tPhaseMoles_old_a/denom)) {
3264  plogf("check_tmoles: we have found a problem with phase %d: %20.15g, %20.15g\n",
3265  i, m_tPhaseMoles_old[i], m_tPhaseMoles_old_a);
3266  }
3267  }
3268 }
3269 
3270 void VCS_SOLVE::vcs_updateVP(const int vcsState)
3271 {
3272  for (size_t i = 0; i < m_numPhases; i++) {
3273  vcs_VolPhase* Vphase = m_VolPhaseList[i];
3274  if (vcsState == VCS_STATECALC_OLD) {
3276  &m_molNumSpecies_old[0],
3277  &m_tPhaseMoles_old[0]);
3278  } else if (vcsState == VCS_STATECALC_NEW) {
3280  &m_molNumSpecies_new[0],
3281  &m_tPhaseMoles_new[0]);
3282  } else {
3283  throw CanteraError("VCS_SOLVE::vcs_updateVP",
3284  "wrong stateCalc value: {}", vcsState);
3285  }
3286  }
3287 }
3288 
3289 bool VCS_SOLVE::vcs_evaluate_speciesType()
3290 {
3291  m_numRxnMinorZeroed = 0;
3292  if (m_debug_print_lvl >= 2) {
3293  plogf(" --- Species Status decision is reevaluated: All species are minor except for:\n");
3294  } else if (m_debug_print_lvl >= 5) {
3295  plogf(" --- Species Status decision is reevaluated");
3296  plogendl();
3297  }
3298  for (size_t kspec = 0; kspec < m_numSpeciesTot; ++kspec) {
3299  m_speciesStatus[kspec] = vcs_species_type(kspec);
3300  if (m_debug_print_lvl >= 5) {
3301  plogf(" --- %-16s: ", m_speciesName[kspec]);
3302  if (kspec < m_numComponents) {
3303  plogf("(COMP) ");
3304  } else {
3305  plogf(" ");
3306  }
3307  plogf(" %10.3g ", m_molNumSpecies_old[kspec]);
3308  const char* sString = vcs_speciesType_string(m_speciesStatus[kspec], 100);
3309  plogf("%s\n", sString);
3310  } else if (m_debug_print_lvl >= 2) {
3311  if (m_speciesStatus[kspec] != VCS_SPECIES_MINOR) {
3312  switch (m_speciesStatus[kspec]) {
3313  case VCS_SPECIES_COMPONENT:
3314  break;
3315  case VCS_SPECIES_MAJOR:
3316  plogf(" --- Major Species : %-s\n", m_speciesName[kspec]);
3317  break;
3319  plogf(" --- Purposely Zeroed-Phase Species (not in problem): %-s\n",
3320  m_speciesName[kspec]);
3321  break;
3322  case VCS_SPECIES_ZEROEDMS:
3323  plogf(" --- Zeroed-MS Phase Species: %-s\n", m_speciesName[kspec]);
3324  break;
3325  case VCS_SPECIES_ZEROEDSS:
3326  plogf(" --- Zeroed-SS Phase Species: %-s\n", m_speciesName[kspec]);
3327  break;
3328  case VCS_SPECIES_DELETED:
3329  plogf(" --- Deleted-Small Species : %-s\n", m_speciesName[kspec]);
3330  break;
3332  plogf(" --- Zeroed Species in an active MS phase (tmp): %-s\n",
3333  m_speciesName[kspec]);
3334  break;
3336  plogf(" --- Zeroed Species in an active MS phase (Stoich Constraint): %-s\n",
3337  m_speciesName[kspec]);
3338  break;
3340  plogf(" --- InterfaceVoltage Species: %-s\n", m_speciesName[kspec]);
3341  break;
3342  default:
3343  throw CanteraError("VCS_SOLVE::vcs_evaluate_speciesType",
3344  "Unknown type: {}", m_speciesStatus[kspec]);
3345  }
3346  }
3347  }
3348  if (kspec >= m_numComponents && m_speciesStatus[kspec] != VCS_SPECIES_MAJOR) {
3349  ++m_numRxnMinorZeroed;
3350  }
3351  }
3352  if (m_debug_print_lvl >= 2) {
3353  plogf(" ---");
3354  plogendl();
3355  }
3356  return (m_numRxnMinorZeroed >= m_numRxnRdc);
3357 }
3358 
3359 void VCS_SOLVE::vcs_deltag(const int L, const bool doDeleted,
3360  const int vcsState, const bool alterZeroedPhases)
3361 {
3362  size_t irxnl = m_numRxnRdc;
3363  if (doDeleted) {
3364  irxnl = m_numRxnTot;
3365  }
3366 
3367  double* deltaGRxn;
3368  double* feSpecies;
3369  double* molNumSpecies;
3370  double* actCoeffSpecies;
3371  if (vcsState == VCS_STATECALC_NEW) {
3372  deltaGRxn = &m_deltaGRxn_new[0];
3373  feSpecies = &m_feSpecies_new[0];
3374  molNumSpecies = &m_molNumSpecies_new[0];
3375  actCoeffSpecies = &m_actCoeffSpecies_new[0];
3376  } else if (vcsState == VCS_STATECALC_OLD) {
3377  deltaGRxn = &m_deltaGRxn_old[0];
3378  feSpecies = &m_feSpecies_old[0];
3379  molNumSpecies = &m_molNumSpecies_old[0];
3380  actCoeffSpecies = &m_actCoeffSpecies_old[0];
3381  } else {
3382  throw CanteraError("VCS_SOLVE::vcs_deltag", "bad vcsState");
3383  }
3384 
3385  if (m_debug_print_lvl >= 2) {
3386  plogf(" --- Subroutine vcs_deltag called for ");
3387  if (L < 0) {
3388  plogf("major noncomponents\n");
3389  } else if (L == 0) {
3390  plogf("all noncomponents\n");
3391  } else {
3392  plogf("minor noncomponents\n");
3393  }
3394  }
3395 
3396  if (L < 0) {
3397  // MAJORS and ZEROED SPECIES ONLY
3398  for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
3399  size_t kspec = irxn + m_numComponents;
3400  if (m_speciesStatus[kspec] != VCS_SPECIES_MINOR) {
3401  int icase = 0;
3402  deltaGRxn[irxn] = feSpecies[m_indexRxnToSpecies[irxn]];
3403  double* dtmp_ptr = m_stoichCoeffRxnMatrix.ptrColumn(irxn);
3404  for (kspec = 0; kspec < m_numComponents; ++kspec) {
3405  deltaGRxn[irxn] += dtmp_ptr[kspec] * feSpecies[kspec];
3406  if (molNumSpecies[kspec] < VCS_DELETE_MINORSPECIES_CUTOFF && dtmp_ptr[kspec] < 0.0) {
3407  icase = 1;
3408  }
3409  }
3410  if (icase) {
3411  deltaGRxn[irxn] = std::max(0.0, deltaGRxn[irxn]);
3412  }
3413  }
3414  }
3415  } else if (L == 0) {
3416  // ALL REACTIONS
3417  for (size_t irxn = 0; irxn < irxnl; ++irxn) {
3418  int icase = 0;
3419  deltaGRxn[irxn] = feSpecies[m_indexRxnToSpecies[irxn]];
3420  double* dtmp_ptr = m_stoichCoeffRxnMatrix.ptrColumn(irxn);
3421  for (size_t kspec = 0; kspec < m_numComponents; ++kspec) {
3422  deltaGRxn[irxn] += dtmp_ptr[kspec] * feSpecies[kspec];
3423  if (molNumSpecies[kspec] < VCS_DELETE_MINORSPECIES_CUTOFF &&
3424  dtmp_ptr[kspec] < 0.0) {
3425  icase = 1;
3426  }
3427  }
3428  if (icase) {
3429  deltaGRxn[irxn] = std::max(0.0, deltaGRxn[irxn]);
3430  }
3431  }
3432  } else {
3433  // MINORS AND ZEROED SPECIES
3434  for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
3435  size_t kspec = irxn + m_numComponents;
3436  if (m_speciesStatus[kspec] <= VCS_SPECIES_MINOR) {
3437  int icase = 0;
3438  deltaGRxn[irxn] = feSpecies[m_indexRxnToSpecies[irxn]];
3439  double* dtmp_ptr = m_stoichCoeffRxnMatrix.ptrColumn(irxn);
3440  for (kspec = 0; kspec < m_numComponents; ++kspec) {
3441  deltaGRxn[irxn] += dtmp_ptr[kspec] * feSpecies[kspec];
3442  if (m_molNumSpecies_old[kspec] < VCS_DELETE_MINORSPECIES_CUTOFF &&
3443  dtmp_ptr[kspec] < 0.0) {
3444  icase = 1;
3445  }
3446  }
3447  if (icase) {
3448  deltaGRxn[irxn] = std::max(0.0, deltaGRxn[irxn]);
3449  }
3450  }
3451  }
3452  }
3453 
3454  /* **** MULTISPECIES PHASES WITH ZERO MOLES ******** */
3455  //
3456  // Massage the free energies for species with zero mole fractions in
3457  // multispecies phases. This section implements the Equation 3.8-5 in Smith
3458  // and Missen, p.59. A multispecies phase will exist iff
3459  //
3460  // 1 < sum_i(exp(-dg_i)/AC_i)
3461  //
3462  // If DG is negative then that species wants to be reintroduced into the
3463  // calculation. For small dg_i, the expression below becomes:
3464  //
3465  // 1 - sum_i(exp(-dg_i)/AC_i) ~ sum_i((dg_i-1)/AC_i) + 1
3466  //
3467  // So, what we are doing here is equalizing all DG's in a multispecies phase
3468  // whose total mole number has already been zeroed out. It must have to do
3469  // with the case where a complete multispecies phase is currently zeroed
3470  // out. In that case, when one species in that phase has a negative DG, then
3471  // the phase should kick in. This code section will cause that to happen,
3472  // because a negative DG will dominate the calculation of SDEL. Then, DG(I)
3473  // for all species in that phase will be forced to be equal and negative.
3474  // Thus, all species in that phase will come into being at the same time.
3475  //
3476  // HKM -> The ratio of mole fractions at the reinstatement time should be
3477  // equal to the normalized weighting of exp(-dg_i) / AC_i. This should be
3478  // implemented.
3479  //
3480  // HKM -> There is circular logic here. ActCoeff depends on the mole
3481  // fractions of a phase that does not exist. In actuality the proto-mole
3482  // fractions should be selected from the solution of a nonlinear problem
3483  // with NsPhase unknowns
3484  //
3485  // X_i = exp(-dg[irxn]) / ActCoeff_i / denom
3486  //
3487  // where
3488  //
3489  // denom = sum_i[ exp(-dg[irxn]) / ActCoeff_i ]
3490  //
3491  // This can probably be solved by successive iteration. This should be
3492  // implemented.
3493  if (alterZeroedPhases && false) {
3494  for (size_t iph = 0; iph < m_numPhases; iph++) {
3495  bool lneed = false;
3496  vcs_VolPhase* Vphase = m_VolPhaseList[iph];
3497  if (! Vphase->m_singleSpecies) {
3498  double sum = 0.0;
3499  for (size_t k = 0; k < Vphase->nSpecies(); k++) {
3500  size_t kspec = Vphase->spGlobalIndexVCS(k);
3501  if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
3502  sum += molNumSpecies[kspec];
3503  }
3504  if (sum > 0.0) {
3505  break;
3506  }
3507  }
3508  if (sum == 0.0) {
3509  lneed = true;
3510  }
3511  }
3512 
3513  if (lneed) {
3514  double poly = 0.0;
3515  for (size_t k = 0; k < Vphase->nSpecies(); k++) {
3516  size_t kspec = Vphase->spGlobalIndexVCS(k);
3517  // We may need to look at deltaGRxn for components!
3518  if (kspec >= m_numComponents) {
3519  size_t irxn = kspec - m_numComponents;
3520  deltaGRxn[irxn] = clip(deltaGRxn[irxn], -50.0, 50.0);
3521  poly += exp(-deltaGRxn[irxn])/actCoeffSpecies[kspec];
3522  }
3523  }
3524 
3525  // Calculate deltaGRxn[] for each species in a zeroed
3526  // multispecies phase. All of the m_deltaGRxn_new[]'s will be
3527  // equal. If deltaGRxn[] is negative, then the phase will come
3528  // back into existence.
3529  for (size_t k = 0; k < Vphase->nSpecies(); k++) {
3530  size_t kspec = Vphase->spGlobalIndexVCS(k);
3531  if (kspec >= m_numComponents) {
3532  size_t irxn = kspec - m_numComponents;
3533  deltaGRxn[irxn] = 1.0 - poly;
3534  }
3535  }
3536  }
3537  }
3538  }
3539 }
3540 
3541 void VCS_SOLVE::vcs_printDeltaG(const int stateCalc)
3542 {
3543  double* deltaGRxn = &m_deltaGRxn_old[0];
3544  double* feSpecies = &m_feSpecies_old[0];
3545  double* molNumSpecies = &m_molNumSpecies_old[0];
3546  const double* tPhMoles_ptr = &m_tPhaseMoles_old[0];
3547  const double* actCoeff_ptr = &m_actCoeffSpecies_old[0];
3548  if (stateCalc == VCS_STATECALC_NEW) {
3549  deltaGRxn = &m_deltaGRxn_new[0];
3550  feSpecies = &m_feSpecies_new[0];
3551  molNumSpecies = &m_molNumSpecies_new[0];
3552  actCoeff_ptr = &m_actCoeffSpecies_new[0];
3553  tPhMoles_ptr = &m_tPhaseMoles_new[0];
3554  }
3555  double RT = m_temperature * GasConstant;
3556  bool zeroedPhase = false;
3557  if (m_debug_print_lvl >= 2) {
3558  plogf(" --- DELTA_G TABLE Components:");
3559  for (size_t j = 0; j < m_numComponents; j++) {
3560  plogf(" %3d ", j);
3561  }
3562  plogf("\n --- Components Moles:");
3563  for (size_t j = 0; j < m_numComponents; j++) {
3564  plogf("%10.3g", m_molNumSpecies_old[j]);
3565  }
3566  plogf("\n --- NonComponent| Moles | ");
3567  for (size_t j = 0; j < m_numComponents; j++) {
3568  plogf("%-10.10s", m_speciesName[j]);
3569  }
3570  plogf("\n");
3571  for (size_t i = 0; i < m_numRxnTot; i++) {
3572  plogf(" --- %3d ", m_indexRxnToSpecies[i]);
3573  plogf("%-10.10s", m_speciesName[m_indexRxnToSpecies[i]]);
3574  if (m_speciesUnknownType[m_indexRxnToSpecies[i]] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
3575  plogf("| NA |");
3576  } else {
3577  plogf("|%10.3g|", m_molNumSpecies_old[m_indexRxnToSpecies[i]]);
3578  }
3579  for (size_t j = 0; j < m_numComponents; j++) {
3580  plogf(" %6.2f", m_stoichCoeffRxnMatrix(j,i));
3581  }
3582  plogf("\n");
3583  }
3584  plogf(" ");
3585  for (int i=0; i<77; i++) {
3586  plogf("-");
3587  }
3588  plogf("\n");
3589  }
3590 
3591  writelog(" --- DeltaG Table (J/kmol) Name PhID MoleNum MolFR "
3592  " ElectrChemStar ElectrChem DeltaGStar DeltaG(Pred) Stability\n");
3593  writelog(" ");
3594  writeline('-', 132);
3595 
3596  for (size_t kspec = 0; kspec < m_numSpeciesTot; kspec++) {
3597  size_t irxn = npos;
3598  if (kspec >= m_numComponents) {
3599  irxn = kspec - m_numComponents;
3600  }
3601  double mfValue = 1.0;
3602  size_t iphase = m_phaseID[kspec];
3603  const vcs_VolPhase* Vphase = m_VolPhaseList[iphase];
3604  if ((m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDMS) ||
3605  (m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDPHASE) ||
3606  (m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDSS)) {
3607  zeroedPhase = true;
3608  } else {
3609  zeroedPhase = false;
3610  }
3611  if (tPhMoles_ptr[iphase] > 0.0) {
3612  if (molNumSpecies[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) {
3613  mfValue = VCS_DELETE_MINORSPECIES_CUTOFF / tPhMoles_ptr[iphase];
3614  } else {
3615  mfValue = molNumSpecies[kspec] / tPhMoles_ptr[iphase];
3616  }
3617  } else {
3618  size_t klocal = m_speciesLocalPhaseIndex[kspec];
3619  mfValue = Vphase->moleFraction(klocal);
3620  }
3621  if (zeroedPhase) {
3622  writelog(" --- ** zp *** ");
3623  } else {
3624  writelog(" --- ");
3625  }
3626  double feFull = feSpecies[kspec];
3627  if ((m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDMS) ||
3628  (m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDPHASE)) {
3629  feFull += log(actCoeff_ptr[kspec]) + log(mfValue);
3630  }
3631  writelogf("%-24.24s", m_speciesName[kspec]);
3632  writelogf(" %3d", iphase);
3633  if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
3634  writelog(" NA ");
3635  } else {
3636  writelogf(" % -12.4e", molNumSpecies[kspec]);
3637  }
3638  writelogf(" % -12.4e", mfValue);
3639  writelogf(" % -12.4e", feSpecies[kspec] * RT);
3640  writelogf(" % -12.4e", feFull * RT);
3641  if (irxn != npos) {
3642  writelogf(" % -12.4e", deltaGRxn[irxn] * RT);
3643  writelogf(" % -12.4e", (deltaGRxn[irxn] + feFull - feSpecies[kspec]) * RT);
3644 
3645  if (deltaGRxn[irxn] < 0.0) {
3646  if (molNumSpecies[kspec] > 0.0) {
3647  writelog(" growing");
3648  } else {
3649  writelog(" stable");
3650  }
3651  } else if (deltaGRxn[irxn] > 0.0) {
3652  if (molNumSpecies[kspec] > 0.0) {
3653  writelog(" shrinking");
3654  } else {
3655  writelog(" unstable");
3656  }
3657  } else {
3658  writelog(" balanced");
3659  }
3660  }
3661  writelog(" \n");
3662  }
3663  writelog(" ");
3664  writeline('-', 132);
3665 }
3666 
3667 void VCS_SOLVE::vcs_deltag_Phase(const size_t iphase, const bool doDeleted,
3668  const int stateCalc, const bool alterZeroedPhases)
3669 {
3670  warn_deprecated("VCS_SOLVE::vcs_deltag_Phase",
3671  "Unused. To be removed after Cantera 2.3.");
3672  double* feSpecies=0;
3673  double* deltaGRxn=0;
3674  double* actCoeffSpecies=0;
3675  if (stateCalc == VCS_STATECALC_NEW) {
3676  feSpecies = &m_feSpecies_new[0];
3677  deltaGRxn = &m_deltaGRxn_new[0];
3678  actCoeffSpecies = &m_actCoeffSpecies_new[0];
3679  } else if (stateCalc == VCS_STATECALC_OLD) {
3680  feSpecies = &m_feSpecies_old[0];
3681  deltaGRxn = &m_deltaGRxn_old[0];
3682  actCoeffSpecies = &m_actCoeffSpecies_old[0];
3683  } else {
3684  throw CanteraError("VCS_SOLVE::vcs_deltag_Phase", "bad stateCalc");
3685  }
3686 
3687  size_t irxnl = (doDeleted) ? m_numRxnTot : m_numRxnRdc;
3688  vcs_VolPhase* vPhase = m_VolPhaseList[iphase];
3689 
3690  if (m_debug_print_lvl >= 2) {
3691  plogf(" --- Subroutine vcs_deltag_Phase called for phase %d\n",
3692  iphase);
3693  }
3694 
3695  if (vPhase->m_singleSpecies) {
3696  // Single species Phase
3697  size_t kspec = vPhase->spGlobalIndexVCS(0);
3698  AssertThrowMsg(iphase == m_phaseID[kspec], "VCS_SOLVE::vcs_deltag_Phase",
3699  "index error");
3700  if (kspec >= m_numComponents) {
3701  size_t irxn = kspec - m_numComponents;
3702  deltaGRxn[irxn] = feSpecies[kspec];
3703  for (size_t kcomp = 0; kcomp < m_numComponents; ++kcomp) {
3704  deltaGRxn[irxn] += m_stoichCoeffRxnMatrix(kcomp,irxn) * feSpecies[kcomp];
3705  }
3706  }
3707  } else {
3708  // Multispecies Phase
3709  bool zeroedPhase = true;
3710  for (size_t irxn = 0; irxn < irxnl; ++irxn) {
3711  size_t kspec = m_indexRxnToSpecies[irxn];
3712  if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE && m_phaseID[kspec] == iphase) {
3713  if (m_molNumSpecies_old[kspec] > 0.0) {
3714  zeroedPhase = false;
3715  }
3716  deltaGRxn[irxn] = feSpecies[kspec];
3717  for (size_t kcomp = 0; kcomp < m_numComponents; ++kcomp) {
3718  deltaGRxn[irxn] += m_stoichCoeffRxnMatrix(kcomp,irxn) * feSpecies[kcomp];
3719  }
3720  }
3721  }
3722 
3723  // MULTISPECIES PHASES WITH ZERO MOLES
3724  //
3725  // Massage the free energies for species with zero mole fractions in
3726  // multispecies phases. This section implements the Equation 3.8-5 in
3727  // Smith and Missen, p.59. A multispecies phase will exist iff
3728  //
3729  // 1 < sum_i(exp(-dg_i)/AC_i)
3730  //
3731  // If DG is negative then that species wants to be reintroduced into the
3732  // calculation. For small dg_i, the expression below becomes:
3733  //
3734  // 1 - sum_i(exp(-dg_i)/AC_i) ~ sum_i((dg_i-1)/AC_i) + 1
3735  //
3736  // HKM -> The ratio of mole fractions at the reinstatement time should
3737  // be equal to the normalized weighting of exp(-dg_i) / AC_i. This
3738  // should be implemented.
3739  //
3740  // HKM -> There is circular logic here. ActCoeff depends on the mole
3741  // fractions of a phase that does not exist. In actuality the proto-mole
3742  // fractions should be selected from the solution of a nonlinear problem
3743  // with NsPhase unknowns
3744  //
3745  // X_i = exp(-dg[irxn]) / ActCoeff_i / denom
3746  //
3747  // where
3748  //
3749  // denom = sum_i[ exp(-dg[irxn]) / ActCoeff_i ]
3750  //
3751  // This can probably be solved by successive iteration. This should be
3752  // implemented.
3753  //
3754  // Calculate dg[] for each species in a zeroed multispecies phase. All
3755  // of the dg[]'s will be equal. If dg[] is negative, then the phase will
3756  // come back into existence.
3757  if (alterZeroedPhases && zeroedPhase) {
3758  double phaseDG = 1.0;
3759  for (size_t irxn = 0; irxn < irxnl; ++irxn) {
3760  size_t kspec = m_indexRxnToSpecies[irxn];
3761  if (m_phaseID[kspec] == iphase) {
3762  deltaGRxn[irxn] = clip(deltaGRxn[irxn], -50.0, 50.0);
3763  phaseDG -= exp(-deltaGRxn[irxn])/actCoeffSpecies[kspec];
3764  }
3765  }
3766 
3767  // Overwrite the individual dg's with the phase DG.
3768  for (size_t irxn = 0; irxn < irxnl; ++irxn) {
3769  size_t kspec = m_indexRxnToSpecies[irxn];
3770  if (m_phaseID[kspec] == iphase) {
3771  deltaGRxn[irxn] = 1.0 - phaseDG;
3772  }
3773  }
3774  }
3775  }
3776 }
3777 
3778 void VCS_SOLVE::vcs_switch_pos(const bool ifunc, const size_t k1, const size_t k2)
3779 {
3780  if (k1 == k2) {
3781  return;
3782  }
3783  if (k1 >= m_numSpeciesTot || k2 >= m_numSpeciesTot) {
3784  plogf("vcs_switch_pos: ifunc = 0: inappropriate args: %d %d\n",
3785  k1, k2);
3786  }
3787 
3788  // Handle the index pointer in the phase structures first
3789  vcs_VolPhase* pv1 = m_VolPhaseList[m_phaseID[k1]];
3790  vcs_VolPhase* pv2 = m_VolPhaseList[m_phaseID[k2]];
3791  size_t kp1 = m_speciesLocalPhaseIndex[k1];
3792  size_t kp2 = m_speciesLocalPhaseIndex[k2];
3793  AssertThrowMsg(pv1->spGlobalIndexVCS(kp1) == k1, "VCS_SOLVE::vcs_switch_pos",
3794  "Indexing error");
3795  AssertThrowMsg(pv2->spGlobalIndexVCS(kp2) == k2, "VCS_SOLVE::vcs_switch_pos",
3796  "Indexing error");
3797  pv1->setSpGlobalIndexVCS(kp1, k2);
3798  pv2->setSpGlobalIndexVCS(kp2, k1);
3799  std::swap(m_speciesName[k1], m_speciesName[k2]);
3800  std::swap(m_molNumSpecies_old[k1], m_molNumSpecies_old[k2]);
3801  std::swap(m_speciesUnknownType[k1], m_speciesUnknownType[k2]);
3802  std::swap(m_molNumSpecies_new[k1], m_molNumSpecies_new[k2]);
3803  std::swap(m_SSfeSpecies[k1], m_SSfeSpecies[k2]);
3804  std::swap(m_spSize[k1], m_spSize[k2]);
3805  std::swap(m_deltaMolNumSpecies[k1], m_deltaMolNumSpecies[k2]);
3806  std::swap(m_feSpecies_old[k1], m_feSpecies_old[k2]);
3807  std::swap(m_feSpecies_new[k1], m_feSpecies_new[k2]);
3808  std::swap(m_SSPhase[k1], m_SSPhase[k2]);
3809  std::swap(m_phaseID[k1], m_phaseID[k2]);
3810  std::swap(m_speciesMapIndex[k1], m_speciesMapIndex[k2]);
3811  std::swap(m_speciesLocalPhaseIndex[k1], m_speciesLocalPhaseIndex[k2]);
3812  std::swap(m_actConventionSpecies[k1], m_actConventionSpecies[k2]);
3813  std::swap(m_lnMnaughtSpecies[k1], m_lnMnaughtSpecies[k2]);
3814  std::swap(m_actCoeffSpecies_new[k1], m_actCoeffSpecies_new[k2]);
3815  std::swap(m_actCoeffSpecies_old[k1], m_actCoeffSpecies_old[k2]);
3816  std::swap(m_wtSpecies[k1], m_wtSpecies[k2]);
3817  std::swap(m_chargeSpecies[k1], m_chargeSpecies[k2]);
3818  std::swap(m_speciesThermoList[k1], m_speciesThermoList[k2]);
3819  std::swap(m_PMVolumeSpecies[k1], m_PMVolumeSpecies[k2]);
3820 
3821  for (size_t j = 0; j < m_numElemConstraints; ++j) {
3822  std::swap(m_formulaMatrix(k1,j), m_formulaMatrix(k2,j));
3823  }
3824  if (m_useActCoeffJac && k1 != k2) {
3825  for (size_t i = 0; i < m_numSpeciesTot; i++) {
3826  std::swap(m_np_dLnActCoeffdMolNum(k1,i), m_np_dLnActCoeffdMolNum(k2,i));
3827  }
3828  for (size_t i = 0; i < m_numSpeciesTot; i++) {
3829  std::swap(m_np_dLnActCoeffdMolNum(i,k1), m_np_dLnActCoeffdMolNum(i,k2));
3830  }
3831  }
3832  std::swap(m_speciesStatus[k1], m_speciesStatus[k2]);
3833 
3834  // Handle the index pointer in the phase structures
3835  if (ifunc) {
3836  // Find the Rxn indices corresponding to the two species
3837  size_t i1 = k1 - m_numComponents;
3838  size_t i2 = k2 - m_numComponents;
3839  if (i1 > m_numRxnTot || i2 >= m_numRxnTot) {
3840  plogf("switch_pos: ifunc = 1: inappropriate noncomp values: %d %d\n",
3841  i1 , i2);
3842  }
3843  for (size_t j = 0; j < m_numComponents; ++j) {
3844  std::swap(m_stoichCoeffRxnMatrix(j,i1), m_stoichCoeffRxnMatrix(j,i2));
3845  }
3846  std::swap(m_scSize[i1], m_scSize[i2]);
3847  for (size_t iph = 0; iph < m_numPhases; iph++) {
3848  std::swap(m_deltaMolNumPhase(iph,i1), m_deltaMolNumPhase(iph,i2));
3849  std::swap(m_phaseParticipation(iph,i1),
3850  m_phaseParticipation(iph,i2));
3851  }
3852  std::swap(m_deltaGRxn_new[i1], m_deltaGRxn_new[i2]);
3853  std::swap(m_deltaGRxn_old[i1], m_deltaGRxn_old[i2]);
3854  std::swap(m_deltaGRxn_tmp[i1], m_deltaGRxn_tmp[i2]);
3855  }
3856 }
3857 
3858 double VCS_SOLVE::vcs_birthGuess(const int kspec)
3859 {
3860  warn_deprecated("VCS_SOLVE::vcs_birthGuess",
3861  "Unused. To be removed after Cantera 2.3.");
3862  size_t irxn = kspec - m_numComponents;
3863  double dx = 0.0;
3864  if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
3865  return dx;
3866  }
3867 
3868  // Check to make sure that species is zero in the solution vector
3869  // If it isn't, we don't know what's happening
3870  AssertThrowMsg(m_molNumSpecies_old[kspec] == 0.0,
3871  "VCS_SOLVE::vcs_birthGuess", "we shouldn't be here");
3872  if (!m_SSPhase[kspec]) {
3873  // Logic to handle species in multiple species phases. We cap the moles
3874  // here at 1.0E-15 kmol.
3875  bool soldel_ret;
3876  double dxm = vcs_minor_alt_calc(kspec, irxn, &soldel_ret);
3877  dx = std::min(VCS_DELETE_MINORSPECIES_CUTOFF + dxm, 1e-15);
3878  } else {
3879  // Logic to handle single species phases. There is no real way to
3880  // estimate the moles. So we set it to a small number.
3881  dx = 1.0E-30;
3882  }
3883 
3884  // Check to see if the current value of the components allow the dx just
3885  // estimated. If we are in danger of zeroing a component, only go 1/3 the
3886  // way to zeroing the component with this dx. Note, this may mean that dx= 0
3887  // coming back from this routine. This evaluation should be respected.
3888  double* sc_irxn = m_stoichCoeffRxnMatrix.ptrColumn(irxn);
3889  for (size_t j = 0; j < m_numComponents; ++j) {
3890  // Only loop over element constraints that involve positive def. constraints
3891  if (m_speciesUnknownType[j] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
3892  if (m_molNumSpecies_old[j] > 0.0) {
3893  double tmp = sc_irxn[j] * dx;
3894  if (3.0*(-tmp) > m_molNumSpecies_old[j]) {
3895  dx = std::min(dx, - 0.3333* m_molNumSpecies_old[j] / sc_irxn[j]);
3896  }
3897  }
3898  if (m_molNumSpecies_old[j] <= 0.0 && sc_irxn[j] < 0.0) {
3899  dx = 0.0;
3900  }
3901  }
3902  }
3903  return dx;
3904 }
3905 
3906 void VCS_SOLVE::vcs_setFlagsVolPhases(const bool upToDate, const int stateCalc)
3907 {
3908  if (!upToDate) {
3909  for (size_t iph = 0; iph < m_numPhases; iph++) {
3910  m_VolPhaseList[iph]->setMolesOutOfDate(stateCalc);
3911  }
3912  } else {
3913  for (size_t iph = 0; iph < m_numPhases; iph++) {
3914  m_VolPhaseList[iph]->setMolesCurrent(stateCalc);
3915  }
3916  }
3917 }
3918 
3919 void VCS_SOLVE::vcs_setFlagsVolPhase(const size_t iph, const bool upToDate,
3920  const int stateCalc)
3921 {
3922  if (!upToDate) {
3923  m_VolPhaseList[iph]->setMolesOutOfDate(stateCalc);
3924  } else {
3925  m_VolPhaseList[iph]->setMolesCurrent(stateCalc);
3926  }
3927 }
3928 
3929 void VCS_SOLVE::vcs_updateMolNumVolPhases(const int stateCalc)
3930 {
3931  for (size_t iph = 0; iph < m_numPhases; iph++) {
3932  m_VolPhaseList[iph]->updateFromVCS_MoleNumbers(stateCalc);
3933  }
3934 }
3935 
3936 }
#define VCS_PHASE_EXIST_NO
Phase doesn&#39;t currently exist in the mixture.
Definition: vcs_defs.h:218
bool vcs_doubleEqual(double d1, double d2)
Simple routine to check whether two doubles are equal up to roundoff error.
Definition: vcs_util.cpp:116
bool m_singleSpecies
If true, this phase consists of a single species.
Definition: vcs_VolPhase.h:566
void updateFromVCS_MoleNumbers(const int stateCalc)
Update the moles within the phase, if necessary.
#define VCS_SPECIES_ZEROEDMS
Species lies in a multicomponent phase with concentration zero.
Definition: vcs_defs.h:143
#define VCS_SPECIES_MINOR
Species is a major species.
Definition: vcs_defs.h:129
Declarations for a simple class that implements an Ansi C wall clock timer (see Cantera::clockWC).
#define VCS_PHASE_EXIST_ALWAYS
Always exists because it contains inerts which can&#39;t exist in any other phase.
Definition: vcs_defs.h:205
void setMolesFromVCSCheck(const int vcsStateStatus, const double *molesSpeciesVCS, const double *const TPhMoles)
Set the moles within the phase.
#define VCS_PHASE_EXIST_ZEROEDPHASE
Phase currently is zeroed due to a programmatic issue.
Definition: vcs_defs.h:224
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:165
void writelog(const std::string &fmt, const Args &... args)
Write a formatted message to the screen.
Definition: global.h:179
double electricPotential() const
Returns the electric field of the phase.
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.
int solve(DenseMatrix &A, double *b, size_t nrhs, size_t ldb)
Solve Ax = b. Array b is overwritten on exit with x.
The class provides the wall clock timer in seconds.
Definition: clockWC.h:44
#define VCS_SPECIES_INTERFACIALVOLTAGE
Species refers to an electron in the metal.
Definition: vcs_defs.h:165
std::string PhaseName
String name for the phase.
Definition: vcs_VolPhase.h:653
doublereal * ptrColumn(size_t j)
Return a pointer to the top of column j, columns are contiguous in memory.
Definition: Array.h:314
#define VCS_PHASE_EXIST_YES
Phase is a normal phase that currently exists.
Definition: vcs_defs.h:208
void setExistence(const int existence)
Set the existence flag in the object.
void setMolesFromVCS(const int stateCalc, const double *molesSpeciesVCS=0)
Set the moles within the phase.
std::vector< int > vector_int
Vector of ints.
Definition: ct_defs.h:159
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_SPECIES_ZEROEDSS
Species is a SS phase, that is currently zeroed out.
Definition: vcs_defs.h:149
#define VCS_SPECIES_TYPE_MOLNUM
Unknown refers to mole number of a single species.
Definition: vcs_defs.h:301
#define VCS_ELEM_TYPE_ABSPOS
Normal element constraint consisting of positive coefficients for the formula matrix.
Definition: vcs_defs.h:248
void resize(size_t n, size_t m, doublereal v=0.0)
Resize the matrix.
Definition: DenseMatrix.cpp:69
#define VCS_SPECIES_ZEROEDPHASE
Species lies in a multicomponent phase that is zeroed atm.
Definition: vcs_defs.h:173
#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_SUCCESS
Definition: vcs_defs.h:18
double secondsWC()
Returns the wall clock time in seconds since the last reset.
Definition: clockWC.cpp:32
#define VCS_DELETE_PHASE_CUTOFF
Cutoff relative moles below which a phase is deleted from the equilibrium problem.
Definition: vcs_defs.h:65
#define VCS_STATECALC_NEW
State Calculation based on the new or tentative mole numbers.
Definition: vcs_defs.h:323
size_t nSpecies() const
Return the number of species in the phase.
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:65
#define VCS_SPECIES_ACTIVEBUTZERO
Species lies in a multicomponent phase that is active, but species concentration is zero...
Definition: vcs_defs.h:183
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
#define VCS_RELDELETE_SPECIES_CUTOFF
Cutoff relative mole fraction value, below which species are deleted from the equilibrium problem...
Definition: vcs_defs.h:47
#define VCS_SPECIES_STOICHZERO
Species lies in a multicomponent phase that is active, but species concentration is zero due to stoic...
Definition: vcs_defs.h:195
void writelogf(const char *fmt, const Args &... args)
Write a formatted message to the screen.
Definition: global.h:200
Phase information and Phase calculations for vcs.
Definition: vcs_VolPhase.h:81
#define VCS_DIMENSIONAL_G
dimensioned
Definition: vcs_defs.h:96
#define AssertThrowMsg(expr, procedure,...)
Assertion must be true or an error is thrown.
Definition: ctexceptions.h:270
#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
#define plogendl()
define this Cantera function to replace cout << endl;
Definition: vcs_internal.h:25
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
Headers for the DenseMatrix object, which deals with dense rectangular matrices and description of th...
void setSpGlobalIndexVCS(const size_t spIndex, const size_t spGlobalIndex)
set the Global VCS index of the kth species in the phase
Contains declarations for string manipulation functions within Cantera.
const char * vcs_speciesType_string(int speciesStatus, int length)
Returns a const char string representing the type of the species given by the first argument...
Definition: vcs_util.cpp:58
#define plogf
define this Cantera function to replace printf
Definition: vcs_internal.h:18
#define VCS_SPECIES_TYPE_INTERFACIALVOLTAGE
Unknown refers to the voltage level of a phase.
Definition: vcs_defs.h:309
#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
#define VCS_SPECIES_DELETED
Species has such a small mole fraction it is deleted even though its phase may possibly exist...
Definition: vcs_defs.h:158
Definitions for the classes that are thrown when Cantera experiences an error condition (also contain...
A class for full (non-sparse) matrices with Fortran-compatible data storage, which adds matrix operat...
Definition: DenseMatrix.h:72
void setTotalMoles(const double totalMols)
Sets the total moles in the phase.