Cantera  2.5.1
IonsFromNeutralVPSSTP.cpp
Go to the documentation of this file.
1 /**
2  * @file IonsFromNeutralVPSSTP.cpp
3  * Definitions for the object which treats ionic liquids as made of ions as species
4  * even though the thermodynamics is obtained from the neutral molecule representation.
5  * (see \ref thermoprops
6  * and class \link Cantera::IonsFromNeutralVPSSTP IonsFromNeutralVPSSTP\endlink).
7  *
8  * Header file for a derived class of ThermoPhase that handles variable pressure
9  * standard state methods for calculating thermodynamic properties that are
10  * further based upon expressions for the excess Gibbs free energy expressed as
11  * a function of the mole fractions.
12  */
13 
14 // This file is part of Cantera. See License.txt in the top-level directory or
15 // at https://cantera.org/license.txt for license and copyright information.
16 
21 
22 #include <fstream>
23 
24 using namespace std;
25 
26 namespace Cantera
27 {
28 
29 IonsFromNeutralVPSSTP::IonsFromNeutralVPSSTP() :
30  ionSolnType_(cIonSolnType_SINGLEANION),
31  numNeutralMoleculeSpecies_(0),
32  indexSpecialSpecies_(npos),
33  neutralMoleculePhase_(0),
34  geThermo(0)
35 {
36 }
37 
38 IonsFromNeutralVPSSTP::IonsFromNeutralVPSSTP(const std::string& inputFile,
39  const std::string& id_) :
40  ionSolnType_(cIonSolnType_SINGLEANION),
41  numNeutralMoleculeSpecies_(0),
42  indexSpecialSpecies_(npos)
43 {
44  initThermoFile(inputFile, id_);
45 }
46 
48  const std::string& id_) :
49  ionSolnType_(cIonSolnType_SINGLEANION),
50  numNeutralMoleculeSpecies_(0),
51  indexSpecialSpecies_(npos)
52 {
53  importPhase(phaseRoot, this);
54 }
55 
56 // ------------ Molar Thermodynamic Properties ----------------------
57 
59 {
60  getPartialMolarEnthalpies(m_work.data());
61  return mean_X(m_work);
62 }
63 
65 {
66  getPartialMolarEntropies(m_work.data());
67  return mean_X(m_work);
68 }
69 
71 {
72  getChemPotentials(m_work.data());
73  return mean_X(m_work);
74 }
75 
77 {
78  getPartialMolarCp(m_work.data());
79  return mean_X(m_work);
80 }
81 
83 {
84  // Need to revisit this, as it is wrong
85  getPartialMolarCp(m_work.data());
86  return mean_X(m_work);
87 }
88 
89 // -- Activities, Standard States, Activity Concentrations -----------
90 
92  vector_fp& charges, std::vector<size_t>& neutMolIndex) const
93 {
94  coeffs = fm_neutralMolec_ions_;
95  charges = m_speciesCharge;
96  neutMolIndex = fm_invert_ionForNeutral;
97 }
98 
100 {
101  // Update the activity coefficients
103 
104  // take the exp of the internally stored coefficients.
105  for (size_t k = 0; k < m_kk; k++) {
106  ac[k] = exp(lnActCoeff_Scaled_[k]);
107  }
108 }
109 
110 // --------- Partial Molar Properties of the Solution -------------
111 
113 {
114  size_t icat, jNeut;
115  doublereal xx, fact2;
116 
117  // Get the standard chemical potentials of neutral molecules
118  neutralMoleculePhase_->getStandardChemPotentials(muNeutralMolecule_.data());
119 
120  switch (ionSolnType_) {
121  case cIonSolnType_PASSTHROUGH:
122  neutralMoleculePhase_->getChemPotentials(mu);
123  break;
124  case cIonSolnType_SINGLEANION:
125  neutralMoleculePhase_->getLnActivityCoefficients(lnActCoeff_NeutralMolecule_.data());
126  fact2 = 2.0 * RT() * log(2.0);
127 
128  // Do the cation list
129  for (size_t k = 0; k < cationList_.size(); k++) {
130  // Get the id for the next cation
131  icat = cationList_[k];
132  jNeut = fm_invert_ionForNeutral[icat];
133  xx = std::max(SmallNumber, moleFractions_[icat]);
134  mu[icat] = muNeutralMolecule_[jNeut] + fact2 + RT() * (lnActCoeff_NeutralMolecule_[jNeut] + log(xx));
135  }
136 
137  // Do the anion list
138  icat = anionList_[0];
139  jNeut = fm_invert_ionForNeutral[icat];
140  xx = std::max(SmallNumber, moleFractions_[icat]);
141  mu[icat] = RT() * log(xx);
142 
143  // Do the list of neutral molecules
144  for (size_t k = 0; k < passThroughList_.size(); k++) {
145  icat = passThroughList_[k];
146  jNeut = fm_invert_ionForNeutral[icat];
147  xx = std::max(SmallNumber, moleFractions_[icat]);
148  mu[icat] = muNeutralMolecule_[jNeut] + RT() * (lnActCoeff_NeutralMolecule_[jNeut] + log(xx));
149  }
150  break;
151 
152  case cIonSolnType_SINGLECATION:
153  throw CanteraError("IonsFromNeutralVPSSTP::getChemPotentials", "Unknown type");
154  case cIonSolnType_MULTICATIONANION:
155  throw CanteraError("IonsFromNeutralVPSSTP::getChemPotentials", "Unknown type");
156  default:
157  throw CanteraError("IonsFromNeutralVPSSTP::getChemPotentials", "Unknown type");
158  }
159 }
160 
162 {
163  // Get the nondimensional standard state enthalpies
164  getEnthalpy_RT(hbar);
165 
166  // dimensionalize it.
167  for (size_t k = 0; k < m_kk; k++) {
168  hbar[k] *= RT();
169  }
170 
171  // Update the activity coefficients, This also update the internally stored
172  // molalities.
175  for (size_t k = 0; k < m_kk; k++) {
176  hbar[k] -= RT() * temperature() * dlnActCoeffdT_Scaled_[k];
177  }
178 }
179 
181 {
182  // Get the nondimensional standard state entropies
183  getEntropy_R(sbar);
184 
185  // Update the activity coefficients, This also update the internally stored
186  // molalities.
189 
190  for (size_t k = 0; k < m_kk; k++) {
191  double xx = std::max(moleFractions_[k], SmallNumber);
192  sbar[k] += - lnActCoeff_Scaled_[k] -log(xx) - temperature() * dlnActCoeffdT_Scaled_[k];
193  }
194 
195  // dimensionalize it.
196  for (size_t k = 0; k < m_kk; k++) {
197  sbar[k] *= GasConstant;
198  }
199 }
200 
201 void IonsFromNeutralVPSSTP::getdlnActCoeffdlnX_diag(doublereal* dlnActCoeffdlnX_diag) const
202 {
205 
206  for (size_t k = 0; k < m_kk; k++) {
207  dlnActCoeffdlnX_diag[k] = dlnActCoeffdlnX_diag_[k];
208  }
209 }
210 
211 void IonsFromNeutralVPSSTP::getdlnActCoeffdlnN_diag(doublereal* dlnActCoeffdlnN_diag) const
212 {
215 
216  for (size_t k = 0; k < m_kk; k++) {
217  dlnActCoeffdlnN_diag[k] = dlnActCoeffdlnN_diag_[k];
218  }
219 }
220 
221 void IonsFromNeutralVPSSTP::getdlnActCoeffdlnN(const size_t ld, doublereal* dlnActCoeffdlnN)
222 {
225  double* data = & dlnActCoeffdlnN_(0,0);
226  for (size_t k = 0; k < m_kk; k++) {
227  for (size_t m = 0; m < m_kk; m++) {
228  dlnActCoeffdlnN[ld * k + m] = data[m_kk * k + m];
229  }
230  }
231 }
232 
234 {
235  // This is a two phase process. First, we calculate the standard states
236  // within the neutral molecule phase.
237  neutralMoleculePhase_->setState_TP(temperature(), pressure());
238 
239  // Calculate the partial molar volumes, and then the density of the fluid
241 }
242 
243 void IonsFromNeutralVPSSTP::calcIonMoleFractions(doublereal* const mf) const
244 {
245  // Download the neutral mole fraction vector into the vector,
246  // NeutralMolecMoleFractions_[]
247  neutralMoleculePhase_->getMoleFractions(NeutralMolecMoleFractions_.data());
248 
249  // Zero the mole fractions
250  for (size_t k = 0; k < m_kk; k++) {
251  mf[k] = 0.0;
252  }
253 
254  // Use the formula matrix to calculate the relative mole numbers.
255  for (size_t jNeut = 0; jNeut < numNeutralMoleculeSpecies_; jNeut++) {
256  for (size_t k = 0; k < m_kk; k++) {
257  double fmij = fm_neutralMolec_ions_[k + jNeut * m_kk];
258  mf[k] += fmij * NeutralMolecMoleFractions_[jNeut];
259  }
260  }
261 
262  // Normalize the new mole fractions
263  doublereal sum = 0.0;
264  for (size_t k = 0; k < m_kk; k++) {
265  sum += mf[k];
266  }
267  for (size_t k = 0; k < m_kk; k++) {
268  mf[k] /= sum;
269  }
270 }
271 
273 {
274  size_t icat, jNeut;
275  doublereal fmij;
276  doublereal sum = 0.0;
277 
278  // Zero the vector we are trying to find.
279  for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
281  }
282  sum = -1.0;
283  for (size_t k = 0; k < m_kk; k++) {
284  sum += moleFractions_[k];
285  }
286  if (fabs(sum) > 1.0E-11) {
287  throw CanteraError("IonsFromNeutralVPSSTP::calcNeutralMoleculeMoleFractions",
288  "molefracts don't sum to one: {}", sum);
289  }
290 
291  switch (ionSolnType_) {
292  case cIonSolnType_PASSTHROUGH:
293  for (size_t k = 0; k < m_kk; k++) {
295  }
296  break;
297 
298  case cIonSolnType_SINGLEANION:
299  for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
301  }
302 
303  for (size_t k = 0; k < cationList_.size(); k++) {
304  // Get the id for the next cation
305  icat = cationList_[k];
306  jNeut = fm_invert_ionForNeutral[icat];
307  if (jNeut != npos) {
308  fmij = fm_neutralMolec_ions_[icat + jNeut * m_kk];
309  AssertTrace(fmij != 0.0);
310  NeutralMolecMoleFractions_[jNeut] += moleFractions_[icat] / fmij;
311  }
312  }
313 
314  for (size_t k = 0; k < passThroughList_.size(); k++) {
315  icat = passThroughList_[k];
316  jNeut = fm_invert_ionForNeutral[icat];
317  fmij = fm_neutralMolec_ions_[ icat + jNeut * m_kk];
318  NeutralMolecMoleFractions_[jNeut] += moleFractions_[icat] / fmij;
319  }
320 
321  for (size_t k = 0; k < m_kk; k++) {
323  }
324  for (jNeut = 0; jNeut < numNeutralMoleculeSpecies_; jNeut++) {
325  for (size_t k = 0; k < m_kk; k++) {
326  fmij = fm_neutralMolec_ions_[k + jNeut * m_kk];
327  moleFractionsTmp_[k] -= fmij * NeutralMolecMoleFractions_[jNeut];
328  }
329  }
330  for (size_t k = 0; k < m_kk; k++) {
331  if (fabs(moleFractionsTmp_[k]) > 1.0E-13) {
332  // Check to see if we have in fact found the inverse.
333  if (anionList_[0] != k) {
334  throw CanteraError("IonsFromNeutralVPSSTP::calcNeutralMoleculeMoleFractions",
335  "neutral molecule calc error");
336  } else {
337  // For the single anion case, we will allow some slippage
338  if (fabs(moleFractionsTmp_[k]) > 1.0E-5) {
339  throw CanteraError("IonsFromNeutralVPSSTP::calcNeutralMoleculeMoleFractions",
340  "neutral molecule calc error - anion");
341  }
342  }
343  }
344  }
345 
346  // Normalize the Neutral Molecule mole fractions
347  sum = 0.0;
348  for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
349  sum += NeutralMolecMoleFractions_[k];
350  }
351  for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
352  NeutralMolecMoleFractions_[k] /= sum;
353  }
354  break;
355 
356  case cIonSolnType_SINGLECATION:
357  throw CanteraError("IonsFromNeutralVPSSTP::calcNeutralMoleculeMoleFractions", "Unknown type");
358  break;
359  case cIonSolnType_MULTICATIONANION:
360  throw CanteraError("IonsFromNeutralVPSSTP::calcNeutralMoleculeMoleFractions", "Unknown type");
361  break;
362  default:
363  throw CanteraError("IonsFromNeutralVPSSTP::calcNeutralMoleculeMoleFractions", "Unknown type");
364  break;
365  }
366 }
367 
368 void IonsFromNeutralVPSSTP::getNeutralMoleculeMoleGrads(const doublereal* const dx, doublereal* const dy) const
369 {
370  doublereal sumy, sumdy;
371 
372  // check sum dx = 0
373  // Zero the vector we are trying to find.
374  for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
375  y_[k] = 0.0;
376  dy[k] = 0.0;
377  }
378 
379  switch (ionSolnType_) {
380 
381  case cIonSolnType_PASSTHROUGH:
382  for (size_t k = 0; k < m_kk; k++) {
383  dy[k] = dx[k];
384  }
385  break;
386 
387  case cIonSolnType_SINGLEANION:
388  for (size_t k = 0; k < cationList_.size(); k++) {
389  // Get the id for the next cation
390  size_t icat = cationList_[k];
391  size_t jNeut = fm_invert_ionForNeutral[icat];
392  if (jNeut != npos) {
393  double fmij = fm_neutralMolec_ions_[icat + jNeut * m_kk];
394  AssertTrace(fmij != 0.0);
395  const doublereal temp = 1.0/fmij;
396  dy[jNeut] += dx[icat] * temp;
397  y_[jNeut] += moleFractions_[icat] * temp;
398  }
399  }
400 
401  for (size_t k = 0; k < passThroughList_.size(); k++) {
402  size_t icat = passThroughList_[k];
403  size_t jNeut = fm_invert_ionForNeutral[icat];
404  double fmij = fm_neutralMolec_ions_[ icat + jNeut * m_kk];
405  const doublereal temp = 1.0/fmij;
406  dy[jNeut] += dx[icat] * temp;
407  y_[jNeut] += moleFractions_[icat] * temp;
408  }
409  // Normalize the Neutral Molecule mole fractions
410  sumy = 0.0;
411  sumdy = 0.0;
412  for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
413  sumy += y_[k];
414  sumdy += dy[k];
415  }
416  sumy = 1.0 / sumy;
417  for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
418  dy[k] = dy[k] * sumy - y_[k]*sumdy*sumy*sumy;
419  }
420 
421  break;
422 
423  case cIonSolnType_SINGLECATION:
424  throw CanteraError("IonsFromNeutralVPSSTP::getNeutralMoleculeMoleGrads",
425  "Unknown type");
426  break;
427  case cIonSolnType_MULTICATIONANION:
428  throw CanteraError("IonsFromNeutralVPSSTP::getNeutralMoleculeMoleGrads",
429  "Unknown type");
430  break;
431  default:
432  throw CanteraError("IonsFromNeutralVPSSTP::getNeutralMoleculeMoleGrads",
433  "Unknown type");
434  break;
435  }
436 }
437 
439 {
442  neutralMoleculePhase_->setMoleFractions(NeutralMolecMoleFractions_.data());
443 }
444 
445 // ------------ Partial Molar Properties of the Solution ------------
446 
447 //! Return the factor overlap
448 /*!
449  * @param elnamesVN
450  * @param elemVectorN
451  * @param nElementsN
452  * @param elnamesVI
453  * @param elemVectorI
454  * @param nElementsI
455  */
456 static double factorOverlap(const std::vector<std::string>& elnamesVN ,
457  const vector_fp& elemVectorN,
458  const size_t nElementsN,
459  const std::vector<std::string>& elnamesVI ,
460  const vector_fp& elemVectorI,
461  const size_t nElementsI)
462 {
463  double fMax = 1.0E100;
464  for (size_t mi = 0; mi < nElementsI; mi++) {
465  if (elnamesVI[mi] != "E" && elemVectorI[mi] > 1.0E-13) {
466  double eiNum = elemVectorI[mi];
467  for (size_t mn = 0; mn < nElementsN; mn++) {
468  if (elnamesVI[mi] == elnamesVN[mn]) {
469  if (elemVectorN[mn] <= 1.0E-13) {
470  return 0.0;
471  }
472  fMax = std::min(fMax, elemVectorN[mn]/eiNum);
473  }
474  }
475  }
476  }
477  return fMax;
478 }
479 
481  const AnyMap& rootNode)
482 {
483  ThermoPhase::setParameters(phaseNode, rootNode);
484  m_rootNode = rootNode;
485 }
486 
488 {
489  if (m_input.hasKey("neutral-phase")) {
490  string neutralName = m_input["neutral-phase"].asString();
491  const auto& slash = boost::ifind_last(neutralName, "/");
492  if (slash) {
493  string fileName(neutralName.begin(), slash.begin());
494  neutralName = string(slash.end(), neutralName.end());
495  AnyMap infile = AnyMap::fromYamlFile(fileName,
496  m_input.getString("__file__", ""));
497  AnyMap& phaseNode = infile["phases"].getMapWhere("name", neutralName);
498  setNeutralMoleculePhase(newPhase(phaseNode, infile));
499  } else {
500  AnyMap& phaseNode = m_rootNode["phases"].getMapWhere("name", neutralName);
501  setNeutralMoleculePhase(newPhase(phaseNode, m_rootNode));
502  }
503  }
504 
505  if (!neutralMoleculePhase_) {
506  throw CanteraError(
507  "IonsFromNeutralVPSSTP::initThermo",
508  "The neutral phase has not been initialized. Are you missing the "
509  "'neutral-phase' key?"
510  );
511  }
512 
513  size_t nElementsN = neutralMoleculePhase_->nElements();
514  const std::vector<std::string>& elnamesVN = neutralMoleculePhase_->elementNames();
515  vector_fp elemVectorN(nElementsN);
516 
517  size_t nElementsI = nElements();
518  const std::vector<std::string>& elnamesVI = elementNames();
519  vector_fp elemVectorI(nElementsI);
520 
521  if (indexSpecialSpecies_ == npos) {
522  throw CanteraError(
523  "IonsFromNeutralVPSSTP::initThermo",
524  "No special-species were specified in the phase."
525  );
526  }
527  for (size_t m = 0; m < nElementsI; m++) {
528  elemVectorI[m] = nAtoms(indexSpecialSpecies_, m);
529  }
530 
531  for (size_t jNeut = 0; jNeut < numNeutralMoleculeSpecies_; jNeut++) {
532  for (size_t m = 0; m < nElementsN; m++) {
533  elemVectorN[m] = neutralMoleculePhase_->nAtoms(jNeut, m);
534  }
535 
536  double fac = factorOverlap(elnamesVN, elemVectorN, nElementsN,
537  elnamesVI ,elemVectorI, nElementsI);
538  if (fac > 0.0) {
539  for (size_t m = 0; m < nElementsN; m++) {
540  for (size_t mi = 0; mi < nElementsI; mi++) {
541  if (elnamesVN[m] == elnamesVI[mi]) {
542  elemVectorN[m] -= fac * elemVectorI[mi];
543  }
544 
545  }
546  }
547  }
549 
550  for (size_t k = 0; k < m_kk; k++) {
551  for (size_t m = 0; m < nElementsI; m++) {
552  elemVectorI[m] = nAtoms(k, m);
553  }
554  fac = factorOverlap(elnamesVN, elemVectorN, nElementsN,
555  elnamesVI ,elemVectorI, nElementsI);
556  if (fac > 0.0) {
557  for (size_t m = 0; m < nElementsN; m++) {
558  for (size_t mi = 0; mi < nElementsI; mi++) {
559  if (elnamesVN[m] == elnamesVI[mi]) {
560  elemVectorN[m] -= fac * elemVectorI[mi];
561  }
562  }
563  }
564  bool notTaken = true;
565  for (size_t iNeut = 0; iNeut < jNeut; iNeut++) {
566  if (fm_invert_ionForNeutral[k] == iNeut) {
567  notTaken = false;
568  }
569  }
570  if (notTaken) {
571  fm_invert_ionForNeutral[k] = jNeut;
572  } else {
573  throw CanteraError("IonsFromNeutralVPSSTP::initThermo",
574  "Simple formula matrix generation failed, one cation is shared between two salts");
575  }
576  }
577  fm_neutralMolec_ions_[k + jNeut * m_kk] += fac;
578  }
579 
580  // Ok check the work
581  for (size_t m = 0; m < nElementsN; m++) {
582  if (fabs(elemVectorN[m]) > 1.0E-13) {
583  throw CanteraError("IonsFromNeutralVPSSTP::initThermo",
584  "Simple formula matrix generation failed");
585  }
586  }
587  }
588 
590 }
591 
592 void IonsFromNeutralVPSSTP::setNeutralMoleculePhase(shared_ptr<ThermoPhase> neutral)
593 {
594  neutralMoleculePhase_ = neutral;
595  geThermo = dynamic_cast<GibbsExcessVPSSTP*>(neutralMoleculePhase_.get());
605  y_.resize(numNeutralMoleculeSpecies_, 0.0);
606  dlnActCoeff_NeutralMolecule_.resize(numNeutralMoleculeSpecies_, 0.0);
607  dX_NeutralMolecule_.resize(numNeutralMoleculeSpecies_, 0.0);
608  for (size_t k = 0; k < nSpecies(); k++) {
609  providePDSS(k)->setParent(this, k);
610  }
611 }
612 
613 shared_ptr<ThermoPhase> IonsFromNeutralVPSSTP::getNeutralMoleculePhase()
614 {
615  return neutralMoleculePhase_;
616 }
617 
618 bool IonsFromNeutralVPSSTP::addSpecies(shared_ptr<Species> spec)
619 {
620  bool added = GibbsExcessVPSSTP::addSpecies(spec);
621  if (added) {
622  moleFractions_.push_back(0.0);
623  moleFractionsTmp_.push_back(0.0);
624  m_work.push_back(0.0);
625  fm_invert_ionForNeutral.push_back(npos);
627 
628  if (spec->charge > 0) {
629  cationList_.push_back(m_kk-1);
630  } else if (spec->charge < 0) {
631  anionList_.push_back(m_kk-1);
632  } else {
633  passThroughList_.push_back(m_kk-1);
634  }
635 
636  if (spec->input.hasKey("equation-of-state")) {
637  auto& ss = spec->input["equation-of-state"].getMapWhere(
638  "model", "ions-from-neutral-molecule");
639  if (ss.getBool("special-species", false)) {
641  }
642  }
643  }
644  return added;
645 }
646 
648 {
650  // Find the Neutral Molecule Phase
651  if (!thermoNode.hasChild("neutralMoleculePhase")) {
652  throw CanteraError("IonsFromNeutralVPSSTP::setParametersFromXML",
653  "no neutralMoleculePhase XML node");
654  }
655  XML_Node& neutralMoleculeNode = thermoNode.child("neutralMoleculePhase");
656 
657  XML_Node* neut_ptr = get_XML_Node(neutralMoleculeNode["datasrc"], 0);
658  if (!neut_ptr) {
659  throw CanteraError("IonsFromNeutralVPSSTP::setParametersFromXML",
660  "neut_ptr = 0");
661  }
662 
663  setNeutralMoleculePhase(shared_ptr<ThermoPhase>(newPhase(*neut_ptr)));
664 }
665 
667 {
668  size_t icat, jNeut;
669  // Get the activity coefficients of the neutral molecules
670  neutralMoleculePhase_->getLnActivityCoefficients(lnActCoeff_NeutralMolecule_.data());
671 
672  switch (ionSolnType_) {
673  case cIonSolnType_PASSTHROUGH:
674  break;
675  case cIonSolnType_SINGLEANION:
676  // Do the cation list
677  for (size_t k = 0; k < cationList_.size(); k++) {
678  // Get the id for the next cation
679  icat = cationList_[k];
680  jNeut = fm_invert_ionForNeutral[icat];
681  double fmij = fm_neutralMolec_ions_[icat + jNeut * m_kk];
682  lnActCoeff_Scaled_[icat] = lnActCoeff_NeutralMolecule_[jNeut] / fmij;
683  }
684 
685  // Do the anion list
686  icat = anionList_[0];
687  jNeut = fm_invert_ionForNeutral[icat];
688  lnActCoeff_Scaled_[icat]= 0.0;
689 
690  // Do the list of neutral molecules
691  for (size_t k = 0; k < passThroughList_.size(); k++) {
692  icat = passThroughList_[k];
693  jNeut = fm_invert_ionForNeutral[icat];
695  }
696  break;
697 
698  case cIonSolnType_SINGLECATION:
699  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeff", "Unimplemented type");
700  break;
701  case cIonSolnType_MULTICATIONANION:
702  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeff", "Unimplemented type");
703  break;
704  default:
705  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeff", "Unimplemented type");
706  break;
707  }
708 }
709 
710 void IonsFromNeutralVPSSTP::getdlnActCoeffds(const doublereal dTds, const doublereal* const dXds,
711  doublereal* dlnActCoeffds) const
712 {
713  size_t icat, jNeut;
714  // Get the activity coefficients of the neutral molecules
715  if (!geThermo) {
716  for (size_t k = 0; k < m_kk; k++) {
717  dlnActCoeffds[k] = dXds[k] / moleFractions_[k];
718  }
719  return;
720  }
721 
722  getNeutralMoleculeMoleGrads(dXds, dX_NeutralMolecule_.data());
723 
724  // All mole fractions returned to normal
725  geThermo->getdlnActCoeffds(dTds, dX_NeutralMolecule_.data(), dlnActCoeff_NeutralMolecule_.data());
726 
727  switch (ionSolnType_) {
728  case cIonSolnType_PASSTHROUGH:
729  break;
730  case cIonSolnType_SINGLEANION:
731  // Do the cation list
732  for (size_t k = 0; k < cationList_.size(); k++) {
733  // Get the id for the next cation
734  icat = cationList_[k];
735  jNeut = fm_invert_ionForNeutral[icat];
736  double fmij = fm_neutralMolec_ions_[icat + jNeut * m_kk];
737  dlnActCoeffds[icat] = dlnActCoeff_NeutralMolecule_[jNeut]/fmij;
738  }
739 
740  // Do the anion list
741  icat = anionList_[0];
742  jNeut = fm_invert_ionForNeutral[icat];
743  dlnActCoeffds[icat]= 0.0;
744 
745  // Do the list of neutral molecules
746  for (size_t k = 0; k < passThroughList_.size(); k++) {
747  icat = passThroughList_[k];
748  jNeut = fm_invert_ionForNeutral[icat];
749  dlnActCoeffds[icat] = dlnActCoeff_NeutralMolecule_[jNeut];
750  }
751  break;
752 
753  case cIonSolnType_SINGLECATION:
754  throw CanteraError("IonsFromNeutralVPSSTP::getdlnActCoeffds", "Unimplemented type");
755  break;
756  case cIonSolnType_MULTICATIONANION:
757  throw CanteraError("IonsFromNeutralVPSSTP::getdlnActCoeffds", "Unimplemented type");
758  break;
759  default:
760  throw CanteraError("IonsFromNeutralVPSSTP::getdlnActCoeffds", "Unimplemented type");
761  break;
762  }
763 }
764 
766 {
767  size_t icat, jNeut;
768 
769  // Get the activity coefficients of the neutral molecules
770  if (!geThermo) {
771  dlnActCoeffdT_Scaled_.assign(m_kk, 0.0);
772  return;
773  }
774 
776 
777  switch (ionSolnType_) {
778  case cIonSolnType_PASSTHROUGH:
779  break;
780  case cIonSolnType_SINGLEANION:
781  // Do the cation list
782  for (size_t k = 0; k < cationList_.size(); k++) {
783  //! Get the id for the next cation
784  icat = cationList_[k];
785  jNeut = fm_invert_ionForNeutral[icat];
786  double fmij = fm_neutralMolec_ions_[icat + jNeut * m_kk];
788  }
789 
790  // Do the anion list
791  icat = anionList_[0];
792  jNeut = fm_invert_ionForNeutral[icat];
793  dlnActCoeffdT_Scaled_[icat]= 0.0;
794 
795  // Do the list of neutral molecules
796  for (size_t k = 0; k < passThroughList_.size(); k++) {
797  icat = passThroughList_[k];
798  jNeut = fm_invert_ionForNeutral[icat];
800  }
801  break;
802 
803  case cIonSolnType_SINGLECATION:
804  throw CanteraError("IonsFromNeutralVPSSTP::s_update_dlnActCoeffdT", "Unimplemented type");
805  break;
806  case cIonSolnType_MULTICATIONANION:
807  throw CanteraError("IonsFromNeutralVPSSTP::s_update_dlnActCoeffdT", "Unimplemented type");
808  break;
809  default:
810  throw CanteraError("IonsFromNeutralVPSSTP::s_update_dlnActCoeffdT", "Unimplemented type");
811  break;
812  }
813 }
814 
816 {
817  size_t icat, jNeut;
818 
819  // Get the activity coefficients of the neutral molecules
820  if (!geThermo) {
821  dlnActCoeffdlnX_diag_.assign(m_kk, 0.0);
822  return;
823  }
824 
826 
827  switch (ionSolnType_) {
828  case cIonSolnType_PASSTHROUGH:
829  break;
830  case cIonSolnType_SINGLEANION:
831  // Do the cation list
832  for (size_t k = 0; k < cationList_.size(); k++) {
833  // Get the id for the next cation
834  icat = cationList_[k];
835  jNeut = fm_invert_ionForNeutral[icat];
836  double fmij = fm_neutralMolec_ions_[icat + jNeut * m_kk];
838  }
839 
840  // Do the anion list
841  icat = anionList_[0];
842  jNeut = fm_invert_ionForNeutral[icat];
843  dlnActCoeffdlnX_diag_[icat]= 0.0;
844 
845  // Do the list of neutral molecules
846  for (size_t k = 0; k < passThroughList_.size(); k++) {
847  icat = passThroughList_[k];
848  jNeut = fm_invert_ionForNeutral[icat];
850  }
851  break;
852 
853  case cIonSolnType_SINGLECATION:
854  throw CanteraError("IonsFromNeutralVPSSTP::s_update_dlnActCoeff_dlnX_diag", "Unimplemented type");
855  break;
856  case cIonSolnType_MULTICATIONANION:
857  throw CanteraError("IonsFromNeutralVPSSTP::s_update_dlnActCoeff_dlnX_diag", "Unimplemented type");
858  break;
859  default:
860  throw CanteraError("IonsFromNeutralVPSSTP::s_update_dlnActCoeff_dlnX_diag", "Unimplemented type");
861  break;
862  }
863 }
864 
866 {
867  size_t icat, jNeut;
868 
869  // Get the activity coefficients of the neutral molecules
870  if (!geThermo) {
871  dlnActCoeffdlnN_diag_.assign(m_kk, 0.0);
872  return;
873  }
874 
876 
877  switch (ionSolnType_) {
878  case cIonSolnType_PASSTHROUGH:
879  break;
880  case cIonSolnType_SINGLEANION:
881  // Do the cation list
882  for (size_t k = 0; k < cationList_.size(); k++) {
883  // Get the id for the next cation
884  icat = cationList_[k];
885  jNeut = fm_invert_ionForNeutral[icat];
886  double fmij = fm_neutralMolec_ions_[icat + jNeut * m_kk];
888  }
889 
890  // Do the anion list
891  icat = anionList_[0];
892  jNeut = fm_invert_ionForNeutral[icat];
893  dlnActCoeffdlnN_diag_[icat]= 0.0;
894 
895  // Do the list of neutral molecules
896  for (size_t k = 0; k < passThroughList_.size(); k++) {
897  icat = passThroughList_[k];
898  jNeut = fm_invert_ionForNeutral[icat];
900  }
901  break;
902 
903  case cIonSolnType_SINGLECATION:
904  throw CanteraError("IonsFromNeutralVPSSTP::s_update_dlnActCoeff_dlnN_diag", "Unimplemented type");
905  break;
906  case cIonSolnType_MULTICATIONANION:
907  throw CanteraError("IonsFromNeutralVPSSTP::s_update_dlnActCoeff_dlnN_diag", "Unimplemented type");
908  break;
909  default:
910  throw CanteraError("IonsFromNeutralVPSSTP::s_update_dlnActCoeff_dlnN_diag", "Unimplemented type");
911  break;
912  }
913 }
914 
916 {
917  size_t kcat = 0, kNeut = 0, mcat = 0, mNeut = 0;
918  doublereal fmij = 0.0;
920  // Get the activity coefficients of the neutral molecules
921  if (!geThermo) {
922  throw CanteraError("IonsFromNeutralVPSSTP::s_update_dlnActCoeff_dlnN", "dynamic cast failed");
923  }
924  size_t nsp_ge = geThermo->nSpecies();
925  geThermo->getdlnActCoeffdlnN(nsp_ge, &dlnActCoeffdlnN_NeutralMolecule_(0,0));
926 
927  switch (ionSolnType_) {
928  case cIonSolnType_PASSTHROUGH:
929  break;
930  case cIonSolnType_SINGLEANION:
931  // Do the cation list
932  for (size_t k = 0; k < cationList_.size(); k++) {
933  for (size_t m = 0; m < cationList_.size(); m++) {
934  kcat = cationList_[k];
935 
936  kNeut = fm_invert_ionForNeutral[kcat];
937  fmij = fm_neutralMolec_ions_[kcat + kNeut * m_kk];
939 
940  mcat = cationList_[m];
941  mNeut = fm_invert_ionForNeutral[mcat];
942  double mfmij = fm_neutralMolec_ions_[mcat + mNeut * m_kk];
943 
944  dlnActCoeffdlnN_(kcat,mcat) = dlnActCoeffdlnN_NeutralMolecule_(kNeut,mNeut) * mfmij / fmij;
945 
946  }
947  for (size_t m = 0; m < passThroughList_.size(); m++) {
948  mcat = passThroughList_[m];
949  mNeut = fm_invert_ionForNeutral[mcat];
950  dlnActCoeffdlnN_(kcat, mcat) = dlnActCoeffdlnN_NeutralMolecule_(kNeut, mNeut) / fmij;
951  }
952  }
953 
954  // Do the anion list -> anion activity coefficient is one
955  kcat = anionList_[0];
956  kNeut = fm_invert_ionForNeutral[kcat];
957  for (size_t k = 0; k < m_kk; k++) {
958  dlnActCoeffdlnN_(kcat, k) = 0.0;
959  dlnActCoeffdlnN_(k, kcat) = 0.0;
960  }
961 
962  // Do the list of neutral molecules
963  for (size_t k = 0; k < passThroughList_.size(); k++) {
964  kcat = passThroughList_[k];
965  kNeut = fm_invert_ionForNeutral[kcat];
967 
968  for (size_t m = 0; m < m_kk; m++) {
969  mcat = passThroughList_[m];
970  mNeut = fm_invert_ionForNeutral[mcat];
971  dlnActCoeffdlnN_(kcat, mcat) = dlnActCoeffdlnN_NeutralMolecule_(kNeut, mNeut);
972  }
973 
974  for (size_t m = 0; m < cationList_.size(); m++) {
975  mcat = cationList_[m];
976  mNeut = fm_invert_ionForNeutral[mcat];
977  dlnActCoeffdlnN_(kcat, mcat) = dlnActCoeffdlnN_NeutralMolecule_(kNeut,mNeut);
978  }
979  }
980  break;
981 
982  case cIonSolnType_SINGLECATION:
983  throw CanteraError("IonsFromNeutralVPSSTP::s_update_dlnActCoeff_dlnN", "Unimplemented type");
984  break;
985  case cIonSolnType_MULTICATIONANION:
986  throw CanteraError("IonsFromNeutralVPSSTP::s_update_dlnActCoeff_dlnN", "Unimplemented type");
987  break;
988  default:
989  throw CanteraError("IonsFromNeutralVPSSTP::s_update_dlnActCoeff_dlnN", "Unimplemented type");
990  break;
991  }
992 }
993 
994 }
Header for intermediate ThermoPhase object for phases which consist of ions whose thermodynamics is c...
Declarations for the class PDSS_IonsFromNeutral ( which handles calculations for a single ion in a fl...
Headers for the factory class that can create known ThermoPhase objects (see Thermodynamic Properties...
A map of string keys to values whose type can vary at runtime.
Definition: AnyMap.h:360
const std::string & getString(const std::string &key, const std::string &default_) const
If key exists, return it as a string, otherwise return default_.
Definition: AnyMap.cpp:1049
static AnyMap fromYamlFile(const std::string &name, const std::string &parent_name="")
Create an AnyMap from a YAML file.
Definition: AnyMap.cpp:1156
bool hasKey(const std::string &key) const
Returns true if the map contains an item named key.
Definition: AnyMap.cpp:984
void resize(size_t n, size_t m, doublereal v=0.0)
Resize the array, and fill the new entries with 'v'.
Definition: Array.h:112
void zero()
Set all of the entries to zero.
Definition: Array.h:198
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:61
virtual void getdlnActCoeffdlnN(const size_t ld, doublereal *const dlnActCoeffdlnN)
Get the array of derivatives of the log activity coefficients with respect to the log of the species ...
Array2D dlnActCoeffdlnN_
Storage for the current derivative values of the gradients with respect to logarithm of the species m...
vector_fp dlnActCoeffdlnX_diag_
Storage for the current derivative values of the gradients with respect to logarithm of the mole frac...
vector_fp lnActCoeff_Scaled_
Storage for the current values of the activity coefficients of the species.
vector_fp moleFractions_
Storage for the current values of the mole fractions of the species.
vector_fp dlnActCoeffdlnN_diag_
Storage for the current derivative values of the gradients with respect to logarithm of the mole frac...
virtual void compositionChanged()
Apply changes to the state which are needed after the composition changes.
virtual void getdlnActCoeffdT(doublereal *dlnActCoeffdT) const
Get the array of temperature derivatives of the log activity coefficients.
vector_fp dlnActCoeffdT_Scaled_
Storage for the current derivative values of the gradients with respect to temperature of the log of ...
AnyMap m_rootNode
Root node of the AnyMap which contains this phase definition.
virtual void setParameters(const AnyMap &phaseNode, const AnyMap &rootNode=AnyMap())
Set equation of state parameters from an AnyMap phase description.
vector_fp dlnActCoeffdT_NeutralMolecule_
Storage vector for the neutral molecule d ln activity coefficients dT.
virtual void getActivityCoefficients(doublereal *ac) const
Get the array of non-dimensional molar-based activity coefficients at the current solution temperatur...
vector_fp lnActCoeff_NeutralMolecule_
Storage vector for the neutral molecule ln activity coefficients.
virtual void calcNeutralMoleculeMoleFractions() const
Calculate neutral molecule mole fractions.
virtual doublereal cp_mole() const
Molar heat capacity at constant pressure. Units: J/kmol/K.
virtual void getPartialMolarEnthalpies(doublereal *hbar) const
Returns an array of partial molar enthalpies for the species in the mixture.
void getNeutralMoleculeMoleGrads(const doublereal *const dx, doublereal *const dy) const
Calculate neutral molecule mole fractions.
virtual void getPartialMolarEntropies(doublereal *sbar) const
Returns an array of partial molar entropies for the species in the mixture.
vector_fp moleFractionsTmp_
Temporary mole fraction vector.
std::vector< size_t > passThroughList_
List of the species in this ThermoPhase which are passed through to the neutralMoleculePhase ThermoPh...
virtual doublereal enthalpy_mole() const
Return the Molar enthalpy. Units: J/kmol.
void s_update_dlnActCoeff_dlnN_diag() const
Update the derivative of the log of the activity coefficients wrt log(number of moles) - diagonal com...
virtual void getdlnActCoeffdlnN(const size_t ld, doublereal *const dlnActCoeffdlnN)
Get the array of derivatives of the log activity coefficients with respect to the log of the species ...
vector_fp muNeutralMolecule_
Storage vector for the neutral molecule chemical potentials.
virtual doublereal cv_mole() const
Molar heat capacity at constant volume. Units: J/kmol/K.
virtual void getdlnActCoeffdlnX_diag(doublereal *dlnActCoeffdlnX_diag) const
Get the array of ln mole fraction derivatives of the log activity coefficients - diagonal component o...
virtual void calcIonMoleFractions(doublereal *const mf) const
Calculate ion mole fractions from neutral molecule mole fractions.
size_t indexSpecialSpecies_
Index of special species.
vector_fp NeutralMolecMoleFractions_
Mole fractions using the Neutral Molecule Mole fraction basis.
vector_fp fm_neutralMolec_ions_
Formula Matrix for composition of neutral molecules in terms of the molecules in this ThermoPhase.
void getDissociationCoeffs(vector_fp &fm_neutralMolec_ions, vector_fp &charges, std::vector< size_t > &neutMolIndex) const
Get the Salt Dissociation Coefficients.
vector_fp dlnActCoeffdlnN_diag_NeutralMolecule_
Storage vector for the neutral molecule d ln activity coefficients dlnN.
virtual doublereal entropy_mole() const
Molar entropy. Units: J/kmol/K.
std::vector< size_t > cationList_
List of the species in this ThermoPhase which are cation species.
Array2D dlnActCoeffdlnN_NeutralMolecule_
Storage vector for the neutral molecule d ln activity coefficients dlnN.
void s_update_dlnActCoeff_dlnN() const
Update the derivative of the log of the activity coefficients wrt log(number of moles) - diagonal com...
shared_ptr< ThermoPhase > neutralMoleculePhase_
This is a pointer to the neutral Molecule Phase.
size_t numNeutralMoleculeSpecies_
Number of neutral molecule species.
virtual doublereal gibbs_mole() const
Molar Gibbs function. Units: J/kmol.
std::vector< size_t > fm_invert_ionForNeutral
Mapping between ion species and neutral molecule for quick invert.
vector_fp dlnActCoeffdlnX_diag_NeutralMolecule_
Storage vector for the neutral molecule d ln activity coefficients dX - diagonal component.
virtual void getdlnActCoeffdlnN_diag(doublereal *dlnActCoeffdlnN_diag) const
Get the array of log species mole number derivatives of the log activity coefficients.
virtual void getdlnActCoeffds(const doublereal dTds, const doublereal *const dXds, doublereal *dlnActCoeffds) const
Get the change in activity coefficients wrt changes in state (temp, mole fraction,...
virtual void compositionChanged()
Apply changes to the state which are needed after the composition changes.
void s_update_dlnActCoeff_dlnX_diag() const
Update the derivative of the log of the activity coefficients wrt log(mole fraction)
void s_update_lnActCoeff() const
Update the activity coefficients.
virtual void getChemPotentials(doublereal *mu) const
Get the species chemical potentials. Units: J/kmol.
void s_update_dlnActCoeffdT() const
Update the temperature derivative of the ln activity coefficients.
IonSolnType_enumType ionSolnType_
Ion solution type.
virtual void setParametersFromXML(const XML_Node &thermoNode)
Set equation of state parameter values from XML entries.
virtual void calcDensity()
Calculate the density of the mixture using the partial molar volumes and mole fractions as input.
std::vector< size_t > anionList_
List of the species in this ThermoPhase which are anion species.
virtual void setParent(VPStandardStateTP *phase, size_t k)
Set the parent VPStandardStateTP object of this PDSS object.
Definition: PDSS.h:420
void assignDensity(const double density_)
Set the internally stored constant density (kg/m^3) of the phase.
Definition: Phase.cpp:727
doublereal mean_X(const doublereal *const Q) const
Evaluate the mole-fraction-weighted mean of an array Q.
Definition: Phase.cpp:746
vector_fp m_speciesCharge
Vector of species charges. length m_kk.
Definition: Phase.h:953
size_t nSpecies() const
Returns the number of species in the phase.
Definition: Phase.h:285
size_t m_kk
Number of species in the phase.
Definition: Phase.h:942
doublereal nAtoms(size_t k, size_t m) const
Number of atoms of element m in species k.
Definition: Phase.cpp:168
const std::vector< std::string > & elementNames() const
Return a read-only reference to the vector of element names.
Definition: Phase.cpp:130
doublereal temperature() const
Temperature (K).
Definition: Phase.h:667
size_t nElements() const
Number of elements.
Definition: Phase.cpp:95
doublereal RT() const
Return the Gas Constant multiplied by the current temperature.
Definition: ThermoPhase.h:776
virtual void initThermoFile(const std::string &inputFile, const std::string &id)
virtual void getPartialMolarCp(doublereal *cpbar) const
Return an array of partial molar heat capacities for the species in the mixture.
Definition: ThermoPhase.h:546
virtual void getdlnActCoeffdlnX_diag(doublereal *dlnActCoeffdlnX_diag) const
Get the array of ln mole fraction derivatives of the log activity coefficients - diagonal component o...
Definition: ThermoPhase.h:1783
virtual void setParameters(int n, doublereal *const c)
Set the equation of state parameters.
Definition: ThermoPhase.h:1691
virtual void getdlnActCoeffds(const doublereal dTds, const doublereal *const dXds, doublereal *dlnActCoeffds) const
Get the change in activity coefficients wrt changes in state (temp, mole fraction,...
Definition: ThermoPhase.h:1763
AnyMap m_input
Data supplied via setParameters.
Definition: ThermoPhase.h:1874
virtual void setParametersFromXML(const XML_Node &eosdata)
Set equation of state parameter values from XML entries.
Definition: ThermoPhase.h:1728
virtual bool addSpecies(shared_ptr< Species > spec)
Add a Species to this Phase.
Definition: Phase.cpp:833
virtual void getdlnActCoeffdlnN_diag(doublereal *dlnActCoeffdlnN_diag) const
Get the array of log species mole number derivatives of the log activity coefficients.
virtual void getEntropy_R(doublereal *sr) const
Get the array of nondimensional Entropy functions for the standard state species at the current T and...
virtual doublereal pressure() const
Returns the current pressure of the phase.
virtual void getEnthalpy_RT(doublereal *hrt) const
Get the nondimensional Enthalpy functions for the species at their standard states at the current T a...
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:104
bool hasChild(const std::string &ch) const
Tests whether the current node has a child node with a particular name.
Definition: xml.cpp:528
XML_Node & child(const size_t n) const
Return a changeable reference to the n'th child of the current node.
Definition: xml.cpp:546
#define AssertTrace(expr)
Assertion must be true or an error is thrown.
Definition: ctexceptions.h:234
XML_Node * get_XML_Node(const std::string &file_ID, XML_Node *root)
This routine will locate an XML node in either the input XML tree or in another input file specified ...
Definition: global.cpp:194
ThermoPhase * newPhase(XML_Node &xmlphase)
Create a new ThermoPhase object and initializes it according to the XML tree.
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:188
const double SmallNumber
smallest number to compare to zero.
Definition: ct_defs.h:149
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
Definition: ct_defs.h:180
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition: ct_defs.h:109
void importPhase(XML_Node &phase, ThermoPhase *th)
Import a phase information into an empty ThermoPhase object.
Namespace for the Cantera kernel.
Definition: AnyMap.cpp:264
static double factorOverlap(const std::vector< std::string > &elnamesVN, const vector_fp &elemVectorN, const size_t nElementsN, const std::vector< std::string > &elnamesVI, const vector_fp &elemVectorI, const size_t nElementsI)
Return the factor overlap.
Contains declarations for string manipulation functions within Cantera.