[Cython] Translated more samples to use use the new API

This commit is contained in:
Ray Speth 2013-02-07 23:41:30 +00:00
parent 7da738d238
commit 840bdfffab
8 changed files with 774 additions and 0 deletions

View file

@ -0,0 +1,94 @@
"""
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.)
"""
import cantera as ct
import numpy as np
import os
# parameter values
p = 0.05 * ct.one_atm # pressure
tburner = 373.0 # burner temperature
tsurf = 500.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 = np.linspace(0.0, 0.2, 12) # 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 = True
# Grid refinement parameters
ratio = 3
slope = 0.1
curve = 0.2
prune = 0.06
# Set up the problem
gas = ct.Solution(rxnmech)
# set state to that of the unburned gas at the burner
gas.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
# catalytic_combustion.py for how to do this.)
sim = ct.ImpingingJet(gas=gas, grid=initial_grid)
# set the properties at the inlet
sim.inlet.mdot = mdot[0]
sim.inlet.X = comp
sim.inlet.T = tburner
# set the surface state
sim.surface.T = tsurf
sim.flame.set_steady_tolerances(default=tol_ss)
sim.flame.set_transient_tolerances(default=tol_ts)
sim.set_grid_min(1e-4)
sim.energy_enabled = False
sim.set_initial_guess(products='equil') # assume adiabatic equilibrium products
sim.show_solution()
sim.solve(loglevel, refine_grid)
sim.set_refine_criteria(ratio=ratio, slope=slope, curve=curve, prune=prune)
sim.energy_enabled = True
outfile = 'stflame1.xml'
if os.path.exists(outfile):
os.remove(outfile)
for m,md in enumerate(mdot):
sim.inlet.mdot = md
sim.solve(loglevel,refine_grid)
sim.save(outfile, 'mdot{}'.format(m), 'mdot = {} kg/m2/s'.format(md))
# write the velocity, temperature, and mole fractions to a CSV file
sim.write_csv('stflame1_{}.csv'.format(m), quiet=False)
sim.show_stats()

View file

@ -0,0 +1,92 @@
"""
A combustor. Two separate stream - one pure methane and the other air, both at
300 K and 1 atm flow into an adiabatic combustor where they mix. We are
interested in the steady-state burning solution. Since at 300 K no reaction
will occur between methane and air, we need to use an 'igniter' to initiate
the chemistry. A simple igniter is a pulsed flow of atomic hydrogen. After the
igniter is turned off, the system approaches the steady burning solution.
"""
import math
import csv
import cantera as ct
# use reaction mechanism GRI-Mech 3.0
gas = ct.Solution('gri30.xml')
# create a reservoir for the fuel inlet, and set to pure methane.
gas.TPX = 300.0, ct.one_atm, 'CH4:1.0'
fuel_in = ct.Reservoir(gas)
fuel_mw = gas.mean_molecular_weight
# use predefined function Air() for the air inlet
air = ct.Solution('air.xml')
air_in = ct.Reservoir(air)
air_mw = air.mean_molecular_weight
# to ignite the fuel/air mixture, we'll introduce a pulse of radicals. The
# steady-state behavior is independent of how we do this, so we'll just use a
# stream of pure atomic hydrogen.
gas.TPX = 300.0, ct.one_atm, 'H:1.0'
igniter = ct.Reservoir(gas)
# create the combustor, and fill it in initially with N2
gas.TPX = 300.0, ct.one_atm, 'N2:1.0'
combustor = ct.Reactor(gas)
combustor.volume = 1.0
# create a reservoir for the exhaust
exhaust = ct.Reservoir(gas)
# lean combustion, phi = 0.5
equiv_ratio = 0.5
# compute fuel and air mass flow rates
factor = 0.1
air_mdot = factor * 9.52 * air_mw
fuel_mdot = factor * equiv_ratio * fuel_mw
# create and install the mass flow controllers. Controllers m1 and m2 provide
# constant mass flow rates, and m3 provides a short Gaussian pulse only to
# ignite the mixture
m1 = ct.MassFlowController(fuel_in, combustor, mdot=fuel_mdot)
# note that this connects two reactors with different reaction mechanisms and
# different numbers of species. Downstream and upstream species are matched by
# name.
m2 = ct.MassFlowController(air_in, combustor, mdot=air_mdot)
# The igniter will use a Gaussian time-dependent mass flow rate.
fwhm = 0.2
amplitude = 0.1
t0 = 1.0
igniter_mdot = lambda t: amplitude * math.exp(-(t-t0)**2 * 4 * math.log(2) / fwhm**2)
m3 = ct.MassFlowController(igniter, combustor, mdot=igniter_mdot)
# put a valve on the exhaust line to regulate the pressure
v = ct.Valve(combustor, exhaust, K=1.0)
# the simulation only contains one reactor
sim = ct.ReactorNet([combustor])
# take single steps to 6 s, writing the results to a CSV file for later
# plotting.
tfinal = 6.0
tnow = 0.0
Tprev = combustor.T
tprev = tnow
outfile = open('combustor.csv','w')
csvwriter = csv.writer(outfile)
while tnow < tfinal:
tnow = sim.step(tfinal)
tres = combustor.mass/v.mdot(tnow)
Tnow = combustor.T
if abs(Tnow - Tprev) > 1.0 or tnow-tprev > 2e-2:
tprev = tnow
Tprev = Tnow
csvwriter.writerow([tnow, combustor.T, tres] +
list(combustor.thermo.X))
outfile.close()

View file

@ -0,0 +1,72 @@
"""
Mixing two streams.
Since reactors can have multiple inlets and outlets, they can be used to
implement mixers, splitters, etc. In this example, air and methane are mixed
in stoichiometric proportions. Due to the low temperature, no reactions occur.
Note that the air stream and the methane stream use *different* reaction
mechanisms, with different numbers of species and reactions. When gas flows
from one reactor or reservoir to another one with a different reaction
mechanism, species are matched by name. If the upstream reactor contains a
species that is not present in the downstream reaction mechanism, it will be
ignored. In general, reaction mechanisms for downstream reactors should
contain all species that might be present in any upstream reactor.
"""
import cantera as ct
# Use air for stream a.
gas_a = ct.Solution('air.xml')
gas_a.TPX = 300.0, ct.one_atm, 'O2:0.21, N2:0.78, AR:0.01'
rho_a = gas_a.density
# Use GRI-Mech 3.0 for stream b (methane) and for the mixer. If it is desired
# to have a pure mixer, with no chemistry, use instead a reaction mechanism
# for gas_b that has no reactions.
gas_b = ct.Solution('gri30.xml')
gas_b.TPX = 300.0, ct.one_atm, 'CH4:1'
rho_b = gas_b.density
# Create reservoirs for the two inlet streams and for the outlet stream. The
# upsteam reservoirs could be replaced by reactors, which might themselves be
# connected to reactors further upstream. The outlet reservoir could be
# replaced with a reactor with no outlet, if it is desired to integrate the
# composition leaving the mixer in time, or by an arbitrary network of
# downstream reactors.
res_a = ct.Reservoir(gas_a)
res_b = ct.Reservoir(gas_b)
downstream = ct.Reservoir(gas_b)
# Create a reactor for the mixer. A reactor is required instead of a
# reservoir, since the state will change with time if the inlet mass flow
# rates change or if there is chemistry occurring.
gas_b.TPX = 300.0, ct.one_atm, 'O2:0.21, N2:0.78, AR:0.01'
mixer = ct.Reactor(gas_b)
# create two mass flow controllers connecting the upstream reservoirs to the
# mixer, and set their mass flow rates to values corresponding to
# stoichiometric combustion.
mfc1 = ct.MassFlowController(res_a, mixer, mdot=rho_a*2.5/0.21)
mfc2 = ct.MassFlowController(res_b, mixer, mdot=rho_b*1.0)
# connect the mixer to the downstream reservoir with a valve.
outlet = ct.Valve(mixer, downstream, K=10.0)
sim = ct.ReactorNet([mixer])
# Since the mixer is a reactor, we need to integrate in time to reach steady
# state. A few residence times should be enough.
print('{:>14s} {:>14s} {:>14s} {:>14s} {:>14s}'.format(
't [s]', 'T [K]', 'h [J/kg]', 'P [Pa]', 'X_CH4'))
t = 0.0
for n in range(30):
tres = mixer.mass/(mfc1.mdot(t) + mfc2.mdot(t))
t += 0.5*tres
sim.advance(t)
print('{:14.5g} {:14.5g} {:14.5g} {:14.5g} {:14.5g}'.format(
t, mixer.T, mixer.thermo.h, mixer.thermo.P, mixer.thermo['CH4'].X[0]))
# view the state of the gas in the mixer
print(mixer.thermo.report())

View file

@ -0,0 +1,90 @@
"""
Gas 1: a stoichiometric H2/O2/Ar mixture
Gas 2: a wet CO/O2 mixture
-------------------------------------
| || |
| || |
| gas 1 || gas 2 |
| || |
| || |
-------------------------------------
The two volumes are connected by an adiabatic free piston. The piston speed is
proportional to the pressure difference between the two chambers.
Note that each side uses a *different* reaction mechanism
"""
import sys
import cantera as ct
fmt = '%10.3f %10.1f %10.4f %10.4g %10.4g %10.4g %10.4g'
print('%10s %10s %10s %10s %10s %10s %10s' % ('time [s]','T1 [K]','T2 [K]',
'V1 [m^3]', 'V2 [m^3]',
'V1+V2 [m^3]','X(CO)'))
gas1 = ct.Solution('h2o2.cti')
gas1.TPX = 900.0, ct.one_atm, 'H2:2, O2:1, AR:20'
gas2 = ct.Solution('gri30.xml')
gas2.TPX = 900.0, ct.one_atm, 'CO:2, H2O:0.01, O2:5'
r1 = ct.Reactor(gas1)
r1.volume = 0.5
r2 = ct.Reactor(gas2)
r2.volume = 0.1
w = ct.Wall(r1, r2, K=1.0e3)
net = ct.ReactorNet([r1, r2])
tim = []
t1 = []
t2 = []
v1 = []
v2 = []
v = []
xco = []
xh2 = []
for n in range(30):
time = (n+1)*0.002
net.advance(time)
print(fmt % (time, r1.T, r2.T, r1.volume, r2.volume,
r1.volume + r2.volume, r2.thermo['CO'].X[0]))
tim.append(time * 1000)
t1.append(r1.T)
t2.append(r2.T)
v1.append(r1.volume)
v2.append(r2.volume)
v.append(r1.volume + r2.volume)
xco.append(r2.thermo['CO'].X[0])
xh2.append(r1.thermo['H2'].X[0])
# plot the results if matplotlib is installed.
if '--plot' in sys.argv:
import matplotlib.pyplot as plt
plt.subplot(2,2,1)
plt.plot(tim,t1,'-',tim,t2,'r-')
plt.xlabel('Time (ms)')
plt.ylabel('Temperature (K)')
plt.subplot(2,2,2)
plt.plot(tim,v1,'-',tim,v2,'r-',tim,v,'g-')
plt.xlabel('Time (ms)')
plt.ylabel('Volume (m3)')
plt.subplot(2,2,3)
plt.plot(tim,xco)
plt.xlabel('Time (ms)')
plt.ylabel('CO Mole Fraction (right)')
plt.subplot(2,2,4)
plt.plot(tim,xh2)
plt.xlabel('Time (ms)')
plt.ylabel('H2 Mole Fraction (left)')
plt.tight_layout()
plt.show()
else:
print("""To view a plot of these results, run this script with the option --plot""")

View file

@ -0,0 +1,63 @@
"""
Constant-pressure, adiabatic kinetics simulation.
"""
import sys
import numpy as np
import cantera as ct
gri3 = ct.Solution('gri30.xml')
air = ct.Solution('air.xml')
gri3.TPX = 1001.0, ct.one_atm, 'H2:2,O2:1,N2:4'
r = ct.Reactor(gri3)
env = ct.Reservoir(air)
# Define a wall between the reactor and the environment, and
# make it flexible, so that the pressure in the reactor is held
# at the environment pressure.
w = ct.Wall(r, env)
w.expansion_rate_coeff = 1.0e6 # set expansion parameter. dV/dt = KA(P_1 - P_2)
w.area = 1.0
sim = ct.ReactorNet([r])
time = 0.0
times = np.zeros(100)
data = np.zeros((100,4))
print('%10s %10s %10s %14s' % ('t [s]','T [K]','P [Pa]','u [J/kg]'))
for n in range(100):
time += 1.e-5
sim.advance(time)
times[n] = time * 1e3 # time in ms
data[n,0] = r.T
data[n,1:] = r.thermo['OH','H','H2'].X
print('%10.3e %10.3f %10.3f %14.6e' % (sim.time, r.T,
r.thermo.P, r.thermo.u))
# Plot the results if matplotlib is installed.
# See http://matplotlib.org/ to get it.
if '--plot' in sys.argv[1:]:
import matplotlib.pyplot as plt
plt.clf()
plt.subplot(2, 2, 1)
plt.plot(times, data[:,0])
plt.xlabel('Time (ms)')
plt.ylabel('Temperature (K)')
plt.subplot(2, 2, 2)
plt.plot(times, data[:,1])
plt.xlabel('Time (ms)')
plt.ylabel('OH Mole Fraction')
plt.subplot(2, 2, 3)
plt.plot(times, data[:,2])
plt.xlabel('Time (ms)')
plt.ylabel('H Mole Fraction')
plt.subplot(2, 2, 4)
plt.plot(times,data[:,3])
plt.xlabel('Time (ms)')
plt.ylabel('H2 Mole Fraction')
plt.tight_layout()
plt.show()
else:
print("To view a plot of these results, run this script with the option --plot")

View file

@ -0,0 +1,113 @@
"""
This script simulates the following situation. A closed cylinder with volume 2
m^3 is divided into two equal parts by a massless piston that moves with speed
proportional to the pressure difference between the two sides. It is
initially held in place in the middle. One side is filled with 1000 K argon at
20 atm, and the other with a combustible 500 K methane/air mixture at 0.1 atm
(phi = 1.1). At t = 0 the piston is released and begins to move due to the
large pressure difference, compressing and heating the methane/air mixture,
which eventually explodes. At the same time, the argon cools as it expands.
The piston is adiabatic, but some heat is lost through the outer cylinder
walls to the environment.
Note that this simulation, being zero-dimensional, takes no account of shock
wave propagation. It is somewhat artifical, but nevertheless instructive.
"""
import sys
import os
import csv
import numpy as np
import cantera as ct
#-----------------------------------------------------------------------
# First create each gas needed, and a reactor or reservoir for each one.
#-----------------------------------------------------------------------
# create an argon gas object and set its state
ar = ct.Solution('argon.xml')
ar.TP = 1000.0, 20.0 * ct.one_atm
# create a reactor to represent the side of the cylinder filled with argon
r1 = ct.Reactor(ar)
# create a reservoir for the environment, and fill it with air.
env = ct.Reservoir(ct.Solution('air.xml'))
# use GRI-Mech 3.0 for the methane/air mixture, and set its initial state
gri3 = ct.Solution('gri30.xml')
gri3.TPX = 500.0, 0.2 * ct.one_atm, 'CH4:1.1, O2:2, N2:7.52'
# create a reactor for the methane/air side
r2 = ct.Reactor(gri3)
#-----------------------------------------------------------------------------
# Now couple the reactors by defining common walls that may move (a piston) or
# conduct heat
#-----------------------------------------------------------------------------
# add a flexible wall (a piston) between r2 and r1
w = ct.Wall(r2, r1, A=1.0, K=0.5e-4, U=100.0)
# heat loss to the environment. Heat loss always occur through walls, so we
# create a wall separating r1 from the environment, give it a non-zero area,
# and specify the overall heat transfer coefficient through the wall.
w2 = ct.Wall(r2, env, A=1.0, U=500.0)
sim = ct.ReactorNet([r1, r2])
# Now the problem is set up, and we're ready to solve it.
print('finished setup, begin solution...')
time = 0.0
n_steps = 300
outfile = open('piston.csv', 'w')
csvfile = csv.writer(outfile)
csvfile.writerow(['time (s)','T1 (K)','P1 (Bar)','V1 (m3)',
'T2 (K)','P2 (Bar)','V2 (m3)'])
temp = np.zeros((n_steps, 2))
pres = np.zeros((n_steps, 2))
vol = np.zeros((n_steps, 2))
tm = np.zeros(n_steps)
for n in range(n_steps):
time += 4.e-4
print(n, time, r2.T)
sim.advance(time)
tm[n] = time
temp[n,:] = r1.T, r2.T
pres[n,:] = 1.0e-5*r1.thermo.P, 1.0e-5*r2.thermo.P
vol[n,:] = r1.volume, r2.volume
csvfile.writerow([tm[n], temp[n,0], pres[n,0], vol[n,0],
temp[n,1], pres[n,1], vol[n,1]])
outfile.close()
print('Output written to file piston.csv')
print('Directory: '+os.getcwd())
if '--plot' in sys.argv:
import matplotlib.pyplot as plt
plt.clf()
plt.subplot(2,2,1)
h = plt.plot(tm, temp[:,0],'g-',tm, temp[:,1],'b-')
#plt.legend(['Reactor 1','Reactor 2'],2)
plt.xlabel('Time (s)')
plt.ylabel('Temperature (K)')
plt.subplot(2,2,2)
plt.plot(tm, pres[:,0],'g-',tm, pres[:,1],'b-')
#plt.legend(['Reactor 1','Reactor 2'],2)
plt.xlabel('Time (s)')
plt.ylabel('Pressure (Bar)')
plt.subplot(2,2,3)
plt.plot(tm, vol[:,0],'g-',tm, vol[:,1],'b-')
#plt.legend(['Reactor 1','Reactor 2'],2)
plt.xlabel('Time (s)')
plt.ylabel('Volume (m$^3$)')
plt.figlegend(h, ['Reactor 1', 'Reactor 2'], loc='lower right')
plt.tight_layout()
plt.show()
else:
print("""To view a plot of these results, run this script with the option -plot""")

View file

@ -0,0 +1,92 @@
"""
Constant-pressure, adiabatic kinetics simulation with sensitivity analysis
"""
import sys
import numpy as np
import cantera as ct
gri3 = ct.Solution('gri30.xml')
temp = 1500.0
pres = ct.one_atm
gri3.TPX = temp, pres, 'CH4:0.1, O2:2, N2:7.52'
r = ct.Reactor(gri3)
air = ct.Solution('air.xml')
air.TP = temp, pres
env = ct.Reservoir(air)
# Define a wall between the reactor and the environment, and make it flexible,
# so that the pressure in the reactor is held at the environment pressure.
w = ct.Wall(r, env)
w.expansion_rate_coeff = 1.0e6 # set expansion parameter. dV/dt = KA(P_1 - P_2)
w.area = 1.0
sim = ct.ReactorNet([r])
# enable sensitivity with respect to the rates of the first 10
# reactions (reactions 0 through 9)
for i in range(10):
r.add_sensitivity_reaction(i)
# set the tolerances for the solution and for the sensitivity coefficients
sim.rtol = 1.0e-6
sim.atol = 1.0e-15
sim.rtol_sensitivity = 1.0e-6
sim.atol_sensitivity = 1.0e-6
n_times = 400
tim = np.zeros(n_times)
data = np.zeros((n_times,6))
time = 0.0
for n in range(n_times):
time += 5.0e-6
sim.advance(time)
tim[n] = 1000 * time
data[n,0] = r.T
data[n,1:4] = r.thermo['OH','H','CH4'].X
# sensitivity of OH to reaction 2
data[n,4] = sim.sensitivity('OH',2)
# sensitivity of OH to reaction 3
data[n,5] = sim.sensitivity('OH',3)
print('%10.3e %10.3f %10.3f %14.6e %10.3f %10.3f' %
(sim.time, r.T, r.thermo.P, r.thermo.u, data[n,4], data[n,5]))
# plot the results if matplotlib is installed.
# see http://matplotlib.org/ to get it
if '--plot' in sys.argv:
import matplotlib.pyplot as plt
plt.subplot(2,2,1)
plt.plot(tim,data[:,0])
plt.xlabel('Time (ms)')
plt.ylabel('Temperature (K)')
plt.subplot(2,2,2)
plt.plot(tim,data[:,1])
plt.xlabel('Time (ms)')
plt.ylabel('OH Mole Fraction')
plt.subplot(2,2,3)
plt.plot(tim,data[:,2])
plt.xlabel('Time (ms)')
plt.ylabel('H Mole Fraction')
plt.subplot(2,2,4)
plt.plot(tim,data[:,3])
plt.xlabel('Time (ms)')
plt.ylabel('H2 Mole Fraction')
plt.tight_layout()
plt.figure(2)
plt.plot(tim,data[:,4],'-',tim,data[:,5],'-g')
plt.legend([sim.sensitivity_parameter_name(2),sim.sensitivity_parameter_name(3)],'best')
plt.xlabel('Time (ms)')
plt.ylabel('OH Sensitivity')
plt.tight_layout()
plt.show()
else:
print("""To view a plot of these results, run this script with the option '--plot""")

View file

@ -0,0 +1,158 @@
"""
This example solves a plug flow reactor problem, where the chemistry is
surface chemistry. The specific problem simulated is the partial oxidation of
methane over a platinum catalyst in a packed bed reactor.
"""
import csv
import cantera as ct
# unit conversion factors to SI
cm = 0.01
minute = 60.0
#######################################################################
# Input Parameters
#######################################################################
tc = 800.0 # Temperature in Celsius
length = 0.3 * cm # Catalyst bed length
area = 1.0 * cm**2 # Catalyst bed area
cat_area_per_vol = 1000.0 / cm # Catalyst particle surface area per unit volume
velocity = 40.0 * cm / minute # gas velocity
porosity = 0.3 # Catalyst bed porosity
# input file containing the surface reaction mechanism
cti_file = 'methane_pox_on_pt.cti'
output_filename = 'surf_pfr_output.csv'
# The PFR will be simulated by a chain of 'NReactors' stirred reactors.
NReactors = 201
dt = 1.0
#####################################################################
t = tc + 273.15 # convert to Kelvin
# import the gas model and set the initial conditions
gas = ct.Solution(cti_file, 'gas')
gas.TPX = t, ct.one_atm, 'CH4:1, O2:1.5, AR:0.1'
# import the surface model
surf = ct.Interface(cti_file,'Pt_surf', [gas])
surf.TP = t, ct.one_atm
rlen = length/(NReactors-1)
rvol = area * rlen * porosity
outfile = open(output_filename,'w')
writer = csv.writer(outfile)
writer.writerow(['Distance (mm)', 'T (C)', 'P (atm)'] +
gas.species_names + surf.species_names)
# catalyst area in one reactor
cat_area = cat_area_per_vol * rvol
mass_flow_rate = velocity * gas.density * area
# The plug flow reactor is represented by a linear chain of zero-dimensional
# reactors. The gas at the inlet to the first one has the specified inlet
# composition, and for all others the inlet composition is fixed at the
# composition of the reactor immediately upstream. Since in a PFR model there
# is no diffusion, the upstream reactors are not affected by any downstream
# reactors, and therefore the problem may be solved by simply marching from
# the first to last reactor, integrating each one to steady state.
TDY = gas.TDY
cov = surf.coverages
print((' {:>10s}'*4).format('distance', 'X_CH4', 'X_H2', 'X_CO'))
for n in range(NReactors):
surf.TP = TDY[0], ct.one_atm
surf.coverages = cov
# create a new reactor
gas.TDY = TDY
r = ct.Reactor(gas, energy='off')
r.volume = rvol
# create a reservoir to represent the reactor immediately upstream. Note
# that the gas object is set already to the state of the upstream reactor
upstream = ct.Reservoir(gas, name='upstream')
# create a reservoir for the reactor to exhaust into. The composition of
# this reservoir is irrelevant.
downstream = ct.Reservoir(gas, name='downstream')
# use a 'Wall' object to implement the reacting surface in the reactor.
# Since walls have to be installed between two reactors/reserviors, we'll
# install it between the upstream reservoir and the reactor. The area is
# set to the desired catalyst area in the reactor, and surface reactions
# are included only on the side facing the reactor.
w = ct.Wall(upstream, r, A=cat_area, kinetics=[None, surf])
# We need a valve between the reactor and the downstream reservoir. This
# will determine the pressure in the reactor. Set K large enough that the
# pressure difference is very small.
v = ct.Valve(r, downstream, K=1e-4)
# The mass flow rate into the reactor will be fixed by using a
# MassFlowController object.
m = ct.MassFlowController(upstream, r, mdot=mass_flow_rate)
sim = ct.ReactorNet([r])
sim.max_err_test_fails = 12
# set relative and absolute tolerances on the simulation
sim.rtol = 1.0e-9
sim.atol = 1.0e-21
T_start, rho_start, Y_start = r.thermo.TDY
cov_start = surf.coverages
V_start = r.volume
Tu_start, rhou_start, Yu_start = upstream.thermo.TDY
time = 0
all_done = False
while not all_done:
time += dt
sim.advance(time)
# check whether surface coverages are in steady state. This will be
# the case if the creation and destruction rates for a surface (but
# not gas) species are equal.
all_done = True
# Note: netProduction = creation - destruction. By supplying the
# surface object as an argument, only the values for the surface
# species are returned by these methods
sdot = surf.get_net_production_rates(surf)
cdot = surf.get_creation_rates(surf)
ddot = surf.get_destruction_rates(surf)
for ks in range(surf.n_species):
ratio = abs(sdot[ks]/(cdot[ks] + ddot[ks]))
if ratio > 1.0e-9 or time < 10*dt:
all_done = False
# Save the reactor and surface states, in preparation for the simulation
# of the next reactor downstream, where this object will set the inlet
# conditions and the initial surface coverages
TDY = r.thermo.TDY
cov = surf.coverages
dist = n * rlen * 1.0e3 # distance in mm
if not n % 10:
print((' {:10f}'*4).format(dist, *gas['CH4','H2','CO'].X))
# write the gas mole fractions and surface coverages vs. distance
writer.writerow([dist, r.T - 273.15, r.thermo.P/ct.one_atm] +
list(gas.X) + list(surf.coverages))
outfile.close()
print("Results saved to '{}'".format(output_filename))