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