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