Prerelease_1_6_0_branch merged into trunk

This commit is contained in:
Dave Goodwin
2005-06-18 16:58:39 +00:00
parent 4158c19375
commit 9bd690ae04
295 changed files with 24396 additions and 5446 deletions

View File

@@ -52,17 +52,16 @@ class Kinetics:
p4 = phases[4].thermophase()
if np >= 6:
raise CanteraError("a maximum of 4 neighbor phases allowed")
self.ckin = _cantera.KineticsFromXML(xml_phase,
p0, p1, p2, p3, p4)
for nn in range(self._np):
p = self.phase(nn)
p = phases[nn] # self.phase(nn)
self._phnum[p.thermophase()] = nn
self._end.append(self._end[-1]+p.nSpecies())
for k in range(p.nSpecies()):
self._sp.append(p.speciesName(k))
def __del__(self):
self.clear()
@@ -141,7 +140,10 @@ class Kinetics:
nur = _cantera.kin_rstoichcoeff(self.ckin,k,i)
if nur <> 0.0:
if nur <> 1.0:
s += `int(nur)`+' '
if nur <> round(nur):
s += `nur`+' '
else:
s += `int(nur)`+' '
s += self._sp[k]+' + '
s = s[:-2]
if self.isReversible(i):
@@ -152,7 +154,10 @@ class Kinetics:
nup = _cantera.kin_pstoichcoeff(self.ckin,k,i)
if nup <> 0.0:
if nup <> 1.0:
s += `int(nup)`+' '
if nup <> round(nup):
s += `nup`+' '
else:
s += `int(nup)`+' '
s += self._sp[k]+' + '
s = s[:-2]
return s

View File

@@ -55,7 +55,7 @@ class BurnerFlame(Stack):
t0 = self.burner.temperature()
# get adiabatic flame temperature and composition
gas.equilibrate('HP')
gas.equilibrate('HP',solver=1)
teq = gas.temperature()
yeq = gas.massFractions()
u1 = self.burner.mdot()/gas.density()

View File

@@ -0,0 +1,135 @@
from onedim import *
from Cantera import _cantera
from Cantera.num import array, zeros
class FreeFlame(Stack):
"""A freely-propagating flat flame."""
def __init__(self, gas = None, grid = None, tfix = 500.0):
"""
gas -- object to use to evaluate all gas properties and reaction
rates. Required
grid -- array of initial grid points
A domain of type FreeFlame named 'flame' will be created to
represent the flame. The three domains comprising the stack
are stored as self.inlet, self.flame, and self.outlet.
"""
self.inlet = Inlet('burner')
self.gas = gas
self.inlet.set(temperature = gas.temperature())
self.outlet = Outlet('outlet')
self.pressure = gas.pressure()
# type 2 is Cantera C++ class FreeFlame
self.flame = AxisymmetricFlow('flame',gas = gas,type=2)
self.flame.setupGrid(grid)
Stack.__init__(self, [self.inlet, self.flame, self.outlet])
self.setRefineCriteria()
self._initialized = 0
self.tfix = tfix
def init(self):
"""Set the initial guess for the solution. The adiabatic flame
temperature and equilibrium composition are computed for the
inlet gas composition. The temperature profile rises linearly
in the first 20% of the flame to Tad, then is flat. The mass
fraction profiles are set similarly.
"""
self.getInitialSoln()
gas = self.gas
nsp = gas.nSpecies()
yin = zeros(nsp, 'd')
for k in range(nsp):
yin[k] = self.inlet.massFraction(k)
gas.setState_TPY(self.inlet.temperature(), self.pressure, yin)
u0 = self.inlet.mdot()/gas.density()
t0 = self.inlet.temperature()
# get adiabatic flame temperature and composition
gas.equilibrate('HP',solver=1)
teq = gas.temperature()
yeq = gas.massFractions()
u1 = self.inlet.mdot()/gas.density()
z1 = 0.5
locs = array([0.0, 0.3, z1, 1.0],'d')
self.setProfile('u', locs, [u0, u0, u1, u1])
self.setProfile('T', locs, [t0, t0, teq, teq])
self.setFixedTemperature(self.tfix)
for n in range(nsp):
self.setProfile(gas.speciesName(n), locs, [yin[n], yin[n],
yeq[n], yeq[n]])
self._initialized = 1
def solve(self, loglevel = 1, refine_grid = 1):
"""Solve the flame. See Stack.solve"""
if not self._initialized: self.init()
Stack.solve(self, loglevel = loglevel, refine_grid = refine_grid)
def setRefineCriteria(self, ratio = 10.0, slope = 0.8,
curve = 0.8, prune = 0.0):
"""See Stack.setRefineCriteria"""
Stack.setRefineCriteria(self, domain = self.flame,
ratio = ratio, slope = slope, curve = curve,
prune = prune)
def setFixedTemperature(self, temp):
_cantera.sim1D_setFixedTemperature(self._hndl, temp)
def setProfile(self, component, locs, vals):
"""Set a profile in the flame"""
self._initialized = 1
Stack.setProfile(self, self.flame, component, locs, vals)
def set(self, tol = None, energy = '', tol_time = None):
"""Set parameters.
tol -- (rtol, atol) for steady-state
tol_time -- (rtol, atol) for time stepping
energy -- 'on' or 'off' to enable or disable the energy equation
"""
if tol:
self.flame.setTolerances(default = tol)
if tol_time:
self.flame.setTolerances(default = tol_time, time = 1)
if energy:
self.flame.set(energy = energy)
def T(self, point = -1):
"""Temperature profile or value at one point."""
return self.solution('T', point)
def u(self, point = -1):
"""Axial velocity profile or value at one point."""
return self.solution('u', point)
def V(self, point = -1):
"""Radial velocity profile or value at one point."""
return self.solution('V', point)
def solution(self, component = '', point = -1):
"""Solution component at one point, or full profile if no
point specified."""
if point >= 0: return self.value(self.flame, component, point)
else: return self.profile(self.flame, component)
def setGasState(self, j):
"""Set the state of the object representing the gas to the
current solution at grid point j."""
nsp = self.gas.nSpecies()
y = zeros(nsp, 'd')
for n in range(nsp):
nm = self.gas.speciesName(n)
y[n] = self.solution(nm, j)
self.gas.setState_TPY(self.T(j), self.pressure, y)

View File

@@ -371,12 +371,12 @@ class AxisymmetricFlow(Domain1D):
allowed, as well as arbitrary variation of the transport
properties.
"""
def __init__(self, id = 'axisymmetric_flow', gas = None):
def __init__(self, id = 'axisymmetric_flow', gas = None, type = 1):
Domain1D.__init__(self)
iph = gas.thermo_hndl()
ikin = gas.kinetics_hndl()
itr = gas.transport_hndl()
self._hndl = _cantera.stflow_new(iph, ikin, itr)
self._hndl = _cantera.stflow_new(iph, ikin, itr, type)
if id: self.setID(id)
self._p = -1.0
self.setPressure(gas.pressure())
@@ -676,6 +676,10 @@ class Stack:
"""Set the maximum and minimum time steps."""
return _cantera.sim1D_setTimeStepLimits(self._hndl, tsmin, tsmax)
def setFixedTemperature(self, temp):
"""This is a temporary fix."""
_cantera.sim1D_setFixedTemperature(self._hndl, temp)
def clearDomains():
"""Clear all domains."""
_cantera.domain_clear()

View File

@@ -12,6 +12,14 @@ from exceptions import CanteraError
__revision__ = "$Id$"
# return true is x is a sequence
def _isseq(n, x):
try:
y = x[n-1]
return 1
except:
return 0
class Phase:
"""Phases of matter.
@@ -224,6 +232,10 @@ class Phase:
def setDensity(self, rho):
"""Set the density [kg/m3]."""
_cantera.phase_setfp(self._phase_id,2,rho)
def setMolarDensity(self, n):
"""Set the density [kmol/m3]."""
_cantera.phase_setfp(self._phase_id,3,n)
def setMoleFractions(self, x, norm = 1):
"""Set the mole fractions.
@@ -241,9 +253,11 @@ class Phase:
"""
if type(x) == types.StringType:
_cantera.phase_setstring(self._phase_id,1,x)
else:
elif _isseq(self.nSpecies(), x):
_cantera.phase_setarray(self._phase_id,1,norm,asarray(x))
else:
raise CanteraError('mole fractions must be a string or array')
def setMassFractions(self, x, norm = 1):
"""Set the mass fractions.
@@ -251,8 +265,11 @@ class Phase:
"""
if type(x) == types.StringType:
_cantera.phase_setstring(self._phase_id,2,x)
else:
_cantera.phase_setarray(self._phase_id,2,norm,asarray(x))
elif _isseq(self.nSpecies(), x):
_cantera.phase_setarray(self._phase_id,2,norm,asarray(x))
else:
raise CanteraError('mass fractions must be a string or array')
def setState_TRX(self, t, rho, x):
"""Set the temperature, density, and mole fractions. The mole
@@ -264,6 +281,16 @@ class Phase:
self.setMoleFractions(x)
self.setDensity(rho)
def setState_TNX(self, t, n, x):
"""Set the temperature, molardensity, and mole fractions. The mole
fractions may be entered as a string or array,
>>> ph.setState_TNX(600.0, 2.0e-3, 'CH4:0.4, O2:0.6')
"""
self.setTemperature(t)
self.setMoleFractions(x)
self.setMolarDensity(n)
def setState_TRY(self, t, rho, y):
"""Set the temperature, density, and mass fractions."""
self.setTemperature(t)
@@ -293,7 +320,7 @@ class Phase:
fs.append(f[k])
return asarray(fs)
else:
return f
return asarray(f)
def selectElements(self, f, elements):
"""Given an array 'f' of floating-point element properties,
@@ -310,5 +337,5 @@ class Phase:
fs.append(f[k])
return asarray(fs)
else:
return f
return asarray(f)

View File

@@ -269,7 +269,8 @@ class ThermoPhase(Phase):
"""Set the electric potential."""
_cantera.thermo_setfp(self._phase_id, 6, v, 0);
def equilibrate(self, XY):
def equilibrate(self, XY, solver = 0, rtol = 1.0e-9,
maxsteps = 1000, loglevel = 0):
"""Set to a state of chemical equilibrium holding property pair
'XY' constant. The pair is specified by a two-letter string,
which must be one of the set
@@ -279,7 +280,7 @@ class ThermoPhase(Phase):
"""
ixy = ThermoPhase._equilmap[XY]
if ixy > 0:
_cantera.thermo_equil(self._phase_id, ixy)
_cantera.thermo_equil(self._phase_id, ixy, solver, rtol, maxsteps, loglevel)
else:
raise 'invalid equilibrium option: '+XY

View File

@@ -4,15 +4,13 @@
"""
import types
import _cantera
from constants import *
from exceptions import *
from gases import *
from set import set
from importFromFile import *
from mixture import Mixture
from num import *
def writeCSV(f, list):
"""
Write list items to file 'f' in
@@ -40,17 +38,25 @@ def table(keys, values):
def getCanteraError():
"""Return the Cantera error message, if any."""
import _cantera
return _cantera.get_Cantera_Error()
def refCount(a):
"""Return the reference count for an object."""
import _cantera
return _cantera.ct_refcnt(a)
def addDirectory(dir):
"""Add a directory to search for Cantera data files."""
import _cantera
return _cantera.ct_addDirectory(dir)
def writeLogFile(file):
return _cantera.ct_writelogfile(file)
# workaround for case problems in CVS repository file Mixture.py. On some
# systems it appears as mixture.py, and on others as Mixture.py
try:
from Mixture import Mixture
except:
from mixture import Mixture
from num import *

View File

@@ -0,0 +1,34 @@
import _cantera
"""
Convert a Chemkin-format input file to CTI format.
Parameters:
infile - name of the Chemkin-format input file.
thermodb - Thermodynamic database. This may be a standard
Chemkin-format thermo database, or may be any
Chemkin-format input file containing a THERMO section.
trandb - Transport database. File containing species transport
parameters in Chemkin format. If this argument is omitted,
the CTI file will not contain transport property information.
idtag - ID tag. Used to identify the ideal_gas entry in the CTI file. Optional.
debug - If set to 1, extra debugging output will be written. This
should only be used if ck2cti fails, in order to view
intermediate output of the parser. Default: off (0).
validate - If set to 1, the mechanism will be checked for errors. This
is recommended, but for very large mechanisms may slow down
the conversion process. Default: on (1).
The translated file is written to the standard output.
"""
def ck2cti(infile = "chem.inp", thermodb = "", trandb
= "", idtag = "", debug = 0, validate = 1):
_cantera.ct_ck2cti(infile,
thermodb, trandb, idtag, debug, validate)

View File

@@ -6,7 +6,7 @@ Physical Constants
OneAtm = 101325.0
# The ideal gas constant in J/kmo-K
GasConstant = 8314.0
GasConstant = 8314.47215
# Avogadro's Number, /kmol
Avogadro = 6.022136736e26

View File

@@ -6,10 +6,11 @@ import _cantera
import types
from Cantera.num import zeros, array, asarray
from exceptions import CanteraError
from Cantera import writeLogFile
class Mixture:
"""
xxx Multiphase mixtures. Class Mixture represents
Multiphase mixtures. Class Mixture represents
mixtures of one or more phases of matter. To construct a mixture,
supply a list of phases to the constructor, each paired with the
number of moles for that phase:
@@ -180,14 +181,14 @@ class Mixture:
def setPhaseMoles(self, n, moles):
"""Set the number of moles of phase n."""
_cantera.mix_setPhaseMoles(self.__mixid, n, moles)
def setMoles(self, moles):
def setSpeciesMoles(self, moles):
"""Set the moles of the species [kmol]. The moles may be
specified either as a string, or as an array. If an array is
used, it must be dimensioned at least as large as the total
number of species in the mixture. Note that the species may
belong to any phase, and unspecified species are set to zero.
>>> mix.setMoles('C(s):1.0, CH4:2.0, O2:0.2')
>>> mix.setSpeciesMoles('C(s):1.0, CH4:2.0, O2:0.2')
"""
if type(moles) == types.StringType:
@@ -197,7 +198,7 @@ class Mixture:
def speciesMoles(self, species = ""):
"""Moles of species k."""
moles = array(self.nSpecies(),'d')
moles = zeros(self.nSpecies(),'d')
for k in range(self.nSpecies()):
moles[k] = _cantera.mix_speciesMoles(self.__mixid, k)
return self.selectSpecies(moles, species)
@@ -227,30 +228,64 @@ class Mixture:
else:
raise CanteraError("unknown property: "+o)
def equilibrate(self, XY = "TP", err = 1.0e-9, maxiter = 1000):
def equilibrate(self, XY = "TP", err = 1.0e-9,
maxsteps = 1000, maxiter = 200, loglevel = 0):
"""Set the mixture to a state of chemical equilibrium.
This method uses the VCS algorithm to find the composition
that minimizes the total Gibbs free energy of the mixture,
subject to element conservation constraints. For a description
of the theory, see Smith and Missen, "Chemical Reaction
Equilibrium." The VCS algorithm is implemented in Cantera
kernel class MultiPhaseEquil.
This method uses a version of the VCS algorithm to find the
composition that minimizes the total Gibbs free energy of the
mixture, subject to element conservation constraints. For a
description of the theory, see Smith and Missen, "Chemical
Reaction Equilibrium." The VCS algorithm is implemented in
Cantera kernel class MultiPhaseEquil.
The VCS algorithm solves for the equilibrium composition for
specified temperature and pressure. If any other property pair
other than "TP" is specified, then an outer iteration loop is
used to adjust T and/or P so that the specified property
values are obtained.
XY - Two-letter string specifying the two properties to hold fixed.
Currently, only TP (constant T and P) is implemented. Default: "TP".
Currently, 'TP', 'HP', and 'SP' are implemented. Default: 'TP'.
err - Error tolerance. Iteration will continue until (Delta
mu)/RT is less than this value for each reaction. Default:
1.0e-9.
1.0e-9. Note that this default is very conservative, and good
equilibrium solutions may be obtained with larger error
tolerances.
maxiter - Maximum number of iterations to attempt. Default: 1000.
maxsteps - Maximum number of steps to take while solving the
equilibrium problem for specified T and P. Default: 1000.
maxiter - Maximum number of temperature and/or pressure iterations.
This is only relevant if a property pair other than (T,P) is
specified. Default: 200.
loglevel - Controls the amount of diagnostic output. If
loglevel = 0, no diagnostic output is written. For values > 0,
more detailed information is written to the log file as
loglevel increases. The default is loglevel = 0.
The logfile is written in HTML format, and may be viewed with
any web browser. The default log file name is
"equilibrium_log.html", but if this file exists, the log
information will be written to "equilibrium_log{n}.html",
where {n} is an integer chosen so that the log file does not
already exist. Therefore, if 'equilibrate' is called multiple
times, multiple log files will be written, with names
"equilibrate_log.html", "equilibrate_log1.html",
"equilibrate_log2.html", and so on. Existing log files will
not be overwritten.
>>> mix.equilibrate('TP')
>>> mix.equilibrate('TP', err = 1.0e-6, maxiter = 500)
"""
return _cantera.mix_equilibrate(self.__mixid, XY, err, maxiter)
i = _cantera.mix_equilibrate(self.__mixid, XY, err, maxsteps,
maxiter, loglevel)
if loglevel > 0:
writeLogFile("equilibrate_log");
def selectSpecies(self, f, species):
"""Given an array 'f' of floating-point species properties,
@@ -263,12 +298,12 @@ class Mixture:
sp = []
if species:
if type(species) == types.StringType:
sp = [sp]
sp = [species]
else:
sp = species
fs = []
k = 0
for s in s:
for s in sp:
k = self.speciesIndex(s)
fs.append(f[k])
return asarray(fs)

View File

@@ -1,26 +1,27 @@
import _cantera
nummodule = None
try:
import numarray
nummodule = numarray
except:
try:
if _cantera.nummod == 'numarray':
import numarray
nummodule = numarray
else:
import Numeric
nummodule = Numeric
except:
print """
except:
print """
ERROR: numarray / numeric not found!
ERROR: """+_cantera.nummod+""" not found!
Cantera uses a set of numerical extensions to Python, but these do
not appear to be present on your system. To install the required
package, go to http://sourceforge.net/projects/numpy, and install
either the numarray or Numeric package for your system. If you are
either the """+_cantera.nummod+""" package for your system. If you are
using a Windows system, use the binary installer to install the
selected package for you automatically.
"""
raise "could not import numarray or Numeric"
raise "could not import "+_cantera.nummod
zeros = nummodule.zeros
array = nummodule.array

View File

@@ -15,7 +15,10 @@ python_site_package_topdir=@python_prefix@
CANTERA_LIBDIR= @buildlib@
LIB_DEPS = $(CANTERA_LIBDIR)/libcantera.a $(CANTERA_LIBDIR)/libzeroD.a \
$(CANTERA_LIBDIR)/liboneD.a \
$(CANTERA_LIBDIR)/libtransport.a
$(CANTERA_LIBDIR)/libtransport.a \
$(CANTERA_LIBDIR)/libclib.a \
$(CANTERA_LIBDIR)/libconverters.a
WIN_LIB_DEPS = $(CANTERA_LIBDIR)/cantera.lib $(CANTERA_LIBDIR)/zeroD.lib \
$(CANTERA_LIBDIR)/oneD.lib \
$(CANTERA_LIBDIR)/transport.lib
@@ -40,7 +43,7 @@ all: _build
win: _winbuild
_build: $(SRCS) $(LIB_DEPS)
_build: $(SRCS) $(LIB_DEPS) Makefile
touch src/pycantera.cpp
(CXX=@CXX@; export CXX; CC=@CC@; export CC; @PYTHON_CMD@ setup.py build)
echo 'ok' > _build

View File

@@ -199,17 +199,25 @@ _pref = 1.0e5 # 1 bar
_name = 'noname'
# these lists store top-level entries
_elements = []
_species = []
_speciesnames = []
_phases = []
_reactions = []
_atw = {}
_enames = {}
_valsp = ''
_valrxn = ''
_valexport = ''
_valfmt = ''
def export_species(file, fmt = 'CSV'):
global _valexport
global _valfmt
_valexport = file
_valfmt = fmt
def validate(species = 'yes', reactions = 'yes'):
global _valsp
global _valrxn
@@ -265,7 +273,12 @@ def write():
v = x.addChild("validate")
v["species"] = _valsp
v["reactions"] = _valrxn
if _elements:
ed = x.addChild("elementData")
for e in _elements:
e.build(ed)
for ph in _phases:
ph.build(x)
s = species_set(name = _name, species = _species)
@@ -281,6 +294,11 @@ def write():
else:
print x
if _valexport:
f = open(_valexport,'w')
for s in _species:
s.export(f, _valfmt)
f.close()
def addFloat(x, nm, val, fmt='', defunits=''):
"""
@@ -320,20 +338,65 @@ def getAtomicComp(atoms):
return d
def getReactionSpecies(s):
"""Take a reaction string and return a
dictionary mapping species names to stoichiometric
coefficients. If any species appears more than once,
the returned stoichiometric coefficient is the sum.
>>> s = 'CH3 + 3 H + 5.2 O2 + 0.7 H'
>>> getReactionSpecies(s)
>>> {'CH3':1, 'H':3.7, 'O2':5.2}
"""
# get rid of the '+' signs separating species. Only plus signs
# surrounded by spaces are replaced, so that plus signs may be
# used in species names (e.g. 'Ar3+')
toks = s.replace(' + ',' ').split()
d = {}
n = 1
n = 1.0
for t in toks:
if t > '0' and t < '9':
n = int(t)
else:
if d.has_key(t):
d[t] += n
# try to convert the token to a number.
try:
n = float(t)
if n <= 0.0:
raise CTI_Error("zero or negative stoichiometric coefficient:"
+s)
#if t > '0' and t < '9':
# n = int(t)
#else:
# token isn't a number, so it must be a species name
except:
if d.has_key(t): # already seen this token
d[t] += n # so increment its value by the last
# value of n
else:
d[t] = n
n = 1
d[t] = n # first time this token has been seen,
# so set its value to n
n = 1 # reset n to 1.0 for species that do not
# specify a stoichiometric coefficient
return d
class element:
def __init__(self, symbol = '',
atomic_mass = 0.01,
atomic_number = 0):
self._sym = symbol
self._atw = atomic_mass
self._num = atomic_number
global _elements
_elements.append(self)
def build(self, db):
e = db.addChild("element")
e["name"] = self._sym
e["atomicWt"] = `self._atw`
e["atomicNumber"] = `self._num`
class species_set:
def __init__(self, name = '', species = []):
self._s = species
@@ -384,12 +447,33 @@ class species:
# self.type = SPECIES
global _species
global _enames
_species.append(self)
global _speciesnames
if name in _speciesnames:
raise CTI_Error('species '+name+' multiply defined.')
_speciesnames.append(name)
for e in self._atoms.keys():
_enames[e] = 1
def export(self, f, fmt = 'CSV'):
global _enames
if fmt == 'CSV':
str = self._name+','
for e in _enames:
if self._atoms.has_key(e):
str += `self._atoms[e]`+','
else:
str += '0,'
f.write(str)
if type(self._thermo) == types.InstanceType:
self._thermo.export(f, fmt)
else:
nt = len(self._thermo)
for n in range(nt):
self._thermo[n].export(f, fmt)
f.write('\n')
def build(self, p):
hdr = ' species '+self._name+' '
@@ -425,6 +509,8 @@ class thermo:
"""Base class for species standard-state thermodynamic properties."""
def _build(self, p):
return p.addChild("thermo")
def export(self, f, fmt = 'CSV'):
pass
class NASA(thermo):
@@ -438,7 +524,14 @@ class NASA(thermo):
raise CTI_Error('NASA coefficient list must have length = 7')
self._coeffs = coeffs
def export(self, f, fmt='CSV'):
if fmt == 'CSV':
str = 'NASA,'+`self._t[0]`+','+`self._t[1]`+','
for i in range(7):
str += '%17.9E, ' % self._coeffs[i]
f.write(str)
def build(self, t):
n = t.addChild("NASA")
n['Tmin'] = `self._t[0]`
@@ -786,7 +879,6 @@ class reaction:
unit_fctr = (math.pow(_length[_ulen], -ldim) *
math.pow(_moles[_umol], -mdim) / _time[_utime])
if type(kf) == types.InstanceType:
k = kf
else:
@@ -1320,6 +1412,64 @@ class incompressible_solid(phase):
k['model'] = 'none'
class lattice:
def __init__(self, name = '', site_density = -1.0,
vacancy_species = ''):
self._name = name
self._n = site_density
self._vac = vacancy_species
if name == '':
raise CTI_Error('sublattice name must be specified')
if site_density < 0.0:
raise CTI_Error('sublattice '+name
+' site density must be specified')
def build(self,p):
lat = p.addChild('Lattice')
lat['name'] = self._name
addFloat(lat, 'site_density', self._n, defunits = _umol+'/'+_ulen+'3')
if self._vac:
lat.addChild('vacancy_species',self._vac)
class lattice_solid(phase):
"""A solid crystal consisting of one or more sublattices."""
def __init__(self,
name = '',
elements = '',
species = '',
lattices = [],
transport = 'None',
initial_state = None,
options = []):
phase.__init__(self, name, 3, elements, species, 'none',
initial_state, options)
self._lattices = lattices
if lattices == []:
raise CTI_Error('One or more sublattices must be specified.')
self._pure = 0
self._tr = transport
def conc_dim(self):
return (0,0)
def build(self, p):
ph = phase.build(self, p)
e = ph.addChild("thermo")
e['model'] = 'LatticeSolid'
if self._lattices:
lat = e.addChild('LatticeArray')
for n in self._lattices:
n.build(lat)
if self._tr:
t = ph.addChild('transport')
t['model'] = self._tr
k = ph.addChild("kinetics")
k['model'] = 'none'
class liquid_vapor(phase):
"""A fluid with a complete liquid/vapor equation of state.
This entry type selects one of a set of predefined fluids with
@@ -1485,105 +1635,3 @@ if __name__ == "__main__":
execfile(file)
write()
##########################################
#
# $Author$
# $Revision$
# $Date$
# $Log$
# Revision 1.9 2005-01-08 22:28:01 dggoodwin
# *** empty log message ***
#
# Revision 1.8 2004/12/02 22:11:28 dggoodwin
# *** empty log message ***
#
# Revision 1.7 2004/11/15 02:33:21 dggoodwin
# changed f90 mod file handling in Makefiles
#
# Revision 1.6 2004/09/29 11:00:39 dggoodwin
# *** empty log message ***
#
# Revision 1.5 2004/09/20 10:25:02 dggoodwin
# *** empty log message ***
#
# Revision 1.4 2004/07/14 11:24:13 dggoodwin
# *** empty log message ***
#
# Revision 1.3 2004/06/09 01:02:31 dggoodwin
# *** empty log message ***
#
# Revision 1.2 2004/06/04 06:05:31 dggoodwin
# *** empty log message ***
#
# Revision 1.1 2004/06/02 04:39:09 dggoodwin
# moved ctml_writer.py out of Cantera package
#
# Revision 1.34 2004/05/30 04:02:55 dggoodwin
# converted to pure python
#
# Revision 1.33 2004/04/23 19:03:21 dggoodwin
# *** empty log message ***
#
# Revision 1.32 2004/04/23 16:35:32 dggoodwin
# *** empty log message ***
#
# Revision 1.31 2004/03/12 05:59:59 dggoodwin
# *** empty log message ***
#
# Revision 1.30 2004/02/08 13:25:21 dggoodwin
# *** empty log message ***
#
# Revision 1.29 2004/02/08 13:22:31 dggoodwin
# *** empty log message ***
#
# Revision 1.28 2004/02/08 13:09:10 dggoodwin
# *** empty log message ***
#
# Revision 1.27 2004/02/03 16:42:54 dggoodwin
# *** empty log message ***
#
# Revision 1.26 2004/02/03 03:31:06 dggoodwin
# *** empty log message ***
#
# Revision 1.25 2003/11/24 16:39:33 dggoodwin
# -
#
# Revision 1.24 2003/11/13 12:29:45 dggoodwin
# *** empty log message ***
#
# Revision 1.23 2003/11/12 18:58:15 dggoodwin
# *** empty log message ***
#
# Revision 1.22 2003/11/01 04:48:20 dggoodwin
# added capability to have species names with embedded commas
#
# Revision 1.21 2003/10/14 06:48:07 dggoodwin
# *** empty log message ***
#
# Revision 1.20 2003/09/22 13:14:34 dggoodwin
# *** empty log message ***
#
# Revision 1.19 2003/08/26 03:39:02 dggoodwin
# *** empty log message ***
#
# Revision 1.18 2003/08/21 14:29:53 dggoodwin
# *** empty log message ***
#
# Revision 1.17 2003/08/20 15:35:32 dggoodwin
# *** empty log message ***
#
# Revision 1.16 2003/08/19 22:02:01 hkmoffa
# Fixed an error in an argument list
#
# Revision 1.15 2003/08/18 05:05:02 dggoodwin
# added support for specified reaction order, sticking coefficients,
# coverage dependence of rate coefficients; fixed error where site_density
# was not being converted to SI.
#
# Revision 1.14 2003/08/16 20:17:21 dggoodwin
# changed handling of reaction pre-exponential units to write converted
# value to CTML, rather than pass original value with a units string
#
#
###########################################

View File

@@ -0,0 +1,70 @@
units(length = "cm", time = "s", quantity = "mol", act_energy = "cal/mol")
# ideal gas containing all gaseous species in the NASA database
# that contain the elements K, O, and H. "Element" E is included
# too for charged species.
ideal_gas(name = "KOH_plasma",
elements = " K O H E",
species = """nasa_gas: all""",
options = ["skip_undeclared_elements"],
initial_state = state(temperature = 300.0,
pressure = OneAtm) )
# solid potassium
stoichiometric_solid(name = "K_solid",
elements = "K",
density = (0.86,'g/cm3'),
species = "nasa_condensed: K(cr)")
# liquid potassium
stoichiometric_liquid(name = "K_liquid",
elements = "K",
density = (1.0,'g/cm3'),
species = "nasa_condensed: K(L)")
# potassium hydroxide "a"
stoichiometric_solid(name = "KOH_a",
elements = "K O H",
density = (2.04,'g/cm3'),
species = "nasa_condensed: KOH(a)")
# potassium hydroxide "b"
stoichiometric_solid(name = "KOH_b",
elements = "K O H",
density = (1.0,'g/cm3'),
species = "nasa_condensed: KOH(b)")
# liquid potassium hydroxide
stoichiometric_liquid(name = "KOH_liquid",
elements = "K O H",
density = (1.0,'g/cm3'),
species = "nasa_condensed: KOH(L)")
stoichiometric_solid(name = "K2O2_solid",
elements = "K O",
density = (1.0,'g/cm3'),
species = "nasa_condensed: K2O2(s)")
stoichiometric_solid(name = "K2O_solid",
elements = "K O",
density = (1.0,'g/cm3'),
species = "nasa_condensed: K2O(s)")
stoichiometric_solid(name = "KO2_solid",
elements = "K O",
density = (1.0,'g/cm3'),
species = "nasa_condensed: KO2(s)")
stoichiometric_solid(name = "ice",
elements = "H O",
density = (0.917,'g/cm3'),
species = "nasa_condensed: H2O(s)")
stoichiometric_liquid(name = "liquid_water",
elements = "H O",
density = (1.0,'g/cm3'),
species = "nasa_condensed: H2O(L)")

View File

@@ -0,0 +1,100 @@
"""
Adiabatic flame temperature and equilibrium composition for a
fuel/air mixture as a function of equivalence ratio,
including formation of solid carbon.
"""
from Cantera import *
import sys
##############################################################
#
# Edit these parameters to change the initial temperature, the
# pressure, and the phases in the mixture
#
###############################################################
temp = 300.0
pres = 101325.0
# phases
gas = importPhase('gri30.cti')
carbon = importPhase('graphite.cti')
# the phases that will be included in the calculation, and their
# initial moles
mix_phases = [ (gas, 1.0), (carbon, 0.0) ]
# gaseous fuel species
fuel_species = 'CH4'
# air composition
air_N2_O2_ratio = 3.76
# equivalence ratio range
phi_min = 0.3
phi_max = 3.5
npoints = 50
##################################################
mix = Mixture(mix_phases)
nsp = mix.nSpecies()
# create some arrays to hold the data
phi = zeros(npoints,'d')
tad = zeros(npoints,'d')
xeq = zeros([nsp,npoints],'d')
# find fuel, nitrogen, and oxygen indices
ifuel, io2, in2 = gas.speciesIndex([fuel_species, 'O2', 'N2'])
if ifuel < 0:
raise "fuel species "+fuel_species+" not present!"
if gas.nAtoms(fuel_species,'O') > 0 or gas.nAtoms(fuel_species,'N') > 0:
raise "Error: only hydrocarbon fuels are supported."
stoich_O2 = gas.nAtoms(fuel_species,'C') + 0.25*gas.nAtoms(fuel_species,'H')
for i in range(npoints):
phi[i] = phi_min + (phi_max - phi_min)*i/(npoints - 1)
x = zeros(nsp,'d')
x[ifuel] = phi[i]
x[io2] = stoich_O2
x[in2] = stoich_O2*air_N2_O2_ratio
# set the gas state
gas.set(T = temp, P = pres, X = x)
# create a mixture of 1 mole of gas, and 0 moles of solid carbon.
mix = Mixture(mix_phases)
mix.setTemperature(temp)
mix.setPressure(pres)
# equilibrate the mixture adiabatically at constant P
mix.equilibrate('HP', maxsteps = 1000,
err = 1.0e-6, maxiter = 200, loglevel=0)
tad[i] = mix.temperature();
print 'At phi = %12.4g, Tad = %12.4g' % (phi[i],tad[i])
xeq[:,i] = mix.speciesMoles()
# write output CSV file for importing into Excel
csvfile = 'adiabatic.csv'
f = open(csvfile,'w')
writeCSV(f,['T (K)']+mix.speciesNames())
for n in range(npoints):
writeCSV(f,[phi[n], tad[n]]+list(xeq[:,n]))
f.close()
print 'output written to '+csvfile
# make plots
if '--plot' in sys.argv:
import plotting
plotting.plotEquilData(mix, phi, tad, xeq)

View File

@@ -0,0 +1,40 @@
# An equilibrium example with charged species in the gas phase
# and multiple condensed phases.
from Cantera import *
# create objects representing the gas phase and the condensed
# phases. The gas is a mixture of multiple species, and the condensed
# phases are all modeled as incompressible stoichiometric
# substances. See file KOH.cti for more information.
phases = importPhases('KOH.cti',
['K_solid',
'K_liquid', 'KOH_a', 'KOH_b',
'KOH_liquid', 'K2O2_solid',
'K2O_solid', 'KO2_solid',
'ice', 'liquid_water','KOH_plasma'])
# create the Mixture object from the list of phases
mix = Mixture(phases)
# open the output file and write the column headings
f = open('equil_koh.csv','w')
writeCSV(f,['T']+mix.speciesNames())
# loop over temperature
for n in range(500):
t = 350.0 + 10.0*n
print 'T = ',t
mix.set(T= t, P = OneAtm, Moles="K:1.03, H2:2.12, O2:0.9")
# set the mixture to a state of chemical equilibrium holding
# temperature and pressure fixed
mix.equilibrate("TP",maxsteps=1000,loglevel=0)
# write out the moles of each species
writeCSV(f,[t]+ list(mix.speciesMoles()))
# close the output file
f.close()

View File

@@ -0,0 +1,85 @@
from matplotlib.pylab import *
from matplotlib import get_backend, interactive
import sys
# Print a warning if 'python' rather than 'pythonw' is invoked on a
# Mac. Does nothing on other platforms.
def warnMac():
if sys.platform == 'darwin':
if sys.executable == '/usr/bin/python':
b = get_backend()
if b[-3:] == 'Agg':
print 'Error: on a Mac, this script must be run with pythonw instead of python'
print 'to display plots'
return -1
return 0
def plotEquilData(mix, phi, tad, xeq):
if warnMac() < 0: return
npoints = len(phi)
nsp = mix.nSpecies()
#titles = ['Major Species', 'Minor Species', 'N Minor Species']
# assign species to 3 plots
p = {}
mm = 0
for k in range(nsp):
if amax(xeq[k,:]) > 0.01:
p[k] = 0 # major species plot
else:
mm += 1
if mix.nAtoms(k,'N') <= 0:
p[k] = 1 # non-N minor species plot
else:
p[k] = 2 # N-containing minor species plot
clf
subplot(2,2,1)
plot(phi,tad)
xlabel('Equivalence Ratio')
ylabel('Temperature (K)')
title('Adiabatic Flame Temperature')
axis([phi[1], phi[-1], amin(tad)-200.0, amax(tad)+200.0])
# do three species plots
for m in range(3):
subplot(2,2,2+m);
hold(True);
for i in range(nsp):
if p[i] == m:
for j in range(npoints):
if xeq[i,j] <= 0.0:
xeq[i,j] = 1.0e-20
if m == 0:
plot(phi, xeq[i,:])
else:
semilogy(phi,xeq[i,:])
xmax = amax(xeq[i,:])
for j in range(npoints):
if xeq[i,j] == xmax: break
offset = 0.0
if j == 0:
offset = 0.3
elif j >= npoints-1:
offset = -0.3
text(phi[j]+offset,xeq[i,j],mix.speciesName(i))
if m == 0:
axis([phi[1], phi[-1], 0.0, 1.0]);
else:
axis([phi[1], phi[-1], 1.0e-14, 1]);
xlabel('Equivalence Ratio');
ylabel('Mole Fraction');
#title(titles[m]);
hold(False)
show()

View File

@@ -0,0 +1,23 @@
# homogeneous equilibrium of a gas
from Cantera import *
# create an object representing the gas phase
gas = importPhase("h2o2.cti")
# set the initial state
gas.set(T = 2000.0, P = 0.1*OneAtm, X = "H2:1.0, O2:0.6")
# equilibrate the gas holding T and P fixed
gas.equilibrate("TP")
# print a summary of the results
print gas
# Individual properties can also be retrieved...
x = gas.moleFractions()
y = gas.massFractions()
mu = gas.chemPotentials()
names = gas.speciesNames()
for n in range(gas.nSpecies()):
print "%20s %10.4g %10.4g %10.4g " % (names[n], x[n], y[n], mu[n])

View File

@@ -0,0 +1,61 @@
# Equilibrium of a (nearly) stoichiometric hydrogen/oxygen mixture at
# fixed temperature.
# Cantera has 2 different equilibrium solvers. The 'ChemEquil' solver
# uses the element potential method for homogeneous equilibrium in gas
# mixtures. It is fast, but sometimes doesn't converge. The
# 'MultiPhaseEquil' solver uses the VCS algorithm (Gibbs
# minimization), which is slower but more robust. As the name
# suggests, it can also handle multiple phases. Here we'll solve a
# problem for which the ChemEquil solver fails, but the
# MultiPhaseEquil solver has no problem.
from Cantera import *
# create an object representing the gas phase
gas = importPhase("h2o2.cti")
temp = 400.0
# make the composition very close to stoichiometric
comp = "H2:1.00000001, O2:0.5"
# set the initial state
gas.set(T = temp, P = OneAtm, X = comp)
# equilibrate the gas holding T and P fixed. First try the default
# (ChemEquil) solver... (This will fail, throwing an exception that
# will be caught in the 'except' block, where we will try the other
# solver.)
try:
gas.equilibrate("TP")
except:
print "ChemEquil solver failed! Try the MultiPhaseEquil solver..."
# Try again. Reset the gas to the initial state
gas.set(T = temp, P = OneAtm, X = comp)
# setting parameter 'solver' to 1 requests that the
# MultiPhaseEquil solver be used (specifying 0 would cause
# ChemEquil to be used). Some other useful parameters are rtol
# (relative error tolerance, default = 1.0e-9), max_steps (default = 1000),
# loglevel (default = 0).
gas.equilibrate("TP", solver = 1, rtol = 1.0e-10, loglevel = 4)
# print a summary of the results
print gas
# To check that this is an equilibrium state, verify that the chemical
# potentials may be computed by summing the element potentials for each atom.
# (The element potentials are the chemical potentials of the atomic vapors.)
mu_H2, mu_OH, mu_H2O, mu_O2, lambda_H, lambda_O = gas.chemPotentials(
["H2", "OH", "H2O", "O2", "H", "O"])
print mu_H2, 2.0*lambda_H
print mu_O2, 2.0*lambda_O
print mu_OH, lambda_H + lambda_O
print mu_H2O, 2.0*lambda_H + lambda_O

View File

@@ -55,7 +55,7 @@ f.burner.set(massflux = mdot, mole_fractions = comp, temperature = tburner)
f.set(tol = tol_ss, tol_time = tol_ts)
f.setMaxJacAge(5, 10)
f.set(energy = 'off')
f.init()
#f.init()
f.showSolution()
f.solve(loglevel, refine_grid)

View File

@@ -0,0 +1,84 @@
#
# ADIABATIC_FLAME - A freely-propagating, premixed methane/air flat
# flame with multicomponent transport properties
#
from Cantera import *
from Cantera.OneD import *
from Cantera.OneD.FreeFlame import FreeFlame
################################################################
#
# parameter values
#
p = OneAtm # pressure
tin = 300.0 # unburned gas temperature
mdot = 0.04 # kg/m^2/s
comp = 'CH4:0.45, O2:1, N2:3.76' # premixed gas composition
initial_grid = [0.0, 0.001, 0.01, 0.02, 0.029, 0.03] # m
tol_ss = [1.0e-5, 1.0e-9] # [rtol atol] for steady-state
# problem
tol_ts = [1.0e-5, 1.0e-4] # [rtol atol] for time stepping
loglevel = 1 # amount of diagnostic output (0
# to 5)
refine_grid = 1 # 1 to enable refinement, 0 to
# disable
gas = GRI30('Mix')
gas.addTransportModel('Multi')
# set its state to that of the unburned gas
gas.setState_TPX(tin, p, comp)
f = FreeFlame(gas = gas, grid = initial_grid, tfix = 600.0)
# set the upstream properties
f.inlet.set(mole_fractions = comp, temperature = tin)
f.set(tol = tol_ss, tol_time = tol_ts)
f.showSolution()
f.set(energy = 'off')
f.setRefineCriteria(ratio = 10.0, slope = 1, curve = 1)
f.setMaxJacAge(50, 50)
f.setTimeStep(1.0e-5, [1, 2, 5, 10, 20])
f.solve(loglevel, refine_grid)
f.save('ch4_adiabatic.xml','no_energy',
'solution with the energy equation disabled')
f.set(energy = 'on')
f.setRefineCriteria(ratio = 3.0, slope = 0.1, curve = 0.2)
f.solve(loglevel, refine_grid)
f.save('ch4_adiabatic.xml','energy',
'solution with the energy equation enabled')
print 'mixture-averaged flamespeed = ',f.u()[0]
gas.switchTransportModel('Multi')
f.flame.setTransportModel(gas)
f.solve(loglevel, refine_grid)
f.save('ch4_adiabatic.xml','energy_multi',
'solution with the energy equation enabled and multicomponent transport')
# write the velocity, temperature, density, and mole fractions to a CSV file
z = f.flame.grid()
T = f.T()
u = f.u()
V = f.V()
fcsv = open('adiabatic_flame.csv','w')
writeCSV(fcsv, ['z (m)', 'u (m/s)', 'V (1/s)', 'T (K)', 'rho (kg/m3)']
+ list(gas.speciesNames()))
for n in range(f.flame.nPoints()):
f.setGasState(n)
writeCSV(fcsv, [z[n], u[n], V[n], T[n], gas.density()]
+list(gas.moleFractions()))
fcsv.close()
print 'solution saved to adiabatic_flame.csv'
print 'multicomponent flamespeed = ',u[0]
f.showStats()

View File

@@ -0,0 +1,88 @@
#
# FLAME1 - A burner-stabilized flat flame
#
# This script simulates a burner-stablized lean hydrogen-oxygen flame
# at low pressure.
#
from Cantera import *
from Cantera.OneD import *
from Cantera.OneD.BurnerFlame import BurnerFlame
################################################################
#
# parameter values
#
p = 0.05*OneAtm # pressure
tburner = 373.0 # burner temperature
mdot = 0.06 # kg/m^2/s
rxnmech = 'h2o2.cti' # reaction mechanism file
mix = 'ohmech' # gas mixture model
comp = 'H2:1.8, O2:1, AR:7' # premixed gas composition
# The solution domain is chosen to be 50 cm, and a point very near the
# downstream boundary is added to help with the zero-gradient boundary
# condition at this boundary.
initial_grid = [0.0, 0.02, 0.04, 0.06, 0.08, 0.1,
0.15, 0.2, 0.4, 0.49, 0.5] # m
tol_ss = [1.0e-5, 1.0e-13] # [rtol atol] for steady-state
# problem
tol_ts = [1.0e-4, 1.0e-9] # [rtol atol] for time stepping
loglevel = 1 # amount of diagnostic output (0
# to 5)
refine_grid = 1 # 1 to enable refinement, 0 to
# disable
################ create the gas object ########################
#
# This object will be used to evaluate all thermodynamic, kinetic,
# and transport properties
#
gas = IdealGasMix(rxnmech, mix)
# set its state to that of the unburned gas at the burner
gas.set(T = tburner, P = p, X = comp)
f = BurnerFlame(gas = gas, grid = initial_grid)
# set the properties at the burner
f.burner.set(massflux = mdot, mole_fractions = comp, temperature = tburner)
f.set(tol = tol_ss, tol_time = tol_ts)
f.setMaxJacAge(5, 10)
f.set(energy = 'off')
f.init()
f.showSolution()
f.solve(loglevel, refine_grid)
f.setRefineCriteria(ratio = 200.0, slope = 0.05, curve = 0.1)
f.set(energy = 'on')
f.solve(loglevel,refine_grid)
f.save('flame1.xml')
f.showSolution()
# write the velocity, temperature, and mole fractions to a CSV file
z = f.flame.grid()
T = f.T()
u = f.u()
V = f.V()
fcsv = open('flame1.csv','w')
writeCSV(fcsv, ['z (m)', 'u (m/s)', 'V (1/s)', 'T (K)', 'rho (kg/m3)']
+ list(gas.speciesNames()))
for n in range(f.flame.nPoints()):
f.setGasState(n)
writeCSV(fcsv, [z[n], u[n], V[n], T[n], gas.density()]
+list(gas.moleFractions()))
fcsv.close()
print 'solution saved to flame1.csv'
f.showStats()

View File

@@ -0,0 +1,97 @@
#
# FLAME2 - A burner-stabilized, premixed methane/air flat flame
# with multicomponent transport properties
#
from Cantera import *
from Cantera.OneD import *
from Cantera.OneD.BurnerFlame import BurnerFlame
################################################################
#
# parameter values
#
p = OneAtm # pressure
tburner = 373.7 # burner temperature
mdot = 0.04 # kg/m^2/s
comp = 'CH4:0.65, O2:1, N2:3.76' # premixed gas composition
# The solution domain is chosen to be 1 cm, and a point very near the
# downstream boundary is added to help with the zero-gradient boundary
# condition at this boundary.
initial_grid = [0.0, 0.0025, 0.005, 0.0075, 0.0099, 0.01] # m
tol_ss = [1.0e-5, 1.0e-9] # [rtol atol] for steady-state
# problem
tol_ts = [1.0e-5, 1.0e-4] # [rtol atol] for time stepping
loglevel = 1 # amount of diagnostic output (0
# to 5)
refine_grid = 1 # 1 to enable refinement, 0 to
# disable
################ create the gas object ########################
#
# This object will be used to evaluate all thermodynamic, kinetic, and
# transport properties. It is created with two transport managers, to
# enable switching from mixture-averaged to multicomponent transport
# on the last solution.
gas = GRI30('Mix')
gas.addTransportModel('Multi')
# set its state to that of the unburned gas at the burner
gas.setState_TPX(tburner, p, comp)
f = BurnerFlame(gas = gas, grid = initial_grid)
# set the properties at the burner
f.burner.set(massflux = mdot, mole_fractions = comp, temperature = tburner)
f.set(tol = tol_ss, tol_time = tol_ts)
f.showSolution()
f.set(energy = 'off')
f.setRefineCriteria(ratio = 10.0, slope = 1, curve = 1)
f.setMaxJacAge(50, 50)
f.setTimeStep(1.0e-5, [1, 2, 5, 10, 20])
f.solve(loglevel, refine_grid)
f.save('ch4_flame1.xml','no_energy',
'solution with the energy equation disabled')
f.set(energy = 'on')
f.setRefineCriteria(ratio = 3.0, slope = 0.1, curve = 0.2)
f.solve(loglevel, refine_grid)
f.save('ch4_flame1.xml','energy',
'solution with the energy equation enabled')
gas.switchTransportModel('Multi')
f.flame.setTransportModel(gas)
f.solve(loglevel, refine_grid)
f.save('ch4_flame1.xml','energy_multi',
'solution with the energy equation enabled and multicomponent transport')
# write the velocity, temperature, density, and mole fractions to a CSV file
z = f.flame.grid()
T = f.T()
u = f.u()
V = f.V()
fcsv = open('flame2.csv','w')
writeCSV(fcsv, ['z (m)', 'u (m/s)', 'V (1/s)', 'T (K)', 'rho (kg/m3)']
+ list(gas.speciesNames()))
for n in range(f.flame.nPoints()):
f.setGasState(n)
writeCSV(fcsv, [z[n], u[n], V[n], T[n], gas.density()]
+list(gas.moleFractions()))
fcsv.close()
print 'solution saved to flame2.csv'
f.showStats()

View File

@@ -0,0 +1,117 @@
#
# STFLAME1 - A detached flat flame stabilized at a stagnation point
#
# This script simulates a lean hydrogen-oxygen flame stabilized in
# a strained flowfield at an axisymmetric stagnation point on a
# non-reacting surface. The solution begins with a flame attached
# to the inlet (burner), and the mass flow rate is progressively
# increased, causing the flame to detach and move closer to the
# surface. This example illustrates use of the new 'prune' grid
# refinement parameter, which allows grid points to be removed if
# they are no longer required to resolve the solution. This is
# important here, since the flamefront moves as the mass flowrate
# is increased. Without using 'prune', a large number of grid
# points would be concentrated upsteam of the flame, where the
# flamefront had been previously. (To see this, try setting prune
# to zero.)
from Cantera import *
from Cantera.OneD import *
from Cantera.OneD.StagnationFlow import StagnationFlow
################################################################
#
# parameter values
#
p = 0.05*OneAtm # pressure
tburner = 373.0 # burner temperature
tsurf = 600.0
# each mdot value will be solved to convergence, with grid refinement,
# and then that solution will be used for the next mdot
mdot = [0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12] # kg/m^2/s
rxnmech = 'h2o2.cti' # reaction mechanism file
comp = 'H2:1.8, O2:1, AR:7' # premixed gas composition
# The solution domain is chosen to be 50 cm, and a point very near the
# downstream boundary is added to help with the zero-gradient boundary
# condition at this boundary.
initial_grid = [0.0, 0.02, 0.04, 0.06, 0.08, 0.1,
0.15, 0.2] # m
tol_ss = [1.0e-5, 1.0e-13] # [rtol atol] for steady-state
# problem
tol_ts = [1.0e-4, 1.0e-9] # [rtol atol] for time stepping
loglevel = 1 # amount of diagnostic output (0
# to 5)
refine_grid = 1 # 1 to enable refinement, 0 to
# disable
ratio = 5.0
slope = 0.1
curve = 0.2
prune = 0.05
################ create the gas object ########################
#
# This object will be used to evaluate all thermodynamic, kinetic,
# and transport properties
#
gas = IdealGasMix(rxnmech)
# set its state to that of the unburned gas at the burner
gas.setState_TPX(tburner, p, comp)
# Create the stagnation flow object with a non-reactive surface. (To
# make the surface reactive, supply a surface reaction mechanism. see
# example catcomb.py for how to do this.)
f = StagnationFlow(gas = gas, grid = initial_grid)
# set the properties at the inlet
f.inlet.set(massflux = mdot[0], mole_fractions = comp, temperature = tburner)
# set the surface state
f.surface.setTemperature(tsurf)
f.set(tol = tol_ss, tol_time = tol_ts)
f.setMaxJacAge(5, 10)
f.set(energy = 'off')
f.init(products = 'equil') # assume adiabatic equilibrium products
f.showSolution()
f.solve(loglevel, refine_grid)
f.setRefineCriteria(ratio = ratio, slope = slope,
curve = curve, prune = prune)
f.set(energy = 'on')
m = 0
for md in mdot:
f.inlet.set(mdot = md)
f.solve(loglevel,refine_grid)
m = m + 1
f.save('stflame1.xml','mdot'+`m`,'mdot = '+`md`+' kg/m2/s')
# write the velocity, temperature, and mole fractions to a CSV file
z = f.flow.grid()
T = f.T()
u = f.u()
V = f.V()
fcsv = open('stflame1_'+`m`+'.csv','w')
writeCSV(fcsv, ['z (m)', 'u (m/s)', 'V (1/s)', 'T (K)']
+ list(gas.speciesNames()))
for n in range(f.flow.nPoints()):
f.setGasState(n)
writeCSV(fcsv, [z[n], u[n], V[n], T[n]]+list(gas.moleFractions()))
fcsv.close()
print 'solution saved to flame1.csv'
f.showStats()

View File

@@ -0,0 +1,48 @@
# print forward and reverse rate coefficients for all reactions
from Cantera import *
def show_rate_coefficients(mech, doIrrev = 0):
""" Print the rate coefficients for a reaction mechanism. The
rate coefficients include all factors that multiply the reactant
concentrations in the law of mass action. The rate coefficients
may depend on temperature and/or pressure, and are evaluated at
the temperature and pressure of the object 'mech.'
If doIrrev has a non-zero value, then the reverse rate coefficients
will be computed for all reactions, including irreversible
ones. Otherwise, the rate coefficients for irreversible reactions
will be set to zero. """
# get the array of forward rate coefficients
kf = mech.fwdRateConstants()
# get the array of reverse rate coefficients
kr = mech.revRateConstants(doIrreversible = doIrrev)
nr = mech.nReactions()
# get the array of reaction equations
eqs = mech.reactionEqn(range(nr))
for i in range(nr):
print '%40s %12.5g %12.5g ' % (eqs[i], kf[i], kr[i])
print 'units: kmol, m, s'
if __name__ == "__main__":
import sys
if len(sys.argv) == 2:
mech = importPhase(sys.argv[1])
elif len(sys.argv) > 2:
mech = importPhase(sys.argv[1], sys.argv[2])
else:
mech = GRI30()
show_rate_coefficients(mech)

View File

@@ -10,10 +10,21 @@ platform = sys.platform
flibs = '@FLIBS@'
#linkargs = '@LCXX_FLAGS@'
numarray_incl = "@NUMARRAY_INC_DIR@"
incdirs=["../../build/include", "src", "../clib/src"]
if numarray_incl <> '':
incdirs.append(numarray_incl)
bllibstr = "@BLAS_LAPACK_LIBS@"
bllibs = bllibstr.replace('-l',' ')
bllist = bllibs.split()
extra_link = "@EXTRA_LINK@"
linkargs = extra_link.split()
bldirstr = "@LOCAL_LIB_DIRS@"
bldirs = bldirstr.replace('-L',' ')
dirlist = bldirs.split()
@@ -23,7 +34,7 @@ for d in dirlist:
if platform == "win32":
libs = ["clib", "zeroD","oneD","transport",
"cantera","recipes"] + bllist + ["ctmath", "cvode", "tpx"]
"cantera","recipes"] + bllist + ["ctmath", "cvode", "tpx", "converters"]
else:
# The library 'g2c' is needed if the g77 Fortran compiler is
@@ -32,40 +43,54 @@ else:
# compiler is used, add the appropriate libraries here.
libs = ["clib", "zeroD","oneD","transport",
"cantera","recipes"] + bllist + ["ctmath", "cvode", "tpx",
"cantera","recipes", "converters"] + bllist + ["ctmath", "cvode", "tpx",
"stdc++", "g2c", "m"]
# values:
# 0 do nothing
# 1 install only ctml_writer.py
# 2 install full package
# 3 try to install full, but install ctml_writer if full package
# install fails
buildPython = @BUILD_PYTHON@
if buildPython >= 2:
if @BUILD_PYTHON@ == 2:
try:
setup(name="Cantera",
version="@ctversion@",
description="The Cantera Python Interface",
long_description="""
""",
author="Prof. D. G. Goodwin, Caltech",
author_email="dgoodwin@caltech.edu",
url="http://www.cantera.org",
package_dir = {'MixMaster':'../../apps/MixMaster'},
packages = ["","Cantera","Cantera.OneD",
"MixMaster","MixMaster.Units"],
ext_modules=[ Extension("Cantera._cantera",
["src/pycantera.cpp"],
include_dirs=incdirs,
library_dirs = libdir,
libraries = libs,
extra_link_args = linkargs
)
],
)
except:
buildPython = 1
setup(name="Cantera",
version="@ctversion@",
description="The Cantera Python Interface",
long_description="""
""",
author="Prof. D. G. Goodwin, Caltech",
author_email="dgoodwin@caltech.edu",
url="http://www.cantera.org",
package_dir = {'MixMaster':'../../apps/MixMaster'},
packages = ["","Cantera","Cantera.OneD",
"MixMaster","MixMaster.Units"],
ext_modules=[ Extension("Cantera._cantera",
["src/pycantera.cpp"],
include_dirs=["../../build/include",
"src", "../clib/src"],
library_dirs = libdir,
libraries = libs)
],
)
else:
setup(name="Cantera CTI File Processor",
version="@ctversion@",
description="Converts .cti files to CTML",
long_description="""
""",
author="Prof. D. G. Goodwin, Caltech",
author_email="dgoodwin@caltech.edu",
url="http://www.cantera.org",
py_modules = ["ctml_writer"],
)
if buildPython == 1:
try:
setup(name="Cantera CTI File Processor",
version="@ctversion@",
description="Converts .cti files to CTML",
long_description="""
""",
author="Prof. D. G. Goodwin, Caltech",
author_email="dgoodwin@caltech.edu",
url="http://www.cantera.org",
py_modules = ["ctml_writer"],
)
except:
raise 'Error encountered while building or installing the Cantera CTI file preprocessor!'

View File

@@ -1,2 +0,0 @@
EXPORTS
initcantera

View File

@@ -1,2 +0,0 @@
EXPORTS
initctphase

View File

@@ -1,2 +0,0 @@
EXPORTS
initctflow

View File

@@ -67,18 +67,33 @@ ct_addDirectory(PyObject *self, PyObject *args)
// return Py_BuildValue("s","");
//}
// static PyObject *
// ct_ck2cti(PyObject *self, PyObject *args)
// {
// int iok;
// char *infile, *thermo, *tran, *idtag;
// if (!PyArg_ParseTuple(args, "ssss:ck2cti", &infile,
// &thermo, &tran, &idtag))
// return NULL;
// iok = ck_to_cti(infile, thermo, tran, idtag);
// if (iok == -1) { return reportCanteraError();}
// return Py_BuildValue("i",iok);
// }
static PyObject *
ct_ck2cti(PyObject *self, PyObject *args)
{
int iok;
char *infile, *thermo, *tran, *idtag;
int debug, validate;
if (!PyArg_ParseTuple(args, "ssssii:ck2cti", &infile,
&thermo, &tran, &idtag, &debug, &validate))
return NULL;
iok = ck_to_cti(infile, thermo, tran, idtag, debug, validate);
if (iok == -1) { return reportCanteraError();}
return Py_BuildValue("i",iok);
}
static PyObject *
ct_writelogfile(PyObject *self, PyObject *args)
{
int iok;
char *logfile;
if (!PyArg_ParseTuple(args, "s:ck2cti", &logfile))
return NULL;
iok = writelogfile(logfile);
if (iok == -1) { return reportCanteraError();}
return Py_BuildValue("i",iok);
}

View File

@@ -1,2 +0,0 @@
EXPORTS
initctkinetics

View File

@@ -265,11 +265,12 @@ py_mix_equilibrate(PyObject *self, PyObject *args)
int i;
char* XY;
double err;
int maxiter;
if (!PyArg_ParseTuple(args, "isdi:mix_equilibrate", &i, &XY, &err, &maxiter))
int maxsteps, maxiter, loglevel;
if (!PyArg_ParseTuple(args, "isdiii:mix_equilibrate", &i, &XY, &err,
&maxsteps, &maxiter, &loglevel))
return NULL;
_val = mix_equilibrate(i,XY,err,maxiter);
_val = mix_equilibrate(i,XY,err,maxsteps,maxiter,loglevel);
if (int(_val) < -900) return reportCanteraError();
return Py_BuildValue("d",_val);
}

View File

@@ -1,2 +0,0 @@
EXPORTS
initctnumerics

View File

@@ -475,10 +475,11 @@ py_stflow_new(PyObject *self, PyObject *args)
int iph;
int ikin;
int itr;
if (!PyArg_ParseTuple(args, "iii:stflow_new", &iph, &ikin, &itr))
int itype;
if (!PyArg_ParseTuple(args, "iiii:stflow_new", &iph, &ikin, &itr, &itype))
return NULL;
_val = stflow_new(iph,ikin,itr);
_val = stflow_new(iph,ikin,itr,itype);
if (int(_val) == -1) return reportCanteraError();
return Py_BuildValue("i",_val);
}
@@ -943,3 +944,17 @@ py_sim1D_setTimeStepLimits(PyObject *self, PyObject *args)
return Py_BuildValue("i",_val);
}
static PyObject *
py_sim1D_setFixedTemperature(PyObject *self, PyObject *args)
{
int _val;
int i;
double temp;
if (!PyArg_ParseTuple(args, "id:sim1D_setFixedTemperature", &i, &temp))
return NULL;
_val = sim1D_setFixedTemperature(i,temp);
if (int(_val) == -1) return reportCanteraError();
return Py_BuildValue("i",_val);
}

View File

@@ -1,2 +0,0 @@
EXPORTS
initctphase

View File

@@ -139,7 +139,7 @@ phase_getarray(PyObject *self, PyObject *args)
break;
case 22:
iok = phase_getMolecularWeights(ph,nsp,xd);
break;
break;
default:
;
}
@@ -222,6 +222,8 @@ phase_setfp(PyObject *self, PyObject *args)
iok = phase_setTemperature(ph, vv); break;
case 2:
iok = phase_setDensity(ph, vv); break;
case 3:
iok = phase_setMolarDensity(ph, vv); break;
default:
iok = -10;
}

View File

@@ -1,2 +0,0 @@
EXPORTS
initctsurf

View File

@@ -1,2 +0,0 @@
EXPORTS
initctthermo

View File

@@ -1,4 +1,4 @@
static PyObject *
ct_newThermoFromXML(PyObject *self, PyObject *args)
{
@@ -232,11 +232,16 @@ thermo_equil(PyObject *self, PyObject *args)
int iok = -2;
int th;
int XY;
if (!PyArg_ParseTuple(args, "ii:thermo_equil", &th, &XY))
int solver;
double rtol;
int maxsteps;
int loglevel;
if (!PyArg_ParseTuple(args, "iiidii:thermo_equil", &th, &XY,
&solver, &rtol, &maxsteps, &loglevel))
return NULL;
iok = th_equil(th, XY);
iok = th_equil(th, XY, solver, rtol, maxsteps, loglevel);
if (iok >= 0)
return Py_BuildValue("i",iok);
if (iok == -1) return reportCanteraError();

View File

@@ -1,2 +0,0 @@
EXPORTS
initcttransport

View File

@@ -86,6 +86,8 @@ static PyMethodDef ct_methods[] = {
//{"ct_print", ct_print, METH_VARARGS},
{"ct_addDirectory", ct_addDirectory, METH_VARARGS},
{"ct_refcnt", ct_refcnt, METH_VARARGS},
{"ct_ck2cti", ct_ck2cti, METH_VARARGS},
{"ct_writelogfile", ct_writelogfile, METH_VARARGS},
//{"readlog", ct_readlog, METH_VARARGS},
//{"buildSolutionFromXML", ct_buildSolutionFromXML, METH_VARARGS},
@@ -150,6 +152,7 @@ static PyMethodDef ct_methods[] = {
{"sim1D_setMaxJacAge", py_sim1D_setMaxJacAge, METH_VARARGS},
{"sim1D_timeStepFactor", py_sim1D_timeStepFactor, METH_VARARGS},
{"sim1D_setTimeStepLimits", py_sim1D_setTimeStepLimits, METH_VARARGS},
{"sim1D_setFixedTemperature", py_sim1D_setFixedTemperature, METH_VARARGS},
{"surf_setsitedensity", py_surf_setsitedensity, METH_VARARGS},
{"surf_sitedensity", py_surf_sitedensity, METH_VARARGS},

View File

@@ -11,7 +11,7 @@
#pragma warning(disable:4503)
#endif
#include "../../../config.h"
#include "../../src/config.h"
#include "Python.h"
@@ -42,7 +42,7 @@ static PyObject *ErrorObject;
#include "ctphase_methods.cpp"
#include "ctthermo_methods.cpp"
#include "ctkinetics_methods.cpp"
#include "ctkinetics_methods.cpp"
#include "cttransport_methods.cpp"
#include "ctxml_methods.cpp"
#include "ctfuncs.cpp"
@@ -84,6 +84,11 @@ extern "C" {
d = PyModule_GetDict(m);
ErrorObject = PyErr_NewException("cantera.error", NULL, NULL);
PyDict_SetItemString(d, "error", ErrorObject);
#ifdef HAS_NUMERIC
PyDict_SetItemString(d, "nummod",PyString_FromString("Numeric"));
#else
PyDict_SetItemString(d, "nummod",PyString_FromString("numarray"));
#endif
}
}

View File

@@ -11,6 +11,8 @@ static std::string ss = "print \"\"\" ";
namespace Cantera {
/// Logger for Python.
/// @ingroup textlogs
class Py_Logger : public Logger {
public:
Py_Logger() {}

View File

@@ -2,17 +2,15 @@
#define CTPY_UTILS
#include "Python.h"
//#include "../../clib/src/ct.h"
static PyObject* reportCanteraError() {
//showCanteraErrors();
char* buf = 0;
int buflen = getCanteraError(0, buf) + 1;
buf = new char[buflen+1];
getCanteraError(buflen, buf);
PyErr_SetString(ErrorObject,buf);
delete buf;
//PyErr_SetString(ErrorObject,"An exception was thrown by Cantera.");
//writelogfile("log.html");
return NULL;
}
@@ -24,119 +22,5 @@ static PyObject* reportError(int n) {
return NULL;
}
// template<class V>
// int pyNumericSequence_ToVector(PyObject* seq, V& vec)
// {
// if (!PySequence_Check(seq)) return -1;
// int n = PySequence_Size(seq);
// vec.resize(n);
// PyObject* item;
// double x;
// for (int i = 0; i < n; i++) {
// item = PySequence_GetItem(seq, i);
// if (PyFloat_Check(item))
// x = PyFloat_AsDouble(item);
// else if (PyInt_Check(item))
// x = 1.0*PyInt_AsLong(item);
// else
// return -2;
// vec[i] = x;
// }
// return 1;
// }
// template<class V>
// PyObject* pyNumericTuple_FromVector(const V& vec)
// {
// int n = vec.size();
// PyObject* seq = PyTuple_New(n);
// PyObject* item;
// for (int i = 0; i < n; i++) {
// item = PyFloat_FromDouble(1.0*vec[i]);
// if (PyTuple_SetItem(seq, i, item) < 0) return NULL;
// }
// return seq;
// }
// template<class V>
// PyObject* pyNumericList_FromVector(const V& vec)
// {
// int n = vec.size();
// PyObject* seq = PyList_New(n);
// PyObject* item;
// for (int i = 0; i < n; i++) {
// item = PyFloat_FromDouble(1.0*vec[i]);
// if (PyList_SetItem(seq, i, item) < 0) return NULL;
// }
// return seq;
// }
// template<class V>
// PyObject* pyStringTuple_FromVector(const V& vec)
// {
// int n = vec.size();
// PyObject* seq = PyTuple_New(n);
// PyObject* item;
// for (int i = 0; i < n; i++) {
// item = PyString_FromString(vec[i].c_str());
// if (PyTuple_SetItem(seq, i, item) < 0) return NULL;
// }
// return seq;
// }
// template<class M>
// int pyStrNumMapping_ToMap(PyObject* d, M& m)
// {
// if (!PyMapping_Check(d)) return -1;
// int n = PyMapping_Length(d);
// PyObject *keys, *values, *ky, *v;
// double x;
// string key;
// keys = PyMapping_Keys(d);
// values = PyMapping_Values(d);
// for (int i = 0; i < n; i++) {
// ky = PySequence_GetItem(keys, i);
// v = PySequence_GetItem(values, i);
// key = string(PyString_AsString(ky));
// if (PyFloat_Check(v))
// x = PyFloat_AsDouble(v);
// else if (PyInt_Check(v))
// x = 1.0*PyInt_AsLong(v);
// else
// return -2;
// m[key] = x;
// }
// return 1;
// }
// template<class M>
// PyObject* pyStrNumMapping_FromMap(M& m)
// {
// if (!PyMapping_Check(d)) return -1;
// int n = m.size();
// double value;
// char* key;
// PyObject* x;
// TYPENAME_KEYWORD M::const_iter ptr;
// PyObject* map = PyDict_New();
// for (int i = 0; i < n; i++) {
// key = ptr->first.c_str();
// value = ptr->second;
// x = PyFloat_FromDouble(value);
// PyMapping_SetItemString(map, key, x);
// }
// return map;
// }
// static int lengthError() {
// PyErr_SetString(PyExc_AttributeError,
// "wrong length for sequence");
// return -1;
// }
#endif

View File

@@ -3,7 +3,7 @@ from distutils.core import setup, Extension
libdir = ['../../build/lib/i686-pc-win32']
setup(name="Cantera",
version="1.5.5",
version="1.6.0",
description="The Cantera Python Interface",
long_description="""
""",