Cantera  3.1.0a1
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 
11 
14 
15 #include <cstdio>
16 
17 namespace Cantera
18 {
19 
20 vcs_VolPhase::vcs_VolPhase(VCS_SOLVE* owningSolverObject) :
21  m_owningSolverObject(owningSolverObject)
22 {
23 }
24 
25 vcs_VolPhase::~vcs_VolPhase()
26 {
27  for (size_t k = 0; k < m_numSpecies; k++) {
28  delete ListSpeciesPtr[k];
29  }
30 }
31 
32 void vcs_VolPhase::resize(const size_t phaseNum, const size_t nspecies,
33  const size_t numElem, const char* const phaseName,
34  const double molesInert)
35 {
36  AssertThrowMsg(nspecies > 0, "vcs_VolPhase::resize", "nspecies Error");
37  setTotalMolesInert(molesInert);
38  m_phi = 0.0;
39  m_phiVarIndex = npos;
40 
41  if (phaseNum == VP_ID_) {
42  if (strcmp(PhaseName.c_str(), phaseName)) {
43  throw CanteraError("vcs_VolPhase::resize",
44  "Strings are different: " + PhaseName + " " +
45  phaseName + " :unknown situation");
46  }
47  } else {
48  VP_ID_ = phaseNum;
49  if (!phaseName) {
50  PhaseName = fmt::format("Phase_{}", VP_ID_);
51  } else {
52  PhaseName = phaseName;
53  }
54  }
55  if (nspecies > 1) {
56  m_singleSpecies = false;
57  } else {
58  m_singleSpecies = true;
59  }
60 
61  if (m_numSpecies == nspecies && numElem == m_numElemConstraints) {
62  return;
63  }
64 
65  m_numSpecies = nspecies;
66  if (nspecies > 1) {
67  m_singleSpecies = false;
68  }
69 
70  IndSpecies.resize(nspecies, npos);
71 
72  if (ListSpeciesPtr.size() >= m_numSpecies) {
73  for (size_t i = 0; i < m_numSpecies; i++) {
74  if (ListSpeciesPtr[i]) {
75  delete ListSpeciesPtr[i];
76  ListSpeciesPtr[i] = 0;
77  }
78  }
79  }
80  ListSpeciesPtr.resize(nspecies, 0);
81  for (size_t i = 0; i < nspecies; i++) {
82  ListSpeciesPtr[i] = new vcs_SpeciesProperties(phaseNum, i, this);
83  }
84 
85  Xmol_.resize(nspecies, 0.0);
86  creationMoleNumbers_.resize(nspecies, 0.0);
87  creationGlobalRxnNumbers_.resize(nspecies, npos);
88  for (size_t i = 0; i < nspecies; i++) {
89  Xmol_[i] = 1.0/nspecies;
90  creationMoleNumbers_[i] = 1.0/nspecies;
91  if (IndSpecies[i] >= m_numElemConstraints) {
92  creationGlobalRxnNumbers_[i] = IndSpecies[i] - m_numElemConstraints;
93  } else {
94  creationGlobalRxnNumbers_[i] = npos;
95  }
96  }
97 
98  SS0ChemicalPotential.resize(nspecies, -1.0);
99  StarChemicalPotential.resize(nspecies, -1.0);
100  StarMolarVol.resize(nspecies, -1.0);
101  PartialMolarVol.resize(nspecies, -1.0);
102  ActCoeff.resize(nspecies, 1.0);
103  np_dLnActCoeffdMolNumber.resize(nspecies, nspecies, 0.0);
104 
105  m_speciesUnknownType.resize(nspecies, VCS_SPECIES_TYPE_MOLNUM);
106  m_UpToDate = false;
107  m_vcsStateStatus = VCS_STATECALC_OLD;
108  m_UpToDate_AC = false;
109  m_UpToDate_VolStar = false;
110  m_UpToDate_VolPM = false;
111  m_UpToDate_GStar = false;
112  m_UpToDate_G0 = false;
113 
114  elemResize(numElem);
115 }
116 
117 void vcs_VolPhase::elemResize(const size_t numElemConstraints)
118 {
119  m_elementNames.resize(numElemConstraints);
120  m_elementActive.resize(numElemConstraints+1, 1);
121  m_elementType.resize(numElemConstraints, VCS_ELEM_TYPE_ABSPOS);
122  m_formulaMatrix.resize(m_numSpecies, numElemConstraints, 0.0);
123  m_elementNames.resize(numElemConstraints, "");
124  m_elemGlobalIndex.resize(numElemConstraints, npos);
125  m_numElemConstraints = numElemConstraints;
126 }
127 
128 void vcs_VolPhase::_updateActCoeff() const
129 {
130  if (m_isIdealSoln) {
131  m_UpToDate_AC = true;
132  return;
133  }
134  TP_ptr->getActivityCoefficients(&ActCoeff[0]);
135  m_UpToDate_AC = true;
136 }
137 
138 void vcs_VolPhase::_updateG0() const
139 {
140  TP_ptr->getGibbs_ref(&SS0ChemicalPotential[0]);
141  m_UpToDate_G0 = true;
142 }
143 
144 double vcs_VolPhase::G0_calc_one(size_t kspec) const
145 {
146  if (!m_UpToDate_G0) {
147  _updateG0();
148  }
149  return SS0ChemicalPotential[kspec];
150 }
151 
152 void vcs_VolPhase::_updateGStar() const
153 {
154  TP_ptr->getStandardChemPotentials(&StarChemicalPotential[0]);
155  m_UpToDate_GStar = true;
156 }
157 
158 double vcs_VolPhase::GStar_calc_one(size_t kspec) const
159 {
160  if (!m_UpToDate_GStar) {
161  _updateGStar();
162  }
163  return StarChemicalPotential[kspec];
164 }
165 
166 void vcs_VolPhase::setMoleFractions(const double* const xmol)
167 {
168  double sum = -1.0;
169  for (size_t k = 0; k < m_numSpecies; k++) {
170  Xmol_[k] = xmol[k];
171  sum+= xmol[k];
172  }
173  if (std::fabs(sum) > 1.0E-13) {
174  for (size_t k = 0; k < m_numSpecies; k++) {
175  Xmol_[k] /= sum;
176  }
177  }
178  _updateMoleFractionDependencies();
179  m_UpToDate = false;
180  m_vcsStateStatus = VCS_STATECALC_TMP;
181 }
182 
183 void vcs_VolPhase::_updateMoleFractionDependencies()
184 {
185  if (TP_ptr) {
186  TP_ptr->setMoleFractions(&Xmol_[m_MFStartIndex]);
187  TP_ptr->setPressure(Pres_);
188  }
189  if (!m_isIdealSoln) {
190  m_UpToDate_AC = false;
191  m_UpToDate_VolPM = false;
192  }
193 }
194 
195 const vector<double> & vcs_VolPhase::moleFractions() const
196 {
197  return Xmol_;
198 }
199 
200 double vcs_VolPhase::moleFraction(size_t k) const
201 {
202  return Xmol_[k];
203 }
204 
205 void vcs_VolPhase::setMoleFractionsState(const double totalMoles,
206  const double* const moleFractions,
207  const int vcsStateStatus)
208 {
209  if (totalMoles != 0.0) {
210  // There are other ways to set the mole fractions when VCS_STATECALC
211  // is set to a normal settting.
212  if (vcsStateStatus != VCS_STATECALC_TMP) {
213  throw CanteraError("vcs_VolPhase::setMolesFractionsState",
214  "inappropriate usage");
215  }
216  m_UpToDate = false;
217  m_vcsStateStatus = VCS_STATECALC_TMP;
218  if (m_existence == VCS_PHASE_EXIST_ZEROEDPHASE) {
219  throw CanteraError("vcs_VolPhase::setMolesFractionsState",
220  "inappropriate usage");
221  }
222  m_existence = VCS_PHASE_EXIST_YES;
223  } else {
224  m_UpToDate = true;
225  m_vcsStateStatus = vcsStateStatus;
226  m_existence = std::min(m_existence, VCS_PHASE_EXIST_NO);
227  }
228  double fractotal = 1.0;
229  v_totalMoles = totalMoles;
230  if (m_totalMolesInert > 0.0) {
231  if (m_totalMolesInert > v_totalMoles) {
232  throw CanteraError("vcs_VolPhase::setMolesFractionsState",
233  "inerts greater than total: {} {}",
234  v_totalMoles, m_totalMolesInert);
235  }
236  fractotal = 1.0 - m_totalMolesInert/v_totalMoles;
237  }
238  double sum = 0.0;
239  for (size_t k = 0; k < m_numSpecies; k++) {
240  Xmol_[k] = moleFractions[k];
241  sum += moleFractions[k];
242  }
243  if (sum == 0.0) {
244  throw CanteraError("vcs_VolPhase::setMolesFractionsState",
245  "inappropriate usage");
246  }
247  if (sum != fractotal) {
248  for (size_t k = 0; k < m_numSpecies; k++) {
249  Xmol_[k] *= (fractotal /sum);
250  }
251  }
252  _updateMoleFractionDependencies();
253 }
254 
255 void vcs_VolPhase::setMolesFromVCS(const int stateCalc,
256  const double* molesSpeciesVCS)
257 {
258  v_totalMoles = m_totalMolesInert;
259 
260  if (molesSpeciesVCS == 0) {
261  AssertThrowMsg(m_owningSolverObject, "vcs_VolPhase::setMolesFromVCS",
262  "shouldn't be here");
263  if (stateCalc == VCS_STATECALC_OLD) {
264  molesSpeciesVCS = &m_owningSolverObject->m_molNumSpecies_old[0];
265  } else if (stateCalc == VCS_STATECALC_NEW) {
266  molesSpeciesVCS = &m_owningSolverObject->m_molNumSpecies_new[0];
267  } else {
268  throw CanteraError("vcs_VolPhase::setMolesFromVCS", "shouldn't be here");
269  }
270  } else if (m_owningSolverObject) {
271  // if (stateCalc == VCS_STATECALC_OLD) {
272  // if (molesSpeciesVCS != &m_owningSolverObject->m_molNumSpecies_old[0]) {
273  // throw CanteraError("vcs_VolPhase::setMolesFromVCS", "shouldn't be here");
274  // }
275  // } else if (stateCalc == VCS_STATECALC_NEW) {
276  // if (molesSpeciesVCS != &m_owningSolverObject->m_molNumSpecies_new[0]) {
277  // throw CanteraError("vcs_VolPhase::setMolesFromVCS", "shouldn't be here");
278  // }
279  // }
280  }
281 
282  for (size_t k = 0; k < m_numSpecies; k++) {
283  if (m_speciesUnknownType[k] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
284  size_t kglob = IndSpecies[k];
285  v_totalMoles += std::max(0.0, molesSpeciesVCS[kglob]);
286  }
287  }
288  if (v_totalMoles > 0.0) {
289  for (size_t k = 0; k < m_numSpecies; k++) {
290  if (m_speciesUnknownType[k] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) {
291  size_t kglob = IndSpecies[k];
292  double tmp = std::max(0.0, molesSpeciesVCS[kglob]);
293  Xmol_[k] = tmp / v_totalMoles;
294  }
295  }
296  m_existence = VCS_PHASE_EXIST_YES;
297  } else {
298  // This is where we will start to store a better approximation
299  // for the mole fractions, when the phase doesn't exist.
300  // This is currently unimplemented.
301  m_existence = VCS_PHASE_EXIST_NO;
302  }
303 
304  // Update the electric potential if it is a solution variable in the
305  // equation system
306  if (m_phiVarIndex != npos) {
307  size_t kglob = IndSpecies[m_phiVarIndex];
308  if (m_numSpecies == 1) {
309  Xmol_[m_phiVarIndex] = 1.0;
310  } else {
311  Xmol_[m_phiVarIndex] = 0.0;
312  }
313  double phi = molesSpeciesVCS[kglob];
314  setElectricPotential(phi);
315  if (m_numSpecies == 1) {
316  m_existence = VCS_PHASE_EXIST_YES;
317  }
318  }
319  _updateMoleFractionDependencies();
320  if (m_totalMolesInert > 0.0) {
321  m_existence = VCS_PHASE_EXIST_ALWAYS;
322  }
323 
324  // If stateCalc is old and the total moles is positive, then we have a valid
325  // state. If the phase went away, it would be a valid starting point for
326  // F_k's. So, save the state.
327  if (stateCalc == VCS_STATECALC_OLD && v_totalMoles > 0.0) {
328  creationMoleNumbers_ = Xmol_;
329  }
330 
331  // Set flags indicating we are up to date with the VCS state vector.
332  m_UpToDate = true;
333  m_vcsStateStatus = stateCalc;
334 }
335 
336 void vcs_VolPhase::setMolesFromVCSCheck(const int vcsStateStatus,
337  const double* molesSpeciesVCS,
338  const double* const TPhMoles)
339 {
340  setMolesFromVCS(vcsStateStatus, molesSpeciesVCS);
341 
342  // Check for consistency with TPhMoles[]
343  double Tcheck = TPhMoles[VP_ID_];
344  if (Tcheck != v_totalMoles) {
345  if (vcs_doubleEqual(Tcheck, v_totalMoles)) {
346  Tcheck = v_totalMoles;
347  } else {
348  throw CanteraError("vcs_VolPhase::setMolesFromVCSCheck",
349  "We have a consistency problem: {} {}", Tcheck, v_totalMoles);
350  }
351  }
352 }
353 
354 void vcs_VolPhase::updateFromVCS_MoleNumbers(const int vcsStateStatus)
355 {
356  if ((!m_UpToDate || vcsStateStatus != m_vcsStateStatus) && m_owningSolverObject &&
357  (vcsStateStatus == VCS_STATECALC_OLD || vcsStateStatus == VCS_STATECALC_NEW)) {
358  setMolesFromVCS(vcsStateStatus);
359  }
360 }
361 
362 void vcs_VolPhase::sendToVCS_ActCoeff(const int vcsStateStatus,
363  double* const AC)
364 {
365  updateFromVCS_MoleNumbers(vcsStateStatus);
366  if (!m_UpToDate_AC) {
367  _updateActCoeff();
368  }
369  for (size_t k = 0; k < m_numSpecies; k++) {
370  size_t kglob = IndSpecies[k];
371  AC[kglob] = ActCoeff[k];
372  }
373 }
374 
375 double vcs_VolPhase::sendToVCS_VolPM(double* const VolPM) const
376 {
377  if (!m_UpToDate_VolPM) {
378  _updateVolPM();
379  }
380  for (size_t k = 0; k < m_numSpecies; k++) {
381  size_t kglob = IndSpecies[k];
382  VolPM[kglob] = PartialMolarVol[k];
383  }
384  return m_totalVol;
385 }
386 
387 void vcs_VolPhase::sendToVCS_GStar(double* const gstar) const
388 {
389  if (!m_UpToDate_GStar) {
390  _updateGStar();
391  }
392  for (size_t k = 0; k < m_numSpecies; k++) {
393  size_t kglob = IndSpecies[k];
394  gstar[kglob] = StarChemicalPotential[k];
395  }
396 }
397 
398 void vcs_VolPhase::setElectricPotential(const double phi)
399 {
400  m_phi = phi;
401  TP_ptr->setElectricPotential(m_phi);
402  // We have changed the state variable. Set uptodate flags to false
403  m_UpToDate_AC = false;
404  m_UpToDate_VolStar = false;
405  m_UpToDate_VolPM = false;
406  m_UpToDate_GStar = false;
407 }
408 
409 double vcs_VolPhase::electricPotential() const
410 {
411  return m_phi;
412 }
413 
414 void vcs_VolPhase::setState_TP(const double temp, const double pres)
415 {
416  if (Temp_ == temp && Pres_ == pres) {
417  return;
418  }
419  TP_ptr->setElectricPotential(m_phi);
420  TP_ptr->setState_TP(temp, pres);
421  Temp_ = temp;
422  Pres_ = pres;
423  m_UpToDate_AC = false;
424  m_UpToDate_VolStar = false;
425  m_UpToDate_VolPM = false;
426  m_UpToDate_GStar = false;
427  m_UpToDate_G0 = false;
428 }
429 
430 void vcs_VolPhase::setState_T(const double temp)
431 {
432  setState_TP(temp, Pres_);
433 }
434 
435 void vcs_VolPhase::_updateVolStar() const
436 {
437  TP_ptr->getStandardVolumes(&StarMolarVol[0]);
438  m_UpToDate_VolStar = true;
439 }
440 
441 double vcs_VolPhase::VolStar_calc_one(size_t kspec) const
442 {
443  if (!m_UpToDate_VolStar) {
444  _updateVolStar();
445  }
446  return StarMolarVol[kspec];
447 }
448 
449 double vcs_VolPhase::_updateVolPM() const
450 {
451  TP_ptr->getPartialMolarVolumes(&PartialMolarVol[0]);
452  m_totalVol = 0.0;
453  for (size_t k = 0; k < m_numSpecies; k++) {
454  m_totalVol += PartialMolarVol[k] * Xmol_[k];
455  }
456  m_totalVol *= v_totalMoles;
457 
458  if (m_totalMolesInert > 0.0) {
459  if (m_gasPhase) {
460  double volI = m_totalMolesInert * GasConstant * Temp_ / Pres_;
461  m_totalVol += volI;
462  } else {
463  throw CanteraError("vcs_VolPhase::_updateVolPM", "unknown situation");
464  }
465  }
466  m_UpToDate_VolPM = true;
467  return m_totalVol;
468 }
469 
470 void vcs_VolPhase::_updateLnActCoeffJac()
471 {
472  double phaseTotalMoles = v_totalMoles;
473  if (phaseTotalMoles < 1.0E-14) {
474  phaseTotalMoles = 1.0;
475  }
476 
477  // Evaluate the current base activity coefficients if necessary
478  if (!m_UpToDate_AC) {
479  _updateActCoeff();
480  }
481  if (!TP_ptr) {
482  return;
483  }
484  TP_ptr->getdlnActCoeffdlnN(m_numSpecies, &np_dLnActCoeffdMolNumber(0,0));
485  for (size_t j = 0; j < m_numSpecies; j++) {
486  double moles_j_base = phaseTotalMoles * Xmol_[j];
487  double* const np_lnActCoeffCol = np_dLnActCoeffdMolNumber.ptrColumn(j);
488  if (moles_j_base < 1.0E-200) {
489  moles_j_base = 1.0E-7 * moles_j_base + 1.0E-13 * phaseTotalMoles + 1.0E-150;
490  }
491  for (size_t k = 0; k < m_numSpecies; k++) {
492  np_lnActCoeffCol[k] = np_lnActCoeffCol[k] * phaseTotalMoles / moles_j_base;
493  }
494  }
495 
496  double deltaMoles_j = 0.0;
497  // Make copies of ActCoeff and Xmol_ for use in taking differences
498  vector<double> ActCoeff_Base(ActCoeff);
499  vector<double> Xmol_Base(Xmol_);
500  double TMoles_base = phaseTotalMoles;
501 
502  // Loop over the columns species to be deltad
503  for (size_t j = 0; j < m_numSpecies; j++) {
504  // Calculate a value for the delta moles of species j. Note Xmol_[] and
505  // Tmoles are always positive or zero quantities.
506  double moles_j_base = phaseTotalMoles * Xmol_Base[j];
507  deltaMoles_j = 1.0E-7 * moles_j_base + 1.0E-13 * phaseTotalMoles + 1.0E-150;
508 
509  // Now, update the total moles in the phase and all of the mole
510  // fractions based on this.
511  phaseTotalMoles = TMoles_base + deltaMoles_j;
512  for (size_t k = 0; k < m_numSpecies; k++) {
513  Xmol_[k] = Xmol_Base[k] * TMoles_base / phaseTotalMoles;
514  }
515  Xmol_[j] = (moles_j_base + deltaMoles_j) / phaseTotalMoles;
516 
517  // Go get new values for the activity coefficients. Note this calls
518  // setState_PX();
519  _updateMoleFractionDependencies();
520  _updateActCoeff();
521 
522  // Revert to the base case Xmol_, v_totalMoles
523  v_totalMoles = TMoles_base;
524  Xmol_ = Xmol_Base;
525  }
526 
527  // Go get base values for the activity coefficients. Note this calls
528  // setState_TPX() again; Just wanted to make sure that cantera is in sync
529  // with VolPhase after this call.
530  setMoleFractions(&Xmol_Base[0]);
531  _updateMoleFractionDependencies();
532  _updateActCoeff();
533 }
534 
535 void vcs_VolPhase::sendToVCS_LnActCoeffJac(Array2D& np_LnACJac_VCS)
536 {
537  // update the Ln Act Coeff Jacobian entries with respect to the mole number
538  // of species in the phase -> we always assume that they are out of date.
539  _updateLnActCoeffJac();
540 
541  // Now copy over the values
542  for (size_t j = 0; j < m_numSpecies; j++) {
543  size_t jglob = IndSpecies[j];
544  for (size_t k = 0; k < m_numSpecies; k++) {
545  size_t kglob = IndSpecies[k];
546  np_LnACJac_VCS(kglob,jglob) = np_dLnActCoeffdMolNumber(k,j);
547  }
548  }
549 }
550 
551 void vcs_VolPhase::setPtrThermoPhase(ThermoPhase* tp_ptr)
552 {
553  TP_ptr = tp_ptr;
554  Temp_ = TP_ptr->temperature();
555  Pres_ = TP_ptr->pressure();
556  setState_TP(Temp_, Pres_);
557  m_phi = TP_ptr->electricPotential();
558  size_t nsp = TP_ptr->nSpecies();
559  size_t nelem = TP_ptr->nElements();
560  if (nsp != m_numSpecies) {
561  if (m_numSpecies != 0) {
562  warn_user("vcs_VolPhase::setPtrThermoPhase",
563  "Nsp != NVolSpeces: {} {}", nsp, m_numSpecies);
564  }
565  resize(VP_ID_, nsp, nelem, PhaseName.c_str());
566  }
567  TP_ptr->getMoleFractions(&Xmol_[0]);
568  creationMoleNumbers_ = Xmol_;
569  _updateMoleFractionDependencies();
570 
571  // figure out ideal solution tag
572  if (nsp == 1) {
573  m_isIdealSoln = true;
574  } else {
575  m_isIdealSoln = TP_ptr->isIdeal();
576  }
577 }
578 
579 const ThermoPhase* vcs_VolPhase::ptrThermoPhase() const
580 {
581  return TP_ptr;
582 }
583 
584 double vcs_VolPhase::totalMoles() const
585 {
586  return v_totalMoles;
587 }
588 
589 double vcs_VolPhase::molefraction(size_t k) const
590 {
591  return Xmol_[k];
592 }
593 
594 void vcs_VolPhase::setCreationMoleNumbers(const double* const n_k,
595  const vector<size_t> &creationGlobalRxnNumbers)
596 {
597  creationMoleNumbers_.assign(n_k, n_k+m_numSpecies);
598  for (size_t k = 0; k < m_numSpecies; k++) {
599  creationGlobalRxnNumbers_[k] = creationGlobalRxnNumbers[k];
600  }
601 }
602 
603 const vector<double>& vcs_VolPhase::creationMoleNumbers(
604  vector<size_t> &creationGlobalRxnNumbers) const
605 {
606  creationGlobalRxnNumbers = creationGlobalRxnNumbers_;
607  return creationMoleNumbers_;
608 }
609 
610 void vcs_VolPhase::setTotalMoles(const double totalMols)
611 {
612  v_totalMoles = totalMols;
613  if (m_totalMolesInert > 0.0) {
614  m_existence = VCS_PHASE_EXIST_ALWAYS;
615  AssertThrowMsg(totalMols >= m_totalMolesInert,
616  "vcs_VolPhase::setTotalMoles",
617  "totalMoles less than inert moles: {} {}",
618  totalMols, m_totalMolesInert);
619  } else {
620  if (m_singleSpecies && (m_phiVarIndex == 0)) {
621  m_existence = VCS_PHASE_EXIST_ALWAYS;
622  } else {
623  if (totalMols > 0.0) {
624  m_existence = VCS_PHASE_EXIST_YES;
625  } else {
626  m_existence = VCS_PHASE_EXIST_NO;
627  }
628  }
629  }
630 }
631 
632 void vcs_VolPhase::setMolesOutOfDate(int stateCalc)
633 {
634  m_UpToDate = false;
635  if (stateCalc != -1) {
636  m_vcsStateStatus = stateCalc;
637  }
638 }
639 
640 void vcs_VolPhase::setMolesCurrent(int stateCalc)
641 {
642  m_UpToDate = true;
643  m_vcsStateStatus = stateCalc;
644 }
645 
646 bool vcs_VolPhase::isIdealSoln() const
647 {
648  return m_isIdealSoln;
649 }
650 
651 size_t vcs_VolPhase::phiVarIndex() const
652 {
653  return m_phiVarIndex;
654 }
655 
656 void vcs_VolPhase::setPhiVarIndex(size_t phiVarIndex)
657 {
658  m_phiVarIndex = phiVarIndex;
659  m_speciesUnknownType[m_phiVarIndex] = VCS_SPECIES_TYPE_INTERFACIALVOLTAGE;
660  if (m_singleSpecies && m_phiVarIndex == 0) {
661  m_existence = VCS_PHASE_EXIST_ALWAYS;
662  }
663 }
664 
665 vcs_SpeciesProperties* vcs_VolPhase::speciesProperty(const size_t kindex)
666 {
667  return ListSpeciesPtr[kindex];
668 }
669 
670 int vcs_VolPhase::exists() const
671 {
672  return m_existence;
673 }
674 
675 void vcs_VolPhase::setExistence(const int existence)
676 {
677  if (existence == VCS_PHASE_EXIST_NO || existence == VCS_PHASE_EXIST_ZEROEDPHASE) {
678  if (v_totalMoles != 0.0) {
679  throw CanteraError("vcs_VolPhase::setExistence",
680  "setting false existence for phase with moles");
681  }
682  } else if (m_totalMolesInert == 0.0) {
683  if (v_totalMoles == 0.0 && (!m_singleSpecies || m_phiVarIndex != 0)) {
684  throw CanteraError("vcs_VolPhase::setExistence",
685  "setting true existence for phase with no moles");
686  }
687  }
688  if (m_singleSpecies && m_phiVarIndex == 0 && (existence == VCS_PHASE_EXIST_NO || existence == VCS_PHASE_EXIST_ZEROEDPHASE)) {
689  throw CanteraError("vcs_VolPhase::setExistence",
690  "Trying to set existence of an electron phase to false");
691  }
692  m_existence = existence;
693 }
694 
695 size_t vcs_VolPhase::spGlobalIndexVCS(const size_t spIndex) const
696 {
697  return IndSpecies[spIndex];
698 }
699 
700 void vcs_VolPhase::setSpGlobalIndexVCS(const size_t spIndex,
701  const size_t spGlobalIndex)
702 {
703  IndSpecies[spIndex] = spGlobalIndex;
704  if (spGlobalIndex >= m_numElemConstraints) {
705  creationGlobalRxnNumbers_[spIndex] = spGlobalIndex - m_numElemConstraints;
706  }
707 }
708 
709 void vcs_VolPhase::setTotalMolesInert(const double tMolesInert)
710 {
711  if (m_totalMolesInert != tMolesInert) {
712  m_UpToDate = false;
713  m_UpToDate_AC = false;
714  m_UpToDate_VolStar = false;
715  m_UpToDate_VolPM = false;
716  m_UpToDate_GStar = false;
717  m_UpToDate_G0 = false;
718  v_totalMoles += (tMolesInert - m_totalMolesInert);
719  m_totalMolesInert = tMolesInert;
720  }
721  if (m_totalMolesInert > 0.0) {
722  m_existence = VCS_PHASE_EXIST_ALWAYS;
723  } else if (m_singleSpecies && (m_phiVarIndex == 0)) {
724  m_existence = VCS_PHASE_EXIST_ALWAYS;
725  } else {
726  if (v_totalMoles > 0.0) {
727  m_existence = VCS_PHASE_EXIST_YES;
728  } else {
729  m_existence = VCS_PHASE_EXIST_NO;
730  }
731  }
732 }
733 
734 double vcs_VolPhase::totalMolesInert() const
735 {
736  return m_totalMolesInert;
737 }
738 
739 size_t vcs_VolPhase::elemGlobalIndex(const size_t e) const
740 {
741  AssertThrow(e < m_numElemConstraints, " vcs_VolPhase::elemGlobalIndex");
742  return m_elemGlobalIndex[e];
743 }
744 
745 void vcs_VolPhase::setElemGlobalIndex(const size_t eLocal, const size_t eGlobal)
746 {
747  AssertThrow(eLocal < m_numElemConstraints,
748  "vcs_VolPhase::setElemGlobalIndex");
749  m_elemGlobalIndex[eLocal] = eGlobal;
750 }
751 
752 size_t vcs_VolPhase::nElemConstraints() const
753 {
754  return m_numElemConstraints;
755 }
756 
757 string vcs_VolPhase::elementName(const size_t e) const
758 {
759  return m_elementNames[e];
760 }
761 
762 //! This function decides whether a phase has charged species or not.
763 static bool hasChargedSpecies(const ThermoPhase* const tPhase)
764 {
765  for (size_t k = 0; k < tPhase->nSpecies(); k++) {
766  if (tPhase->charge(k) != 0.0) {
767  return true;
768  }
769  }
770  return false;
771 }
772 
773 //! This utility routine decides whether a Cantera ThermoPhase needs
774 //! a constraint equation representing the charge neutrality of the
775 //! phase. It does this by searching for charged species. If it
776 //! finds one, and if the phase needs one, then it returns true.
777 static bool chargeNeutralityElement(const ThermoPhase* const tPhase)
778 {
779  int hasCharge = hasChargedSpecies(tPhase);
780  if (tPhase->chargeNeutralityNecessary() && hasCharge) {
781  return true;
782  }
783  return false;
784 }
785 
786 size_t vcs_VolPhase::transferElementsFM(const ThermoPhase* const tPhase)
787 {
788  size_t nebase = tPhase->nElements();
789  size_t ne = nebase;
790  size_t ns = tPhase->nSpecies();
791 
792  // Decide whether we need an extra element constraint for charge
793  // neutrality of the phase
794  bool cne = chargeNeutralityElement(tPhase);
795  if (cne) {
796  ChargeNeutralityElement = ne;
797  ne++;
798  }
799 
800  // Assign and resize structures
801  elemResize(ne);
802 
803  if (ChargeNeutralityElement != npos) {
804  m_elementType[ChargeNeutralityElement] = VCS_ELEM_TYPE_CHARGENEUTRALITY;
805  }
806 
807  size_t eFound = npos;
808  if (hasChargedSpecies(tPhase)) {
809  if (cne) {
810  // We need a charge neutrality constraint. We also have an Electron
811  // Element. These are duplicates of each other. To avoid trouble
812  // with possible range error conflicts, sometimes we eliminate the
813  // Electron condition. Flag that condition for elimination by
814  // toggling the ElActive variable. If we find we need it later, we
815  // will retoggle ElActive to true.
816  for (size_t eT = 0; eT < nebase; eT++) {
817  if (tPhase->elementName(eT) == "E") {
818  eFound = eT;
819  m_elementActive[eT] = 0;
820  m_elementType[eT] = VCS_ELEM_TYPE_ELECTRONCHARGE;
821  }
822  }
823  } else {
824  for (size_t eT = 0; eT < nebase; eT++) {
825  if (tPhase->elementName(eT) == "E") {
826  eFound = eT;
827  m_elementType[eT] = VCS_ELEM_TYPE_ELECTRONCHARGE;
828  }
829  }
830  }
831  if (eFound == npos) {
832  eFound = ne;
833  m_elementType[ne] = VCS_ELEM_TYPE_ELECTRONCHARGE;
834  m_elementActive[ne] = 0;
835  string ename = "E";
836  m_elementNames[ne] = ename;
837  ne++;
838  elemResize(ne);
839  }
840  }
841 
842  m_formulaMatrix.resize(ns, ne, 0.0);
843  m_speciesUnknownType.resize(ns, VCS_SPECIES_TYPE_MOLNUM);
844  elemResize(ne);
845 
846  size_t e = 0;
847  for (size_t eT = 0; eT < nebase; eT++) {
848  m_elementNames[e] = tPhase->elementName(eT);
849  m_elementType[e] = tPhase->elementType(eT);
850  e++;
851  }
852 
853  if (cne) {
854  string pname = tPhase->name();
855  if (pname == "") {
856  pname = fmt::format("phase{}", VP_ID_);
857  }
858  e = ChargeNeutralityElement;
859  m_elementNames[e] = "cn_" + pname;
860  }
861 
862  for (size_t k = 0; k < ns; k++) {
863  e = 0;
864  for (size_t eT = 0; eT < nebase; eT++) {
865  m_formulaMatrix(k,e) = tPhase->nAtoms(k, eT);
866  e++;
867  }
868  if (eFound != npos) {
869  m_formulaMatrix(k,eFound) = - tPhase->charge(k);
870  }
871  }
872 
873  if (cne) {
874  for (size_t k = 0; k < ns; k++) {
875  m_formulaMatrix(k,ChargeNeutralityElement) = tPhase->charge(k);
876  }
877  }
878 
879  // Here, we figure out what is the species types are The logic isn't set in
880  // stone, and is just for a particular type of problem that I'm solving
881  // first.
882  if (ns == 1 && tPhase->charge(0) != 0.0) {
883  m_speciesUnknownType[0] = VCS_SPECIES_TYPE_INTERFACIALVOLTAGE;
884  setPhiVarIndex(0);
885  }
886 
887  return ne;
888 }
889 
890 int vcs_VolPhase::elementType(const size_t e) const
891 {
892  return m_elementType[e];
893 }
894 
895 const Array2D& vcs_VolPhase::getFormulaMatrix() const
896 {
897  return m_formulaMatrix;
898 }
899 
900 int vcs_VolPhase::speciesUnknownType(const size_t k) const
901 {
902  return m_speciesUnknownType[k];
903 }
904 
905 int vcs_VolPhase::elementActive(const size_t e) const
906 {
907  return m_elementActive[e];
908 }
909 
910 size_t vcs_VolPhase::nSpecies() const
911 {
912  return m_numSpecies;
913 }
914 
915 string vcs_VolPhase::eos_name() const
916 {
917  switch (m_eqnState) {
918  case VCS_EOS_CONSTANT:
919  return "Constant";
920  case VCS_EOS_IDEAL_GAS:
921  return "Ideal Gas";
922  case VCS_EOS_STOICH_SUB:
923  return "Stoich Sub";
924  case VCS_EOS_IDEAL_SOLN:
925  return "Ideal Soln";
926  case VCS_EOS_DEBEYE_HUCKEL:
927  return "Debeye Huckel";
928  case VCS_EOS_REDLICH_KWONG:
929  return "Redlich_Kwong";
930  case VCS_EOS_REGULAR_SOLN:
931  return "Regular Soln";
932  default:
933  return fmt::format("UnkType: {:7d}", m_eqnState);
934  break;
935  }
936 }
937 
938 }
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.
Definition: ctexceptions.h:66
size_t nSpecies() const
Returns the number of species in the phase.
Definition: Phase.h:231
double temperature() const
Temperature (K).
Definition: Phase.h:562
int elementType(size_t m) const
Return the element constraint type Possible types include:
Definition: Phase.cpp:91
double nAtoms(size_t k, size_t m) const
Number of atoms of element m in species k.
Definition: Phase.cpp:103
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:49
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:538
string name() const
Return the name of the phase.
Definition: Phase.cpp:20
Base class for a phase with thermodynamic properties.
Definition: ThermoPhase.h:390
bool chargeNeutralityNecessary() const
Returns the chargeNeutralityNecessity boolean.
Definition: ThermoPhase.h:516
Properties of a single species.
#define AssertThrow(expr, procedure)
Assertion must be true or an error is thrown.
Definition: ctexceptions.h:261
#define AssertThrowMsg(expr, procedure,...)
Assertion must be true or an error is thrown.
Definition: ctexceptions.h:278
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition: ct_defs.h:120
void warn_user(const string &method, const string &msg, const Args &... args)
Print a user warning raised from method as CanteraWarning.
Definition: global.h:267
Namespace for the Cantera kernel.
Definition: AnyMap.cpp:564
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:180
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
static bool chargeNeutralityElement(const ThermoPhase *const tPhase)
This utility routine decides whether a Cantera ThermoPhase needs a constraint equation representing t...
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:286
#define VCS_STATECALC_OLD
State Calculation based on the old or base mole numbers.
Definition: vcs_defs.h:297
#define VCS_STATECALC_TMP
State Calculation based on a temporary set of mole numbers.
Definition: vcs_defs.h:308
#define VCS_PHASE_EXIST_NO
Phase doesn't currently exist in the mixture.
Definition: vcs_defs.h:200
#define VCS_PHASE_EXIST_ALWAYS
Definition: vcs_defs.h:187
#define VCS_ELEM_TYPE_ABSPOS
Normal element constraint consisting of positive coefficients for the formula matrix.
Definition: vcs_defs.h:226
#define VCS_STATECALC_NEW
State Calculation based on the new or tentative mole numbers.
Definition: vcs_defs.h:300
#define VCS_SPECIES_TYPE_MOLNUM
Unknown refers to mole number of a single species.
Definition: vcs_defs.h:278
#define VCS_ELEM_TYPE_ELECTRONCHARGE
This refers to conservation of electrons.
Definition: vcs_defs.h:232
#define VCS_ELEM_TYPE_CHARGENEUTRALITY
This refers to a charge neutrality of a single phase.
Definition: vcs_defs.h:238
#define VCS_PHASE_EXIST_YES
Phase is a normal phase that currently exists.
Definition: vcs_defs.h:190
#define VCS_PHASE_EXIST_ZEROEDPHASE
Phase currently is zeroed due to a programmatic issue.
Definition: vcs_defs.h:206
Header file for the internal object that holds the vcs equilibrium problem (see Class VCS_SOLVE and C...