Cantera  4.0.0a1
Loading...
Searching...
No Matches
vcs_solve.cpp
Go to the documentation of this file.
1/*!
2 * @file vcs_solve.cpp Implementation file for the internal class that holds
3 * the problem.
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
16
17using namespace std;
18
19namespace Cantera
20{
21
22namespace {
23
24void printProgress(const vector<string>& spName, const vector<double>& soln,
25 const vector<double>& ff)
26{
27 double sum = 0.0;
28 plogf(" --- Summary of current progress:\n");
29 plogf(" --- Name Moles - SSGibbs \n");
30 plogf(" -------------------------------------------------------------------------------------\n");
31 for (size_t k = 0; k < soln.size(); k++) {
32 plogf(" --- %20s %12.4g - %12.4g\n", spName[k], soln[k], ff[k]);
33 sum += soln[k] * ff[k];
34 }
35 plogf(" --- Total sum to be minimized = %g\n", sum);
36}
37
38} // anonymous namespace
39
40VCS_SOLVE::VCS_SOLVE(MultiPhase* mphase, int printLvl) :
41 m_printLvl(printLvl),
42 m_mix(mphase),
43 m_nsp(mphase->nSpecies()),
44 m_numSpeciesRdc(mphase->nSpecies()),
45 m_numPhases(mphase->nPhases()),
46 m_temperature(mphase->temperature()),
47 m_pressurePA(mphase->pressure()),
48 m_Faraday_dim(Faraday / (m_temperature * GasConstant))
49{
50 string ser = "VCS_SOLVE: ERROR:\n\t";
51 if (m_nsp <= 0) {
52 plogf("%s Number of species is nonpositive\n", ser);
53 throw CanteraError("VCS_SOLVE::VCS_SOLVE", ser +
54 " Number of species is nonpositive\n");
55 }
56 if (m_numPhases <= 0) {
57 plogf("%s Number of phases is nonpositive\n", ser);
58 throw CanteraError("VCS_SOLVE::VCS_SOLVE", ser +
59 " Number of phases is nonpositive\n");
60 }
61
62 /*
63 * We will initialize sc[] to note the fact that it needs to be
64 * filled with meaningful information.
65 */
66 m_scSize.resize(m_nsp, 0.0);
67 m_spSize.resize(m_nsp, 1.0);
68 m_SSfeSpecies.resize(m_nsp, 0.0);
69 m_feSpecies_new.resize(m_nsp, 0.0);
70 m_molNumSpecies_old.resize(m_nsp, 0.0);
74 m_phasePhi.resize(m_numPhases, 0.0);
75 m_molNumSpecies_new.resize(m_nsp, 0.0);
76 m_deltaGRxn_new.resize(m_nsp, 0.0);
77 m_deltaGRxn_old.resize(m_nsp, 0.0);
78 m_deltaGRxn_Deficient.resize(m_nsp, 0.0);
79 m_deltaGRxn_tmp.resize(m_nsp, 0.0);
80 m_deltaMolNumSpecies.resize(m_nsp, 0.0);
81 m_feSpecies_old.resize(m_nsp, 0.0);
82 m_tPhaseMoles_old.resize(m_numPhases, 0.0);
83 m_tPhaseMoles_new.resize(m_numPhases, 0.0);
84 m_deltaPhaseMoles.resize(m_numPhases, 0.0);
85 m_TmpPhase.resize(m_numPhases, 0.0);
86 m_TmpPhase2.resize(m_numPhases, 0.0);
87
88 // ind[] is an index variable that keep track of solution vector rotations.
89 m_speciesMapIndex.resize(m_nsp, 0);
91
92 // ir[] is an index vector that keeps track of the irxn to species mapping.
93 // We can't fill it in until we know the number of c components in the
94 // problem
95 m_indexRxnToSpecies.resize(m_nsp, 0);
96
97 // Initialize all species to be major species
99
100 m_SSPhase.resize(2*m_nsp, 0);
101 m_phaseID.resize(m_nsp, 0);
102 m_speciesName.resize(m_nsp);
103
104 // space for activity coefficients for all species. Set it equal to one.
105 m_actConventionSpecies.resize(m_nsp, 0);
107 m_lnMnaughtSpecies.resize(m_nsp, 0.0);
108 m_actCoeffSpecies_new.resize(m_nsp, 1.0);
109 m_actCoeffSpecies_old.resize(m_nsp, 1.0);
110 m_wtSpecies.resize(m_nsp, 0.0);
111 m_chargeSpecies.resize(m_nsp, 0.0);
112
113 // Phase Info
115
116 // For Future expansion
117 m_useActCoeffJac = true;
118 if (m_useActCoeffJac) {
120 }
121
122 m_PMVolumeSpecies.resize(m_nsp, 0.0);
123
124 // counters kept within vcs
125 m_VCount = new VCS_COUNTERS();
127
128 // Loop over the phases, transferring pertinent information
129 int kT = 0;
130 for (size_t iphase = 0; iphase < m_numPhases; iphase++) {
131 // Get the ThermoPhase object - assume volume phase
132 ThermoPhase* tPhase = &mphase->phase(iphase);
133
134 // Assign the value of eqn of state. Handle conflicts here.
135 if (tPhase->nDim() != 3) {
136 throw CanteraError("VCS_SOLVE::VCS_SOLVE",
137 "Surface/edge phase not handled yet.");
138 }
139
140 m_VolPhaseList.emplace_back(
141 make_unique<vcs_VolPhase>(this, tPhase, iphase));
142 vcs_VolPhase* VolPhase = m_VolPhaseList[iphase].get();
144
145 // Loop through each species in the current phase
146 size_t nSpPhase = tPhase->nSpecies();
147 for (size_t k = 0; k < nSpPhase; k++) {
148 // Obtain the molecular weight of the species from the
149 // ThermoPhase object
150 m_wtSpecies[kT] = tPhase->molecularWeight(k);
151
152 // Obtain the charges of the species from the ThermoPhase object
153 m_chargeSpecies[kT] = tPhase->charge(k);
154
155 // Set the phaseid of the species
156 m_phaseID[kT] = iphase;
157
158 // Transfer the type of unknown
159 m_speciesUnknownType[kT] = VolPhase->speciesUnknownType(k);
161 // Set the initial number of kmoles of the species
162 m_molNumSpecies_old[kT] = mphase->speciesMoles(kT);
165 } else {
166 throw CanteraError("VCS_SOLVE::VCS_SOLVE",
167 "Unknown species type: {}", m_speciesUnknownType[kT]);
168 }
169
170 // Transfer the species information from the
171 // volPhase structure to the VPROB structure
172 // This includes:
173 // FormulaMatrix[][]
174 // VolPhase->IndSpecies[]
175 addOnePhaseSpecies(VolPhase, k, kT);
176
177 kT++;
178 }
179
181 }
182
183 // Work arrays used by vcs_basopt
184 m_sm.assign(m_nelem * m_nelem, 0.0);
185 m_ss.assign(m_nelem, 0.0);
186 m_sa.assign(m_nelem, 0.0);
187 m_aw.assign(m_nsp, 0.0);
188 m_wx.assign(m_nelem, 0.0);
189
190 // Transfer initial element abundances based on the species mole numbers
191 for (size_t j = 0; j < m_nelem; j++) {
192 for (size_t kspec = 0; kspec < m_nsp; kspec++) {
195 }
196 }
197 }
198
199 // Printout the species information: PhaseID's and mole nums
200 if (m_printLvl > 1) {
201 writeline('=', 80, true, true);
202 writeline('=', 16, false);
203 plogf(" Cantera_to_vprob: START OF PROBLEM STATEMENT ");
204 writeline('=', 20);
205 writeline('=', 80);
206 plogf(" Phase IDs of species\n");
207 plogf(" species phaseID phaseName ");
208 plogf(" Initial_Estimated_kMols\n");
209 for (size_t i = 0; i < m_nsp; i++) {
210 size_t iphase = m_phaseID[i];
211
212 vcs_VolPhase* VolPhase = m_VolPhaseList[iphase].get();
213 plogf("%16s %5d %16s", mphase->speciesName(i).c_str(), iphase,
214 VolPhase->PhaseName.c_str());
216 plogf(" Volts = %-10.5g\n", m_molNumSpecies_old[i]);
217 } else {
218 plogf(" %-10.5g\n", m_molNumSpecies_old[i]);
219 }
220 }
221
222 // Printout of the Phase structure information
223 writeline('-', 80, true, true);
224 plogf(" Information about phases\n");
225 plogf(" PhaseName PhaseNum SingSpec EqnState NumSpec "
226 "Tmoles(kmol)\n");
227
228 for (size_t iphase = 0; iphase < m_numPhases; iphase++) {
229 vcs_VolPhase* VolPhase = m_VolPhaseList[iphase].get();
230 plogf("%16s %8d %8d %16s %8d ", VolPhase->PhaseName.c_str(),
231 VolPhase->VP_ID_, VolPhase->m_singleSpecies,
232 VolPhase->eos_name(), VolPhase->nSpecies());
233 plogf("%16e\n", VolPhase->totalMoles());
234 }
235
236 writeline('=', 80, true, true);
237 writeline('=', 16, false);
238 plogf(" Cantera_to_vprob: END OF PROBLEM STATEMENT ");
239 writeline('=', 20);
240 writeline('=', 80);
241 plogf("\n");
242 }
243
244 // m_speciesIndexVector[] is an index variable that keep track of solution
245 // vector rotations.
246 for (size_t i = 0; i < m_nsp; i++) {
247 m_speciesMapIndex[i] = i;
248 }
249
250 // IndEl[] is an index variable that keep track of element vector rotations.
251 for (size_t i = 0; i < m_nelem; i++) {
252 m_elementMapIndex[i] = i;
253 }
254
255 // Fill in the species to phase mapping. Check for bad values at the same
256 // time.
257 vector<size_t> numPhSp(m_numPhases, 0);
258 for (size_t kspec = 0; kspec < m_nsp; kspec++) {
259 size_t iph = m_phaseID[kspec];
260 if (iph >= m_numPhases) {
261 throw CanteraError("VCS_SOLVE::VCS_SOLVE",
262 "Species to Phase Mapping, PhaseID, has a bad value\n"
263 "\tm_phaseID[{}] = {}\n"
264 "Allowed values: 0 to {}", kspec, iph, m_numPhases - 1);
265 }
266 m_phaseID[kspec] = m_phaseID[kspec];
267 m_speciesLocalPhaseIndex[kspec] = numPhSp[iph];
268 numPhSp[iph]++;
269 }
270 for (size_t iph = 0; iph < m_numPhases; iph++) {
271 vcs_VolPhase* Vphase = m_VolPhaseList[iph].get();
272 if (numPhSp[iph] != Vphase->nSpecies()) {
273 throw CanteraError("VCS_SOLVE::VCS_SOLVE",
274 "Number of species in phase {}, {}, doesn't match ({} != {}) [vphase = {}]",
275 ser, iph, Vphase->PhaseName, numPhSp[iph], Vphase->nSpecies(), (size_t) Vphase);
276 }
277 }
278
279 for (size_t i = 0; i < m_nelem; i++) {
281 if (m_elemAbundancesGoal[i] != 0.0) {
282 if (fabs(m_elemAbundancesGoal[i]) > 1.0E-9) {
283 throw CanteraError("VCS_SOLVE::VCS_SOLVE",
284 "Charge neutrality condition {} is signicantly "
285 "nonzero, {}. Giving up",
287 } else {
288 if (m_debug_print_lvl >= 2) {
289 plogf("Charge neutrality condition %s not zero, %g. Setting it zero\n",
291 }
292 m_elemAbundancesGoal[i] = 0.0;
293 }
294 }
295 }
296 }
297
298 // Copy over the species names
299 for (size_t i = 0; i < m_nsp; i++) {
300 m_speciesName[i] = m_mix->speciesName(i);
301 }
302
303 // Specify the Activity Convention information
304 for (size_t iph = 0; iph < m_numPhases; iph++) {
305 vcs_VolPhase* Vphase = m_VolPhaseList[iph].get();
307 if (Vphase->p_activityConvention != 0) {
308 // We assume here that species 0 is the solvent. The solvent isn't
309 // on a unity activity basis The activity for the solvent assumes
310 // that the it goes to one as the species mole fraction goes to one;
311 // that is, it's really on a molarity framework. So
312 // SpecLnMnaught[iSolvent] = 0.0, and the loop below starts at 1,
313 // not 0.
314 size_t iSolvent = Vphase->spGlobalIndexVCS(0);
315 double mnaught = m_wtSpecies[iSolvent] / 1000.;
316 for (size_t k = 1; k < Vphase->nSpecies(); k++) {
317 size_t kspec = Vphase->spGlobalIndexVCS(k);
319 m_lnMnaughtSpecies[kspec] = log(mnaught);
320 }
321 }
322 }
323
324}
325
326VCS_SOLVE::~VCS_SOLVE()
327{
328 delete m_VCount;
329}
330
331bool VCS_SOLVE::vcs_popPhasePossible(const size_t iphasePop) const
332{
333 vcs_VolPhase* Vphase = m_VolPhaseList[iphasePop].get();
334 AssertThrowMsg(!Vphase->exists(), "VCS_SOLVE::vcs_popPhasePossible",
335 "called for a phase that exists!");
336
337 // Loop through all of the species in the phase. We say the phase can be
338 // popped, if there is one species in the phase that can be popped. This
339 // does not mean that the phase will be popped or that it leads to a lower
340 // Gibbs free energy.
341 for (size_t k = 0; k < Vphase->nSpecies(); k++) {
342 size_t kspec = Vphase->spGlobalIndexVCS(k);
344 "VCS_SOLVE::vcs_popPhasePossible",
345 "we shouldn't be here {}: {} > 0.0", kspec,
346 m_molNumSpecies_old[kspec]);
347 size_t irxn = kspec - m_numComponents;
348 if (kspec >= m_numComponents) {
349 bool iPopPossible = true;
350
351 // Note one case is if the component is a member of the popping
352 // phase. This component will be zeroed and the logic here will
353 // negate the current species from causing a positive if this
354 // component is consumed.
355 for (size_t j = 0; j < m_numComponents; ++j) {
356 if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
357 double stoicC = m_stoichCoeffRxnMatrix(j,irxn);
358 if (stoicC != 0.0) {
359 double negChangeComp = - stoicC;
360 if (negChangeComp > 0.0) {
361 // If there is no component to give, then the
362 // species can't be created
364 iPopPossible = false;
365 }
366 }
367 }
368 }
369 }
370 // We are here when the species can be popped because all its needed
371 // components have positive mole numbers
372 if (iPopPossible) {
373 return true;
374 }
375 } else {
376 // We are here when the species, k, in the phase is a component. Its
377 // mole number is zero. We loop through the regular reaction looking
378 // for a reaction that can pop the component.
379 for (size_t jrxn = 0; jrxn < m_numRxnRdc; jrxn++) {
380 bool foundJrxn = false;
381 // First, if the component is a product of the reaction
382 if (m_stoichCoeffRxnMatrix(kspec,jrxn) > 0.0) {
383 foundJrxn = true;
384 // We can do the reaction if all other reactant components
385 // have positive mole fractions
386 for (size_t kcomp = 0; kcomp < m_numComponents; kcomp++) {
387 if (m_stoichCoeffRxnMatrix(kcomp,jrxn) < 0.0 && m_molNumSpecies_old[kcomp] <= VCS_DELETE_ELEMENTABS_CUTOFF*0.5) {
388 foundJrxn = false;
389 }
390 }
391 if (foundJrxn) {
392 return true;
393 }
394 } else if (m_stoichCoeffRxnMatrix(kspec,jrxn) < 0.0) {
395 // Second we are here if the component is a reactant in the
396 // reaction, and the reaction goes backwards.
397 foundJrxn = true;
398 size_t jspec = jrxn + m_numComponents;
400 foundJrxn = false;
401 continue;
402 }
403 // We can do the backwards reaction if all of the product
404 // components species are positive
405 for (size_t kcomp = 0; kcomp < m_numComponents; kcomp++) {
406 if (m_stoichCoeffRxnMatrix(kcomp,jrxn) > 0.0 && m_molNumSpecies_old[kcomp] <= VCS_DELETE_ELEMENTABS_CUTOFF*0.5) {
407 foundJrxn = false;
408 }
409 }
410 if (foundJrxn) {
411 return true;
412 }
413 }
414 }
415 }
416 }
417 return false;
418}
419
421{
422 size_t iphasePop = npos;
423 double FephaseMax = -1.0E30;
424 double Fephase = -1.0E30;
425
426 string note;
427 if (m_debug_print_lvl >= 2) {
428 plogf(" --- vcs_popPhaseID() called\n");
429 plogf(" --- Phase Status F_e MoleNum\n");
430 plogf(" --------------------------------------------------------------------------\n");
431 }
432 for (size_t iph = 0; iph < m_numPhases; iph++) {
433 vcs_VolPhase* Vphase = m_VolPhaseList[iph].get();
434 int existence = Vphase->exists();
435 note = "";
436 if (existence > 0) {
437 if (m_debug_print_lvl >= 2) {
438 plogf(" --- %18s %5d NA %11.3e\n",
439 Vphase->PhaseName, existence, m_tPhaseMoles_old[iph]);
440 }
441 } else {
442 if (Vphase->m_singleSpecies) {
443 // Single Phase Stability Resolution
444 size_t kspec = Vphase->spGlobalIndexVCS(0);
445 size_t irxn = kspec - m_numComponents;
446 if (irxn > m_deltaGRxn_old.size()) {
447 throw CanteraError("VCS_SOLVE::vcs_popPhaseID",
448 "Index out of bounds due to logic error.");
449 }
450 double deltaGRxn = m_deltaGRxn_old[irxn];
451 Fephase = exp(-deltaGRxn) - 1.0;
452 if (Fephase > 0.0) {
453 note = "(ready to be birthed)";
454 if (Fephase > FephaseMax) {
455 iphasePop = iph;
456 FephaseMax = Fephase;
457 note = "(chosen to be birthed)";
458 }
459 }
460 if (Fephase < 0.0) {
461 note = "(not stable)";
463 "VCS_SOLVE::vcs_popPhaseID", "shouldn't be here");
464 }
465
466 if (m_debug_print_lvl >= 2) {
467 plogf(" --- %18s %5d %10.3g %10.3g %s\n",
468 Vphase->PhaseName, existence, Fephase,
469 m_tPhaseMoles_old[iph], note.c_str());
470 }
471 } else if (vcs_popPhasePossible(iph)) {
472 // MultiSpecies Phase Stability Resolution
473 Fephase = vcs_phaseStabilityTest(iph);
474 if (Fephase > 0.0) {
475 if (Fephase > FephaseMax) {
476 iphasePop = iph;
477 FephaseMax = Fephase;
478 }
479 } else {
480 FephaseMax = std::max(FephaseMax, Fephase);
481 }
482 if (m_debug_print_lvl >= 2) {
483 plogf(" --- %18s %5d %11.3g %11.3g\n",
484 Vphase->PhaseName, existence, Fephase,
485 m_tPhaseMoles_old[iph]);
486 }
487 } else if (m_debug_print_lvl >= 2) {
488 plogf(" --- %18s %5d blocked %11.3g\n",
489 Vphase->PhaseName,
490 existence, m_tPhaseMoles_old[iph]);
491 }
492 }
493 }
494 // Insert logic here to figure out if phase pops are linked together. Only
495 // do one linked pop at a time.
496 if (m_debug_print_lvl >= 2) {
497 plogf(" ---------------------------------------------------------------------\n");
498 }
499 return iphasePop;
500}
501
502int VCS_SOLVE::vcs_popPhaseRxnStepSizes(const size_t iphasePop)
503{
504 vcs_VolPhase* Vphase = m_VolPhaseList[iphasePop].get();
505 // Identify the first species in the phase
506 size_t kspec = Vphase->spGlobalIndexVCS(0);
507 // Identify the formation reaction for that species
508 size_t irxn = kspec - m_numComponents;
509 vector<size_t> creationGlobalRxnNumbers(Vphase->nSpecies(), npos);
510
511 // Calculate the initial moles of the phase being born.
512 // Here we set it to 10x of the value which would cause the phase to be
513 // zeroed out within the algorithm. We may later adjust the value.
514 double tPhaseMoles = 10. * m_totalMolNum * VCS_DELETE_PHASE_CUTOFF;
515
516 AssertThrowMsg(!Vphase->exists(), "VCS_SOLVE::vcs_popPhaseRxnStepSizes",
517 "called for a phase that exists!");
518 if (m_debug_print_lvl >= 2) {
519 plogf(" --- vcs_popPhaseRxnStepSizes() called to pop phase %s %d into existence\n",
520 Vphase->PhaseName, iphasePop);
521 }
522 // Section for a single-species phase
523 if (Vphase->m_singleSpecies) {
524 double s = 0.0;
525 for (size_t j = 0; j < m_numComponents; ++j) {
526 if (!m_SSPhase[j] && m_molNumSpecies_old[j] > 0.0) {
527 s += pow(m_stoichCoeffRxnMatrix(j,irxn), 2) / m_molNumSpecies_old[j];
528 }
529 }
530 for (size_t j = 0; j < m_numPhases; j++) {
531 Vphase = m_VolPhaseList[j].get();
532 if (! Vphase->m_singleSpecies && m_tPhaseMoles_old[j] > 0.0) {
533 s -= pow(m_deltaMolNumPhase(j,irxn), 2) / m_tPhaseMoles_old[j];
534 }
535 }
536 if (s != 0.0) {
537 s = vcs_Hessian_diag_adj(irxn, s);
538 m_deltaMolNumSpecies[kspec] = -m_deltaGRxn_new[irxn] / s;
539 } else {
540 // Ok, s is equal to zero. We can not apply a sophisticated theory
541 // to birth the phase. Just pick a small delta and go with it.
542 m_deltaMolNumSpecies[kspec] = tPhaseMoles;
543 }
544
545 // section to do damping of the m_deltaMolNumSpecies[]
546 for (size_t j = 0; j < m_numComponents; ++j) {
547 double stoicC = m_stoichCoeffRxnMatrix(j,irxn);
548 if (stoicC != 0.0 && m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
549 double negChangeComp = - stoicC * m_deltaMolNumSpecies[kspec];
550 if (negChangeComp > m_molNumSpecies_old[j]) {
551 if (m_molNumSpecies_old[j] > 0.0) {
552 m_deltaMolNumSpecies[kspec] = - 0.5 * m_molNumSpecies_old[j] / stoicC;
553 } else {
554 m_deltaMolNumSpecies[kspec] = 0.0;
555 }
556 }
557 }
558 }
559 // Implement a damping term that limits m_deltaMolNumSpecies to the size
560 // of the mole number
561 if (-m_deltaMolNumSpecies[kspec] > m_molNumSpecies_old[kspec]) {
563 }
564 } else {
565 vector<double> fracDelta(Vphase->nSpecies());
566 vector<double> X_est(Vphase->nSpecies());
567 auto storedDelta = Vphase->creationMoleNumbers(creationGlobalRxnNumbers);
568 copy(storedDelta.begin(), storedDelta.end(), fracDelta.begin());
569
570 double sumFrac = 0.0;
571 for (size_t k = 0; k < Vphase->nSpecies(); k++) {
572 sumFrac += fracDelta[k];
573 }
574 for (size_t k = 0; k < Vphase->nSpecies(); k++) {
575 X_est[k] = fracDelta[k] / sumFrac;
576 }
577
578 double deltaMolNumPhase = tPhaseMoles;
579 double damp = 1.0;
581 double* molNumSpecies_tmp = m_deltaGRxn_tmp.data();
582
583 for (size_t k = 0; k < Vphase->nSpecies(); k++) {
584 kspec = Vphase->spGlobalIndexVCS(k);
585 double delmol = deltaMolNumPhase * X_est[k];
586 if (kspec >= m_numComponents) {
587 irxn = kspec - m_numComponents;
588 if (irxn > m_stoichCoeffRxnMatrix.nColumns()) {
589 throw CanteraError("VCS_SOLVE::vcs_popPhaseRxnStepSizes",
590 "Index out of bounds due to logic error.");
591 }
592 for (size_t j = 0; j < m_numComponents; ++j) {
593 double stoicC = m_stoichCoeffRxnMatrix(j,irxn);
594 if (stoicC != 0.0 && m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
595 molNumSpecies_tmp[j] += stoicC * delmol;
596 }
597 }
598 }
599 }
600
601 double ratioComp = 0.0;
602 for (size_t j = 0; j < m_numComponents; ++j) {
603 double deltaJ = m_molNumSpecies_old[j] - molNumSpecies_tmp[j];
604 if (molNumSpecies_tmp[j] < 0.0) {
605 ratioComp = 1.0;
606 if (deltaJ > 0.0) {
607 double delta0 = m_molNumSpecies_old[j];
608 damp = std::min(damp, delta0 / deltaJ * 0.9);
609 }
610 } else {
611 if (m_elType[j] == VCS_ELEM_TYPE_ABSPOS) {
612 size_t jph = m_phaseID[j];
613 if ((jph != iphasePop) && (!m_SSPhase[j])) {
614 double fdeltaJ = fabs(deltaJ);
615 if (m_molNumSpecies_old[j] > 0.0) {
616 ratioComp = std::max(ratioComp, fdeltaJ/ m_molNumSpecies_old[j]);
617 }
618 }
619 }
620 }
621 }
622
623 // We may have greatly underestimated the deltaMoles for the phase pop
624 // Here we create a damp > 1 to account for this possibility. We adjust
625 // upwards to make sure that a component in an existing multispecies
626 // phase is modified by a factor of 1/1000.
627 if (ratioComp > 1.0E-30 && ratioComp < 0.001) {
628 damp = 0.001 / ratioComp;
629 }
630 if (damp <= 1.0E-6) {
631 return 3;
632 }
633
634 for (size_t k = 0; k < Vphase->nSpecies(); k++) {
635 kspec = Vphase->spGlobalIndexVCS(k);
636 if (kspec < m_numComponents) {
638 } else {
639 m_deltaMolNumSpecies[kspec] = deltaMolNumPhase * X_est[k] * damp;
640 m_speciesStatus[kspec] = (X_est[k] > 1.0E-3) ? VCS_SPECIES_MAJOR
642 }
643 }
644 }
645 return 0;
646}
647
648size_t VCS_SOLVE::vcs_RxnStepSizes(int& forceComponentCalc, size_t& kSpecial)
649{
650 size_t iphDel = npos;
651 size_t k = 0;
652 if (m_debug_print_lvl >= 2) {
653 plogf(" ");
654 for (int j = 0; j < 82; j++) {
655 plogf("-");
656 }
657 plogf("\n");
658 plogf(" --- Subroutine vcs_RxnStepSizes called - Details:\n");
659 plogf(" ");
660 for (int j = 0; j < 82; j++) {
661 plogf("-");
662 }
663 plogf("\n");
664 plogf(" --- Species KMoles Rxn_Adjustment DeltaG\n");
665 }
666
667 // We update the matrix dlnActCoeffdmolNumber[][] at the top of the loop,
668 // when necessary
669 if (m_useActCoeffJac) {
671 }
672
673 // LOOP OVER THE FORMATION REACTIONS
674 for (size_t irxn = 0; irxn < m_numRxnRdc; ++irxn) {
675
676 size_t kspec = m_indexRxnToSpecies[irxn];
678 m_deltaMolNumSpecies[kspec] = 0.0;
680 if (m_molNumSpecies_old[kspec] == 0.0 && (!m_SSPhase[kspec])) {
681 // MULTISPECIES PHASE WITH total moles equal to zero
682 //
683 // If dg[irxn] is negative, then the multispecies phase should
684 // come alive again. Add a small positive step size to make it
685 // come alive.
686 if (m_deltaGRxn_new[irxn] < -1.0e-4) {
687 // First decide if this species is part of a multiphase that
688 // is nontrivial in size.
689 size_t iph = m_phaseID[kspec];
690 double tphmoles = m_tPhaseMoles_old[iph];
691 double trphmoles = tphmoles / m_totalMolNum;
692 vcs_VolPhase* Vphase = m_VolPhaseList[iph].get();
693 if (Vphase->exists() && (trphmoles > VCS_DELETE_PHASE_CUTOFF)) {
696 m_deltaMolNumSpecies[kspec] = 0.0;
697 } else {
699 }
700 } else {
701 m_deltaMolNumSpecies[kspec] = 0.0;
702 if (Vphase->exists() > 0 && trphmoles > 0.0) {
704 }
705 }
706 } else {
707 m_deltaMolNumSpecies[kspec] = 0.0;
708 }
709 } else {
710 // REGULAR PROCESSING
711 //
712 // First take care of cases where we want to bail out. Don't
713 // bother if superconvergence has already been achieved in this
714 // mode.
715 if (fabs(m_deltaGRxn_new[irxn]) <= m_tolmaj2) {
716 if (m_debug_print_lvl >= 2) {
717 plogf(" --- %-12.12s", m_speciesName[kspec]);
718 plogf(" %12.4E %12.4E %12.4E\n",
720 m_deltaGRxn_new[irxn]);
721 }
722 continue;
723 }
724
725 // Don't calculate for minor or nonexistent species if their
726 // values are to be decreasing anyway.
727 if ((m_speciesStatus[kspec] != VCS_SPECIES_MAJOR) && (m_deltaGRxn_new[irxn] >= 0.0)) {
728 if (m_debug_print_lvl >= 2) {
729 plogf(" --- %-12.12s", m_speciesName[kspec]);
730 plogf(" %12.4E %12.4E %12.4E\n",
732 m_deltaGRxn_new[irxn]);
733 }
734 continue;
735 }
736
737 // Start of the regular processing
738 double s = (m_SSPhase[kspec]) ? 0.0 : 1.0 / m_molNumSpecies_old[kspec];
739 for (size_t j = 0; j < m_numComponents; ++j) {
740 if (!m_SSPhase[j] && m_molNumSpecies_old[j] > 0.0) {
741 s += pow(m_stoichCoeffRxnMatrix(j,irxn), 2) / m_molNumSpecies_old[j];
742 }
743 }
744 for (size_t j = 0; j < m_numPhases; j++) {
745 vcs_VolPhase* Vphase = m_VolPhaseList[j].get();
746 if (!Vphase->m_singleSpecies && m_tPhaseMoles_old[j] > 0.0) {
747 s -= pow(m_deltaMolNumPhase(j,irxn), 2) / m_tPhaseMoles_old[j];
748 }
749 }
750 if (s != 0.0) {
751 // Take into account of the derivatives of the activity
752 // coefficients with respect to the mole numbers, even in
753 // our diagonal approximation.
754 if (m_useActCoeffJac) {
755 double s_old = s;
756 s = vcs_Hessian_diag_adj(irxn, s_old);
757 }
758
759 m_deltaMolNumSpecies[kspec] = -m_deltaGRxn_new[irxn] / s;
760 // New section to do damping of the m_deltaMolNumSpecies[]
761 for (size_t j = 0; j < m_numComponents; ++j) {
762 double stoicC = m_stoichCoeffRxnMatrix(j,irxn);
763 if (stoicC != 0.0) {
764 double negChangeComp = -stoicC * m_deltaMolNumSpecies[kspec];
765 if (negChangeComp > m_molNumSpecies_old[j]) {
766 if (m_molNumSpecies_old[j] > 0.0) {
767 m_deltaMolNumSpecies[kspec] = -m_molNumSpecies_old[j] / stoicC;
768 } else {
769 m_deltaMolNumSpecies[kspec] = 0.0;
770 }
771 }
772 }
773 }
774 // Implement a damping term that limits m_deltaMolNumSpecies
775 // to the size of the mole number
776 if (-m_deltaMolNumSpecies[kspec] > m_molNumSpecies_old[kspec]) {
778 }
779 } else {
780 // REACTION IS ENTIRELY AMONGST SINGLE SPECIES PHASES.
781 // DELETE ONE OF THE PHASES AND RECOMPUTE BASIS.
782 //
783 // Either the species L will disappear or one of the
784 // component single species phases will disappear. The sign
785 // of DG(I) will indicate which way the reaction will go.
786 // Then, we need to follow the reaction to see which species
787 // will zero out first. The species to be zeroed out will be
788 // "k".
789 double dss;
790 if (m_deltaGRxn_new[irxn] > 0.0) {
791 dss = m_molNumSpecies_old[kspec];
792 k = kspec;
793 for (size_t j = 0; j < m_numComponents; ++j) {
794 if (m_stoichCoeffRxnMatrix(j,irxn) > 0.0) {
795 double xx = m_molNumSpecies_old[j] / m_stoichCoeffRxnMatrix(j,irxn);
796 if (xx < dss) {
797 dss = xx;
798 k = j;
799 }
800 }
801 }
802 dss = -dss;
803 } else {
804 dss = 1.0e10;
805 for (size_t j = 0; j < m_numComponents; ++j) {
806 if (m_stoichCoeffRxnMatrix(j,irxn) < 0.0) {
807 double xx = -m_molNumSpecies_old[j] / m_stoichCoeffRxnMatrix(j,irxn);
808 if (xx < dss) {
809 dss = xx;
810 k = j;
811 }
812 }
813 }
814 }
815
816 // Here we adjust the mole fractions according to DSS and
817 // the stoichiometric array to take into account that we are
818 // eliminating the kth species. DSS contains the amount of
819 // moles of the kth species that needs to be added back into
820 // the component species.
821 if (dss != 0.0) {
822 if ((k == kspec) && (m_SSPhase[kspec] != 1)) {
823 // Found out that we can be in this spot, when
824 // components of multispecies phases are zeroed,
825 // leaving noncomponent species of the same phase
826 // having all of the mole numbers of that phases. it
827 // seems that we can suggest a zero of the species
828 // and the code will recover.
830 if (m_debug_print_lvl >= 2) {
831 plogf(" --- %-12.12s", m_speciesName[kspec]);
832 plogf(" %12.4E %12.4E %12.4E\n",
834 m_deltaGRxn_new[irxn]);
835 }
836 continue;
837 }
838
839 // Delete the single species phase
840 for (size_t j = 0; j < m_nsp; j++) {
841 m_deltaMolNumSpecies[j] = 0.0;
842 }
843 m_deltaMolNumSpecies[kspec] = dss;
844 for (size_t j = 0; j < m_numComponents; ++j) {
846 }
847
848 iphDel = m_phaseID[k];
849 kSpecial = k;
850
851 if (k != kspec) {
852 } else {
853 }
854 if (m_debug_print_lvl >= 2) {
855 plogf(" --- %-12.12s", m_speciesName[kspec]);
856 plogf(" %12.4E %12.4E %12.4E\n",
858 m_deltaGRxn_new[irxn]);
859 plogf(" --- vcs_RxnStepSizes Special section to set up to delete %s\n",
860 m_speciesName[k]);
861 }
862 if (k != kspec) {
863 forceComponentCalc = 1;
864 debuglog(" --- Force a component recalculation\n\n", m_debug_print_lvl >= 2);
865 }
866 if (m_debug_print_lvl >= 2) {
867 plogf(" ");
868 writeline('-', 82);
869 }
870 return iphDel;
871 }
872 }
873 } // End of regular processing
874 if (m_debug_print_lvl >= 2) {
875 plogf(" --- %-12.12s", m_speciesName[kspec]);
876 plogf(" %12.4E %12.4E %12.4E\n",
878 m_deltaGRxn_new[irxn]);
879 }
880 } // End of loop over m_speciesUnknownType
881 } // End of loop over non-component stoichiometric formation reactions
882 if (m_debug_print_lvl >= 2) {
883 plogf(" ");
884 writeline('-', 82);
885 }
886 return iphDel;
887}
888
889double VCS_SOLVE::vcs_phaseStabilityTest(const size_t iph)
890{
891 // We will use the _new state calc here
892 vcs_VolPhase* Vphase = m_VolPhaseList[iph].get();
893 const size_t nsp = Vphase->nSpecies();
894 int minNumberIterations = 3;
895 if (nsp <= 1) {
896 minNumberIterations = 1;
897 }
898
899 // We will do a full Newton calculation later, but for now, ...
900 bool doSuccessiveSubstitution = true;
901 double funcPhaseStability;
902 vector<double> X_est(nsp, 0.0);
903 vector<double> delFrac(nsp, 0.0);
904 vector<double> E_phi(nsp, 0.0);
905 vector<double> fracDelta_old(nsp, 0.0);
906 vector<double> fracDelta_raw(nsp, 0.0);
907 vector<size_t> creationGlobalRxnNumbers(nsp, npos);
909 vector<double> feSpecies_Deficient = m_feSpecies_old;
910
911 // get the activity coefficients
913
914 // Get the stored estimate for the composition of the phase if
915 // it gets created
916 vector<double> fracDelta_new(nsp, 0.0);
917 const auto& storedDelta = Vphase->creationMoleNumbers(creationGlobalRxnNumbers);
918 copy(storedDelta.begin(), storedDelta.end(), fracDelta_new.begin());
919
920 vector<size_t> componentList;
921 for (size_t k = 0; k < nsp; k++) {
922 size_t kspec = Vphase->spGlobalIndexVCS(k);
923 if (kspec < m_numComponents) {
924 componentList.push_back(k);
925 }
926 }
927
928 double normUpdate = 0.1 * vcs_l2norm(fracDelta_new);
929 double damp = 1.0E-2;
930
931 if (doSuccessiveSubstitution) {
932 int KP = 0;
933 if (m_debug_print_lvl >= 2) {
934 plogf(" --- vcs_phaseStabilityTest() called\n");
935 plogf(" --- Its X_old[%2d] FracDel_old[%2d] deltaF[%2d] FracDel_new[%2d]"
936 " normUpdate damp FuncPhaseStability\n", KP, KP, KP, KP);
937 plogf(" --------------------------------------------------------------"
938 "--------------------------------------------------------\n");
939 } else if (m_debug_print_lvl == 1) {
940 plogf(" --- vcs_phaseStabilityTest() called for phase %d\n", iph);
941 }
942
943 for (size_t k = 0; k < nsp; k++) {
944 if (fracDelta_new[k] < 1.0E-13) {
945 fracDelta_new[k] =1.0E-13;
946 }
947 }
948 bool converged = false;
949 double dirProd = 0.0;
950 for (int its = 0; its < 200 && (!converged); its++) {
951 double dampOld = damp;
952 double normUpdateOld = normUpdate;
953 fracDelta_old = fracDelta_new;
954 double dirProdOld = dirProd;
955
956 // Given a set of fracDelta's, we calculate the fracDelta's
957 // for the component species, if any
958 for (size_t i = 0; i < componentList.size(); i++) {
959 size_t kc = componentList[i];
960 size_t kc_spec = Vphase->spGlobalIndexVCS(kc);
961 fracDelta_old[kc] = 0.0;
962 for (size_t k = 0; k < nsp; k++) {
963 size_t kspec = Vphase->spGlobalIndexVCS(k);
964 if (kspec >= m_numComponents) {
965 size_t irxn = kspec - m_numComponents;
966 fracDelta_old[kc] += m_stoichCoeffRxnMatrix(kc_spec,irxn) * fracDelta_old[k];
967 }
968 }
969 }
970
971 // Now, calculate the predicted mole fractions, X_est[k]
972 double sumFrac = 0.0;
973 for (size_t k = 0; k < nsp; k++) {
974 sumFrac += fracDelta_old[k];
975 }
976 // Necessary because this can be identically zero. -> we need to fix
977 // this algorithm!
978 if (sumFrac <= 0.0) {
979 sumFrac = 1.0;
980 }
981 double sum_Xcomp = 0.0;
982 for (size_t k = 0; k < nsp; k++) {
983 X_est[k] = fracDelta_old[k] / sumFrac;
984 if (Vphase->spGlobalIndexVCS(k) < m_numComponents) {
985 sum_Xcomp += X_est[k];
986 }
987 }
988
989 // Feed the newly formed estimate of the mole fractions back into the
990 // ThermoPhase object
992
993 // get the activity coefficients
995
996 // First calculate altered chemical potentials for component species
997 // belonging to this phase.
998 for (size_t i = 0; i < componentList.size(); i++) {
999 size_t kc = componentList[i];
1000 size_t kc_spec = Vphase->spGlobalIndexVCS(kc);
1001 double moleFrac = std::max(X_est[kc], VCS_DELETE_MINORSPECIES_CUTOFF);
1002 feSpecies_Deficient[kc_spec] = m_feSpecies_old[kc_spec]
1003 + log(m_actCoeffSpecies_new[kc_spec] * moleFrac);
1004 }
1005
1006 for (size_t i = 0; i < componentList.size(); i++) {
1007 size_t kc_spec = Vphase->spGlobalIndexVCS(componentList[i]);
1008 for (size_t k = 0; k < Vphase->nSpecies(); k++) {
1009 size_t kspec = Vphase->spGlobalIndexVCS(k);
1010 if (kspec >= m_numComponents) {
1011 size_t irxn = kspec - m_numComponents;
1012 if (i == 0) {
1014 }
1015 if (m_stoichCoeffRxnMatrix(kc_spec,irxn) != 0.0) {
1016 m_deltaGRxn_Deficient[irxn] +=
1017 m_stoichCoeffRxnMatrix(kc_spec,irxn) * (feSpecies_Deficient[kc_spec]- m_feSpecies_old[kc_spec]);
1018 }
1019 }
1020 }
1021 }
1022
1023 // Calculate the E_phi's
1024 double sum = 0.0;
1025 funcPhaseStability = sum_Xcomp - 1.0;
1026 for (size_t k = 0; k < nsp; k++) {
1027 size_t kspec = Vphase->spGlobalIndexVCS(k);
1028 if (kspec >= m_numComponents) {
1029 size_t irxn = kspec - m_numComponents;
1030 double deltaGRxn = clip(m_deltaGRxn_Deficient[irxn], -50.0, 50.0);
1031 E_phi[k] = std::exp(-deltaGRxn) / m_actCoeffSpecies_new[kspec];
1032 sum += E_phi[k];
1033 funcPhaseStability += E_phi[k];
1034 } else {
1035 E_phi[k] = 0.0;
1036 }
1037 }
1038
1039 // Calculate the raw estimate of the new fracs
1040 for (size_t k = 0; k < nsp; k++) {
1041 size_t kspec = Vphase->spGlobalIndexVCS(k);
1042 double b = E_phi[k] / sum * (1.0 - sum_Xcomp);
1043 if (kspec >= m_numComponents) {
1044 fracDelta_raw[k] = b;
1045 }
1046 }
1047
1048 // Given a set of fracDelta's, we calculate the fracDelta's
1049 // for the component species, if any
1050 for (size_t i = 0; i < componentList.size(); i++) {
1051 size_t kc = componentList[i];
1052 size_t kc_spec = Vphase->spGlobalIndexVCS(kc);
1053 fracDelta_raw[kc] = 0.0;
1054 for (size_t k = 0; k < nsp; k++) {
1055 size_t kspec = Vphase->spGlobalIndexVCS(k);
1056 if (kspec >= m_numComponents) {
1057 size_t irxn = kspec - m_numComponents;
1058 fracDelta_raw[kc] += m_stoichCoeffRxnMatrix(kc_spec,irxn) * fracDelta_raw[k];
1059 }
1060 }
1061 }
1062
1063 // Now possibly dampen the estimate.
1064 for (size_t k = 0; k < nsp; k++) {
1065 delFrac[k] = fracDelta_raw[k] - fracDelta_old[k];
1066 }
1067 normUpdate = vcs_l2norm(delFrac);
1068
1069 dirProd = 0.0;
1070 for (size_t k = 0; k < nsp; k++) {
1071 dirProd += fracDelta_old[k] * delFrac[k];
1072 }
1073 bool crossedSign = false;
1074 if (dirProd * dirProdOld < 0.0) {
1075 crossedSign = true;
1076 }
1077
1078 damp = 0.5;
1079 if (dampOld < 0.25) {
1080 damp = 2.0 * dampOld;
1081 }
1082 if (crossedSign) {
1083 if (normUpdate *1.5 > normUpdateOld) {
1084 damp = 0.5 * dampOld;
1085 } else if (normUpdate *2.0 > normUpdateOld) {
1086 damp = 0.8 * dampOld;
1087 }
1088 } else {
1089 if (normUpdate > normUpdateOld * 2.0) {
1090 damp = 0.6 * dampOld;
1091 } else if (normUpdate > normUpdateOld * 1.2) {
1092 damp = 0.9 * dampOld;
1093 }
1094 }
1095
1096 for (size_t k = 0; k < nsp; k++) {
1097 if (fabs(damp * delFrac[k]) > 0.3*fabs(fracDelta_old[k])) {
1098 damp = std::max(0.3*fabs(fracDelta_old[k]) / fabs(delFrac[k]),
1099 1.0E-8/fabs(delFrac[k]));
1100 }
1101 if (delFrac[k] < 0.0 && 2.0 * damp * (-delFrac[k]) > fracDelta_old[k]) {
1102 damp = fracDelta_old[k] / (2.0 * -delFrac[k]);
1103 }
1104 if (delFrac[k] > 0.0 && 2.0 * damp * delFrac[k] > fracDelta_old[k]) {
1105 damp = fracDelta_old[k] / (2.0 * delFrac[k]);
1106 }
1107 }
1108 damp = std::max(damp, 0.000001);
1109 for (size_t k = 0; k < nsp; k++) {
1110 fracDelta_new[k] = fracDelta_old[k] + damp * delFrac[k];
1111 }
1112
1113 if (m_debug_print_lvl >= 2) {
1114 plogf(" --- %3d %12g %12g %12g %12g %12g %12g %12g\n", its, X_est[KP], fracDelta_old[KP],
1115 delFrac[KP], fracDelta_new[KP], normUpdate, damp, funcPhaseStability);
1116 }
1117
1118 if (normUpdate < 1.0E-5 * damp) {
1119 converged = true;
1120 if (its < minNumberIterations) {
1121 converged = false;
1122 }
1123 }
1124 }
1125
1126 if (converged) {
1127 // Save the final optimized stated back into the VolPhase object for later use
1129
1130 // Save fracDelta for later use to initialize the problem better
1131 // @todo creationGlobalRxnNumbers needs to be calculated here and stored.
1132 Vphase->setCreationMoleNumbers(fracDelta_new, creationGlobalRxnNumbers);
1133 }
1134 } else {
1135 throw CanteraError("VCS_SOLVE::vcs_phaseStabilityTest", "not done yet");
1136 }
1137 if (m_debug_print_lvl >= 2) {
1138 plogf(" ------------------------------------------------------------"
1139 "-------------------------------------------------------------\n");
1140 } else if (m_debug_print_lvl == 1) {
1141 if (funcPhaseStability > 0.0) {
1142 plogf(" --- phase %d with func = %g is to be born\n", iph, funcPhaseStability);
1143 } else {
1144 plogf(" --- phase %d with func = %g stays dead\n", iph, funcPhaseStability);
1145 }
1146 }
1147 return funcPhaseStability;
1148}
1149
1150int VCS_SOLVE::vcs_evalSS_TP(int ipr, int ip1, double Temp, double pres)
1151{
1152 for (size_t iph = 0; iph < m_numPhases; iph++) {
1153 vcs_VolPhase* vph = m_VolPhaseList[iph].get();
1156 }
1157 for (size_t k = 0; k < m_nsp; k++) {
1159 }
1160
1161 return VCS_SUCCESS;
1162}
1163
1164void VCS_SOLVE::vcs_prep(int printLvl)
1165{
1166 m_debug_print_lvl = printLvl;
1167
1168 // Calculate the Single Species status of phases
1169 // Also calculate the number of species per phase
1170 vcs_SSPhase();
1171
1172 // Set an initial estimate for the number of noncomponent species equal to
1173 // nspecies - nelements. This may be changed below
1174 m_numRxnTot = (m_nelem > m_nsp) ? 0 : m_nsp - m_nelem;
1177 for (size_t i = 0; i < m_numRxnRdc; ++i) {
1179 }
1180
1181 for (size_t kspec = 0; kspec < m_nsp; ++kspec) {
1182 double sz = 0.0;
1183 for (size_t e = 0; e < m_nelem; e++) {
1184 sz += fabs(m_formulaMatrix(kspec, e));
1185 }
1186 m_spSize[kspec] = (sz > 0.0) ? sz : 1.0;
1187 }
1188
1189 // DETERMINE THE NUMBER OF COMPONENTS
1190 //
1191 // Obtain a valid estimate of the mole fraction. This will be used as an
1192 // initial ordering vector for prioritizing which species are defined as
1193 // components.
1194 //
1195 // If a mole number estimate was supplied from the input file, use that mole
1196 // number estimate.
1197 //
1198 // If a solution estimate wasn't supplied from the input file, supply an
1199 // initial estimate for the mole fractions based on the relative reverse
1200 // ordering of the chemical potentials.
1201 //
1202 // For voltage unknowns, set these to zero for the moment.
1203 double test = -1.0e-10;
1204 bool modifiedSoln = false;
1205 if (m_doEstimateEquil < 0) {
1206 double sum = 0.0;
1207 for (size_t kspec = 0; kspec < m_nsp; ++kspec) {
1209 sum += fabs(m_molNumSpecies_old[kspec]);
1210 }
1211 }
1212 if (fabs(sum) < 1.0E-6) {
1213 modifiedSoln = true;
1214 double pres = (m_pressurePA <= 0.0) ? 1.01325E5 : m_pressurePA;
1215 vcs_evalSS_TP(0, 0, m_temperature, pres);
1216 for (size_t kspec = 0; kspec < m_nsp; ++kspec) {
1218 m_molNumSpecies_old[kspec] = - m_SSfeSpecies[kspec];
1219 } else {
1220 m_molNumSpecies_old[kspec] = 0.0;
1221 }
1222 }
1223 }
1224 test = -1.0e20;
1225 }
1226
1227 // NC = number of components is in the vcs.h common block. This call to
1228 // BASOPT doesn't calculate the stoichiometric reaction matrix.
1229 vcs_basopt(true, test);
1230
1231 if (m_nsp >= m_numComponents) {
1233 for (size_t i = 0; i < m_numRxnRdc; ++i) {
1235 }
1236 } else {
1238 }
1239
1240 // The elements might need to be rearranged.
1242
1243 // If we mucked up the solution unknowns because they were all
1244 // zero to start with, set them back to zero here
1245 if (modifiedSoln) {
1246 for (size_t kspec = 0; kspec < m_nsp; ++kspec) {
1247 m_molNumSpecies_old[kspec] = 0.0;
1248 }
1249 }
1250
1251 // Initialize various arrays in the data to zero
1252 m_feSpecies_old.assign(m_feSpecies_old.size(), 0.0);
1253 m_feSpecies_new.assign(m_feSpecies_new.size(), 0.0);
1254 m_molNumSpecies_new.assign(m_molNumSpecies_new.size(), 0.0);
1257 m_deltaPhaseMoles.assign(m_deltaPhaseMoles.size(), 0.0);
1258 m_tPhaseMoles_new.assign(m_tPhaseMoles_new.size(), 0.0);
1259
1260 // Calculate the total number of moles in all phases.
1261 vcs_tmoles();
1262
1263 // Check to see if the current problem is well posed.
1264 double sum = 0.0;
1265 for (size_t e = 0; e < m_mix->nElements(); e++) {
1266 sum += m_mix->elementMoles(e);
1267 }
1268 if (sum < 1.0E-20) {
1269 throw CanteraError("VCS_SOLVE::vcs_prep", "The problem is not well posed"
1270 " because the total number of element moles is zero.");
1271 }
1272}
1273
1275{
1276 vector<double> awSpace(m_nsp + (m_nelem + 2)*(m_nelem), 0.0);
1277 vector<double> aw(m_nelem), sa(m_nelem), ss(m_nelem);
1278 vector<double> sm(m_nelem*m_nelem);
1279
1280 size_t ncomponents = m_numComponents;
1281 if (m_debug_print_lvl >= 2) {
1282 plogf(" ");
1283 writeline('-', 77);
1284 plogf(" --- Subroutine elem_rearrange() called to ");
1285 plogf("check stoich. coefficient matrix\n");
1286 plogf(" --- and to rearrange the element ordering once\n");
1287 }
1288
1289 // Use a temporary work array for the element numbers
1290 // Also make sure the value of test is unique.
1291 bool lindep = true;
1292 double test = -1.0E10;
1293 while (lindep) {
1294 lindep = false;
1295 for (size_t i = 0; i < m_nelem; ++i) {
1296 test -= 1.0;
1297 aw[i] = m_elemAbundancesGoal[i];
1298 if (test == aw[i]) {
1299 lindep = true;
1300 }
1301 }
1302 }
1303
1304 // Top of a loop of some sort based on the index JR. JR is the current
1305 // number independent elements found.
1306 size_t jr = 0;
1307 while (jr < ncomponents) {
1308 size_t k;
1309
1310 // Top of another loop point based on finding a linearly independent
1311 // species
1312 while (true) {
1313 // Search the remaining part of the mole fraction vector, AW, for
1314 // the largest remaining species. Return its identity in K.
1315 k = m_nelem;
1316 for (size_t ielem = jr; ielem < m_nelem; ielem++) {
1317 if (m_elementActive[ielem] && aw[ielem] != test) {
1318 k = ielem;
1319 break;
1320 }
1321 }
1322 if (k == m_nelem) {
1323 throw CanteraError("VCS_SOLVE::vcs_elem_rearrange",
1324 "Shouldn't be here. Algorithm misfired.");
1325 }
1326
1327 // Assign a large negative number to the element that we have just
1328 // found, in order to take it out of further consideration.
1329 aw[k] = test;
1330
1331 // CHECK LINEAR INDEPENDENCE OF CURRENT FORMULA MATRIX LINE WITH
1332 // PREVIOUS LINES OF THE FORMULA MATRIX
1333 //
1334 // Modified Gram-Schmidt Method, p. 202 Dalquist QR factorization of
1335 // a matrix without row pivoting.
1336 size_t jl = jr;
1337
1338 // Fill in the row for the current element, k, under consideration
1339 // The row will contain the Formula matrix value for that element
1340 // from the current component.
1341 for (size_t j = 0; j < ncomponents; ++j) {
1342 sm[j + jr*ncomponents] = m_formulaMatrix(j,k);
1343 }
1344 if (jl > 0) {
1345 // Compute the coefficients of JA column of the the upper
1346 // triangular R matrix, SS(J) = R_J_JR (this is slightly
1347 // different than Dalquist) R_JA_JA = 1
1348 for (size_t j = 0; j < jl; ++j) {
1349 ss[j] = 0.0;
1350 for (size_t i = 0; i < ncomponents; ++i) {
1351 ss[j] += sm[i + jr*ncomponents] * sm[i + j*ncomponents];
1352 }
1353 ss[j] /= sa[j];
1354 }
1355
1356 // Now make the new column, (*,JR), orthogonal to the previous
1357 // columns
1358 for (size_t j = 0; j < jl; ++j) {
1359 for (size_t i = 0; i < ncomponents; ++i) {
1360 sm[i + jr*ncomponents] -= ss[j] * sm[i + j*ncomponents];
1361 }
1362 }
1363 }
1364
1365 // Find the new length of the new column in Q. It will be used in
1366 // the denominator in future row calcs.
1367 sa[jr] = 0.0;
1368 for (size_t ml = 0; ml < ncomponents; ++ml) {
1369 sa[jr] += pow(sm[ml + jr*ncomponents], 2);
1370 }
1371 // IF NORM OF NEW ROW .LT. 1E-6 REJECT
1372 if (sa[jr] > 1.0e-6) {
1373 break;
1374 }
1375 }
1376 // REARRANGE THE DATA
1377 if (jr != k) {
1378 if (m_debug_print_lvl >= 2) {
1379 plogf(" --- %-2.2s(%9.2g) replaces %-2.2s(%9.2g) as element %3d\n",
1381 m_elementName[jr], m_elemAbundancesGoal[jr], jr);
1382 }
1383 vcs_switch_elem_pos(jr, k);
1384 std::swap(aw[jr], aw[k]);
1385 }
1386
1387 // If we haven't found enough components, go back and find some more.
1388 jr++;
1389 }
1390}
1391
1392void VCS_SOLVE::vcs_switch_elem_pos(size_t ipos, size_t jpos)
1393{
1394 if (ipos == jpos) {
1395 return;
1396 }
1397 AssertThrowMsg(ipos < m_nelem && jpos < m_nelem,
1398 "vcs_switch_elem_pos",
1399 "inappropriate args: {} {}", ipos, jpos);
1400
1401 // Change the element Global Index list in each vcs_VolPhase object
1402 // to reflect the switch in the element positions.
1403 for (size_t iph = 0; iph < m_numPhases; iph++) {
1404 vcs_VolPhase* volPhase = m_VolPhaseList[iph].get();
1405 for (size_t e = 0; e < volPhase->nElemConstraints(); e++) {
1406 if (volPhase->elemGlobalIndex(e) == ipos) {
1407 volPhase->setElemGlobalIndex(e, jpos);
1408 }
1409 if (volPhase->elemGlobalIndex(e) == jpos) {
1410 volPhase->setElemGlobalIndex(e, ipos);
1411 }
1412 }
1413 }
1414 std::swap(m_elemAbundancesGoal[ipos], m_elemAbundancesGoal[jpos]);
1415 std::swap(m_elemAbundances[ipos], m_elemAbundances[jpos]);
1416 std::swap(m_elementMapIndex[ipos], m_elementMapIndex[jpos]);
1417 std::swap(m_elType[ipos], m_elType[jpos]);
1418 std::swap(m_elementActive[ipos], m_elementActive[jpos]);
1419 for (size_t j = 0; j < m_nsp; ++j) {
1420 std::swap(m_formulaMatrix(j,ipos), m_formulaMatrix(j,jpos));
1421 }
1422 std::swap(m_elementName[ipos], m_elementName[jpos]);
1423}
1424
1425double VCS_SOLVE::vcs_Hessian_diag_adj(size_t irxn, double hessianDiag_Ideal)
1426{
1427 double diag = hessianDiag_Ideal;
1428 double hessActCoef = vcs_Hessian_actCoeff_diag(irxn);
1429 if (hessianDiag_Ideal <= 0.0) {
1430 throw CanteraError("VCS_SOLVE::vcs_Hessian_diag_adj",
1431 "We shouldn't be here");
1432 }
1433 if (hessActCoef >= 0.0) {
1434 diag += hessActCoef;
1435 } else if (fabs(hessActCoef) < 0.6666 * hessianDiag_Ideal) {
1436 diag += hessActCoef;
1437 } else {
1438 diag -= 0.6666 * hessianDiag_Ideal;
1439 }
1440 return diag;
1441}
1442
1444{
1445 size_t kspec = m_indexRxnToSpecies[irxn];
1446 size_t kph = m_phaseID[kspec];
1447 double np_kspec = std::max(m_tPhaseMoles_old[kph], 1e-13);
1448 auto sc_irxn = m_stoichCoeffRxnMatrix.col(irxn);
1449
1450 // First the diagonal term of the Jacobian
1451 double s = m_np_dLnActCoeffdMolNum(kspec,kspec) / np_kspec;
1452
1453 // Next, the other terms. Note this only a loop over the components So, it's
1454 // not too expensive to calculate.
1455 for (size_t j = 0; j < m_numComponents; j++) {
1456 if (!m_SSPhase[j]) {
1457 for (size_t k = 0; k < m_numComponents; ++k) {
1458 if (m_phaseID[k] == m_phaseID[j]) {
1459 double np = m_tPhaseMoles_old[m_phaseID[k]];
1460 if (np > 0.0) {
1461 s += sc_irxn[k] * sc_irxn[j] * m_np_dLnActCoeffdMolNum(j,k) / np;
1462 }
1463 }
1464 }
1465 if (kph == m_phaseID[j]) {
1466 s += sc_irxn[j] * (m_np_dLnActCoeffdMolNum(j,kspec) + m_np_dLnActCoeffdMolNum(kspec,j)) / np_kspec;
1467 }
1468 }
1469 }
1470 return s;
1471}
1472
1473void VCS_SOLVE::vcs_CalcLnActCoeffJac(span<const double> moleSpeciesVCS)
1474{
1475 for (auto& Vphase : m_VolPhaseList) {
1476 // We don't need to call single species phases;
1477 if (!Vphase->m_singleSpecies && !Vphase->isIdealSoln()) {
1478 // update the mole numbers
1479 Vphase->setMolesFromVCS(VCS_STATECALC_OLD, moleSpeciesVCS);
1480
1481 // Download the resulting calculation into the full vector. This
1482 // scatter calculation is carried out in the vcs_VolPhase object.
1483 Vphase->sendToVCS_LnActCoeffJac(m_np_dLnActCoeffdMolNum);
1484 }
1485 }
1486}
1487
1489{
1490 // SORT DEPENDENT SPECIES IN DECREASING ORDER OF MOLES
1491 vector<pair<double, size_t>> x_order;
1492 for (size_t i = 0; i < m_nsp; i++) {
1493 x_order.push_back({-m_molNumSpecies_old[i], i});
1494 }
1495 std::sort(x_order.begin() + m_numComponents,
1496 x_order.begin() + m_numSpeciesRdc);
1497
1498 vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
1500
1501 // PRINT OUT RESULTS
1502 plogf("\n\n\n\n");
1503 writeline('-', 80);
1504 writeline('-', 80);
1505 plogf("\t\t VCS_TP REPORT\n");
1506 writeline('-', 80);
1507 writeline('-', 80);
1508 if (iconv < 0) {
1509 plogf(" ERROR: CONVERGENCE CRITERION NOT SATISFIED.\n");
1510 } else if (iconv == 1) {
1511 plogf(" RANGE SPACE ERROR: Equilibrium Found but not all Element Abundances are Satisfied\n");
1512 }
1513
1514 // Calculate some quantities that may need updating
1515 vcs_tmoles();
1516 double totalVolume = 0.0;
1517 for (size_t iphase = 0; iphase < m_numPhases; iphase++) {
1518 vcs_VolPhase* Vphase = m_VolPhaseList[iphase].get();
1521 double Volp = Vphase->sendToVCS_VolPM(m_PMVolumeSpecies);
1522 totalVolume += Volp;
1523 }
1524
1525 plogf("\t\tTemperature = %15.2g Kelvin\n", m_temperature);
1526 plogf("\t\tPressure = %15.5g Pa \n", m_pressurePA);
1527 plogf("\t\ttotal Volume = %15.5g m**3\n", totalVolume);
1528
1529 // TABLE OF SPECIES IN DECREASING MOLE NUMBERS
1530 plogf("\n\n");
1531 writeline('-', 80);
1532 plogf(" Species Equilibrium kmoles ");
1533 plogf("Mole Fraction ChemPot/RT SpecUnkType\n");
1534 writeline('-', 80);
1535 for (size_t i = 0; i < m_numComponents; ++i) {
1536 plogf(" %-12.12s", m_speciesName[i]);
1537 writeline(' ', 13, false);
1538 plogf("%14.7E %14.7E %12.4E", m_molNumSpecies_old[i],
1540 plogf(" %3d", m_speciesUnknownType[i]);
1541 plogf("\n");
1542 }
1543 for (size_t i = m_numComponents; i < m_numSpeciesRdc; ++i) {
1544 size_t j = x_order[i].second;
1545 plogf(" %-12.12s", m_speciesName[j]);
1546 writeline(' ', 13, false);
1547
1549 plogf("%14.7E %14.7E %12.4E", m_molNumSpecies_old[j],
1551 plogf(" KMolNum ");
1553 plogf(" NA %14.7E %12.4E", 1.0, m_feSpecies_old[j]);
1554 plogf(" Voltage = %14.7E", m_molNumSpecies_old[j]);
1555 } else {
1556 throw CanteraError("VCS_SOLVE::vcs_report", "we have a problem");
1557 }
1558 plogf("\n");
1559 }
1560 if (m_numSpeciesRdc != m_nsp) {
1561 plogf("\n SPECIES WITH LESS THAN 1.0E-32 KMOLES:\n\n");
1562 for (size_t kspec = m_numSpeciesRdc; kspec < m_nsp; ++kspec) {
1563 plogf(" %-12.12s", m_speciesName[kspec]);
1564 // Note m_deltaGRxn_new[] stores in kspec slot not irxn slot, after solve
1565 plogf(" %14.7E %14.7E %12.4E",
1566 m_molNumSpecies_old[kspec],
1567 m_molNumSpecies_new[kspec], m_deltaGRxn_new[kspec]);
1569 plogf(" KMol_Num");
1571 plogf(" Voltage");
1572 } else {
1573 plogf(" Unknown");
1574 }
1575 plogf("\n");
1576 }
1577 }
1578 writeline('-', 80);
1579 plogf("\n");
1580
1581 // TABLE OF SPECIES FORMATION REACTIONS
1582 writeline('-', m_numComponents*10 + 45, true, true);
1583 plogf(" |ComponentID|");
1584 for (size_t j = 0; j < m_numComponents; j++) {
1585 plogf(" %3d", j);
1586 }
1587 plogf(" | |\n");
1588 plogf(" | Components|");
1589 for (size_t j = 0; j < m_numComponents; j++) {
1590 plogf(" %10.10s", m_speciesName[j]);
1591 }
1592 plogf(" | |\n");
1593 plogf(" NonComponent | Moles |");
1594 for (size_t j = 0; j < m_numComponents; j++) {
1595 plogf(" %10.3g", m_molNumSpecies_old[j]);
1596 }
1597 plogf(" | DG/RT Rxn |\n");
1598 writeline('-', m_numComponents*10 + 45);
1599 for (size_t irxn = 0; irxn < m_numRxnTot; irxn++) {
1600 size_t kspec = m_indexRxnToSpecies[irxn];
1601 plogf(" %3d ", kspec);
1602 plogf("%-10.10s", m_speciesName[kspec]);
1603 plogf("|%10.3g |", m_molNumSpecies_old[kspec]);
1604 for (size_t j = 0; j < m_numComponents; j++) {
1605 plogf(" %6.2f", m_stoichCoeffRxnMatrix(j,irxn));
1606 }
1607 plogf(" |%10.3g |", m_deltaGRxn_new[irxn]);
1608 plogf("\n");
1609 }
1610 writeline('-', m_numComponents*10 + 45);
1611 plogf("\n");
1612
1613 // TABLE OF PHASE INFORMATION
1614 vector<double> gaTPhase(m_nelem, 0.0);
1615 double totalMoles = 0.0;
1616 double gibbsPhase = 0.0;
1617 double gibbsTotal = 0.0;
1618 plogf("\n\n");
1619 plogf("\n");
1620 writeline('-', m_nelem*10 + 58);
1621 plogf(" | ElementID |");
1622 for (size_t j = 0; j < m_nelem; j++) {
1623 plogf(" %3d", j);
1624 }
1625 plogf(" | |\n");
1626 plogf(" | Element |");
1627 for (size_t j = 0; j < m_nelem; j++) {
1628 plogf(" %10.10s", m_elementName[j]);
1629 }
1630 plogf(" | |\n");
1631 plogf(" PhaseName |KMolTarget |");
1632 for (size_t j = 0; j < m_nelem; j++) {
1633 plogf(" %10.3g", m_elemAbundancesGoal[j]);
1634 }
1635 plogf(" | Gibbs Total |\n");
1636 writeline('-', m_nelem*10 + 58);
1637 for (size_t iphase = 0; iphase < m_numPhases; iphase++) {
1638 plogf(" %3d ", iphase);
1639 vcs_VolPhase* VPhase = m_VolPhaseList[iphase].get();
1640 plogf("%-12.12s |",VPhase->PhaseName);
1641 plogf("%10.3e |", m_tPhaseMoles_old[iphase]);
1642 totalMoles += m_tPhaseMoles_old[iphase];
1643 if (m_tPhaseMoles_old[iphase] != VPhase->totalMoles() &&
1644 !vcs_doubleEqual(m_tPhaseMoles_old[iphase], VPhase->totalMoles())) {
1645 throw CanteraError("VCS_SOLVE::vcs_report", "we have a problem");
1646 }
1647 // Compute the elemental abundances for each element in this phase
1648 for (size_t j = 0; j < m_nelem; ++j) {
1649 double abundance_j = 0.0;
1650 for (size_t i = 0; i < m_nsp; ++i) {
1652 && m_phaseID[i] == iphase)
1653 {
1654 abundance_j += m_formulaMatrix(i,j) * m_molNumSpecies_old[i];
1655 }
1656 }
1657 plogf(" %10.3g", abundance_j);
1658 gaTPhase[j] += abundance_j;
1659 }
1660 gibbsPhase = 0.0;
1661 for (size_t kspec = 0; kspec < m_numSpeciesRdc; ++kspec) {
1662 if (m_phaseID[kspec] == iphase && m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
1663 gibbsPhase += m_molNumSpecies_old[kspec] * m_feSpecies_old[kspec];
1664 }
1665 }
1666 gibbsTotal += gibbsPhase;
1667 plogf(" | %18.11E |\n", gibbsPhase);
1668 }
1669 writeline('-', m_nelem*10 + 58);
1670 plogf(" TOTAL |%10.3e |", totalMoles);
1671 for (size_t j = 0; j < m_nelem; j++) {
1672 plogf(" %10.3g", gaTPhase[j]);
1673 }
1674 plogf(" | %18.11E |\n", gibbsTotal);
1675
1676 writeline('-', m_nelem*10 + 58);
1677 plogf("\n");
1678
1679 // GLOBAL SATISFACTION INFORMATION
1680
1681 // Calculate the total dimensionless Gibbs Free Energy.
1683 plogf("\n\tTotal Dimensionless Gibbs Free Energy = G/RT = %15.7E\n", g);
1684 plogf("\nElemental Abundances (kmol): ");
1685 plogf(" Actual Target Type ElActive\n");
1686 for (size_t i = 0; i < m_nelem; ++i) {
1687 writeline(' ', 26, false);
1688 plogf("%-2.2s", m_elementName[i]);
1689 plogf("%20.12E %20.12E", m_elemAbundances[i], m_elemAbundancesGoal[i]);
1690 plogf(" %3d %3d\n", m_elType[i], m_elementActive[i]);
1691 }
1692 plogf("\n");
1693
1694 // TABLE OF SPECIES CHEM POTS
1695 writeline('-', 93, true, true);
1696 plogf("Chemical Potentials of the Species: (dimensionless)\n");
1697
1698 plogf("\t\t(RT = %g J/kmol)\n", GasConstant * m_temperature);
1699 plogf(" Name TKMoles StandStateChemPot "
1700 " ln(AC) ln(X_i) | F z_i phi | ChemPot | (-lnMnaught)");
1701 plogf("| (MolNum ChemPot)|");
1702 writeline('-', 147, true, true);
1703 for (size_t i = 0; i < m_nsp; ++i) {
1704 size_t j = x_order[i].second;
1705 size_t pid = m_phaseID[j];
1706 plogf(" %-12.12s", m_speciesName[j]);
1707 plogf(" %14.7E ", m_molNumSpecies_old[j]);
1708 plogf("%14.7E ", m_SSfeSpecies[j]);
1709 plogf("%14.7E ", log(m_actCoeffSpecies_old[j]));
1710 double tpmoles = m_tPhaseMoles_old[pid];
1711 double phi = m_phasePhi[pid];
1712 double eContrib = phi * m_chargeSpecies[j] * m_Faraday_dim;
1713 double lx = 0.0;
1715 lx = 0.0;
1716 } else {
1717 if (tpmoles > 0.0 && m_molNumSpecies_old[j] > 0.0) {
1718 double tmp = std::max(VCS_DELETE_MINORSPECIES_CUTOFF, m_molNumSpecies_old[j]);
1719 lx = log(tmp) - log(tpmoles);
1720 } else {
1721 lx = m_feSpecies_old[j] - m_SSfeSpecies[j]
1723 }
1724 }
1725 plogf("%14.7E |", lx);
1726 plogf("%14.7E | ", eContrib);
1727 double tmp = m_SSfeSpecies[j] + log(m_actCoeffSpecies_old[j])
1728 + lx - m_lnMnaughtSpecies[j] + eContrib;
1729 if (fabs(m_feSpecies_old[j] - tmp) > 1.0E-7) {
1730 throw CanteraError("VCS_SOLVE::vcs_report",
1731 "we have a problem - doesn't add up");
1732 }
1733 plogf(" %12.4E |", m_feSpecies_old[j]);
1734 if (m_lnMnaughtSpecies[j] != 0.0) {
1735 plogf("(%11.5E)", - m_lnMnaughtSpecies[j]);
1736 } else {
1737 plogf(" ");
1738 }
1739
1740 plogf("| %20.9E |", m_feSpecies_old[j] * m_molNumSpecies_old[j]);
1741 plogf("\n");
1742 }
1743 for (size_t i = 0; i < 125; i++) {
1744 plogf(" ");
1745 }
1746 plogf(" %20.9E\n", g);
1747 writeline('-', 147);
1748
1749 // TABLE OF SOLUTION COUNTERS
1750 plogf("\n");
1751 plogf("\nCounters: Iterations\n");
1752 plogf(" vcs_basopt: %5d\n", m_VCount->Basis_Opts);
1753 plogf(" vcs_TP: %5d\n", m_VCount->Its);
1754 writeline('-', 80);
1755 writeline('-', 80);
1756}
1757
1759{
1760 for (size_t j = 0; j < m_nelem; ++j) {
1761 m_elemAbundances[j] = 0.0;
1762 for (size_t i = 0; i < m_nsp; ++i) {
1765 }
1766 }
1767 }
1768}
1769
1771{
1772 size_t top = (ibound) ? m_nelem : m_numComponents;
1773 for (size_t i = 0; i < top; ++i) {
1774 // Require 12 digits of accuracy on non-zero constraints.
1775 if (m_elementActive[i] && fabs(m_elemAbundances[i] - m_elemAbundancesGoal[i]) > fabs(m_elemAbundancesGoal[i]) * 1.0e-12) {
1776 // This logic is for charge neutrality condition
1778 m_elemAbundancesGoal[i] != 0.0) {
1779 throw CanteraError("VCS_SOLVE::vcs_elabcheck",
1780 "Problem with charge neutrality condition");
1781 }
1784
1785 // Find out if the constraint is a multisign constraint. If it
1786 // is, then we have to worry about roundoff error in the
1787 // addition of terms. We are limited to 13 digits of finite
1788 // arithmetic accuracy.
1789 bool multisign = false;
1790 for (size_t kspec = 0; kspec < m_nsp; kspec++) {
1791 double eval = m_formulaMatrix(kspec,i);
1792 if (eval < 0.0) {
1793 multisign = true;
1794 }
1795 if (eval != 0.0) {
1796 scale = std::max(scale, fabs(eval * m_molNumSpecies_old[kspec]));
1797 }
1798 }
1799 if (multisign) {
1800 if (fabs(m_elemAbundances[i] - m_elemAbundancesGoal[i]) > 1e-11 * scale) {
1801 return false;
1802 }
1803 } else {
1805 return false;
1806 }
1807 }
1808 } else {
1809 return false;
1810 }
1811 }
1812 }
1813 return true;
1814}
1815
1816int VCS_SOLVE::vcs_elcorr(span<double> aa, span<double> x)
1817{
1818 checkArraySize("VCS_SOLVE::vcs_elcorr[x]", x.size(), m_nelem);
1819 checkArraySize("VCS_SOLVE::vcs_elcorr[aa]", aa.size(), m_nelem * m_nelem);
1820 int retn = 0;
1821
1822 vector<double> ga_save(m_elemAbundances);
1823 if (m_debug_print_lvl >= 2) {
1824 plogf(" --- vcsc_elcorr: Element abundances correction routine");
1825 if (m_nelem != m_numComponents) {
1826 plogf(" (m_numComponents != m_nelem)");
1827 }
1828 plogf("\n");
1829 }
1830
1831 for (size_t i = 0; i < m_nelem; ++i) {
1833 }
1834 double l2before = 0.0;
1835 for (size_t i = 0; i < m_nelem; ++i) {
1836 l2before += x[i] * x[i];
1837 }
1838 l2before = sqrt(l2before/m_nelem);
1839
1840 // Special section to take out single species, single component,
1841 // moles. These are species which have non-zero entries in the
1842 // formula matrix, and no other species have zero values either.
1843 bool changed = false;
1844 for (size_t i = 0; i < m_nelem; ++i) {
1845 int numNonZero = 0;
1846 bool multisign = false;
1847 for (size_t kspec = 0; kspec < m_nsp; kspec++) {
1849 double eval = m_formulaMatrix(kspec,i);
1850 if (eval < 0.0) {
1851 multisign = true;
1852 }
1853 if (eval != 0.0) {
1854 numNonZero++;
1855 }
1856 }
1857 }
1858 if (!multisign) {
1859 if (numNonZero < 2) {
1860 for (size_t kspec = 0; kspec < m_nsp; kspec++) {
1862 double eval = m_formulaMatrix(kspec,i);
1863 if (eval > 0.0) {
1864 m_molNumSpecies_old[kspec] = m_elemAbundancesGoal[i] / eval;
1865 changed = true;
1866 }
1867 }
1868 }
1869 } else {
1870 int numCompNonZero = 0;
1871 size_t compID = npos;
1872 for (size_t kspec = 0; kspec < m_numComponents; kspec++) {
1874 double eval = m_formulaMatrix(kspec,i);
1875 if (eval > 0.0) {
1876 compID = kspec;
1877 numCompNonZero++;
1878 }
1879 }
1880 }
1881 if (numCompNonZero == 1) {
1882 double diff = m_elemAbundancesGoal[i];
1883 for (size_t kspec = m_numComponents; kspec < m_nsp; kspec++) {
1885 double eval = m_formulaMatrix(kspec,i);
1886 diff -= eval * m_molNumSpecies_old[kspec];
1887 }
1888 m_molNumSpecies_old[compID] = std::max(0.0,diff/m_formulaMatrix(compID,i));
1889 changed = true;
1890 }
1891 }
1892 }
1893 }
1894 }
1895 if (changed) {
1896 vcs_elab();
1897 }
1898
1899 // Section to check for maximum bounds errors on all species due to
1900 // elements. This may only be tried on element types which are
1901 // VCS_ELEM_TYPE_ABSPOS. This is because no other species may have a
1902 // negative number of these.
1903 //
1904 // Note, also we can do this over ne, the number of elements, not just the
1905 // number of components.
1906 changed = false;
1907 for (size_t i = 0; i < m_nelem; ++i) {
1908 int elType = m_elType[i];
1909 if (elType == VCS_ELEM_TYPE_ABSPOS) {
1910 for (size_t kspec = 0; kspec < m_nsp; kspec++) {
1912 double atomComp = m_formulaMatrix(kspec,i);
1913 if (atomComp > 0.0) {
1914 double maxPermissible = m_elemAbundancesGoal[i] / atomComp;
1915 if (m_molNumSpecies_old[kspec] > maxPermissible) {
1916 if (m_debug_print_lvl >= 3) {
1917 plogf(" --- vcs_elcorr: Reduced species %s from %g to %g "
1918 "due to %s max bounds constraint\n",
1919 m_speciesName[kspec], m_molNumSpecies_old[kspec],
1920 maxPermissible, m_elementName[i]);
1921 }
1922 m_molNumSpecies_old[kspec] = maxPermissible;
1923 changed = true;
1925 m_molNumSpecies_old[kspec] = 0.0;
1926 if (m_SSPhase[kspec]) {
1928 } else {
1930 }
1931 if (m_debug_print_lvl >= 2) {
1932 plogf(" --- vcs_elcorr: Zeroed species %s and changed "
1933 "status to %d due to max bounds constraint\n",
1934 m_speciesName[kspec], m_speciesStatus[kspec]);
1935 }
1936 }
1937 }
1938 }
1939 }
1940 }
1941 }
1942 }
1943
1944 // Recalculate the element abundances if something has changed.
1945 if (changed) {
1946 vcs_elab();
1947 }
1948
1949 // Ok, do the general case. Linear algebra problem is of length nc, not ne,
1950 // as there may be degenerate rows when nc .ne. ne.
1952 for (size_t i = 0; i < m_numComponents; ++i) {
1954 if (fabs(x[i]) > 1.0E-13) {
1955 retn = 1;
1956 }
1957 for (size_t j = 0; j < m_numComponents; ++j) {
1958 A(j, i) = - m_formulaMatrix(i,j);
1959 }
1960 }
1961
1962 solve(A, x.first(m_nelem), 1, m_nelem);
1963
1964 // Now apply the new direction without creating negative species.
1965 double par = 0.5;
1966 for (size_t i = 0; i < m_numComponents; ++i) {
1967 if (m_molNumSpecies_old[i] > 0.0) {
1968 par = std::max(par, -x[i] / m_molNumSpecies_old[i]);
1969 }
1970 }
1971 par = std::min(par, 100.0);
1972 par = 1.0 / par;
1973 if (par < 1.0 && par > 0.0) {
1974 retn = 2;
1975 par *= 0.9999;
1976 } else {
1977 par = 1.0;
1978 }
1979 for (size_t i = 0; i < m_numComponents; ++i) {
1980 double tmp = m_molNumSpecies_old[i] + par * x[i];
1981 if (tmp > 0.0) {
1982 m_molNumSpecies_old[i] = tmp;
1983 } else if (m_SSPhase[i]) {
1984 m_molNumSpecies_old[i] = 0.0;
1985 } else {
1986 m_molNumSpecies_old[i] *= 0.0001;
1987 }
1988 }
1989
1990 // We have changed the element abundances. Calculate them again
1991 vcs_elab();
1992
1993 // We have changed the total moles in each phase. Calculate them again
1994 vcs_tmoles();
1995
1996 // cleanup/logging function to be called before returning
1997 auto finalize = [&]() {
1998 vcs_tmoles();
1999 double l2after = 0.0;
2000 for (size_t i = 0; i < m_nelem; ++i) {
2001 l2after += pow(m_elemAbundances[i] - m_elemAbundancesGoal[i], 2);
2002 }
2003 l2after = sqrt(l2after/m_nelem);
2004 if (m_debug_print_lvl >= 2) {
2005 plogf(" --- Elem_Abund: Correct Initial "
2006 " Final\n");
2007 for (size_t i = 0; i < m_nelem; ++i) {
2008 plogf(" --- ");
2009 plogf("%-2.2s", m_elementName[i]);
2010 plogf(" %20.12E %20.12E %20.12E\n", m_elemAbundancesGoal[i], ga_save[i], m_elemAbundances[i]);
2011 }
2012 plogf(" --- Diff_Norm: %20.12E %20.12E\n",
2013 l2before, l2after);
2014 }
2015 };
2016
2017 // Try some ad hoc procedures for fixing the problem
2018 if (retn >= 2) {
2019 // First find a species whose adjustment is a win-win situation.
2020 for (size_t kspec = 0; kspec < m_nsp; kspec++) {
2022 continue;
2023 }
2024 double saveDir = 0.0;
2025 bool goodSpec = true;
2026 for (size_t i = 0; i < m_numComponents; ++i) {
2027 double dir = m_formulaMatrix(kspec,i) * (m_elemAbundancesGoal[i] - m_elemAbundances[i]);
2028 if (fabs(dir) > 1.0E-10) {
2029 if (dir > 0.0) {
2030 if (saveDir < 0.0) {
2031 goodSpec = false;
2032 break;
2033 }
2034 } else if (saveDir > 0.0) {
2035 goodSpec = false;
2036 break;
2037 }
2038 saveDir = dir;
2039 } else if (m_formulaMatrix(kspec,i) != 0.0) {
2040 goodSpec = false;
2041 break;
2042 }
2043 }
2044 if (goodSpec) {
2045 int its = 0;
2046 double xx = 0.0;
2047 for (size_t i = 0; i < m_numComponents; ++i) {
2048 if (m_formulaMatrix(kspec,i) != 0.0) {
2049 xx += (m_elemAbundancesGoal[i] - m_elemAbundances[i]) / m_formulaMatrix(kspec,i);
2050 its++;
2051 }
2052 }
2053 if (its > 0) {
2054 xx /= its;
2055 }
2056 m_molNumSpecies_old[kspec] += xx;
2057 m_molNumSpecies_old[kspec] = std::max(m_molNumSpecies_old[kspec], 1.0E-10);
2058
2059 // If we are dealing with a deleted species, then we need to
2060 // reinsert it into the active list.
2061 if (kspec >= m_numSpeciesRdc) {
2062 vcs_reinsert_deleted(kspec);
2064 vcs_elab();
2065 finalize();
2066 return retn;
2067 }
2068 vcs_elab();
2069 }
2070 }
2071 }
2072 if (vcs_elabcheck(0)) {
2073 finalize();
2074 return 1;
2075 }
2076
2077 for (size_t i = 0; i < m_nelem; ++i) {
2079 (m_elType[i] == VCS_ELEM_TYPE_ABSPOS && m_elemAbundancesGoal[i] == 0.0)) {
2080 for (size_t kspec = 0; kspec < m_numSpeciesRdc; kspec++) {
2081 if (m_elemAbundances[i] > 0.0 && m_formulaMatrix(kspec,i) < 0.0) {
2083 m_molNumSpecies_old[kspec] = std::max(m_molNumSpecies_old[kspec], 0.0);
2084 vcs_elab();
2085 break;
2086 }
2087 if (m_elemAbundances[i] < 0.0 && m_formulaMatrix(kspec,i) > 0.0) {
2089 m_molNumSpecies_old[kspec] = std::max(m_molNumSpecies_old[kspec], 0.0);
2090 vcs_elab();
2091 break;
2092 }
2093 }
2094 }
2095 }
2096 if (vcs_elabcheck(1)) {
2097 finalize();
2098 return 1;
2099 }
2100
2101 // For electron charges element types, we try positive deltas in the species
2102 // concentrations to match the desired electron charge exactly.
2103 for (size_t i = 0; i < m_nelem; ++i) {
2104 double dev = m_elemAbundancesGoal[i] - m_elemAbundances[i];
2105 if (m_elType[i] == VCS_ELEM_TYPE_ELECTRONCHARGE && (fabs(dev) > 1.0E-300)) {
2106 bool useZeroed = true;
2107 for (size_t kspec = 0; kspec < m_numSpeciesRdc; kspec++) {
2108 if (dev < 0.0) {
2109 if (m_formulaMatrix(kspec,i) < 0.0 && m_molNumSpecies_old[kspec] > 0.0) {
2110 useZeroed = false;
2111 }
2112 } else {
2113 if (m_formulaMatrix(kspec,i) > 0.0 && m_molNumSpecies_old[kspec] > 0.0) {
2114 useZeroed = false;
2115 }
2116 }
2117 }
2118 for (size_t kspec = 0; kspec < m_numSpeciesRdc; kspec++) {
2119 if (m_molNumSpecies_old[kspec] > 0.0 || useZeroed) {
2120 if (dev < 0.0 && m_formulaMatrix(kspec,i) < 0.0) {
2121 double delta = dev / m_formulaMatrix(kspec,i);
2122 m_molNumSpecies_old[kspec] += delta;
2123 m_molNumSpecies_old[kspec] = std::max(m_molNumSpecies_old[kspec], 0.0);
2124 vcs_elab();
2125 break;
2126 }
2127 if (dev > 0.0 && m_formulaMatrix(kspec,i) > 0.0) {
2128 double delta = dev / m_formulaMatrix(kspec,i);
2129 m_molNumSpecies_old[kspec] += delta;
2130 m_molNumSpecies_old[kspec] = std::max(m_molNumSpecies_old[kspec], 0.0);
2131 vcs_elab();
2132 break;
2133 }
2134 }
2135 }
2136 }
2137 }
2138 if (vcs_elabcheck(1)) {
2139 finalize();
2140 return 1;
2141 }
2142 return retn;
2143}
2144
2146{
2147 double test = -1.0E-10;
2148
2149 if (m_debug_print_lvl >= 2) {
2150 plogf(" --- call setInitialMoles\n");
2151 }
2152
2153 int iter = 0;
2154 bool abundancesOK = true;
2155 vector<double> sm(m_nelem * m_nelem, 0.0);
2156 vector<double> ss(m_nelem, 0.0);
2157 vector<double> sa(m_nelem, 0.0);
2158 vector<double> wx(m_nelem, 0.0);
2159 vector<double> aw(m_nsp, 0.0);
2160
2161 for (size_t ik = 0; ik < m_nsp; ik++) {
2163 m_molNumSpecies_old[ik] = max(0.0, m_molNumSpecies_old[ik]);
2164 }
2165 }
2166
2167 if (m_debug_print_lvl >= 2) {
2169 }
2170
2171 bool redo = true;
2172 while (redo) {
2173 if (!vcs_elabcheck(0)) {
2174 if (m_debug_print_lvl >= 2) {
2175 plogf(" --- seMolesLinProg Mole numbers failing element abundances\n");
2176 plogf(" --- seMolesLinProg Call vcs_elcorr to attempt fix\n");
2177 }
2178 int retn = vcs_elcorr(sm, wx);
2179 abundancesOK = (retn < 2);
2180 } else {
2181 abundancesOK = true;
2182 }
2183
2184 // Now find the optimized basis that spans the stoichiometric
2185 // coefficient matrix, based on the current composition,
2186 // m_molNumSpecies_old[] We also calculate sc[][], the reaction matrix.
2187 vcs_basopt(false, test);
2188
2189 if (m_debug_print_lvl >= 2) {
2190 plogf("iteration %d\n", iter);
2191 }
2192 redo = false;
2193 iter++;
2194 if (iter > 15) {
2195 break;
2196 }
2197
2198 // loop over all reactions
2199 for (size_t irxn = 0; irxn < m_numRxnTot; irxn++) {
2200 // dg_rt is the Delta_G / RT value for the reaction
2201 size_t ik = m_numComponents + irxn;
2202 double dg_rt = m_SSfeSpecies[ik];
2203 double dxi_min = 1.0e10;
2204 auto sc_irxn = m_stoichCoeffRxnMatrix.col(irxn);
2205 for (size_t jcomp = 0; jcomp < m_nelem; jcomp++) {
2206 dg_rt += m_SSfeSpecies[jcomp] * sc_irxn[jcomp];
2207 }
2208 // fwd or rev direction.
2209 // idir > 0 implies increasing the current species
2210 // idir < 0 implies decreasing the current species
2211 int idir = (dg_rt < 0.0 ? 1 : -1);
2212 if (idir < 0) {
2213 dxi_min = m_molNumSpecies_old[ik];
2214 }
2215
2216 for (size_t jcomp = 0; jcomp < m_numComponents; jcomp++) {
2217 double nu = sc_irxn[jcomp];
2218 // set max change in progress variable by
2219 // non-negativity requirement
2220 if (nu*idir < 0) {
2221 double delta_xi = fabs(m_molNumSpecies_old[jcomp]/nu);
2222 // if a component has nearly zero moles, redo
2223 // with a new set of components
2224 if (!redo && delta_xi < 1.0e-10 && (m_molNumSpecies_old[ik] >= 1.0E-10)) {
2225 if (m_debug_print_lvl >= 2) {
2226 plogf(" --- Component too small: %s\n", m_speciesName[jcomp]);
2227 }
2228 redo = true;
2229 }
2230 dxi_min = std::min(dxi_min, delta_xi);
2231 }
2232 }
2233
2234 // step the composition by dxi_min, check against zero, since
2235 // we are zeroing components and species on every step.
2236 // Redo the iteration, if a component went from positive to zero on this step.
2237 double dsLocal = idir*dxi_min;
2238 m_molNumSpecies_old[ik] += dsLocal;
2239 m_molNumSpecies_old[ik] = max(0.0, m_molNumSpecies_old[ik]);
2240 for (size_t jcomp = 0; jcomp < m_numComponents; jcomp++) {
2241 bool full = false;
2242 if (m_molNumSpecies_old[jcomp] > 1.0E-15) {
2243 full = true;
2244 }
2245 m_molNumSpecies_old[jcomp] += sc_irxn[jcomp] * dsLocal;
2246 m_molNumSpecies_old[jcomp] = max(0.0, m_molNumSpecies_old[jcomp]);
2247 if (full && m_molNumSpecies_old[jcomp] < 1.0E-60) {
2248 redo = true;
2249 }
2250 }
2251 }
2252
2253 if (m_debug_print_lvl >= 2) {
2255 }
2256 }
2257
2258 if (m_debug_print_lvl == 1) {
2260 plogf(" --- setInitialMoles end\n");
2261 }
2262 if (!abundancesOK) {
2263 return -1;
2264 } else if (iter > 15) {
2265 return 1;
2266 }
2267 return 0;
2268}
2269
2270double VCS_SOLVE::vcs_Total_Gibbs(span<const double> molesSp,
2271 span<const double> chemPot,
2272 span<const double> tPhMoles)
2273{
2274 double g = 0.0;
2275
2276 for (size_t kspec = 0; kspec < m_numSpeciesRdc; ++kspec) {
2278 g += molesSp[kspec] * chemPot[kspec];
2279 }
2280 }
2281
2282 return g;
2283}
2284
2286{
2287 // Whether we have an estimate or not gets overwritten on
2288 // the call to the equilibrium solver.
2289 m_temperature = m_mix->temperature();
2290 m_pressurePA = m_mix->pressure();
2292
2293 for (size_t iphase = 0; iphase < m_numPhases; iphase++) {
2294 vcs_VolPhase* volPhase = m_VolPhaseList[iphase].get();
2295
2297
2298 // Loop through each species in the current phase
2299 size_t nSpPhase = m_mix->phase(iphase).nSpecies();
2300 if ((nSpPhase == 1) && (volPhase->phiVarIndex() == 0)) {
2302 } else if (volPhase->totalMoles() > 0.0) {
2304 } else {
2306 }
2307 }
2308
2309 // Printout the species information: PhaseID's and mole nums
2310 if (m_printLvl > 1) {
2311 writeline('=', 80, true, true);
2312 writeline('=', 20, false);
2313 plogf(" Cantera_to_vprob: START OF PROBLEM STATEMENT ");
2314 writeline('=', 20);
2315 writeline('=', 80);
2316 plogf("\n");
2317 plogf(" Phase IDs of species\n");
2318 plogf(" species phaseID phaseName ");
2319 plogf(" Initial_Estimated_kMols\n");
2320 for (size_t i = 0; i < m_nsp; i++) {
2321 size_t iphase = m_phaseID[i];
2322 vcs_VolPhase* VolPhase = m_VolPhaseList[iphase].get();
2323 plogf("%16s %5d %16s", m_speciesName[i].c_str(), iphase,
2324 VolPhase->PhaseName.c_str());
2326 plogf(" Volts = %-10.5g\n", m_molNumSpecies_old[i]);
2327 } else {
2328 plogf(" %-10.5g\n", m_molNumSpecies_old[i]);
2329 }
2330 }
2331
2332 // Printout of the Phase structure information
2333 writeline('-', 80, true, true);
2334 plogf(" Information about phases\n");
2335 plogf(" PhaseName PhaseNum SingSpec EqnState NumSpec "
2336 "Tmoles(kmol)\n");
2337
2338 for (size_t iphase = 0; iphase < m_numPhases; iphase++) {
2339 vcs_VolPhase* VolPhase = m_VolPhaseList[iphase].get();
2340 plogf("%16s %8d %8d %16s %8d ", VolPhase->PhaseName.c_str(),
2341 VolPhase->VP_ID_, VolPhase->m_singleSpecies,
2342 VolPhase->eos_name(), VolPhase->nSpecies());
2343 plogf("%16e\n", VolPhase->totalMoles());
2344 }
2345
2346 writeline('=', 80, true, true);
2347 writeline('=', 20, false);
2348 plogf(" Cantera_to_vprob: END OF PROBLEM STATEMENT ");
2349 writeline('=', 20);
2350 writeline('=', 80);
2351 plogf("\n");
2352 }
2353
2354 // m_numRxnTot = number of noncomponents, also equal to the number of
2355 // reactions. Note, it's possible that the number of elements is greater
2356 // than the number of species. In that case set the number of reactions to
2357 // zero.
2358 m_numRxnTot = (m_nelem > m_nsp) ? 0 : m_nsp - m_nelem;
2360}
2361
2363{
2364 const char* pprefix = " --- vcs_inest: ";
2365 if (m_doEstimateEquil > 0) {
2366 // Calculate the elemental abundances
2367 vcs_elab();
2368 if (vcs_elabcheck(0)) {
2369 if (m_debug_print_lvl >= 2) {
2370 plogf("%s Initial guess passed element abundances on input\n", pprefix);
2371 plogf("%s m_doEstimateEquil = 1 so will use the input mole "
2372 "numbers as estimates\n", pprefix);
2373 }
2374 return;
2375 } else if (m_debug_print_lvl >= 2) {
2376 plogf("%s Initial guess failed element abundances on input\n", pprefix);
2377 plogf("%s m_doEstimateEquil = 1 so will discard input "
2378 "mole numbers and find our own estimate\n", pprefix);
2379 }
2380 }
2381
2382 // temporary space for usage in this routine and in subroutines
2383 vector<double> sm(m_nelem*m_nelem, 0.0);
2384 vector<double> ss(m_nelem, 0.0);
2385 vector<double> sa(m_nelem, 0.0);
2386 vector<double> aw(m_nsp + m_nelem, 0.0);
2387
2388 // Go get the estimate of the solution
2389 if (m_debug_print_lvl >= 2) {
2390 plogf("%sGo find an initial estimate for the equilibrium problem\n",
2391 pprefix);
2392 }
2393 double test = -1.0E20;
2394 size_t nrxn = m_numRxnTot;
2395
2396 // CALL ROUTINE TO SOLVE MAX(CC*molNum) SUCH THAT AX*molNum = BB AND
2397 // molNum(I) .GE. 0.0. Note, both of these programs do this.
2399
2400 if (m_debug_print_lvl >= 2) {
2401 plogf("%s Mole Numbers returned from linear programming (vcs_inest initial guess):\n",
2402 pprefix);
2403 plogf("%s SPECIES MOLE_NUMBER -SS_ChemPotential\n", pprefix);
2404 for (size_t kspec = 0; kspec < m_nsp; ++kspec) {
2405 plogf("%s ", pprefix);
2406 plogf("%-12.12s", m_speciesName[kspec]);
2407 plogf(" %15.5g %12.3g\n", m_molNumSpecies_old[kspec], -m_SSfeSpecies[kspec]);
2408 }
2409 plogf("%s Element Abundance Agreement returned from linear "
2410 "programming (vcs_inest initial guess):\n", pprefix);
2411 plogf("%s Element Goal Actual\n", pprefix);
2412 for (size_t j = 0; j < m_nelem; j++) {
2413 if (m_elementActive[j]) {
2414 double tmp = 0.0;
2415 for (size_t kspec = 0; kspec < m_nsp; ++kspec) {
2416 tmp += m_formulaMatrix(kspec,j) * m_molNumSpecies_old[kspec];
2417 }
2418 plogf("%s ", pprefix);
2419 plogf(" %-9.9s", m_elementName[j]);
2420 plogf(" %12.3g %12.3g\n", m_elemAbundancesGoal[j], tmp);
2421 }
2422 }
2423 writelogendl();
2424 }
2425
2426 // Make sure all species have positive definite mole numbers Set voltages to
2427 // zero for now, until we figure out what to do
2428 m_deltaMolNumSpecies.assign(m_deltaMolNumSpecies.size(), 0.0);
2429 for (size_t kspec = 0; kspec < m_nsp; ++kspec) {
2431 if (m_molNumSpecies_old[kspec] <= 0.0) {
2432 // HKM Should eventually include logic here for non SS phases
2433 if (!m_SSPhase[kspec]) {
2434 m_molNumSpecies_old[kspec] = 1.0e-30;
2435 }
2436 }
2437 } else {
2438 m_molNumSpecies_old[kspec] = 0.0;
2439 }
2440 }
2441
2442 // Now find the optimized basis that spans the stoichiometric coefficient matrix
2443 vcs_basopt(false, test);
2444
2445 // CALCULATE TOTAL MOLES, CHEMICAL POTENTIALS OF BASIS
2446
2447 // Calculate TMoles and m_tPhaseMoles_old[]
2448 vcs_tmoles();
2449
2450 // m_tPhaseMoles_new[] will consist of just the component moles
2451 m_tPhaseMoles_new.assign(m_numPhases, 1.0e-20);
2452 for (size_t kspec = 0; kspec < m_numComponents; ++kspec) {
2455 }
2456 }
2457 double TMolesMultiphase = 0.0;
2458 for (size_t iph = 0; iph < m_numPhases; iph++) {
2459 if (! m_VolPhaseList[iph]->m_singleSpecies) {
2460 TMolesMultiphase += m_tPhaseMoles_new[iph];
2461 }
2462 }
2464 for (size_t kspec = 0; kspec < m_numComponents; ++kspec) {
2466 m_molNumSpecies_new[kspec] = 0.0;
2467 }
2468 }
2470
2471 for (size_t kspec = 0; kspec < m_numComponents; ++kspec) {
2473 if (! m_SSPhase[kspec]) {
2474 size_t iph = m_phaseID[kspec];
2475 m_feSpecies_new[kspec] += log(m_molNumSpecies_new[kspec] / m_tPhaseMoles_old[iph]);
2476 }
2477 } else {
2478 m_molNumSpecies_new[kspec] = 0.0;
2479 }
2480 }
2481 vcs_deltag(0, true, VCS_STATECALC_NEW);
2482 if (m_debug_print_lvl >= 2) {
2483 for (size_t kspec = 0; kspec < m_nsp; ++kspec) {
2484 plogf("%s", pprefix);
2485 plogf("%-12.12s", m_speciesName[kspec]);
2486 if (kspec < m_numComponents) {
2487 plogf("fe* = %15.5g ff = %15.5g\n", m_feSpecies_new[kspec],
2488 m_SSfeSpecies[kspec]);
2489 } else {
2490 plogf("fe* = %15.5g ff = %15.5g dg* = %15.5g\n",
2492 }
2493 }
2494 }
2495
2496 // ESTIMATE REACTION ADJUSTMENTS
2497 vector<double>& xtphMax = m_TmpPhase;
2498 vector<double>& xtphMin = m_TmpPhase2;
2499 m_deltaPhaseMoles.assign(m_deltaPhaseMoles.size(), 0.0);
2500 for (size_t iph = 0; iph < m_numPhases; iph++) {
2501 xtphMax[iph] = log(m_tPhaseMoles_new[iph] * 1.0E32);
2502 xtphMin[iph] = log(m_tPhaseMoles_new[iph] * 1.0E-32);
2503 }
2504 for (size_t irxn = 0; irxn < nrxn; ++irxn) {
2505 size_t kspec = m_indexRxnToSpecies[irxn];
2506
2507 // For single species phases, we will not estimate the mole numbers. If
2508 // the phase exists, it stays. If it doesn't exist in the estimate, it
2509 // doesn't come into existence here.
2510 if (! m_SSPhase[kspec]) {
2511 size_t iph = m_phaseID[kspec];
2512 if (m_deltaGRxn_new[irxn] > xtphMax[iph]) {
2513 m_deltaGRxn_new[irxn] = 0.8 * xtphMax[iph];
2514 }
2515 if (m_deltaGRxn_new[irxn] < xtphMin[iph]) {
2516 m_deltaGRxn_new[irxn] = 0.8 * xtphMin[iph];
2517 }
2518
2519 // HKM -> The TMolesMultiphase is a change of mine. It more evenly
2520 // distributes the initial moles amongst multiple multispecies
2521 // phases according to the relative values of the standard state
2522 // free energies. There is no change for problems with one
2523 // multispecies phase. It cut diamond4.vin iterations down from 62
2524 // to 14.
2525 m_deltaMolNumSpecies[kspec] = 0.5 * (m_tPhaseMoles_new[iph] + TMolesMultiphase)
2526 * exp(-m_deltaGRxn_new[irxn]);
2527
2528 for (size_t k = 0; k < m_numComponents; ++k) {
2530 }
2531
2532 for (iph = 0; iph < m_numPhases; iph++) {
2534 }
2535 }
2536 }
2537 if (m_debug_print_lvl >= 2) {
2538 for (size_t kspec = 0; kspec < m_nsp; ++kspec) {
2540 plogf("%sdirection (", pprefix);
2541 plogf("%-12.12s", m_speciesName[kspec]);
2542 plogf(") = %g", m_deltaMolNumSpecies[kspec]);
2543 if (m_SSPhase[kspec]) {
2544 if (m_molNumSpecies_old[kspec] > 0.0) {
2545 plogf(" (ssPhase exists at w = %g moles)", m_molNumSpecies_old[kspec]);
2546 } else {
2547 plogf(" (ssPhase doesn't exist -> stability not checked)");
2548 }
2549 }
2550 writelogendl();
2551 }
2552 }
2553 }
2554
2555 // KEEP COMPONENT SPECIES POSITIVE
2556 double par = 0.5;
2557 for (size_t kspec = 0; kspec < m_numComponents; ++kspec) {
2559 par < -m_deltaMolNumSpecies[kspec] / m_molNumSpecies_new[kspec]) {
2560 par = -m_deltaMolNumSpecies[kspec] / m_molNumSpecies_new[kspec];
2561 }
2562 }
2563 par = 1. / par;
2564 if (par <= 1.0 && par > 0.0) {
2565 par *= 0.8;
2566 } else {
2567 par = 1.0;
2568 }
2569
2570 // CALCULATE NEW MOLE NUMBERS
2571 size_t lt = 0;
2572 size_t ikl = 0;
2573 double s1 = 0.0;
2574 while (true) {
2575 for (size_t kspec = 0; kspec < m_numComponents; ++kspec) {
2577 m_molNumSpecies_old[kspec] = m_molNumSpecies_new[kspec] + par * m_deltaMolNumSpecies[kspec];
2578 } else {
2579 m_deltaMolNumSpecies[kspec] = 0.0;
2580 }
2581 }
2582 for (size_t kspec = m_numComponents; kspec < m_nsp; ++kspec) {
2584 m_deltaMolNumSpecies[kspec] != 0.0) {
2585 m_molNumSpecies_old[kspec] = m_deltaMolNumSpecies[kspec] * par;
2586 }
2587 }
2588
2589 // We have a new w[] estimate, go get the TMoles and m_tPhaseMoles_old[]
2590 // values
2591 vcs_tmoles();
2592 if (lt > 0) {
2593 break;
2594 }
2595
2596 // CONVERGENCE FORCING SECTION
2597 vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD);
2599 double s = 0.0;
2600 for (size_t kspec = 0; kspec < m_nsp; ++kspec) {
2601 s += m_deltaMolNumSpecies[kspec] * m_feSpecies_old[kspec];
2602 }
2603 if (s == 0.0) {
2604 break;
2605 }
2606 if (s < 0.0 && ikl == 0) {
2607 break;
2608 }
2609
2610 // TRY HALF STEP SIZE
2611 if (ikl == 0) {
2612 s1 = s;
2613 par *= 0.5;
2614 ikl = 1;
2615 continue;
2616 }
2617
2618 // FIT PARABOLA THROUGH HALF AND FULL STEPS
2619 double xl = (1.0 - s / (s1 - s)) * 0.5;
2620 if (xl < 0.0) {
2621 // POOR DIRECTION, REDUCE STEP SIZE TO 0.2
2622 par *= 0.2;
2623 } else {
2624 if (xl > 1.0) {
2625 // TOO BIG A STEP, TAKE ORIGINAL FULL STEP
2626 par *= 2.0;
2627 } else {
2628 // ACCEPT RESULTS OF FORCER
2629 par = par * 2.0 * xl;
2630 }
2631 }
2632 lt = 1;
2633 }
2634
2635 if (m_debug_print_lvl >= 2) {
2636 plogf("%s Final Mole Numbers produced by inest:\n",
2637 pprefix);
2638 plogf("%s SPECIES MOLE_NUMBER\n", pprefix);
2639 for (size_t kspec = 0; kspec < m_nsp; ++kspec) {
2640 plogf("%s %-12.12s %g\n",
2641 pprefix, m_speciesName[kspec], m_molNumSpecies_old[kspec]);
2642 }
2643 }
2644
2645 // Calculate the elemental abundances
2646 vcs_elab();
2647
2648 // If we still fail to achieve the correct elemental abundances, try to fix
2649 // the problem again by calling the main elemental abundances fixer routine,
2650 // used in the main program. This attempts to tweak the mole numbers of the
2651 // component species to satisfy the element abundance constraints.
2652 //
2653 // Note: We won't do this unless we have to since it involves inverting a
2654 // matrix.
2655 bool rangeCheck = vcs_elabcheck(1);
2656 if (!vcs_elabcheck(0)) {
2657 if (m_debug_print_lvl >= 2) {
2658 plogf("%sInitial guess failed element abundances\n", pprefix);
2659 plogf("%sCall vcs_elcorr to attempt fix\n", pprefix);
2660 }
2661 vcs_elcorr(sm, aw);
2662 rangeCheck = vcs_elabcheck(1);
2663 if (!vcs_elabcheck(0)) {
2664 throw CanteraError("VCS_SOLVE::vcs_inest",
2665 "Initial guess still fails element abundance equations\n"
2666 "Inability to ever satisfy element abundance constraints is probable");
2667 } else {
2668 if (m_debug_print_lvl >= 2) {
2669 if (rangeCheck) {
2670 plogf("%sInitial guess now satisfies element abundances\n", pprefix);
2671 } else {
2672 plogf("%sElement Abundances RANGE ERROR\n", pprefix);
2673 plogf("%s - Initial guess satisfies NC=%d element abundances, "
2674 "BUT not NE=%d element abundances\n", pprefix,
2676 }
2677 }
2678 }
2679 } else {
2680 if (m_debug_print_lvl >= 2) {
2681 if (rangeCheck) {
2682 plogf("%sInitial guess satisfies element abundances\n", pprefix);
2683 } else {
2684 plogf("%sElement Abundances RANGE ERROR\n", pprefix);
2685 plogf("%s - Initial guess satisfies NC=%d element abundances, "
2686 "BUT not NE=%d element abundances\n", pprefix,
2688 }
2689 }
2690 }
2691
2692 if (m_debug_print_lvl >= 2) {
2693 plogf("%sTotal Dimensionless Gibbs Free Energy = %15.7E\n", pprefix,
2695 }
2696
2698}
2699
2701{
2702 vector<int> numPhSpecies(m_numPhases, 0);
2703 for (size_t kspec = 0; kspec < m_nsp; ++kspec) {
2704 numPhSpecies[m_phaseID[kspec]]++;
2705 }
2706
2707 // Handle the special case of a single species in a phase that has been
2708 // earmarked as a multispecies phase. Treat that species as a single-species
2709 // phase
2710 for (size_t iph = 0; iph < m_numPhases; iph++) {
2711 m_VolPhaseList[iph]->m_singleSpecies = (numPhSpecies[iph] <= 1);
2712 }
2713
2714 // Fill in some useful arrays here that have to do with the static
2715 // information concerning the phase ID of species. SSPhase = Boolean
2716 // indicating whether a species is in a single species phase or not.
2717 for (size_t kspec = 0; kspec < m_nsp; kspec++) {
2718 m_SSPhase[kspec] = m_VolPhaseList[m_phaseID[kspec]]->m_singleSpecies;
2719 }
2720}
2721
2723{
2724 m_VCount->Its = 0;
2725 m_VCount->Basis_Opts = 0;
2726 if (ifunc) {
2727 m_VCount->T_Its = 0;
2731 }
2732}
2733
2735{
2736 plogf("\nTCounters: Num_Calls Total_Its\n");
2737 plogf(" vcs_basopt: %5d %5d\n",
2739 plogf(" vcs_TP: %5d %5d\n",
2741 plogf(" vcs_inest: %5d\n", m_VCount->T_Calls_Inest);
2742}
2743
2744void VCS_SOLVE::prob_report(int print_lvl)
2745{
2746 m_printLvl = print_lvl;
2747
2748 // Printout the species information: PhaseID's and mole nums
2749 if (m_printLvl > 0) {
2750 writeline('=', 80, true, true);
2751 writeline('=', 20, false);
2752 plogf(" VCS_PROB: PROBLEM STATEMENT ");
2753 writeline('=', 31);
2754 writeline('=', 80);
2755 plogf("\n");
2756 plogf("\tSolve a constant T, P problem:\n");
2757 plogf("\t\tT = %g K\n", m_temperature);
2758 double pres_atm = m_pressurePA / 1.01325E5;
2759
2760 plogf("\t\tPres = %g atm\n", pres_atm);
2761 plogf("\n");
2762 plogf(" Phase IDs of species\n");
2763 plogf(" species phaseID phaseName ");
2764 plogf(" Initial_Estimated_Moles Species_Type\n");
2765 for (size_t i = 0; i < m_nsp; i++) {
2766 vcs_VolPhase* Vphase = m_VolPhaseList[m_phaseID[i]].get();
2767 plogf("%16s %5d %16s", m_mix->speciesName(i), m_phaseID[i],
2768 Vphase->PhaseName);
2769 if (m_doEstimateEquil >= 0) {
2770 plogf(" %-10.5g", m_molNumSpecies_old[i]);
2771 } else {
2772 plogf(" N/A");
2773 }
2775 plogf(" Mol_Num");
2777 plogf(" Voltage");
2778 } else {
2779 plogf(" ");
2780 }
2781 plogf("\n");
2782 }
2783
2784 // Printout of the Phase structure information
2785 writeline('-', 80, true, true);
2786 plogf(" Information about phases\n");
2787 plogf(" PhaseName PhaseNum SingSpec EqnState NumSpec "
2788 "Tmoles(kmol)\n");
2789
2790 for (size_t iphase = 0; iphase < m_numPhases; iphase++) {
2791 vcs_VolPhase* Vphase = m_VolPhaseList[iphase].get();
2792 plogf("%16s %8d %8d ", Vphase->PhaseName,
2793 Vphase->VP_ID_, Vphase->m_singleSpecies);
2794 plogf("%16s %8d ", Vphase->eos_name(),
2795 Vphase->nSpecies());
2796 if (m_doEstimateEquil >= 0) {
2797 plogf("%16e\n", Vphase->totalMoles());
2798 } else {
2799 plogf(" N/A\n");
2800 }
2801 }
2802
2803 plogf("\nElemental Abundances: ");
2804 plogf(" Target_kmol ElemType ElActive\n");
2805 for (size_t i = 0; i < m_nelem; ++i) {
2806 writeline(' ', 26, false);
2807 plogf("%-2.2s", m_elementName[i]);
2808 plogf("%20.12E ", m_elemAbundancesGoal[i]);
2809 plogf("%3d %3d\n", m_elType[i], m_elementActive[i]);
2810 }
2811
2812 plogf("\nChemical Potentials: (J/kmol)\n");
2813 plogf(" Species (phase) "
2814 " SS0ChemPot StarChemPot\n");
2815 for (size_t iphase = 0; iphase < m_numPhases; iphase++) {
2816 vcs_VolPhase* Vphase = m_VolPhaseList[iphase].get();
2818 for (size_t kindex = 0; kindex < Vphase->nSpecies(); kindex++) {
2819 size_t kglob = Vphase->spGlobalIndexVCS(kindex);
2820 plogf("%16s ", m_mix->speciesName(kglob));
2821 if (kindex == 0) {
2822 plogf("%16s", Vphase->PhaseName);
2823 } else {
2824 plogf(" ");
2825 }
2826
2827 plogf("%16g %16g\n", Vphase->G0_calc_one(kindex),
2828 Vphase->GStar_calc_one(kindex));
2829 }
2830 }
2831 writeline('=', 80, true, true);
2832 writeline('=', 20, false);
2833 plogf(" VCS_PROB: END OF PROBLEM STATEMENT ");
2834 writeline('=', 24);
2835 writeline('=', 80);
2836 plogf("\n");
2837 }
2838}
2839
2840size_t VCS_SOLVE::addOnePhaseSpecies(vcs_VolPhase* volPhase, size_t k, size_t kT)
2841{
2842 if (kT > m_nsp) {
2843 // Need to expand the number of species here
2844 throw CanteraError("VCS_SOLVE::addOnePhaseSpecies", "Shouldn't be here");
2845 }
2846 const Array2D& fm = volPhase->getFormulaMatrix();
2847 for (size_t eVP = 0; eVP < volPhase->nElemConstraints(); eVP++) {
2848 size_t e = volPhase->elemGlobalIndex(eVP);
2849 AssertThrowMsg(e != npos, "VCS_PROB::addOnePhaseSpecies",
2850 "element not found");
2851 m_formulaMatrix(kT,e) = fm(k,eVP);
2852 }
2853
2854 // Tell the phase object about the current position of the species within
2855 // the global species vector
2856 volPhase->setSpGlobalIndexVCS(k, kT);
2857 return kT;
2858}
2859
2860size_t VCS_SOLVE::addElement(const char* elNameNew, int elType, int elactive)
2861{
2862 if (!elNameNew) {
2863 throw CanteraError("VCS_SOLVE::addElement",
2864 "error: element must have a name");
2865 }
2866 m_nelem++;
2868
2871 m_elType.push_back(elType);
2872 m_elementActive.push_back(elactive);
2873 m_elemAbundances.push_back(0.0);
2874 m_elemAbundancesGoal.push_back(0.0);
2875 m_elementMapIndex.push_back(0);
2876 m_elementName.push_back(elNameNew);
2877 return m_nelem - 1;
2878}
2879
2880size_t VCS_SOLVE::elementIndex(const string& elementName) const
2881{
2882 for (size_t e = 0; e < m_nelem; e++) {
2883 if (m_elementName[e] == elementName) {
2884 return e;
2885 }
2886 }
2887 return npos;
2888}
2889
2890}
Headers for the MultiPhase object that is used to set up multiphase equilibrium problems (see Chemica...
Header file for class ThermoPhase, the base class for phases with thermodynamic properties,...
A class for 2D arrays stored in column-major (Fortran-compatible) form.
Definition Array.h:32
void zero()
Set all of the entries to zero.
Definition Array.h:118
size_t nColumns() const
Number of columns.
Definition Array.h:172
span< double > col(size_t j)
Return a writable span over column j.
Definition Array.h:189
virtual void resize(size_t n, size_t m, double v=0.0)
Resize the array, and fill the new entries with 'v'.
Definition Array.cpp:52
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
A class for multiphase mixtures.
Definition MultiPhase.h:62
double speciesMoles(size_t kGlob) const
Returns the moles of global species k. units = kmol.
double pressure() const
Pressure [Pa].
Definition MultiPhase.h:352
double temperature() const
Temperature [K].
Definition MultiPhase.h:308
string speciesName(size_t kGlob) const
Name of species with global index kGlob.
size_t nElements() const
Number of elements.
Definition MultiPhase.h:86
ThermoPhase & phase(size_t n)
Return a reference to phase n.
double elementMoles(size_t m) const
Total moles of global element m, summed over all phases.
size_t nSpecies() const
Returns the number of species in the phase.
Definition Phase.h:246
size_t nDim() const
Returns the number of spatial dimensions (1, 2, or 3)
Definition Phase.h:569
double molecularWeight(size_t k) const
Molecular weight of species k.
Definition Phase.cpp:388
double charge(size_t k) const
Dimensionless electrical charge of a single molecule of species k The charge is normalized by the the...
Definition Phase.h:561
Base class for a phase with thermodynamic properties.
double electricPotential() const
Returns the electric potential of this phase (V).
Class to keep track of time and iterations.
int Its
Current number of iterations in the main loop of vcs_TP() to solve for thermo equilibrium.
int T_Calls_Inest
Current number of times the initial thermo equilibrium estimator has been called.
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
VCS_SOLVE(MultiPhase *mphase, int printLvl=0)
Initialize the sizes within the VCS_SOLVE object.
Definition vcs_solve.cpp:40
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
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_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
void vcs_CalcLnActCoeffJac(span< const double > moleSpeciesVCS)
Recalculate all of the activity coefficients in all of the phases based on input mole numbers.
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_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
double vcs_phaseStabilityTest(const size_t iph)
Main program to test whether a deleted phase should be brought back into existence.
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
bool vcs_popPhasePossible(const size_t iphasePop) const
Utility function that evaluates whether a phase can be popped into existence.
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.
int vcs_setMolesLinProg()
Estimate the initial mole numbers by constrained linear programming.
vector< double > m_molNumSpecies_old
Total moles of the species.
Definition vcs_solve.h:959
vector< size_t > m_elementMapIndex
Index vector that keeps track of the rearrangement of the elements.
Definition vcs_solve.h:1132
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
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
size_t addOnePhaseSpecies(vcs_VolPhase *volPhase, size_t k, size_t kT)
Add elements to the local element list This routines adds entries for the formula matrix for one spec...
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
double vcs_Hessian_diag_adj(size_t irxn, double hessianDiag_Ideal)
Calculates the diagonal contribution to the Hessian due to the dependence of the activity coefficient...
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.
double m_pressurePA
Pressure.
Definition vcs_solve.h:1079
void vcs_SSPhase()
Calculate the status of single species phases.
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
void vcs_elem_rearrange()
Rearrange the constraint equations represented by the Formula Matrix so that the operational ones are...
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
vector< double > m_phasePhi
electric potential of the iph phase
Definition vcs_solve.h:986
double vcs_Hessian_actCoeff_diag(size_t irxn)
Calculates the diagonal contribution to the Hessian due to the dependence of the activity coefficient...
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_phaseActConvention
specifies the activity convention of the phase.
Definition vcs_solve.h:1208
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
vector< double > m_TmpPhase2
Temporary vector of length NPhase.
Definition vcs_solve.h:1067
void vcs_switch_elem_pos(size_t ipos, size_t jpos)
Swaps the indices for all of the global data for two elements, ipos and jpos.
double vcs_tmoles()
Calculates the total number of moles of species in all phases.
size_t elementIndex(const string &elementName) const
Return the index of an element by name, or npos if not found.
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
vector< double > m_deltaGRxn_old
Last deltag[irxn] from the previous step.
Definition vcs_solve.h:1003
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.
size_t addElement(const char *elNameNew, int elType, int elactive)
This routine resizes the number of elements in the VCS_SOLVE object by adding a new element to the en...
double m_totalMolNum
Total number of kmoles in all phases.
Definition vcs_solve.h:1044
Phase information and Phase calculations for vcs.
int speciesUnknownType(const size_t k) const
Returns the type of the species unknown.
size_t elemGlobalIndex(const size_t e) const
Returns the global index of the local element index for the phase.
void setMolesFromVCS(const int stateCalc, span< const double > molesSpeciesVCS={})
Set the moles within the phase.
size_t nSpecies() const
Return the number of species in the phase.
string PhaseName
String name for the phase.
size_t VP_ID_
Original ID of the phase in the problem.
const Array2D & getFormulaMatrix() const
Get a constant form of the Species Formula Matrix.
void sendToVCS_ActCoeff(const int stateCalc, span< double > AC)
Fill in an activity coefficients vector within a VCS_SOLVE object.
size_t phiVarIndex() const
Return the index of the species that represents the the voltage of the phase.
bool m_singleSpecies
If true, this phase consists of a single species.
void setElemGlobalIndex(const size_t eLocal, const size_t eGlobal)
sets a local phase element to a global index value
int exists() const
Retrieve the kth Species structure for the species belonging to this phase.
size_t nElemConstraints() const
Returns the number of element constraints.
size_t spGlobalIndexVCS(const size_t spIndex) const
Return the Global VCS index of the kth species in the phase.
double GStar_calc_one(size_t kspec) const
Gibbs free energy calculation for standard state of one species.
int p_activityConvention
Convention for the activity formulation.
void setCreationMoleNumbers(span< const double > n_k, span< const size_t > creationGlobalRxnNumbers)
Sets the creationMoleNum's within the phase object.
span< const double > creationMoleNumbers(span< size_t > creationGlobalRxnNumbers) const
Return a const reference to the creationMoleNumbers stored in the object.
double G0_calc_one(size_t kspec) const
Gibbs free energy calculation at a temperature for the reference state of a species,...
void setState_TP(const double temperature_Kelvin, const double pressure_PA)
Sets the temperature and pressure in this object and underlying ThermoPhase objects.
string eos_name() const
Return the name corresponding to the equation of state.
void setMoleFractionsState(const double molNum, span< const double > moleFracVec, const int vcsStateStatus)
Set the moles and/or mole fractions within the phase.
double sendToVCS_VolPM(span< double > VolPM) const
Fill in the partial molar volume vector for VCS.
void sendToVCS_GStar(span< double > gstar) const
Fill in the standard state Gibbs free energy vector for VCS.
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.
double totalMoles() const
Return the total moles in the phase.
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 writelogendl()
Write an end of line character to the screen and flush output.
Definition global.cpp:41
void scale(InputIter begin, InputIter end, OutputIter out, S scale_factor)
Multiply elements of an array by a scale factor.
Definition utilities.h:118
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.
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
double vcs_l2norm(span< const double > vec)
determine the l2 norm of a vector of doubles
Definition vcs_util.cpp:19
void checkArraySize(const char *procedure, size_t available, size_t required)
Wrapper for throwing ArraySizeError.
Contains const definitions for types of species reference-state thermodynamics managers (see Species ...
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_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_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_ELEM_TYPE_ELECTRONCHARGE
This refers to conservation of electrons.
Definition vcs_defs.h:176
#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_SMALL_MULTIPHASE_SPECIES
Relative value of multiphase species mole number for a multiphase species which is small.
Definition vcs_defs.h:36
#define VCS_ELEM_TYPE_CHARGENEUTRALITY
This refers to a charge neutrality of a single phase.
Definition vcs_defs.h:182
#define VCS_SPECIES_MINOR
Species is a major species.
Definition vcs_defs.h:72
#define VCS_DELETE_ELEMENTABS_CUTOFF
Cutoff moles below which a phase or species which comprises the bulk of an element's total concentrat...
Definition vcs_defs.h:44
#define VCS_PHASE_EXIST_YES
Phase is a normal phase that currently exists.
Definition vcs_defs.h:144
#define VCS_SUCCESS
ERROR CODES.
Definition vcs_defs.h:18
#define VCS_STATECALC_PHASESTABILITY
State Calculation based on tentative mole numbers for a phase which is currently zeroed,...
Definition vcs_defs.h:214
#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...