Cantera  3.3.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(double* const dsLocal,
29 double* const 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[0], &m_wx[0]);
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 vector<size_t> phasePopPhaseIDs(0);
405 size_t iphasePop = vcs_popPhaseID(phasePopPhaseIDs);
406 if (iphasePop != npos) {
407 int soldel = vcs_popPhaseRxnStepSizes(iphasePop);
408 if (soldel == 3) {
409 iphasePop = npos;
410 if (m_debug_print_lvl >= 2) {
411 plogf(" --- vcs_popPhaseRxnStepSizes() was called but stoich "
412 "prevented phase %d popping\n");
413 }
414 }
415 }
416
417 // DETERMINE THE REACTION STEP SIZES FOR MAIN STEP AND IF PHASES DIE
418 //
419 // Don't do this step if there is a phase pop
420 size_t iphaseDelete = npos;
421 size_t kspec;
422 if (iphasePop == npos) {
423 // Figure out the new reaction step sizes for the major species (do
424 // minor species in the future too)
425 kspec = npos;
426 iphaseDelete = vcs_RxnStepSizes(forceComponentCalc, kspec);
427 } else if (m_debug_print_lvl >= 2) {
428 plogf(" --- vcs_RxnStepSizes not called because alternative"
429 "phase creation delta was used instead\n");
430 }
431 size_t doPhaseDeleteKspec = npos;
432 size_t doPhaseDeleteIph = npos;
433
434 // Zero out the net change in moles of multispecies phases
435 m_deltaPhaseMoles.assign(m_deltaPhaseMoles.size(), 0.0);
436
437 // MAIN LOOP IN CALCULATION: LOOP OVER IRXN TO DETERMINE STEP SIZE
438 //
439 // Loop through all of the reactions, irxn, pertaining to the formation
440 // reaction for species kspec in canonical form.
441 //
442 // At the end of this loop, we will have a new estimate for the mole numbers
443 // for all species consistent with an extent of reaction for all
444 // noncomponent species formation reactions. We will have also ensured that
445 // all predicted non-component mole numbers are greater than zero.
446 //
447 // Old_Solution New_Solution Description
448 // -----------------------------------------------------------------------------
449 // m_molNumSpecies_old[kspec] m_molNumSpecies_new[kspec] Species Mole Numbers
450 // m_deltaMolNumSpecies[kspec] Delta in the Species Mole Numbers
451 if (iphaseDelete != npos) {
452 debuglog(" --- Main Loop Treatment -> Circumvented due to Phase Deletion\n", m_debug_print_lvl >= 2);
453 for (size_t k = 0; k < m_nsp; k++) {
455 size_t iph = m_phaseID[k];
457 }
458 if (kspec >= m_numComponents) {
459 if (m_SSPhase[kspec] == 1) {
461 } else {
462 throw CanteraError("VCS_SOLVE::solve_tp_inner",
463 "we shouldn't be here!");
464 }
466 allMinorZeroedSpecies = (m_numRxnMinorZeroed == m_numRxnRdc);
467 }
468
469 // Set the flags indicating the mole numbers in the vcs_VolPhase objects
470 // are out of date.
471 vcs_setFlagsVolPhases(false, VCS_STATECALC_NEW);
472
473 // Calculate the new chemical potentials using the tentative solution
474 // values. We only calculate a subset of these, because we have only
475 // updated a subset of the W().
477
478 // Evaluate DeltaG for all components if ITI=0, and for major components
479 // only if ITI NE 0
480 vcs_deltag(0, false, VCS_STATECALC_NEW);
481 } else {
482 if (m_debug_print_lvl >= 2) {
483 plogf(" --- Main Loop Treatment of each non-component species ");
484 if (iti == 0) {
485 plogf("- Full Calculation:\n");
486 } else {
487 plogf("- Major Components Calculation:\n");
488 }
489 plogf(" --- Species IC KMoles Tent_KMoles Rxn_Adj\n");
490 }
491 for (size_t irxn = 0; irxn < m_numRxnRdc; irxn++) {
492 size_t kspec = m_indexRxnToSpecies[irxn];
493 double* sc_irxn = m_stoichCoeffRxnMatrix.ptrColumn(irxn);
494 size_t iph = m_phaseID[kspec];
495 vcs_VolPhase* Vphase = m_VolPhaseList[iph].get();
496 double dx;
497
498 if (iphasePop != npos) {
499 if (iph == iphasePop) {
500 dx = m_deltaMolNumSpecies[kspec];
502 } else {
503 dx = 0.0;
505 }
506 } else if (m_speciesStatus[kspec] == VCS_SPECIES_INTERFACIALVOLTAGE) {
507 // VOLTAGE SPECIES
508 bool soldel_ret;
509 dx = vcs_minor_alt_calc(kspec, irxn, &soldel_ret);
510 m_deltaMolNumSpecies[kspec] = dx;
511 } else if (m_speciesStatus[kspec] < VCS_SPECIES_MINOR) {
512 // ZEROED OUT SPECIES
513 bool resurrect = (m_deltaMolNumSpecies[kspec] > 0.0);
514 if (m_debug_print_lvl >= 3) {
515 plogf(" --- %s currently zeroed (SpStatus=%-2d):",
516 m_speciesName[kspec], m_speciesStatus[kspec]);
517 plogf("%3d DG = %11.4E WT = %11.4E W = %11.4E DS = %11.4E\n",
518 irxn, m_deltaGRxn_new[irxn], m_molNumSpecies_new[kspec],
520 }
521 if (m_deltaGRxn_new[irxn] >= 0.0 || !resurrect) {
523 m_deltaMolNumSpecies[kspec] = 0.0;
524 resurrect = false;
525 } else {
526 for (size_t j = 0; j < m_nelem; ++j) {
527 int elType = m_elType[j];
528 if (elType == VCS_ELEM_TYPE_ABSPOS) {
529 double atomComp = m_formulaMatrix(kspec,j);
530 if (atomComp > 0.0) {
531 double maxPermissible = m_elemAbundancesGoal[j] / atomComp;
532 if (maxPermissible < VCS_DELETE_MINORSPECIES_CUTOFF) {
533 resurrect = false;
534 break;
535 }
536 }
537 }
538 }
539 }
540
541 // Resurrect the species
542 if (resurrect) {
543 if (Vphase->exists() == VCS_PHASE_EXIST_NO) {
544 if (m_debug_print_lvl >= 2) {
545 plogf(" --- Zeroed species changed to major: ");
546 plogf("%-12s\n", m_speciesName[kspec]);
547 }
549 allMinorZeroedSpecies = false;
550 } else {
551 if (m_debug_print_lvl >= 2) {
552 plogf(" --- Zeroed species changed to minor: ");
553 plogf("%-12s\n", m_speciesName[kspec]);
554 }
556 }
557 if (m_deltaMolNumSpecies[kspec] > 0.0) {
558 dx = m_deltaMolNumSpecies[kspec] * 0.01;
559 m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + dx;
560 } else {
562 dx = m_molNumSpecies_new[kspec] - m_molNumSpecies_old[kspec];
563 }
564 m_deltaMolNumSpecies[kspec] = dx;
565 } else {
567 m_deltaMolNumSpecies[kspec] = 0.0;
568 dx = 0.0;
569 }
570 } else if (m_speciesStatus[kspec] == VCS_SPECIES_MINOR) {
571 // MINOR SPECIES
572 //
573 // Unless ITI isn't equal to zero we zero out changes to minor
574 // species.
575 if (iti != 0) {
577 m_deltaMolNumSpecies[kspec] = 0.0;
578 dx = 0.0;
579 if (m_debug_print_lvl >= 2) {
580 plogf(" --- %-12s%3d% 11.4E %11.4E %11.4E\n",
582 m_deltaMolNumSpecies[kspec]);
583 }
584 continue;
585 }
586
587 // Minor species alternative calculation
588 //
589 // This is based upon the following approximation:
590 // The mole fraction changes due to these reactions don't affect
591 // the mole numbers of the component species. Therefore the
592 // following approximation is valid for an ideal solution
593 // 0 = DG(I) + log(WT(I)/W(I))
594 // (DG contains the contribution from FF(I) + log(W(I)/TL) )
595 // Thus,
596 // WT(I) = W(I) EXP(-DG(I))
597 // If soldel is true on return, then we branch to the section
598 // that deletes a species from the current set of active species.
599 bool soldel_ret;
600 dx = vcs_minor_alt_calc(kspec, irxn, &soldel_ret);
601 m_deltaMolNumSpecies[kspec] = dx;
602 m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + dx;
603 if (soldel_ret) {
604 // DELETE MINOR SPECIES LESS THAN VCS_DELETE_SPECIES_CUTOFF
605 // MOLE NUMBER
606 if (m_debug_print_lvl >= 2) {
607 plogf(" --- Delete minor species in multispec phase: %-12s\n",
608 m_speciesName[kspec]);
609 }
610 m_deltaMolNumSpecies[kspec] = 0.0;
611
612 // Delete species, kspec. The alternate return is for the
613 // case where all species become deleted. Then, we need to
614 // branch to the code where we reevaluate the deletion of
615 // all species.
616 size_t lnospec = vcs_delete_species(kspec);
617 if (lnospec) {
618 stage = RECHECK_DELETED;
619 break;
620 }
621
622 // Go back to consider the next species in the list. Note,
623 // however, that the next species in the list is now in slot
624 // l. In deleting the previous species L, We have exchanged
625 // slot MR with slot l, and then have decremented MR.
626 // Therefore, we will decrement the species counter, here.
627 --irxn;
628 continue;
629 }
630 } else {
631 // MAJOR SPECIES
632
633 // Check for superconvergence of the formation reaction. Do
634 // nothing if it is superconverged. Skip to the end of the irxn
635 // loop if it is superconverged.
636 if (fabs(m_deltaGRxn_new[irxn]) <= m_tolmaj2) {
638 m_deltaMolNumSpecies[kspec] = 0.0;
639 dx = 0.0;
640 if (m_debug_print_lvl >= 2) {
641 plogf(" --- %-12s %3d %11.4E %11.4E %11.4E\n",
643 m_deltaMolNumSpecies[kspec]);
644 }
645 continue;
646 }
647
648 // Set the initial step size, dx, equal to the value produced by
649 // the routine, vcs_RxnStepSize().
650 //
651 // Note the multiplication logic is to make sure that dg[]
652 // didn't change sign due to w[] changing in the middle of the
653 // iteration. (it can if a single species phase goes out of
654 // existence).
655 if ((m_deltaGRxn_new[irxn] * m_deltaMolNumSpecies[kspec]) <= 0.0) {
656 dx = m_deltaMolNumSpecies[kspec];
657 } else {
658 dx = 0.0;
659 m_deltaMolNumSpecies[kspec] = 0.0;
660 }
661
662 //Form a tentative value of the new species moles
663 m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + dx;
664
665 // Check for non-positive mole fraction of major species. If we
666 // find one, we branch to a section below. Then, depending upon
667 // the outcome, we branch to sections below, or we restart the
668 // entire iteration.
669 if (m_molNumSpecies_new[kspec] <= 0.0) {
670 // NON-POSITIVE MOLES OF MAJOR SPECIES
671 //
672 // We are here when a tentative value of a mole fraction
673 // created by a tentative value of M_DELTAMOLNUMSPECIES(*)
674 // is negative. We branch from here depending upon whether
675 // this species is in a single species phase or in a
676 // multispecies phase.
677 if (!m_SSPhase[kspec]) {
678 // Section for multispecies phases:
679 // - Cut reaction adjustment for positive kmoles of
680 // major species in multispecies phases. Decrease
681 // its concentration by a factor of 10.
682 dx = -0.9 * m_molNumSpecies_old[kspec];
683 m_deltaMolNumSpecies[kspec] = dx;
684 m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + dx;
685 } else {
686 // Section for single species phases:
687 // Calculate a dx that will wipe out the
688 // moles in the phase.
689 dx = -m_molNumSpecies_old[kspec];
690
691 // Calculate an update that doesn't create a negative
692 // mole number for a component species. Actually,
693 // restrict this a little more so that the component
694 // values can only be reduced by two 99%,
695 for (size_t j = 0; j < m_numComponents; ++j) {
696 if (sc_irxn[j] != 0.0) {
697 m_wx[j] = m_molNumSpecies_old[j] + sc_irxn[j] * dx;
698 if (m_wx[j] <= m_molNumSpecies_old[j] * 0.01 - 1.0E-150) {
699 dx = std::max(dx, m_molNumSpecies_old[j] * -0.99 / sc_irxn[j]);
700 }
701 } else {
702 m_wx[j] = m_molNumSpecies_old[j];
703 }
704 }
705 m_molNumSpecies_new[kspec] = m_molNumSpecies_old[kspec] + dx;
706 if (m_molNumSpecies_new[kspec] > 0.0) {
707 m_deltaMolNumSpecies[kspec] = dx;
708 } else {
709 // We are going to zero the single species phase.
710 // Set the existence flag
711 iph = m_phaseID[kspec];
712 Vphase = m_VolPhaseList[iph].get();
713 // Change the base mole numbers for the iteration.
714 // We need to do this here, because we have decided
715 // to eliminate the phase in this special section
716 // outside the main loop.
717 m_molNumSpecies_new[kspec] = 0.0;
718 doPhaseDeleteIph = iph;
719
720 doPhaseDeleteKspec = kspec;
721 if (m_debug_print_lvl >= 2 && m_speciesStatus[kspec] >= 0) {
722 plogf(" --- SS species changed to zeroedss: ");
723 plogf("%-12s\n", m_speciesName[kspec]);
724 }
727 allMinorZeroedSpecies = (m_numRxnMinorZeroed == m_numRxnRdc);
728
729 for (size_t kk = 0; kk < m_nsp; kk++) {
730 m_deltaMolNumSpecies[kk] = 0.0;
732 }
733 m_deltaMolNumSpecies[kspec] = dx;
734 m_molNumSpecies_new[kspec] = 0.0;
735
736 for (size_t k = 0; k < m_numComponents; ++k) {
737 m_deltaMolNumSpecies[k] = 0.0;
738 }
739 for (iph = 0; iph < m_numPhases; iph++) {
740 m_deltaPhaseMoles[iph] = 0.0;
741 }
742 }
743 }
744 }
745 } // End of Loop on ic[irxn] -> the type of species
746
747 // CALCULATE KMOLE NUMBER CHANGE FOR THE COMPONENT BASIS
748 if (dx != 0.0 && (m_speciesUnknownType[kspec] !=
750
751 // Change the amount of the component compounds according to the
752 // reaction delta that we just computed. This should keep the
753 // amount of material constant.
754 AssertThrowMsg(fabs(m_deltaMolNumSpecies[kspec] -dx) <
755 1.0E-14*(fabs(m_deltaMolNumSpecies[kspec]) + fabs(dx) + 1.0E-32),
756 "VCS_SOLVE::solve_tp_inner",
757 "ds[kspec] = {}, dx = {}, kspec = {}\nwe have a problem!",
758 m_deltaMolNumSpecies[kspec], dx, kspec);
759 for (size_t k = 0; k < m_numComponents; ++k) {
760 m_deltaMolNumSpecies[k] += sc_irxn[k] * dx;
761 }
762
763 // Calculate the tentative change in the total number of moles
764 // in all of the phases
765 for (iph = 0; iph < m_numPhases; iph++) {
766 m_deltaPhaseMoles[iph] += dx * m_deltaMolNumPhase(iph,irxn);
767 }
768 }
769
770 checkDelta1(&m_deltaMolNumSpecies[0],
771 &m_deltaPhaseMoles[0], kspec+1);
772
773 // Branch point for returning
774 if (m_debug_print_lvl >= 2) {
776 plogf(" --- %-12.12s%3d %11.4E %11.4E %11.4E\n",
777 m_speciesName[kspec], m_speciesStatus[kspec],
779 m_deltaMolNumSpecies[kspec]);
780 }
781
782 if (doPhaseDeleteIph != npos) {
783 if (m_debug_print_lvl >= 2) {
784 plogf(" --- %-12.12s Main Loop Special Case deleting phase with species:\n",
785 m_speciesName[doPhaseDeleteKspec]);
786 }
787 break;
788 }
789 } // END OF MAIN LOOP OVER FORMATION REACTIONS
790 if (m_debug_print_lvl >= 2) {
791 for (size_t k = 0; k < m_numComponents; k++) {
792 plogf(" --- ");
793 plogf("%-12.12s", m_speciesName[k]);
794 plogf(" c %11.4E %11.4E %11.4E |\n",
797 }
798 plogf(" ");
799 writeline('-', 80);
800 plogf(" --- Finished Main Loop\n");
801 }
802
803 // LIMIT REDUCTION OF BASIS SPECIES TO 99%
804 //
805 // We have a tentative m_deltaMolNumSpecies[]. Now apply other criteria
806 // to limit its magnitude.
807 double par = 0.5;
808 size_t ll = 0;
809 for (size_t k = 0; k < m_numComponents; ++k) {
810 if (m_molNumSpecies_old[k] > 0.0) {
811 double xx = -m_deltaMolNumSpecies[k] / m_molNumSpecies_old[k];
812 if (par < xx) {
813 par = xx;
814 ll = k;
815 }
816 } else if (m_deltaMolNumSpecies[k] < 0.0) {
817 // If we are here, we then do a step which violates element
818 // conservation.
819 size_t iph = m_phaseID[k];
821 m_deltaMolNumSpecies[k] = 0.0;
822 }
823 }
824 par = 1.0 / par;
825 if (par <= 1.01 && par > 0.0) {
826 // Reduce the size of the step by the multiplicative factor, par
827 par *= 0.99;
828 if (m_debug_print_lvl >= 2) {
829 plogf(" --- Reduction in step size due to component %s going negative = %11.3E\n",
830 m_speciesName[ll], par);
831 }
832 for (size_t i = 0; i < m_nsp; ++i) {
833 m_deltaMolNumSpecies[i] *= par;
834 }
835 for (size_t iph = 0; iph < m_numPhases; iph++) {
836 m_deltaPhaseMoles[iph] *= par;
837 }
838 } else {
839 par = 1.0;
840 }
841 checkDelta1(&m_deltaMolNumSpecies[0],
843
844 // Now adjust the wt[kspec]'s so that the reflect the decrease in the
845 // overall length of m_deltaMolNumSpecies[kspec] just calculated. At the
846 // end of this section wt[], m_deltaMolNumSpecies[], tPhMoles, and
847 // tPhMoles1 should all be consistent with a new estimate of the state
848 // of the system.
849 for (size_t kspec = 0; kspec < m_nsp; ++kspec) {
851 if (m_molNumSpecies_new[kspec] < 0.0 && (m_speciesUnknownType[kspec]
853 throw CanteraError("VCS_SOLVE::solve_tp_inner",
854 "vcs_solve_TP: ERROR on step change wt[{}:{}]: {} < 0.0",
855 kspec, m_speciesName[kspec], m_molNumSpecies_new[kspec]);
856 }
857 }
858
859 // Calculate the tentative total mole numbers for each phase
860 for (size_t iph = 0; iph < m_numPhases; iph++) {
862 }
863
864 // Set the flags indicating the mole numbers in the vcs_VolPhase objects
865 // are out of date.
866 vcs_setFlagsVolPhases(false, VCS_STATECALC_NEW);
867
868 // Calculate the new chemical potentials using the tentative solution
869 // values. We only calculate a subset of these, because we have only
870 // updated a subset of the W().
872
873 // Evaluate DeltaG for all components if ITI=0, and for major components
874 // only if ITI NE 0
875 vcs_deltag(0, false, VCS_STATECALC_NEW);
876
877 // CONVERGENCE FORCER SECTION
878 if (printDetails) {
879 plogf(" --- Total Old Dimensionless Gibbs Free Energy = %20.13E\n",
881 &m_tPhaseMoles_old[0]));
882 plogf(" --- Total tentative Dimensionless Gibbs Free Energy = %20.13E\n",
884 &m_tPhaseMoles_new[0]));
885 }
886
887 bool forced = vcs_globStepDamp();
888
889 // Print out the changes to the solution that FORCER produced
890 if (printDetails && forced) {
891 plogf(" -----------------------------------------------------\n");
892 plogf(" --- FORCER SUBROUTINE changed the solution:\n");
893 plogf(" --- SPECIES Status INIT MOLES TENT_MOLES");
894 plogf(" FINAL KMOLES INIT_DEL_G/RT TENT_DEL_G/RT FINAL_DELTA_G/RT\n");
895 for (size_t i = 0; i < m_numComponents; ++i) {
896 plogf(" --- %-12.12s", m_speciesName[i]);
897 plogf(" %14.6E %14.6E %14.6E\n", m_molNumSpecies_old[i],
899 }
900 for (size_t kspec = m_numComponents; kspec < m_numSpeciesRdc; ++kspec) {
901 size_t irxn = kspec - m_numComponents;
902 plogf(" --- %-12.12s", m_speciesName[kspec]);
903 plogf(" %2d %14.6E%14.6E%14.6E%14.6E%14.6E%14.6E\n", m_speciesStatus[kspec],
904 m_molNumSpecies_old[kspec],
907 m_deltaGRxn_tmp[irxn], m_deltaGRxn_new[irxn]);
908 }
909 writeline(' ', 26, false);
910 plogf("Norms of Delta G():%14.6E%14.6E\n",
913 plogf(" Total kmoles of gas = %15.7E\n", m_tPhaseMoles_old[0]);
914 if ((m_numPhases > 1) && (!m_VolPhaseList[1]->m_singleSpecies)) {
915 plogf(" Total kmoles of liquid = %15.7E\n", m_tPhaseMoles_old[1]);
916 } else {
917 plogf(" Total kmoles of liquid = %15.7E\n", 0.0);
918 }
919 plogf(" Total New Dimensionless Gibbs Free Energy = %20.13E\n",
921 &m_tPhaseMoles_new[0]));
922 plogf(" -----------------------------------------------------\n");
923 }
924
925 }
926
927 // ITERATION SUMMARY PRINTOUT SECTION
928 if (printDetails) {
929 plogf(" ");
930 writeline('-', 103);
931 plogf(" --- Summary of the Update ");
932 if (iti == 0) {
933 plogf(" (all species):");
934 } else {
935 plogf(" (only major species):");
936 }
937 plogf("\n");
938 plogf(" --- Species Status Initial_KMoles Final_KMoles Initial_Mu/RT");
939 plogf(" Mu/RT Init_Del_G/RT Delta_G/RT\n");
940 for (size_t i = 0; i < m_numComponents; ++i) {
941 plogf(" --- %-12.12s", m_speciesName[i]);
942 plogf(" ");
943 plogf("%14.6E%14.6E%14.6E%14.6E\n", m_molNumSpecies_old[i],
945 }
946 for (size_t i = m_numComponents; i < m_numSpeciesRdc; ++i) {
947 size_t l1 = i - m_numComponents;
948 plogf(" --- %-12.12s", m_speciesName[i]);
949 plogf(" %2d %14.6E%14.6E%14.6E%14.6E%14.6E%14.6E\n",
953 }
954 for (size_t kspec = m_numSpeciesRdc; kspec < m_nsp; ++kspec) {
955 size_t l1 = kspec - m_numComponents;
956 plogf(" --- %-12.12s", m_speciesName[kspec]);
957 plogf(" %2d %14.6E%14.6E%14.6E%14.6E%14.6E%14.6E\n",
961 }
962 plogf(" ---");
963 writeline(' ', 56, false);
964 plogf("Norms of Delta G():%14.6E%14.6E\n",
967
968 plogf(" --- Phase_Name KMoles(after update)\n");
969 plogf(" --- ");
970 writeline('-', 50);
971 for (size_t iph = 0; iph < m_numPhases; iph++) {
972 vcs_VolPhase* Vphase = m_VolPhaseList[iph].get();
973 plogf(" --- %18s = %15.7E\n", Vphase->PhaseName, m_tPhaseMoles_new[iph]);
974 }
975 plogf(" ");
976 writeline('-', 103);
977 plogf(" --- Total Old Dimensionless Gibbs Free Energy = %20.13E\n",
979 &m_tPhaseMoles_old[0]));
980 plogf(" --- Total New Dimensionless Gibbs Free Energy = %20.13E\n",
982 &m_tPhaseMoles_new[0]));
983 debuglog(" --- Troublesome solve\n", m_VCount->Its > 550);
984 }
985
986 // RESET VALUES AT END OF ITERATION
987 // UPDATE MOLE NUMBERS
988 //
989 // If the solution wasn't changed in the forcer routine, then copy the
990 // tentative mole numbers and Phase moles into the actual mole numbers and
991 // phase moles. We will consider this current step to be completed.
992 //
993 // Accept the step. -> the tentative solution now becomes the real solution.
994 // If FORCED is true, then we have already done this inside the FORCED loop.
1001
1002 vcs_setFlagsVolPhases(true, VCS_STATECALC_OLD);
1003
1004 // Increment the iteration counters
1005 ++m_VCount->Its;
1006 ++it1;
1007 if (m_debug_print_lvl >= 2) {
1008 plogf(" --- Increment counter increased, step is accepted: %4d\n",
1009 m_VCount->Its);
1010 }
1011
1012 // HANDLE DELETION OF MULTISPECIES PHASES
1013 //
1014 // We delete multiphases, when the total moles in the multiphase is reduced
1015 // below a relative threshold. Set microscopic multispecies phases with
1016 // total relative number of moles less than VCS_DELETE_PHASE_CUTOFF to
1017 // absolute zero.
1018 bool justDeletedMultiPhase = false;
1019 for (size_t iph = 0; iph < m_numPhases; iph++) {
1020 if (!m_VolPhaseList[iph]->m_singleSpecies && m_tPhaseMoles_old[iph] != 0.0 &&
1022 if (m_debug_print_lvl >= 1) {
1023 plogf(" --- Setting microscopic phase %d to zero\n", iph);
1024 }
1025 justDeletedMultiPhase = true;
1027 }
1028 }
1029
1030 // If we have deleted a multispecies phase because the equilibrium moles
1031 // decreased, then we will update all the component basis calculation, and
1032 // therefore all of the thermo functions just to be safe.
1033 if (justDeletedMultiPhase) {
1034 double test = -1.0e-10;
1035 vcs_basopt(false, test);
1036 vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
1038 vcs_deltag(0, true, VCS_STATECALC_OLD);
1039 iti = 0;
1040 return;
1041 }
1042
1043 // CHECK FOR ELEMENT ABUNDANCE
1044 if (m_debug_print_lvl >= 2) {
1045 plogf(" --- Normal element abundance check");
1046 }
1047 vcs_elab();
1048 if (! vcs_elabcheck(0)) {
1049 debuglog(" - failed -> redoing element abundances.\n", m_debug_print_lvl >= 2);
1050 vcs_elcorr(&m_sm[0], &m_wx[0]);
1051 vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
1053 vcs_deltag(0, true, VCS_STATECALC_OLD);
1054 uptodate_minors = true;
1055 } else {
1056 debuglog(" - passed\n", m_debug_print_lvl >= 2);
1057 }
1058
1059 // CHECK FOR OPTIMUM BASIS
1060 for (size_t i = 0; i < m_numRxnRdc; ++i) {
1061 size_t k = m_indexRxnToSpecies[i];
1063 continue;
1064 }
1065 for (size_t j = 0; j < m_numComponents; ++j) {
1066 bool doSwap = false;
1067 if (m_SSPhase[j]) {
1068 doSwap = (m_molNumSpecies_old[k] * m_spSize[k]) >
1069 (m_molNumSpecies_old[j] * m_spSize[j] * 1.01);
1070 if (!m_SSPhase[k] && doSwap) {
1071 doSwap = m_molNumSpecies_old[k] > (m_molNumSpecies_old[j] * 1.01);
1072 }
1073 } else {
1074 if (m_SSPhase[k]) {
1075 doSwap = (m_molNumSpecies_old[k] * m_spSize[k]) >
1076 (m_molNumSpecies_old[j] * m_spSize[j] * 1.01);
1077 if (!doSwap) {
1078 doSwap = m_molNumSpecies_old[k] > (m_molNumSpecies_old[j] * 1.01);
1079 }
1080 } else {
1081 doSwap = (m_molNumSpecies_old[k] * m_spSize[k]) >
1082 (m_molNumSpecies_old[j] * m_spSize[j] * 1.01);
1083 }
1084 }
1085 if (doSwap && m_stoichCoeffRxnMatrix(j,i) != 0.0) {
1086 if (m_debug_print_lvl >= 2) {
1087 plogf(" --- Get a new basis because %s", m_speciesName[k]);
1088 plogf(" is better than comp %s", m_speciesName[j]);
1089 plogf(" and share nonzero stoic: %-9.1f\n",
1091 }
1092 forceComponentCalc = 1;
1093 return;
1094 }
1095 }
1096 }
1097 debuglog(" --- Check for an optimum basis passed\n", m_debug_print_lvl >= 2);
1098 stage = EQUILIB_CHECK;
1099
1100 // RE-EVALUATE MAJOR-MINOR VECTOR IF NECESSARY
1101 //
1102 // Skip this section if we haven't done a full calculation. Go right to the
1103 // check equilibrium section
1104 if (iti != 0) {
1105 return;
1106 }
1107 if (m_debug_print_lvl >= 2) {
1108 plogf(" --- Reevaluate major-minor status of noncomponents:\n");
1109 }
1111 for (size_t irxn = 0; irxn < m_numRxnRdc; irxn++) {
1112 size_t kspec = m_indexRxnToSpecies[irxn];
1113 int speciesType = vcs_species_type(kspec);
1114 if (speciesType < VCS_SPECIES_MINOR) {
1115 if (m_debug_print_lvl >= 2 && m_speciesStatus[kspec] >= VCS_SPECIES_MINOR) {
1116 plogf(" --- major/minor species is now zeroed out: %s\n",
1117 m_speciesName[kspec]);
1118 }
1120 } else if (speciesType == VCS_SPECIES_MINOR) {
1121 if (m_debug_print_lvl >= 2 && m_speciesStatus[kspec] != VCS_SPECIES_MINOR) {
1122 if (m_speciesStatus[kspec] == VCS_SPECIES_MAJOR) {
1123 plogf(" --- Noncomponent turned from major to minor: ");
1124 } else if (kspec < m_numComponents) {
1125 plogf(" --- Component turned into a minor species: ");
1126 } else {
1127 plogf(" --- Zeroed Species turned into a "
1128 "minor species: ");
1129 }
1130 plogf("%s\n", m_speciesName[kspec]);
1131 }
1133 } else if (speciesType == VCS_SPECIES_MAJOR) {
1134 if (m_speciesStatus[kspec] != VCS_SPECIES_MAJOR) {
1135 if (m_debug_print_lvl >= 2) {
1136 if (m_speciesStatus[kspec] == VCS_SPECIES_MINOR) {
1137 plogf(" --- Noncomponent turned from minor to major: ");
1138 } else if (kspec < m_numComponents) {
1139 plogf(" --- Component turned into a major: ");
1140 } else {
1141 plogf(" --- Noncomponent turned from zeroed to major: ");
1142 }
1143 plogf("%s\n", m_speciesName[kspec]);
1144 }
1146 }
1147 }
1148 m_speciesStatus[kspec] = speciesType;
1149 }
1150
1151 // This logical variable indicates whether all current non-component species
1152 // are minor or nonexistent
1153 allMinorZeroedSpecies = (m_numRxnMinorZeroed == m_numRxnRdc);
1154}
1155
1156void VCS_SOLVE::solve_tp_equilib_check(bool& allMinorZeroedSpecies,
1157 bool& uptodate_minors,
1158 bool& giveUpOnElemAbund, int& solveFail,
1159 size_t& iti, size_t& it1, int maxit,
1160 int& stage, bool& lec)
1161{
1162 if (! allMinorZeroedSpecies) {
1163 if (m_debug_print_lvl >= 2) {
1164 plogf(" --- Equilibrium check for major species: ");
1165 }
1166 for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
1167 size_t kspec = irxn + m_numComponents;
1168 if (m_speciesStatus[kspec] == VCS_SPECIES_MAJOR && (fabs(m_deltaGRxn_new[irxn]) > m_tolmaj)) {
1169 if (m_VCount->Its >= maxit) {
1170 solveFail = -1;
1171
1172 // Clean up and exit code even though we haven't converged.
1173 // -> we have run out of iterations!
1174 stage = RETURN_A;
1175 } else {
1176 if (m_debug_print_lvl >= 2) {
1177 plogf("%s failed\n", m_speciesName[m_indexRxnToSpecies[irxn]]);
1178 }
1179 // Convergence amongst major species has not been achieved.
1180 // Go back and do another iteration with variable ITI
1181 iti = ((it1/4) *4) - it1;
1182 stage = MAIN;
1183 }
1184 return;
1185 }
1186 }
1187 debuglog(" MAJOR SPECIES CONVERGENCE achieved", m_debug_print_lvl >= 2);
1188 } else {
1189 debuglog(" MAJOR SPECIES CONVERGENCE achieved "
1190 "(because there are no major species)\n", m_debug_print_lvl >= 2);
1191 }
1192 // Convergence amongst major species has been achieved
1193
1194 // EQUILIBRIUM CHECK FOR MINOR SPECIES
1195 if (m_numRxnMinorZeroed != 0) {
1196 // Calculate the chemical potential and reaction DeltaG for minor
1197 // species, if needed.
1198 if (iti != 0) {
1199 vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
1201 vcs_deltag(1, false, VCS_STATECALC_OLD);
1202 uptodate_minors = true;
1203 }
1204 if (m_debug_print_lvl >= 2) {
1205 plogf(" --- Equilibrium check for minor species: ");
1206 }
1207 for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
1208 size_t kspec = irxn + m_numComponents;
1209 if (m_speciesStatus[kspec] == VCS_SPECIES_MINOR && (fabs(m_deltaGRxn_new[irxn]) > m_tolmin)) {
1210 if (m_VCount->Its >= maxit) {
1211 solveFail = -1;
1212 // Clean up and exit code. -> Even though we have not
1213 // converged, we have run out of iterations !
1214 stage = RETURN_A;
1215 return;
1216 }
1217 if (m_debug_print_lvl >= 2) {
1218 plogf("%s failed\n", m_speciesName[m_indexRxnToSpecies[irxn]]);
1219 }
1220
1221 // Set iti to zero to force a full calculation, and go back to
1222 // the main loop to do another iteration.
1223 iti = 0;
1224 stage = MAIN;
1225 return;
1226 }
1227 }
1228 if (m_debug_print_lvl >= 2) {
1229 plogf(" CONVERGENCE achieved\n");
1230 }
1231 }
1232
1233 // FINAL ELEMENTAL ABUNDANCE CHECK
1234 // Recalculate the element abundance vector again
1236 vcs_elab();
1237
1238 // LEC is only true when we are near the end game
1239 if (lec) {
1240 if (!giveUpOnElemAbund) {
1241 if (m_debug_print_lvl >= 2) {
1242 plogf(" --- Check the Full Element Abundances: ");
1243 }
1244
1245 // Final element abundance check: If we fail then we need to go back
1246 // and correct the element abundances, and then go do a major step
1247 if (! vcs_elabcheck(1)) {
1248 if (m_debug_print_lvl >= 2) {
1249 if (! vcs_elabcheck(0)) {
1250 plogf(" failed\n");
1251 } else {
1252 plogf(" passed for NC but failed for NE: RANGE ERROR\n");
1253 }
1254 }
1255 // delete?
1256 stage = ELEM_ABUND_CHECK;
1257 return;
1258 }
1259 if (m_debug_print_lvl >= 2) {
1260 plogf(" passed\n");
1261 }
1262 }
1263
1264 // If we have deleted a species then we need to recheck the the deleted
1265 // species, before exiting
1266 if (m_numSpeciesRdc != m_nsp) {
1267 stage = RECHECK_DELETED;
1268 return;
1269 }
1270 // Final checks are passed -> go check out
1271 stage = RETURN_A;
1272 }
1273 lec = true;
1274}
1275
1276void VCS_SOLVE::solve_tp_elem_abund_check(size_t& iti, int& stage, bool& lec,
1277 bool& giveUpOnElemAbund,
1278 int& finalElemAbundAttempts,
1279 int& rangeErrorFound)
1280{
1281
1282 // HKM - Put in an element abundance check. The element abundances were
1283 // being corrected even if they were perfectly OK to start with. This is
1284 // actually an expensive operation, so I took it out. Also vcs_dfe() doesn't
1285 // need to be called if no changes were made.
1286 rangeErrorFound = 0;
1287 if (! vcs_elabcheck(1)) {
1288 bool ncBefore = vcs_elabcheck(0);
1289 vcs_elcorr(&m_sm[0], &m_wx[0]);
1290 bool ncAfter = vcs_elabcheck(0);
1291 bool neAfter = vcs_elabcheck(1);
1292
1293 // Go back to evaluate the total moles of gas and liquid.
1294 vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
1296 vcs_deltag(0, false, VCS_STATECALC_OLD);
1297 if (!ncBefore) {
1298 if (ncAfter) {
1299 // We have breathed new life into the old problem. Now the
1300 // element abundances up to NC agree. Go back and restart the
1301 // main loop calculation, resetting the end conditions.
1302 lec = false;
1303 iti = 0;
1304 stage = MAIN;
1305 } else {
1306 // We are still hosed
1307 if (finalElemAbundAttempts >= 3) {
1308 giveUpOnElemAbund = true;
1309 stage = EQUILIB_CHECK;
1310 } else {
1311 finalElemAbundAttempts++;
1312 lec = false;
1313 iti = 0;
1314 stage = MAIN;
1315 }
1316 }
1317 return;
1318 } else if (ncAfter) {
1319 if (!neAfter) {
1320 // Probably an unrecoverable range error
1321 if (m_debug_print_lvl >= 2) {
1322 plogf(" --- vcs_solve_tp: RANGE SPACE ERROR ENCOUNTERED\n");
1323 plogf(" --- vcs_solve_tp: - Giving up on NE Element Abundance satisfaction \n");
1324 plogf(" --- vcs_solve_tp: - However, NC Element Abundance criteria is satisfied \n");
1325 plogf(" --- vcs_solve_tp: - Returning the calculated equilibrium condition\n");
1326 }
1327 rangeErrorFound = 1;
1328 giveUpOnElemAbund = true;
1329 }
1330
1331 // Recovery of end element abundances -> go do equilibrium check
1332 // again and then check out.
1333 stage = EQUILIB_CHECK;
1334 return;
1335 }
1336 }
1337 // Calculate delta g's
1338 vcs_deltag(0, false, VCS_STATECALC_OLD);
1339 // Go back to equilibrium check as a prep to eventually checking out
1340 stage = EQUILIB_CHECK;
1341}
1342
1343double VCS_SOLVE::vcs_minor_alt_calc(size_t kspec, size_t irxn, bool* do_delete) const
1344{
1345 double w_kspec = m_molNumSpecies_old[kspec];
1346 double dg_irxn = m_deltaGRxn_old[irxn];
1347 size_t iph = m_phaseID[kspec];
1348
1349 *do_delete = false;
1351 if (w_kspec <= 0.0) {
1353 }
1354 dg_irxn = std::max(dg_irxn, -200.0);
1355 if (dg_irxn >= 23.0) {
1356 double molNum_kspec_new = w_kspec * 1.0e-10;
1357 if (w_kspec < VCS_DELETE_MINORSPECIES_CUTOFF) {
1358 // delete the species from the current list of active species,
1359 // because its concentration has gotten too small.
1360 *do_delete = true;
1361 return - w_kspec;
1362 }
1363 return molNum_kspec_new - w_kspec;
1364 } else {
1365 if (fabs(dg_irxn) <= m_tolmin2) {
1366 return 0.0;
1367 }
1368 }
1369
1370 // get the diagonal of the activity coefficient Jacobian
1371 double s = m_np_dLnActCoeffdMolNum(kspec,kspec) / m_tPhaseMoles_old[iph];
1372
1373 // We fit it to a power law approximation of the activity coefficient
1374 //
1375 // gamma = gamma_0 * ( x / x0)**a
1376 //
1377 // where a is forced to be a little bit greater than -1.
1378 // We do this so that the resulting expression is always nonnegative
1379 // We then solve the resulting calculation:
1380 //
1381 // gamma * x = gamma_0 * x0 exp (-deltaG/RT);
1382 double a = clip(w_kspec * s, -1.0+1e-8, 100.0);
1383 double tmp = clip(-dg_irxn / (1.0 + a), -200.0, 200.0);
1384 double wTrial = w_kspec * exp(tmp);
1385 double molNum_kspec_new = wTrial;
1386
1387 if (wTrial > 100. * w_kspec) {
1388 double molNumMax = 0.0001 * m_tPhaseMoles_old[iph];
1389 if (molNumMax < 100. * w_kspec) {
1390 molNumMax = 100. * w_kspec;
1391 }
1392 if (wTrial > molNumMax) {
1393 molNum_kspec_new = molNumMax;
1394 } else {
1395 molNum_kspec_new = wTrial;
1396 }
1397 } else if (1.0E10 * wTrial < w_kspec) {
1398 molNum_kspec_new= 1.0E-10 * w_kspec;
1399 } else {
1400 molNum_kspec_new = wTrial;
1401 }
1402
1403 if ((molNum_kspec_new) < VCS_DELETE_MINORSPECIES_CUTOFF) {
1404 // delete the species from the current list of active species,
1405 // because its concentration has gotten too small.
1406 *do_delete = true;
1407 return - w_kspec;
1408 }
1409 return molNum_kspec_new - w_kspec;
1410
1411 } else {
1412 // Voltage calculation
1413 // Need to check the sign -> This is good for electrons
1414 double dx = m_deltaGRxn_old[irxn]/ m_Faraday_dim;
1415 return dx;
1416 }
1417}
1418
1419int VCS_SOLVE::delta_species(const size_t kspec, double* const delta_ptr)
1420{
1421 size_t irxn = kspec - m_numComponents;
1422 int retn = 1;
1423 AssertThrowMsg(kspec >= m_numComponents, "VCS_SOLVE::delta_species",
1424 "delete_species() ERROR: called for a component {}", kspec);
1426 // Attempt the given dx. If it doesn't work, try to see if a smaller one
1427 // would work,
1428 double dx = *delta_ptr;
1429 double* sc_irxn = m_stoichCoeffRxnMatrix.ptrColumn(irxn);
1430 for (size_t j = 0; j < m_numComponents; ++j) {
1431 if (m_molNumSpecies_old[j] > 0.0) {
1432 double tmp = sc_irxn[j] * dx;
1433 if (-tmp > m_molNumSpecies_old[j]) {
1434 retn = 0;
1435 dx = std::min(dx, - m_molNumSpecies_old[j] / sc_irxn[j]);
1436 }
1437 }
1438
1439 // If the component has a zero concentration and is a reactant
1440 // in the formation reaction, then dx == 0.0, and we just return.
1441 if (m_molNumSpecies_old[j] <= 0.0 && sc_irxn[j] < 0.0) {
1442 *delta_ptr = 0.0;
1443 return 0;
1444 }
1445 }
1446
1447 // ok, we found a positive dx. implement it.
1448 *delta_ptr = dx;
1449 m_molNumSpecies_old[kspec] += dx;
1450 size_t iph = m_phaseID[kspec];
1451 m_tPhaseMoles_old[iph] += dx;
1452 m_VolPhaseList[iph]->setMolesOutOfDate(VCS_STATECALC_OLD);
1453
1454 for (size_t j = 0; j < m_numComponents; ++j) {
1455 double tmp = sc_irxn[j] * dx;
1456 if (tmp != 0.0) {
1457 iph = m_phaseID[j];
1458 m_molNumSpecies_old[j] += tmp;
1459 m_tPhaseMoles_old[iph] += tmp;
1460 m_VolPhaseList[iph]->setMolesOutOfDate(VCS_STATECALC_OLD);
1461 m_molNumSpecies_old[j] = std::max(m_molNumSpecies_old[j], 0.0);
1462 }
1463 }
1464 }
1465 return retn;
1466}
1467
1468int VCS_SOLVE::vcs_zero_species(const size_t kspec)
1469{
1470 int retn = 1;
1471
1472 // Calculate a delta that will eliminate the species.
1474 double dx = -m_molNumSpecies_old[kspec];
1475 if (dx != 0.0) {
1476 retn = delta_species(kspec, &dx);
1477 if (!retn && m_debug_print_lvl >= 1) {
1478 plogf("vcs_zero_species: Couldn't zero the species %d, "
1479 "did delta of %g. orig conc of %g\n",
1480 kspec, dx, m_molNumSpecies_old[kspec] + dx);
1481 }
1482 }
1483 }
1484 return retn;
1485}
1486
1487int VCS_SOLVE::vcs_delete_species(const size_t kspec)
1488{
1489 const size_t klast = m_numSpeciesRdc - 1;
1490 const size_t iph = m_phaseID[kspec];
1491 vcs_VolPhase* const Vphase = m_VolPhaseList[iph].get();
1492 const size_t irxn = kspec - m_numComponents;
1493
1494 // Zero the concentration of the species.
1495 // -> This zeroes w[kspec] and modifies m_tPhaseMoles_old[]
1496 const int retn = vcs_zero_species(kspec);
1497 if (!retn) {
1498 throw CanteraError("VCS_SOLVE::vcs_delete_species",
1499 "Failed to delete a species!");
1500 }
1501
1502 // Decrement the minor species counter if the current species is a minor
1503 // species
1504 if (m_speciesStatus[kspec] != VCS_SPECIES_MAJOR) {
1506 }
1508 m_deltaGRxn_new[irxn] = 0.0;
1509 m_deltaGRxn_old[irxn] = 0.0;
1510 m_feSpecies_new[kspec] = 0.0;
1511 m_feSpecies_old[kspec] = 0.0;
1513
1514 // Rearrange the data if the current species isn't the last active species.
1515 if (kspec != klast) {
1516 vcs_switch_pos(true, klast, kspec);
1517 }
1518
1519 // Adjust the total moles in a phase downwards.
1521 &m_tPhaseMoles_old[0]);
1522
1523 // Adjust the current number of active species and reactions counters
1524 --m_numRxnRdc;
1526
1527 // Check to see whether we have just annihilated a multispecies phase. If it
1528 // is extinct, call the delete_multiphase() function.
1529 if (! m_SSPhase[klast] && Vphase->exists() != VCS_PHASE_EXIST_ALWAYS) {
1530 bool stillExists = false;
1531 for (size_t k = 0; k < m_numSpeciesRdc; k++) {
1533 m_phaseID[k] == iph && m_molNumSpecies_old[k] > 0.0) {
1534 stillExists = true;
1535 break;
1536 }
1537 }
1538 if (!stillExists) {
1540 }
1541 }
1542
1543 // When the total number of noncomponent species is zero, we have to signal
1544 // the calling code
1545 return (m_numRxnRdc == 0);
1546}
1547
1549{
1550 size_t iph = m_phaseID[kspec];
1551 if (m_debug_print_lvl >= 2) {
1552 plogf(" --- Add back a deleted species: %-12s\n", m_speciesName[kspec]);
1553 }
1554
1555 // Set the species back to minor species status
1556 // this adjusts m_molNumSpecies_old[] and m_tPhaseMoles_old[]
1557 // HKM -> make this a relative mole number!
1558 double dx = m_tPhaseMoles_old[iph] * VCS_RELDELETE_SPECIES_CUTOFF * 10.;
1559 delta_species(kspec, &dx);
1561
1562 if (m_SSPhase[kspec]) {
1565 }
1566
1567 vcs_VolPhase* Vphase = m_VolPhaseList[iph].get();
1570 &m_tPhaseMoles_old[0]);
1571
1572 // We may have popped a multispecies phase back into existence. If we did,
1573 // we have to check the other species in that phase. Take care of the
1574 // m_speciesStatus[] flag. The value of m_speciesStatus[] must change from
1575 // VCS_SPECIES_ZEROEDPHASE to VCS_SPECIES_ZEROEDMS for those other species.
1576 if (! m_SSPhase[kspec]) {
1577 if (Vphase->exists() == VCS_PHASE_EXIST_NO) {
1579 for (size_t k = 0; k < m_nsp; k++) {
1580 if (m_phaseID[k] == iph && m_speciesStatus[k] != VCS_SPECIES_DELETED) {
1582 }
1583 }
1584 }
1585 } else {
1587 }
1588
1589 ++m_numRxnRdc;
1592
1593 if (kspec != (m_numSpeciesRdc - 1)) {
1594 // Rearrange both the species and the non-component global data
1595 vcs_switch_pos(true, (m_numSpeciesRdc - 1), kspec);
1596 }
1597}
1598
1600{
1601 vcs_VolPhase* Vphase = m_VolPhaseList[iph].get();
1602 bool successful = true;
1603
1604 // set the phase existence flag to dead
1605 Vphase->setTotalMoles(0.0);
1606 if (m_debug_print_lvl >= 2) {
1607 plogf(" --- delete_multiphase %d, %s\n", iph, Vphase->PhaseName);
1608 }
1609
1610 // Loop over all of the species in the phase.
1611 for (size_t kspec = m_numComponents; kspec < m_numSpeciesRdc; ++kspec) {
1612 if (m_phaseID[kspec] == iph) {
1614 // calculate an extent of rxn, dx, that zeroes out the species.
1615 double dx = - m_molNumSpecies_old[kspec];
1616 double dxTent = dx;
1617
1618 int retn = delta_species(kspec, &dxTent);
1619 if (retn != 1) {
1620 successful = false;
1621 if (m_debug_print_lvl >= 2) {
1622 plogf(" --- delete_multiphase %d, %s ERROR problems deleting species %s\n",
1623 iph, Vphase->PhaseName, m_speciesName[kspec]);
1624 plogf(" --- delta attempted: %g achieved: %g "
1625 " Zeroing it manually\n", dx, dxTent);
1626 }
1627 m_molNumSpecies_old[kspec] = 0.0;
1628 m_molNumSpecies_new[kspec] = 0.0;
1629 m_deltaMolNumSpecies[kspec] = 0.0;
1630 // recover the total phase moles.
1631 vcs_tmoles();
1632 } else {
1633 // Set the mole number of that species to zero.
1634 m_molNumSpecies_old[kspec] = 0.0;
1635 m_molNumSpecies_new[kspec] = 0.0;
1636 m_deltaMolNumSpecies[kspec] = 0.0;
1637 }
1638
1639 // Change the status flag of the species to that of an zeroed
1640 // phase
1642 }
1643 }
1644 }
1645
1646 double dxPerm = 0.0, dxPerm2 = 0.0;
1647 for (size_t kcomp = 0; kcomp < m_numComponents; ++kcomp) {
1648 if (m_phaseID[kcomp] == iph) {
1649 if (m_debug_print_lvl >= 2) {
1650 plogf(" --- delete_multiphase One of the species is a component %d - %s with mole number %g\n",
1651 kcomp, m_speciesName[kcomp], m_molNumSpecies_old[kcomp]);
1652 }
1653 if (m_molNumSpecies_old[kcomp] != 0.0) {
1654 for (size_t kspec = m_numComponents; kspec < m_numSpeciesRdc; ++kspec) {
1655 size_t irxn = kspec - m_numComponents;
1656 if (m_phaseID[kspec] != iph) {
1657 if (m_stoichCoeffRxnMatrix(kcomp,irxn) != 0.0) {
1658 double dxWant = -m_molNumSpecies_old[kcomp] / m_stoichCoeffRxnMatrix(kcomp,irxn);
1659 if (dxWant + m_molNumSpecies_old[kspec] < 0.0) {
1660 dxPerm = -m_molNumSpecies_old[kspec];
1661 }
1662 for (size_t jcomp = 0; kcomp < m_numComponents; ++kcomp) {
1663 if (jcomp != kcomp) {
1664 if (m_phaseID[jcomp] == iph) {
1665 dxPerm = 0.0;
1666 } else {
1667 double dj = dxWant * m_stoichCoeffRxnMatrix(jcomp,irxn);
1668 if (dj + m_molNumSpecies_old[kcomp] < 0.0) {
1669 dxPerm2 = -m_molNumSpecies_old[kcomp] / m_stoichCoeffRxnMatrix(jcomp,irxn);
1670 }
1671 if (fabs(dxPerm2) < fabs(dxPerm)) {
1672 dxPerm = dxPerm2;
1673 }
1674 }
1675 }
1676 }
1677 }
1678 if (dxPerm != 0.0) {
1679 delta_species(kspec, &dxPerm);
1680 }
1681 }
1682 }
1683
1684 }
1685 if (m_molNumSpecies_old[kcomp] != 0.0) {
1686 if (m_debug_print_lvl >= 2) {
1687 plogf(" --- delete_multiphase One of the species is a component %d - %s still with mole number %g\n",
1688 kcomp, m_speciesName[kcomp], m_molNumSpecies_old[kcomp]);
1689 plogf(" --- zeroing it \n");
1690 }
1691 m_molNumSpecies_old[kcomp] = 0.0;
1692 }
1694 }
1695 }
1696
1697 // Loop over all of the inactive species in the phase: Right now we
1698 // reinstate all species in a deleted multiphase. We may only want to
1699 // reinstate the "major ones" in the future. Note, species in phases with
1700 // zero mole numbers are still considered active. Whether the phase pops
1701 // back into existence or not is checked as part of the main iteration loop.
1702 for (size_t kspec = m_numSpeciesRdc; kspec < m_nsp; ++kspec) {
1703 if (m_phaseID[kspec] == iph) {
1704 m_molNumSpecies_old[kspec] = 0.0;
1705 m_molNumSpecies_new[kspec] = 0.0;
1706 m_deltaMolNumSpecies[kspec] = 0.0;
1708
1709 ++m_numRxnRdc;
1711 if (m_debug_print_lvl >= 2) {
1712 plogf(" --- Make %s", m_speciesName[kspec]);
1713 plogf(" an active but zeroed species because its phase "
1714 "was zeroed\n");
1715 }
1716 if (kspec != (m_numSpeciesRdc - 1)) {
1717 // Rearrange both the species and the non-component global data
1718 vcs_switch_pos(true, (m_numSpeciesRdc - 1), kspec);
1719 }
1720 }
1721 }
1722
1723 // Zero out the total moles counters for the phase
1724 m_tPhaseMoles_old[iph] = 0.0;
1725 m_tPhaseMoles_new[iph] = 0.0;
1726 m_deltaPhaseMoles[iph] = 0.0;
1727
1728 // Upload the state to the VP object
1729 Vphase->setTotalMoles(0.0);
1730 return successful;
1731}
1732
1734{
1735 vector<double>& xtcutoff = m_TmpPhase;
1736 if (m_debug_print_lvl >= 2) {
1737 plogf(" --- Start rechecking deleted species in multispec phases\n");
1738 }
1739 if (m_numSpeciesRdc == m_nsp) {
1740 return 0;
1741 }
1742
1743 // Use the standard chemical potentials for the chemical potentials of
1744 // deleted species. Then, calculate Delta G for for formation reactions.
1745 // Note: fe[] here includes everything except for the ln(x[i]) term
1746 for (size_t kspec = m_numSpeciesRdc; kspec < m_nsp; ++kspec) {
1747 size_t iph = m_phaseID[kspec];
1748 m_feSpecies_new[kspec] = (m_SSfeSpecies[kspec] + log(m_actCoeffSpecies_old[kspec])
1749 - m_lnMnaughtSpecies[kspec]
1750 + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iph]);
1751 }
1752
1753 // Recalculate the DeltaG's of the formation reactions for the deleted
1754 // species in the mechanism
1755 vcs_deltag(0, true, VCS_STATECALC_NEW);
1756
1757 for (size_t iph = 0; iph < m_numPhases; iph++) {
1758 if (m_tPhaseMoles_old[iph] > 0.0) {
1759 xtcutoff[iph] = log(1.0 / VCS_RELDELETE_SPECIES_CUTOFF);
1760 } else {
1761 xtcutoff[iph] = 0.0;
1762 }
1763 }
1764
1765 // We are checking the equation:
1766 //
1767 // sum_u = sum_j_comp [ sigma_i_j * u_j ]
1768 // = u_i_O + log((AC_i * W_i)/m_tPhaseMoles_old)
1769 //
1770 // by first evaluating:
1771 //
1772 // DG_i_O = u_i_O - sum_u.
1773 //
1774 // Then, if TL is zero, the phase pops into existence if DG_i_O < 0. Also,
1775 // if the phase exists, then we check to see if the species can have a mole
1776 // number larger than VCS_DELETE_SPECIES_CUTOFF (default value = 1.0E-32).
1777 //
1778 // HKM: This seems to be an inconsistency in the algorithm here that needs
1779 // correcting. The requirement above may bypass some multiphases which
1780 // should exist. The real requirement for the phase to exist is:
1781 //
1782 // sum_i_in_phase [ exp(-DG_i_O) ] >= 1.0
1783 //
1784 // Thus, we need to amend the code. Also nonideal solutions will tend to
1785 // complicate matters severely also.
1786 int npb = 0;
1787 for (size_t irxn = m_numRxnRdc; irxn < m_numRxnTot; ++irxn) {
1788 size_t kspec = m_indexRxnToSpecies[irxn];
1789 size_t iph = m_phaseID[kspec];
1790 if (m_tPhaseMoles_old[iph] == 0.0) {
1791 if (m_deltaGRxn_new[irxn] < 0.0) {
1792 vcs_reinsert_deleted(kspec);
1793 npb++;
1794 } else {
1795 m_molNumSpecies_old[kspec] = 0.0;
1796 }
1797 } else if (m_tPhaseMoles_old[iph] > 0.0) {
1798 if (m_deltaGRxn_new[irxn] < xtcutoff[iph]) {
1799 vcs_reinsert_deleted(kspec);
1800 npb++;
1801 }
1802 }
1803 }
1804 return npb;
1805}
1806
1808{
1809 if (m_numSpeciesRdc == m_nsp) {
1810 return 0;
1811 }
1812
1813 // Use the standard chemical potentials for the chemical potentials of
1814 // deleted species. Then, calculate Delta G for for formation reactions. We
1815 // are relying here on a old saved value of m_actCoeffSpecies_old[kspec]
1816 // being sufficiently good. Note, we will recalculate everything at the end
1817 // of the routine.
1819 for (int cits = 0; cits < 3; cits++) {
1820 for (size_t kspec = m_numSpeciesRdc; kspec < m_nsp; ++kspec) {
1821 size_t iph = m_phaseID[kspec];
1822 vcs_VolPhase* Vphase = m_VolPhaseList[iph].get();
1823 if (m_molNumSpecies_new[kspec] == 0.0) {
1825 }
1826 if (!Vphase->m_singleSpecies) {
1828 }
1829 m_feSpecies_new[kspec] = (m_SSfeSpecies[kspec] + log(m_actCoeffSpecies_new[kspec]) - m_lnMnaughtSpecies[kspec]
1830 + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iph]);
1831 }
1832
1833 // Recalculate the DeltaG's of the formation reactions for the deleted
1834 // species in the mechanism
1835 vcs_deltag(0, true, VCS_STATECALC_NEW);
1836 for (size_t irxn = m_numRxnRdc; irxn < m_numRxnTot; ++irxn) {
1837 size_t kspec = m_indexRxnToSpecies[irxn];
1838 size_t iph = m_phaseID[kspec];
1839 if (m_tPhaseMoles_old[iph] > 0.0) {
1840 double maxDG = std::min(m_deltaGRxn_new[irxn], 690.0);
1841 double dx = m_tPhaseMoles_old[iph] * exp(- maxDG);
1842 m_molNumSpecies_new[kspec] = dx;
1843 }
1844 }
1845 }
1846 for (size_t irxn = m_numRxnRdc; irxn < m_numRxnTot; ++irxn) {
1847 size_t kspec = m_indexRxnToSpecies[irxn];
1848 size_t iph = m_phaseID[kspec];
1849 if (m_tPhaseMoles_old[iph] > 0.0) {
1850 double dx = m_molNumSpecies_new[kspec];
1851 size_t retn = delta_species(kspec, &dx);
1852 if (retn == 0) {
1853 if (m_debug_print_lvl) {
1854 plogf(" --- add_deleted(): delta_species() failed for species %s (%d) with mol number %g\n",
1855 m_speciesName[kspec], kspec, dx);
1856 }
1857 if (dx > 1.0E-50) {
1858 dx = 1.0E-50;
1859 retn = delta_species(kspec, &dx);
1860 if (retn == 0 && m_debug_print_lvl) {
1861 plogf(" --- add_deleted(): delta_species() failed for species %s (%d) with mol number %g\n",
1862 m_speciesName[kspec], kspec, dx);
1863 }
1864 }
1865 }
1866 if (m_debug_print_lvl >= 2) {
1867 if (retn != 0) {
1868 plogf(" --- add_deleted(): species %s added back in with mol number %g\n",
1869 m_speciesName[kspec], dx);
1870 } else {
1871 plogf(" --- add_deleted(): species %s failed to be added back in\n");
1872 }
1873 }
1874 }
1875 }
1876 vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
1878 vcs_deltag(0, true, VCS_STATECALC_OLD);
1879
1880 size_t retn = 0;
1881 for (size_t irxn = m_numRxnRdc; irxn < m_numRxnTot; ++irxn) {
1882 size_t kspec = m_indexRxnToSpecies[irxn];
1883 size_t iph = m_phaseID[kspec];
1884 if (m_tPhaseMoles_old[iph] > 0.0 && fabs(m_deltaGRxn_old[irxn]) > m_tolmin) {
1885 if (((m_molNumSpecies_old[kspec] * exp(-m_deltaGRxn_old[irxn])) >
1888 retn++;
1889 if (m_debug_print_lvl >= 2) {
1890 plogf(" --- add_deleted(): species %s with mol number %g not converged: DG = %g\n",
1891 m_speciesName[kspec], m_molNumSpecies_old[kspec],
1892 m_deltaGRxn_old[irxn]);
1893 }
1894 }
1895 }
1896 }
1897 return retn;
1898}
1899
1901{
1902 // CALCULATE SLOPE AT END OF THE STEP
1903 double s2 = 0.0;
1904 for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
1905 size_t kspec = irxn + m_numComponents;
1907 s2 += m_deltaGRxn_new[irxn] * m_deltaMolNumSpecies[kspec];
1908 }
1909 }
1910
1911 // CALCULATE ORIGINAL SLOPE
1912 double s1 = 0.0;
1913 for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
1914 size_t kspec = irxn + m_numComponents;
1916 s1 += m_deltaGRxn_old[irxn] * m_deltaMolNumSpecies[kspec];
1917 }
1918 }
1919
1920 if (m_debug_print_lvl >= 2) {
1921 plogf(" --- subroutine FORCE: Beginning Slope = %g\n", s1);
1922 plogf(" --- subroutine FORCE: End Slope = %g\n", s2);
1923 }
1924
1925 if (s1 > 0.0 || s2 <= 0.0) {
1926 debuglog(" --- subroutine FORCE produced no adjustments\n", m_debug_print_lvl >= 2);
1927 return false;
1928 }
1929
1930 // FIT PARABOLA
1931 double al = 1.0;
1932 if (fabs(s1 -s2) > 1.0E-200) {
1933 al = s1 / (s1 - s2);
1934 }
1935 if (al >= 0.95 || al < 0.0) {
1936 debuglog(" --- subroutine FORCE produced no adjustments\n", m_debug_print_lvl >= 2);
1937 return false;
1938 }
1939 if (m_debug_print_lvl >= 2) {
1940 plogf(" --- subroutine FORCE produced a damping factor = %g\n", al);
1941 }
1942
1943 // ADJUST MOLE NUMBERS, CHEM. POT
1944 if (m_debug_print_lvl >= 2) {
1946 }
1947
1948 for (size_t kspec = 0; kspec < m_numSpeciesRdc; ++kspec) {
1950 al * m_deltaMolNumSpecies[kspec];
1951 }
1952 for (size_t iph = 0; iph < m_numPhases; iph++) {
1954 }
1956
1957 if (m_debug_print_lvl >= 2) {
1958 plogf(" --- subroutine FORCE adjusted the mole "
1959 "numbers, AL = %10.3f\n", al);
1960 }
1961
1962 // Because we changed the mole numbers, we need to calculate the chemical
1963 // potentials again. If a major-only step is being carried out, then we
1964 // don't need to update the minor noncomponents.
1965 vcs_setFlagsVolPhases(false, VCS_STATECALC_NEW);
1967
1968 // Evaluate DeltaG for all components if ITI=0, and for major components
1969 // only if ITI NE 0
1970 vcs_deltag(0, false, VCS_STATECALC_NEW);
1971
1972 s2 = 0.0;
1973 for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
1974 size_t kspec = irxn + m_numComponents;
1976 s2 += m_deltaGRxn_new[irxn] * m_deltaMolNumSpecies[kspec];
1977 }
1978 }
1979
1980 if (m_debug_print_lvl >= 2) {
1981 plogf(" --- subroutine FORCE: Adj End Slope = %g\n", s2);
1982 }
1983 return true;
1984}
1985
1986void VCS_SOLVE::vcs_basopt(const bool doJustComponents, double test)
1987{
1988 size_t k;
1989 size_t juse = npos;
1990 size_t jlose = npos;
1991 DenseMatrix C;
1992 if (m_debug_print_lvl >= 2) {
1993 plogf(" ");
1994 for (size_t i=0; i<77; i++) {
1995 plogf("-");
1996 }
1997 plogf("\n");
1998 plogf(" --- Subroutine BASOPT called to ");
1999 if (doJustComponents) {
2000 plogf("calculate the number of components\n");
2001 } else {
2002 plogf("reevaluate the components\n");
2003 }
2004 if (m_debug_print_lvl >= 2) {
2005 plogf("\n");
2006 plogf(" --- Formula Matrix used in BASOPT calculation\n");
2007 plogf(" --- Active | ");
2008 for (size_t j = 0; j < m_nelem; j++) {
2009 plogf(" %1d ", m_elementActive[j]);
2010 }
2011 plogf("\n");
2012 plogf(" --- Species | ");
2013 for (size_t j = 0; j < m_nelem; j++) {
2014 writelog(" {:>8.8s}", m_elementName[j]);
2015 }
2016 plogf("\n");
2017 for (k = 0; k < m_nsp; k++) {
2018 writelog(" --- {:>11.11s} | ", m_speciesName[k]);
2019 for (size_t j = 0; j < m_nelem; j++) {
2020 plogf(" %8.2g", m_formulaMatrix(k,j));
2021 }
2022 plogf("\n");
2023 }
2024 writelogendl();
2025 }
2026 }
2027
2028 // Calculate the maximum value of the number of components possible. It's
2029 // equal to the minimum of the number of elements and the number of total
2030 // species.
2031 size_t ncTrial = std::min(m_nelem, m_nsp);
2032 m_numComponents = ncTrial;
2033 vector<int> ipiv(ncTrial);
2034
2035 // Use a temporary work array for the mole numbers, aw[]
2036 std::copy(m_molNumSpecies_old.begin(),
2037 m_molNumSpecies_old.begin() + m_nsp, m_aw.begin());
2038
2039 // Take out the Voltage unknowns from consideration
2040 for (k = 0; k < m_nsp; k++) {
2042 m_aw[k] = test;
2043 }
2044 }
2045
2046 size_t jr = 0;
2047
2048 // Top of a loop of some sort based on the index JR. JR is the current
2049 // number of component species found.
2050 while (jr < ncTrial) {
2051 bool exhausted = false;
2052 // Top of another loop point based on finding a linearly independent
2053 // species
2054 while (true) {
2055 // Search the remaining part of the mole fraction vector, AW, for
2056 // the largest remaining species. Return its identity in K. The
2057 // first search criteria is always the largest positive magnitude of
2058 // the mole number.
2059 if (m_spSize[jr] <= 0.0) {
2060 throw CanteraError("VCS_SOLVE::vcs_basopt", "spSize is nonpositive");
2061 }
2062 k = jr;
2063 // The factors of 1.01 and 1.001 are placed in this routine for a purpose.
2064 // The purpose is to ensure that roundoff errors don't influence major
2065 // decisions. This means that the optimized and non-optimized versions of
2066 // the code remain close to each other.
2067 double big = m_aw[jr] * m_spSize[jr] * 1.01;
2068 for (size_t i = jr + 1; i < m_nsp; ++i) {
2069 if (m_spSize[i] <= 0.0) {
2070 throw CanteraError("VCS_SOLVE::vcs_basopt", "spSize is nonpositive");
2071 }
2072 bool doSwap = false;
2073 if (m_SSPhase[jr]) {
2074 doSwap = (m_aw[i] * m_spSize[i]) > big;
2075 if (!m_SSPhase[i] && doSwap) {
2076 doSwap = m_aw[i] > (m_aw[k] * 1.001);
2077 }
2078 } else {
2079 if (m_SSPhase[i]) {
2080 doSwap = (m_aw[i] * m_spSize[i]) > big;
2081 if (!doSwap) {
2082 doSwap = m_aw[i] > (m_aw[k] * 1.001);
2083 }
2084 } else {
2085 doSwap = (m_aw[i] * m_spSize[i]) > big;
2086 }
2087 }
2088 if (doSwap) {
2089 k = i;
2090 big = m_aw[i] * m_spSize[i] * 1.01;
2091 }
2092 }
2093
2094 // The fun really starts when you have run out of species that have
2095 // a significant concentration. It becomes extremely important to
2096 // make a good choice of which species you want to pick to fill out
2097 // the basis. Basically, you don't want to use species with elements
2098 // abundances which aren't pegged to zero. This means that those
2099 // modes will never be allowed to grow. You want to have the best
2100 // chance that the component will grow positively.
2101 //
2102 // Suppose you start with CH4, N2, as the only species with nonzero
2103 // compositions. You have the following abundances:
2104 //
2105 // Abundances:
2106 // ----------------
2107 // C 2.0
2108 // N 2.0
2109 // H 4.0
2110 // O 0.0
2111 //
2112 // For example, Make the following choice:
2113 //
2114 // CH4 N2 O choose -> OH
2115 // or
2116 // CH4 N2 O choose -> H2
2117 //
2118 // OH and H2 both fill out the basis. They will pass the algorithm.
2119 // However, choosing OH as the next species will create a situation
2120 // where H2 can not grow in concentration. This happened in
2121 // practice, btw. The reason is that the formation reaction for H2
2122 // will cause one of the component species to go negative.
2123 //
2124 // The basic idea here is to pick a simple species whose mole number
2125 // can grow according to the element compositions. Candidates are
2126 // still filtered according to their linear independence.
2127 //
2128 // Note, if there is electronic charge and the electron species, you
2129 // should probably pick the electron as a component, if it linearly
2130 // independent. The algorithm below will do this automagically.
2131 if ((m_aw[k] != test) && m_aw[k] < VCS_DELETE_MINORSPECIES_CUTOFF) {
2132 double maxConcPossKspec = 0.0;
2133 double maxConcPoss = 0.0;
2134 size_t kfound = npos;
2135 int minNonZeroes = 100000;
2136 int nonZeroesKspec = 0;
2137 for (size_t kspec = ncTrial; kspec < m_nsp; kspec++) {
2138 if (m_aw[kspec] >= 0.0 && m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
2139 maxConcPossKspec = 1.0E10;
2140 nonZeroesKspec = 0;
2141 for (size_t j = 0; j < m_nelem; ++j) {
2143 double nu = m_formulaMatrix(kspec,j);
2144 if (nu != 0.0) {
2145 nonZeroesKspec++;
2146 maxConcPossKspec = std::min(m_elemAbundancesGoal[j] / nu, maxConcPossKspec);
2147 }
2148 }
2149 }
2150 if ((maxConcPossKspec >= maxConcPoss) || (maxConcPossKspec > 1.0E-5)) {
2151 if (nonZeroesKspec <= minNonZeroes) {
2152 if (kfound == npos || nonZeroesKspec < minNonZeroes) {
2153 kfound = kspec;
2154 } else {
2155 // ok we are sitting pretty equal here
2156 // decide on the raw ss Gibbs energy
2157 if (m_SSfeSpecies[kspec] <= m_SSfeSpecies[kfound]) {
2158 kfound = kspec;
2159 }
2160 }
2161 }
2162 minNonZeroes = std::min(minNonZeroes, nonZeroesKspec);
2163 maxConcPoss = std::max(maxConcPoss, maxConcPossKspec);
2164 }
2165 }
2166 }
2167 if (kfound == npos) {
2168 double gmin = 0.0;
2169 kfound = k;
2170 for (size_t kspec = ncTrial; kspec < m_nsp; kspec++) {
2171 if (m_aw[kspec] >= 0.0) {
2172 size_t irxn = kspec - ncTrial;
2173 if (m_deltaGRxn_new[irxn] < gmin) {
2174 gmin = m_deltaGRxn_new[irxn];
2175 kfound = kspec;
2176 }
2177 }
2178 }
2179 }
2180 k = kfound;
2181 }
2182
2183 if (m_aw[k] == test) {
2184 exhausted = true;
2185 m_numComponents = jr;
2186 ncTrial = m_numComponents;
2187 size_t numPreDeleted = m_numRxnTot - m_numRxnRdc;
2188 if (numPreDeleted != (m_nsp - m_numSpeciesRdc)) {
2189 throw CanteraError("VCS_SOLVE::vcs_basopt", "we shouldn't be here");
2190 }
2191 m_numRxnTot = m_nsp - ncTrial;
2192 m_numRxnRdc = m_numRxnTot - numPreDeleted;
2193 m_numSpeciesRdc = m_nsp - numPreDeleted;
2194 for (size_t i = 0; i < m_nsp; ++i) {
2195 m_indexRxnToSpecies[i] = ncTrial + i;
2196 }
2197 if (m_debug_print_lvl >= 2) {
2198 plogf(" --- Total number of components found = %3d (ne = %d)\n ",
2199 ncTrial, m_nelem);
2200 }
2201 break;
2202 }
2203
2204 // Assign a small negative number to the component that we have just
2205 // found, in order to take it out of further consideration.
2206 m_aw[k] = test;
2207
2208 // CHECK LINEAR INDEPENDENCE WITH PREVIOUS SPECIES
2209 //
2210 // Modified Gram-Schmidt Method, p. 202 Dalquist
2211 // QR factorization of a matrix without row pivoting.
2212 size_t jl = jr;
2213 for (size_t j = 0; j < m_nelem; ++j) {
2214 m_sm[j + jr*m_nelem] = m_formulaMatrix(k,j);
2215 }
2216 if (jl > 0) {
2217 // Compute the coefficients of JA column of the the upper
2218 // triangular R matrix, SS(J) = R_J_JR this is slightly
2219 // different than Dalquist) R_JA_JA = 1
2220 for (size_t j = 0; j < jl; ++j) {
2221 m_ss[j] = 0.0;
2222 for (size_t i = 0; i < m_nelem; ++i) {
2223 m_ss[j] += m_sm[i + jr*m_nelem] * m_sm[i + j*m_nelem];
2224 }
2225 m_ss[j] /= m_sa[j];
2226 }
2227 // Now make the new column, (*,JR), orthogonal to the previous
2228 // columns
2229 for (size_t j = 0; j < jl; ++j) {
2230 for (size_t i = 0; i < m_nelem; ++i) {
2231 m_sm[i + jr*m_nelem] -= m_ss[j] * m_sm[i + j*m_nelem];
2232 }
2233 }
2234 }
2235
2236 // Find the new length of the new column in Q. It will be used in
2237 // the denominator in future row calcs.
2238 m_sa[jr] = 0.0;
2239 for (size_t ml = 0; ml < m_nelem; ++ml) {
2240 m_sa[jr] += pow(m_sm[ml + jr*m_nelem], 2);
2241 }
2242
2243 // IF NORM OF NEW ROW .LT. 1E-3 REJECT
2244 if (m_sa[jr] > 1.0e-6) {
2245 break;
2246 }
2247 }
2248
2249 if (exhausted) {
2250 break;
2251 }
2252
2253 // REARRANGE THE DATA
2254 if (jr != k) {
2255 if (m_debug_print_lvl >= 2) {
2256 plogf(" --- %-12.12s", m_speciesName[k]);
2258 plogf("(Volts = %9.2g)", m_molNumSpecies_old[k]);
2259 } else {
2260 plogf("(%9.2g)", m_molNumSpecies_old[k]);
2261 }
2262 plogf(" replaces %-12.12s", m_speciesName[jr]);
2264 plogf("(Volts = %9.2g)", m_molNumSpecies_old[jr]);
2265 } else {
2266 plogf("(%9.2g)", m_molNumSpecies_old[jr]);
2267 }
2268 plogf(" as component %3d\n", jr);
2269 }
2270 vcs_switch_pos(false, jr, k);
2271 std::swap(m_aw[jr], m_aw[k]);
2272 } else if (m_debug_print_lvl >= 2) {
2273 plogf(" --- %-12.12s", m_speciesName[k]);
2275 plogf("(Volts = %9.2g) remains ", m_molNumSpecies_old[k]);
2276 } else {
2277 plogf("(%9.2g) remains ", m_molNumSpecies_old[k]);
2278 }
2279 plogf(" as component %3d\n", jr);
2280 }
2281 // If we haven't found enough components, go back and find some more.
2282 jr++;
2283 }
2284
2285 if (doJustComponents) {
2287 return;
2288 }
2289
2290 // EVALUATE THE STOICHIOMETRY
2291 //
2292 // Formulate the matrix problem for the stoichiometric
2293 // coefficients. CX + B = 0
2294 //
2295 // C will be an nc x nc matrix made up of the formula vectors for the
2296 // components. n RHS's will be solved for. Thus, B is an nc x n matrix.
2297 //
2298 // BIG PROBLEM 1/21/99:
2299 //
2300 // This algorithm makes the assumption that the first nc rows of the formula
2301 // matrix aren't rank deficient. However, this might not be the case. For
2302 // example, assume that the first element in m_formulaMatrix[] is argon.
2303 // Assume that no species in the matrix problem actually includes argon.
2304 // Then, the first row in sm[], below will be identically zero. bleh.
2305 //
2306 // What needs to be done is to perform a rearrangement of the ELEMENTS ->
2307 // that is, rearrange, m_formulaMatrix, sp, and m_elemAbundancesGoal, such that
2308 // the first nc elements form in combination with the nc components create
2309 // an invertible sm[]. not a small project, but very doable.
2310 //
2311 // An alternative would be to turn the matrix problem below into an ne x nc
2312 // problem, and do QR elimination instead of Gauss-Jordan elimination. Note
2313 // the rearrangement of elements need only be done once in the problem. It's
2314 // actually very similar to the top of this program with ne being the
2315 // species and nc being the elements!!
2316 C.resize(ncTrial, ncTrial);
2317 for (size_t j = 0; j < ncTrial; ++j) {
2318 for (size_t i = 0; i < ncTrial; ++i) {
2319 C(i, j) = m_formulaMatrix(j,i);
2320 }
2321 }
2322 for (size_t i = 0; i < m_numRxnTot; ++i) {
2323 k = m_indexRxnToSpecies[i];
2324 for (size_t j = 0; j < ncTrial; ++j) {
2326 }
2327 }
2328 // Solve the linear system to calculate the reaction matrix,
2329 // m_stoichCoeffRxnMatrix.
2331
2332 // NOW, if we have interfacial voltage unknowns, what we did was just wrong
2333 // -> hopefully it didn't blow up. Redo the problem. Search for inactive E
2334 juse = npos;
2335 jlose = npos;
2336 for (size_t j = 0; j < m_nelem; j++) {
2337 if (!m_elementActive[j] && !strcmp(m_elementName[j].c_str(), "E")) {
2338 juse = j;
2339 }
2340 }
2341 for (size_t j = 0; j < m_nelem; j++) {
2342 if (m_elementActive[j] && !strncmp((m_elementName[j]).c_str(), "cn_", 3)) {
2343 jlose = j;
2344 }
2345 }
2346 for (k = 0; k < m_nsp; k++) {
2348 for (size_t j = 0; j < ncTrial; ++j) {
2349 for (size_t i = 0; i < ncTrial; ++i) {
2350 if (i == jlose) {
2351 C(i, j) = m_formulaMatrix(j,juse);
2352 } else {
2353 C(i, j) = m_formulaMatrix(j,i);
2354 }
2355 }
2356 }
2357 for (size_t i = 0; i < m_numRxnTot; ++i) {
2358 k = m_indexRxnToSpecies[i];
2359 for (size_t j = 0; j < ncTrial; ++j) {
2360 if (j == jlose) {
2361 m_aw[j] = - m_formulaMatrix(k,juse);
2362 } else {
2363 m_aw[j] = - m_formulaMatrix(k,j);
2364 }
2365 }
2366 }
2367
2368 solve(C, m_aw.data(), 1, m_nelem);
2369 size_t i = k - ncTrial;
2370 for (size_t j = 0; j < ncTrial; j++) {
2371 m_stoichCoeffRxnMatrix(j,i) = m_aw[j];
2372 }
2373 }
2374 }
2375
2376 // Calculate the szTmp array for each formation reaction
2377 for (size_t i = 0; i < m_numRxnTot; i++) {
2378 double szTmp = 0.0;
2379 for (size_t j = 0; j < ncTrial; j++) {
2380 szTmp += fabs(m_stoichCoeffRxnMatrix(j,i));
2381 }
2382 m_scSize[i] = szTmp;
2383 }
2384
2385 if (m_debug_print_lvl >= 2) {
2386 plogf(" --- Components:");
2387 for (size_t j = 0; j < ncTrial; j++) {
2388 plogf(" %3d", j);
2389 }
2390 plogf("\n --- Components Moles:");
2391 for (size_t j = 0; j < ncTrial; j++) {
2393 plogf(" % -10.3E", 0.0);
2394 } else {
2395 plogf(" % -10.3E", m_molNumSpecies_old[j]);
2396 }
2397 }
2398 plogf("\n --- NonComponent| Moles |");
2399 for (size_t j = 0; j < ncTrial; j++) {
2400 plogf(" %10.10s", m_speciesName[j]);
2401 }
2402 plogf("\n");
2403 for (size_t i = 0; i < m_numRxnTot; i++) {
2404 plogf(" --- %3d ", m_indexRxnToSpecies[i]);
2405 plogf("%-10.10s", m_speciesName[m_indexRxnToSpecies[i]]);
2407 plogf("|% -10.3E|", 0.0);
2408 } else {
2410 }
2411 for (size_t j = 0; j < ncTrial; j++) {
2412 plogf(" %+7.3f", m_stoichCoeffRxnMatrix(j,i));
2413 }
2414 plogf("\n");
2415 }
2416
2417 // Manual check on the satisfaction of the reaction matrix's ability to
2418 // conserve elements
2419 double sumMax = -1.0;
2420 size_t iMax = npos;
2421 size_t jMax = npos;
2422 for (size_t i = 0; i < m_numRxnTot; ++i) {
2423 k = m_indexRxnToSpecies[i];
2424 double sum;
2425 for (size_t j = 0; j < ncTrial; ++j) {
2426 if (j == jlose) {
2427 sum = m_formulaMatrix(k,juse);
2428 for (size_t n = 0; n < ncTrial; n++) {
2429 double numElements = m_formulaMatrix(n,juse);
2430 double coeff = m_stoichCoeffRxnMatrix(n,i);
2431 sum += coeff * numElements;
2432 }
2433 } else {
2434 sum = m_formulaMatrix(k,j);
2435 for (size_t n = 0; n < ncTrial; n++) {
2436 double numElements = m_formulaMatrix(n,j);
2437 double coeff = m_stoichCoeffRxnMatrix(n,i);
2438 sum += coeff * numElements;
2439 }
2440 }
2441 if (fabs(sum) > sumMax) {
2442 sumMax = fabs(sum);
2443 iMax = i;
2444 jMax = j;
2445 if (j == jlose) {
2446 jMax = juse;
2447 }
2448 }
2449 if (fabs(sum) > 1.0E-6) {
2450 throw CanteraError("VCS_SOLVE::vcs_basopt", "we have a prob");
2451 }
2452 }
2453 }
2454 plogf(" --- largest error in Stoich coeff = %g at rxn = %d ", sumMax, iMax);
2455 plogf("%-10.10s", m_speciesName[m_indexRxnToSpecies[iMax]]);
2456 plogf(" element = %d ", jMax);
2457 plogf("%-5.5s", m_elementName[jMax]);
2458 plogf("\n");
2459 plogf(" ");
2460 for (size_t i=0; i<77; i++) {
2461 plogf("-");
2462 }
2463 plogf("\n");
2464 }
2465
2466 // EVALUATE DELTA N VALUES
2467 //
2468 // Evaluate the change in gas and liquid total moles due to reaction
2469 // vectors, DNG and DNL.
2470
2471 // Zero out the change of Phase Moles array
2474
2475 // Loop over each reaction, creating the change in Phase Moles array,
2476 // m_deltaMolNumPhase(iphase,irxn), and the phase participation array,
2477 // PhaseParticipation[irxn][iphase]
2478 for (size_t irxn = 0; irxn < m_numRxnTot; ++irxn) {
2479 double* scrxn_ptr = m_stoichCoeffRxnMatrix.ptrColumn(irxn);
2480 size_t kspec = m_indexRxnToSpecies[irxn];
2481 size_t iph = m_phaseID[kspec];
2482 m_deltaMolNumPhase(iph,irxn) = 1.0;
2483 m_phaseParticipation(iph,irxn)++;
2484 for (size_t j = 0; j < ncTrial; ++j) {
2485 iph = m_phaseID[j];
2486 if (fabs(scrxn_ptr[j]) <= 1.0e-6) {
2487 scrxn_ptr[j] = 0.0;
2488 } else {
2489 m_deltaMolNumPhase(iph,irxn) += scrxn_ptr[j];
2490 m_phaseParticipation(iph,irxn)++;
2491 }
2492 }
2493 }
2495}
2496
2497int VCS_SOLVE::vcs_species_type(const size_t kspec) const
2498{
2499 // Treat special cases first
2502 }
2503
2504 size_t iph = m_phaseID[kspec];
2505 int irxn = int(kspec) - int(m_numComponents);
2506 int phaseExist = m_VolPhaseList[iph]->exists();
2507
2508 // Treat zeroed out species first
2509 if (m_molNumSpecies_old[kspec] <= 0.0) {
2510 if (m_tPhaseMoles_old[iph] <= 0.0 && !m_SSPhase[kspec]) {
2511 return VCS_SPECIES_ZEROEDMS;
2512 }
2513
2514 // see if the species has an element which is so low that species will
2515 // always be zero
2516 for (size_t j = 0; j < m_nelem; ++j) {
2517 if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
2518 double atomComp = m_formulaMatrix(kspec,j);
2519 if (atomComp > 0.0) {
2520 double maxPermissible = m_elemAbundancesGoal[j] / atomComp;
2521 if (maxPermissible < VCS_DELETE_MINORSPECIES_CUTOFF) {
2522 if (m_debug_print_lvl >= 2) {
2523 plogf(" --- %s can not be nonzero because"
2524 " needed element %s is zero\n",
2525 m_speciesName[kspec], m_elementName[j]);
2526 }
2527 if (m_SSPhase[kspec]) {
2528 return VCS_SPECIES_ZEROEDSS;
2529 } else {
2531 }
2532 }
2533 }
2534 }
2535 }
2536
2537 // The Gibbs free energy for this species is such that it will pop back
2538 // into existence. An exception to this is if a needed regular element
2539 // is also zeroed out. Then, don't pop the phase or the species back
2540 // into existence.
2541 if (irxn >= 0) {
2542 for (size_t j = 0; j < m_numComponents; ++j) {
2543 double stoicC = m_stoichCoeffRxnMatrix(j,irxn);
2544 if (stoicC != 0.0) {
2545 double negChangeComp = - stoicC;
2546 if (negChangeComp > 0.0) {
2547 if (m_molNumSpecies_old[j] < 1.0E-60) {
2548 if (m_debug_print_lvl >= 2) {
2549 plogf(" --- %s is prevented from popping into existence because"
2550 " a needed component to be consumed, %s, has a zero mole number\n",
2551 m_speciesName[kspec], m_speciesName[j]);
2552 }
2553 if (m_SSPhase[kspec]) {
2554 return VCS_SPECIES_ZEROEDSS;
2555 } else {
2557 }
2558 }
2559 } else if (negChangeComp < 0.0) {
2560 if (m_VolPhaseList[m_phaseID[j]]->exists() <= 0) {
2561 if (m_debug_print_lvl >= 2) {
2562 plogf(" --- %s is prevented from popping into existence because"
2563 " a needed component %s is in a zeroed-phase that would be "
2564 "popped into existence at the same time\n",
2565 m_speciesName[kspec], m_speciesName[j]);
2566 }
2567 if (m_SSPhase[kspec]) {
2568 return VCS_SPECIES_ZEROEDSS;
2569 } else {
2571 }
2572 }
2573 }
2574 }
2575 }
2576 }
2577
2578 if (irxn >= 0 && m_deltaGRxn_old[irxn] >= 0.0) {
2579 // We are here when the species is or should remain zeroed out
2580 if (m_SSPhase[kspec]) {
2581 return VCS_SPECIES_ZEROEDSS;
2582 } else {
2583 if (phaseExist >= VCS_PHASE_EXIST_YES) {
2585 } else if (phaseExist == VCS_PHASE_EXIST_ZEROEDPHASE) {
2587 } else {
2588 return VCS_SPECIES_ZEROEDMS;
2589 }
2590 }
2591 }
2592
2593 // If the current phase already exists,
2594 if (m_tPhaseMoles_old[iph] > 0.0) {
2595 if (m_SSPhase[kspec]) {
2596 return VCS_SPECIES_MAJOR;
2597 } else {
2599 }
2600 }
2601
2602 // The Gibbs free energy for this species is such that it will pop back
2603 // into existence. Set it to a major species in anticipation. Note, if
2604 // we had an estimate for the emerging mole fraction of the species in
2605 // the phase, we could do better here.
2606 if (m_tPhaseMoles_old[iph] <= 0.0) {
2607 if (m_SSPhase[kspec]) {
2608 return VCS_SPECIES_MAJOR;
2609 } else {
2610 return VCS_SPECIES_ZEROEDMS;
2611 }
2612 }
2613 }
2614
2615 // Treat species with non-zero mole numbers next
2616
2617 // Always treat species in single species phases as majors if the phase
2618 // exists.
2619 if (m_SSPhase[kspec]) {
2620 return VCS_SPECIES_MAJOR;
2621 }
2622
2623 // Check to see whether the current species is a major component of its
2624 // phase. If it is, it is a major component. This is consistent with the
2625 // above rule about single species phases. A major component that is, a species
2626 // with a high mole fraction) in any phase is always treated as a major
2627 // species
2628 if (m_molNumSpecies_old[kspec] > (m_tPhaseMoles_old[iph] * 0.001)) {
2629 return VCS_SPECIES_MAJOR;
2630 }
2631
2632 // Main check in the loop: Check to see if there is a component with a mole
2633 // number that is within a factor of 100 of the current species. If there is
2634 // and that component is not part of a single species phase and shares a
2635 // non-zero stoichiometric coefficient, then the current species is a major
2636 // species.
2637 if (irxn < 0) {
2638 return VCS_SPECIES_MAJOR;
2639 } else {
2640 double szAdj = m_scSize[irxn] * std::sqrt((double)m_numRxnTot);
2641 for (size_t k = 0; k < m_numComponents; ++k) {
2642 if (!m_SSPhase[k] && m_stoichCoeffRxnMatrix(k,irxn) != 0.0 && m_molNumSpecies_old[kspec] * szAdj >= m_molNumSpecies_old[k] * 0.01) {
2643 return VCS_SPECIES_MAJOR;
2644 }
2645 }
2646 }
2647 return VCS_SPECIES_MINOR;
2648}
2649
2650void VCS_SOLVE::vcs_dfe(const int stateCalc,
2651 const int ll, const size_t lbot, const size_t ltop)
2652{
2653 double* tPhMoles_ptr=0;
2654 double* actCoeff_ptr=0;
2655 double* feSpecies=0;
2656 double* molNum=0;
2657 if (stateCalc == VCS_STATECALC_OLD) {
2658 feSpecies = &m_feSpecies_old[0];
2659 tPhMoles_ptr = &m_tPhaseMoles_old[0];
2660 actCoeff_ptr = &m_actCoeffSpecies_old[0];
2661 molNum = &m_molNumSpecies_old[0];
2662 } else if (stateCalc == VCS_STATECALC_NEW) {
2663 feSpecies = &m_feSpecies_new[0];
2664 tPhMoles_ptr = &m_tPhaseMoles_new[0];
2665 actCoeff_ptr = &m_actCoeffSpecies_new[0];
2666 molNum = &m_molNumSpecies_new[0];
2667 } else {
2668 throw CanteraError("VCS_SOLVE::vcs_dfe",
2669 "Subroutine vcs_dfe called with bad stateCalc value: {}", stateCalc);
2670 }
2671
2672 if (m_debug_print_lvl >= 2) {
2673 if (ll == 0) {
2674 if (lbot != 0) {
2675 plogf(" --- Subroutine vcs_dfe called for one species: ");
2676 plogf("%-12.12s", m_speciesName[lbot]);
2677 } else {
2678 plogf(" --- Subroutine vcs_dfe called for all species");
2679 }
2680 } else if (ll > 0) {
2681 plogf(" --- Subroutine vcs_dfe called for components and minors");
2682 } else {
2683 plogf(" --- Subroutine vcs_dfe called for components and majors");
2684 }
2685 if (stateCalc == VCS_STATECALC_NEW) {
2686 plogf(" using tentative solution");
2687 }
2688 writelogendl();
2689 }
2690
2691 double* tlogMoles = &m_TmpPhase[0];
2692
2693 // Might as well recalculate the phase mole vector and compare to the stored
2694 // one. They should be correct.
2695 for (size_t kspec = 0; kspec < m_nsp; kspec++) {
2697 size_t iph = m_phaseID[kspec];
2698 tlogMoles[iph] += molNum[kspec];
2699 }
2700 }
2701 for (size_t iph = 0; iph < m_numPhases; iph++) {
2702 AssertThrowMsg(vcs_doubleEqual(tlogMoles[iph], tPhMoles_ptr[iph]),
2703 "VCS_SOLVE::vcs_dfe",
2704 "phase Moles may be off, iph = {}, {} {}",
2705 iph, tlogMoles[iph], tPhMoles_ptr[iph]);
2706 }
2707 m_TmpPhase.assign(m_TmpPhase.size(), 0.0);
2708 for (size_t iph = 0; iph < m_numPhases; iph++) {
2709 if (tPhMoles_ptr[iph] > 0.0) {
2710 tlogMoles[iph] = log(tPhMoles_ptr[iph]);
2711 }
2712 }
2713
2714 // Calculate activity coefficients for all phases that are not current. Here
2715 // we also trigger an update check for each VolPhase to see if its mole
2716 // numbers are current with vcs
2717 for (size_t iphase = 0; iphase < m_numPhases; iphase++) {
2718 vcs_VolPhase* Vphase = m_VolPhaseList[iphase].get();
2719 Vphase->updateFromVCS_MoleNumbers(stateCalc);
2720 if (!Vphase->m_singleSpecies) {
2721 Vphase->sendToVCS_ActCoeff(stateCalc, &actCoeff_ptr[0]);
2722 }
2723 m_phasePhi[iphase] = Vphase->electricPotential();
2724 }
2725
2726 auto updateSpecies = [&](size_t kspec) {
2727 size_t iphase = m_phaseID[kspec];
2729 AssertThrowMsg(molNum[kspec] == m_phasePhi[iphase], "VCS_SOLVE::vcs_dfe",
2730 "We have an inconsistency!");
2731 AssertThrowMsg(m_chargeSpecies[kspec] == -1.0, "VCS_SOLVE::vcs_dfe",
2732 "We have an unexpected situation!");
2733 return m_SSfeSpecies[kspec]
2734 + m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
2735 }
2736
2737 double phiTerm = m_chargeSpecies[kspec] * m_Faraday_dim * m_phasePhi[iphase];
2738 if (m_SSPhase[kspec]) {
2739 return m_SSfeSpecies[kspec] + phiTerm;
2740 }
2741 if ((m_speciesStatus[kspec] == VCS_SPECIES_ZEROEDMS) ||
2743 return m_SSfeSpecies[kspec] - m_lnMnaughtSpecies[kspec] + phiTerm;
2744 }
2745
2746 double logTerm;
2747 if (molNum[kspec] <= VCS_DELETE_MINORSPECIES_CUTOFF) {
2748 if (tPhMoles_ptr[iphase] > 0.0) {
2749 logTerm = log(actCoeff_ptr[kspec] * VCS_DELETE_MINORSPECIES_CUTOFF)
2750 - tlogMoles[iphase];
2751 } else {
2752 return m_SSfeSpecies[kspec] - m_lnMnaughtSpecies[kspec] + phiTerm;
2753 }
2754 } else {
2755 logTerm = log(actCoeff_ptr[kspec] * molNum[kspec]) - tlogMoles[iphase];
2756 }
2757 return m_SSfeSpecies[kspec] + logTerm - m_lnMnaughtSpecies[kspec] + phiTerm;
2758 };
2759
2760 // ALL SPECIES, OR COMPONENTS
2761 //
2762 // Do all of the species when LL = 0. Then we are done for the routine When
2763 // LL ne 0., just do the initial components. We will then finish up below
2764 // with loops over either the major noncomponent species or the minor
2765 // noncomponent species.
2766 if (ll == 0) {
2767 for (size_t kspec = lbot; kspec < ltop; ++kspec) {
2768 feSpecies[kspec] = updateSpecies(kspec);
2769 }
2770 } else {
2771 for (size_t kspec = lbot; kspec < m_numComponents; ++kspec) {
2772 feSpecies[kspec] = updateSpecies(kspec);
2773 }
2774 }
2775
2776 if (ll < 0) {
2777 // MAJORS ONLY
2778 for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
2779 size_t kspec = m_indexRxnToSpecies[irxn];
2780 if (m_speciesStatus[kspec] != VCS_SPECIES_MINOR) {
2781 feSpecies[kspec] = updateSpecies(kspec);
2782 }
2783 }
2784 } else if (ll > 0) {
2785 // MINORS ONLY
2786 for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
2787 size_t kspec = m_indexRxnToSpecies[irxn];
2788 if (m_speciesStatus[kspec] == VCS_SPECIES_MINOR) {
2789 feSpecies[kspec] = updateSpecies(kspec);
2790 }
2791 }
2792 }
2793}
2794
2795double VCS_SOLVE::l2normdg(double dgLocal[]) const
2796{
2797 if (m_numRxnRdc <= 0) {
2798 return 0.0;
2799 }
2800 double tmp = 0;
2801 for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
2802 size_t kspec = irxn + m_numComponents;
2804 dgLocal[irxn] < 0.0) {
2805 if (m_speciesStatus[kspec] != VCS_SPECIES_ZEROEDMS) {
2806 tmp += dgLocal[irxn] * dgLocal[irxn];
2807 }
2808 }
2809 }
2810 return std::sqrt(tmp / m_numRxnRdc);
2811}
2812
2814{
2815 for (size_t i = 0; i < m_numPhases; i++) {
2816 m_tPhaseMoles_old[i] = 0.0;
2817 }
2818 for (size_t i = 0; i < m_nsp; i++) {
2821 }
2822 }
2823 double sum = 0.0;
2824 for (size_t i = 0; i < m_numPhases; i++) {
2825 sum += m_tPhaseMoles_old[i];
2826 vcs_VolPhase* Vphase = m_VolPhaseList[i].get();
2827 if (m_tPhaseMoles_old[i] == 0.0) {
2828 Vphase->setTotalMoles(0.0);
2829 } else {
2830 Vphase->setTotalMoles(m_tPhaseMoles_old[i]);
2831 }
2832 }
2833 m_totalMolNum = sum;
2834 return m_totalMolNum;
2835}
2836
2837void VCS_SOLVE::check_tmoles() const
2838{
2839 for (size_t i = 0; i < m_numPhases; i++) {
2840 double m_tPhaseMoles_old_a = 0.0;
2841
2842 for (size_t k = 0; k < m_nsp; k++) {
2844 m_tPhaseMoles_old_a += m_molNumSpecies_old[k];
2845 }
2846 }
2847
2848 double denom = m_tPhaseMoles_old[i]+ m_tPhaseMoles_old_a + 1.0E-19;
2849 if (!vcs_doubleEqual(m_tPhaseMoles_old[i]/denom, m_tPhaseMoles_old_a/denom)) {
2850 plogf("check_tmoles: we have found a problem with phase %d: %20.15g, %20.15g\n",
2851 i, m_tPhaseMoles_old[i], m_tPhaseMoles_old_a);
2852 }
2853 }
2854}
2855
2856void VCS_SOLVE::vcs_updateVP(const int vcsState)
2857{
2858 for (auto& Vphase : m_VolPhaseList) {
2859 if (vcsState == VCS_STATECALC_OLD) {
2860 Vphase->setMolesFromVCSCheck(VCS_STATECALC_OLD,
2862 &m_tPhaseMoles_old[0]);
2863 } else if (vcsState == VCS_STATECALC_NEW) {
2864 Vphase->setMolesFromVCSCheck(VCS_STATECALC_NEW,
2866 &m_tPhaseMoles_new[0]);
2867 } else {
2868 throw CanteraError("VCS_SOLVE::vcs_updateVP",
2869 "wrong stateCalc value: {}", vcsState);
2870 }
2871 }
2872}
2873
2875{
2877 if (m_debug_print_lvl >= 2) {
2878 plogf(" --- Species Status decision is reevaluated: All species are minor except for:\n");
2879 } else if (m_debug_print_lvl >= 5) {
2880 plogf(" --- Species Status decision is reevaluated\n");
2881 }
2882 for (size_t kspec = 0; kspec < m_nsp; ++kspec) {
2883 m_speciesStatus[kspec] = vcs_species_type(kspec);
2884 if (m_debug_print_lvl >= 5) {
2885 plogf(" --- %-16s: ", m_speciesName[kspec]);
2886 if (kspec < m_numComponents) {
2887 plogf("(COMP) ");
2888 } else {
2889 plogf(" ");
2890 }
2891 plogf(" %10.3g ", m_molNumSpecies_old[kspec]);
2892 const char* sString = vcs_speciesType_string(m_speciesStatus[kspec], 100);
2893 plogf("%s\n", sString);
2894 } else if (m_debug_print_lvl >= 2) {
2895 if (m_speciesStatus[kspec] != VCS_SPECIES_MINOR) {
2896 switch (m_speciesStatus[kspec]) {
2898 break;
2899 case VCS_SPECIES_MAJOR:
2900 plogf(" --- Major Species : %-s\n", m_speciesName[kspec]);
2901 break;
2903 plogf(" --- Purposely Zeroed-Phase Species (not in problem): %-s\n",
2904 m_speciesName[kspec]);
2905 break;
2907 plogf(" --- Zeroed-MS Phase Species: %-s\n", m_speciesName[kspec]);
2908 break;
2910 plogf(" --- Zeroed-SS Phase Species: %-s\n", m_speciesName[kspec]);
2911 break;
2913 plogf(" --- Deleted-Small Species : %-s\n", m_speciesName[kspec]);
2914 break;
2916 plogf(" --- Zeroed Species in an active MS phase (tmp): %-s\n",
2917 m_speciesName[kspec]);
2918 break;
2920 plogf(" --- Zeroed Species in an active MS phase (Stoich Constraint): %-s\n",
2921 m_speciesName[kspec]);
2922 break;
2924 plogf(" --- InterfaceVoltage Species: %-s\n", m_speciesName[kspec]);
2925 break;
2926 default:
2927 throw CanteraError("VCS_SOLVE::vcs_evaluate_speciesType",
2928 "Unknown type: {}", m_speciesStatus[kspec]);
2929 }
2930 }
2931 }
2932 if (kspec >= m_numComponents && m_speciesStatus[kspec] != VCS_SPECIES_MAJOR) {
2934 }
2935 }
2936 debuglog(" ---\n", m_debug_print_lvl >= 2);
2937 return (m_numRxnMinorZeroed >= m_numRxnRdc);
2938}
2939
2940void VCS_SOLVE::vcs_deltag(const int L, const bool doDeleted,
2941 const int vcsState, const bool alterZeroedPhases)
2942{
2943 size_t irxnl = m_numRxnRdc;
2944 if (doDeleted) {
2945 irxnl = m_numRxnTot;
2946 }
2947
2948 double* deltaGRxn;
2949 double* feSpecies;
2950 double* molNumSpecies;
2951 double* actCoeffSpecies;
2952 if (vcsState == VCS_STATECALC_NEW) {
2953 deltaGRxn = &m_deltaGRxn_new[0];
2954 feSpecies = &m_feSpecies_new[0];
2955 molNumSpecies = &m_molNumSpecies_new[0];
2956 actCoeffSpecies = &m_actCoeffSpecies_new[0];
2957 } else if (vcsState == VCS_STATECALC_OLD) {
2958 deltaGRxn = &m_deltaGRxn_old[0];
2959 feSpecies = &m_feSpecies_old[0];
2960 molNumSpecies = &m_molNumSpecies_old[0];
2961 actCoeffSpecies = &m_actCoeffSpecies_old[0];
2962 } else {
2963 throw CanteraError("VCS_SOLVE::vcs_deltag", "bad vcsState");
2964 }
2965
2966 if (m_debug_print_lvl >= 2) {
2967 plogf(" --- Subroutine vcs_deltag called for ");
2968 if (L < 0) {
2969 plogf("major noncomponents\n");
2970 } else if (L == 0) {
2971 plogf("all noncomponents\n");
2972 } else {
2973 plogf("minor noncomponents\n");
2974 }
2975 }
2976
2977 if (L < 0) {
2978 // MAJORS and ZEROED SPECIES ONLY
2979 for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
2980 size_t kspec = irxn + m_numComponents;
2981 if (m_speciesStatus[kspec] != VCS_SPECIES_MINOR) {
2982 int icase = 0;
2983 deltaGRxn[irxn] = feSpecies[m_indexRxnToSpecies[irxn]];
2984 double* dtmp_ptr = m_stoichCoeffRxnMatrix.ptrColumn(irxn);
2985 for (kspec = 0; kspec < m_numComponents; ++kspec) {
2986 deltaGRxn[irxn] += dtmp_ptr[kspec] * feSpecies[kspec];
2987 if (molNumSpecies[kspec] < VCS_DELETE_MINORSPECIES_CUTOFF && dtmp_ptr[kspec] < 0.0) {
2988 icase = 1;
2989 }
2990 }
2991 if (icase) {
2992 deltaGRxn[irxn] = std::max(0.0, deltaGRxn[irxn]);
2993 }
2994 }
2995 }
2996 } else if (L == 0) {
2997 // ALL REACTIONS
2998 for (size_t irxn = 0; irxn < irxnl; ++irxn) {
2999 int icase = 0;
3000 deltaGRxn[irxn] = feSpecies[m_indexRxnToSpecies[irxn]];
3001 double* dtmp_ptr = m_stoichCoeffRxnMatrix.ptrColumn(irxn);
3002 for (size_t kspec = 0; kspec < m_numComponents; ++kspec) {
3003 deltaGRxn[irxn] += dtmp_ptr[kspec] * feSpecies[kspec];
3004 if (molNumSpecies[kspec] < VCS_DELETE_MINORSPECIES_CUTOFF &&
3005 dtmp_ptr[kspec] < 0.0) {
3006 icase = 1;
3007 }
3008 }
3009 if (icase) {
3010 deltaGRxn[irxn] = std::max(0.0, deltaGRxn[irxn]);
3011 }
3012 }
3013 } else {
3014 // MINORS AND ZEROED SPECIES
3015 for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
3016 size_t kspec = irxn + m_numComponents;
3017 if (m_speciesStatus[kspec] <= VCS_SPECIES_MINOR) {
3018 int icase = 0;
3019 deltaGRxn[irxn] = feSpecies[m_indexRxnToSpecies[irxn]];
3020 double* dtmp_ptr = m_stoichCoeffRxnMatrix.ptrColumn(irxn);
3021 for (kspec = 0; kspec < m_numComponents; ++kspec) {
3022 deltaGRxn[irxn] += dtmp_ptr[kspec] * feSpecies[kspec];
3024 dtmp_ptr[kspec] < 0.0) {
3025 icase = 1;
3026 }
3027 }
3028 if (icase) {
3029 deltaGRxn[irxn] = std::max(0.0, deltaGRxn[irxn]);
3030 }
3031 }
3032 }
3033 }
3034
3035 /* **** MULTISPECIES PHASES WITH ZERO MOLES ******** */
3036 //
3037 // Massage the free energies for species with zero mole fractions in
3038 // multispecies phases. This section implements the Equation 3.8-5 in Smith
3039 // and Missen, p.59. A multispecies phase will exist iff
3040 //
3041 // 1 < sum_i(exp(-dg_i)/AC_i)
3042 //
3043 // If DG is negative then that species wants to be reintroduced into the
3044 // calculation. For small dg_i, the expression below becomes:
3045 //
3046 // 1 - sum_i(exp(-dg_i)/AC_i) ~ sum_i((dg_i-1)/AC_i) + 1
3047 //
3048 // So, what we are doing here is equalizing all DG's in a multispecies phase
3049 // whose total mole number has already been zeroed out. It must have to do
3050 // with the case where a complete multispecies phase is currently zeroed
3051 // out. In that case, when one species in that phase has a negative DG, then
3052 // the phase should kick in. This code section will cause that to happen,
3053 // because a negative DG will dominate the calculation of SDEL. Then, DG(I)
3054 // for all species in that phase will be forced to be equal and negative.
3055 // Thus, all species in that phase will come into being at the same time.
3056 //
3057 // HKM -> The ratio of mole fractions at the reinstatement time should be
3058 // equal to the normalized weighting of exp(-dg_i) / AC_i. This should be
3059 // implemented.
3060 //
3061 // HKM -> There is circular logic here. ActCoeff depends on the mole
3062 // fractions of a phase that does not exist. In actuality the proto-mole
3063 // fractions should be selected from the solution of a nonlinear problem
3064 // with NsPhase unknowns
3065 //
3066 // X_i = exp(-dg[irxn]) / ActCoeff_i / denom
3067 //
3068 // where
3069 //
3070 // denom = sum_i[ exp(-dg[irxn]) / ActCoeff_i ]
3071 //
3072 // This can probably be solved by successive iteration. This should be
3073 // implemented.
3074 if (alterZeroedPhases && false) {
3075 for (size_t iph = 0; iph < m_numPhases; iph++) {
3076 bool lneed = false;
3077 vcs_VolPhase* Vphase = m_VolPhaseList[iph].get();
3078 if (! Vphase->m_singleSpecies) {
3079 double sum = 0.0;
3080 for (size_t k = 0; k < Vphase->nSpecies(); k++) {
3081 size_t kspec = Vphase->spGlobalIndexVCS(k);
3083 sum += molNumSpecies[kspec];
3084 }
3085 if (sum > 0.0) {
3086 break;
3087 }
3088 }
3089 if (sum == 0.0) {
3090 lneed = true;
3091 }
3092 }
3093
3094 if (lneed) {
3095 double poly = 0.0;
3096 for (size_t k = 0; k < Vphase->nSpecies(); k++) {
3097 size_t kspec = Vphase->spGlobalIndexVCS(k);
3098 // We may need to look at deltaGRxn for components!
3099 if (kspec >= m_numComponents) {
3100 size_t irxn = kspec - m_numComponents;
3101 deltaGRxn[irxn] = clip(deltaGRxn[irxn], -50.0, 50.0);
3102 poly += exp(-deltaGRxn[irxn])/actCoeffSpecies[kspec];
3103 }
3104 }
3105
3106 // Calculate deltaGRxn[] for each species in a zeroed
3107 // multispecies phase. All of the m_deltaGRxn_new[]'s will be
3108 // equal. If deltaGRxn[] is negative, then the phase will come
3109 // back into existence.
3110 for (size_t k = 0; k < Vphase->nSpecies(); k++) {
3111 size_t kspec = Vphase->spGlobalIndexVCS(k);
3112 if (kspec >= m_numComponents) {
3113 size_t irxn = kspec - m_numComponents;
3114 deltaGRxn[irxn] = 1.0 - poly;
3115 }
3116 }
3117 }
3118 }
3119 }
3120}
3121
3122void VCS_SOLVE::vcs_switch_pos(const bool ifunc, const size_t k1, const size_t k2)
3123{
3124 if (k1 == k2) {
3125 return;
3126 }
3127 if (k1 >= m_nsp || k2 >= m_nsp) {
3128 plogf("vcs_switch_pos: ifunc = 0: inappropriate args: %d %d\n",
3129 k1, k2);
3130 }
3131
3132 // Handle the index pointer in the phase structures first
3133 vcs_VolPhase* pv1 = m_VolPhaseList[m_phaseID[k1]].get();
3134 vcs_VolPhase* pv2 = m_VolPhaseList[m_phaseID[k2]].get();
3135 size_t kp1 = m_speciesLocalPhaseIndex[k1];
3136 size_t kp2 = m_speciesLocalPhaseIndex[k2];
3137 AssertThrowMsg(pv1->spGlobalIndexVCS(kp1) == k1, "VCS_SOLVE::vcs_switch_pos",
3138 "Indexing error");
3139 AssertThrowMsg(pv2->spGlobalIndexVCS(kp2) == k2, "VCS_SOLVE::vcs_switch_pos",
3140 "Indexing error");
3141 pv1->setSpGlobalIndexVCS(kp1, k2);
3142 pv2->setSpGlobalIndexVCS(kp2, k1);
3143 std::swap(m_speciesName[k1], m_speciesName[k2]);
3144 std::swap(m_molNumSpecies_old[k1], m_molNumSpecies_old[k2]);
3145 std::swap(m_speciesUnknownType[k1], m_speciesUnknownType[k2]);
3146 std::swap(m_molNumSpecies_new[k1], m_molNumSpecies_new[k2]);
3147 std::swap(m_SSfeSpecies[k1], m_SSfeSpecies[k2]);
3148 std::swap(m_spSize[k1], m_spSize[k2]);
3149 std::swap(m_deltaMolNumSpecies[k1], m_deltaMolNumSpecies[k2]);
3150 std::swap(m_feSpecies_old[k1], m_feSpecies_old[k2]);
3151 std::swap(m_feSpecies_new[k1], m_feSpecies_new[k2]);
3152 std::swap(m_SSPhase[k1], m_SSPhase[k2]);
3153 std::swap(m_phaseID[k1], m_phaseID[k2]);
3154 std::swap(m_speciesMapIndex[k1], m_speciesMapIndex[k2]);
3157 std::swap(m_lnMnaughtSpecies[k1], m_lnMnaughtSpecies[k2]);
3158 std::swap(m_actCoeffSpecies_new[k1], m_actCoeffSpecies_new[k2]);
3159 std::swap(m_actCoeffSpecies_old[k1], m_actCoeffSpecies_old[k2]);
3160 std::swap(m_wtSpecies[k1], m_wtSpecies[k2]);
3161 std::swap(m_chargeSpecies[k1], m_chargeSpecies[k2]);
3162 std::swap(m_PMVolumeSpecies[k1], m_PMVolumeSpecies[k2]);
3163
3164 for (size_t j = 0; j < m_nelem; ++j) {
3165 std::swap(m_formulaMatrix(k1,j), m_formulaMatrix(k2,j));
3166 }
3167 if (m_useActCoeffJac && k1 != k2) {
3168 for (size_t i = 0; i < m_nsp; i++) {
3169 std::swap(m_np_dLnActCoeffdMolNum(k1,i), m_np_dLnActCoeffdMolNum(k2,i));
3170 }
3171 for (size_t i = 0; i < m_nsp; i++) {
3172 std::swap(m_np_dLnActCoeffdMolNum(i,k1), m_np_dLnActCoeffdMolNum(i,k2));
3173 }
3174 }
3175 std::swap(m_speciesStatus[k1], m_speciesStatus[k2]);
3176
3177 // Handle the index pointer in the phase structures
3178 if (ifunc) {
3179 // Find the Rxn indices corresponding to the two species
3180 size_t i1 = k1 - m_numComponents;
3181 size_t i2 = k2 - m_numComponents;
3182 if (i1 > m_numRxnTot || i2 >= m_numRxnTot) {
3183 plogf("switch_pos: ifunc = 1: inappropriate noncomp values: %d %d\n",
3184 i1 , i2);
3185 }
3186 for (size_t j = 0; j < m_numComponents; ++j) {
3187 std::swap(m_stoichCoeffRxnMatrix(j,i1), m_stoichCoeffRxnMatrix(j,i2));
3188 }
3189 std::swap(m_scSize[i1], m_scSize[i2]);
3190 for (size_t iph = 0; iph < m_numPhases; iph++) {
3191 std::swap(m_deltaMolNumPhase(iph,i1), m_deltaMolNumPhase(iph,i2));
3192 std::swap(m_phaseParticipation(iph,i1),
3193 m_phaseParticipation(iph,i2));
3194 }
3195 std::swap(m_deltaGRxn_new[i1], m_deltaGRxn_new[i2]);
3196 std::swap(m_deltaGRxn_old[i1], m_deltaGRxn_old[i2]);
3197 std::swap(m_deltaGRxn_tmp[i1], m_deltaGRxn_tmp[i2]);
3198 }
3199}
3200
3201void VCS_SOLVE::vcs_setFlagsVolPhases(const bool upToDate, const int stateCalc)
3202{
3203 if (!upToDate) {
3204 for (size_t iph = 0; iph < m_numPhases; iph++) {
3205 m_VolPhaseList[iph]->setMolesOutOfDate(stateCalc);
3206 }
3207 } else {
3208 for (size_t iph = 0; iph < m_numPhases; iph++) {
3209 m_VolPhaseList[iph]->setMolesCurrent(stateCalc);
3210 }
3211 }
3212}
3213
3215{
3216 for (size_t iph = 0; iph < m_numPhases; iph++) {
3217 m_VolPhaseList[iph]->updateFromVCS_MoleNumbers(stateCalc);
3218 }
3219}
3220
3221}
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:127
double * ptrColumn(size_t j)
Return a pointer to the top of column j, columns are contiguous in memory.
Definition Array.h:203
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.
size_t vcs_popPhaseID(vector< size_t > &phasePopPhaseIDs)
Decision as to whether a phase pops back into existence.
vector< double > m_deltaPhaseMoles
Change in the total moles in each phase.
Definition vcs_solve.h:1074
size_t m_numPhases
Number of Phases in the problem.
Definition vcs_solve.h:876
int m_doEstimateEquil
Setting for whether to do an initial estimate.
Definition vcs_solve.h:953
vector< double > m_deltaMolNumSpecies
Reaction Adjustments for each species during the current step.
Definition vcs_solve.h:1021
vector< double > m_spSize
total size of the species
Definition vcs_solve.h:919
vector< double > m_scSize
Absolute size of the stoichiometric coefficients.
Definition vcs_solve.h:912
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:798
vector< double > m_wtSpecies
Molecular weight of each species.
Definition vcs_solve.h:1245
vector< size_t > m_indexRxnToSpecies
Mapping between the species index for noncomponent species and the full species index.
Definition vcs_solve.h:1146
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:800
vector< size_t > m_phaseID
Mapping from the species number to the phase number.
Definition vcs_solve.h:1157
vector< string > m_speciesName
Species string name for the kth species.
Definition vcs_solve.h:1164
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:1089
vector< double > m_deltaGRxn_new
Delta G(irxn) for the noncomponent species in the mechanism.
Definition vcs_solve.h:1001
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:1237
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:855
vector< double > m_PMVolumeSpecies
Partial molar volumes of the species.
Definition vcs_solve.h:1264
vector< int > m_elementActive
Specifies whether an element constraint is active.
Definition vcs_solve.h:1188
int m_useActCoeffJac
Choice of Hessians.
Definition vcs_solve.h:1257
vector< size_t > m_speciesLocalPhaseIndex
Index that keeps track of the index of the species within the local phase.
Definition vcs_solve.h:1120
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:1170
vector< int > m_speciesUnknownType
Specifies the species unknown type.
Definition vcs_solve.h:973
int vcs_elcorr(double aa[], double x[])
This subroutine corrects for element abundances.
vector< double > m_TmpPhase
Temporary vector of length NPhase.
Definition vcs_solve.h:1065
vector< size_t > m_speciesMapIndex
Index vector that keeps track of the species vector rearrangement.
Definition vcs_solve.h:1106
Array2D m_phaseParticipation
This is 1 if the phase, iphase, participates in the formation reaction irxn, and zero otherwise.
Definition vcs_solve.h:984
vector< int > m_actConventionSpecies
specifies the activity convention of the phase containing the species
Definition vcs_solve.h:1200
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:1030
vector< double > m_tPhaseMoles_new
total kmols of species in each phase in the tentative soln vector
Definition vcs_solve.h:1062
vector< double > m_actCoeffSpecies_old
Molar-based Activity Coefficients for Species based on old mole numbers.
Definition vcs_solve.h:1227
double vcs_minor_alt_calc(size_t kspec, size_t irxn, bool *do_delete) const
Minor species alternative calculation.
vector< double > m_SSfeSpecies
Standard state chemical potentials for species K at the current temperature and pressure.
Definition vcs_solve.h:927
double l2normdg(double dg[]) const
Calculate the norm of a deltaGibbs free energy vector.
int delta_species(const size_t kspec, double *const delta_ptr)
Change the concentration of a species by delta moles.
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:1092
vector< double > m_lnMnaughtSpecies
specifies the ln(Mnaught) used to calculate the chemical potentials
Definition vcs_solve.h:1216
vector< int > m_elType
Type of the element constraint.
Definition vcs_solve.h:1182
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.
vector< double > m_molNumSpecies_old
Total moles of the species.
Definition vcs_solve.h:960
double vcs_Total_Gibbs(double *w, double *fe, double *tPhMoles)
Calculate the total dimensionless Gibbs free energy.
size_t m_numRxnMinorZeroed
Number of active species which are currently either treated as minor species.
Definition vcs_solve.h:873
vector< double > m_aw
work array of mole fractions used in vcs_basopt()
Definition vcs_solve.h:801
size_t m_numSpeciesRdc
Current number of species in the problems.
Definition vcs_solve.h:865
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:934
int m_debug_print_lvl
Debug printing lvl.
Definition vcs_solve.h:1284
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:799
vector< double > m_chargeSpecies
Charge of each species. Length = number of species.
Definition vcs_solve.h:1248
Array2D m_formulaMatrix
Formula matrix for the problem.
Definition vcs_solve.h:884
Array2D m_stoichCoeffRxnMatrix
Stoichiometric coefficient matrix for the reaction mechanism expressed in Reduced Canonical Form.
Definition vcs_solve.h:903
size_t m_numRxnRdc
Current number of non-component species in the problem.
Definition vcs_solve.h:869
VCS_COUNTERS * m_VCount
Timing and iteration counters for the vcs object.
Definition vcs_solve.h:1270
vector< double > m_deltaGRxn_Deficient
Last deltag[irxn] from the previous step with additions for possible births of zeroed phases.
Definition vcs_solve.h:1008
vector< unique_ptr< vcs_VolPhase > > m_VolPhaseList
Array of Phase Structures. Length = number of phases.
Definition vcs_solve.h:1191
size_t m_numComponents
Number of components calculated for the problem.
Definition vcs_solve.h:858
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:1080
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:943
double m_Faraday_dim
dimensionless value of Faraday's constant, F / RT (1/volt)
Definition vcs_solve.h:1267
int vcs_recheck_deleted()
Recheck deleted species in multispecies phases.
double m_tolmin
Tolerance requirements for minor species.
Definition vcs_solve.h:1086
size_t m_nsp
Total number of species in the problems.
Definition vcs_solve.h:852
vector< double > m_actCoeffSpecies_new
Molar-based Activity Coefficients for Species.
Definition vcs_solve.h:1220
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:1161
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:987
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:1039
size_t vcs_RxnStepSizes(int &forceComponentCalc, size_t &kSpecial)
Calculates formation reaction step sizes.
Array2D m_deltaMolNumPhase
Change in the number of moles of phase, iphase, due to the noncomponent formation reaction,...
Definition vcs_solve.h:980
vector< int > m_speciesStatus
Major -Minor status vector for the species in the problem.
Definition vcs_solve.h:1154
vector< double > m_deltaGRxn_tmp
Temporary vector of Rxn DeltaG's.
Definition vcs_solve.h:1014
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:806
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:1053
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:861
vector< double > m_molNumSpecies_new
Tentative value of the mole number vector.
Definition vcs_solve.h:991
double m_temperature
Temperature (Kelvin)
Definition vcs_solve.h:1077
double m_tolmaj
Tolerance requirement for major species.
Definition vcs_solve.h:1083
vector< double > m_deltaGRxn_old
Last deltag[irxn] from the previous step.
Definition vcs_solve.h:1004
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.
double m_totalMolNum
Total number of kmoles in all phases.
Definition vcs_solve.h:1045
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 setMolesFromVCSCheck(const int vcsStateStatus, const double *molesSpeciesVCS, const double *const TPhMoles)
Set the moles within the phase.
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 setTotalMoles(const double totalMols)
Sets the total moles in the phase.
void sendToVCS_ActCoeff(const int stateCalc, double *const AC)
Fill in an activity coefficients vector within a VCS_SOLVE object.
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:131
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition ct_defs.h:120
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:180
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 solve(DenseMatrix &A, double *b, size_t nrhs, size_t ldb)
Solve Ax = b. Array b is overwritten on exit with x.
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...