/* * This routine is mostly concerned with changing the private data * to be consistent with that needed for solution. It is called for * every invocation of the vcs_solve() except for the cleanup invocation. * * Tasks: * 1) Initialization of arrays to zero. * 2) Calculate total number of moles in all phases * * return code * VCS_SUCCESS = everything went OK * VCS_PUB_BAD = There is an irreconcilable difference in the * public data structure from when the problem was * initially set up. */ int VCS_SOLVE::vcs_prep() { /* * Initialize various arrays in the data to zero */ vcs_vdzero(m_feSpecies_old, m_numSpeciesTot); vcs_vdzero(m_feSpecies_new, m_numSpeciesTot); vcs_vdzero(m_molNumSpecies_new, m_numSpeciesTot); vcs_dzero(&(m_deltaMolNumPhase[0][0]), m_numSpeciesTot * m_numPhases); vcs_izero(&(m_phaseParticipation[0][0]), m_numSpeciesTot * m_numPhases); vcs_dzero(VCS_DATA_PTR(m_deltaPhaseMoles), m_numPhases); vcs_dzero(VCS_DATA_PTR(m_tPhaseMoles_new), m_numPhases); /* * Calculate the total number of moles in all phases. */ vcs_tmoles(); return VCS_SUCCESS; }
void vcs_VolPhase::_updateActCoeff() const { if (m_isIdealSoln) { m_UpToDate_AC = true; return; } TP_ptr->getActivityCoefficients(VCS_DATA_PTR(ActCoeff)); m_UpToDate_AC = true; }
/* * Calculates the element abundance vectors from the mole * numbers */ void VCS_PROB::set_gai() { double* ElemAbund = VCS_DATA_PTR(gai); double* const* const fm = FormulaMatrix.baseDataAddr(); vcs_dzero(ElemAbund, ne); for (size_t j = 0; j < ne; j++) { for (size_t kspec = 0; kspec < nspecies; kspec++) { ElemAbund[j] += fm[j][kspec] * w[kspec]; } } }
/* * This function knows all of the element information with VCS_SOLVE, and * can therefore switch element positions * * @param ipos first global element index * @param jpos second global element index */ void VCS_SOLVE::vcs_switch_elem_pos(size_t ipos, size_t jpos) { if (ipos == jpos) return; size_t j; double dtmp; vcs_VolPhase *volPhase; #ifdef DEBUG_MODE if (ipos > (m_numElemConstraints - 1) || jpos > (m_numElemConstraints - 1)) { plogf("vcs_switch_elem_pos: ifunc = 0: inappropriate args: %d %d\n", ipos, jpos); plogendl(); exit(EXIT_FAILURE); } #endif /* * Change the element Global Index list in each vcs_VolPhase object * to reflect the switch in the element positions. */ for (size_t iph = 0; iph < m_numPhases; iph++) { volPhase = m_VolPhaseList[iph]; for (size_t e = 0; e < volPhase->nElemConstraints(); e++) { if (volPhase->elemGlobalIndex(e) == ipos) { volPhase->setElemGlobalIndex(e, jpos); } if (volPhase->elemGlobalIndex(e) == jpos) { volPhase->setElemGlobalIndex(e, ipos); } } } vcsUtil_dsw(VCS_DATA_PTR(m_elemAbundancesGoal), ipos, jpos); vcsUtil_dsw(VCS_DATA_PTR(m_elemAbundances), ipos, jpos); vcsUtil_ssw(VCS_DATA_PTR(m_elementMapIndex), ipos, jpos); vcsUtil_isw(VCS_DATA_PTR(m_elType), ipos, jpos); vcsUtil_isw(VCS_DATA_PTR(m_elementActive), ipos, jpos); for (j = 0; j < m_numSpeciesTot; ++j) { SWAP(m_formulaMatrix[ipos][j], m_formulaMatrix[jpos][j], dtmp); } vcsUtil_stsw(m_elementName, ipos, jpos); }
void VCS_PROB::set_gai() { gai.assign(gai.size(), 0.0); double* ElemAbund = VCS_DATA_PTR(gai); for (size_t j = 0; j < ne; j++) { for (size_t kspec = 0; kspec < nspecies; kspec++) { if (SpeciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { ElemAbund[j] += FormulaMatrix(kspec,j) * w[kspec]; } } } }
void vcs_VolPhase::setPtrThermoPhase(ThermoPhase* tp_ptr) { TP_ptr = tp_ptr; Temp_ = TP_ptr->temperature(); Pres_ = TP_ptr->pressure(); setState_TP(Temp_, Pres_); p_VCS_UnitsFormat = VCS_UNITS_MKS; m_phi = TP_ptr->electricPotential(); size_t nsp = TP_ptr->nSpecies(); size_t nelem = TP_ptr->nElements(); if (nsp != m_numSpecies) { if (m_numSpecies != 0) { plogf("Warning Nsp != NVolSpeces: %d %d \n", nsp, m_numSpecies); } resize(VP_ID_, nsp, nelem, PhaseName.c_str()); } TP_ptr->getMoleFractions(VCS_DATA_PTR(Xmol_)); creationMoleNumbers_ = Xmol_; _updateMoleFractionDependencies(); /* * figure out ideal solution tag */ if (nsp == 1) { m_isIdealSoln = true; } else { int eos = TP_ptr->eosType(); switch (eos) { case cIdealGas: case cIncompressible: case cSurf: case cMetal: case cStoichSubstance: case cSemiconductor: case cLatticeSolid: case cLattice: case cEdge: case cIdealSolidSolnPhase: m_isIdealSoln = true; break; default: m_isIdealSoln = false; }; } }
double vcs_VolPhase::_updateVolPM() const { TP_ptr->getPartialMolarVolumes(VCS_DATA_PTR(PartialMolarVol)); m_totalVol = 0.0; for (size_t k = 0; k < m_numSpecies; k++) { m_totalVol += PartialMolarVol[k] * Xmol_[k]; } m_totalVol *= v_totalMoles; if (m_totalMolesInert > 0.0) { if (m_gasPhase) { double volI = m_totalMolesInert * GasConstant * Temp_ / Pres_; m_totalVol += volI; } else { throw CanteraError("vcs_VolPhase::_updateVolPM", "unknown situation"); } } m_UpToDate_VolPM = true; return m_totalVol; }
void vcs_VolPhase::_updateLnActCoeffJac() { double phaseTotalMoles = v_totalMoles; if (phaseTotalMoles < 1.0E-14) { phaseTotalMoles = 1.0; } /* * Evaluate the current base activity coefficients if necessary */ if (!m_UpToDate_AC) { _updateActCoeff(); } if (!TP_ptr) { return; } TP_ptr->getdlnActCoeffdlnN(m_numSpecies, &np_dLnActCoeffdMolNumber(0,0)); for (size_t j = 0; j < m_numSpecies; j++) { double moles_j_base = phaseTotalMoles * Xmol_[j]; double* const np_lnActCoeffCol = np_dLnActCoeffdMolNumber.ptrColumn(j); if (moles_j_base < 1.0E-200) { moles_j_base = 1.0E-7 * moles_j_base + 1.0E-13 * phaseTotalMoles + 1.0E-150; } for (size_t k = 0; k < m_numSpecies; k++) { np_lnActCoeffCol[k] = np_lnActCoeffCol[k] * phaseTotalMoles / moles_j_base; } } double deltaMoles_j = 0.0; // Make copies of ActCoeff and Xmol_ for use in taking differences std::vector<double> ActCoeff_Base(ActCoeff); std::vector<double> Xmol_Base(Xmol_); double TMoles_base = phaseTotalMoles; /* * Loop over the columns species to be deltad */ for (size_t j = 0; j < m_numSpecies; j++) { /* * Calculate a value for the delta moles of species j * -> Note Xmol_[] and Tmoles are always positive or zero * quantities. */ double moles_j_base = phaseTotalMoles * Xmol_Base[j]; deltaMoles_j = 1.0E-7 * moles_j_base + 1.0E-13 * phaseTotalMoles + 1.0E-150; /* * Now, update the total moles in the phase and all of the * mole fractions based on this. */ phaseTotalMoles = TMoles_base + deltaMoles_j; for (size_t k = 0; k < m_numSpecies; k++) { Xmol_[k] = Xmol_Base[k] * TMoles_base / phaseTotalMoles; } Xmol_[j] = (moles_j_base + deltaMoles_j) / phaseTotalMoles; /* * Go get new values for the activity coefficients. * -> Note this calls setState_PX(); */ _updateMoleFractionDependencies(); _updateActCoeff(); /* * Revert to the base case Xmol_, v_totalMoles */ v_totalMoles = TMoles_base; Xmol_ = Xmol_Base; } /* * Go get base values for the activity coefficients. * -> Note this calls setState_TPX() again; * -> Just wanted to make sure that cantera is in sync * with VolPhase after this call. */ setMoleFractions(VCS_DATA_PTR(Xmol_Base)); _updateMoleFractionDependencies(); _updateActCoeff(); }
void vcs_VolPhase::_updateVolStar() const { TP_ptr->getStandardVolumes(VCS_DATA_PTR(StarMolarVol)); m_UpToDate_VolStar = true; }
void vcs_VolPhase::setMolesFromVCS(const int stateCalc, const double* molesSpeciesVCS) { v_totalMoles = m_totalMolesInert; if (molesSpeciesVCS == 0) { AssertThrowMsg(m_owningSolverObject, "vcs_VolPhase::setMolesFromVCS", "shouldn't be here"); if (stateCalc == VCS_STATECALC_OLD) { molesSpeciesVCS = VCS_DATA_PTR(m_owningSolverObject->m_molNumSpecies_old); } else if (stateCalc == VCS_STATECALC_NEW) { molesSpeciesVCS = VCS_DATA_PTR(m_owningSolverObject->m_molNumSpecies_new); } else if (DEBUG_MODE_ENABLED) { throw CanteraError("vcs_VolPhase::setMolesFromVCS", "shouldn't be here"); } } else if (DEBUG_MODE_ENABLED && m_owningSolverObject) { if (stateCalc == VCS_STATECALC_OLD) { if (molesSpeciesVCS != VCS_DATA_PTR(m_owningSolverObject->m_molNumSpecies_old)) { throw CanteraError("vcs_VolPhase::setMolesFromVCS", "shouldn't be here"); } } else if (stateCalc == VCS_STATECALC_NEW) { if (molesSpeciesVCS != VCS_DATA_PTR(m_owningSolverObject->m_molNumSpecies_new)) { throw CanteraError("vcs_VolPhase::setMolesFromVCS", "shouldn't be here"); } } } for (size_t k = 0; k < m_numSpecies; k++) { if (m_speciesUnknownType[k] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { size_t kglob = IndSpecies[k]; v_totalMoles += std::max(0.0, molesSpeciesVCS[kglob]); } } if (v_totalMoles > 0.0) { for (size_t k = 0; k < m_numSpecies; k++) { if (m_speciesUnknownType[k] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { size_t kglob = IndSpecies[k]; double tmp = std::max(0.0, molesSpeciesVCS[kglob]); Xmol_[k] = tmp / v_totalMoles; } } m_existence = VCS_PHASE_EXIST_YES; } else { // This is where we will start to store a better approximation // for the mole fractions, when the phase doesn't exist. // This is currently unimplemented. m_existence = VCS_PHASE_EXIST_NO; } /* * Update the electric potential if it is a solution variable * in the equation system */ if (m_phiVarIndex != npos) { size_t kglob = IndSpecies[m_phiVarIndex]; if (m_numSpecies == 1) { Xmol_[m_phiVarIndex] = 1.0; } else { Xmol_[m_phiVarIndex] = 0.0; } double phi = molesSpeciesVCS[kglob]; setElectricPotential(phi); if (m_numSpecies == 1) { m_existence = VCS_PHASE_EXIST_YES; } } _updateMoleFractionDependencies(); if (m_totalMolesInert > 0.0) { m_existence = VCS_PHASE_EXIST_ALWAYS; } /* * If stateCalc is old and the total moles is positive, * then we have a valid state. If the phase went away, it would * be a valid starting point for F_k's. So, save the state. */ if (stateCalc == VCS_STATECALC_OLD) { if (v_totalMoles > 0.0) { creationMoleNumbers_ = Xmol_; } } /* * Set flags indicating we are up to date with the VCS state vector. */ m_UpToDate = true; m_vcsStateStatus = stateCalc; }
void vcs_VolPhase::_updateGStar() const { TP_ptr->getStandardChemPotentials(VCS_DATA_PTR(StarChemicalPotential)); m_UpToDate_GStar = true; }
/* * Use the contents of the VCS_PROB to specify the contents of the * private data, VCS_SOLVE. * * @param pub Pointer to VCS_PROB that will be used to * initialize the current equilibrium problem */ int VCS_SOLVE::vcs_prob_specifyFully(const VCS_PROB* pub) { vcs_VolPhase* Vphase = 0; const char* ser = "vcs_pub_to_priv ERROR :ill defined interface -> bailout:\n\t"; /* * First Check to see whether we have room for the current problem * size */ size_t nspecies = pub->nspecies; if (NSPECIES0 < nspecies) { plogf("%sPrivate Data is dimensioned too small\n", ser); return VCS_PUB_BAD; } size_t nph = pub->NPhase; if (NPHASE0 < nph) { plogf("%sPrivate Data is dimensioned too small\n", ser); return VCS_PUB_BAD; } size_t nelements = pub->ne; if (m_numElemConstraints < nelements) { plogf("%sPrivate Data is dimensioned too small\n", ser); return VCS_PUB_BAD; } /* * OK, We have room. Now, transfer the integer numbers */ m_numElemConstraints = nelements; m_numSpeciesTot = nspecies; m_numSpeciesRdc = m_numSpeciesTot; /* * nc = number of components -> will be determined later. * but set it to its maximum possible value here. */ m_numComponents = nelements; /* * m_numRxnTot = number of noncomponents, also equal to the * number of reactions * Note, it's possible that the number of elements is greater than * the number of species. In that case set the number of reactions * to zero. */ if (nelements > nspecies) { m_numRxnTot = 0; } else { m_numRxnTot = nspecies - nelements; } m_numRxnRdc = m_numRxnTot; /* * number of minor species rxn -> all species rxn are major at the start. */ m_numRxnMinorZeroed = 0; /* * NPhase = number of phases */ m_numPhases = nph; #ifdef DEBUG_MODE m_debug_print_lvl = pub->vcs_debug_print_lvl; #else m_debug_print_lvl = std::min(2, pub->vcs_debug_print_lvl); #endif /* * FormulaMatrix[] -> Copy the formula matrix over */ for (size_t i = 0; i < nspecies; i++) { bool nonzero = false; for (size_t j = 0; j < nelements; j++) { if (pub->FormulaMatrix[j][i] != 0.0) { nonzero = true; } m_formulaMatrix[j][i] = pub->FormulaMatrix[j][i]; } if (!nonzero) { plogf("vcs_prob_specifyFully:: species %d %s has a zero formula matrix!\n", i, pub->SpName[i].c_str()); return VCS_PUB_BAD; } } /* * Copy over the species molecular weights */ vcs_vdcopy(m_wtSpecies, pub->WtSpecies, nspecies); /* * Copy over the charges */ vcs_vdcopy(m_chargeSpecies, pub->Charge, nspecies); /* * Malloc and Copy the VCS_SPECIES_THERMO structures * */ for (size_t kspec = 0; kspec < nspecies; kspec++) { if (m_speciesThermoList[kspec] != NULL) { delete m_speciesThermoList[kspec]; } VCS_SPECIES_THERMO* spf = pub->SpeciesThermo[kspec]; m_speciesThermoList[kspec] = spf->duplMyselfAsVCS_SPECIES_THERMO(); if (m_speciesThermoList[kspec] == NULL) { plogf(" duplMyselfAsVCS_SPECIES_THERMO returned an error!\n"); return VCS_PUB_BAD; } } /* * Copy the species unknown type */ vcs_icopy(VCS_DATA_PTR(m_speciesUnknownType), VCS_DATA_PTR(pub->SpeciesUnknownType), nspecies); /* * iest => Do we have an initial estimate of the species mole numbers ? */ m_doEstimateEquil = pub->iest; /* * w[] -> Copy the equilibrium mole number estimate if it exists. */ if (pub->w.size() != 0) { vcs_vdcopy(m_molNumSpecies_old, pub->w, nspecies); } else { m_doEstimateEquil = -1; vcs_dzero(VCS_DATA_PTR(m_molNumSpecies_old), nspecies); } /* * Formulate the Goal Element Abundance Vector */ if (pub->gai.size() != 0) { for (size_t i = 0; i < nelements; i++) { m_elemAbundancesGoal[i] = pub->gai[i]; if (pub->m_elType[i] == VCS_ELEM_TYPE_LATTICERATIO) { if (m_elemAbundancesGoal[i] < 1.0E-10) { m_elemAbundancesGoal[i] = 0.0; } } } } else { if (m_doEstimateEquil == 0) { double sum = 0; for (size_t j = 0; j < nelements; j++) { m_elemAbundancesGoal[j] = 0.0; for (size_t kspec = 0; kspec < nspecies; kspec++) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { sum += m_molNumSpecies_old[kspec]; m_elemAbundancesGoal[j] += m_formulaMatrix[j][kspec] * m_molNumSpecies_old[kspec]; } } if (pub->m_elType[j] == VCS_ELEM_TYPE_LATTICERATIO) { if (m_elemAbundancesGoal[j] < 1.0E-10 * sum) { m_elemAbundancesGoal[j] = 0.0; } } } } else { plogf("%sElement Abundances, m_elemAbundancesGoal[], not specified\n", ser); return VCS_PUB_BAD; } } /* * zero out values that will be filled in later */ /* * TPhMoles[] -> Untouched here. These will be filled in vcs_prep.c * TPhMoles1[] * DelTPhMoles[] * * * T, Pres, copy over here */ if (pub->T > 0.0) { m_temperature = pub->T; } else { m_temperature = 293.15; } if (pub->PresPA > 0.0) { m_pressurePA = pub->PresPA; } else { m_pressurePA = Cantera::OneAtm; } /* * TPhInertMoles[] -> must be copied over here */ for (size_t iph = 0; iph < nph; iph++) { Vphase = pub->VPhaseList[iph]; TPhInertMoles[iph] = Vphase->totalMolesInert(); } /* * if__ : Copy over the units for the chemical potential */ m_VCS_UnitsFormat = pub->m_VCS_UnitsFormat; /* * tolerance requirements -> copy them over here and later */ m_tolmaj = pub->tolmaj; m_tolmin = pub->tolmin; m_tolmaj2 = 0.01 * m_tolmaj; m_tolmin2 = 0.01 * m_tolmin; /* * m_speciesIndexVector[] is an index variable that keep track * of solution vector rotations. */ for (size_t i = 0; i < nspecies; i++) { m_speciesMapIndex[i] = i; } /* * IndEl[] is an index variable that keep track of element vector * rotations. */ for (size_t i = 0; i < nelements; i++) { m_elementMapIndex[i] = i; } /* * Define all species to be major species, initially. */ for (size_t i = 0; i < nspecies; i++) { // m_rxnStatus[i] = VCS_SPECIES_MAJOR; m_speciesStatus[i] = VCS_SPECIES_MAJOR; } /* * PhaseID: Fill in the species to phase mapping * -> Check for bad values at the same time. */ if (pub->PhaseID.size() != 0) { std::vector<size_t> numPhSp(nph, 0); for (size_t kspec = 0; kspec < nspecies; kspec++) { size_t iph = pub->PhaseID[kspec]; if (iph >= nph) { plogf("%sSpecies to Phase Mapping, PhaseID, has a bad value\n", ser); plogf("\tPhaseID[%d] = %d\n", kspec, iph); plogf("\tAllowed values: 0 to %d\n", nph - 1); return VCS_PUB_BAD; } m_phaseID[kspec] = pub->PhaseID[kspec]; m_speciesLocalPhaseIndex[kspec] = numPhSp[iph]; numPhSp[iph]++; } for (size_t iph = 0; iph < nph; iph++) { Vphase = pub->VPhaseList[iph]; if (numPhSp[iph] != Vphase->nSpecies()) { plogf("%sNumber of species in phase %d, %s, doesn't match\n", ser, iph, Vphase->PhaseName.c_str()); return VCS_PUB_BAD; } } } else { if (m_numPhases == 1) { for (size_t kspec = 0; kspec < nspecies; kspec++) { m_phaseID[kspec] = 0; m_speciesLocalPhaseIndex[kspec] = kspec; } } else { plogf("%sSpecies to Phase Mapping, PhaseID, is not defined\n", ser); return VCS_PUB_BAD; } } /* * Copy over the element types */ m_elType.resize(nelements, VCS_ELEM_TYPE_ABSPOS); m_elementActive.resize(nelements, 1); /* * Copy over the element names and types */ for (size_t i = 0; i < nelements; i++) { m_elementName[i] = pub->ElName[i]; m_elType[i] = pub->m_elType[i]; m_elementActive[i] = pub->ElActive[i]; if (!strncmp(m_elementName[i].c_str(), "cn_", 3)) { m_elType[i] = VCS_ELEM_TYPE_CHARGENEUTRALITY; if (pub->m_elType[i] != VCS_ELEM_TYPE_CHARGENEUTRALITY) { plogf("we have an inconsistency!\n"); exit(EXIT_FAILURE); } } } for (size_t i = 0; i < nelements; i++) { if (m_elType[i] == VCS_ELEM_TYPE_CHARGENEUTRALITY) { if (m_elemAbundancesGoal[i] != 0.0) { if (fabs(m_elemAbundancesGoal[i]) > 1.0E-9) { plogf("Charge neutrality condition %s is signicantly nonzero, %g. Giving up\n", m_elementName[i].c_str(), m_elemAbundancesGoal[i]); exit(EXIT_FAILURE); } else { if (m_debug_print_lvl >= 2) { plogf("Charge neutrality condition %s not zero, %g. Setting it zero\n", m_elementName[i].c_str(), m_elemAbundancesGoal[i]); } m_elemAbundancesGoal[i] = 0.0; } } } } /* * Copy over the species names */ for (size_t i = 0; i < nspecies; i++) { m_speciesName[i] = pub->SpName[i]; } /* * Copy over all of the phase information * Use the object's assignment operator */ for (size_t iph = 0; iph < nph; iph++) { *(m_VolPhaseList[iph]) = *(pub->VPhaseList[iph]); /* * Fix up the species thermo pointer in the vcs_SpeciesThermo object * It should point to the species thermo pointer in the private * data space. */ Vphase = m_VolPhaseList[iph]; for (size_t k = 0; k < Vphase->nSpecies(); k++) { vcs_SpeciesProperties* sProp = Vphase->speciesProperty(k); size_t kT = Vphase->spGlobalIndexVCS(k); sProp->SpeciesThermo = m_speciesThermoList[kT]; } } /* * Specify the Activity Convention information */ for (size_t iph = 0; iph < nph; iph++) { Vphase = m_VolPhaseList[iph]; m_phaseActConvention[iph] = Vphase->p_activityConvention; if (Vphase->p_activityConvention != 0) { /* * We assume here that species 0 is the solvent. * The solvent isn't on a unity activity basis * The activity for the solvent assumes that the * it goes to one as the species mole fraction goes to * one; i.e., it's really on a molarity framework. * So SpecLnMnaught[iSolvent] = 0.0, and the * loop below starts at 1, not 0. */ size_t iSolvent = Vphase->spGlobalIndexVCS(0); double mnaught = m_wtSpecies[iSolvent] / 1000.; for (size_t k = 1; k < Vphase->nSpecies(); k++) { size_t kspec = Vphase->spGlobalIndexVCS(k); m_actConventionSpecies[kspec] = Vphase->p_activityConvention; m_lnMnaughtSpecies[kspec] = log(mnaught); } } } /* * Copy the title info */ if (pub->Title.size() == 0) { m_title = "Unspecified Problem Title"; } else { m_title = pub->Title; } /* * Copy the volume info */ m_totalVol = pub->Vol; if (m_PMVolumeSpecies.size() != 0) { vcs_dcopy(VCS_DATA_PTR(m_PMVolumeSpecies), VCS_DATA_PTR(pub->VolPM), nspecies); } /* * Return the success flag */ return VCS_SUCCESS; }
/* * The VCS_PUB structure is returned to the user. * * @param pub Pointer to VCS_PROB that will get the results of the * equilibrium calculation transfered to it. */ int VCS_SOLVE::vcs_prob_update(VCS_PROB* pub) { size_t k1 = 0; vcs_tmoles(); m_totalVol = vcs_VolTotal(m_temperature, m_pressurePA, VCS_DATA_PTR(m_molNumSpecies_old), VCS_DATA_PTR(m_PMVolumeSpecies)); for (size_t i = 0; i < m_numSpeciesTot; ++i) { /* * Find the index of I in the index vector, m_speciesIndexVector[]. * Call it K1 and continue. */ for (size_t j = 0; j < m_numSpeciesTot; ++j) { k1 = j; if (m_speciesMapIndex[j] == i) { break; } } /* * - Switch the species data back from K1 into I */ if (pub->SpeciesUnknownType[i] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { pub->w[i] = m_molNumSpecies_old[k1]; } else { pub->w[i] = 0.0; // plogf("voltage species = %g\n", m_molNumSpecies_old[k1]); } //pub->mf[i] = m_molNumSpecies_new[k1]; pub->m_gibbsSpecies[i] = m_feSpecies_old[k1]; pub->VolPM[i] = m_PMVolumeSpecies[k1]; } pub->T = m_temperature; pub->PresPA = m_pressurePA; pub->Vol = m_totalVol; size_t kT = 0; for (size_t iph = 0; iph < pub->NPhase; iph++) { vcs_VolPhase* pubPhase = pub->VPhaseList[iph]; vcs_VolPhase* vPhase = m_VolPhaseList[iph]; pubPhase->setTotalMolesInert(vPhase->totalMolesInert()); pubPhase->setTotalMoles(vPhase->totalMoles()); pubPhase->setElectricPotential(vPhase->electricPotential()); double sumMoles = pubPhase->totalMolesInert(); pubPhase->setMoleFractionsState(vPhase->totalMoles(), VCS_DATA_PTR(vPhase->moleFractions()), VCS_STATECALC_TMP); const std::vector<double> & mfVector = pubPhase->moleFractions(); for (size_t k = 0; k < pubPhase->nSpecies(); k++) { kT = pubPhase->spGlobalIndexVCS(k); pub->mf[kT] = mfVector[k]; if (pubPhase->phiVarIndex() == k) { k1 = vPhase->spGlobalIndexVCS(k); double tmp = m_molNumSpecies_old[k1]; if (! vcs_doubleEqual(pubPhase->electricPotential() , tmp)) { plogf("We have an inconsistency in voltage, %g, %g\n", pubPhase->electricPotential(), tmp); exit(EXIT_FAILURE); } } if (! vcs_doubleEqual(pub->mf[kT], vPhase->molefraction(k))) { plogf("We have an inconsistency in mole fraction, %g, %g\n", pub->mf[kT], vPhase->molefraction(k)); exit(EXIT_FAILURE); } if (pubPhase->speciesUnknownType(k) != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { sumMoles += pub->w[kT]; } } if (! vcs_doubleEqual(sumMoles, vPhase->totalMoles())) { plogf("We have an inconsistency in total moles, %g %g\n", sumMoles, pubPhase->totalMoles()); exit(EXIT_FAILURE); } } pub->m_Iterations = m_VCount->Its; pub->m_NumBasisOptimizations = m_VCount->Basis_Opts; return VCS_SUCCESS; }
int VCS_SOLVE::vcs_elcorr(double aa[], double x[]) /************************************************************************** * * vcs_elcorr: * * This subroutine corrects for element abundances. At the end of the * surbroutine, the total moles in all phases are recalculated again, * because we have changed the number of moles in this routine. * * Input * -> temporary work vectors: * aa[ne*ne] * x[ne] * * Return Values: * 0 = Nothing of significance happened, * Element abundances were and still are good. * 1 = The solution changed significantly; * The element abundances are now good. * 2 = The solution changed significantly, * The element abundances are still bad. * 3 = The solution changed significantly, * The element abundances are still bad and a component * species got zeroed out. * * Internal data to be worked on:: * * ga Current element abundances * m_elemAbundancesGoal Required elemental abundances * m_molNumSpecies_old Current mole number of species. * m_formulaMatrix[][] Formular matrix of the species * ne Number of elements * nc Number of components. * * NOTES: * This routine is turning out to be very problematic. There are * lots of special cases and problems with zeroing out species. * * Still need to check out when we do loops over nc vs. ne. * *************************************************************************/ { int i, j, retn = 0, kspec, goodSpec, its; double xx, par, saveDir, dir; #ifdef DEBUG_MODE double l2before = 0.0, l2after = 0.0; std::vector<double> ga_save(m_numElemConstraints, 0.0); vcs_dcopy(VCS_DATA_PTR(ga_save), VCS_DATA_PTR(m_elemAbundances), m_numElemConstraints); if (m_debug_print_lvl >= 2) { plogf(" --- vcsc_elcorr: Element abundances correction routine"); if (m_numElemConstraints != m_numComponents) { plogf(" (m_numComponents != m_numElemConstraints)"); } plogf("\n"); } for (i = 0; i < m_numElemConstraints; ++i) { x[i] = m_elemAbundances[i] - m_elemAbundancesGoal[i]; } l2before = 0.0; for (i = 0; i < m_numElemConstraints; ++i) { l2before += x[i] * x[i]; } l2before = sqrt(l2before/m_numElemConstraints); #endif /* * Special section to take out single species, single component, * moles. These are species which have non-zero entries in the * formula matrix, and no other species have zero values either. * */ int numNonZero = 0; bool changed = false; bool multisign = false; for (i = 0; i < m_numElemConstraints; ++i) { numNonZero = 0; multisign = false; for (kspec = 0; kspec < m_numSpeciesTot; kspec++) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { double eval = m_formulaMatrix[i][kspec]; if (eval < 0.0) { multisign = true; } if (eval != 0.0) { numNonZero++; } } } if (!multisign) { if (numNonZero < 2) { for (kspec = 0; kspec < m_numSpeciesTot; kspec++) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { double eval = m_formulaMatrix[i][kspec]; if (eval > 0.0) { m_molNumSpecies_old[kspec] = m_elemAbundancesGoal[i] / eval; changed = true; } } } } else { int numCompNonZero = 0; int compID = -1; for (kspec = 0; kspec < m_numComponents; kspec++) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { double eval = m_formulaMatrix[i][kspec]; if (eval > 0.0) { compID = kspec; numCompNonZero++; } } } if (numCompNonZero == 1) { double diff = m_elemAbundancesGoal[i]; for (kspec = m_numComponents; kspec < m_numSpeciesTot; kspec++) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { double eval = m_formulaMatrix[i][kspec]; diff -= eval * m_molNumSpecies_old[kspec]; } m_molNumSpecies_old[compID] = MAX(0.0,diff/m_formulaMatrix[i][compID]); changed = true; } } } } } if (changed) { vcs_elab(); } /* * Section to check for maximum bounds errors on all species * due to elements. * This may only be tried on element types which are VCS_ELEM_TYPE_ABSPOS. * This is because no other species may have a negative number of these. * * Note, also we can do this over ne, the number of elements, not just * the number of components. */ changed = false; for (i = 0; i < m_numElemConstraints; ++i) { int elType = m_elType[i]; if (elType == VCS_ELEM_TYPE_ABSPOS) { for (kspec = 0; kspec < m_numSpeciesTot; kspec++) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { double atomComp = m_formulaMatrix[i][kspec]; if (atomComp > 0.0) { double maxPermissible = m_elemAbundancesGoal[i] / atomComp; if (m_molNumSpecies_old[kspec] > maxPermissible) { #ifdef DEBUG_MODE if (m_debug_print_lvl >= 3) { plogf(" --- vcs_elcorr: Reduced species %s from %g to %g " "due to %s max bounds constraint\n", m_speciesName[kspec].c_str(), m_molNumSpecies_old[kspec], maxPermissible, m_elementName[i].c_str()); } #endif m_molNumSpecies_old[kspec] = maxPermissible; changed = true; if (m_molNumSpecies_old[kspec] < VCS_DELETE_MINORSPECIES_CUTOFF) { m_molNumSpecies_old[kspec] = 0.0; if (m_SSPhase[kspec]) { m_speciesStatus[kspec] = VCS_SPECIES_ZEROEDSS; } else { m_speciesStatus[kspec] = VCS_SPECIES_ACTIVEBUTZERO; } #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { plogf(" --- vcs_elcorr: Zeroed species %s and changed " "status to %d due to max bounds constraint\n", m_speciesName[kspec].c_str(), m_speciesStatus[kspec]); } #endif } } } } } } } // Recalculate the element abundances if something has changed. if (changed) { vcs_elab(); } /* * Ok, do the general case. Linear algebra problem is * of length nc, not ne, as there may be degenerate rows when * nc .ne. ne. */ for (i = 0; i < m_numComponents; ++i) { x[i] = m_elemAbundances[i] - m_elemAbundancesGoal[i]; if (fabs(x[i]) > 1.0E-13) retn = 1; for (j = 0; j < m_numComponents; ++j) { aa[j + i*m_numElemConstraints] = m_formulaMatrix[j][i]; } } i = vcsUtil_mlequ(aa, m_numElemConstraints, m_numComponents, x, 1); if (i == 1) { plogf("vcs_elcorr ERROR: mlequ returned error condition\n"); return VCS_FAILED_CONVERGENCE; } /* * Now apply the new direction without creating negative species. */ par = 0.5; for (i = 0; i < m_numComponents; ++i) { if (m_molNumSpecies_old[i] > 0.0) { xx = -x[i] / m_molNumSpecies_old[i]; if (par < xx) par = xx; } } if (par > 100.0) { par = 100.0; } par = 1.0 / par; if (par < 1.0 && par > 0.0) { retn = 2; par *= 0.9999; for (i = 0; i < m_numComponents; ++i) { double tmp = m_molNumSpecies_old[i] + par * x[i]; if (tmp > 0.0) { m_molNumSpecies_old[i] = tmp; } else { if (m_SSPhase[i]) { m_molNumSpecies_old[i] = 0.0; } else { m_molNumSpecies_old[i] = m_molNumSpecies_old[i] * 0.0001; } } } } else { for (i = 0; i < m_numComponents; ++i) { double tmp = m_molNumSpecies_old[i] + x[i]; if (tmp > 0.0) { m_molNumSpecies_old[i] = tmp; } else { if (m_SSPhase[i]) { m_molNumSpecies_old[i] = 0.0; } else { m_molNumSpecies_old[i] = m_molNumSpecies_old[i] * 0.0001; } } } } /* * We have changed the element abundances. Calculate them again */ vcs_elab(); /* * We have changed the total moles in each phase. Calculate them again */ vcs_tmoles(); /* * Try some ad hoc procedures for fixing the problem */ if (retn >= 2) { /* * First find a species whose adjustment is a win-win * situation. */ for (kspec = 0; kspec < m_numSpeciesTot; kspec++) { if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { continue; } saveDir = 0.0; goodSpec = TRUE; for (i = 0; i < m_numComponents; ++i) { dir = m_formulaMatrix[i][kspec] * (m_elemAbundancesGoal[i] - m_elemAbundances[i]); if (fabs(dir) > 1.0E-10) { if (dir > 0.0) { if (saveDir < 0.0) { goodSpec = FALSE; break; } } else { if (saveDir > 0.0) { goodSpec = FALSE; break; } } saveDir = dir; } else { if (m_formulaMatrix[i][kspec] != 0.) { goodSpec = FALSE; break; } } } if (goodSpec) { its = 0; xx = 0.0; for (i = 0; i < m_numComponents; ++i) { if (m_formulaMatrix[i][kspec] != 0.0) { xx += (m_elemAbundancesGoal[i] - m_elemAbundances[i]) / m_formulaMatrix[i][kspec]; its++; } } if (its > 0) xx /= its; m_molNumSpecies_old[kspec] += xx; m_molNumSpecies_old[kspec] = MAX(m_molNumSpecies_old[kspec], 1.0E-10); /* * If we are dealing with a deleted species, then * we need to reinsert it into the active list. */ if (kspec >= m_numSpeciesRdc) { vcs_reinsert_deleted(kspec); m_molNumSpecies_old[m_numSpeciesRdc - 1] = xx; vcs_elab(); goto L_CLEANUP; } vcs_elab(); } } } if (vcs_elabcheck(0)) { retn = 1; goto L_CLEANUP; } for (i = 0; i < m_numElemConstraints; ++i) { if (m_elType[i] == VCS_ELEM_TYPE_CHARGENEUTRALITY || (m_elType[i] == VCS_ELEM_TYPE_ABSPOS && m_elemAbundancesGoal[i] == 0.0)) { for (kspec = 0; kspec < m_numSpeciesRdc; kspec++) { if (m_elemAbundances[i] > 0.0) { if (m_formulaMatrix[i][kspec] < 0.0) { m_molNumSpecies_old[kspec] -= m_elemAbundances[i] / m_formulaMatrix[i][kspec] ; if (m_molNumSpecies_old[kspec] < 0.0) { m_molNumSpecies_old[kspec] = 0.0; } vcs_elab(); break; } } if (m_elemAbundances[i] < 0.0) { if (m_formulaMatrix[i][kspec] > 0.0) { m_molNumSpecies_old[kspec] -= m_elemAbundances[i] / m_formulaMatrix[i][kspec]; if (m_molNumSpecies_old[kspec] < 0.0) { m_molNumSpecies_old[kspec] = 0.0; } vcs_elab(); break; } } } } } if (vcs_elabcheck(1)) { retn = 1; goto L_CLEANUP; } /* * For electron charges element types, we try positive deltas * in the species concentrations to match the desired * electron charge exactly. */ for (i = 0; i < m_numElemConstraints; ++i) { double dev = m_elemAbundancesGoal[i] - m_elemAbundances[i]; if (m_elType[i] == VCS_ELEM_TYPE_ELECTRONCHARGE && (fabs(dev) > 1.0E-300)) { bool useZeroed = true; for (kspec = 0; kspec < m_numSpeciesRdc; kspec++) { if (dev < 0.0) { if (m_formulaMatrix[i][kspec] < 0.0) { if (m_molNumSpecies_old[kspec] > 0.0) { useZeroed = false; } } } else { if (m_formulaMatrix[i][kspec] > 0.0) { if (m_molNumSpecies_old[kspec] > 0.0) { useZeroed = false; } } } } for (kspec = 0; kspec < m_numSpeciesRdc; kspec++) { if (m_molNumSpecies_old[kspec] > 0.0 || useZeroed) { if (dev < 0.0) { if (m_formulaMatrix[i][kspec] < 0.0) { double delta = dev / m_formulaMatrix[i][kspec] ; m_molNumSpecies_old[kspec] += delta; if (m_molNumSpecies_old[kspec] < 0.0) { m_molNumSpecies_old[kspec] = 0.0; } vcs_elab(); break; } } if (dev > 0.0) { if (m_formulaMatrix[i][kspec] > 0.0) { double delta = dev / m_formulaMatrix[i][kspec] ; m_molNumSpecies_old[kspec] += delta; if (m_molNumSpecies_old[kspec] < 0.0) { m_molNumSpecies_old[kspec] = 0.0; } vcs_elab(); break; } } } } } } if (vcs_elabcheck(1)) { retn = 1; goto L_CLEANUP; } L_CLEANUP: ; vcs_tmoles(); #ifdef DEBUG_MODE l2after = 0.0; for (i = 0; i < m_numElemConstraints; ++i) { l2after += SQUARE(m_elemAbundances[i] - m_elemAbundancesGoal[i]); } l2after = sqrt(l2after/m_numElemConstraints); if (m_debug_print_lvl >= 2) { plogf(" --- Elem_Abund: Correct Initial " " Final\n"); for (i = 0; i < m_numElemConstraints; ++i) { plogf(" --- "); plogf("%-2.2s", m_elementName[i].c_str()); plogf(" %20.12E %20.12E %20.12E\n", m_elemAbundancesGoal[i], ga_save[i], m_elemAbundances[i]); } plogf(" --- Diff_Norm: %20.12E %20.12E\n", l2before, l2after); } #endif return retn; }
/************************************************************************** * * vcs_report: * * Print out a report on the state of the equilibrium problem to * standard output. * This prints out the current contents of the VCS_SOLVE class, V. * The "old" solution vector is printed out. ***************************************************************************/ int VCS_SOLVE::vcs_report(int iconv) { bool printActualMoles = true, inertYes = false; size_t i, j, l, k, kspec; size_t nspecies = m_numSpeciesTot; double g; char originalUnitsState = m_unitsState; std::vector<size_t> sortindex(nspecies,0); std::vector<double> xy(nspecies,0.0); /* ************************************************************** */ /* **** SORT DEPENDENT SPECIES IN DECREASING ORDER OF MOLES ***** */ /* ************************************************************** */ for (i = 0; i < nspecies; ++i) { sortindex[i] = i; xy[i] = m_molNumSpecies_old[i]; } /* * Sort the XY vector, the mole fraction vector, * and the sort index vector, sortindex, according to * the magnitude of the mole fraction vector. */ for (l = m_numComponents; l < m_numSpeciesRdc; ++l) { k = vcs_optMax(VCS_DATA_PTR(xy), 0, l, m_numSpeciesRdc); if (k != l) { vcsUtil_dsw(VCS_DATA_PTR(xy), k, l); vcsUtil_ssw(VCS_DATA_PTR(sortindex), k, l); } } /* * Decide whether we have to nondimensionalize the equations. * -> For the printouts from this routine, we will use nondimensional * representations. This may be expanded in the future. */ if (m_unitsState == VCS_DIMENSIONAL_G) { vcs_nondim_TP(); } double molScale = 1.0; if (printActualMoles) { molScale = m_totalMoleScale; } vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD); vcs_dfe(VCS_STATECALC_OLD, 0, 0, m_numSpeciesTot); /* ******************************************************** */ /* *** PRINT OUT RESULTS ********************************** */ /* ******************************************************** */ plogf("\n\n\n\n"); print_line("-", 80); print_line("-", 80); plogf("\t\t VCS_TP REPORT\n"); print_line("-", 80); print_line("-", 80); if (iconv < 0) { plogf(" ERROR: CONVERGENCE CRITERION NOT SATISFIED.\n"); } else if (iconv == 1) { plogf(" RANGE SPACE ERROR: Equilibrium Found but not all Element Abundances are Satisfied\n"); } /* * Calculate some quantities that may need updating */ vcs_tmoles(); m_totalVol = vcs_VolTotal(m_temperature, m_pressurePA, VCS_DATA_PTR(m_molNumSpecies_old), VCS_DATA_PTR(m_PMVolumeSpecies)); plogf("\t\tTemperature = %15.2g Kelvin\n", m_temperature); plogf("\t\tPressure = %15.5g Pa \n", m_pressurePA); plogf("\t\ttotal Volume = %15.5g m**3\n", m_totalVol * molScale); if (!printActualMoles) { plogf("\t\tMole Scale = %15.5g kmol (all mole numbers and volumes are scaled by this value)\n", molScale); } /* * -------- TABLE OF SPECIES IN DECREASING MOLE NUMBERS -------------- */ plogf("\n\n"); print_line("-", 80); plogf(" Species Equilibrium kmoles "); plogf("Mole Fraction ChemPot/RT SpecUnkType\n"); print_line("-", 80); for (i = 0; i < m_numComponents; ++i) { plogf(" %-12.12s", m_speciesName[i].c_str()); print_space(13); plogf("%14.7E %14.7E %12.4E", m_molNumSpecies_old[i] * molScale, m_molNumSpecies_new[i] * molScale, m_feSpecies_old[i]); plogf(" %3d", m_speciesUnknownType[i]); plogf("\n"); } for (i = m_numComponents; i < m_numSpeciesRdc; ++i) { l = sortindex[i]; plogf(" %-12.12s", m_speciesName[l].c_str()); print_space(13); if (m_speciesUnknownType[l] == VCS_SPECIES_TYPE_MOLNUM) { plogf("%14.7E %14.7E %12.4E", m_molNumSpecies_old[l] * molScale, m_molNumSpecies_new[l] * molScale, m_feSpecies_old[l]); plogf(" KMolNum "); } else if (m_speciesUnknownType[l] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf(" NA %14.7E %12.4E", 1.0, m_feSpecies_old[l]); plogf(" Voltage = %14.7E", m_molNumSpecies_old[l] * molScale); } else { plogf("we have a problem\n"); exit(EXIT_FAILURE); } plogf("\n"); } for (i = 0; i < m_numPhases; i++) { if (TPhInertMoles[i] > 0.0) { inertYes = true; if (i == 0) { plogf(" Inert Gas Species "); } else { plogf(" Inert Species in phase %16s ", (m_VolPhaseList[i])->PhaseName.c_str()); } plogf("%14.7E %14.7E %12.4E\n", TPhInertMoles[i] * molScale, TPhInertMoles[i] / m_tPhaseMoles_old[i], 0.0); } } if (m_numSpeciesRdc != nspecies) { plogf("\n SPECIES WITH LESS THAN 1.0E-32 KMOLES:\n\n"); for (kspec = m_numSpeciesRdc; kspec < nspecies; ++kspec) { plogf(" %-12.12s", m_speciesName[kspec].c_str()); // Note m_deltaGRxn_new[] stores in kspec slot not irxn slot, after solve plogf(" %14.7E %14.7E %12.4E", m_molNumSpecies_old[kspec]*molScale, m_molNumSpecies_new[kspec]*molScale, m_deltaGRxn_new[kspec]); if (m_speciesUnknownType[i] == VCS_SPECIES_TYPE_MOLNUM) { plogf(" KMol_Num"); } else if (m_speciesUnknownType[i] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf(" Voltage"); } else { plogf(" Unknown"); } plogf("\n"); } } print_line("-", 80); plogf("\n"); /* * ---------- TABLE OF SPECIES FORMATION REACTIONS ------------------ */ plogf("\n"); print_line("-", m_numComponents*10 + 45); plogf(" |ComponentID|"); for (j = 0; j < m_numComponents; j++) { plogf(" %3d", j); } plogf(" | |\n"); plogf(" | Components|"); for (j = 0; j < m_numComponents; j++) { plogf(" %10.10s", m_speciesName[j].c_str()); } plogf(" | |\n"); plogf(" NonComponent | Moles |"); for (j = 0; j < m_numComponents; j++) { plogf(" %10.3g", m_molNumSpecies_old[j] * molScale); } plogf(" | DG/RT Rxn |\n"); print_line("-", m_numComponents*10 + 45); for (size_t irxn = 0; irxn < m_numRxnTot; irxn++) { size_t kspec = m_indexRxnToSpecies[irxn]; plogf(" %3d ", kspec); plogf("%-10.10s", m_speciesName[kspec].c_str()); plogf("|%10.3g |", m_molNumSpecies_old[kspec]*molScale); for (j = 0; j < m_numComponents; j++) { plogf(" %6.2f", m_stoichCoeffRxnMatrix[irxn][j]); } plogf(" |%10.3g |", m_deltaGRxn_new[irxn]); plogf("\n"); } print_line("-", m_numComponents*10 + 45); plogf("\n"); /* * ------------------ TABLE OF PHASE INFORMATION --------------------- */ std::vector<double> gaPhase(m_numElemConstraints, 0.0); std::vector<double> gaTPhase(m_numElemConstraints, 0.0); double totalMoles = 0.0; double gibbsPhase = 0.0; double gibbsTotal = 0.0; plogf("\n\n"); plogf("\n"); print_line("-", m_numElemConstraints*10 + 58); plogf(" | ElementID |"); for (j = 0; j < m_numElemConstraints; j++) { plogf(" %3d", j); } plogf(" | |\n"); plogf(" | Element |"); for (j = 0; j < m_numElemConstraints; j++) { plogf(" %10.10s", (m_elementName[j]).c_str()); } plogf(" | |\n"); plogf(" PhaseName |KMolTarget |"); for (j = 0; j < m_numElemConstraints; j++) { plogf(" %10.3g", m_elemAbundancesGoal[j]); } plogf(" | Gibbs Total |\n"); print_line("-", m_numElemConstraints*10 + 58); for (size_t iphase = 0; iphase < m_numPhases; iphase++) { plogf(" %3d ", iphase); vcs_VolPhase *VPhase = m_VolPhaseList[iphase]; plogf("%-12.12s |",VPhase->PhaseName.c_str()); plogf("%10.3e |", m_tPhaseMoles_old[iphase]*molScale); totalMoles += m_tPhaseMoles_old[iphase]; if (m_tPhaseMoles_old[iphase] != VPhase->totalMoles()) { if (! vcs_doubleEqual(m_tPhaseMoles_old[iphase], VPhase->totalMoles())) { plogf("We have a problem\n"); exit(EXIT_FAILURE); } } vcs_elabPhase(iphase, VCS_DATA_PTR(gaPhase)); for (j = 0; j < m_numElemConstraints; j++) { plogf(" %10.3g", gaPhase[j]); gaTPhase[j] += gaPhase[j]; } gibbsPhase = vcs_GibbsPhase(iphase, VCS_DATA_PTR(m_molNumSpecies_old), VCS_DATA_PTR(m_feSpecies_old)); gibbsTotal += gibbsPhase; plogf(" | %18.11E |\n", gibbsPhase); } print_line("-", m_numElemConstraints*10 + 58); plogf(" TOTAL |%10.3e |", totalMoles); for (j = 0; j < m_numElemConstraints; j++) { plogf(" %10.3g", gaTPhase[j]); } plogf(" | %18.11E |\n", gibbsTotal); print_line("-", m_numElemConstraints*10 + 58); plogf("\n"); /* * ----------- GLOBAL SATISFACTION INFORMATION ----------------------- */ /* * Calculate the total dimensionless Gibbs Free Energy * -> Inert species are handled as if they had a standard free * energy of zero */ g = vcs_Total_Gibbs(VCS_DATA_PTR(m_molNumSpecies_old), VCS_DATA_PTR(m_feSpecies_old), VCS_DATA_PTR(m_tPhaseMoles_old)); plogf("\n\tTotal Dimensionless Gibbs Free Energy = G/RT = %15.7E\n", g); if (inertYes) plogf("\t\t(Inert species have standard free energy of zero)\n"); plogf("\nElemental Abundances (kmol): "); plogf(" Actual Target Type ElActive\n"); for (i = 0; i < m_numElemConstraints; ++i) { print_space(26); plogf("%-2.2s", (m_elementName[i]).c_str()); plogf("%20.12E %20.12E", m_elemAbundances[i]*molScale, m_elemAbundancesGoal[i]*molScale); plogf(" %3d %3d\n", m_elType[i], m_elementActive[i]); } plogf("\n"); /* * ------------------ TABLE OF SPECIES CHEM POTS --------------------- */ plogf("\n"); print_line("-", 93); plogf("Chemical Potentials of the Species: (dimensionless)\n"); double rt = vcs_nondimMult_TP(m_VCS_UnitsFormat, m_temperature); plogf("\t\t(RT = %g ", rt); vcs_printChemPotUnits(m_VCS_UnitsFormat); plogf(")\n"); plogf(" Name TKMoles StandStateChemPot " " ln(AC) ln(X_i) | F z_i phi | ChemPot | (-lnMnaught)"); plogf("| (MolNum ChemPot)|"); plogf("\n"); print_line("-", 147); for (i = 0; i < nspecies; ++i) { l = sortindex[i]; size_t pid = m_phaseID[l]; plogf(" %-12.12s", m_speciesName[l].c_str()); plogf(" %14.7E ", m_molNumSpecies_old[l]*molScale); plogf("%14.7E ", m_SSfeSpecies[l]); plogf("%14.7E ", log(m_actCoeffSpecies_old[l])); double tpmoles = m_tPhaseMoles_old[pid]; double phi = m_phasePhi[pid]; double eContrib = phi * m_chargeSpecies[l] * m_Faraday_dim; double lx = 0.0; if (m_speciesUnknownType[l] == VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { lx = 0.0; } else { if (tpmoles > 0.0 && m_molNumSpecies_old[l] > 0.0) { double tmp = MAX(VCS_DELETE_MINORSPECIES_CUTOFF, m_molNumSpecies_old[l]); lx = log(tmp) - log(tpmoles); } else { lx = m_feSpecies_old[l] - m_SSfeSpecies[l] - log(m_actCoeffSpecies_old[l]) + m_lnMnaughtSpecies[l]; } } plogf("%14.7E |", lx); plogf("%14.7E | ", eContrib); double tmp = m_SSfeSpecies[l] + log(m_actCoeffSpecies_old[l]) + lx - m_lnMnaughtSpecies[l] + eContrib; if (fabs(m_feSpecies_old[l] - tmp) > 1.0E-7) { plogf("\n\t\twe have a problem - doesn't add up\n"); exit(EXIT_FAILURE); } plogf(" %12.4E |", m_feSpecies_old[l]); if( m_lnMnaughtSpecies[l] != 0.0) { plogf("(%11.5E)", - m_lnMnaughtSpecies[l]); } else { plogf(" "); } plogf("| %20.9E |", m_feSpecies_old[l] * m_molNumSpecies_old[l] * molScale); plogf("\n"); } for (i = 0; i < 125; i++) plogf(" "); plogf(" %20.9E\n", g); print_line("-", 147); /* * ------------- TABLE OF SOLUTION COUNTERS -------------------------- */ plogf("\n"); plogf("\nCounters: Iterations Time (seconds)\n"); if (m_timing_print_lvl > 0) { plogf(" vcs_basopt: %5d %11.5E\n", m_VCount->Basis_Opts, m_VCount->Time_basopt); plogf(" vcs_TP: %5d %11.5E\n", m_VCount->Its, m_VCount->Time_vcs_TP); } else { plogf(" vcs_basopt: %5d %11s\n", m_VCount->Basis_Opts," NA "); plogf(" vcs_TP: %5d %11s\n", m_VCount->Its," NA " ); } print_line("-", 80); print_line("-", 80); /* * Set the Units state of the system back to where it was when we * entered the program. */ if (originalUnitsState != m_unitsState) { if (originalUnitsState == VCS_DIMENSIONAL_G ) vcs_redim_TP(); else vcs_nondim_TP(); } /* * Return a successful completion flag */ return VCS_SUCCESS; } /* vcs_report() ************************************************************/
int VCS_SOLVE::vcs_inest_TP() { int retn = 0; Cantera::clockWC tickTock; if (m_doEstimateEquil > 0) { /* * Calculate the elemental abundances */ vcs_elab(); if (vcs_elabcheck(0)) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf("%s Initial guess passed element abundances on input\n", pprefix); plogf("%s m_doEstimateEquil = 1 so will use the input mole " "numbers as estimates", pprefix); plogendl(); } return retn; } else if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf("%s Initial guess failed element abundances on input\n", pprefix); plogf("%s m_doEstimateEquil = 1 so will discard input " "mole numbers and find our own estimate", pprefix); plogendl(); } } /* * Malloc temporary space for usage in this routine and in * subroutines * sm[ne*ne] * ss[ne] * sa[ne] * aw[m] */ std::vector<double> sm(m_numElemConstraints*m_numElemConstraints, 0.0); std::vector<double> ss(m_numElemConstraints, 0.0); std::vector<double> sa(m_numElemConstraints, 0.0); std::vector<double> aw(m_numSpeciesTot+ m_numElemConstraints, 0.0); /* * Go get the estimate of the solution */ if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf("%sGo find an initial estimate for the equilibrium problem", pprefix); plogendl(); } double test = -1.0E20; vcs_inest(VCS_DATA_PTR(aw), VCS_DATA_PTR(sa), VCS_DATA_PTR(sm), VCS_DATA_PTR(ss), test); /* * Calculate the elemental abundances */ vcs_elab(); /* * If we still fail to achieve the correct elemental abundances, * try to fix the problem again by calling the main elemental abundances * fixer routine, used in the main program. This * attempts to tweak the mole numbers of the component species to * satisfy the element abundance constraints. * * Note: We won't do this unless we have to since it involves inverting * a matrix. */ bool rangeCheck = vcs_elabcheck(1); if (!vcs_elabcheck(0)) { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf("%sInitial guess failed element abundances\n", pprefix); plogf("%sCall vcs_elcorr to attempt fix", pprefix); plogendl(); } vcs_elcorr(VCS_DATA_PTR(sm), VCS_DATA_PTR(aw)); rangeCheck = vcs_elabcheck(1); if (!vcs_elabcheck(0)) { plogf("%sInitial guess still fails element abundance equations\n", pprefix); plogf("%s - Inability to ever satisfy element abundance " "constraints is probable", pprefix); plogendl(); retn = -1; } else { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { if (rangeCheck) { plogf("%sInitial guess now satisfies element abundances", pprefix); plogendl(); } else { plogf("%sElement Abundances RANGE ERROR\n", pprefix); plogf("%s - Initial guess satisfies NC=%d element abundances, " "BUT not NE=%d element abundances", pprefix, m_numComponents, m_numElemConstraints); plogendl(); } } } } else { if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { if (rangeCheck) { plogf("%sInitial guess satisfies element abundances", pprefix); plogendl(); } else { plogf("%sElement Abundances RANGE ERROR\n", pprefix); plogf("%s - Initial guess satisfies NC=%d element abundances, " "BUT not NE=%d element abundances", pprefix, m_numComponents, m_numElemConstraints); plogendl(); } } } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf("%sTotal Dimensionless Gibbs Free Energy = %15.7E", pprefix, vcs_Total_Gibbs(VCS_DATA_PTR(m_molNumSpecies_old), VCS_DATA_PTR(m_feSpecies_new), VCS_DATA_PTR(m_tPhaseMoles_old))); plogendl(); } /* * Record time */ m_VCount->T_Time_inest += tickTock.secondsWC(); (m_VCount->T_Calls_Inest)++; return retn; }
void VCS_SOLVE::vcs_inest(double* const aw, double* const sa, double* const sm, double* const ss, double test) { size_t nspecies = m_numSpeciesTot; size_t nrxn = m_numRxnTot; /* * CALL ROUTINE TO SOLVE MAX(CC*molNum) SUCH THAT AX*molNum = BB * AND molNum(I) .GE. 0.0 * * Note, both of these programs do this. */ vcs_setMolesLinProg(); if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf("%s Mole Numbers returned from linear programming (vcs_inest initial guess):\n", pprefix); plogf("%s SPECIES MOLE_NUMBER -SS_ChemPotential\n", pprefix); for (size_t kspec = 0; kspec < nspecies; ++kspec) { plogf("%s ", pprefix); plogf("%-12.12s", m_speciesName[kspec].c_str()); plogf(" %15.5g %12.3g\n", m_molNumSpecies_old[kspec], -m_SSfeSpecies[kspec]); } plogf("%s Element Abundance Agreement returned from linear " "programming (vcs_inest initial guess):", pprefix); plogendl(); plogf("%s Element Goal Actual\n", pprefix); for (size_t j = 0; j < m_numElemConstraints; j++) { if (m_elementActive[j]) { double tmp = 0.0; for (size_t kspec = 0; kspec < nspecies; ++kspec) { tmp += m_formulaMatrix(kspec,j) * m_molNumSpecies_old[kspec]; } plogf("%s ", pprefix); plogf(" %-9.9s", (m_elementName[j]).c_str()); plogf(" %12.3g %12.3g\n", m_elemAbundancesGoal[j], tmp); } } plogendl(); } /* * Make sure all species have positive definite mole numbers * Set voltages to zero for now, until we figure out what to do */ m_deltaMolNumSpecies.assign(m_deltaMolNumSpecies.size(), 0.0); for (size_t kspec = 0; kspec < nspecies; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (m_molNumSpecies_old[kspec] <= 0.0) { /* * HKM Should eventually include logic here for non SS phases */ if (!m_SSPhase[kspec]) { m_molNumSpecies_old[kspec] = 1.0e-30; } } } else { m_molNumSpecies_old[kspec] = 0.0; } } /* * Now find the optimized basis that spans the stoichiometric * coefficient matrix */ bool conv; (void) vcs_basopt(false, aw, sa, sm, ss, test, &conv); /* ***************************************************************** */ /* **** CALCULATE TOTAL MOLES, ****************** */ /* **** CHEMICAL POTENTIALS OF BASIS ****************** */ /* ***************************************************************** */ /* * Calculate TMoles and m_tPhaseMoles_old[] */ vcs_tmoles(); /* * m_tPhaseMoles_new[] will consist of just the component moles */ for (size_t iph = 0; iph < m_numPhases; iph++) { m_tPhaseMoles_new[iph] = TPhInertMoles[iph] + 1.0E-20; } for (size_t kspec = 0; kspec < m_numComponents; ++kspec) { if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { m_tPhaseMoles_new[m_phaseID[kspec]] += m_molNumSpecies_old[kspec]; } } double TMolesMultiphase = 0.0; for (size_t iph = 0; iph < m_numPhases; iph++) { if (! m_VolPhaseList[iph]->m_singleSpecies) { TMolesMultiphase += m_tPhaseMoles_new[iph]; } } m_molNumSpecies_new = m_molNumSpecies_old; for (size_t kspec = 0; kspec < m_numComponents; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_MOLNUM) { m_molNumSpecies_new[kspec] = 0.0; } } m_feSpecies_new = m_SSfeSpecies; for (size_t kspec = 0; kspec < m_numComponents; ++kspec) { if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { if (! m_SSPhase[kspec]) { size_t iph = m_phaseID[kspec]; m_feSpecies_new[kspec] += log(m_molNumSpecies_new[kspec] / m_tPhaseMoles_old[iph]); } } else { m_molNumSpecies_new[kspec] = 0.0; } } vcs_deltag(0, true, VCS_STATECALC_NEW); if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { for (size_t kspec = 0; kspec < nspecies; ++kspec) { plogf("%s", pprefix); plogf("%-12.12s", m_speciesName[kspec].c_str()); if (kspec < m_numComponents) plogf("fe* = %15.5g ff = %15.5g\n", m_feSpecies_new[kspec], m_SSfeSpecies[kspec]); else plogf("fe* = %15.5g ff = %15.5g dg* = %15.5g\n", m_feSpecies_new[kspec], m_SSfeSpecies[kspec], m_deltaGRxn_new[kspec-m_numComponents]); } } /* ********************************************************** */ /* **** ESTIMATE REACTION ADJUSTMENTS *********************** */ /* ********************************************************** */ double* xtphMax = VCS_DATA_PTR(m_TmpPhase); double* xtphMin = VCS_DATA_PTR(m_TmpPhase2); m_deltaPhaseMoles.assign(m_deltaPhaseMoles.size(), 0.0); for (size_t iph = 0; iph < m_numPhases; iph++) { xtphMax[iph] = log(m_tPhaseMoles_new[iph] * 1.0E32); xtphMin[iph] = log(m_tPhaseMoles_new[iph] * 1.0E-32); } for (size_t irxn = 0; irxn < nrxn; ++irxn) { size_t kspec = m_indexRxnToSpecies[irxn]; /* * For single species phases, we will not estimate the * mole numbers. If the phase exists, it stays. If it * doesn't exist in the estimate, it doesn't come into * existence here. */ if (! m_SSPhase[kspec]) { size_t iph = m_phaseID[kspec]; if (m_deltaGRxn_new[irxn] > xtphMax[iph]) { m_deltaGRxn_new[irxn] = 0.8 * xtphMax[iph]; } if (m_deltaGRxn_new[irxn] < xtphMin[iph]) { m_deltaGRxn_new[irxn] = 0.8 * xtphMin[iph]; } /* * HKM -> The TMolesMultiphase is a change of mine. * It more evenly distributes the initial moles amongst * multiple multispecies phases according to the * relative values of the standard state free energies. * There is no change for problems with one multispecies * phase. * It cut diamond4.vin iterations down from 62 to 14. */ m_deltaMolNumSpecies[kspec] = 0.5 * (m_tPhaseMoles_new[iph] + TMolesMultiphase) * exp(-m_deltaGRxn_new[irxn]); for (size_t k = 0; k < m_numComponents; ++k) { m_deltaMolNumSpecies[k] += m_stoichCoeffRxnMatrix(k,irxn) * m_deltaMolNumSpecies[kspec]; } for (iph = 0; iph < m_numPhases; iph++) { m_deltaPhaseMoles[iph] += m_deltaMolNumPhase(iph,irxn) * m_deltaMolNumSpecies[kspec]; } } } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { for (size_t kspec = 0; kspec < nspecies; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { plogf("%sdirection (", pprefix); plogf("%-12.12s", m_speciesName[kspec].c_str()); plogf(") = %g", m_deltaMolNumSpecies[kspec]); if (m_SSPhase[kspec]) { if (m_molNumSpecies_old[kspec] > 0.0) { plogf(" (ssPhase exists at w = %g moles)", m_molNumSpecies_old[kspec]); } else { plogf(" (ssPhase doesn't exist -> stability not checked)"); } } plogendl(); } } } /* *********************************************************** */ /* **** KEEP COMPONENT SPECIES POSITIVE ********************** */ /* *********************************************************** */ double par = 0.5; for (size_t kspec = 0; kspec < m_numComponents; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (par < -m_deltaMolNumSpecies[kspec] / m_molNumSpecies_new[kspec]) { par = -m_deltaMolNumSpecies[kspec] / m_molNumSpecies_new[kspec]; } } } par = 1. / par; if (par <= 1.0 && par > 0.0) { par *= 0.8; } else { par = 1.0; } /* ******************************************** */ /* **** CALCULATE NEW MOLE NUMBERS ************ */ /* ******************************************** */ size_t lt = 0; size_t ikl = 0; double s1 = 0.0; while (true) { for (size_t kspec = 0; kspec < m_numComponents; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { m_molNumSpecies_old[kspec] = m_molNumSpecies_new[kspec] + par * m_deltaMolNumSpecies[kspec]; } else { m_deltaMolNumSpecies[kspec] = 0.0; } } for (size_t kspec = m_numComponents; kspec < nspecies; ++kspec) { if (m_speciesUnknownType[kspec] != VCS_SPECIES_TYPE_INTERFACIALVOLTAGE) { if (m_deltaMolNumSpecies[kspec] != 0.0) { m_molNumSpecies_old[kspec] = m_deltaMolNumSpecies[kspec] * par; } } } /* * We have a new w[] estimate, go get the * TMoles and m_tPhaseMoles_old[] values */ vcs_tmoles(); if (lt > 0) { break; } /* ******************************************* */ /* **** CONVERGENCE FORCING SECTION ********** */ /* ******************************************* */ vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD); vcs_dfe(VCS_STATECALC_OLD, 0, 0, nspecies); double s = 0.0; for (size_t kspec = 0; kspec < nspecies; ++kspec) { s += m_deltaMolNumSpecies[kspec] * m_feSpecies_old[kspec]; } if (s == 0.0) { break; } if (s < 0.0) { if (ikl == 0) { break; } } /* ***************************************** */ /* *** TRY HALF STEP SIZE ****************** */ /* ***************************************** */ if (ikl == 0) { s1 = s; par *= 0.5; ikl = 1; continue; } /* **************************************************** */ /* **** FIT PARABOLA THROUGH HALF AND FULL STEPS ****** */ /* **************************************************** */ double xl = (1.0 - s / (s1 - s)) * 0.5; if (xl < 0.0) { /* *************************************************** */ /* *** POOR DIRECTION, REDUCE STEP SIZE TO 0.2 ******* */ /* *************************************************** */ par *= 0.2; } else { if (xl > 1.0) { /* *************************************************** */ /* **** TOO BIG A STEP, TAKE ORIGINAL FULL STEP ****** */ /* *************************************************** */ par *= 2.0; } else { /* *************************************************** */ /* **** ACCEPT RESULTS OF FORCER ********************* */ /* *************************************************** */ par = par * 2.0 * xl; } } lt = 1; } if (DEBUG_MODE_ENABLED && m_debug_print_lvl >= 2) { plogf("%s Final Mole Numbers produced by inest:\n", pprefix); plogf("%s SPECIES MOLE_NUMBER\n", pprefix); for (size_t kspec = 0; kspec < nspecies; ++kspec) { plogf("%s ", pprefix); plogf("%-12.12s", m_speciesName[kspec].c_str()); plogf(" %g", m_molNumSpecies_old[kspec]); plogendl(); } } }
int VCS_SOLVE::vcs_rank(const double * awtmp, size_t numSpecies, const double matrix[], size_t numElemConstraints, std::vector<size_t> &compRes, std::vector<size_t>& elemComp, int * const usedZeroedSpecies) const { int lindep; size_t j, k, jl, i, l, ml; int numComponents = 0; compRes.clear(); elemComp.clear(); vector<double> sm(numElemConstraints*numSpecies); vector<double> sa(numSpecies); vector<double> ss(numSpecies); double test = -0.2512345E298; #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { plogf(" "); for(i=0; i<77; i++) plogf("-"); plogf("\n"); plogf(" --- Subroutine vcs_rank called to "); plogf("calculate the rank and independent rows /colums of the following matrix\n"); if (m_debug_print_lvl >= 5) { plogf(" --- Species | "); for (j = 0; j < numElemConstraints; j++) { plogf(" "); plogf(" %3d ", j); } plogf("\n"); plogf(" --- -----------"); for (j = 0; j < numElemConstraints; j++) { plogf("---------"); } plogf("\n"); for (k = 0; k < numSpecies; k++) { plogf(" --- "); plogf(" %3d ", k); plogf(" |"); for (j = 0; j < numElemConstraints; j++) { plogf(" %8.2g", matrix[j*numSpecies + k]); } plogf("\n"); } plogf(" ---"); plogendl(); } } #endif /* * Calculate the maximum value of the number of components possible * It's equal to the minimum of the number of elements and the * number of total species. */ int ncTrial = std::min(numElemConstraints, numSpecies); numComponents = ncTrial; *usedZeroedSpecies = false; /* * Use a temporary work array for the mole numbers, aw[] */ std::vector<double> aw(numSpecies); for (j = 0; j < numSpecies; j++) { aw[j] = awtmp[j]; } int jr = -1; /* * Top of a loop of some sort based on the index JR. JR is the * current number of component species found. */ do { ++jr; /* - Top of another loop point based on finding a linearly */ /* - independent species */ do { /* * Search the remaining part of the mole number vector, AW, * for the largest remaining species. Return its identity in K. * The first search criteria is always the largest positive * magnitude of the mole number. */ k = basisOptMax1(VCS_DATA_PTR(aw), numSpecies); if ((aw[k] != test) && fabs(aw[k]) == 0.0) { *usedZeroedSpecies = true; } if (aw[k] == test) { numComponents = jr; goto L_CLEANUP; } /* * Assign a small negative number to the component that we have * just found, in order to take it out of further consideration. */ aw[k] = test; /* *********************************************************** */ /* **** CHECK LINEAR INDEPENDENCE WITH PREVIOUS SPECIES ****** */ /* *********************************************************** */ /* * Modified Gram-Schmidt Method, p. 202 Dalquist * QR factorization of a matrix without row pivoting. */ jl = jr; for (j = 0; j < numElemConstraints; ++j) { sm[j + jr*numElemConstraints] = matrix[j*numSpecies + k]; } if (jl > 0) { /* * Compute the coefficients of JA column of the * the upper triangular R matrix, SS(J) = R_J_JR * (this is slightly different than Dalquist) * R_JA_JA = 1 */ for (j = 0; j < jl; ++j) { ss[j] = 0.0; for (i = 0; i < numElemConstraints; ++i) { ss[j] += sm[i + jr* numElemConstraints] * sm[i + j* numElemConstraints]; } ss[j] /= sa[j]; } /* * Now make the new column, (*,JR), orthogonal to the * previous columns */ for (j = 0; j < jl; ++j) { for (l = 0; l < numElemConstraints; ++l) { sm[l + jr*numElemConstraints] -= ss[j] * sm[l + j*numElemConstraints]; } } } /* * Find the new length of the new column in Q. * It will be used in the denominator in future row calcs. */ sa[jr] = 0.0; for (ml = 0; ml < numElemConstraints; ++ml) { sa[jr] += SQUARE(sm[ml + jr * numElemConstraints]); } /* **************************************************** */ /* **** IF NORM OF NEW ROW .LT. 1E-3 REJECT ********** */ /* **************************************************** */ if (sa[jr] < 1.0e-6) lindep = true; else lindep = false; } while(lindep); /* ****************************************** */ /* **** REARRANGE THE DATA ****************** */ /* ****************************************** */ compRes.push_back(k); elemComp.push_back(jr); } while (jr < (ncTrial-1)); L_CLEANUP: ; if (numComponents == ncTrial && numElemConstraints == numSpecies) { return numComponents; } int numComponentsR = numComponents; ss.resize(numElemConstraints); sa.resize(numElemConstraints); elemComp.clear(); aw.resize(numElemConstraints); for (j = 0; j < numSpecies; j++) { aw[j] = 1.0; } jr = -1; do { ++jr; do { k = basisOptMax1(VCS_DATA_PTR(aw), numElemConstraints); if (aw[k] == test) { numComponents = jr; goto LE_CLEANUP; } aw[k] = test; jl = jr; for (j = 0; j < numSpecies; ++j) { sm[j + jr*numSpecies] = matrix[k*numSpecies + j]; } if (jl > 0) { for (j = 0; j < jl; ++j) { ss[j] = 0.0; for (i = 0; i < numSpecies; ++i) { ss[j] += sm[i + jr* numSpecies] * sm[i + j* numSpecies]; } ss[j] /= sa[j]; } for (j = 0; j < jl; ++j) { for (l = 0; l < numSpecies; ++l) { sm[l + jr*numSpecies] -= ss[j] * sm[l + j*numSpecies]; } } } sa[jr] = 0.0; for (ml = 0; ml < numSpecies; ++ml) { sa[jr] += SQUARE(sm[ml + jr * numSpecies]); } if (sa[jr] < 1.0e-6) lindep = true; else lindep = false; } while(lindep); elemComp.push_back(k); } while (jr < (ncTrial-1)); numComponents = jr; LE_CLEANUP: ; #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { plogf(" --- vcs_rank found rank %d\n", numComponents); if (m_debug_print_lvl >= 5) { if (compRes.size() == elemComp.size()) { printf(" --- compRes elemComp\n"); for (int i = 0; i < (int) compRes.size(); i++) { printf(" --- %d %d \n", (int) compRes[i], (int) elemComp[i]); } } else { for (int i = 0; i < (int) compRes.size(); i++) { printf(" --- compRes[%d] = %d \n", (int) i, (int) compRes[i]); } for (int i = 0; i < (int) elemComp.size(); i++) { printf(" --- elemComp[%d] = %d \n", (int) i, (int) elemComp[i]); } } } } #endif if (numComponentsR != numComponents) { printf("vcs_rank ERROR: number of components are different: %d %d\n", numComponentsR, numComponents); throw Cantera::CanteraError("vcs_rank ERROR:", " logical inconsistency"); exit(-1); } return numComponents; }
double VCS_SOLVE::vcs_line_search(const size_t irxn, const double dx_orig) #endif { int its = 0; size_t k; size_t kspec = m_indexRxnToSpecies[irxn]; const int MAXITS = 10; double dx = dx_orig; double* sc_irxn = m_stoichCoeffRxnMatrix[irxn]; double* molNumBase = VCS_DATA_PTR(m_molNumSpecies_old); double* acBase = VCS_DATA_PTR(m_actCoeffSpecies_old); double* ac = VCS_DATA_PTR(m_actCoeffSpecies_new); double molSum = 0.0; double slope; /* * Calculate the deltaG value at the dx = 0.0 point */ vcs_setFlagsVolPhases(false, VCS_STATECALC_OLD); double deltaGOrig = deltaG_Recalc_Rxn(VCS_STATECALC_OLD, irxn, molNumBase, acBase, VCS_DATA_PTR(m_feSpecies_old)); double forig = fabs(deltaGOrig) + 1.0E-15; if (deltaGOrig > 0.0) { if (dx_orig > 0.0) { dx = 0.0; #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { //plogf(" --- %s :Warning possible error dx>0 dg > 0\n", SpName[kspec]); } sprintf(ANOTE, "Rxn reduced to zero step size in line search: dx>0 dg > 0"); #endif return dx; } } else if (deltaGOrig < 0.0) { if (dx_orig < 0.0) { dx = 0.0; #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { //plogf(" --- %s :Warning possible error dx<0 dg < 0\n", SpName[kspec]); } sprintf(ANOTE, "Rxn reduced to zero step size in line search: dx<0 dg < 0"); #endif return dx; } } else if (deltaGOrig == 0.0) { return 0.0; } if (dx_orig == 0.0) { return 0.0; } vcs_dcopy(VCS_DATA_PTR(m_molNumSpecies_new), molNumBase, m_numSpeciesRdc); molSum = molNumBase[kspec]; m_molNumSpecies_new[kspec] = molNumBase[kspec] + dx_orig; for (k = 0; k < m_numComponents; k++) { m_molNumSpecies_new[k] = molNumBase[k] + sc_irxn[k] * dx_orig; molSum += molNumBase[k]; } vcs_setFlagsVolPhases(false, VCS_STATECALC_NEW); double deltaG1 = deltaG_Recalc_Rxn(VCS_STATECALC_NEW, irxn, VCS_DATA_PTR(m_molNumSpecies_new), ac, VCS_DATA_PTR(m_feSpecies_new)); /* * If deltaG hasn't switched signs when going the full distance * then we are heading in the appropriate direction, and * we should accept the current full step size */ if (deltaG1 * deltaGOrig > 0.0) { dx = dx_orig; goto finalize; } /* * If we have decreased somewhat, the deltaG return after finding * a better estimate for the line search. */ if (fabs(deltaG1) < 0.8 * forig) { if (deltaG1 * deltaGOrig < 0.0) { slope = (deltaG1 - deltaGOrig) / dx_orig; dx = -deltaGOrig / slope; } else { dx = dx_orig; } goto finalize; } dx = dx_orig; for (its = 0; its < MAXITS; its++) { /* * Calculate the approximation to the total Gibbs free energy at * the dx *= 0.5 point */ dx *= 0.5; m_molNumSpecies_new[kspec] = molNumBase[kspec] + dx; for (k = 0; k < m_numComponents; k++) { m_molNumSpecies_new[k] = molNumBase[k] + sc_irxn[k] * dx; } vcs_setFlagsVolPhases(false, VCS_STATECALC_NEW); double deltaG = deltaG_Recalc_Rxn(VCS_STATECALC_NEW, irxn, VCS_DATA_PTR(m_molNumSpecies_new), ac, VCS_DATA_PTR(m_feSpecies_new)); /* * If deltaG hasn't switched signs when going the full distance * then we are heading in the appropriate direction, and * we should accept the current step */ if (deltaG * deltaGOrig > 0.0) { goto finalize; } /* * If we have decreased somewhat, the deltaG return after finding * a better estimate for the line search. */ if (fabs(deltaG) / forig < (1.0 - 0.1 * dx / dx_orig)) { if (deltaG * deltaGOrig < 0.0) { slope = (deltaG - deltaGOrig) / dx; dx = -deltaGOrig / slope; } goto finalize; } } finalize: vcs_setFlagsVolPhases(false, VCS_STATECALC_NEW); if (its >= MAXITS) { #ifdef DEBUG_MODE sprintf(ANOTE, "Rxn reduced to zero step size from %g to %g (MAXITS)", dx_orig, dx); return dx; #endif } #ifdef DEBUG_MODE if (dx != dx_orig) { sprintf(ANOTE, "Line Search reduced step size from %g to %g", dx_orig, dx); } #endif return dx; }
void VCS_PROB::reportCSV(const std::string& reportFile) { size_t k; size_t istart; double vol = 0.0; string sName; FILE* FP = fopen(reportFile.c_str(), "w"); if (!FP) { plogf("Failure to open file\n"); exit(EXIT_FAILURE); } double Temp = T; std::vector<double> volPM(nspecies, 0.0); std::vector<double> activity(nspecies, 0.0); std::vector<double> ac(nspecies, 0.0); std::vector<double> mu(nspecies, 0.0); std::vector<double> mu0(nspecies, 0.0); std::vector<double> molalities(nspecies, 0.0); vol = 0.0; size_t iK = 0; for (size_t iphase = 0; iphase < NPhase; iphase++) { istart = iK; vcs_VolPhase* volP = VPhaseList[iphase]; //const Cantera::ThermoPhase *tptr = volP->ptrThermoPhase(); size_t nSpeciesPhase = volP->nSpecies(); volPM.resize(nSpeciesPhase, 0.0); volP->sendToVCS_VolPM(VCS_DATA_PTR(volPM)); double TMolesPhase = volP->totalMoles(); double VolPhaseVolumes = 0.0; for (k = 0; k < nSpeciesPhase; k++) { iK++; VolPhaseVolumes += volPM[istart + k] * mf[istart + k]; } VolPhaseVolumes *= TMolesPhase; vol += VolPhaseVolumes; } fprintf(FP,"--------------------- VCS_MULTIPHASE_EQUIL FINAL REPORT" " -----------------------------\n"); fprintf(FP,"Temperature = %11.5g kelvin\n", Temp); fprintf(FP,"Pressure = %11.5g Pascal\n", PresPA); fprintf(FP,"Total Volume = %11.5g m**3\n", vol); fprintf(FP,"Number Basis optimizations = %d\n", m_NumBasisOptimizations); fprintf(FP,"Number VCS iterations = %d\n", m_Iterations); iK = 0; for (size_t iphase = 0; iphase < NPhase; iphase++) { istart = iK; vcs_VolPhase* volP = VPhaseList[iphase]; const Cantera::ThermoPhase<doublereal>* tp = volP->ptrThermoPhase(); string phaseName = volP->PhaseName; size_t nSpeciesPhase = volP->nSpecies(); volP->sendToVCS_VolPM(VCS_DATA_PTR(volPM)); double TMolesPhase = volP->totalMoles(); //AssertTrace(TMolesPhase == m_mix->phaseMoles(iphase)); activity.resize(nSpeciesPhase, 0.0); ac.resize(nSpeciesPhase, 0.0); mu0.resize(nSpeciesPhase, 0.0); mu.resize(nSpeciesPhase, 0.0); volPM.resize(nSpeciesPhase, 0.0); molalities.resize(nSpeciesPhase, 0.0); int actConvention = tp->activityConvention(); tp->getActivities(VCS_DATA_PTR(activity)); tp->getActivityCoefficients(VCS_DATA_PTR(ac)); tp->getStandardChemPotentials(VCS_DATA_PTR(mu0)); tp->getPartialMolarVolumes(VCS_DATA_PTR(volPM)); tp->getChemPotentials(VCS_DATA_PTR(mu)); double VolPhaseVolumes = 0.0; for (k = 0; k < nSpeciesPhase; k++) { VolPhaseVolumes += volPM[k] * mf[istart + k]; } VolPhaseVolumes *= TMolesPhase; vol += VolPhaseVolumes; if (actConvention == 1) { const Cantera::MolalityVPSSTP* mTP = static_cast<const Cantera::MolalityVPSSTP*>(tp); tp->getChemPotentials(VCS_DATA_PTR(mu)); mTP->getMolalities(VCS_DATA_PTR(molalities)); tp->getChemPotentials(VCS_DATA_PTR(mu)); if (iphase == 0) { fprintf(FP," Name, Phase, PhaseMoles, Mole_Fract, " "Molalities, ActCoeff, Activity," "ChemPot_SS0, ChemPot, mole_num, PMVol, Phase_Volume\n"); fprintf(FP," , , (kmol), , " " , , ," " (J/kmol), (J/kmol), (kmol), (m**3/kmol), (m**3)\n"); } for (k = 0; k < nSpeciesPhase; k++) { sName = tp->speciesName(k); fprintf(FP,"%12s, %11s, %11.3e, %11.3e, %11.3e, %11.3e, %11.3e," "%11.3e, %11.3e, %11.3e, %11.3e, %11.3e\n", sName.c_str(), phaseName.c_str(), TMolesPhase, mf[istart + k], molalities[k], ac[k], activity[k], mu0[k]*1.0E-6, mu[k]*1.0E-6, mf[istart + k] * TMolesPhase, volPM[k], VolPhaseVolumes); } } else { if (iphase == 0) { fprintf(FP," Name, Phase, PhaseMoles, Mole_Fract, " "Molalities, ActCoeff, Activity," " ChemPotSS0, ChemPot, mole_num, PMVol, Phase_Volume\n"); fprintf(FP," , , (kmol), , " " , , ," " (J/kmol), (J/kmol), (kmol), (m**3/kmol), (m**3)\n"); } for (k = 0; k < nSpeciesPhase; k++) { molalities[k] = 0.0; } for (k = 0; k < nSpeciesPhase; k++) { sName = tp->speciesName(k); fprintf(FP,"%12s, %11s, %11.3e, %11.3e, %11.3e, %11.3e, %11.3e, " "%11.3e, %11.3e,% 11.3e, %11.3e, %11.3e\n", sName.c_str(), phaseName.c_str(), TMolesPhase, mf[istart + k], molalities[k], ac[k], activity[k], mu0[k]*1.0E-6, mu[k]*1.0E-6, mf[istart + k] * TMolesPhase, volPM[k], VolPhaseVolumes); } } #ifdef DEBUG_MODE /* * Check consistency: These should be equal */ tp->getChemPotentials(VCS_DATA_PTR(m_gibbsSpecies)+istart); for (k = 0; k < nSpeciesPhase; k++) { if (!vcs_doubleEqual(m_gibbsSpecies[istart+k], mu[k])) { fprintf(FP,"ERROR: incompatibility!\n"); fclose(FP); plogf("ERROR: incompatibility!\n"); exit(EXIT_FAILURE); } } #endif iK += nSpeciesPhase; } fclose(FP); }
/* * This routine is always followed by vcs_prep(). Therefore, tasks * that need to be done for every call to vcsc() should be placed in * vcs_prep() and not in this routine. * * The problem structure refers to: * * the number and identity of the species. * the formula matrix and thus the number of components. * the number and identity of the phases. * the equation of state * the method and parameters for determining the standard state * The method and parameters for determining the activity coefficients. * * Tasks: * 0) Fill in the SSPhase[] array. * 1) Check to see if any multispecies phases actually have only one * species in that phase. If true, reassign that phase and species * to be a single-species phase. * 2) Determine the number of components in the problem if not already * done so. During this process the order of the species is changed * in the private data structure. All references to the species * properties must employ the ind[] index vector. * * @param printLvl Print level of the routine * * @return the return code * VCS_SUCCESS = everything went OK * */ int VCS_SOLVE::vcs_prep_oneTime(int printLvl) { size_t kspec, i; int retn = VCS_SUCCESS; double pres, test; double *aw, *sa, *sm, *ss; bool modifiedSoln = false; bool conv; m_debug_print_lvl = printLvl; /* * Calculate the Single Species status of phases * Also calculate the number of species per phase */ vcs_SSPhase(); /* * Set an initial estimate for the number of noncomponent species * equal to nspecies - nelements. This may be changed below */ m_numRxnTot = m_numSpeciesTot - m_numElemConstraints; m_numRxnRdc = m_numRxnTot; m_numSpeciesRdc = m_numSpeciesTot; for (i = 0; i < m_numRxnRdc; ++i) { m_indexRxnToSpecies[i] = m_numElemConstraints + i; } for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) { size_t pID = m_phaseID[kspec]; size_t spPhIndex = m_speciesLocalPhaseIndex[kspec]; vcs_VolPhase *vPhase = m_VolPhaseList[pID]; vcs_SpeciesProperties *spProp = vPhase->speciesProperty(spPhIndex); double sz = 0.0; size_t eSize = spProp->FormulaMatrixCol.size(); for (size_t e = 0; e < eSize; e++) { sz += fabs(spProp->FormulaMatrixCol[e]); } if (sz > 0.0) { m_spSize[kspec] = sz; } else { m_spSize[kspec] = 1.0; } } /* ***************************************************** */ /* **** DETERMINE THE NUMBER OF COMPONENTS ************* */ /* ***************************************************** */ /* * Obtain a valid estimate of the mole fraction. This will * be used as an initial ordering vector for prioritizing * which species are defined as components. * * If a mole number estimate was supplied from the * input file, use that mole number estimate. * * If a solution estimate wasn't supplied from the input file, * supply an initial estimate for the mole fractions * based on the relative reverse ordering of the * chemical potentials. * * For voltage unknowns, set these to zero for the moment. */ test = -1.0e-10; if (m_doEstimateEquil < 0) { double sum = 0.0; for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) { if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { sum += fabs(m_molNumSpecies_old[kspec]); } } if (fabs(sum) < 1.0E-6) { modifiedSoln = true; if (m_pressurePA <= 0.0) pres = 1.01325E5; else pres = m_pressurePA; retn = vcs_evalSS_TP(0, 0, m_temperature, pres); for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) { if (m_speciesUnknownType[kspec] == VCS_SPECIES_TYPE_MOLNUM) { m_molNumSpecies_old[kspec] = - m_SSfeSpecies[kspec]; } else { m_molNumSpecies_old[kspec] = 0.0; } } } test = -1.0e20; } /* * NC = number of components is in the vcs.h common block * This call to BASOPT doesn't calculate the stoichiometric * reaction matrix. */ std::vector<double> awSpace( m_numSpeciesTot + (m_numElemConstraints + 2)*(m_numElemConstraints), 0.0); aw = VCS_DATA_PTR(awSpace); if (aw == NULL) { plogf("vcs_prep_oneTime: failed to get memory: global bailout\n"); return VCS_NOMEMORY; } sa = aw + m_numSpeciesTot; sm = sa + m_numElemConstraints; ss = sm + (m_numElemConstraints)*(m_numElemConstraints); retn = vcs_basopt(true, aw, sa, sm, ss, test, &conv); if (retn != VCS_SUCCESS) { plogf("vcs_prep_oneTime:"); plogf(" Determination of number of components failed: %d\n", retn); plogf(" Global Bailout!\n"); return retn; } if (m_numElemConstraints != m_numComponents) { m_numRxnTot = m_numRxnRdc = m_numSpeciesTot - m_numComponents; for (i = 0; i < m_numRxnRdc; ++i) { m_indexRxnToSpecies[i] = m_numComponents + i; } } /* * The elements might need to be rearranged. */ awSpace.resize(m_numElemConstraints + (m_numElemConstraints + 2)*(m_numElemConstraints), 0.0); aw = VCS_DATA_PTR(awSpace); sa = aw + m_numElemConstraints; sm = sa + m_numElemConstraints; ss = sm + (m_numElemConstraints)*(m_numElemConstraints); retn = vcs_elem_rearrange(aw, sa, sm, ss); if (retn != VCS_SUCCESS) { plogf("vcs_prep_oneTime:"); plogf(" Determination of element reordering failed: %d\n", retn); plogf(" Global Bailout!\n"); return retn; } // If we mucked up the solution unknowns because they were all // zero to start with, set them back to zero here if (modifiedSoln) { for (kspec = 0; kspec < m_numSpeciesTot; ++kspec) { m_molNumSpecies_old[kspec] = 0.0; } } return VCS_SUCCESS; }
void vcs_VolPhase::_updateG0() const { TP_ptr->getGibbs_ref(VCS_DATA_PTR(SS0ChemicalPotential)); m_UpToDate_G0 = true; }
/*! * This is done by running * each reaction as far forward or backward as possible, subject * to the constraint that all mole numbers remain * non-negative. Reactions for which \f$ \Delta \mu^0 \f$ are * positive are run in reverse, and ones for which it is negative * are run in the forward direction. The end result is equivalent * to solving the linear programming problem of minimizing the * linear Gibbs function subject to the element and * non-negativity constraints. */ int VCS_SOLVE::vcs_setMolesLinProg() { int ik, irxn; double test = -1.0E-10; #ifdef DEBUG_MODE std::string pprefix(" --- seMolesLinProg "); if (m_debug_print_lvl >= 2) { plogf(" --- call setInitialMoles\n"); } #endif // m_mu are standard state chemical potentials // Boolean on the end specifies standard chem potentials // m_mix->getValidChemPotentials(not_mu, DATA_PTR(m_mu), true); // -> This is already done coming into the routine. double dg_rt; int idir; double nu; double delta_xi, dxi_min = 1.0e10; bool redo = true; int jcomp; int retn; int iter = 0; bool abundancesOK = true; int usedZeroedSpecies; std::vector<double> sm(m_numElemConstraints*m_numElemConstraints, 0.0); std::vector<double> ss(m_numElemConstraints, 0.0); std::vector<double> sa(m_numElemConstraints, 0.0); std::vector<double> wx(m_numElemConstraints, 0.0); std::vector<double> aw(m_numSpeciesTot, 0.0); for (ik = 0; ik < m_numSpeciesTot; ik++) { if (m_speciesUnknownType[ik] != VCS_SPECIES_INTERFACIALVOLTAGE) { m_molNumSpecies_old[ik] = MAX(0.0, m_molNumSpecies_old[ik]); } } #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { printProgress(m_speciesName, m_molNumSpecies_old, m_SSfeSpecies); } #endif while (redo) { if (!vcs_elabcheck(0)) { #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { plogf("%s Mole numbers failing element abundances\n", pprefix.c_str()); plogf("%sCall vcs_elcorr to attempt fix\n", pprefix.c_str()); } #endif retn = vcs_elcorr(VCS_DATA_PTR(sm), VCS_DATA_PTR(wx)); if (retn >= 2) { abundancesOK = false; } else { abundancesOK = true; } } else { abundancesOK = true; } /* * Now find the optimized basis that spans the stoichiometric * coefficient matrix, based on the current composition, m_molNumSpecies_old[] * We also calculate sc[][], the reaction matrix. */ retn = vcs_basopt(FALSE, VCS_DATA_PTR(aw), VCS_DATA_PTR(sa), VCS_DATA_PTR(sm), VCS_DATA_PTR(ss), test, &usedZeroedSpecies); if (retn != VCS_SUCCESS) return retn; #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { plogf("iteration %d\n", iter); } #endif redo = false; iter++; if (iter > 15) break; // loop over all reactions for (irxn = 0; irxn < m_numRxnTot; irxn++) { // dg_rt is the Delta_G / RT value for the reaction ik = m_numComponents + irxn; dg_rt = m_SSfeSpecies[ik]; dxi_min = 1.0e10; const double *sc_irxn = m_stoichCoeffRxnMatrix[irxn]; for (jcomp = 0; jcomp < m_numElemConstraints; jcomp++) { dg_rt += m_SSfeSpecies[jcomp] * sc_irxn[jcomp]; } // fwd or rev direction. // idir > 0 implies increasing the current species // idir < 0 implies decreasing the current species idir = (dg_rt < 0.0 ? 1 : -1); if (idir < 0) { dxi_min = m_molNumSpecies_old[ik]; } for (jcomp = 0; jcomp < m_numComponents; jcomp++) { nu = sc_irxn[jcomp]; // set max change in progress variable by // non-negativity requirement if (nu*idir < 0) { delta_xi = fabs(m_molNumSpecies_old[jcomp]/nu); // if a component has nearly zero moles, redo // with a new set of components if (!redo) { if (delta_xi < 1.0e-10 && (m_molNumSpecies_old[ik] >= 1.0E-10)) { #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { plogf(" --- Component too small: %s\n", m_speciesName[jcomp].c_str()); } #endif redo = true; } } if (delta_xi < dxi_min) dxi_min = delta_xi; } } // step the composition by dxi_min, check against zero, since // we are zeroing components and species on every step. // Redo the iteration, if a component went from positive to zero on this step. double dsLocal = idir*dxi_min; m_molNumSpecies_old[ik] += dsLocal; m_molNumSpecies_old[ik] = MAX(0.0, m_molNumSpecies_old[ik]); for (jcomp = 0; jcomp < m_numComponents; jcomp++) { bool full = false; if (m_molNumSpecies_old[jcomp] > 1.0E-15) { full = true; } m_molNumSpecies_old[jcomp] += sc_irxn[jcomp] * dsLocal; m_molNumSpecies_old[jcomp] = MAX(0.0, m_molNumSpecies_old[jcomp]); if (full) { if (m_molNumSpecies_old[jcomp] < 1.0E-60) { redo = true; } } } } // set the moles of the phase objects to match // updateMixMoles(); // Update the phase objects with the contents of the m_molNumSpecies_old vector // vcs_updateVP(0); #ifdef DEBUG_MODE if (m_debug_print_lvl >= 2) { printProgress(m_speciesName, m_molNumSpecies_old, m_SSfeSpecies); } #endif } #ifdef DEBUG_MODE if (m_debug_print_lvl == 1) { printProgress(m_speciesName, m_molNumSpecies_old, m_SSfeSpecies); plogf(" --- setInitialMoles end\n"); } #endif retn = 0; if (!abundancesOK) { retn = -1; } else if (iter > 15) { retn = 1; } return retn; }
//==================================================================================================================== double vcs_PhaseStabilitySolve::vcs_phaseStabilitySubSolve(const int iph) { /* * We will use the _new state calc here */ int kspec, irxn, k, i, kc, kc_spec; vcs_VolPhase *Vphase = fVS_->m_VolPhaseList[iph]; doublereal deltaGRxn; // We will do a full newton calculation later, but for now, ... bool doSuccessiveSubstitution = true; double funcPhaseStability; vector<doublereal> X_est(Vphase->nSpecies(), 0.0); vector<doublereal> delFrac(Vphase->nSpecies(), 0.0); vector<doublereal> E_phi(Vphase->nSpecies(), 0.0); vector<doublereal> fracDelta_new(Vphase->nSpecies(), 0.0); vector<doublereal> fracDelta_old(Vphase->nSpecies(), 0.0); vector<doublereal> fracDelta_raw(Vphase->nSpecies(), 0.0); vector<int> creationGlobalRxnNumbers(Vphase->nSpecies(), -1); vcs_dcopy(VCS_DATA_PTR(fVS_->m_deltaGRxn_Deficient), VCS_DATA_PTR(fVS_->m_deltaGRxn_old), fVS_->m_numRxnRdc); std::vector<doublereal> m_feSpecies_Deficient(fVS_->m_numComponents, 0.0); doublereal damp = 1.0; doublereal dampOld = 1.0; doublereal normUpdate = 1.0; doublereal normUpdateOld = 1.0; doublereal sum = 0.0; doublereal dirProd = 0.0; doublereal dirProdOld = 0.0; // get the activity coefficients Vphase->sendToVCS_ActCoeff(VCS_STATECALC_OLD, VCS_DATA_PTR(fVS_->m_actCoeffSpecies_new)); // Get the storred estimate for the composition of the phase if // it gets created fracDelta_new = Vphase->creationMoleNumbers(creationGlobalRxnNumbers); bool oneIsComponent = false; std::vector<int> componentList; for (k = 0; k < Vphase->nSpecies(); k++) { kspec = Vphase->spGlobalIndexVCS(k); if (kspec < fVS_->m_numComponents) { oneIsComponent = true; componentList.push_back(k); } } for (k = 0; k < fVS_->m_numComponents; k++) { m_feSpecies_Deficient[k] = fVS_->m_feSpecies_old[k]; } normUpdate = 0.1 * vcs_l2norm(fracDelta_new); damp = 1.0E-2; if (doSuccessiveSubstitution) { #ifdef DEBUG_MODE int KP = 0; if (fVS_->m_debug_print_lvl >= 2) { plogf(" --- vcs_phaseStabilityTest() called\n"); plogf(" --- Its X_old[%2d] FracDel_old[%2d] deltaF[%2d] FracDel_new[%2d]" " normUpdate damp FuncPhaseStability\n", KP, KP, KP, KP); plogf(" --------------------------------------------------------------" "--------------------------------------------------------\n"); } else if (fVS_->m_debug_print_lvl == 1) { plogf(" --- vcs_phaseStabilityTest() called for phase %d\n", iph); } #endif for (k = 0; k < Vphase->nSpecies(); k++) { if (fracDelta_new[k] < 1.0E-13) { fracDelta_new[k] = 1.0E-13; } } bool converged = false; for (int its = 0; its < 200 && (!converged); its++) { dampOld = damp; normUpdateOld = normUpdate; fracDelta_old = fracDelta_new; dirProdOld = dirProd; // Given a set of fracDelta's, we calculate the fracDelta's // for the component species, if any for (i = 0; i < (int) componentList.size(); i++) { kc = componentList[i]; kc_spec = Vphase->spGlobalIndexVCS(kc); fracDelta_old[kc] = 0.0; for (k = 0; k < Vphase->nSpecies(); k++) { kspec = Vphase->spGlobalIndexVCS(k); irxn = kspec - fVS_->m_numComponents; if (irxn >= 0) { fracDelta_old[kc] += fVS_->m_stoichCoeffRxnMatrix[irxn][kc_spec] * fracDelta_old[k]; } } } // Now, calculate the predicted mole fractions, X_est[k] double sumFrac = 0.0; for (k = 0; k < Vphase->nSpecies(); k++) { sumFrac += fracDelta_old[k]; } // Necessary because this can be identically zero. -> we need to fix this algorithm! if (sumFrac <= 0.0) { sumFrac = 1.0; } double sum_Xcomp = 0.0; for (k = 0; k < Vphase->nSpecies(); k++) { X_est[k] = fracDelta_old[k] / sumFrac; kc_spec = Vphase->spGlobalIndexVCS(k); if (kc_spec < fVS_->m_numComponents) { sum_Xcomp += X_est[k]; } } /* * Feed the newly formed estimate of the mole fractions back into the * ThermoPhase object */ Vphase->setMoleFractionsState(0.0, VCS_DATA_PTR(X_est), VCS_STATECALC_PHASESTABILITY); /* * get the activity coefficients */ Vphase->sendToVCS_ActCoeff(VCS_STATECALC_OLD, VCS_DATA_PTR(fVS_->m_actCoeffSpecies_new)); /* * First calculate altered chemical potentials for component species * belonging to this phase. */ for (i = 0; i < (int) componentList.size(); i++) { kc = componentList[i]; kc_spec = Vphase->spGlobalIndexVCS(kc); if ( X_est[kc] > VCS_DELETE_MINORSPECIES_CUTOFF) { m_feSpecies_Deficient[kc_spec] = fVS_->m_feSpecies_old[kc_spec] + log(fVS_->m_actCoeffSpecies_new[kc_spec] * X_est[kc]); } else { m_feSpecies_Deficient[kc_spec] = fVS_->m_feSpecies_old[kc_spec] + log(fVS_->m_actCoeffSpecies_new[kc_spec] * VCS_DELETE_MINORSPECIES_CUTOFF); } } for (i = 0; i < (int) componentList.size(); i++) { kc = componentList[i]; kc_spec = Vphase->spGlobalIndexVCS(kc); for (k = 0; k < Vphase->nSpecies(); k++) { kspec = Vphase->spGlobalIndexVCS(k); irxn = kspec - fVS_->m_numComponents; if (irxn >= 0) { if (i == 0) { fVS_->m_deltaGRxn_Deficient[irxn] = fVS_->m_deltaGRxn_old[irxn]; } double *dtmp_ptr = fVS_->m_stoichCoeffRxnMatrix[irxn]; if (dtmp_ptr[kc_spec] != 0.0) { fVS_->m_deltaGRxn_Deficient[irxn] += dtmp_ptr[kc_spec] * (m_feSpecies_Deficient[kc_spec]- fVS_->m_feSpecies_old[kc_spec]); } } } } /* * Calculate the E_phi's */ sum = 0.0; funcPhaseStability = sum_Xcomp - 1.0; for (k = 0; k < Vphase->nSpecies(); k++) { kspec = Vphase->spGlobalIndexVCS(k); irxn = kspec - fVS_->m_numComponents; if (irxn >= 0) { deltaGRxn = fVS_->m_deltaGRxn_Deficient[irxn]; if (deltaGRxn > 50.0) deltaGRxn = 50.0; if (deltaGRxn < -50.0) deltaGRxn = -50.0; E_phi[k] = std::exp(-deltaGRxn) / fVS_->m_actCoeffSpecies_new[kspec]; sum += E_phi[k]; funcPhaseStability += E_phi[k]; } else { E_phi[k] = 0.0; } } /* * Calculate the raw estimate of the new fracs */ for (k = 0; k < Vphase->nSpecies(); k++) { kspec = Vphase->spGlobalIndexVCS(k); irxn = kspec - fVS_->m_numComponents; double b = E_phi[k] / sum * (1.0 - sum_Xcomp); if (irxn >= 0) { fracDelta_raw[k] = b; } } // Given a set of fracDelta's, we calculate the fracDelta's // for the component species, if any for (i = 0; i < (int) componentList.size(); i++) { kc = componentList[i]; kc_spec = Vphase->spGlobalIndexVCS(kc); fracDelta_raw[kc] = 0.0; for (k = 0; k < Vphase->nSpecies(); k++) { kspec = Vphase->spGlobalIndexVCS(k); irxn = kspec - fVS_->m_numComponents; if (irxn >= 0) { fracDelta_raw[kc] += fVS_->m_stoichCoeffRxnMatrix[irxn][kc_spec] * fracDelta_raw[k]; } } } /* * Now possibly dampen the estimate. */ doublereal sumADel = 0.0; for (k = 0; k < Vphase->nSpecies(); k++) { delFrac[k] = fracDelta_raw[k] - fracDelta_old[k]; sumADel += fabs(delFrac[k]); } normUpdate = vcs_l2norm(delFrac); dirProd = 0.0; for (k = 0; k < Vphase->nSpecies(); k++) { dirProd += fracDelta_old[k] * delFrac[k]; } bool crossedSign = false; if (dirProd * dirProdOld < 0.0) { crossedSign = true; } damp = 0.5; if (dampOld < 0.25) { damp = 2.0 * dampOld; } if (crossedSign) { if (normUpdate *1.5 > normUpdateOld) { damp = 0.5 * dampOld; } else if (normUpdate *2.0 > normUpdateOld) { damp = 0.8 * dampOld; } } else { if (normUpdate > normUpdateOld * 2.0) { damp = 0.6 * dampOld; } else if (normUpdate > normUpdateOld * 1.2) { damp = 0.9 * dampOld; } } for (k = 0; k < Vphase->nSpecies(); k++) { if (fabs(damp * delFrac[k]) > 0.3*fabs(fracDelta_old[k])) { damp = MAX(0.3*fabs(fracDelta_old[k]) / fabs( delFrac[k]), 1.0E-8/fabs( delFrac[k])); } if (delFrac[k] < 0.0) { if (2.0 * damp * (-delFrac[k]) > fracDelta_old[k]) { damp = fracDelta_old[k] / (2.0 * (-delFrac[k])); } } if (delFrac[k] > 0.0) { if (2.0 * damp * delFrac[k] > fracDelta_old[k]) { damp = fracDelta_old[k] / (2.0 * delFrac[k]); } } } if (damp < 0.000001) { damp = 0.000001; } for (k = 0; k < Vphase->nSpecies(); k++) { fracDelta_new[k] = fracDelta_old[k] + damp * (delFrac[k]); } #ifdef DEBUG_MODE if (fVS_->m_debug_print_lvl >= 2) { plogf(" --- %3d %12g %12g %12g %12g %12g %12g %12g\n", its, X_est[KP], fracDelta_old[KP], delFrac[KP], fracDelta_new[KP], normUpdate, damp, funcPhaseStability); } #endif if (normUpdate < 1.0E-5) { converged = true; } } if (converged) { Vphase->setMoleFractionsState(0.0, VCS_DATA_PTR(X_est), VCS_STATECALC_PHASESTABILITY); Vphase->setCreationMoleNumbers(VCS_DATA_PTR(fracDelta_new), creationGlobalRxnNumbers); } } else { printf("not done yet\n"); exit(-1); } #ifdef DEBUG_MODE if (fVS_->m_debug_print_lvl >= 2) { plogf(" ------------------------------------------------------------" "-------------------------------------------------------------\n"); } else if (fVS_->m_debug_print_lvl == 1) { if (funcPhaseStability > 0.0) { plogf(" --- phase %d with func = %g is to be born\n", iph, funcPhaseStability); } else { plogf(" --- phase %d with func = %g stays dead\n", iph, funcPhaseStability); } } #endif return funcPhaseStability; }