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