Cantera  2.1.2
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
9  * variable pressure standard state methods for calculating
10  * thermodynamic properties that are further based upon expressions
11  * for the excess gibbs free energy expressed as a function of
12  * the mole fractions.
13  */
14 /*
15  * Copyright (2009) Sandia Corporation. Under the terms of
16  * Contract DE-AC04-94AL85000 with Sandia Corporation, the
17  * U.S. Government retains certain rights in this software.
18  */
23 
24 #include <fstream>
25 
26 using namespace std;
27 
28 namespace Cantera
29 {
30 
31 IonsFromNeutralVPSSTP::IonsFromNeutralVPSSTP() :
33  ionSolnType_(cIonSolnType_SINGLEANION),
34  numNeutralMoleculeSpecies_(0),
35  indexSpecialSpecies_(npos),
36  indexSecondSpecialSpecies_(npos),
37  numCationSpecies_(0),
38  numAnionSpecies_(0),
39  numPassThroughSpecies_(0),
40  neutralMoleculePhase_(0),
41  geThermo(0),
42  IOwnNThermoPhase_(true),
43  moleFractionsTmp_(0),
44  muNeutralMolecule_(0),
45  lnActCoeff_NeutralMolecule_(0)
46 {
47 }
48 
49 IonsFromNeutralVPSSTP::IonsFromNeutralVPSSTP(const std::string& inputFile,
50  const std::string& id_,
51  ThermoPhase* neutralPhase) :
53  ionSolnType_(cIonSolnType_SINGLEANION),
54  numNeutralMoleculeSpecies_(0),
55  indexSpecialSpecies_(npos),
56  indexSecondSpecialSpecies_(npos),
57  numCationSpecies_(0),
58  numAnionSpecies_(0),
59  numPassThroughSpecies_(0),
60  neutralMoleculePhase_(neutralPhase),
61  IOwnNThermoPhase_(true),
62  moleFractionsTmp_(0),
63  muNeutralMolecule_(0),
64  lnActCoeff_NeutralMolecule_(0)
65 {
66  if (neutralPhase) {
67  IOwnNThermoPhase_ = false;
68  }
69  constructPhaseFile(inputFile, id_);
70  geThermo = dynamic_cast<GibbsExcessVPSSTP*>(neutralMoleculePhase_);
71  //y.resize(numNeutralMoleculeSpecies_,0.0);
72  //size_t numNeutMolSpec = geThermo->nSpecies();
73  //dlnActCoeff_NeutralMolecule.resize(numNeutMolSpec);
74  //dX_NeutralMolecule.resize(numNeutMolSpec);
75 }
76 
78  const std::string& id_, ThermoPhase* neutralPhase) :
80  ionSolnType_(cIonSolnType_SINGLEANION),
81  numNeutralMoleculeSpecies_(0),
82  indexSpecialSpecies_(npos),
83  indexSecondSpecialSpecies_(npos),
84  numCationSpecies_(0),
85  numAnionSpecies_(0),
86  numPassThroughSpecies_(0),
87  neutralMoleculePhase_(neutralPhase),
88  IOwnNThermoPhase_(true),
89  moleFractionsTmp_(0),
90  muNeutralMolecule_(0),
91 
92  lnActCoeff_NeutralMolecule_(0)
93 {
94  if (neutralPhase) {
95  IOwnNThermoPhase_ = false;
96  }
97  constructPhaseXML(phaseRoot, id_);
98  geThermo = dynamic_cast<GibbsExcessVPSSTP*>(neutralMoleculePhase_);
99  y_.resize(numNeutralMoleculeSpecies_,0.0);
100  size_t numNeutMolSpec = geThermo->nSpecies();
101  dlnActCoeff_NeutralMolecule_.resize(numNeutMolSpec);
102  dX_NeutralMolecule_.resize(numNeutMolSpec);
103 }
104 
107  ionSolnType_(cIonSolnType_SINGLEANION),
108  numNeutralMoleculeSpecies_(0),
109  indexSpecialSpecies_(npos),
110  indexSecondSpecialSpecies_(npos),
111  numCationSpecies_(0),
112  numAnionSpecies_(0),
113  numPassThroughSpecies_(0),
114  neutralMoleculePhase_(0),
115  geThermo(0),
116  IOwnNThermoPhase_(true),
117  moleFractionsTmp_(0),
118  muNeutralMolecule_(0),
119 
120  lnActCoeff_NeutralMolecule_(0)
121 {
123 }
124 
127 {
128  if (&b == this) {
129  return *this;
130  }
131 
132  /*
133  * If we own the underlying neutral molecule phase, then we do a deep
134  * copy. If not, we do a shallow copy. We get a valid pointer for
135  * neutralMoleculePhase_ first, because we need it to assign the pointers
136  * within the PDSS_IonsFromNeutral object. which is done in the
137  * GibbsExcessVPSSTP::operator=(b) step.
138  */
139  if (IOwnNThermoPhase_) {
140  if (b.neutralMoleculePhase_) {
141  delete neutralMoleculePhase_;
143  } else {
145  }
146  } else {
148  }
149  geThermo = dynamic_cast<GibbsExcessVPSSTP*>(neutralMoleculePhase_);
150 
152 
153 
167 
168  y_ = b.y_;
169  dlnActCoeff_NeutralMolecule_ = b.dlnActCoeff_NeutralMolecule_;
170  dX_NeutralMolecule_ = b.dX_NeutralMolecule_;
171 
175  // gammaNeutralMolecule_ = b.gammaNeutralMolecule_;
181 
182  return *this;
183 }
184 
186 {
187  if (IOwnNThermoPhase_) {
188  delete neutralMoleculePhase_;
190  }
191 }
192 
195 {
196  return new IonsFromNeutralVPSSTP(*this);
197 }
198 
199 void IonsFromNeutralVPSSTP::constructPhaseFile(std::string inputFile, std::string id_)
200 {
201 
202  if (inputFile.size() == 0) {
203  throw CanteraError("MargulesVPSSTP:constructPhaseFile",
204  "input file is null");
205  }
206  string path = findInputFile(inputFile);
207  std::ifstream fin(path.c_str());
208  if (!fin) {
209  throw CanteraError("MargulesVPSSTP:constructPhaseFile","could not open "
210  +path+" for reading.");
211  }
212  /*
213  * The phase object automatically constructs an XML object.
214  * Use this object to store information.
215  */
216  XML_Node& phaseNode_XML = xml();
217  XML_Node* fxml = new XML_Node();
218  fxml->build(fin);
219  XML_Node* fxml_phase = findXMLPhase(fxml, id_);
220  if (!fxml_phase) {
221  throw CanteraError("MargulesVPSSTP:constructPhaseFile",
222  "ERROR: Can not find phase named " +
223  id_ + " in file named " + inputFile);
224  }
225  fxml_phase->copy(&phaseNode_XML);
226  constructPhaseXML(*fxml_phase, id_);
227  delete fxml;
228 }
229 
230 void IonsFromNeutralVPSSTP::constructPhaseXML(XML_Node& phaseNode, std::string id_)
231 {
232  string stemp;
233  if (id_.size() > 0) {
234  string idp = phaseNode.id();
235  if (idp != id_) {
236  throw CanteraError("IonsFromNeutralVPSSTP::constructPhaseXML",
237  "phasenode and Id are incompatible");
238  }
239  }
240 
241  /*
242  * Find the thermo XML node
243  */
244  if (!phaseNode.hasChild("thermo")) {
245  throw CanteraError("IonsFromNeutralVPSSTP::constructPhaseXML",
246  "no thermo XML node");
247  }
248  XML_Node& thermoNode = phaseNode.child("thermo");
249 
250 
251 
252  /*
253  * Make sure that the thermo model is IonsFromNeutralMolecule
254  */
255  stemp = thermoNode.attrib("model");
256  string formString = lowercase(stemp);
257  if (formString != "ionsfromneutralmolecule") {
258  throw CanteraError("IonsFromNeutralVPSSTP::constructPhaseXML",
259  "model name isn't IonsFromNeutralMolecule: " + formString);
260  }
261 
262  /*
263  * Find the Neutral Molecule Phase
264  */
265  if (!thermoNode.hasChild("neutralMoleculePhase")) {
266  throw CanteraError("IonsFromNeutralVPSSTP::constructPhaseXML",
267  "no neutralMoleculePhase XML node");
268  }
269  XML_Node& neutralMoleculeNode = thermoNode.child("neutralMoleculePhase");
270 
271  string nsource = neutralMoleculeNode["datasrc"];
272  XML_Node* neut_ptr = get_XML_Node(nsource, 0);
273  if (!neut_ptr) {
274  throw CanteraError("IonsFromNeutralVPSSTP::constructPhaseXML",
275  "neut_ptr = 0");
276  }
277 
278  /*
279  * Create the neutralMolecule ThermoPhase if we haven't already
280  */
281  if (!neutralMoleculePhase_) {
282  neutralMoleculePhase_ = newPhase(*neut_ptr);
283  }
284 
285  /*
286  * Call the Cantera importPhase() function. This will import
287  * all of the species into the phase. This will also handle
288  * all of the solvent and solute standard states
289  */
290  bool m_ok = importPhase(phaseNode, this);
291  if (!m_ok) {
292  throw CanteraError("IonsFromNeutralVPSSTP::constructPhaseXML",
293  "importPhase failed ");
294  }
295 
296 }
297 
298 /*
299  * -------------- Utilities -------------------------------
300  */
301 
303 {
304  return cIonsFromNeutral;
305 }
306 
307 /*
308  * ------------ Molar Thermodynamic Properties ----------------------
309  */
310 
312 {
314  return mean_X(DATA_PTR(m_pp));
315 }
316 
318 {
319  double hh = enthalpy_mole();
320  double pres = pressure();
321  double molarV = 1.0/molarDensity();
322  return hh - pres * molarV;
323 }
324 
326 {
328  return mean_X(DATA_PTR(m_pp));
329 }
330 
332 {
334  return mean_X(DATA_PTR(m_pp));
335 }
336 
338 {
340  return mean_X(DATA_PTR(m_pp));
341 }
342 
344 {
345  // Need to revisit this, as it is wrong
347  return mean_X(DATA_PTR(m_pp));
348  //err("not implemented");
349  //return 0.0;
350 }
351 
352 /*
353  * - Activities, Standard States, Activity Concentrations -----------
354  */
355 
357  vector_fp& charges, std::vector<size_t>& neutMolIndex) const
358 {
359  coeffs = fm_neutralMolec_ions_;
360  charges = m_speciesCharge;
361  neutMolIndex = fm_invert_ionForNeutral;
362 }
363 
365 {
366 
367  // This stuff has moved to the setState routines
368  // calcNeutralMoleculeMoleFractions();
369  // neutralMoleculePhase_->setState_TPX(temperature(), pressure(), DATA_PTR(NeutralMolecMoleFractions_));
370  // neutralMoleculePhase_->getStandardChemPotentials(DATA_PTR(muNeutralMolecule_));
371 
372  /*
373  * Update the activity coefficients
374  */
376 
377  /*
378  * take the exp of the internally stored coefficients.
379  */
380  for (size_t k = 0; k < m_kk; k++) {
381  ac[k] = exp(lnActCoeff_Scaled_[k]);
382  }
383 }
384 
385 /*
386  * --------- Partial Molar Properties of the Solution -------------
387  */
388 
390 {
391  size_t icat, jNeut;
392  doublereal xx, fact2;
393  /*
394  * Transfer the mole fractions to the slave neutral molecule
395  * phase
396  * Note we may move this in the future.
397  */
398  //calcNeutralMoleculeMoleFractions();
399  //neutralMoleculePhase_->setState_TPX(temperature(), pressure(), DATA_PTR(NeutralMolecMoleFractions_));
400 
401  /*
402  * Get the standard chemical potentials of netural molecules
403  */
405 
406  doublereal RT_ = GasConstant * temperature();
407 
408  switch (ionSolnType_) {
409  case cIonSolnType_PASSTHROUGH:
411  break;
412  case cIonSolnType_SINGLEANION:
413  // neutralMoleculePhase_->getActivityCoefficients(DATA_PTR(gammaNeutralMolecule_));
415 
416  fact2 = 2.0 * RT_ * log(2.0);
417 
418  // Do the cation list
419  for (size_t k = 0; k < cationList_.size(); k++) {
420  //! Get the id for the next cation
421  icat = cationList_[k];
422  jNeut = fm_invert_ionForNeutral[icat];
423  xx = std::max(SmallNumber, moleFractions_[icat]);
424  mu[icat] = muNeutralMolecule_[jNeut] + fact2 + RT_ * (lnActCoeff_NeutralMolecule_[jNeut] + log(xx));
425  }
426 
427  // Do the anion list
428  icat = anionList_[0];
429  jNeut = fm_invert_ionForNeutral[icat];
430  xx = std::max(SmallNumber, moleFractions_[icat]);
431  mu[icat] = RT_ * log(xx);
432 
433  // Do the list of neutral molecules
434  for (size_t k = 0; k < numPassThroughSpecies_; k++) {
435  icat = passThroughList_[k];
436  jNeut = fm_invert_ionForNeutral[icat];
437  xx = std::max(SmallNumber, moleFractions_[icat]);
438  mu[icat] = muNeutralMolecule_[jNeut] + RT_ * (lnActCoeff_NeutralMolecule_[jNeut] + log(xx));
439  }
440  break;
441 
442  case cIonSolnType_SINGLECATION:
443  throw CanteraError("eosType", "Unknown type");
444  break;
445  case cIonSolnType_MULTICATIONANION:
446  throw CanteraError("eosType", "Unknown type");
447  break;
448  default:
449  throw CanteraError("eosType", "Unknown type");
450  break;
451  }
452 }
453 
455 {
456  /*
457  * Get the nondimensional standard state enthalpies
458  */
459  getEnthalpy_RT(hbar);
460  /*
461  * dimensionalize it.
462  */
463  double T = temperature();
464  double RT = GasConstant * T;
465  for (size_t k = 0; k < m_kk; k++) {
466  hbar[k] *= RT;
467  }
468  /*
469  * Update the activity coefficients, This also update the
470  * internally stored molalities.
471  */
474  double RTT = RT * T;
475  for (size_t k = 0; k < m_kk; k++) {
476  hbar[k] -= RTT * dlnActCoeffdT_Scaled_[k];
477  }
478 }
479 
481 {
482  double xx;
483  /*
484  * Get the nondimensional standard state entropies
485  */
486  getEntropy_R(sbar);
487  double T = temperature();
488  /*
489  * Update the activity coefficients, This also update the
490  * internally stored molalities.
491  */
494 
495  for (size_t k = 0; k < m_kk; k++) {
496  xx = std::max(moleFractions_[k], SmallNumber);
497  sbar[k] += - lnActCoeff_Scaled_[k] -log(xx) - T * dlnActCoeffdT_Scaled_[k];
498  }
499  /*
500  * dimensionalize it.
501  */
502  for (size_t k = 0; k < m_kk; k++) {
503  sbar[k] *= GasConstant;
504  }
505 }
506 
507 void IonsFromNeutralVPSSTP::getdlnActCoeffdlnX_diag(doublereal* dlnActCoeffdlnX_diag) const
508 {
511 
512  for (size_t k = 0; k < m_kk; k++) {
513  dlnActCoeffdlnX_diag[k] = dlnActCoeffdlnX_diag_[k];
514  }
515 }
516 
517 void IonsFromNeutralVPSSTP::getdlnActCoeffdlnN_diag(doublereal* dlnActCoeffdlnN_diag) const
518 {
521 
522  for (size_t k = 0; k < m_kk; k++) {
523  dlnActCoeffdlnN_diag[k] = dlnActCoeffdlnN_diag_[k];
524  }
525 }
526 
527 void IonsFromNeutralVPSSTP::getdlnActCoeffdlnN(const size_t ld, doublereal* dlnActCoeffdlnN)
528 {
531  double* data = & dlnActCoeffdlnN_(0,0);
532  for (size_t k = 0; k < m_kk; k++) {
533  for (size_t m = 0; m < m_kk; m++) {
534  dlnActCoeffdlnN[ld * k + m] = data[m_kk * k + m];
535  }
536  }
537 }
538 
539 void IonsFromNeutralVPSSTP::setTemperature(const doublereal temp)
540 {
541  double p = pressure();
543 }
544 
546 {
547  double t = temperature();
549 }
550 
551 void IonsFromNeutralVPSSTP::setState_TP(doublereal t, doublereal p)
552 {
553  /*
554  * This is a two phase process. First, we calculate the standard states
555  * within the neutral molecule phase.
556  */
559 
560  /*
561  * Calculate the partial molar volumes, and then the density of the fluid
562  */
563 
564  //calcDensity();
565  double dd = neutralMoleculePhase_->density();
566  Phase::setDensity(dd);
567 }
568 
569 void IonsFromNeutralVPSSTP::calcIonMoleFractions(doublereal* const mf) const
570 {
571  doublereal fmij;
572  /*
573  * Download the neutral mole fraction vector into the
574  * vector, NeutralMolecMoleFractions_[]
575  */
577 
578  // Zero the mole fractions
579  for (size_t k = 0; k < m_kk; k++) {
580  mf[k] = 0.0;
581  }
582 
583  /*
584  * Use the formula matrix to calculate the relative mole numbers.
585  */
586  for (size_t jNeut = 0; jNeut < numNeutralMoleculeSpecies_; jNeut++) {
587  for (size_t k = 0; k < m_kk; k++) {
588  fmij = fm_neutralMolec_ions_[k + jNeut * m_kk];
589  mf[k] += fmij * NeutralMolecMoleFractions_[jNeut];
590  }
591  }
592 
593  /*
594  * Normalize the new mole fractions
595  */
596  doublereal sum = 0.0;
597  for (size_t k = 0; k < m_kk; k++) {
598  sum += mf[k];
599  }
600  for (size_t k = 0; k < m_kk; k++) {
601  mf[k] /= sum;
602  }
603 
604 }
605 
607 {
608  size_t icat, jNeut;
609  doublereal fmij;
610  doublereal sum = 0.0;
611 
612  //! Zero the vector we are trying to find.
613  for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
615  }
616 #ifdef DEBUG_MODE
617  sum = -1.0;
618  for (size_t k = 0; k < m_kk; k++) {
619  sum += moleFractions_[k];
620  }
621  if (fabs(sum) > 1.0E-11) {
622  throw CanteraError("IonsFromNeutralVPSSTP::calcNeutralMoleculeMoleFractions",
623  "molefracts don't sum to one: " + fp2str(sum));
624  }
625 #endif
626 
627  // bool fmSimple = true;
628 
629  switch (ionSolnType_) {
630 
631  case cIonSolnType_PASSTHROUGH:
632  for (size_t k = 0; k < m_kk; k++) {
634  }
635  break;
636 
637  case cIonSolnType_SINGLEANION:
638  for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
640  }
641 
642  for (size_t k = 0; k < cationList_.size(); k++) {
643  //! Get the id for the next cation
644  icat = cationList_[k];
645  jNeut = fm_invert_ionForNeutral[icat];
646  if (jNeut != npos) {
647  fmij = fm_neutralMolec_ions_[icat + jNeut * m_kk];
648  AssertTrace(fmij != 0.0);
649  NeutralMolecMoleFractions_[jNeut] += moleFractions_[icat] / fmij;
650  }
651  }
652 
653  for (size_t k = 0; k < numPassThroughSpecies_; k++) {
654  icat = passThroughList_[k];
655  jNeut = fm_invert_ionForNeutral[icat];
656  fmij = fm_neutralMolec_ions_[ icat + jNeut * m_kk];
657  NeutralMolecMoleFractions_[jNeut] += moleFractions_[icat] / fmij;
658  }
659 
660 #ifdef DEBUG_MODE
661  for (size_t k = 0; k < m_kk; k++) {
663  }
664  for (jNeut = 0; jNeut < numNeutralMoleculeSpecies_; jNeut++) {
665  for (size_t k = 0; k < m_kk; k++) {
666  fmij = fm_neutralMolec_ions_[k + jNeut * m_kk];
667  moleFractionsTmp_[k] -= fmij * NeutralMolecMoleFractions_[jNeut];
668  }
669  }
670  for (size_t k = 0; k < m_kk; k++) {
671  if (fabs(moleFractionsTmp_[k]) > 1.0E-13) {
672  //! Check to see if we have in fact found the inverse.
673  if (anionList_[0] != k) {
674  throw CanteraError("", "neutral molecule calc error");
675  } else {
676  //! For the single anion case, we will allow some slippage
677  if (fabs(moleFractionsTmp_[k]) > 1.0E-5) {
678  throw CanteraError("", "neutral molecule calc error - anion");
679  }
680  }
681  }
682  }
683 #endif
684 
685  // Normalize the Neutral Molecule mole fractions
686  sum = 0.0;
687  for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
688  sum += NeutralMolecMoleFractions_[k];
689  }
690  for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
691  NeutralMolecMoleFractions_[k] /= sum;
692  }
693 
694  break;
695 
696  case cIonSolnType_SINGLECATION:
697 
698  throw CanteraError("eosType", "Unknown type");
699 
700  break;
701 
702  case cIonSolnType_MULTICATIONANION:
703 
704  throw CanteraError("eosType", "Unknown type");
705  break;
706 
707  default:
708 
709  throw CanteraError("eosType", "Unknown type");
710  break;
711 
712  }
713 }
714 
715 void IonsFromNeutralVPSSTP::getNeutralMoleculeMoleGrads(const doublereal* const dx, doublereal* const dy) const
716 {
717  doublereal fmij;
718  doublereal sumy, sumdy;
719 
720  //check sum dx = 0
721 
722  //! Zero the vector we are trying to find.
723  for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
724  y_[k] = 0.0;
725  dy[k] = 0.0;
726  }
727 
728 
729  // bool fmSimple = true;
730 
731  switch (ionSolnType_) {
732 
733  case cIonSolnType_PASSTHROUGH:
734  for (size_t k = 0; k < m_kk; k++) {
735  dy[k] = dx[k];
736  }
737  break;
738 
739  case cIonSolnType_SINGLEANION:
740  for (size_t k = 0; k < cationList_.size(); k++) {
741  //! Get the id for the next cation
742  size_t icat = cationList_[k];
743  size_t jNeut = fm_invert_ionForNeutral[icat];
744  if (jNeut != npos) {
745  fmij = fm_neutralMolec_ions_[icat + jNeut * m_kk];
746  AssertTrace(fmij != 0.0);
747  const doublereal temp = 1.0/fmij;
748  dy[jNeut] += dx[icat] * temp;
749  y_[jNeut] += moleFractions_[icat] * temp;
750  }
751  }
752 
753  for (size_t k = 0; k < numPassThroughSpecies_; k++) {
754  size_t icat = passThroughList_[k];
755  size_t jNeut = fm_invert_ionForNeutral[icat];
756  fmij = fm_neutralMolec_ions_[ icat + jNeut * m_kk];
757  const doublereal temp = 1.0/fmij;
758  dy[jNeut] += dx[icat] * temp;
759  y_[jNeut] += moleFractions_[icat] * temp;
760  }
761 #ifdef DEBUG_MODE_NOT
762  //check dy sum to zero
763  for (size_t k = 0; k < m_kk; k++) {
764  moleFractionsTmp_[k] = dx[k];
765  }
766  for (jNeut = 0; jNeut < numNeutralMoleculeSpecies_; jNeut++) {
767  for (size_t k = 0; k < m_kk; k++) {
768  fmij = fm_neutralMolec_ions_[k + jNeut * m_kk];
769  moleFractionsTmp_[k] -= fmij * dy[jNeut];
770  }
771  }
772  for (size_t k = 0; k < m_kk; k++) {
773  if (fabs(moleFractionsTmp_[k]) > 1.0E-13) {
774  //! Check to see if we have in fact found the inverse.
775  if (anionList_[0] != k) {
776  throw CanteraError("", "neutral molecule calc error");
777  } else {
778  //! For the single anion case, we will allow some slippage
779  if (fabs(moleFractionsTmp_[k]) > 1.0E-5) {
780  throw CanteraError("", "neutral molecule calc error - anion");
781  }
782  }
783  }
784  }
785 #endif
786  // Normalize the Neutral Molecule mole fractions
787  sumy = 0.0;
788  sumdy = 0.0;
789  for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
790  sumy += y_[k];
791  sumdy += dy[k];
792  }
793  sumy = 1.0 / sumy;
794  for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
795  dy[k] = dy[k] * sumy - y_[k]*sumdy*sumy*sumy;
796  }
797 
798  break;
799 
800  case cIonSolnType_SINGLECATION:
801 
802  throw CanteraError("eosType", "Unknown type");
803 
804  break;
805 
806  case cIonSolnType_MULTICATIONANION:
807 
808  throw CanteraError("eosType", "Unknown type");
809  break;
810 
811  default:
812 
813  throw CanteraError("eosType", "Unknown type");
814  break;
815 
816  }
817 }
818 
819 void IonsFromNeutralVPSSTP::setMassFractions(const doublereal* const y)
820 {
824 }
825 
826 void IonsFromNeutralVPSSTP::setMassFractions_NoNorm(const doublereal* const y)
827 {
831 }
832 
833 void IonsFromNeutralVPSSTP::setMoleFractions(const doublereal* const x)
834 {
838 }
839 
840 void IonsFromNeutralVPSSTP::setMoleFractions_NoNorm(const doublereal* const x)
841 {
845 }
846 
847 void IonsFromNeutralVPSSTP::setConcentrations(const doublereal* const c)
848 {
852 }
853 
854 /*
855  * ------------ Partial Molar Properties of the Solution ------------
856  */
857 
858 doublereal IonsFromNeutralVPSSTP::err(const std::string& msg) const
859 {
860  throw CanteraError("IonsFromNeutralVPSSTP","Base class method "
861  +msg+" called. Equation of state type: "+int2str(eosType()));
862  return 0;
863 }
864 
866 {
867  initLengths();
869 }
870 
872 {
873  m_kk = nSpecies();
875  moleFractions_.resize(m_kk);
877  fm_invert_ionForNeutral.resize(m_kk);
879  cationList_.resize(m_kk);
880  anionList_.resize(m_kk);
881  passThroughList_.resize(m_kk);
882  moleFractionsTmp_.resize(m_kk);
889 
890  y_.resize(numNeutralMoleculeSpecies_, 0.0);
891  dlnActCoeff_NeutralMolecule_.resize(numNeutralMoleculeSpecies_, 0.0);
892  dX_NeutralMolecule_.resize(numNeutralMoleculeSpecies_, 0.0);
893 
894 }
895 
896 //! Return the factor overlap
897 /*!
898  * @param elnamesVN
899  * @param elemVectorN
900  * @param nElementsN
901  * @param elnamesVI
902  * @param elemVectorI
903  * @param nElementsI
904  */
905 static double factorOverlap(const std::vector<std::string>& elnamesVN ,
906  const std::vector<double>& elemVectorN,
907  const size_t nElementsN,
908  const std::vector<std::string>& elnamesVI ,
909  const std::vector<double>& elemVectorI,
910  const size_t nElementsI)
911 {
912  double fMax = 1.0E100;
913  for (size_t mi = 0; mi < nElementsI; mi++) {
914  if (elnamesVI[mi] != "E") {
915  if (elemVectorI[mi] > 1.0E-13) {
916  double eiNum = elemVectorI[mi];
917  for (size_t mn = 0; mn < nElementsN; mn++) {
918  if (elnamesVI[mi] == elnamesVN[mn]) {
919  if (elemVectorN[mn] <= 1.0E-13) {
920  return 0.0;
921  }
922  fMax = std::min(fMax, elemVectorN[mn]/eiNum);
923  }
924  }
925  }
926  }
927  }
928  return fMax;
929 }
930 void IonsFromNeutralVPSSTP::initThermoXML(XML_Node& phaseNode, const std::string& id_)
931 {
932  string stemp;
933  if (id_.size() > 0) {
934  string idp = phaseNode.id();
935  if (idp != id_) {
936  throw CanteraError("IonsFromNeutralVPSSTP::initThermoXML",
937  "phasenode and Id are incompatible");
938  }
939  }
940 
941  /*
942  * Find the Thermo XML node
943  */
944  if (!phaseNode.hasChild("thermo")) {
945  throw CanteraError("IonsFromNeutralVPSSTP::initThermoXML",
946  "no thermo XML node");
947  }
948  XML_Node& thermoNode = phaseNode.child("thermo");
949 
950 
951 
952  /*
953  * Make sure that the thermo model is IonsFromNeutralMolecule
954  */
955  stemp = thermoNode.attrib("model");
956  string formString = lowercase(stemp);
957  if (formString != "ionsfromneutralmolecule") {
958  throw CanteraError("IonsFromNeutralVPSSTP::initThermoXML",
959  "model name isn't IonsFromNeutralMolecule: " + formString);
960  }
961 
962  /*
963  * Find the Neutral Molecule Phase
964  */
965  if (!thermoNode.hasChild("neutralMoleculePhase")) {
966  throw CanteraError("IonsFromNeutralVPSSTP::initThermoXML",
967  "no neutralMoleculePhase XML node");
968  }
969  XML_Node& neutralMoleculeNode = thermoNode.child("neutralMoleculePhase");
970 
971  string nsource = neutralMoleculeNode["datasrc"];
972  XML_Node* neut_ptr = get_XML_Node(nsource, 0);
973  if (!neut_ptr) {
974  throw CanteraError("IonsFromNeutralVPSSTP::initThermoXML",
975  "neut_ptr = 0");
976  }
977 
978  /*
979  * Create the neutralMolecule ThermoPhase if we haven't already
980  */
981  if (!neutralMoleculePhase_) {
982  neutralMoleculePhase_ = newPhase(*neut_ptr);
983  }
984 
985  /*
986  * variables that need to be populated
987  *
988  * cationList_
989  * numCationSpecies_;
990  */
991 
992  numCationSpecies_ = 0;
993  cationList_.clear();
994  for (size_t k = 0; k < m_kk; k++) {
995  if (charge(k) > 0) {
996  cationList_.push_back(k);
998  }
999  }
1000 
1001  numAnionSpecies_ = 0;
1002  anionList_.clear();
1003  for (size_t k = 0; k < m_kk; k++) {
1004  if (charge(k) < 0) {
1005  anionList_.push_back(k);
1006  numAnionSpecies_++;
1007  }
1008  }
1009 
1011  passThroughList_.clear();
1012  for (size_t k = 0; k < m_kk; k++) {
1013  if (charge(k) == 0) {
1014  passThroughList_.push_back(k);
1016  }
1017  }
1018 
1019  PDSS_IonsFromNeutral* speciesSS = 0;
1021  for (size_t k = 0; k < m_kk; k++) {
1022  speciesSS = dynamic_cast<PDSS_IonsFromNeutral*>(providePDSS(k));
1023  if (!speciesSS) {
1024  throw CanteraError("initThermoXML", "Dynamic cast failed");
1025  }
1026  if (speciesSS->specialSpecies_ == 1) {
1028  }
1029  if (speciesSS->specialSpecies_ == 2) {
1031  }
1032  }
1033 
1034 
1035  size_t nElementsN = neutralMoleculePhase_->nElements();
1036  const std::vector<std::string>& elnamesVN = neutralMoleculePhase_->elementNames();
1037  std::vector<double> elemVectorN(nElementsN);
1038  std::vector<double> elemVectorN_orig(nElementsN);
1039 
1040  size_t nElementsI = nElements();
1041  const std::vector<std::string>& elnamesVI = elementNames();
1042  std::vector<double> elemVectorI(nElementsI);
1043 
1044  vector<doublereal> fm_tmp(m_kk);
1045  for (size_t k = 0; k < m_kk; k++) {
1047  }
1048  /* for (int jNeut = 0; jNeut < numNeutralMoleculeSpecies_; jNeut++) {
1049  fm_invert_ionForNeutral[jNeut] = -1;
1050  }*/
1051  for (size_t jNeut = 0; jNeut < numNeutralMoleculeSpecies_; jNeut++) {
1052  for (size_t m = 0; m < nElementsN; m++) {
1053  elemVectorN[m] = neutralMoleculePhase_->nAtoms(jNeut, m);
1054  }
1055  elemVectorN_orig = elemVectorN;
1056  fm_tmp.assign(m_kk, 0.0);
1057 
1058  for (size_t m = 0; m < nElementsI; m++) {
1059  elemVectorI[m] = nAtoms(indexSpecialSpecies_, m);
1060  }
1061  double fac = factorOverlap(elnamesVN, elemVectorN, nElementsN,
1062  elnamesVI ,elemVectorI, nElementsI);
1063  if (fac > 0.0) {
1064  for (size_t m = 0; m < nElementsN; m++) {
1065  std::string mName = elnamesVN[m];
1066  for (size_t mi = 0; mi < nElementsI; mi++) {
1067  std::string eName = elnamesVI[mi];
1068  if (mName == eName) {
1069  elemVectorN[m] -= fac * elemVectorI[mi];
1070  }
1071 
1072  }
1073  }
1074  }
1075  fm_neutralMolec_ions_[indexSpecialSpecies_ + jNeut * m_kk ] += fac;
1076 
1077 
1078  for (size_t k = 0; k < m_kk; k++) {
1079  for (size_t m = 0; m < nElementsI; m++) {
1080  elemVectorI[m] = nAtoms(k, m);
1081  }
1082  fac = factorOverlap(elnamesVN, elemVectorN, nElementsN,
1083  elnamesVI ,elemVectorI, nElementsI);
1084  if (fac > 0.0) {
1085  for (size_t m = 0; m < nElementsN; m++) {
1086  std::string mName = elnamesVN[m];
1087  for (size_t mi = 0; mi < nElementsI; mi++) {
1088  std::string eName = elnamesVI[mi];
1089  if (mName == eName) {
1090  elemVectorN[m] -= fac * elemVectorI[mi];
1091  }
1092 
1093  }
1094  }
1095  bool notTaken = true;
1096  for (size_t iNeut = 0; iNeut < jNeut; iNeut++) {
1097  if (fm_invert_ionForNeutral[k] == iNeut) {
1098  notTaken = false;
1099  }
1100  }
1101  if (notTaken) {
1102  fm_invert_ionForNeutral[k] = jNeut;
1103  } else {
1104  throw CanteraError("IonsFromNeutralVPSSTP::initThermoXML",
1105  "Simple formula matrix generation failed, one cation is shared between two salts");
1106  }
1107  }
1108  fm_neutralMolec_ions_[k + jNeut * m_kk] += fac;
1109  }
1110 
1111  // Ok check the work
1112  for (size_t m = 0; m < nElementsN; m++) {
1113  if (fabs(elemVectorN[m]) > 1.0E-13) {
1114  throw CanteraError("IonsFromNeutralVPSSTP::initThermoXML",
1115  "Simple formula matrix generation failed");
1116  }
1117  }
1118 
1119 
1120  }
1121  /*
1122  * This includes the setStateFromXML calls
1123  */
1124  GibbsExcessVPSSTP::initThermoXML(phaseNode, id_);
1125 
1126  /*
1127  * There is one extra step here. We assure ourselves that we
1128  * have charge conservation.
1129  */
1130 }
1131 
1133 {
1134  size_t icat, jNeut;
1135  doublereal fmij;
1136  /*
1137  * Get the activity coefficiens of the neutral molecules
1138  */
1140 
1141  switch (ionSolnType_) {
1142  case cIonSolnType_PASSTHROUGH:
1143  break;
1144  case cIonSolnType_SINGLEANION:
1145 
1146  // Do the cation list
1147  for (size_t k = 0; k < cationList_.size(); k++) {
1148  //! Get the id for the next cation
1149  icat = cationList_[k];
1150  jNeut = fm_invert_ionForNeutral[icat];
1151  fmij = fm_neutralMolec_ions_[icat + jNeut * m_kk];
1152  lnActCoeff_Scaled_[icat] = lnActCoeff_NeutralMolecule_[jNeut] / fmij;
1153  }
1154 
1155  // Do the anion list
1156  icat = anionList_[0];
1157  jNeut = fm_invert_ionForNeutral[icat];
1158  lnActCoeff_Scaled_[icat]= 0.0;
1159 
1160  // Do the list of neutral molecules
1161  for (size_t k = 0; k < numPassThroughSpecies_; k++) {
1162  icat = passThroughList_[k];
1163  jNeut = fm_invert_ionForNeutral[icat];
1165  }
1166  break;
1167 
1168  case cIonSolnType_SINGLECATION:
1169  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeff", "Unimplemented type");
1170  break;
1171  case cIonSolnType_MULTICATIONANION:
1172  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeff", "Unimplemented type");
1173  break;
1174  default:
1175  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeff", "Unimplemented type");
1176  break;
1177  }
1178 
1179 }
1180 
1181 void IonsFromNeutralVPSSTP::getdlnActCoeffds(const doublereal dTds, const doublereal* const dXds,
1182  doublereal* dlnActCoeffds) const
1183 {
1184  size_t icat, jNeut;
1185  doublereal fmij;
1186  /*
1187  * Get the activity coefficients of the neutral molecules
1188  */
1189  if (!geThermo) {
1190  for (size_t k = 0; k < m_kk; k++) {
1191  dlnActCoeffds[k] = dXds[k] / moleFractions_[k];
1192  }
1193  return;
1194  }
1195 
1196  // static vector_fp dlnActCoeff_NeutralMolecule(numNeutMolSpec);
1197  // static vector_fp dX_NeutralMolecule(numNeutMolSpec);
1198 
1199 
1200  getNeutralMoleculeMoleGrads(DATA_PTR(dXds),DATA_PTR(dX_NeutralMolecule_));
1201 
1202  // All mole fractions returned to normal
1203 
1204  geThermo->getdlnActCoeffds(dTds, DATA_PTR(dX_NeutralMolecule_), DATA_PTR(dlnActCoeff_NeutralMolecule_));
1205 
1206  switch (ionSolnType_) {
1207  case cIonSolnType_PASSTHROUGH:
1208  break;
1209  case cIonSolnType_SINGLEANION:
1210 
1211  // Do the cation list
1212  for (size_t k = 0; k < cationList_.size(); k++) {
1213  //! Get the id for the next cation
1214  icat = cationList_[k];
1215  jNeut = fm_invert_ionForNeutral[icat];
1216  fmij = fm_neutralMolec_ions_[icat + jNeut * m_kk];
1217  dlnActCoeffds[icat] = dlnActCoeff_NeutralMolecule_[jNeut]/fmij;
1218  }
1219 
1220  // Do the anion list
1221  icat = anionList_[0];
1222  jNeut = fm_invert_ionForNeutral[icat];
1223  dlnActCoeffds[icat]= 0.0;
1224 
1225  // Do the list of neutral molecules
1226  for (size_t k = 0; k < numPassThroughSpecies_; k++) {
1227  icat = passThroughList_[k];
1228  jNeut = fm_invert_ionForNeutral[icat];
1229  dlnActCoeffds[icat] = dlnActCoeff_NeutralMolecule_[jNeut];
1230  }
1231  break;
1232 
1233  case cIonSolnType_SINGLECATION:
1234  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeffds", "Unimplemented type");
1235  break;
1236  case cIonSolnType_MULTICATIONANION:
1237  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeffds", "Unimplemented type");
1238  break;
1239  default:
1240  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeffds", "Unimplemented type");
1241  break;
1242  }
1243 
1244 }
1245 
1247 {
1248  size_t icat, jNeut;
1249  doublereal fmij;
1250  /*
1251  * Get the activity coefficients of the neutral molecules
1252  */
1253  if (!geThermo) {
1254  dlnActCoeffdT_Scaled_.assign(m_kk, 0.0);
1255  return;
1256  }
1257 
1259 
1260  switch (ionSolnType_) {
1261  case cIonSolnType_PASSTHROUGH:
1262  break;
1263  case cIonSolnType_SINGLEANION:
1264 
1265  // Do the cation list
1266  for (size_t k = 0; k < cationList_.size(); k++) {
1267  //! Get the id for the next cation
1268  icat = cationList_[k];
1269  jNeut = fm_invert_ionForNeutral[icat];
1270  fmij = fm_neutralMolec_ions_[icat + jNeut * m_kk];
1272  }
1273 
1274  // Do the anion list
1275  icat = anionList_[0];
1276  jNeut = fm_invert_ionForNeutral[icat];
1277  dlnActCoeffdT_Scaled_[icat]= 0.0;
1278 
1279  // Do the list of neutral molecules
1280  for (size_t k = 0; k < numPassThroughSpecies_; k++) {
1281  icat = passThroughList_[k];
1282  jNeut = fm_invert_ionForNeutral[icat];
1284  }
1285  break;
1286 
1287  case cIonSolnType_SINGLECATION:
1288  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeffdT", "Unimplemented type");
1289  break;
1290  case cIonSolnType_MULTICATIONANION:
1291  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeffdT", "Unimplemented type");
1292  break;
1293  default:
1294  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeffdT", "Unimplemented type");
1295  break;
1296  }
1297 
1298 }
1299 
1301 {
1302  size_t icat, jNeut;
1303  doublereal fmij;
1304  /*
1305  * Get the activity coefficients of the neutral molecules
1306  */
1307  if (!geThermo) {
1308  dlnActCoeffdlnX_diag_.assign(m_kk, 0.0);
1309  return;
1310  }
1311 
1313 
1314  switch (ionSolnType_) {
1315  case cIonSolnType_PASSTHROUGH:
1316  break;
1317  case cIonSolnType_SINGLEANION:
1318 
1319  // Do the cation list
1320  for (size_t k = 0; k < cationList_.size(); k++) {
1321  //! Get the id for the next cation
1322  icat = cationList_[k];
1323  jNeut = fm_invert_ionForNeutral[icat];
1324  fmij = fm_neutralMolec_ions_[icat + jNeut * m_kk];
1326  }
1327 
1328  // Do the anion list
1329  icat = anionList_[0];
1330  jNeut = fm_invert_ionForNeutral[icat];
1331  dlnActCoeffdlnX_diag_[icat]= 0.0;
1332 
1333  // Do the list of neutral molecules
1334  for (size_t k = 0; k < numPassThroughSpecies_; k++) {
1335  icat = passThroughList_[k];
1336  jNeut = fm_invert_ionForNeutral[icat];
1338  }
1339  break;
1340 
1341  case cIonSolnType_SINGLECATION:
1342  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeff_dlnX_diag()", "Unimplemented type");
1343  break;
1344  case cIonSolnType_MULTICATIONANION:
1345  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeff_dlnX_diag()", "Unimplemented type");
1346  break;
1347  default:
1348  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeff_dlnX_diag()", "Unimplemented type");
1349  break;
1350  }
1351 
1352 }
1353 
1355 {
1356  size_t icat, jNeut;
1357  doublereal fmij;
1358  /*
1359  * Get the activity coefficients of the neutral molecules
1360  */
1361  if (!geThermo) {
1362  dlnActCoeffdlnN_diag_.assign(m_kk, 0.0);
1363  return;
1364  }
1365 
1367 
1368  switch (ionSolnType_) {
1369  case cIonSolnType_PASSTHROUGH:
1370  break;
1371  case cIonSolnType_SINGLEANION:
1372 
1373  // Do the cation list
1374  for (size_t k = 0; k < cationList_.size(); k++) {
1375  //! Get the id for the next cation
1376  icat = cationList_[k];
1377  jNeut = fm_invert_ionForNeutral[icat];
1378  fmij = fm_neutralMolec_ions_[icat + jNeut * m_kk];
1380  }
1381 
1382  // Do the anion list
1383  icat = anionList_[0];
1384  jNeut = fm_invert_ionForNeutral[icat];
1385  dlnActCoeffdlnN_diag_[icat]= 0.0;
1386 
1387  // Do the list of neutral molecules
1388  for (size_t k = 0; k < numPassThroughSpecies_; k++) {
1389  icat = passThroughList_[k];
1390  jNeut = fm_invert_ionForNeutral[icat];
1392  }
1393  break;
1394 
1395  case cIonSolnType_SINGLECATION:
1396  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeff_dlnN_diag()", "Unimplemented type");
1397  break;
1398  case cIonSolnType_MULTICATIONANION:
1399  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeff_dlnN_diag()", "Unimplemented type");
1400  break;
1401  default:
1402  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeff_dlnN_diag()", "Unimplemented type");
1403  break;
1404  }
1405 
1406 }
1407 
1409 {
1410  size_t kcat = 0, kNeut = 0, mcat = 0, mNeut = 0;
1411  doublereal fmij = 0.0, mfmij;
1413  /*
1414  * Get the activity coefficients of the neutral molecules
1415  */
1416  if (!geThermo) {
1417  throw CanteraError("IonsFromNeutralVPSSTP::s_update_dlnActCoeff_dlnN()", "dynamic cast failed");
1418  }
1419  size_t nsp_ge = geThermo->nSpecies();
1420  geThermo->getdlnActCoeffdlnN(nsp_ge, &(dlnActCoeffdlnN_NeutralMolecule_(0,0)));
1421 
1422  switch (ionSolnType_) {
1423  case cIonSolnType_PASSTHROUGH:
1424  break;
1425  case cIonSolnType_SINGLEANION:
1426 
1427  // Do the cation list
1428  for (size_t k = 0; k < cationList_.size(); k++) {
1429  for (size_t m = 0; m < cationList_.size(); m++) {
1430  kcat = cationList_[k];
1431 
1432  kNeut = fm_invert_ionForNeutral[kcat];
1433  fmij = fm_neutralMolec_ions_[kcat + kNeut * m_kk];
1435 
1436  mcat = cationList_[m];
1437  mNeut = fm_invert_ionForNeutral[mcat];
1438  mfmij = fm_neutralMolec_ions_[mcat + mNeut * m_kk];
1439 
1440  dlnActCoeffdlnN_(kcat,mcat) = dlnActCoeffdlnN_NeutralMolecule_(kNeut,mNeut) * mfmij / fmij;
1441 
1442  }
1443  for (size_t m = 0; m < numPassThroughSpecies_; m++) {
1444  mcat = passThroughList_[m];
1445  mNeut = fm_invert_ionForNeutral[mcat];
1446  dlnActCoeffdlnN_(kcat, mcat) = dlnActCoeffdlnN_NeutralMolecule_(kNeut, mNeut) / fmij;
1447  }
1448  }
1449 
1450  // Do the anion list -> anion activity coefficient is one
1451  kcat = anionList_[0];
1452  kNeut = fm_invert_ionForNeutral[kcat];
1453  for (size_t k = 0; k < m_kk; k++) {
1454  dlnActCoeffdlnN_(kcat, k) = 0.0;
1455  dlnActCoeffdlnN_(k, kcat) = 0.0;
1456  }
1457 
1458  // Do the list of neutral molecules
1459  for (size_t k = 0; k < numPassThroughSpecies_; k++) {
1460  kcat = passThroughList_[k];
1461  kNeut = fm_invert_ionForNeutral[kcat];
1463 
1464  for (size_t m = 0; m < m_kk; m++) {
1465  mcat = passThroughList_[m];
1466  mNeut = fm_invert_ionForNeutral[mcat];
1467  dlnActCoeffdlnN_(kcat, mcat) = dlnActCoeffdlnN_NeutralMolecule_(kNeut, mNeut);
1468  }
1469 
1470 
1471  for (size_t m = 0; m < cationList_.size(); m++) {
1472  mcat = cationList_[m];
1473  mNeut = fm_invert_ionForNeutral[mcat];
1474  mfmij = fm_neutralMolec_ions_[mcat + mNeut * m_kk];
1475  dlnActCoeffdlnN_(kcat, mcat) = dlnActCoeffdlnN_NeutralMolecule_(kNeut,mNeut);
1476  }
1477 
1478  }
1479  break;
1480 
1481  case cIonSolnType_SINGLECATION:
1482  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeff_dlnN", "Unimplemented type");
1483  break;
1484  case cIonSolnType_MULTICATIONANION:
1485  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeff_dlnN", "Unimplemented type");
1486  break;
1487  default:
1488  throw CanteraError("IonsFromNeutralVPSSTP::s_update_lnActCoeff_dlnN", "Unimplemented type");
1489  break;
1490  }
1491 }
1492 
1493 }
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 ...
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:249
doublereal nAtoms(size_t k, size_t m) const
Number of atoms of element m in species k.
Definition: Phase.cpp:215
virtual void setMassFractions(const doublereal *const y)
Set the mass fractions to the specified values, and then normalize them so that they sum to 1...
virtual void setMoleFractions_NoNorm(const doublereal *const x)
Set the mole fractions to the specified values without normalizing.
Definition: Phase.cpp:344
ThermoPhase * newPhase(XML_Node &xmlphase)
Create a new ThermoPhase object and initializes it according to the XML tree.
std::vector< doublereal > m_pp
Temporary storage space that is fair game.
virtual doublereal density() const
Density (kg/m^3).
Definition: Phase.h:534
std::string int2str(const int n, const std::string &fmt)
Convert an int to a string using a format converter.
Definition: stringUtils.cpp:40
virtual ~IonsFromNeutralVPSSTP()
Destructor.
virtual ThermoPhase * duplMyselfAsThermoPhase() const
Duplication routine for objects which inherit from ThermoPhase.
virtual void setConcentrations(const doublereal *const c)
Set the concentrations to the specified values within the phase.
virtual void setState_TP(doublereal t, doublereal p)
Set the temperature (K) and pressure (Pa)
XML_Node * findXMLPhase(XML_Node *root, const std::string &idtarget)
Search an XML_Node tree for a named phase XML_Node.
Definition: xml.cpp:1104
Derived class for pressure dependent standard states of an ideal gas species.
IonsFromNeutralVPSSTP & operator=(const IonsFromNeutralVPSSTP &b)
Assignment operator.
size_t nElements() const
Number of elements.
Definition: Phase.cpp:139
static double factorOverlap(const std::vector< std::string > &elnamesVN, const std::vector< double > &elemVectorN, const size_t nElementsN, const std::vector< std::string > &elnamesVI, const std::vector< double > &elemVectorI, const size_t nElementsI)
Return the factor overlap.
virtual doublereal cp_mole() const
Molar heat capacity at constant pressure. Units: J/kmol/K.
std::string attrib(const std::string &attr) const
Function returns the value of an attribute.
Definition: xml.cpp:534
std::vector< double > fm_neutralMolec_ions_
Formula Matrix for composition of neutral molecules in terms of the molecules in this ThermoPhase...
GibbsExcessVPSSTP & operator=(const GibbsExcessVPSSTP &b)
Assignment operator.
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:128
std::string findInputFile(const std::string &name)
Find an input file.
Definition: global.cpp:191
Header for intermediate ThermoPhase object for phases which consist of ions whose thermodynamics is c...
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:173
vector_fp m_speciesCharge
Vector of species charges. length m_kk.
Definition: Phase.h:731
Headers for the factory class that can create known ThermoPhase objects (see Thermodynamic Properties...
size_t indexSpecialSpecies_
Index of special species.
virtual void calcNeutralMoleculeMoleFractions() const
Calculate neutral molecule mole fractions.
virtual doublereal intEnergy_mole() const
Molar internal energy.
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:100
virtual void calcIonMoleFractions(doublereal *const mf) const
Calculate ion mole fractions from neutral molecule mole fractions.
void constructPhaseFile(std::string inputFile, std::string id)
The following methods are used in the process of constructing the phase and setting its parameters fr...
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:1542
virtual void getStandardChemPotentials(doublereal *mu) const
Get the array of chemical potentials at unit activity for the species at their standard states at the...
Definition: ThermoPhase.h:711
doublereal molarDensity() const
Molar density (kmol/m^3).
Definition: Phase.cpp:597
std::string lowercase(const std::string &s)
Cast a copy of a string to lower case.
Definition: stringUtils.cpp:58
void getMoleFractions(doublereal *const x) const
Get the species mole fraction vector.
Definition: Phase.cpp:519
void s_update_dlnActCoeff_dlnN_diag() const
Update the derivative of the log of the activity coefficients wrt log(number of moles) - diagonal com...
std::vector< doublereal > dlnActCoeffdlnN_diag_
Storage for the current derivative values of the gradients with respect to logarithm of the mole frac...
virtual void getActivityCoefficients(doublereal *ac) const
Get the array of non-dimensional molar-based activity coefficients at the current solution temperatur...
bool IOwnNThermoPhase_
If true then we own the underlying neutral Molecule Phase.
void getDissociationCoeffs(vector_fp &fm_neutralMolec_ions, vector_fp &charges, std::vector< size_t > &neutMolIndex) const
Get the Salt Dissociation Coefficients Returns the vector of dissociation coefficients and vector of ...
void constructPhaseXML(XML_Node &phaseNode, std::string id)
Import and initialize an IonsFromNeutralVPSSTP phase specification in an XML tree into the current ob...
XML_Node & child(const size_t n) const
Return a changeable reference to the n'th child of the current node.
Definition: xml.cpp:584
virtual int eosType() const
Equation of state type flag.
Base class for a phase with thermodynamic properties.
Definition: ThermoPhase.h:101
doublereal mean_X(const doublereal *const Q) const
Evaluate the mole-fraction-weighted mean of an array Q.
Definition: Phase.cpp:623
doublereal err(const std::string &msg) const
Error function.
bool importPhase(XML_Node &phase, ThermoPhase *th, SpeciesThermoFactory *spfactory)
Import a phase information into an empty thermophase object.
virtual void getdlnActCoeffdlnN_diag(doublereal *dlnActCoeffdlnN_diag) const
Get the array of log concentration-like derivatives of the log activity coefficients - diagonal compo...
virtual doublereal cv_mole() const
Molar heat capacity at constant volume. Units: J/kmol/K.
virtual doublereal enthalpy_mole() const
Return the Molar enthalpy. Units: J/kmol.
virtual void setTemperature(const doublereal t)
Set the temperature of the phase.
virtual doublereal entropy_mole() const
Molar entropy. Units: J/kmol/K.
doublereal pressure() const
Returns the current pressure of the phase.
virtual void getPartialMolarEntropies(doublereal *sbar) const
Returns an array of partial molar entropies for the species in the mixture.
std::string fp2str(const double x, const std::string &fmt)
Convert a double into a c++ string.
Definition: stringUtils.cpp:29
virtual void setConcentrations(const doublereal *const c)
Set the concentrations to the specified values within the phase.
virtual void getdlnActCoeffdlnN_diag(doublereal *dlnActCoeffdlnN_diag) const
Get the array of log concentration-like derivatives of the log activity coefficients.
virtual void getChemPotentials(doublereal *mu) const
Get the species chemical potentials. Units: J/kmol.
Definition: ThermoPhase.h:595
IonSolnType_enumType ionSolnType_
Ion solution type.
virtual void getdlnActCoeffdT(doublereal *dlnActCoeffdT) const
Get the array of temperature derivatives of the log activity coefficients.
virtual void getdlnActCoeffdlnN(const size_t ld, doublereal *const dlnActCoeffdlnN)
Get the array of derivatives of the ln activity coefficients with respect to the ln species mole numb...
Array2D dlnActCoeffdlnN_NeutralMolecule_
Storage vector for the neutral molecule d ln activity coefficients dlnN.
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, etc) along a line in parameter space or along a line in physical space.
Definition: ThermoPhase.h:1520
std::vector< doublereal > dlnActCoeffdT_NeutralMolecule_
Storage vector for the neutral molecule d ln activity coefficients dT.
void initLengths()
Initialize lengths of local variables after all species have been identified.
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:68
virtual void setMoleFractions(const doublereal *const x)
Set the mole fractions to the specified values, and then normalize them so that they sum to 1...
std::vector< doublereal > muNeutralMolecule_
Storage vector for the neutral molecule chemical potentials.
void s_update_dlnActCoeffdT() const
Update the temperature derivative of the ln activity coefficients.
virtual void setMoleFractions(const doublereal *const x)
Set the mole fractions to the specified values There is no restriction on the sum of the mole fractio...
Definition: Phase.cpp:306
bool hasChild(const std::string &ch) const
Tests whether the current node has a child node with a particular name.
Definition: xml.cpp:574
const std::vector< std::string > & elementNames() const
Return a read-only reference to the vector of element names.
Definition: Phase.cpp:174
virtual void setMassFractions(const doublereal *const y)
Set the mass fractions to the specified values, and then normalize them so that they sum to 1...
virtual void setMoleFractions(const doublereal *const x)
Set the mole fractions to the specified values, and then normalize them so that they sum to 1...
virtual void setMassFractions_NoNorm(const doublereal *const y)
Set the mass fractions to the specified values without normalizing.
void s_update_dlnActCoeff_dlnX_diag() const
Update the derivative of the log of the activity coefficients wrt log(mole fraction) ...
std::vector< size_t > anionList_
List of the species in this ThermoPhase which are anion species.
virtual void setState_TP(doublereal t, doublereal p)
Set the temperature (K) and pressure (Pa)
std::vector< size_t > passThroughList_
List of the species in this ThermoPhase which are passed through to the neutralMoleculePhase ThermoPh...
void copy(XML_Node *const node_dest) const
Copy all of the information in the current XML_Node tree into the destination XML_Node tree...
Definition: xml.cpp:875
void s_update_dlnActCoeff_dlnN() const
Update the derivative of the log of the activity coefficients wrt log(number of moles) - diagonal com...
std::vector< doublereal > lnActCoeff_NeutralMolecule_
Storage vector for the neutral molecule ln activity coefficients.
int numCationSpecies_
Number of cation species.
size_t nSpecies() const
Returns the number of species in the phase.
Definition: Phase.h:252
void getNeutralMoleculeMoleGrads(const doublereal *const dx, doublereal *const dy) const
Calculate neutral molecule mole fractions.
std::vector< doublereal > dlnActCoeffdlnX_diag_
Storage for the current derivative values of the gradients with respect to logarithm of the mole frac...
size_t numPassThroughSpecies_
Number of the species in this ThermoPhase which are passed through to the neutralMoleculePhase Thermo...
#define AssertTrace(expr)
Assertion must be true or an error is thrown.
Definition: ctexceptions.h:216
virtual void getEntropy_R(doublereal *sr) const
Get the array of nondimensional Enthalpy functions for the standard state species at the current T an...
doublereal temperature() const
Temperature (K).
Definition: Phase.h:528
std::string id() const
Return the id attribute, if present.
Definition: xml.cpp:467
virtual void getdlnActCoeffdlnX_diag(doublereal *dlnActCoeffdlnX_diag) const
Get the array of log concentration-like derivatives of the log activity coefficients - diagonal compo...
const doublereal SmallNumber
smallest number to compare to zero.
Definition: ct_defs.h:139
std::vector< doublereal > lnActCoeff_Scaled_
Storage for the current values of the activity coefficients of the species.
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:165
virtual void getLnActivityCoefficients(doublereal *lnac) const
Get the array of non-dimensional molar-based ln activity coefficients at the current solution tempera...
std::vector< doublereal > moleFractions_
Storage for the current values of the mole fractions of the species.
std::vector< size_t > fm_invert_ionForNeutral
Mapping between ion species and neutral molecule for quick invert.
virtual void getEnthalpy_RT(doublereal *hrt) const
Get the nondimensional Enthalpy functions for the species at their standard states at the current T a...
const doublereal GasConstant
Universal Gas Constant. [J/kmol/K].
Definition: ct_defs.h:66
virtual void initThermoXML(XML_Node &phaseNode, const std::string &id)
Initialize a ThermoPhase object, potentially reading activity coefficient information from an XML dat...
virtual void setMoleFractions_NoNorm(const doublereal *const x)
Set the mole fractions to the specified values without normalizing.
Contains declarations for string manipulation functions within Cantera.
#define DATA_PTR(vec)
Creates a pointer to the start of the raw data for a vector.
Definition: ct_defs.h:36
std::vector< doublereal > dlnActCoeffdlnN_diag_NeutralMolecule_
Storage vector for the neutral molecule d ln activity coefficients dlnN - diagonal component...
std::vector< size_t > cationList_
List of the species in this ThermoPhase which are cation species.
virtual doublereal gibbs_mole() const
Molar Gibbs free Energy for an ideal gas. Units = J/kmol.
void initThermoXML(XML_Node &phaseNode, const std::string &id)
Import and initialize a ThermoPhase object.
XML_Node & xml()
Returns a reference to the XML_Node stored for the phase.
Definition: Phase.cpp:114
size_t m_kk
Number of species in the phase.
Definition: Phase.h:716
Array2D dlnActCoeffdlnN_
Storage for the current derivative values of the gradients with respect to logarithm of the species m...
int numAnionSpecies_
Number of anion species.
std::vector< doublereal > dlnActCoeffdlnX_diag_NeutralMolecule_
Storage vector for the neutral molecule d ln activity coefficients dX - diagonal component.
virtual void getPartialMolarCp(doublereal *cpbar) const
Return an array of partial molar heat capacities for the species in the mixture.
Definition: ThermoPhase.h:659
virtual void getdlnActCoeffds(const doublereal dTds, const doublereal *const dXds, doublereal *dlnActCoeffds) const
Get the change in activity coefficients w.r.t.
void zero()
Set all of the entries to zero.
Definition: Array.h:256
virtual void setMoleFractions_NoNorm(const doublereal *const x)
Set the mole fractions to the specified values without normalizing.
std::vector< doublereal > NeutralMolecMoleFractions_
Mole fractions using the Neutral Molecule Mole fraction basis.
virtual void getPartialMolarEnthalpies(doublereal *hbar) const
Returns an array of partial molar enthalpies for the species in the mixture.
virtual void setMassFractions_NoNorm(const doublereal *const y)
Set the mass fractions to the specified values without normalizing.
size_t indexSecondSpecialSpecies_
Index of special species.
Declarations for the class PDSS_IonsFromNeutral ( which handles calculations for a single ion in a fl...
size_t numNeutralMoleculeSpecies_
Number of neutral molecule species.
ThermoPhase * neutralMoleculePhase_
This is a pointer to the neutral Molecule Phase.
virtual void getChemPotentials(doublereal *mu) const
Get the species chemical potentials. Units: J/kmol.
void build(std::istream &f)
Main routine to create an tree-like representation of an XML file.
Definition: xml.cpp:776
void s_update_lnActCoeff() const
Update the activity coefficients.
int specialSpecies_
True if this species is the special species.
std::vector< doublereal > dlnActCoeffdT_Scaled_
Storage for the current derivative values of the gradients with respect to temperature of the log of ...
virtual void setDensity(const doublereal density_)
Set the internally stored density (kg/m^3) of the phase Note the density of a phase is an independent...
Definition: Phase.h:549
virtual void setState_TP(doublereal T, doublereal pres)
Set the temperature and pressure at the same time.
std::vector< doublereal > moleFractionsTmp_
Temporary mole fraction vector.
virtual void setPressure(doublereal p)
Set the internally stored pressure (Pa) at constant temperature and composition.
doublereal 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:504