Cantera 2.6.0
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
22
23#include <fstream>
24
25using namespace std;
26
27namespace Cantera
28{
29
31 const std::string& id_) :
32 ionSolnType_(cIonSolnType_SINGLEANION),
33 numNeutralMoleculeSpecies_(0),
34 indexSpecialSpecies_(npos)
35{
36 initThermoFile(inputFile, id_);
37}
38
40 const std::string& id_) :
41 ionSolnType_(cIonSolnType_SINGLEANION),
42 numNeutralMoleculeSpecies_(0),
43 indexSpecialSpecies_(npos)
44{
45 importPhase(phaseRoot, this);
46}
47
48// ------------ Molar Thermodynamic Properties ----------------------
49
51{
52 getPartialMolarEnthalpies(m_work.data());
53 return mean_X(m_work);
54}
55
57{
58 getPartialMolarEntropies(m_work.data());
59 return mean_X(m_work);
60}
61
63{
64 getChemPotentials(m_work.data());
65 return mean_X(m_work);
66}
67
69{
70 getPartialMolarCp(m_work.data());
71 return mean_X(m_work);
72}
73
75{
76 // Need to revisit this, as it is wrong
77 getPartialMolarCp(m_work.data());
78 return mean_X(m_work);
79}
80
81// -- Activities, Standard States, Activity Concentrations -----------
82
84 vector_fp& charges, std::vector<size_t>& neutMolIndex) const
85{
86 coeffs = fm_neutralMolec_ions_;
87 charges = m_speciesCharge;
88 neutMolIndex = fm_invert_ionForNeutral;
89}
90
92{
93 // Update the activity coefficients
95
96 // take the exp of the internally stored coefficients.
97 for (size_t k = 0; k < m_kk; k++) {
98 ac[k] = exp(lnActCoeff_Scaled_[k]);
99 }
100}
101
102// --------- Partial Molar Properties of the Solution -------------
103
105{
106 size_t icat, jNeut;
107 doublereal xx, fact2;
108
109 // Get the standard chemical potentials of neutral molecules
110 neutralMoleculePhase_->getStandardChemPotentials(muNeutralMolecule_.data());
111
112 switch (ionSolnType_) {
113 case cIonSolnType_PASSTHROUGH:
114 neutralMoleculePhase_->getChemPotentials(mu);
115 break;
116 case cIonSolnType_SINGLEANION:
117 neutralMoleculePhase_->getLnActivityCoefficients(lnActCoeff_NeutralMolecule_.data());
118 fact2 = 2.0 * RT() * log(2.0);
119
120 // Do the cation list
121 for (size_t k = 0; k < cationList_.size(); k++) {
122 // Get the id for the next cation
123 icat = cationList_[k];
124 jNeut = fm_invert_ionForNeutral[icat];
125 xx = std::max(SmallNumber, moleFractions_[icat]);
126 mu[icat] = muNeutralMolecule_[jNeut] + fact2 + RT() * (lnActCoeff_NeutralMolecule_[jNeut] + log(xx));
127 }
128
129 // Do the anion list
130 icat = anionList_[0];
131 jNeut = fm_invert_ionForNeutral[icat];
132 xx = std::max(SmallNumber, moleFractions_[icat]);
133 mu[icat] = RT() * log(xx);
134
135 // Do the list of neutral molecules
136 for (size_t k = 0; k < passThroughList_.size(); k++) {
137 icat = passThroughList_[k];
138 jNeut = fm_invert_ionForNeutral[icat];
139 xx = std::max(SmallNumber, moleFractions_[icat]);
140 mu[icat] = muNeutralMolecule_[jNeut] + RT() * (lnActCoeff_NeutralMolecule_[jNeut] + log(xx));
141 }
142 break;
143
144 case cIonSolnType_SINGLECATION:
145 throw CanteraError("IonsFromNeutralVPSSTP::getChemPotentials", "Unknown type");
146 case cIonSolnType_MULTICATIONANION:
147 throw CanteraError("IonsFromNeutralVPSSTP::getChemPotentials", "Unknown type");
148 default:
149 throw CanteraError("IonsFromNeutralVPSSTP::getChemPotentials", "Unknown type");
150 }
151}
152
154{
155 // Get the nondimensional standard state enthalpies
156 getEnthalpy_RT(hbar);
157
158 // dimensionalize it.
159 for (size_t k = 0; k < m_kk; k++) {
160 hbar[k] *= RT();
161 }
162
163 // Update the activity coefficients, This also update the internally stored
164 // molalities.
167 for (size_t k = 0; k < m_kk; k++) {
168 hbar[k] -= RT() * temperature() * dlnActCoeffdT_Scaled_[k];
169 }
170}
171
173{
174 // Get the nondimensional standard state entropies
175 getEntropy_R(sbar);
176
177 // Update the activity coefficients, This also update the internally stored
178 // molalities.
181
182 for (size_t k = 0; k < m_kk; k++) {
183 double xx = std::max(moleFractions_[k], SmallNumber);
184 sbar[k] += - lnActCoeff_Scaled_[k] -log(xx) - temperature() * dlnActCoeffdT_Scaled_[k];
185 }
186
187 // dimensionalize it.
188 for (size_t k = 0; k < m_kk; k++) {
189 sbar[k] *= GasConstant;
190 }
191}
192
193void IonsFromNeutralVPSSTP::getdlnActCoeffdlnX_diag(doublereal* dlnActCoeffdlnX_diag) const
194{
197
198 for (size_t k = 0; k < m_kk; k++) {
199 dlnActCoeffdlnX_diag[k] = dlnActCoeffdlnX_diag_[k];
200 }
201}
202
203void IonsFromNeutralVPSSTP::getdlnActCoeffdlnN_diag(doublereal* dlnActCoeffdlnN_diag) const
204{
207
208 for (size_t k = 0; k < m_kk; k++) {
209 dlnActCoeffdlnN_diag[k] = dlnActCoeffdlnN_diag_[k];
210 }
211}
212
213void IonsFromNeutralVPSSTP::getdlnActCoeffdlnN(const size_t ld, doublereal* dlnActCoeffdlnN)
214{
217 double* data = & dlnActCoeffdlnN_(0,0);
218 for (size_t k = 0; k < m_kk; k++) {
219 for (size_t m = 0; m < m_kk; m++) {
220 dlnActCoeffdlnN[ld * k + m] = data[m_kk * k + m];
221 }
222 }
223}
224
226{
227 // This is a two phase process. First, we calculate the standard states
228 // within the neutral molecule phase.
229 neutralMoleculePhase_->setState_TP(temperature(), pressure());
230
231 // Calculate the partial molar volumes, and then the density of the fluid
233}
234
235void IonsFromNeutralVPSSTP::calcIonMoleFractions(doublereal* const mf) const
236{
237 // Download the neutral mole fraction vector into the vector,
238 // NeutralMolecMoleFractions_[]
239 neutralMoleculePhase_->getMoleFractions(NeutralMolecMoleFractions_.data());
240
241 // Zero the mole fractions
242 for (size_t k = 0; k < m_kk; k++) {
243 mf[k] = 0.0;
244 }
245
246 // Use the formula matrix to calculate the relative mole numbers.
247 for (size_t jNeut = 0; jNeut < numNeutralMoleculeSpecies_; jNeut++) {
248 for (size_t k = 0; k < m_kk; k++) {
249 double fmij = fm_neutralMolec_ions_[k + jNeut * m_kk];
250 mf[k] += fmij * NeutralMolecMoleFractions_[jNeut];
251 }
252 }
253
254 // Normalize the new mole fractions
255 doublereal sum = 0.0;
256 for (size_t k = 0; k < m_kk; k++) {
257 sum += mf[k];
258 }
259 for (size_t k = 0; k < m_kk; k++) {
260 mf[k] /= sum;
261 }
262}
263
265{
266 size_t icat, jNeut;
267 doublereal fmij;
268 doublereal sum = 0.0;
269
270 // Zero the vector we are trying to find.
271 for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
273 }
274 sum = -1.0;
275 for (size_t k = 0; k < m_kk; k++) {
276 sum += moleFractions_[k];
277 }
278 if (fabs(sum) > 1.0E-11) {
279 throw CanteraError("IonsFromNeutralVPSSTP::calcNeutralMoleculeMoleFractions",
280 "molefracts don't sum to one: {}", sum);
281 }
282
283 switch (ionSolnType_) {
284 case cIonSolnType_PASSTHROUGH:
285 for (size_t k = 0; k < m_kk; k++) {
287 }
288 break;
289
290 case cIonSolnType_SINGLEANION:
291 for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
293 }
294
295 for (size_t k = 0; k < cationList_.size(); k++) {
296 // Get the id for the next cation
297 icat = cationList_[k];
298 jNeut = fm_invert_ionForNeutral[icat];
299 if (jNeut != npos) {
300 fmij = fm_neutralMolec_ions_[icat + jNeut * m_kk];
301 AssertTrace(fmij != 0.0);
302 NeutralMolecMoleFractions_[jNeut] += moleFractions_[icat] / fmij;
303 }
304 }
305
306 for (size_t k = 0; k < passThroughList_.size(); k++) {
307 icat = passThroughList_[k];
308 jNeut = fm_invert_ionForNeutral[icat];
309 fmij = fm_neutralMolec_ions_[ icat + jNeut * m_kk];
310 NeutralMolecMoleFractions_[jNeut] += moleFractions_[icat] / fmij;
311 }
312
313 for (size_t k = 0; k < m_kk; k++) {
315 }
316 for (jNeut = 0; jNeut < numNeutralMoleculeSpecies_; jNeut++) {
317 for (size_t k = 0; k < m_kk; k++) {
318 fmij = fm_neutralMolec_ions_[k + jNeut * m_kk];
320 }
321 }
322 for (size_t k = 0; k < m_kk; k++) {
323 if (fabs(moleFractionsTmp_[k]) > 1.0E-13) {
324 // Check to see if we have in fact found the inverse.
325 if (anionList_[0] != k) {
326 throw CanteraError("IonsFromNeutralVPSSTP::calcNeutralMoleculeMoleFractions",
327 "neutral molecule calc error");
328 } else {
329 // For the single anion case, we will allow some slippage
330 if (fabs(moleFractionsTmp_[k]) > 1.0E-5) {
331 throw CanteraError("IonsFromNeutralVPSSTP::calcNeutralMoleculeMoleFractions",
332 "neutral molecule calc error - anion");
333 }
334 }
335 }
336 }
337
338 // Normalize the Neutral Molecule mole fractions
339 sum = 0.0;
340 for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
342 }
343 for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
345 }
346 break;
347
348 case cIonSolnType_SINGLECATION:
349 throw CanteraError("IonsFromNeutralVPSSTP::calcNeutralMoleculeMoleFractions", "Unknown type");
350 break;
351 case cIonSolnType_MULTICATIONANION:
352 throw CanteraError("IonsFromNeutralVPSSTP::calcNeutralMoleculeMoleFractions", "Unknown type");
353 break;
354 default:
355 throw CanteraError("IonsFromNeutralVPSSTP::calcNeutralMoleculeMoleFractions", "Unknown type");
356 break;
357 }
358}
359
360void IonsFromNeutralVPSSTP::getNeutralMoleculeMoleGrads(const doublereal* const dx, doublereal* const dy) const
361{
362 doublereal sumy, sumdy;
363
364 // check sum dx = 0
365 // Zero the vector we are trying to find.
366 for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
367 y_[k] = 0.0;
368 dy[k] = 0.0;
369 }
370
371 switch (ionSolnType_) {
372
373 case cIonSolnType_PASSTHROUGH:
374 for (size_t k = 0; k < m_kk; k++) {
375 dy[k] = dx[k];
376 }
377 break;
378
379 case cIonSolnType_SINGLEANION:
380 for (size_t k = 0; k < cationList_.size(); k++) {
381 // Get the id for the next cation
382 size_t icat = cationList_[k];
383 size_t jNeut = fm_invert_ionForNeutral[icat];
384 if (jNeut != npos) {
385 double fmij = fm_neutralMolec_ions_[icat + jNeut * m_kk];
386 AssertTrace(fmij != 0.0);
387 const doublereal temp = 1.0/fmij;
388 dy[jNeut] += dx[icat] * temp;
389 y_[jNeut] += moleFractions_[icat] * temp;
390 }
391 }
392
393 for (size_t k = 0; k < passThroughList_.size(); k++) {
394 size_t icat = passThroughList_[k];
395 size_t jNeut = fm_invert_ionForNeutral[icat];
396 double fmij = fm_neutralMolec_ions_[ icat + jNeut * m_kk];
397 const doublereal temp = 1.0/fmij;
398 dy[jNeut] += dx[icat] * temp;
399 y_[jNeut] += moleFractions_[icat] * temp;
400 }
401 // Normalize the Neutral Molecule mole fractions
402 sumy = 0.0;
403 sumdy = 0.0;
404 for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
405 sumy += y_[k];
406 sumdy += dy[k];
407 }
408 sumy = 1.0 / sumy;
409 for (size_t k = 0; k < numNeutralMoleculeSpecies_; k++) {
410 dy[k] = dy[k] * sumy - y_[k]*sumdy*sumy*sumy;
411 }
412
413 break;
414
415 case cIonSolnType_SINGLECATION:
416 throw CanteraError("IonsFromNeutralVPSSTP::getNeutralMoleculeMoleGrads",
417 "Unknown type");
418 break;
419 case cIonSolnType_MULTICATIONANION:
420 throw CanteraError("IonsFromNeutralVPSSTP::getNeutralMoleculeMoleGrads",
421 "Unknown type");
422 break;
423 default:
424 throw CanteraError("IonsFromNeutralVPSSTP::getNeutralMoleculeMoleGrads",
425 "Unknown type");
426 break;
427 }
428}
429
431{
434 neutralMoleculePhase_->setMoleFractions(NeutralMolecMoleFractions_.data());
435}
436
437// ------------ Partial Molar Properties of the Solution ------------
438
439//! Return the factor overlap
440/*!
441 * @param elnamesVN
442 * @param elemVectorN
443 * @param nElementsN
444 * @param elnamesVI
445 * @param elemVectorI
446 * @param nElementsI
447 */
448static double factorOverlap(const std::vector<std::string>& elnamesVN ,
449 const vector_fp& elemVectorN,
450 const size_t nElementsN,
451 const std::vector<std::string>& elnamesVI ,
452 const vector_fp& elemVectorI,
453 const size_t nElementsI)
454{
455 double fMax = 1.0E100;
456 for (size_t mi = 0; mi < nElementsI; mi++) {
457 if (elnamesVI[mi] != "E" && elemVectorI[mi] > 1.0E-13) {
458 double eiNum = elemVectorI[mi];
459 for (size_t mn = 0; mn < nElementsN; mn++) {
460 if (elnamesVI[mi] == elnamesVN[mn]) {
461 if (elemVectorN[mn] <= 1.0E-13) {
462 return 0.0;
463 }
464 fMax = std::min(fMax, elemVectorN[mn]/eiNum);
465 }
466 }
467 }
468 }
469 return fMax;
470}
471
473 const AnyMap& rootNode)
474{
475 ThermoPhase::setParameters(phaseNode, rootNode);
476 m_rootNode = rootNode;
477}
478
480{
481 if (m_input.hasKey("neutral-phase")) {
482 string neutralName = m_input["neutral-phase"].asString();
483 const auto& slash = boost::ifind_last(neutralName, "/");
484 if (slash) {
485 string fileName(neutralName.begin(), slash.begin());
486 neutralName = string(slash.end(), neutralName.end());
487 AnyMap infile = AnyMap::fromYamlFile(fileName,
488 m_input.getString("__file__", ""));
489 AnyMap& phaseNode = infile["phases"].getMapWhere("name", neutralName);
490 setNeutralMoleculePhase(newPhase(phaseNode, infile));
491 } else {
492 AnyMap& phaseNode = m_rootNode["phases"].getMapWhere("name", neutralName);
493 setNeutralMoleculePhase(newPhase(phaseNode, m_rootNode));
494 }
495 }
496
498 throw CanteraError(
499 "IonsFromNeutralVPSSTP::initThermo",
500 "The neutral phase has not been initialized. Are you missing the "
501 "'neutral-phase' key?"
502 );
503 }
504
505 size_t nElementsN = neutralMoleculePhase_->nElements();
506 const std::vector<std::string>& elnamesVN = neutralMoleculePhase_->elementNames();
507 vector_fp elemVectorN(nElementsN);
508
509 size_t nElementsI = nElements();
510 const std::vector<std::string>& elnamesVI = elementNames();
511 vector_fp elemVectorI(nElementsI);
512
513 if (indexSpecialSpecies_ == npos) {
514 throw CanteraError(
515 "IonsFromNeutralVPSSTP::initThermo",
516 "No special-species were specified in the phase."
517 );
518 }
519 for (size_t m = 0; m < nElementsI; m++) {
520 elemVectorI[m] = nAtoms(indexSpecialSpecies_, m);
521 }
522
523 for (size_t jNeut = 0; jNeut < numNeutralMoleculeSpecies_; jNeut++) {
524 for (size_t m = 0; m < nElementsN; m++) {
525 elemVectorN[m] = neutralMoleculePhase_->nAtoms(jNeut, m);
526 }
527
528 double fac = factorOverlap(elnamesVN, elemVectorN, nElementsN,
529 elnamesVI ,elemVectorI, nElementsI);
530 if (fac > 0.0) {
531 for (size_t m = 0; m < nElementsN; m++) {
532 for (size_t mi = 0; mi < nElementsI; mi++) {
533 if (elnamesVN[m] == elnamesVI[mi]) {
534 elemVectorN[m] -= fac * elemVectorI[mi];
535 }
536
537 }
538 }
539 }
541
542 for (size_t k = 0; k < m_kk; k++) {
543 for (size_t m = 0; m < nElementsI; m++) {
544 elemVectorI[m] = nAtoms(k, m);
545 }
546 fac = factorOverlap(elnamesVN, elemVectorN, nElementsN,
547 elnamesVI ,elemVectorI, nElementsI);
548 if (fac > 0.0) {
549 for (size_t m = 0; m < nElementsN; m++) {
550 for (size_t mi = 0; mi < nElementsI; mi++) {
551 if (elnamesVN[m] == elnamesVI[mi]) {
552 elemVectorN[m] -= fac * elemVectorI[mi];
553 }
554 }
555 }
556 bool notTaken = true;
557 for (size_t iNeut = 0; iNeut < jNeut; iNeut++) {
558 if (fm_invert_ionForNeutral[k] == iNeut) {
559 notTaken = false;
560 }
561 }
562 if (notTaken) {
563 fm_invert_ionForNeutral[k] = jNeut;
564 } else {
565 throw CanteraError("IonsFromNeutralVPSSTP::initThermo",
566 "Simple formula matrix generation failed, one cation is shared between two salts");
567 }
568 }
569 fm_neutralMolec_ions_[k + jNeut * m_kk] += fac;
570 }
571
572 // Ok check the work
573 for (size_t m = 0; m < nElementsN; m++) {
574 if (fabs(elemVectorN[m]) > 1.0E-13) {
575 throw CanteraError("IonsFromNeutralVPSSTP::initThermo",
576 "Simple formula matrix generation failed");
577 }
578 }
579 }
580
582}
583
585{
588 phaseNode["neutral-phase"] = neutralMoleculePhase_->name();
589 }
590}
591
592void 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
613shared_ptr<ThermoPhase> IonsFromNeutralVPSSTP::getNeutralMoleculePhase()
614{
616}
617
618bool 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];
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
710void 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();
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...
Declaration for class Cantera::Species.
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:399
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:1502
static AnyMap fromYamlFile(const std::string &name, const std::string &parent_name="")
Create an AnyMap from a YAML file.
Definition: AnyMap.cpp:1743
bool hasKey(const std::string &key) const
Returns true if the map contains an item named key.
Definition: AnyMap.cpp:1406
void zero()
Set all of the entries to zero.
Definition: Array.h:139
void resize(size_t n, size_t m, double v=0.0)
Resize the array, and fill the new entries with 'v'.
Definition: Array.cpp:52
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:61
virtual bool addSpecies(shared_ptr< Species > spec)
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 bool addSpecies(shared_ptr< Species > spec)
virtual void setParameters(const AnyMap &phaseNode, const AnyMap &rootNode=AnyMap())
Set equation of state parameters from an AnyMap phase description.
virtual void getParameters(AnyMap &phaseNode) const
Store the parameters of a ThermoPhase object such that an identical one could be reconstructed using ...
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...
IonsFromNeutralVPSSTP(const std::string &inputFile="", const std::string &id="")
Construct an IonsFromNeutralVPSSTP object from an input file.
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:408
void assignDensity(const double density_)
Set the internally stored constant density (kg/m^3) of the phase.
Definition: Phase.cpp:698
doublereal mean_X(const doublereal *const Q) const
Evaluate the mole-fraction-weighted mean of an array Q.
Definition: Phase.cpp:717
vector_fp m_speciesCharge
Vector of species charges. length m_kk.
Definition: Phase.h:954
size_t nSpecies() const
Returns the number of species in the phase.
Definition: Phase.h:273
size_t m_kk
Number of species in the phase.
Definition: Phase.h:943
doublereal nAtoms(size_t k, size_t m) const
Number of atoms of element m in species k.
Definition: Phase.cpp:154
const std::vector< std::string > & elementNames() const
Return a read-only reference to the vector of element names.
Definition: Phase.cpp:116
doublereal temperature() const
Temperature (K).
Definition: Phase.h:654
size_t nElements() const
Number of elements.
Definition: Phase.cpp:81
doublereal RT() const
Return the Gas Constant multiplied by the current temperature.
Definition: ThermoPhase.h:782
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:552
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:1802
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:1782
AnyMap m_input
Data supplied via setParameters.
Definition: ThermoPhase.h:1898
virtual void setParameters(int n, doublereal *const c)
Set the equation of state parameters.
virtual void setParametersFromXML(const XML_Node &eosdata)
Set equation of state parameter values from XML entries.
Definition: ThermoPhase.h:1747
virtual void getParameters(int &n, doublereal *const c) const
Get the equation of state parameters in a vector.
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:103
bool hasChild(const std::string &ch) const
Tests whether the current node has a child node with a particular name.
Definition: xml.cpp:529
XML_Node & child(const size_t n) const
Return a changeable reference to the n'th child of the current node.
Definition: xml.cpp:547
#define AssertTrace(expr)
Assertion must be true or an error is thrown.
Definition: ctexceptions.h:240
ThermoPhase * newPhase(XML_Node &phase)
Create a new ThermoPhase object and initializes it according to the XML tree.
void importPhase(XML_Node &phase, ThermoPhase *th)
Import a phase information into an empty ThermoPhase object.
Namespace for the Cantera kernel.
Definition: AnyMap.h:29
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:192
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:235
const double SmallNumber
smallest number to compare to zero.
Definition: ct_defs.h:153
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:184
const double GasConstant
Universal Gas Constant [J/kmol/K].
Definition: ct_defs.h:113
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.