32 m_incl_species.resize(m_nsp_mix,1);
33 m_incl_element.resize(m_nel_mix,1);
34 for (
size_t m = 0; m < m_nel_mix; m++) {
38 if (enm ==
"E" || enm ==
"e") {
46 m_incl_element[m] = 0;
47 for (
size_t k = 0; k < m_nsp_mix; k++) {
48 if (m_mix->
nAtoms(k,m) != 0.0) {
49 m_incl_species[k] = 0;
57 if (m_eloc < m_nel_mix) {
58 m_element.push_back(m_eloc);
62 for (
size_t m = 0; m < m_nel_mix; m++) {
63 if (m_incl_element[m] == 1 && m != m_eloc) {
65 m_element.push_back(m);
78 for (
size_t k = 0; k < m_nsp_mix; k++) {
82 m_incl_species[k] = 0;
86 +
" is excluded since its thermo properties are \n"
87 "not valid at this temperature, but it has "
88 "non-zero moles in the initial state.");
94 for (
size_t k = 0; k < m_nsp_mix; k++) {
95 if (m_incl_species[k] ==1) {
97 m_species.push_back(k);
102 m_work.resize(m_nsp);
103 m_work2.resize(m_nsp);
104 m_work3.resize(m_nsp_mix);
105 m_mu.resize(m_nsp_mix);
108 m_moles.resize(m_nsp);
109 m_lastmoles.resize(m_nsp);
110 m_dxi.resize(
nFree());
113 for (
size_t ik = 0; ik < m_nsp; ik++) {
118 m_deltaG_RT.resize(
nFree(), 0.0);
119 m_majorsp.resize(m_nsp);
120 m_sortindex.resize(m_nsp,0);
121 m_lastsort.resize(m_nel);
122 m_solnrxn.resize(
nFree());
123 m_A.
resize(m_nel, m_nsp, 0.0);
125 m_order.resize(std::max(m_nsp, m_nel), 0);
126 iota(m_order.begin(), m_order.begin() + m_nsp, 0);
144 for (
size_t k = 0; k < m_nsp; k++) {
145 m_moles[k] += m_work[k];
146 m_lastmoles[k] = m_moles[k];
148 m_dsoln.push_back(1);
150 m_dsoln.push_back(0);
161doublereal MultiPhaseEquil::equilibrate(
int XY, doublereal err,
162 int maxsteps,
int loglevel)
166 for (i = 0; i < maxsteps; i++) {
173 throw CanteraError(
"MultiPhaseEquil::equilibrate",
174 "no convergence in {} iterations. Error = {}",
181void MultiPhaseEquil::updateMixMoles()
183 fill(m_work3.begin(), m_work3.end(), 0.0);
184 for (
size_t k = 0; k < m_nsp; k++) {
185 m_work3[m_species[k]] = m_moles[k];
192 fill(m_work3.begin(), m_work3.end(), 0.0);
193 for (
size_t k = 0; k < m_nsp; k++) {
194 m_work3[m_species[k]] = (m_moles[k] > 0.0 ? m_moles[k] : 0.0);
201 double not_mu = 1.0e12;
203 double dxi_min = 1.0e10;
217 for (
size_t j = 0; j <
nFree(); j++) {
220 for (
size_t ik = 0; ik < m_nsp; ik++) {
221 dg_rt += mu(ik) * m_N(ik,j);
225 int idir = (dg_rt < 0.0 ? 1 : -1);
227 for (
size_t ik = 0; ik < m_nsp; ik++) {
228 double nu = m_N(ik, j);
236 double delta_xi = fabs(0.99*moles(ik)/nu);
239 if (!redo && delta_xi < 1.0e-10 && ik < m_nel) {
242 dxi_min = std::min(dxi_min, delta_xi);
246 for (
size_t ik = 0; ik < m_nsp; ik++) {
247 moles(ik) += m_N(ik, j) * idir*dxi_min;
260 if (order.size() != m_nsp) {
261 for (
size_t k = 0; k < m_nsp; k++) {
265 for (
size_t k = 0; k < m_nsp; k++) {
266 m_order[k] = order[k];
270 size_t nRows = m_nel;
271 size_t nColumns = m_nsp;
274 for (
size_t m = 0; m < nRows; m++) {
275 for (
size_t k = 0; k < nColumns; k++) {
276 m_A(m, k) = m_mix->
nAtoms(m_species[m_order[k]], m_element[m]);
281 for (
size_t m = 0; m < nRows; m++) {
283 bool isZeroRow =
true;
284 for (
size_t k = m; k < nColumns; k++) {
285 if (fabs(m_A(m,k)) > sqrt(
Tiny)) {
292 size_t n = nRows - 1;
293 bool foundSwapCandidate =
false;
295 for (
size_t k = m; k < nColumns; k++) {
296 if (fabs(m_A(n,k)) > sqrt(
Tiny)) {
297 foundSwapCandidate =
true;
301 if (foundSwapCandidate) {
307 for (
size_t k = 0; k < nColumns; k++) {
308 std::swap(m_A(n,k), m_A(m,k));
319 if (m < nColumns && m_A(m,m) == 0.0) {
326 doublereal maxmoles = -999.0;
328 for (
size_t k = m+1; k < nColumns; k++) {
329 if (m_A(m,k) != 0.0 && fabs(m_moles[m_order[k]]) > maxmoles) {
331 maxmoles = fabs(m_moles[m_order[k]]);
337 for (
size_t n = 0; n < nRows; n++) {
338 std::swap(m_A(n, m), m_A(n, kmax));
342 std::swap(m_order[m], m_order[kmax]);
346 double fctr = 1.0/m_A(m,m);
347 for (
size_t k = 0; k < nColumns; k++) {
353 for (
size_t n = m+1; n < m_nel; n++) {
354 fctr = m_A(n,m)/m_A(m,m);
355 for (
size_t k = 0; k < m_nsp; k++) {
356 m_A(n,k) -= m_A(m,k)*fctr;
363 for (
size_t m = std::min(nRows,nColumns)-1; m > 0; m--) {
364 for (
size_t n = m-1; n !=
npos; n--) {
365 if (m_A(n,m) != 0.0) {
366 double fctr = m_A(n,m);
367 for (
size_t k = m; k < m_nsp; k++) {
368 m_A(n,k) -= fctr*m_A(m,k);
375 for (
size_t n = 0; n < m_nsp; n++) {
377 for (
size_t k = 0; k <
nFree(); k++) {
378 m_N(n, k) = -m_A(n, k + m_nel);
381 for (
size_t k = 0; k <
nFree(); k++) {
384 m_N(n, n - m_nel) = 1.0;
389 for (
size_t j = 0; j <
nFree(); j++) {
390 m_solnrxn[j] =
false;
391 for (
size_t k = 0; k < m_nsp; k++) {
402 for (
size_t k = 0; k < m_nsp; k++) {
403 x[m_order[k]] = m_work2[k];
407void MultiPhaseEquil::step(doublereal omega,
vector_fp& deltaN,
411 throw CanteraError(
"MultiPhaseEquil::step",
"negative omega");
414 for (
size_t ik = 0; ik < m_nel; ik++) {
415 size_t k = m_order[ik];
416 m_lastmoles[k] = m_moles[k];
417 m_moles[k] += omega * deltaN[k];
420 for (
size_t ik = m_nel; ik < m_nsp; ik++) {
421 size_t k = m_order[ik];
422 m_lastmoles[k] = m_moles[k];
424 m_moles[k] += omega * deltaN[k];
426 m_moles[k] = fabs(m_moles[k])*std::min(10.0,
427 exp(-m_deltaG_RT[ik - m_nel]));
440 multiply(m_N, m_dxi.data(), m_work.data());
447 doublereal FCTR = 0.99;
448 const doublereal MAJOR_THRESHOLD = 1.0e-12;
449 double omegamax = 1.0;
450 for (
size_t ik = 0; ik < m_nsp; ik++) {
451 size_t k = m_order[ik];
454 if (m_moles[k] < MAJOR_THRESHOLD) {
463 if (m_dsoln[k] == 1) {
464 if ((m_moles[k] > MAJOR_THRESHOLD) || (ik < m_nel)) {
465 if (m_moles[k] < MAJOR_THRESHOLD) {
468 double omax = m_moles[k]*FCTR/(fabs(m_work[k]) +
Tiny);
469 if (m_work[k] < 0.0 && omax < omegamax) {
471 if (omegamax < 1.0e-5) {
477 m_majorsp[k] =
false;
480 if (m_work[k] < 0.0 && m_moles[k] > 0.0) {
481 double omax = -m_moles[k]/m_work[k];
482 if (omax < omegamax) {
484 if (omegamax < 1.0e-5) {
494 step(omegamax, m_work);
498 doublereal not_mu = 1.0e12;
500 doublereal grad1 = 0.0;
501 for (
size_t k = 0; k < m_nsp; k++) {
502 grad1 += m_work[k] * m_mu[m_species[k]];
505 double omega = omegamax;
507 omega *= fabs(grad0) / (grad1 + fabs(grad0));
508 for (
size_t k = 0; k < m_nsp; k++) {
509 m_moles[k] = m_lastmoles[k];
519 doublereal grad = 0.0;
522 doublereal not_mu = 1.0e12;
525 for (
size_t j = 0; j <
nFree(); j++) {
527 getStoichVector(j, nu);
530 doublereal dg_rt = 0.0;
531 for (
size_t k = 0; k < m_nsp; k++) {
532 dg_rt += m_mu[m_species[k]] * nu[k];
536 m_deltaG_RT[j] = dg_rt;
541 size_t k = m_order[j + m_nel];
543 if (m_moles[k] <= 0.0 && dg_rt > 0.0) {
548 }
else if (!m_solnrxn[j]) {
553 for (k = 0; k < m_nel; k++) {
554 size_t kc = m_order[k];
556 csum += pow(nu[kc], 2)*m_dsoln[kc]/nmoles;
560 size_t kc = m_order[j + m_nel];
562 double term1 = m_dsoln[kc]/nmoles;
565 doublereal sum = 0.0;
566 for (
size_t ip = 0; ip < m_mix->
nPhases(); ip++) {
570 for (k = 0; k < m_nsp; k++) {
573 psum += pow(nu[k], 2);
579 double rfctr = term1 + csum + sum;
580 if (fabs(rfctr) <
Tiny) {
583 fctr = 1.0/(term1 + csum + sum);
586 dxi[j] = -fctr*dg_rt;
588 for (
size_t m = 0; m < m_nel; m++) {
589 if (m_moles[m_order[m]] <= 0.0 && (m_N(m, j)*dxi[j] < 0.0)) {
593 grad += dxi[j]*dg_rt;
599void MultiPhaseEquil::computeN()
602 std::vector<std::pair<double, size_t> > moleFractions(m_nsp);
603 for (
size_t k = 0; k < m_nsp; k++) {
605 moleFractions[k].first = - m_mix->
speciesMoles(m_species[k]);
606 moleFractions[k].second = k;
608 std::sort(moleFractions.begin(), moleFractions.end());
609 for (
size_t k = 0; k < m_nsp; k++) {
610 m_sortindex[k] = moleFractions[k].second;
613 for (
size_t m = 0; m < m_nel; m++) {
615 for (
size_t ik = 0; ik < m_nsp; ik++) {
617 if (m_mix->
nAtoms(m_species[k],m_element[m]) != 0) {
622 for (
size_t ij = 0; ij < m_nel; ij++) {
623 if (k == m_order[ij]) {
627 if (!ok || m_force) {
635doublereal MultiPhaseEquil::error()
637 doublereal err, maxerr = 0.0;
640 for (
size_t j = 0; j <
nFree(); j++) {
641 size_t ik = j + m_nel;
645 if (!isStoichPhase(ik) && fabs(moles(ik)) <=
SmallNumber) {
651 if (isStoichPhase(ik) && moles(ik) <= 0.0 &&
652 m_deltaG_RT[j] >= 0.0) {
655 err = fabs(m_deltaG_RT[j]);
657 maxerr = std::max(maxerr, err);
662double MultiPhaseEquil::phaseMoles(
size_t iph)
const
667void MultiPhaseEquil::reportCSV(
const std::string& reportFile)
669 FILE* FP = fopen(reportFile.c_str(),
"w");
671 throw CanteraError(
"MultiPhaseEquil::reportCSV",
"Failure to open file");
683 for (
size_t iphase = 0; iphase < m_mix->
nPhases(); iphase++) {
685 ThermoPhase& tref = m_mix->
phase(iphase);
687 VolPM.resize(nSpecies, 0.0);
688 tref.getMoleFractions(&mf[istart]);
689 tref.getPartialMolarVolumes(VolPM.data());
691 double TMolesPhase = phaseMoles(iphase);
692 double VolPhaseVolumes = 0.0;
693 for (
size_t k = 0; k < nSpecies; k++) {
694 VolPhaseVolumes += VolPM[k] * mf[istart + k];
696 VolPhaseVolumes *= TMolesPhase;
697 vol += VolPhaseVolumes;
699 fprintf(FP,
"--------------------- VCS_MULTIPHASE_EQUIL FINAL REPORT"
700 " -----------------------------\n");
701 fprintf(FP,
"Temperature = %11.5g kelvin\n", m_mix->
temperature());
702 fprintf(FP,
"Pressure = %11.5g Pascal\n", m_mix->
pressure());
703 fprintf(FP,
"Total Volume = %11.5g m**3\n", vol);
705 for (
size_t iphase = 0; iphase < m_mix->
nPhases(); iphase++) {
707 ThermoPhase& tref = m_mix->
phase(iphase);
708 ThermoPhase* tp = &tref;
710 string phaseName = tref.name();
711 double TMolesPhase = phaseMoles(iphase);
712 size_t nSpecies = tref.nSpecies();
713 activity.resize(nSpecies, 0.0);
714 ac.resize(nSpecies, 0.0);
715 mu0.resize(nSpecies, 0.0);
716 mu.resize(nSpecies, 0.0);
717 VolPM.resize(nSpecies, 0.0);
718 molalities.resize(nSpecies, 0.0);
719 int actConvention = tp->activityConvention();
720 tp->getActivities(activity.data());
721 tp->getActivityCoefficients(ac.data());
722 tp->getStandardChemPotentials(mu0.data());
723 tp->getPartialMolarVolumes(VolPM.data());
724 tp->getChemPotentials(mu.data());
725 double VolPhaseVolumes = 0.0;
726 for (
size_t k = 0; k < nSpecies; k++) {
727 VolPhaseVolumes += VolPM[k] * mf[istart + k];
729 VolPhaseVolumes *= TMolesPhase;
730 vol += VolPhaseVolumes;
731 if (actConvention == 1) {
732 MolalityVPSSTP* mTP =
static_cast<MolalityVPSSTP*
>(tp);
733 mTP->getMolalities(molalities.data());
734 tp->getChemPotentials(mu.data());
737 fprintf(FP,
" Name, Phase, PhaseMoles, Mole_Fract, "
738 "Molalities, ActCoeff, Activity,"
739 "ChemPot_SS0, ChemPot, mole_num, PMVol, Phase_Volume\n");
741 fprintf(FP,
" , , (kmol), , "
743 " (kJ/gmol), (kJ/gmol), (kmol), (m**3/kmol), (m**3)\n");
745 for (
size_t k = 0; k < nSpecies; k++) {
746 string sName = tp->speciesName(k);
747 fprintf(FP,
"%12s, %11s, %11.3e, %11.3e, %11.3e, %11.3e, %11.3e,"
748 "%11.3e, %11.3e, %11.3e, %11.3e, %11.3e\n",
750 phaseName.c_str(), TMolesPhase,
751 mf[istart + k], molalities[k], ac[k], activity[k],
752 mu0[k]*1.0E-6, mu[k]*1.0E-6,
753 mf[istart + k] * TMolesPhase,
754 VolPM[k], VolPhaseVolumes);
758 fprintf(FP,
" Name, Phase, PhaseMoles, Mole_Fract, "
759 "Molalities, ActCoeff, Activity,"
760 " ChemPotSS0, ChemPot, mole_num, PMVol, Phase_Volume\n");
762 fprintf(FP,
" , , (kmol), , "
764 " (kJ/gmol), (kJ/gmol), (kmol), (m**3/kmol), (m**3)\n");
766 for (
size_t k = 0; k < nSpecies; k++) {
769 for (
size_t k = 0; k < nSpecies; k++) {
770 string sName = tp->speciesName(k);
771 fprintf(FP,
"%12s, %11s, %11.3e, %11.3e, %11.3e, %11.3e, %11.3e, "
772 "%11.3e, %11.3e,% 11.3e, %11.3e, %11.3e\n",
774 phaseName.c_str(), TMolesPhase,
775 mf[istart + k], molalities[k], ac[k],
776 activity[k], mu0[k]*1.0E-6, mu[k]*1.0E-6,
777 mf[istart + k] * TMolesPhase,
778 VolPM[k], VolPhaseVolumes);
Header for intermediate ThermoPhase object for phases which employ molality based activity coefficien...
vector_fp & data()
Return a reference to the data vector.
Base class for exceptions thrown by Cantera classes.
void resize(size_t n, size_t m, doublereal v=0.0)
Resize the matrix.
void getComponents(const std::vector< size_t > &order)
This method finds a set of component species and a complete set of formation reactions for the non-co...
doublereal computeReactionSteps(vector_fp &dxi)
Compute the change in extent of reaction for each reaction.
void unsort(vector_fp &x)
Re-arrange a vector of species properties in sorted form (components first) into unsorted,...
void finish()
Clean up the composition.
size_t nFree() const
Number of degrees of freedom.
doublereal stepComposition(int loglevel=0)
Take one step in composition, given the gradient of G at the starting point, and a vector of reaction...
MultiPhaseEquil(MultiPhase *mix, bool start=true, int loglevel=0)
Construct a multiphase equilibrium manager for a multiphase mixture.
int setInitialMoles(int loglevel=0)
Estimate the initial mole numbers.
A class for multiphase mixtures.
size_t speciesIndex(size_t k, size_t p) const
Return the global index of the species belonging to phase number p with local index k within the phas...
bool solutionSpecies(size_t kGlob) const
Return true is species kGlob is a species in a multicomponent solution phase.
void setMoles(const doublereal *n)
Sets all of the global species mole numbers.
size_t nSpecies() const
Number of species, summed over all phases.
doublereal pressure() const
Pressure [Pa].
doublereal speciesMoles(size_t kGlob) const
Returns the moles of global species k. units = kmol.
size_t nPhases() const
Number of phases.
doublereal phaseMoles(const size_t n) const
Return the number of moles in phase n.
std::string speciesName(const size_t kGlob) const
Name of species with global index kGlob.
bool tempOK(size_t p) const
Return true if the phase p has valid thermo data for the current temperature.
std::string elementName(size_t m) const
Returns the name of the global element m.
doublereal nAtoms(const size_t kGlob, const size_t mGlob) const
Returns the Number of atoms of global element mGlob in global species kGlob.
size_t speciesPhaseIndex(const size_t kGlob) const
Returns the phase index of the Kth "global" species.
doublereal elementMoles(size_t m) const
Total moles of global element m, summed over all phases.
void getValidChemPotentials(doublereal not_mu, doublereal *mu, bool standard=false) const
Returns a vector of Valid chemical potentials.
doublereal temperature() const
Temperature [K].
size_t nElements() const
Number of elements.
ThermoPhase & phase(size_t n)
Return a reference to phase n.
size_t nSpecies() const
Returns the number of species in the phase.
void getMoleFractions(double *const x) const
Get the species mole fraction vector.
Base class for a phase with thermodynamic properties.
Namespace for the Cantera kernel.
const size_t npos
index returned by functions to indicate "no position"
const double Tiny
Small number to compare differences of mole fractions against.
void multiply(const DenseMatrix &A, const double *const b, double *const prod)
Multiply A*b and return the result in prod. Uses BLAS routine DGEMV.
const double SmallNumber
smallest number to compare to zero.
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
const double GasConstant
Universal Gas Constant [J/kmol/K].
Contains declarations for string manipulation functions within Cantera.