ThermoPhase.cpp

Go to the documentation of this file.
00001 /**
00002  *  @file ThermoPhase.cpp
00003  * Definition file for class ThermoPhase, the base class for phases with
00004  * thermodynamic properties
00005  * (see class \link Cantera::ThermoPhase ThermoPhase\endlink).
00006  */
00007 
00008 /*
00009  *  $Author: hkmoffa $
00010  *  $Date: 2009-12-05 14:08:43 -0500 (Sat, 05 Dec 2009) $
00011  *  $Revision: 279 $
00012  *
00013  *  Copyright 2002 California Institute of Technology
00014  *
00015  */
00016 
00017 // turn off warnings under Windows
00018 #ifdef WIN32
00019 #pragma warning(disable:4786)
00020 #pragma warning(disable:4503)
00021 #endif
00022 
00023 #include "ThermoPhase.h"
00024 
00025 //@{
00026 #ifndef MAX
00027 #define MAX(x,y)    (( (x) > (y) ) ? (x) : (y))
00028 #endif
00029 //@}
00030 
00031 using namespace std;
00032 
00033 namespace Cantera {
00034 
00035   //! Constructor. Note that ThermoPhase is meant to be used as
00036   //! a base class, so this constructor should not be called
00037   //! explicitly.
00038   ThermoPhase::ThermoPhase() :
00039     Phase(), 
00040     m_spthermo(0), m_speciesData(0),
00041     m_index(-1), 
00042     m_phi(0.0), 
00043     m_hasElementPotentials(false),
00044     m_chargeNeutralityNecessary(false),
00045     m_ssConvention(cSS_CONVENTION_TEMPERATURE)
00046   {
00047   }
00048 
00049   ThermoPhase::~ThermoPhase() 
00050   {
00051     for (int k = 0; k < m_kk; k++) {
00052       if (!m_speciesData[k]) {
00053         delete m_speciesData[k];
00054       }
00055     }
00056     delete m_spthermo;
00057   }
00058 
00059   /**
00060    * Copy Constructor for the ThermoPhase object. 
00061    *
00062    * Currently, this is implemented, but not tested. If called it will
00063    * throw an exception until fully tested.
00064    */
00065   ThermoPhase::ThermoPhase(const ThermoPhase &right)  :
00066     Phase(),
00067     m_spthermo(0), 
00068     m_speciesData(0),
00069     m_index(-1),
00070     m_phi(0.0), 
00071     m_hasElementPotentials(false),
00072     m_chargeNeutralityNecessary(false),
00073     m_ssConvention(cSS_CONVENTION_TEMPERATURE)
00074   {
00075     /*
00076      * Call the assignment operator
00077      */
00078     *this = operator=(right);
00079   }
00080   
00081   /*
00082    * operator=()
00083    *
00084    *  Note this stuff will not work until the underlying phase
00085    *  has a working assignment operator
00086    */
00087   ThermoPhase& ThermoPhase::
00088   operator=(const ThermoPhase &right) {
00089     /*
00090      * Check for self assignment.
00091      */
00092     if (this == &right) return *this;
00093 
00094     /*
00095      * We need to destruct first
00096      */
00097     for (int k = 0; k < m_kk; k++) {
00098       if (!m_speciesData[k]) {
00099         delete m_speciesData[k];
00100       }
00101     }
00102     if (m_spthermo) {
00103       delete m_spthermo;
00104     }
00105 
00106     /*
00107      * Call the base class assignment operator
00108      */
00109     (void)Phase::operator=(right);
00110 
00111     /*
00112      * Pointer to the species thermodynamic property manager
00113      * We own this, so we need to do a deep copy
00114      */
00115     m_spthermo = (right.m_spthermo)->duplMyselfAsSpeciesThermo();
00116 
00117     /*
00118      * Do a deep copy of species Data, because we own this
00119      */
00120     m_speciesData.resize(m_kk); 
00121     for (int k = 0; k < m_kk; k++) {
00122       m_speciesData[k] = new XML_Node(*(right.m_speciesData[k]));
00123     }
00124       
00125     m_index = right.m_index;
00126     m_phi = right.m_phi;
00127     m_lambdaRRT = right.m_lambdaRRT;
00128     m_hasElementPotentials = right.m_hasElementPotentials;
00129     m_chargeNeutralityNecessary = right.m_chargeNeutralityNecessary;
00130     m_ssConvention = right.m_ssConvention;
00131     return *this;
00132   }
00133 
00134   /*
00135    * Duplication routine for objects which inherit from 
00136    * ThermoPhase.
00137    *
00138    *  This virtual routine can be used to duplicate thermophase objects
00139    *  inherited from ThermoPhase even if the application only has
00140    *  a pointer to ThermoPhase to work with.
00141    * 
00142    *  Currently, this is not fully implemented. If called, an
00143    *  exception will be called by the ThermoPhase copy constructor.
00144    */
00145   ThermoPhase *ThermoPhase::duplMyselfAsThermoPhase() const {
00146     ThermoPhase* tp = new ThermoPhase(*this);
00147     return tp;
00148   }
00149 
00150   int ThermoPhase::activityConvention() const {
00151     return cAC_CONVENTION_MOLAR;
00152   }
00153 
00154   int ThermoPhase::standardStateConvention() const {
00155     return m_ssConvention;
00156   }
00157 
00158   doublereal ThermoPhase::logStandardConc(int k) const {
00159     return log(standardConcentration(k));
00160   }
00161 
00162   void ThermoPhase::getActivities(doublereal* a) const {
00163     getActivityConcentrations(a);
00164     int nsp = nSpecies();
00165     int k;
00166     for (k = 0; k < nsp; k++) a[k] /= standardConcentration(k);
00167   }
00168 
00169   void ThermoPhase::getLNActivityCoefficients(doublereal *const lnac) const {
00170     getActivityCoefficients(lnac);
00171     for (int k = 0; k < m_kk; k++) {
00172        lnac[k] = std::log(lnac[k]);
00173     }
00174   }
00175 
00176   void ThermoPhase::setState_TPX(doublereal t, doublereal p, 
00177                                  const doublereal* x) {
00178     setMoleFractions(x); setTemperature(t); setPressure(p);
00179   }
00180 
00181   void ThermoPhase::setState_TPX(doublereal t, doublereal p, 
00182                                  compositionMap& x) {
00183     setMoleFractionsByName(x); setTemperature(t); setPressure(p);
00184   }
00185 
00186   void ThermoPhase::setState_TPX(doublereal t, doublereal p, 
00187                                  const std::string& x) {
00188     compositionMap xx;
00189     int kk = nSpecies();
00190     for (int k = 0; k < kk; k++) xx[speciesName(k)] = -1.0;
00191     try {
00192       parseCompString(x, xx);
00193     }
00194     catch (CanteraError) {
00195       throw CanteraError("setState_TPX",
00196                          "Unknown species in composition map: "+ x);
00197     }
00198     setMoleFractionsByName(xx); setTemperature(t); setPressure(p);
00199   }        
00200 
00201   void ThermoPhase::setState_TPY(doublereal t, doublereal p, 
00202                                  const doublereal* y) {
00203     setMassFractions(y); setTemperature(t); setPressure(p);
00204   }
00205 
00206   void ThermoPhase::setState_TPY(doublereal t, doublereal p, 
00207                                  compositionMap& y) {
00208     setMassFractionsByName(y); setTemperature(t); setPressure(p);
00209   }
00210         
00211   void ThermoPhase::setState_TPY(doublereal t, doublereal p, 
00212                                  const std::string& y) {
00213     compositionMap yy;
00214     int kk = nSpecies();
00215     for (int k = 0; k < kk; k++) yy[speciesName(k)] = -1.0;
00216     try {
00217       parseCompString(y, yy);
00218     }
00219     catch (CanteraError) {
00220       throw CanteraError("setState_TPY",
00221                          "Unknown species in composition map: "+ y);
00222     }
00223     setMassFractionsByName(yy); setTemperature(t); setPressure(p);
00224   }
00225 
00226   void ThermoPhase::setState_TP(doublereal t, doublereal p) {
00227     setTemperature(t); setPressure(p);
00228   }
00229 
00230   void ThermoPhase::setState_PX(doublereal p, doublereal* x) {
00231     setMoleFractions(x); setPressure(p);
00232   }
00233 
00234   void ThermoPhase::setState_PY(doublereal p, doublereal* y) {
00235     setMassFractions(y); setPressure(p);
00236   }
00237 
00238   void ThermoPhase::setState_HP(doublereal Htarget, doublereal p, 
00239                                 doublereal dTtol) {
00240     setState_HPorUV(Htarget, p, dTtol, false);
00241   }
00242 
00243   void ThermoPhase::setState_UV(doublereal u, doublereal v, 
00244                                 doublereal dTtol) {
00245     setState_HPorUV(u, v, dTtol, true);
00246   }
00247 
00248   // Do the convergence work
00249   /*
00250    *  We assume here that H at constant P is a monotonically increasing
00251    *  function of T.
00252    *  We assume here that U at constant V is a monotonically increasing
00253    *  function of T.
00254    *  
00255    *  Note, the value of dTtol may become important for some applications
00256    *  where numerical jacobians are being calculated.
00257    */
00258   void ThermoPhase::setState_HPorUV(doublereal Htarget, doublereal p, 
00259                                     doublereal dTtol, bool doUV) {
00260     doublereal dt;
00261     doublereal Hmax = 0.0, Hmin = 0.0;;
00262     doublereal v = 0.0;
00263 
00264     // Assign the specific volume or pressure and make sure it's positive
00265     if (doUV) {
00266       v = p;
00267       if (v < 1.0E-300) {
00268         throw CanteraError("setState_HPorUV (UV)",
00269                            "Input specific volume is too small or negative. v = " + fp2str(v));
00270       }
00271       setDensity(1.0/v);
00272     } else {
00273       if (p < 1.0E-300) {
00274         throw CanteraError("setState_HPorUV (HP)",
00275                            "Input pressure is too small or negative. p = " + fp2str(p));
00276       }
00277       setPressure(p);
00278     }
00279     double Tmax = maxTemp() + 0.1;
00280     double Tmin = minTemp() - 0.1;
00281 
00282     // Make sure we are within the temperature bounds at the start
00283     // of the iteration
00284     double Tnew = temperature();
00285     double Tinit = Tnew;
00286     if (Tnew > Tmax) {
00287       Tnew = Tmax - 1.0;
00288       if (doUV) {
00289         setTemperature(Tnew);
00290       } else {
00291         setState_TP(Tnew, p);
00292       }
00293     }
00294     if (Tnew < Tmin) {
00295       Tnew = Tmin + 1.0;
00296       if (doUV) {
00297         setTemperature(Tnew);
00298       } else {
00299         setState_TP(Tnew, p);
00300       }
00301     }
00302 
00303     double Hnew = 0.0;
00304     double Cpnew = 0.0;
00305     if (doUV) {
00306       Hnew = intEnergy_mass();
00307       Cpnew = cv_mass();
00308     } else {
00309       Hnew = enthalpy_mass();
00310       Cpnew = cp_mass();
00311     }
00312     double Htop = Hnew;
00313     double Ttop = Tnew;
00314     double Hbot = Hnew;
00315     double Tbot = Tnew;
00316     double Told = Tnew;
00317     double Hold = Hnew;
00318 
00319     bool ignoreBounds = false;
00320     // Unstable phases are those for which
00321     // cp < 0.0. These are possible for cases where
00322     // we have passed the spinodal curve.
00323     bool unstablePhase = false;
00324     // Counter indicating the last temperature point where the
00325     // phase was unstable
00326     double Tunstable = -1.0;
00327     bool unstablePhaseNew = false;
00328    
00329 
00330     // Newton iteration
00331     for (int n = 0; n < 500; n++) {
00332       Told = Tnew;
00333       Hold = Hnew;
00334       double cpd = Cpnew;
00335       if (cpd < 0.0) {
00336         unstablePhase = true;
00337         Tunstable = Tnew;
00338       }
00339       dt = (Htarget - Hold)/cpd;
00340 
00341       // limit step size to 100 K
00342       if (dt > 100.0)       dt =  100.0;
00343       else if (dt < -100.0) dt = -100.0; 
00344 
00345       // Calculate the new T
00346       Tnew = Told + dt;
00347 
00348       // Limit the step size so that we are convergent
00349       // This is the step that makes it different from a 
00350       // Newton's algorithm
00351       if (dt > 0.0) {
00352         if (!unstablePhase) {
00353           if (Htop > Htarget) {
00354             if (Tnew > (0.75 * Ttop + 0.25 * Told)) {
00355               dt = 0.75 * (Ttop - Told);
00356               Tnew = Told + dt;
00357             }
00358           }
00359         } else {
00360           if (Hbot < Htarget) {
00361             if (Tnew < (0.75 * Tbot + 0.25 * Told)) {
00362               dt = 0.75 * (Tbot - Told);
00363               Tnew = Told + dt;
00364             }
00365           }
00366         }
00367       } else {
00368         if (!unstablePhase) {
00369           if (Hbot < Htarget) {
00370             if (Tnew < (0.75 * Tbot + 0.25 * Told)) {
00371               dt = 0.75 * (Tbot - Told);
00372               Tnew = Told + dt;
00373             }
00374           }
00375         } else {
00376           if (Htop > Htarget) {
00377             if (Tnew > (0.75 * Ttop + 0.25 * Told)) {
00378               dt = 0.75 * (Ttop - Told);
00379               Tnew = Told + dt;
00380             }
00381           }
00382         }
00383       }
00384       // Check Max and Min values
00385       if (Tnew > Tmax) {
00386         if (!ignoreBounds) {
00387           if (doUV) {
00388             setTemperature(Tmax);
00389             Hmax = intEnergy_mass();
00390           } else {
00391             setState_TP(Tmax, p);
00392             Hmax = enthalpy_mass();
00393           }
00394           if (Hmax >= Htarget) {
00395             if (Htop < Htarget) {
00396               Ttop = Tmax;
00397               Htop = Hmax;
00398             }
00399           } else {
00400             Tnew = Tmax + 1.0;
00401             ignoreBounds = true;
00402           }
00403         }
00404       }
00405       if (Tnew < Tmin) {
00406         if (!ignoreBounds) {
00407           if (doUV) {
00408             setTemperature(Tmin);
00409             Hmin = intEnergy_mass();
00410           } else {
00411             setState_TP(Tmin, p);
00412             Hmin = enthalpy_mass();
00413           }
00414           if (Hmin <= Htarget) {
00415             if (Hbot > Htarget) {
00416               Tbot = Tmin;
00417               Hbot = Hmin;
00418             }
00419           } else {
00420             Tnew = Tmin - 1.0;
00421             ignoreBounds = true;
00422           }
00423         }
00424       }
00425  
00426       // Try to keep phase within its region of stability
00427       // -> Could do a lot better if I calculate the
00428       //    spinodal value of H.
00429       for (int its = 0; its < 10; its++) {
00430         Tnew = Told + dt;
00431         if (doUV) {
00432           setTemperature(Tnew);
00433           Hnew = intEnergy_mass();
00434           Cpnew = cv_mass();
00435         } else {
00436           setState_TP(Tnew, p);
00437           Hnew = enthalpy_mass();
00438           Cpnew = cp_mass();
00439         }
00440         if (Cpnew < 0.0) {
00441           unstablePhaseNew = true;
00442           Tunstable = Tnew;
00443         } else {
00444           unstablePhaseNew = false;
00445           break;
00446         }
00447         if (unstablePhase == false) {
00448           if (unstablePhaseNew == true) {
00449             dt *= 0.25;
00450           }
00451         }
00452       }
00453 
00454       if (Hnew == Htarget) {
00455         return;
00456       } else if (Hnew > Htarget) {
00457         if ((Htop < Htarget) || (Hnew < Htop)) {
00458           Htop = Hnew;
00459           Ttop = Tnew;
00460         } 
00461       } else if (Hnew < Htarget) {
00462         if ((Hbot > Htarget) || (Hnew > Hbot)) {
00463           Hbot = Hnew;
00464           Tbot = Tnew;
00465         }
00466       }
00467       // Convergence in H
00468       double Herr = Htarget - Hnew;
00469       double acpd = MAX(fabs(cpd), 1.0E-5);
00470       double denom = MAX(fabs(Htarget), acpd * dTtol);
00471       double HConvErr = fabs((Herr)/denom);
00472       if (HConvErr < 0.00001 *dTtol) {
00473         return;
00474       }
00475       if (fabs(dt) < dTtol) {
00476         return;
00477       }
00478 
00479     }
00480     // We are here when there hasn't been convergence
00481     /*
00482      * Formulate a detailed error message, since questions seem to
00483      * arise often about the lack of convergence.
00484      */
00485     string ErrString =  "No convergence in 500 iterations\n";
00486     if (doUV) {
00487       ErrString += "\tTarget Internal Energy  = " + fp2str(Htarget) + "\n";
00488       ErrString += "\tCurrent Specific Volume = " + fp2str(v) + "\n";
00489       ErrString += "\tStarting Temperature    = " + fp2str(Tinit) + "\n";
00490       ErrString += "\tCurrent Temperature     = " + fp2str(Tnew) + "\n";
00491       ErrString += "\tCurrent Internal Energy = " + fp2str(Hnew) + "\n";
00492       ErrString += "\tCurrent Delta T         = " + fp2str(dt) + "\n";
00493     } else {
00494       ErrString += "\tTarget Enthalpy         = " + fp2str(Htarget) + "\n";
00495       ErrString += "\tCurrent Pressure        = " + fp2str(p) + "\n";
00496       ErrString += "\tStarting Temperature    = " + fp2str(Tinit) + "\n";
00497       ErrString += "\tCurrent Temperature     = " + fp2str(Tnew) + "\n";
00498       ErrString += "\tCurrent Enthalpy        = " + fp2str(Hnew) + "\n";
00499       ErrString += "\tCurrent Delta T         = " + fp2str(dt) + "\n";
00500     }
00501     if (unstablePhase) {
00502       ErrString += "\t  - The phase became unstable (Cp < 0) T_unstable_last = "
00503         + fp2str(Tunstable) + "\n";
00504     }
00505     if (doUV) {
00506       throw CanteraError("setState_HPorUV (UV)", ErrString);
00507     } else {
00508       throw CanteraError("setState_HPorUV (HP)", ErrString);
00509     }
00510   }
00511 
00512   void ThermoPhase::setState_SP(doublereal Starget, doublereal p, 
00513                                 doublereal dTtol) {
00514     setState_SPorSV(Starget, p, dTtol, false);
00515   }
00516 
00517   void ThermoPhase::setState_SV(doublereal Starget, doublereal v, 
00518                                 doublereal dTtol) {
00519     setState_SPorSV(Starget, v, dTtol, true);
00520   }
00521 
00522   // Do the convergence work for fixed entropy situations
00523   /*
00524    *  We assume here that S at constant P is a monotonically increasing
00525    *  function of T.
00526    *  We assume here that S at constant V is a monotonically increasing
00527    *  function of T.
00528    *  
00529    *  Note, the value of dTtol may become important for some applications
00530    *  where numerical jacobians are being calculated.
00531    */
00532   void ThermoPhase::setState_SPorSV(doublereal Starget, doublereal p, 
00533                                     doublereal dTtol, bool doSV) {
00534     doublereal v = 0.0;
00535     doublereal dt;
00536     if (doSV) {
00537       v = p;
00538       if (v < 1.0E-300) {
00539         throw CanteraError("setState_SPorSV (SV)",
00540                            "Input specific volume is too small or negative. v = " + fp2str(v));
00541       }
00542       setDensity(1.0/v); 
00543     } else {
00544       if (p < 1.0E-300) {
00545         throw CanteraError("setState_SPorSV (SP)",
00546                            "Input pressure is too small or negative. p = " + fp2str(p));
00547       }
00548       setPressure(p);
00549     }
00550     double Tmax = maxTemp() + 0.1;
00551     double Tmin = minTemp() - 0.1;
00552 
00553     // Make sure we are within the temperature bounds at the start
00554     // of the iteration
00555     double Tnew = temperature();
00556     double Tinit = Tnew;
00557     if (Tnew > Tmax) {
00558       Tnew = Tmax - 1.0;
00559       if (doSV) {
00560         setTemperature(Tnew);
00561       } else {
00562         setState_TP(Tnew, p);
00563       }
00564     }
00565     if (Tnew < Tmin) {
00566       Tnew = Tmin + 1.0;
00567       if (doSV) {
00568         setTemperature(Tnew);
00569       } else {
00570         setState_TP(Tnew, p);
00571       }
00572     }
00573 
00574     double Snew = entropy_mass();
00575     double Cpnew = 0.0;
00576     if (doSV) {
00577       Cpnew = cv_mass();
00578     } else {
00579       Cpnew = cp_mass();
00580     }
00581 
00582     double Stop = Snew;
00583     double Ttop = Tnew;
00584     double Sbot = Snew;
00585     double Tbot = Tnew;
00586     double Told = Tnew;
00587     double Sold = Snew;
00588 
00589     bool ignoreBounds = false;
00590     // Unstable phases are those for which
00591     // Cp < 0.0. These are possible for cases where
00592     // we have passed the spinodal curve.
00593     bool unstablePhase = false;
00594     double Tunstable = -1.0;
00595     bool unstablePhaseNew = false;
00596    
00597 
00598     // Newton iteration
00599     for (int n = 0; n < 500; n++) {
00600       Told = Tnew;
00601       Sold = Snew;
00602       double cpd = Cpnew;
00603       if (cpd < 0.0) {
00604         unstablePhase = true;
00605         Tunstable = Tnew;
00606       }
00607       dt = (Starget - Sold)*Told/cpd;
00608 
00609       // limit step size to 200 K
00610       if (dt > 100.0)       dt =  100.0;
00611       else if (dt < -100.0) dt = -100.0; 
00612       Tnew = Told + dt;
00613       // Limit the step size so that we are convergent
00614       if (dt > 0.0) {
00615         if (!unstablePhase) {
00616           if (Stop > Starget) {
00617             if (Tnew > Ttop) {
00618               dt = 0.75 * (Ttop - Told);
00619               Tnew = Told + dt;
00620             }
00621           }
00622         } else {
00623           if (Sbot < Starget) {
00624             if (Tnew < Tbot) {
00625               dt = 0.75 * (Tbot - Told);
00626               Tnew = Told + dt;
00627             }
00628           }
00629         }
00630       } else {
00631         if (!unstablePhase) {
00632           if (Sbot < Starget) {
00633             if (Tnew < Tbot) {
00634               dt = 0.75 * (Tbot - Told);
00635               Tnew = Told + dt;
00636             }
00637           }
00638         } else {
00639           if (Stop > Starget) {
00640             if (Tnew > Ttop) {
00641               dt = 0.75 * (Ttop - Told);
00642               Tnew = Told + dt;
00643             }
00644           }
00645         }
00646       }
00647       // Check Max and Min values
00648       if (Tnew > Tmax) {
00649         if (!ignoreBounds) {
00650           if (doSV) {
00651             setTemperature(Tmax);
00652           } else {
00653             setState_TP(Tmax, p);
00654           }
00655           double Smax = entropy_mass();
00656           if (Smax >= Starget) {
00657             if (Stop < Starget) {
00658               Ttop = Tmax;
00659               Stop = Smax;
00660             }
00661           } else {
00662             Tnew = Tmax + 1.0;
00663             ignoreBounds = true;
00664           }
00665         }
00666       }
00667       if (Tnew < Tmin) {
00668         if (!ignoreBounds) {
00669           if (doSV) {
00670             setTemperature(Tmin);
00671           } else {
00672             setState_TP(Tmin, p);
00673           }
00674           double Smin = enthalpy_mass();
00675           if (Smin <= Starget) {
00676             if (Sbot > Starget) {
00677               Sbot = Tmin;
00678               Sbot = Smin;
00679             }
00680           } else {
00681             Tnew = Tmin - 1.0;
00682             ignoreBounds = true;
00683           }
00684         }
00685       }
00686  
00687       // Try to keep phase within its region of stability
00688       // -> Could do a lot better if I calculate the
00689       //    spinodal value of H.
00690       for (int its = 0; its < 10; its++) {
00691         Tnew = Told + dt;
00692         if (doSV) {
00693           setTemperature(Tnew);
00694           Cpnew = cv_mass();
00695         } else {
00696           setState_TP(Tnew, p);
00697           Cpnew = cp_mass();
00698         }
00699         Snew = entropy_mass();
00700         if (Cpnew < 0.0) {
00701           unstablePhaseNew = true;
00702           Tunstable = Tnew;
00703         } else {
00704           unstablePhaseNew = false;
00705           break;
00706         }
00707         if (unstablePhase == false) {
00708           if (unstablePhaseNew == true) {
00709             dt *= 0.25;
00710           }
00711         }
00712       }
00713 
00714       if (Snew == Starget) {
00715         return;
00716       } else if (Snew > Starget) {
00717         if ((Stop < Starget) || (Snew < Stop)) {
00718           Stop = Snew;
00719           Ttop = Tnew;
00720         } 
00721       } else if (Snew < Starget) {
00722         if ((Sbot > Starget) || (Snew > Sbot)) {
00723           Sbot = Snew;
00724           Tbot = Tnew;
00725         }
00726       }
00727       // Convergence in S
00728       double Serr = Starget - Snew;
00729       double acpd = MAX(fabs(cpd), 1.0E-5);
00730       double denom = MAX(fabs(Starget), acpd * dTtol);
00731       double SConvErr = fabs((Serr * Tnew)/denom);
00732       if (SConvErr < 0.00001 *dTtol) {
00733         return;
00734       }
00735       if (fabs(dt) < dTtol) {
00736         return;
00737       }
00738     }
00739     // We are here when there hasn't been convergence
00740     /*
00741      * Formulate a detailed error message, since questions seem to
00742      * arise often about the lack of convergence.
00743      */
00744     string ErrString =  "No convergence in 500 iterations\n";
00745     if (doSV) {
00746       ErrString += "\tTarget Entropy          = " + fp2str(Starget) + "\n";
00747       ErrString += "\tCurrent Specific Volume = " + fp2str(v) + "\n";
00748       ErrString += "\tStarting Temperature    = " + fp2str(Tinit) + "\n";
00749       ErrString += "\tCurrent Temperature     = " + fp2str(Tnew) + "\n";
00750       ErrString += "\tCurrent Entropy          = " + fp2str(Snew) + "\n";
00751       ErrString += "\tCurrent Delta T         = " + fp2str(dt) + "\n";
00752     } else {
00753       ErrString += "\tTarget Entropy          = " + fp2str(Starget) + "\n";
00754       ErrString += "\tCurrent Pressure        = " + fp2str(p) + "\n";
00755       ErrString += "\tStarting Temperature    = " + fp2str(Tinit) + "\n";
00756       ErrString += "\tCurrent Temperature     = " + fp2str(Tnew) + "\n";
00757       ErrString += "\tCurrent Entropy         = " + fp2str(Snew) + "\n";
00758       ErrString += "\tCurrent Delta T         = " + fp2str(dt) + "\n";
00759     }
00760     if (unstablePhase) {
00761       ErrString += "\t  - The phase became unstable (Cp < 0) T_unstable_last = "
00762         + fp2str(Tunstable) + "\n";
00763     }
00764     if (doSV) {
00765       throw CanteraError("setState_SPorSV (SV)", ErrString);
00766     } else {
00767       throw CanteraError("setState_SPorSV (SP)", ErrString);
00768     }
00769   }
00770 
00771   doublereal ThermoPhase::err(std::string msg) const {
00772     throw CanteraError("ThermoPhase","Base class method "
00773                        +msg+" called. Equation of state type: "+int2str(eosType()));
00774     return 0.0;
00775   }
00776 
00777   /*
00778    * Returns the units of the standard and general concentrations
00779    * Note they have the same units, as their divisor is 
00780    * defined to be equal to the activity of the kth species
00781    * in the solution, which is unitless.
00782    *
00783    * This routine is used in print out applications where the
00784    * units are needed. Usually, MKS units are assumed throughout
00785    * the program and in the XML input files. 
00786    *
00787    * On return uA contains the powers of the units (MKS assumed)
00788    * of the standard concentrations and generalized concentrations
00789    * for the kth species.
00790    *
00791    * The base %ThermoPhase class assigns thedefault quantities
00792    * of (kmol/m3).
00793    * Inherited classes are responsible for overriding the default 
00794    * values if necessary.
00795    *
00796    *  uA[0] = kmol units - default  = 1
00797    *  uA[1] = m    units - default  = -nDim(), the number of spatial
00798    *                                dimensions in the Phase class.
00799    *  uA[2] = kg   units - default  = 0;
00800    *  uA[3] = Pa(pressure) units - default = 0;
00801    *  uA[4] = Temperature units - default = 0;
00802    *  uA[5] = time units - default = 0
00803    */
00804   void ThermoPhase::getUnitsStandardConc(double *uA, int k, int sizeUA) const {
00805     for (int i = 0; i < sizeUA; i++) {
00806       if (i == 0) uA[0] = 1.0;
00807       if (i == 1) uA[1] = -nDim();
00808       if (i == 2) uA[2] = 0.0;
00809       if (i == 3) uA[3] = 0.0;
00810       if (i == 4) uA[4] = 0.0;
00811       if (i == 5) uA[5] = 0.0;
00812     }
00813   }
00814 
00815   /*
00816    * initThermoFile():
00817    *
00818    * Initialization of a phase using an xml file.
00819    *
00820    * This routine is a precursor to initThermoXML(XML_Node*)
00821    * routine, which does most of the work. 
00822    *
00823    * @param infile XML file containing the description of the
00824    *        phase
00825    *
00826    * @param id  Optional parameter identifying the name of the
00827    *            phase. If none is given, the first XML
00828    *            phase element will be used.
00829    */
00830   void ThermoPhase::initThermoFile(std::string inputFile, std::string id) {
00831 
00832     if (inputFile.size() == 0) {
00833       throw CanteraError("ThermoPhase::initThermoFile",
00834                          "input file is null");
00835     }
00836     string path = findInputFile(inputFile);
00837     ifstream fin(path.c_str());
00838     if (!fin) {
00839       throw CanteraError("initThermoFile","could not open "
00840                          +path+" for reading.");
00841     }
00842     /*
00843      * The phase object automatically constructs an XML object.
00844      * Use this object to store information.
00845      */
00846     XML_Node &phaseNode_XML = xml();
00847     XML_Node *fxml = new XML_Node();
00848     fxml->build(fin);
00849     XML_Node *fxml_phase = findXMLPhase(fxml, id);
00850     if (!fxml_phase) {
00851       throw CanteraError("ThermoPhase::initThermo",
00852                          "ERROR: Can not find phase named " +
00853                          id + " in file named " + inputFile);
00854     }
00855     fxml_phase->copy(&phaseNode_XML);   
00856     initThermoXML(*fxml_phase, id);
00857     delete fxml;
00858   }
00859 
00860   /*
00861    *   Import and initialize a ThermoPhase object
00862    *
00863    *   This function is called from importPhase() 
00864    *   after the elements and the
00865    *   species are initialized with default ideal solution
00866    *   level data.
00867    *
00868    * @param phaseNode This object must be the phase node of a
00869    *             complete XML tree
00870    *             description of the phase, including all of the
00871    *             species data. In other words while "phase" must
00872    *             point to an XML phase object, it must have
00873    *             sibling nodes "speciesData" that describe
00874    *             the species in the phase.
00875    * @param id   ID of the phase. If nonnull, a check is done
00876    *             to see if phaseNode is pointing to the phase
00877    *             with the correct id. 
00878    */
00879   void ThermoPhase::initThermoXML(XML_Node& phaseNode, std::string id) {
00880  
00881     /*
00882      * and sets the state
00883      */
00884     if (phaseNode.hasChild("state")) {
00885       XML_Node& stateNode = phaseNode.child("state");
00886       setStateFromXML(stateNode);
00887     }
00888     setReferenceComposition(0);
00889   }
00890 
00891   void ThermoPhase::setReferenceComposition(const doublereal *const x) {
00892     xMol_Ref.resize(m_kk);
00893     if (x) {
00894       for (int k = 0; k < m_kk; k++) {
00895         xMol_Ref[k] = x[k];
00896       }
00897     } else {
00898       getMoleFractions(DATA_PTR(xMol_Ref));
00899     }
00900     double sum = -1.0;
00901     for (int k = 0; k < m_kk; k++) {
00902       sum += xMol_Ref[k];
00903     }
00904     if (fabs(sum) > 1.0E-11) {
00905       throw CanteraError("ThermoPhase::setReferenceComposition",
00906                          "input mole fractions don't sum to 1.0");
00907     }
00908 
00909   }
00910 
00911   void ThermoPhase::getReferenceComposition( doublereal *const x) const {
00912     for (int k = 0; k < m_kk; k++) {
00913       x[k] = xMol_Ref[k];
00914     }
00915   }
00916   
00917   /*
00918    * Initialize. 
00919    *
00920    * This method is provided to allow
00921    * subclasses to perform any initialization required after all
00922    * species have been added. For example, it might be used to
00923    * resize internal work arrays that must have an entry for
00924    * each species.  The base class implementation does nothing,
00925    * and subclasses that do not require initialization do not
00926    * need to overload this method.  When importing a CTML phase
00927    * description, this method is called just prior to returning
00928    * from function importPhase.
00929    *
00930    * @see importCTML.cpp
00931    */
00932   void ThermoPhase::initThermo() {
00933     // Check to see that there is at least one species defined in the phase
00934     if (m_kk <= 0) {
00935       throw CanteraError("ThermoPhase::initThermo()",
00936                          "Number of species is less than or equal to zero");
00937     }
00938     xMol_Ref.resize(m_kk, 0.0);
00939   }
00940 
00941   void ThermoPhase::saveSpeciesData(const int k, const XML_Node* const data) {
00942     if ((int) m_speciesData.size() < (k + 1)) {
00943       m_speciesData.resize(k+1, 0);
00944     }
00945     m_speciesData[k] = new XML_Node(*data);
00946   }
00947 
00948   //! Return a pointer to the XML tree containing the species
00949   /// data for this phase.
00950   const std::vector<const XML_Node *> & ThermoPhase::speciesData() const { 
00951     if ((int) m_speciesData.size() != m_kk) {
00952       throw CanteraError("ThermoPhase::speciesData",
00953                          "m_speciesData is the wrong size");
00954     }
00955     return m_speciesData;
00956   }
00957 
00958   /*
00959    * Set the thermodynamic state.
00960    */
00961   void ThermoPhase::setStateFromXML(const XML_Node& state) {
00962     string comp = getChildValue(state,"moleFractions");
00963     if (comp != "") 
00964       setMoleFractionsByName(comp);
00965     else {
00966       comp = getChildValue(state,"massFractions");
00967       if (comp != "") 
00968         setMassFractionsByName(comp);
00969     }
00970     if (state.hasChild("temperature")) {
00971       double t = getFloat(state, "temperature", "temperature");
00972       setTemperature(t);
00973     }
00974     if (state.hasChild("pressure")) {
00975       double p = getFloat(state, "pressure", "pressure");
00976       setPressure(p);
00977     }
00978     if (state.hasChild("density")) {
00979       double rho = getFloat(state, "density", "density");
00980       setDensity(rho);
00981     }
00982   }
00983 
00984   /*
00985    * Called by function 'equilibrate' in ChemEquil.h to transfer
00986    * the element potentials to this object after every successful
00987    *  equilibration routine.
00988    * The element potentials are storred in their dimensionless
00989    * forms, calculated by dividing by RT.
00990    *    @param lambda vector containing the element potentials.
00991    *           Length = nElements. Units are Joules/kmol.
00992    */
00993   void ThermoPhase::setElementPotentials(const vector_fp& lambda) {
00994     doublereal rrt = 1.0/(GasConstant* temperature());
00995     int mm = nElements();
00996     if (lambda.size() < (size_t) mm) {
00997       throw CanteraError("setElementPotentials", "lambda too small");
00998     }
00999     if (!m_hasElementPotentials) {
01000       m_lambdaRRT.resize(mm);
01001     }
01002     for (int m = 0; m < mm; m++) {
01003       m_lambdaRRT[m] = lambda[m] * rrt;
01004     }
01005     m_hasElementPotentials = true;
01006   }
01007 
01008   /*
01009    * Returns the storred element potentials.
01010    * The element potentials are retrieved from their storred
01011    * dimensionless forms by multiplying by RT.
01012    * @param lambda Vector containing the element potentials.
01013    *        Length = nElements. Units are Joules/kmol.
01014    */
01015   bool ThermoPhase::getElementPotentials(doublereal* lambda) const {
01016     doublereal rt = GasConstant* temperature();
01017     int mm = nElements();
01018     if (m_hasElementPotentials) {
01019       for (int m = 0; m < mm; m++) {
01020         lambda[m] =  m_lambdaRRT[m] * rt;
01021       }
01022     }
01023     return (m_hasElementPotentials);
01024   }
01025 
01026   /*
01027    * Format a summary of the mixture state for output.
01028    */           
01029   std::string ThermoPhase::report(bool show_thermo) const {
01030     char p[800];
01031     string s = "";
01032     try {
01033       if (name() != "") {
01034         sprintf(p, " \n  %s:\n", name().c_str());
01035         s += p;
01036       }
01037       sprintf(p, " \n       temperature    %12.6g  K\n", temperature());
01038       s += p;
01039       sprintf(p, "          pressure    %12.6g  Pa\n", pressure());
01040       s += p;
01041       sprintf(p, "           density    %12.6g  kg/m^3\n", density());
01042       s += p;
01043       sprintf(p, "  mean mol. weight    %12.6g  amu\n", meanMolecularWeight());
01044       s += p;
01045 
01046       doublereal phi = electricPotential();
01047       if (phi != 0.0) {
01048         sprintf(p, "         potential    %12.6g  V\n", phi);
01049         s += p;
01050       }
01051       if (show_thermo) {
01052         sprintf(p, " \n");
01053         s += p;
01054         sprintf(p, "                          1 kg            1 kmol\n");
01055         s += p;
01056         sprintf(p, "                       -----------      ------------\n");
01057         s += p;
01058         sprintf(p, "          enthalpy    %12.6g     %12.4g     J\n", 
01059                 enthalpy_mass(), enthalpy_mole());
01060         s += p;
01061         sprintf(p, "   internal energy    %12.6g     %12.4g     J\n", 
01062                 intEnergy_mass(), intEnergy_mole());
01063         s += p;
01064         sprintf(p, "           entropy    %12.6g     %12.4g     J/K\n", 
01065                 entropy_mass(), entropy_mole());
01066         s += p;
01067         sprintf(p, "    Gibbs function    %12.6g     %12.4g     J\n", 
01068                 gibbs_mass(), gibbs_mole());
01069         s += p;
01070         sprintf(p, " heat capacity c_p    %12.6g     %12.4g     J/K\n", 
01071                 cp_mass(), cp_mole());
01072         s += p;
01073         try {
01074           sprintf(p, " heat capacity c_v    %12.6g     %12.4g     J/K\n", 
01075                   cv_mass(), cv_mole());
01076           s += p;
01077         }
01078         catch(CanteraError) {
01079           sprintf(p, " heat capacity c_v    <not implemented>       \n");
01080           s += p;
01081         }
01082       }
01083 
01084       int kk = nSpecies();
01085       array_fp x(kk);
01086       array_fp y(kk);
01087       array_fp mu(kk);
01088       getMoleFractions(&x[0]);
01089       getMassFractions(&y[0]);
01090       getChemPotentials(&mu[0]);
01091       doublereal rt = GasConstant * temperature(); 
01092       int k;
01093       //if (th.nSpecies() > 1) {
01094 
01095       if (show_thermo) {
01096         sprintf(p, " \n                           X     "
01097                 "            Y          Chem. Pot. / RT    \n");
01098         s += p;
01099         sprintf(p, "                     -------------     "
01100                 "------------     ------------\n");
01101         s += p;
01102         for (k = 0; k < kk; k++) {
01103           if (x[k] > SmallNumber) {
01104             sprintf(p, "%18s   %12.6g     %12.6g     %12.6g\n", 
01105                     speciesName(k).c_str(), x[k], y[k], mu[k]/rt);
01106           }
01107           else {
01108             sprintf(p, "%18s   %12.6g     %12.6g     \n", 
01109                     speciesName(k).c_str(), x[k], y[k]);
01110           }
01111           s += p;
01112         }
01113       }
01114       else {
01115         sprintf(p, " \n                           X"
01116                 "Y\n");
01117         s += p;
01118         sprintf(p, "                     -------------"
01119                 "     ------------\n");
01120         s += p;
01121         for (k = 0; k < kk; k++) {
01122           sprintf(p, "%18s   %12.6g     %12.6g\n", 
01123                   speciesName(k).c_str(), x[k], y[k]);
01124           s += p;
01125         }
01126       }
01127     }
01128     //}
01129     catch (CanteraError) {
01130       ;
01131     }
01132     return s;
01133   }
01134 
01135  
01136 }
Generated by  doxygen 1.6.3