Cantera  4.0.0a1
Loading...
Searching...
No Matches
vcs_VolPhase.cpp
Go to the documentation of this file.
1/**
2 * @file vcs_VolPhase.cpp
3 */
4
5// This file is part of Cantera. See License.txt in the top-level directory or
6// at https://cantera.org/license.txt for license and copyright information.
7
10
13
14#include <cstdio>
15
16namespace Cantera
17{
18
19vcs_VolPhase::vcs_VolPhase(VCS_SOLVE* owningSolverObject,
20 ThermoPhase* thermoPhase, size_t phaseNum)
21 : m_owningSolverObject(owningSolverObject)
22{
23 if (!m_owningSolverObject) {
24 throw CanteraError("vcs_VolPhase::vcs_VolPhase",
25 "owningSolverObject must not be null");
26 }
27 if (!thermoPhase) {
28 throw CanteraError("vcs_VolPhase::vcs_VolPhase",
29 "thermoPhase must not be null");
30 }
31
32 VP_ID_ = phaseNum;
33 PhaseName = thermoPhase->name().empty() ?
34 fmt::format("Phase_{}", VP_ID_) : thermoPhase->name();
35
36 m_numSpecies = thermoPhase->nSpecies();
37 AssertThrowMsg(m_numSpecies > 0, "vcs_VolPhase::vcs_VolPhase",
38 "nspecies Error");
39 m_singleSpecies = (m_numSpecies == 1);
40 m_phiVarIndex = npos;
41
42 p_activityConvention = thermoPhase->activityConvention();
43
44 IndSpecies.assign(m_numSpecies, npos);
45 Xmol_.assign(m_numSpecies, 0.0);
46 creationMoleNumbers_.assign(m_numSpecies, 0.0);
47 creationGlobalRxnNumbers_.assign(m_numSpecies, npos);
48 for (size_t k = 0; k < m_numSpecies; k++) {
49 double defaultFrac = 1.0 / m_numSpecies;
50 Xmol_[k] = defaultFrac;
51 creationMoleNumbers_[k] = defaultFrac;
52 }
53
54 SS0ChemicalPotential.assign(m_numSpecies, -1.0);
55 StarChemicalPotential.assign(m_numSpecies, -1.0);
56 StarMolarVol.assign(m_numSpecies, -1.0);
57 PartialMolarVol.assign(m_numSpecies, -1.0);
58 ActCoeff.assign(m_numSpecies, 1.0);
59 np_dLnActCoeffdMolNumber.resize(m_numSpecies, m_numSpecies, 0.0);
60
61 m_speciesUnknownType.assign(m_numSpecies, VCS_SPECIES_TYPE_MOLNUM);
62 m_UpToDate = false;
63 m_vcsStateStatus = VCS_STATECALC_OLD;
64 m_UpToDate_AC = false;
65 m_UpToDate_VolStar = false;
66 m_UpToDate_VolPM = false;
67 m_UpToDate_GStar = false;
68 m_UpToDate_G0 = false;
69
70 TP_ptr = thermoPhase;
71 Temp_ = TP_ptr->temperature();
72 Pres_ = TP_ptr->pressure();
73 m_phi = TP_ptr->electricPotential();
74
75 transferElementsFM(TP_ptr);
76 setTotalMoles(0.0);
77
78 // Determine the global index for each of the elements in the phase
79 size_t neVP = nElemConstraints();
80 for (size_t eVP = 0; eVP < neVP; eVP++) {
81 string enVP = elementName(eVP);
82 size_t foundPos = m_owningSolverObject->elementIndex(enVP);
83 if (foundPos == npos) {
84 int elType = elementType(eVP);
85 int elactive = elementActive(eVP);
86 foundPos = m_owningSolverObject->addElement(enVP.c_str(), elType, elactive);
87 }
88 setElemGlobalIndex(eVP, foundPos);
89 }
90
91 setState_TP(Temp_, Pres_);
92 TP_ptr->getMoleFractions(Xmol_);
93 creationMoleNumbers_ = Xmol_;
94 _updateMoleFractionDependencies();
95
96 m_isIdealSoln = m_singleSpecies ? true : TP_ptr->isIdeal();
97}
98
99void vcs_VolPhase::elemResize(const size_t numElemConstraints)
100{
101 m_elementNames.resize(numElemConstraints);
102 m_elementActive.resize(numElemConstraints+1, 1);
103 m_elementType.resize(numElemConstraints, VCS_ELEM_TYPE_ABSPOS);
104 m_formulaMatrix.resize(m_numSpecies, numElemConstraints, 0.0);
105 m_elementNames.resize(numElemConstraints, "");
106 m_elemGlobalIndex.resize(numElemConstraints, npos);
107 m_numElemConstraints = numElemConstraints;
108}
109
110void vcs_VolPhase::_updateActCoeff() const
111{
112 if (m_isIdealSoln) {
113 m_UpToDate_AC = true;
114 return;
115 }
116 TP_ptr->getActivityCoefficients(ActCoeff);
117 m_UpToDate_AC = true;
118}
119
120void vcs_VolPhase::_updateG0() const
121{
122 TP_ptr->getGibbs_ref(SS0ChemicalPotential);
123 m_UpToDate_G0 = true;
124}
125
126double vcs_VolPhase::G0_calc_one(size_t kspec) const
127{
128 if (!m_UpToDate_G0) {
129 _updateG0();
130 }
131 return SS0ChemicalPotential[kspec];
132}
133
134void vcs_VolPhase::_updateGStar() const
135{
136 TP_ptr->getStandardChemPotentials(StarChemicalPotential);
137 m_UpToDate_GStar = true;
138}
139
140double vcs_VolPhase::GStar_calc_one(size_t kspec) const
141{
142 if (!m_UpToDate_GStar) {
143 _updateGStar();
144 }
145 return StarChemicalPotential[kspec];
146}
147
148void vcs_VolPhase::setMoleFractions(span<const double> xmol)
149{
150 checkArraySize("vcs_VolPhase::setMoleFractions", xmol.size(), m_numSpecies);
151 double sum = -1.0;
152 for (size_t k = 0; k < m_numSpecies; k++) {
153 Xmol_[k] = xmol[k];
154 sum+= xmol[k];
155 }
156 if (std::fabs(sum) > 1.0E-13) {
157 for (size_t k = 0; k < m_numSpecies; k++) {
158 Xmol_[k] /= sum;
159 }
160 }
161 _updateMoleFractionDependencies();
162 m_UpToDate = false;
163 m_vcsStateStatus = VCS_STATECALC_TMP;
164}
165
166void vcs_VolPhase::_updateMoleFractionDependencies()
167{
168 if (TP_ptr) {
169 TP_ptr->setMoleFractions(span<const double>(
170 &Xmol_[m_MFStartIndex], m_numSpecies));
171 TP_ptr->setPressure(Pres_);
172 }
173 if (!m_isIdealSoln) {
174 m_UpToDate_AC = false;
175 m_UpToDate_VolPM = false;
176 }
177}
178
179span<const double> vcs_VolPhase::moleFractions() const
180{
181 return Xmol_;
182}
183
184double vcs_VolPhase::moleFraction(size_t k) const
185{
186 return Xmol_[k];
187}
188
189void vcs_VolPhase::setMoleFractionsState(const double totalMoles,
190 span<const double> moleFractions, const int vcsStateStatus)
191{
192 checkArraySize("vcs_VolPhase::setMoleFractionsState", moleFractions.size(),
193 m_numSpecies);
194 if (totalMoles != 0.0) {
195 // There are other ways to set the mole fractions when VCS_STATECALC
196 // is set to a normal settting.
197 if (vcsStateStatus != VCS_STATECALC_TMP) {
198 throw CanteraError("vcs_VolPhase::setMolesFractionsState",
199 "inappropriate usage");
200 }
201 m_UpToDate = false;
202 m_vcsStateStatus = VCS_STATECALC_TMP;
203 if (m_existence == VCS_PHASE_EXIST_ZEROEDPHASE) {
204 throw CanteraError("vcs_VolPhase::setMolesFractionsState",
205 "inappropriate usage");
206 }
207 m_existence = VCS_PHASE_EXIST_YES;
208 } else {
209 m_UpToDate = true;
210 m_vcsStateStatus = vcsStateStatus;
211 m_existence = std::min(m_existence, VCS_PHASE_EXIST_NO);
212 }
213 double fractotal = 1.0;
214 v_totalMoles = totalMoles;
215 double sum = 0.0;
216 for (size_t k = 0; k < m_numSpecies; k++) {
217 Xmol_[k] = moleFractions[k];
218 sum += moleFractions[k];
219 }
220 if (sum == 0.0) {
221 throw CanteraError("vcs_VolPhase::setMolesFractionsState",
222 "inappropriate usage");
223 }
224 if (sum != fractotal) {
225 for (size_t k = 0; k < m_numSpecies; k++) {
226 Xmol_[k] *= (fractotal /sum);
227 }
228 }
229 _updateMoleFractionDependencies();
230}
231
232void vcs_VolPhase::setMolesFromVCS(const int stateCalc,
233 span<const double> molesSpeciesVCS)
234{
235 v_totalMoles = 0.0;
236
237 if (molesSpeciesVCS.empty()) {
238 if (stateCalc == VCS_STATECALC_OLD) {
239 molesSpeciesVCS = span<const double>(m_owningSolverObject->m_molNumSpecies_old);
240 } else if (stateCalc == VCS_STATECALC_NEW) {
241 molesSpeciesVCS = span<const double>(m_owningSolverObject->m_molNumSpecies_new);
242 } else {
243 throw CanteraError("vcs_VolPhase::setMolesFromVCS", "shouldn't be here");
244 }
245 }
246
247 for (size_t k = 0; k < m_numSpecies; k++) {
248 if (m_speciesUnknownType[k] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
249 size_t kglob = IndSpecies[k];
250 v_totalMoles += std::max(0.0, molesSpeciesVCS[kglob]);
251 }
252 }
253 if (v_totalMoles > 0.0) {
254 for (size_t k = 0; k < m_numSpecies; k++) {
255 if (m_speciesUnknownType[k] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
256 size_t kglob = IndSpecies[k];
257 double tmp = std::max(0.0, molesSpeciesVCS[kglob]);
258 Xmol_[k] = tmp / v_totalMoles;
259 }
260 }
261 m_existence = VCS_PHASE_EXIST_YES;
262 } else {
263 // This is where we will start to store a better approximation
264 // for the mole fractions, when the phase doesn't exist.
265 // This is currently unimplemented.
266 m_existence = VCS_PHASE_EXIST_NO;
267 }
268
269 // Update the electric potential if it is a solution variable in the
270 // equation system
271 if (m_phiVarIndex != npos) {
272 size_t kglob = IndSpecies[m_phiVarIndex];
273 if (m_numSpecies == 1) {
274 Xmol_[m_phiVarIndex] = 1.0;
275 } else {
276 Xmol_[m_phiVarIndex] = 0.0;
277 }
278 double phi = molesSpeciesVCS[kglob];
279 setElectricPotential(phi);
280 if (m_numSpecies == 1) {
281 m_existence = VCS_PHASE_EXIST_YES;
282 }
283 }
284 _updateMoleFractionDependencies();
285
286 // If stateCalc is old and the total moles is positive, then we have a valid
287 // state. If the phase went away, it would be a valid starting point for
288 // F_k's. So, save the state.
289 if (stateCalc == VCS_STATECALC_OLD && v_totalMoles > 0.0) {
290 creationMoleNumbers_ = Xmol_;
291 }
292
293 // Set flags indicating we are up to date with the VCS state vector.
294 m_UpToDate = true;
295 m_vcsStateStatus = stateCalc;
296}
297
298void vcs_VolPhase::setMolesFromVCSCheck(const int vcsStateStatus,
299 span<const double> molesSpeciesVCS,
300 span<const double> TPhMoles)
301{
302 setMolesFromVCS(vcsStateStatus, molesSpeciesVCS);
303
304 // Check for consistency with TPhMoles[]
305 double Tcheck = TPhMoles[VP_ID_];
306 if (Tcheck != v_totalMoles) {
307 if (vcs_doubleEqual(Tcheck, v_totalMoles)) {
308 Tcheck = v_totalMoles;
309 } else {
310 throw CanteraError("vcs_VolPhase::setMolesFromVCSCheck",
311 "We have a consistency problem: {} {}", Tcheck, v_totalMoles);
312 }
313 }
314}
315
316void vcs_VolPhase::updateFromVCS_MoleNumbers(const int vcsStateStatus)
317{
318 if ((!m_UpToDate || vcsStateStatus != m_vcsStateStatus) &&
319 (vcsStateStatus == VCS_STATECALC_OLD || vcsStateStatus == VCS_STATECALC_NEW)) {
320 setMolesFromVCS(vcsStateStatus);
321 }
322}
323
324void vcs_VolPhase::sendToVCS_ActCoeff(const int vcsStateStatus, span<double> AC)
325{
326 updateFromVCS_MoleNumbers(vcsStateStatus);
327 if (!m_UpToDate_AC) {
328 _updateActCoeff();
329 }
330 for (size_t k = 0; k < m_numSpecies; k++) {
331 size_t kglob = IndSpecies[k];
332 AC[kglob] = ActCoeff[k];
333 }
334}
335
336double vcs_VolPhase::sendToVCS_VolPM(span<double> VolPM) const
337{
338 if (!m_UpToDate_VolPM) {
339 _updateVolPM();
340 }
341 for (size_t k = 0; k < m_numSpecies; k++) {
342 size_t kglob = IndSpecies[k];
343 VolPM[kglob] = PartialMolarVol[k];
344 }
345 return m_totalVol;
346}
347
348void vcs_VolPhase::sendToVCS_GStar(span<double> gstar) const
349{
350 if (!m_UpToDate_GStar) {
351 _updateGStar();
352 }
353 for (size_t k = 0; k < m_numSpecies; k++) {
354 size_t kglob = IndSpecies[k];
355 gstar[kglob] = StarChemicalPotential[k];
356 }
357}
358
359void vcs_VolPhase::setElectricPotential(const double phi)
360{
361 m_phi = phi;
362 TP_ptr->setElectricPotential(m_phi);
363 // We have changed the state variable. Set uptodate flags to false
364 m_UpToDate_AC = false;
365 m_UpToDate_VolStar = false;
366 m_UpToDate_VolPM = false;
367 m_UpToDate_GStar = false;
368}
369
370double vcs_VolPhase::electricPotential() const
371{
372 return m_phi;
373}
374
375void vcs_VolPhase::setState_TP(const double temp, const double pres)
376{
377 if (Temp_ == temp && Pres_ == pres) {
378 return;
379 }
380 TP_ptr->setElectricPotential(m_phi);
381 TP_ptr->setState_TP(temp, pres);
382 Temp_ = temp;
383 Pres_ = pres;
384 m_UpToDate_AC = false;
385 m_UpToDate_VolStar = false;
386 m_UpToDate_VolPM = false;
387 m_UpToDate_GStar = false;
388 m_UpToDate_G0 = false;
389}
390
391void vcs_VolPhase::_updateVolStar() const
392{
393 TP_ptr->getStandardVolumes(StarMolarVol);
394 m_UpToDate_VolStar = true;
395}
396
397double vcs_VolPhase::_updateVolPM() const
398{
399 TP_ptr->getPartialMolarVolumes(PartialMolarVol);
400 m_totalVol = 0.0;
401 for (size_t k = 0; k < m_numSpecies; k++) {
402 m_totalVol += PartialMolarVol[k] * Xmol_[k];
403 }
404 m_totalVol *= v_totalMoles;
405 m_UpToDate_VolPM = true;
406 return m_totalVol;
407}
408
409void vcs_VolPhase::_updateLnActCoeffJac()
410{
411 double phaseTotalMoles = v_totalMoles;
412 if (phaseTotalMoles < 1.0E-14) {
413 phaseTotalMoles = 1.0;
414 }
415
416 // Evaluate the current base activity coefficients if necessary
417 if (!m_UpToDate_AC) {
418 _updateActCoeff();
419 }
420 if (!TP_ptr) {
421 return;
422 }
423 TP_ptr->getdlnActCoeffdlnN(m_numSpecies,
424 span<double>(&np_dLnActCoeffdMolNumber(0, 0), m_numSpecies * m_numSpecies));
425 for (size_t j = 0; j < m_numSpecies; j++) {
426 double moles_j_base = phaseTotalMoles * Xmol_[j];
427 auto np_lnActCoeffCol = np_dLnActCoeffdMolNumber.col(j);
428 if (moles_j_base < 1.0E-200) {
429 moles_j_base = 1.0E-7 * moles_j_base + 1.0E-13 * phaseTotalMoles + 1.0E-150;
430 }
431 for (size_t k = 0; k < m_numSpecies; k++) {
432 np_lnActCoeffCol[k] = np_lnActCoeffCol[k] * phaseTotalMoles / moles_j_base;
433 }
434 }
435
436 double deltaMoles_j = 0.0;
437 // Make copies of ActCoeff and Xmol_ for use in taking differences
438 vector<double> ActCoeff_Base(ActCoeff);
439 vector<double> Xmol_Base(Xmol_);
440 double TMoles_base = phaseTotalMoles;
441
442 // Loop over the columns species to be deltad
443 for (size_t j = 0; j < m_numSpecies; j++) {
444 // Calculate a value for the delta moles of species j. Note Xmol_[] and
445 // Tmoles are always positive or zero quantities.
446 double moles_j_base = phaseTotalMoles * Xmol_Base[j];
447 deltaMoles_j = 1.0E-7 * moles_j_base + 1.0E-13 * phaseTotalMoles + 1.0E-150;
448
449 // Now, update the total moles in the phase and all of the mole
450 // fractions based on this.
451 phaseTotalMoles = TMoles_base + deltaMoles_j;
452 for (size_t k = 0; k < m_numSpecies; k++) {
453 Xmol_[k] = Xmol_Base[k] * TMoles_base / phaseTotalMoles;
454 }
455 Xmol_[j] = (moles_j_base + deltaMoles_j) / phaseTotalMoles;
456
457 // Go get new values for the activity coefficients. Note this calls
458 // setState_PX();
459 _updateMoleFractionDependencies();
460 _updateActCoeff();
461
462 // Revert to the base case Xmol_, v_totalMoles
463 v_totalMoles = TMoles_base;
464 Xmol_ = Xmol_Base;
465 }
466
467 // Go get base values for the activity coefficients. Note this calls
468 // setState_TPX() again; Just wanted to make sure that cantera is in sync
469 // with VolPhase after this call.
470 setMoleFractions(Xmol_Base);
471 _updateMoleFractionDependencies();
472 _updateActCoeff();
473}
474
475void vcs_VolPhase::sendToVCS_LnActCoeffJac(Array2D& np_LnACJac_VCS)
476{
477 // update the Ln Act Coeff Jacobian entries with respect to the mole number
478 // of species in the phase -> we always assume that they are out of date.
479 _updateLnActCoeffJac();
480
481 // Now copy over the values
482 for (size_t j = 0; j < m_numSpecies; j++) {
483 size_t jglob = IndSpecies[j];
484 for (size_t k = 0; k < m_numSpecies; k++) {
485 size_t kglob = IndSpecies[k];
486 np_LnACJac_VCS(kglob,jglob) = np_dLnActCoeffdMolNumber(k,j);
487 }
488 }
489}
490
491double vcs_VolPhase::totalMoles() const
492{
493 return v_totalMoles;
494}
495
496double vcs_VolPhase::molefraction(size_t k) const
497{
498 return Xmol_[k];
499}
500
501void vcs_VolPhase::setCreationMoleNumbers(span<const double> n_k,
502 span<const size_t> creationGlobalRxnNumbers)
503{
504 checkArraySize("vcs_VolPhase::setCreationMoleNumbers", n_k.size(), m_numSpecies);
505 checkArraySize("vcs_VolPhase::setCreationMoleNumbers", creationGlobalRxnNumbers.size(), m_numSpecies);
506 creationMoleNumbers_.assign(n_k.begin(), n_k.end());
507 for (size_t k = 0; k < m_numSpecies; k++) {
508 creationGlobalRxnNumbers_[k] = creationGlobalRxnNumbers[k];
509 }
510}
511
512span<const double> vcs_VolPhase::creationMoleNumbers(
513 span<size_t> creationGlobalRxnNumbers) const
514{
515 checkArraySize("vcs_VolPhase::creationMoleNumbers", creationGlobalRxnNumbers.size(), m_numSpecies);
516 copy(creationGlobalRxnNumbers_.begin(), creationGlobalRxnNumbers_.end(),
517 creationGlobalRxnNumbers.begin());
518 return creationMoleNumbers_;
519}
520
521void vcs_VolPhase::setTotalMoles(const double totalMols)
522{
523 v_totalMoles = totalMols;
524 if (m_singleSpecies && (m_phiVarIndex == 0)) {
525 m_existence = VCS_PHASE_EXIST_ALWAYS;
526 } else {
527 if (totalMols > 0.0) {
528 m_existence = VCS_PHASE_EXIST_YES;
529 } else {
530 m_existence = VCS_PHASE_EXIST_NO;
531 }
532 }
533}
534
535void vcs_VolPhase::setMolesOutOfDate(int stateCalc)
536{
537 m_UpToDate = false;
538 if (stateCalc != -1) {
539 m_vcsStateStatus = stateCalc;
540 }
541}
542
543void vcs_VolPhase::setMolesCurrent(int stateCalc)
544{
545 m_UpToDate = true;
546 m_vcsStateStatus = stateCalc;
547}
548
549bool vcs_VolPhase::isIdealSoln() const
550{
551 return m_isIdealSoln;
552}
553
554size_t vcs_VolPhase::phiVarIndex() const
555{
556 return m_phiVarIndex;
557}
558
559void vcs_VolPhase::setPhiVarIndex(size_t phiVarIndex)
560{
561 m_phiVarIndex = phiVarIndex;
562 m_speciesUnknownType[m_phiVarIndex] = VCS_SPECIES_TYPE_INTERFACIALVOLTAGE;
563 if (m_singleSpecies && m_phiVarIndex == 0) {
564 m_existence = VCS_PHASE_EXIST_ALWAYS;
565 }
566}
567
568int vcs_VolPhase::exists() const
569{
570 return m_existence;
571}
572
573void vcs_VolPhase::setExistence(const int existence)
574{
575 if (existence == VCS_PHASE_EXIST_NO || existence == VCS_PHASE_EXIST_ZEROEDPHASE) {
576 if (v_totalMoles != 0.0) {
577 throw CanteraError("vcs_VolPhase::setExistence",
578 "setting false existence for phase with moles");
579 }
580 } else if (v_totalMoles == 0.0 && (!m_singleSpecies || m_phiVarIndex != 0)) {
581 throw CanteraError("vcs_VolPhase::setExistence",
582 "setting true existence for phase with no moles");
583 }
584 if (m_singleSpecies && m_phiVarIndex == 0 && (existence == VCS_PHASE_EXIST_NO || existence == VCS_PHASE_EXIST_ZEROEDPHASE)) {
585 throw CanteraError("vcs_VolPhase::setExistence",
586 "Trying to set existence of an electron phase to false");
587 }
588 m_existence = existence;
589}
590
591size_t vcs_VolPhase::spGlobalIndexVCS(const size_t spIndex) const
592{
593 return IndSpecies[spIndex];
594}
595
596void vcs_VolPhase::setSpGlobalIndexVCS(const size_t spIndex,
597 const size_t spGlobalIndex)
598{
599 IndSpecies[spIndex] = spGlobalIndex;
600 if (spGlobalIndex >= m_numElemConstraints) {
601 creationGlobalRxnNumbers_[spIndex] = spGlobalIndex - m_numElemConstraints;
602 }
603}
604
605size_t vcs_VolPhase::elemGlobalIndex(const size_t e) const
606{
607 AssertThrow(e < m_numElemConstraints, " vcs_VolPhase::elemGlobalIndex");
608 return m_elemGlobalIndex[e];
609}
610
611void vcs_VolPhase::setElemGlobalIndex(const size_t eLocal, const size_t eGlobal)
612{
613 AssertThrow(eLocal < m_numElemConstraints,
614 "vcs_VolPhase::setElemGlobalIndex");
615 m_elemGlobalIndex[eLocal] = eGlobal;
616}
617
618size_t vcs_VolPhase::nElemConstraints() const
619{
620 return m_numElemConstraints;
621}
622
623string vcs_VolPhase::elementName(size_t e) const
624{
625
626 if (e < m_elementNames.size()) {
627 return m_elementNames[e];
628 }
629 throw IndexError("vcs_VolPhase::elementName", "element", e, m_elementNames.size());
630}
631
632//! This function decides whether a phase has charged species or not.
633static bool hasChargedSpecies(const ThermoPhase* const tPhase)
634{
635 for (size_t k = 0; k < tPhase->nSpecies(); k++) {
636 if (tPhase->charge(k) != 0.0) {
637 return true;
638 }
639 }
640 return false;
641}
642
643//! This utility routine decides whether a Cantera ThermoPhase needs
644//! a constraint equation representing the charge neutrality of the
645//! phase. It does this by searching for charged species. If it
646//! finds one, and if the phase needs one, then it returns true.
647static bool chargeNeutralityElement(const ThermoPhase* const tPhase)
648{
649 int hasCharge = hasChargedSpecies(tPhase);
650 if (tPhase->chargeNeutralityNecessary() && hasCharge) {
651 return true;
652 }
653 return false;
654}
655
656size_t vcs_VolPhase::transferElementsFM(const ThermoPhase* const tPhase)
657{
658 size_t nebase = tPhase->nElements();
659 size_t ne = nebase;
660 size_t ns = tPhase->nSpecies();
661
662 // Decide whether we need an extra element constraint for charge
663 // neutrality of the phase
664 bool cne = chargeNeutralityElement(tPhase);
665 if (cne) {
666 ChargeNeutralityElement = ne;
667 ne++;
668 }
669
670 // Assign and resize structures
671 elemResize(ne);
672
673 if (ChargeNeutralityElement != npos) {
674 m_elementType[ChargeNeutralityElement] = VCS_ELEM_TYPE_CHARGENEUTRALITY;
675 }
676
677 size_t eFound = npos;
678 if (hasChargedSpecies(tPhase)) {
679 if (cne) {
680 // We need a charge neutrality constraint. We also have an Electron
681 // Element. These are duplicates of each other. To avoid trouble
682 // with possible range error conflicts, sometimes we eliminate the
683 // Electron condition. Flag that condition for elimination by
684 // toggling the ElActive variable. If we find we need it later, we
685 // will retoggle ElActive to true.
686 for (size_t eT = 0; eT < nebase; eT++) {
687 if (tPhase->elementName(eT) == "E") {
688 eFound = eT;
689 m_elementActive[eT] = 0;
690 m_elementType[eT] = VCS_ELEM_TYPE_ELECTRONCHARGE;
691 }
692 }
693 } else {
694 for (size_t eT = 0; eT < nebase; eT++) {
695 if (tPhase->elementName(eT) == "E") {
696 eFound = eT;
697 m_elementType[eT] = VCS_ELEM_TYPE_ELECTRONCHARGE;
698 }
699 }
700 }
701 if (eFound == npos) {
702 eFound = ne;
703 m_elementType[ne] = VCS_ELEM_TYPE_ELECTRONCHARGE;
704 m_elementActive[ne] = 0;
705 string ename = "E";
706 m_elementNames[ne] = ename;
707 ne++;
708 elemResize(ne);
709 }
710 }
711
712 m_formulaMatrix.resize(ns, ne, 0.0);
713 m_speciesUnknownType.resize(ns, VCS_SPECIES_TYPE_MOLNUM);
714 elemResize(ne);
715
716 size_t e = 0;
717 for (size_t eT = 0; eT < nebase; eT++) {
718 m_elementNames[e] = tPhase->elementName(eT);
719 m_elementType[e] = tPhase->elementType(eT);
720 e++;
721 }
722
723 if (cne) {
724 string pname = tPhase->name();
725 if (pname == "") {
726 pname = fmt::format("phase{}", VP_ID_);
727 }
728 e = ChargeNeutralityElement;
729 m_elementNames[e] = "cn_" + pname;
730 }
731
732 for (size_t k = 0; k < ns; k++) {
733 e = 0;
734 for (size_t eT = 0; eT < nebase; eT++) {
735 m_formulaMatrix(k,e) = tPhase->nAtoms(k, eT);
736 e++;
737 }
738 if (eFound != npos) {
739 m_formulaMatrix(k,eFound) = - tPhase->charge(k);
740 }
741 }
742
743 if (cne) {
744 for (size_t k = 0; k < ns; k++) {
745 m_formulaMatrix(k,ChargeNeutralityElement) = tPhase->charge(k);
746 }
747 }
748
749 // Here, we figure out what is the species types are The logic isn't set in
750 // stone, and is just for a particular type of problem that I'm solving
751 // first.
752 if (ns == 1 && tPhase->charge(0) != 0.0) {
753 m_speciesUnknownType[0] = VCS_SPECIES_TYPE_INTERFACIALVOLTAGE;
754 setPhiVarIndex(0);
755 }
756
757 return ne;
758}
759
760int vcs_VolPhase::elementType(const size_t e) const
761{
762 return m_elementType[e];
763}
764
765const Array2D& vcs_VolPhase::getFormulaMatrix() const
766{
767 return m_formulaMatrix;
768}
769
770int vcs_VolPhase::speciesUnknownType(const size_t k) const
771{
772 return m_speciesUnknownType[k];
773}
774
775int vcs_VolPhase::elementActive(const size_t e) const
776{
777 return m_elementActive[e];
778}
779
780size_t vcs_VolPhase::nSpecies() const
781{
782 return m_numSpecies;
783}
784
785string vcs_VolPhase::eos_name() const
786{
787 return TP_ptr->type();
788}
789}
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
Base class for exceptions thrown by Cantera classes.
An array index is out of range.
size_t nSpecies() const
Returns the number of species in the phase.
Definition Phase.h:246
int elementType(size_t m) const
Return the element constraint type Possible types include:
Definition Phase.cpp:89
double nAtoms(size_t k, size_t m) const
Number of atoms of element m in species k.
Definition Phase.cpp:101
size_t nElements() const
Number of elements.
Definition Phase.cpp:30
string elementName(size_t m) const
Name of the element with index m.
Definition Phase.cpp:43
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
string name() const
Return the name of the phase.
Definition Phase.cpp:20
Base class for a phase with thermodynamic properties.
bool chargeNeutralityNecessary() const
Returns the chargeNeutralityNecessity boolean.
#define AssertThrow(expr, procedure)
Assertion must be true or an error is thrown.
#define AssertThrowMsg(expr, procedure,...)
Assertion must be true or an error is thrown.
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
static bool chargeNeutralityElement(const ThermoPhase *const tPhase)
This utility routine decides whether a Cantera ThermoPhase needs a constraint equation representing t...
static bool hasChargedSpecies(const ThermoPhase *const tPhase)
This function decides whether a phase has charged species or not.
bool vcs_doubleEqual(double d1, double d2)
Simple routine to check whether two doubles are equal up to roundoff error.
Definition vcs_util.cpp:89
void checkArraySize(const char *procedure, size_t available, size_t required)
Wrapper for throwing ArraySizeError.
Contains declarations for string manipulation functions within Cantera.
Header for the object representing each phase within vcs.
#define VCS_SPECIES_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_STATECALC_TMP
State Calculation based on a temporary set of mole numbers.
Definition vcs_defs.h:217
#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_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_ELEM_TYPE_ELECTRONCHARGE
This refers to conservation of electrons.
Definition vcs_defs.h:176
#define VCS_ELEM_TYPE_CHARGENEUTRALITY
This refers to a charge neutrality of a single phase.
Definition vcs_defs.h:182
#define VCS_PHASE_EXIST_YES
Phase is a normal phase that currently exists.
Definition vcs_defs.h:144
#define VCS_PHASE_EXIST_ZEROEDPHASE
Phase currently is zeroed due to a programmatic issue.
Definition vcs_defs.h:153
Header file for the internal object that holds the vcs equilibrium problem (see Class VCS_SOLVE and C...