{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Continuous Reactor Example \n",
"### Simulation of a CSTR/PSR/WSR \n",
"\n",
"In this example we will illustrate how Cantera can be used to simulate a Continuously Stirred Tank Reactor (CSTR), also interchangeably referred to as a Perfectly Stirred Reactor or a Well Stirred Reactor, a Jet Stirred Reactor or a Longwell Reactor (there may well be more \"aliases\"). A cartoon of such a reactor is shown below\n",
"\n",
"
\n",
"\n",
"As the figure illustrates, this is an open system (unlike a Batch Reactor which is isolated). P, V and T are the reactor's pressure, volume and temperature respectively. The mass flow rate at which reactants come in is the same as that of the products which exit; and these stay in the reactor for a characteristic time $\\tau$, called the *residence time*. This is a key quantity in sizing the reactor and is defined as follows:\n",
"\n",
"\\begin{equation*}\n",
"\\tau = \\frac{m}{\\dot{m}}\n",
"\\end{equation*}\n",
"\n",
"where $m$ is the mass of the gas"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Running Cantera version: 2.3.0a2\n"
]
}
],
"source": [
"from __future__ import division\n",
"from __future__ import print_function\n",
"\n",
"import pandas as pd\n",
"import numpy as np\n",
"import time\n",
"import cantera as ct\n",
"\n",
"print(\"Running Cantera version: {}\".format(ct.__version__))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Define the gas\n",
"In this example, we will work with $nC\n",
"_{7}H_{16}$/$O_{2}$/$He$ mixtures, for which experimental data can be found in the paper by [Zhang et al.](http://dx.doi.org/10.1016/j.combustflame.2015.08.001). We will use the same mechanism reported in the paper. It consists of 1268 species and 5336 reactions"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"**** WARNING ****\n",
"For species OHV, discontinuity in h/RT detected at Tmid = 1000\n",
"\tValue computed using low-temperature polynomial: 53.6206\n",
"\tValue computed using high-temperature polynomial: 53.5842\n",
"\n",
"\n",
"**** WARNING ****\n",
"For species CHV, discontinuity in h/RT detected at Tmid = 1000\n",
"\tValue computed using low-temperature polynomial: 107.505\n",
"\tValue computed using high-temperature polynomial: 107.348\n",
"\n",
"\n",
"**** WARNING ****\n",
"For species CH2CO, discontinuity in cp/R detected at Tmid = 1000\n",
"\tValue computed using low-temperature polynomial: 10.0876\n",
"\tValue computed using high-temperature polynomial: 10.1013\n",
"\n",
"\n",
"**** WARNING ****\n",
"For species C5H9B-A,COOH, discontinuity in cp/R detected at Tmid = 1675\n",
"\tValue computed using low-temperature polynomial: 47.645\n",
"\tValue computed using high-temperature polynomial: 47.5845\n",
"\n",
"\n",
"**** WARNING ****\n",
"For species C5H9B-C,DOOH, discontinuity in cp/R detected at Tmid = 1675\n",
"\tValue computed using low-temperature polynomial: 47.645\n",
"\tValue computed using high-temperature polynomial: 47.5845\n",
"\n",
"\n",
"**** WARNING ****\n",
"For species C5H9C-A,AOOH, discontinuity in cp/R detected at Tmid = 1681\n",
"\tValue computed using low-temperature polynomial: 48.5491\n",
"\tValue computed using high-temperature polynomial: 48.4914\n",
"\n",
"\n",
"**** WARNING ****\n",
"For species C5H9C-A,DOOH, discontinuity in cp/R detected at Tmid = 1681\n",
"\tValue computed using low-temperature polynomial: 48.5491\n",
"\tValue computed using high-temperature polynomial: 48.4914\n"
]
}
],
"source": [
"gas = ct.Solution('data/galway.cti')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Define initial conditions\n",
"#### Inlet conditions for the gas and reactor parameters"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# Inlet gas conditions\n",
"reactorTemperature = 925 #Kelvin\n",
"reactorPressure = 1.046138*ct.one_atm #in atm. This equals 1.06 bars\n",
"concentrations = {'NC7H16': 0.005, 'O2': 0.0275, 'HE': 0.9675}\n",
"gas.TPX = reactorTemperature, reactorPressure, concentrations \n",
"\n",
"# Reactor parameters\n",
"residenceTime = 2 #s\n",
"reactorVolume = 30.5*(1e-2)**3 #m3\n",
"\n",
"# Instrument parameters\n",
"\n",
"# This is the \"conductance\" of the pressure valve and will determine its efficiency in \n",
"# holding the reactor pressure to the desired conditions. \n",
"pressureValveCoefficient = 0.01\n",
"\n",
"# This parameter will allow you to decide if the valve's conductance is acceptable. If there\n",
"# is a pressure rise in the reactor beyond this tolerance, you will get a warning\n",
"maxPressureRiseAllowed = 0.01"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Simulation parameters"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"# Simulation termination criterion\n",
"maxSimulationTime = 50 # seconds"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Reactor arrangement\n",
"\n",
"We showed a cartoon of the reactor in the first figure in this notebook; but to actually simulate that, we need a few peripherals. A mass-flow controller upstream of the stirred reactor will allow us to flow gases in, and in-turn, a \"reservoir\" which simulates a gas tank is required to supply gases to the mass flow controller. Downstream of the reactor, we install a pressure regulator which allows the reactor pressure to stay within. Downstream of the regulator we will need another reservoir which acts like a \"sink\" or capture tank to capture all exhaust gases (even our simulations are environmentally friendly !). This arrangment is illustrated below\n",
"\n",
"
"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Initialize the stirred reactor and connect all peripherals"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"fuelAirMixtureTank = ct.Reservoir(gas)\n",
"exhaust = ct.Reservoir(gas)\n",
"\n",
"stirredReactor = ct.IdealGasReactor(gas, energy='off', volume=reactorVolume)\n",
"\n",
"massFlowController = ct.MassFlowController(upstream=fuelAirMixtureTank,\n",
" downstream=stirredReactor,\n",
" mdot=stirredReactor.mass/residenceTime)\n",
"\n",
"pressureRegulator = ct.Valve(upstream=stirredReactor,\n",
" downstream=exhaust,\n",
" K=pressureValveCoefficient)\n",
"\n",
"reactorNetwork = ct.ReactorNet([stirredReactor])"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"# now compile a list of all variables for which we will store data\n",
"columnNames = [stirredReactor.component_name(item) for item in range(stirredReactor.n_vars)]\n",
"columnNames = ['pressure'] + columnNames\n",
"\n",
"# use the above list to create a DataFrame\n",
"timeHistory = pd.DataFrame(columns=columnNames)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Simulation Took 80.92s to compute, with 1264 steps\n"
]
}
],
"source": [
"# Start the stopwatch\n",
"tic = time.time()\n",
"\n",
"# Set simulation start time to zero\n",
"t = 0\n",
"counter = 1\n",
"while t < maxSimulationTime:\n",
" t = reactorNetwork.step()\n",
"\n",
" # We will store only every 10th value. Remember, we have 1200+ species, so there will be\n",
" # 1200 columns for us to work with\n",
" if(counter%10 == 0):\n",
" #Extract the state of the reactor\n",
" state = np.hstack([stirredReactor.thermo.P, stirredReactor.mass, \n",
" stirredReactor.volume, stirredReactor.T, stirredReactor.thermo.X])\n",
" \n",
" #Update the dataframe\n",
" timeHistory.loc[t] = state\n",
" \n",
" counter += 1\n",
"\n",
"# Stop the stopwatch\n",
"toc = time.time()\n",
"\n",
"print('Simulation Took {:3.2f}s to compute, with {} steps'.format(toc-tic, counter))\n",
"\n",
"# We now check to see if the pressure rise during the simulation, a.k.a the pressure valve\n",
"# was okay\n",
"pressureDifferential = timeHistory['pressure'].max()-timeHistory['pressure'].min()\n",
"if(abs(pressureDifferential/reactorPressure) > maxPressureRiseAllowed):\n",
" print(\"WARNING: Non-trivial pressure rise in the reactor. Adjust K value in valve\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Plot the results"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Import modules and set plotting defaults"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import matplotlib as mpl\n",
"%matplotlib notebook\n",
"\n",
"plt.style.use('ggplot')\n",
"plt.style.use('seaborn-pastel')\n",
"\n",
"plt.rcParams['axes.labelsize'] = 18\n",
"plt.rcParams['xtick.labelsize'] = 14\n",
"plt.rcParams['ytick.labelsize'] = 14\n",
"plt.rcParams['figure.autolayout'] = True"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As a test, we plot the mole fraction of $CO$ and see if the simulation has converged. If not, go back and adjust max. number of steps and/or simulation time"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"application/javascript": [
"/* Put everything inside the global mpl namespace */\n",
"window.mpl = {};\n",
"\n",
"mpl.get_websocket_type = function() {\n",
" if (typeof(WebSocket) !== 'undefined') {\n",
" return WebSocket;\n",
" } else if (typeof(MozWebSocket) !== 'undefined') {\n",
" return MozWebSocket;\n",
" } else {\n",
" alert('Your browser does not have WebSocket support.' +\n",
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
" 'Firefox 4 and 5 are also supported but you ' +\n",
" 'have to enable WebSockets in about:config.');\n",
" };\n",
"}\n",
"\n",
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
" this.id = figure_id;\n",
"\n",
" this.ws = websocket;\n",
"\n",
" this.supports_binary = (this.ws.binaryType != undefined);\n",
"\n",
" if (!this.supports_binary) {\n",
" var warnings = document.getElementById(\"mpl-warnings\");\n",
" if (warnings) {\n",
" warnings.style.display = 'block';\n",
" warnings.textContent = (\n",
" \"This browser does not support binary websocket messages. \" +\n",
" \"Performance may be slow.\");\n",
" }\n",
" }\n",
"\n",
" this.imageObj = new Image();\n",
"\n",
" this.context = undefined;\n",
" this.message = undefined;\n",
" this.canvas = undefined;\n",
" this.rubberband_canvas = undefined;\n",
" this.rubberband_context = undefined;\n",
" this.format_dropdown = undefined;\n",
"\n",
" this.image_mode = 'full';\n",
"\n",
" this.root = $('