#!/usr/bin/env python # coding: utf-8 # # Problem 1 # When $A -> B$ as simple reaction with the rate $-r_A = k C_A$ and the solution for A could be found in this way: # $\dfrac{d C_A}{dt} = -k C_A(t)$ # $C_{A0} = 10$ mole/L # # In[96]: import matplotlib.pyplot as plt # plotting modules get_ipython().run_line_magic('matplotlib', 'inline') plt.style.use('presentation') # just have in your script for prettier plotting # insted of 'presentation' you can use 'ggplot' import numpy as np # our matlab-like module from scipy.integrate import odeint # integration of ODEs so you don't have to write your finite difference yourself #step 1 definition of the function: def f(C_A, t): dcadt = - k*C_A return dcadt k = 1 C_A0 = 10. time_start = 0. time_finish = 10000. N_points = 100 time_array = np.linspace(0, 10., N_points) C_analyt = C_A0*np.exp(-k*time_array) C_num = odeint(f,C_A0, time_array) plt.plot(time_array, C_analyt, 'r-') plt.plot(time_array, C_analyt, 'go', alpha=0.8, fillstyle='none') # alpha - 80% transparency # fillstyle - none - means no filling of the circles plt.xlabel('t, s') plt.ylabel('$C_A(t)$, mole/L') plt.show() # # Now the same thing for B # $ -(r_B) = -(-r_A) = - k C_A$ # so the design equation will have an additional equation: # # $\dfrac{d C_B}{dt} = k C_A(t)$ # $C_{B0} = 0$ mol/L # # In[22]: import matplotlib.pyplot as plt # plotting modules get_ipython().run_line_magic('matplotlib', 'inline') plt.style.use('presentation') # just have in your script for prettier plotting # insted of 'presentation' you can use 'ggplot' import numpy as np # our matlab-like module from scipy.integrate import odeint # integration of ODEs so you don't have to write your finite difference yourself #step 1 definition of the function: def f(C_list, t): C_A = C_list[0] C_B = C_list[1] dcadt = - k*C_A dcbdt = k*C_A return [dcadt, dcbdt] k = 2 C_A0 = 10. C_B0 = 0. time_start = 0. time_finish = 10. N_points = 100 time_array = np.linspace(0, 10., N_points) C0_list = [1, 0.] C_num_list = odeint(f, C0_list, time_array) print(C_num_list.shape) C_A_num = C_num_list[:,0] C_B_num = C_num_list[:,1] plt.plot(time_array, C_A_num, 'r--', fillstyle='none', label='$A$') plt.plot(time_array, C_B_num, 'g--', fillstyle='none', label='$B$') plt.xlabel('t, s') plt.ylabel('$C(t)$, mole/L') plt.legend() plt.show() # C_analyt = C_A0*np.exp(-k*time_array) # C_num = odeint(f,C_A0, time_array) # In[16]: C_num_list[:,0] # # Problem 2 # # A system of ODES # # $ A + B \to C$, $k_1 = 1$ L/(mol s) # # $ B + C \to D$, $k_1 = 1.5$ L/(mol s) # # with initial concentrations # $C_{A0} = 1$ mol/L # $C_{B0} = 1$ mol/L # $C_{C0} = 0$ mol/L # $C_{D0} = 0$ mol/L # # 1. Calculate concentration of each component # 2. Calculate the selectivity parameters $S = \dfrac{C}{(C+D)}$, S(t=0) = 1.0 # # In[24]: import matplotlib.pyplot as plt # plotting modules get_ipython().run_line_magic('matplotlib', 'inline') plt.style.use('presentation') # just have in your script for prettier plotting # insted of 'presentation' you can use 'ggplot' import numpy as np # our matlab-like module from scipy.integrate import odeint # integration of ODEs so you don't have to write your finite difference yourself #step 1 definition of the function: def f(C_list, t): k1 = 1. k2 = 1.5 C_A = C_list[0] C_B = C_list[1] C_C = C_list[2] r1 = k1*C_A*C_B r2 = k2*C_B*C_C dcAdt = -r1 dcBdt = -r1 - r2 dcCdt = r1 - r2 dcDdt = r2 return [dcAdt,dcBdt,dcCdt,dcDdt] k1 = 1. k2 = 1.5 C0_list = [1.0, 1.0, 0.0, 0.0] time_start = 0. time_finish = 3. N_points = 100 time_array = np.linspace(0, 3., N_points) C_num_list = odeint(f, C0_list, time_array) print(C_num_list.shape) C_A_num = C_num_list[:,0] C_B_num = C_num_list[:,1] C_C_num = C_num_list[:,2] C_D_num = C_num_list[:,3] plt.plot(time_array, C_A_num, 'r-', label='$A$') plt.plot(time_array, C_B_num, 'g-', label='$B$') plt.plot(time_array, C_C_num, 'm--', label='$C$') plt.plot(time_array, C_D_num, 'k--', label='$D$') plt.xlabel('t, s') plt.ylabel('$C(t)$, mole/L') plt.legend() plt.show() # In[34]: S = np.ones(len(C_C_num)) S[1:] = C_C_num[1:]/(C_C_num[1:] + C_D_num[1:]) plt.plot(time_array, S, 'k-',label='selectivity') plt.xlabel('t, s') plt.ylabel('$S$') plt.legend() plt.show() # here are more advanced equations: # https://www.youtube.com/watch?v=8-V5T40aMEc # https://www.youtube.com/watch?v=BRe7qKIAa34 # # zombie apocalypse: # # http://scipy-cookbook.readthedocs.io/items/Zombie_Apocalypse_ODEINT.html # # # Problem 4: Zombie apocalypse: # # Imagine we live in the world of where a part of human population is infected and could take over the world. # # S - Living people are Susceptible (S) victims, so they could be infected and become a zombie # # Z - Number of zombies, they could come from either infected people or 'ressurcted' dead people # # R - rate by which people die # # with the following notations: # ``` # S: the number of susceptible victims # Z: the number of zombies # R: the number of people "killed" # P: the population birth rate # d: the chance of a natural death # B: the chance the "zombie disease" is transmitted (an alive person becomes a zombie) # G: the chance a dead person is resurrected into a zombie # A: the chance a zombie is totally destroyed # ``` # # There are different reactions happening at the same time: # # # **Rate of accumulation of the living people (Susceptible victims)** # # birth rate -> S (something is born -> S zeroth order reaction) # # S -> Infected -> Z ( S + Z -> Z second order reaction with a constant B => $-r_S = B \cdot S \cdot Z$ ) # # S -> Dead ( S -> Dead, first with the rate $-r_S = d \cdot S$) # # So the corresponding design equation will look like: # # $\dfrac{\delta S}{dt} = P - B \cdot S \cdot Z - d \cdot S$ # # # **Rate of accumulation of zombies** # # S -> Infected -> Z ( S + Z -> Z second order reaction with a constant B => $-r_S = B \cdot S \cdot Z$ ) # # Dead person -> Z (resurrection of a dead person into a zombie, first order reaction with a constant R) # # Z -> 0 (Killing of zombies by living people second order reaction S + Z -> 0 $-r_Z = A \cdot S \cdot Z$ # # $\dfrac{\delta Z}{dt} = B \cdot S \cdot Z + G \cdot R - A \cdot S \cdot Z$ # # **Rate of dieing** # # S -> Dead # # S + Z -> 0 # # Dead -> Resurrected # # $\dfrac{\delta R}{dt} = d \cdot S + A \cdot S \cdot Z - G \cdot R$ # # # Initial parameters # ``` # P = 0 # birth rate # d = 0.0001 # natural death percent (per day) # B = 0.0095 # transmission percent (per day) # G = 0.0001 # resurect percent (per day) # A = 0.0001 # destroy percent (per day) # ``` # In[84]: #zombie apolcalypse import matplotlib.pyplot as plt # plotting modules get_ipython().run_line_magic('matplotlib', 'inline') plt.style.use('presentation') # plt.style.use('ggplot') import numpy as np # our matlab-like module from scipy.integrate import odeint # integration of ODEs so you don't have to write your finite difference yourself P = 0. # birth rate d = 0.0001 # natural death percent (per day) B = 0.0095 # transmission percent (per day) # # B = 0.001 # transmission percent (per day) G = 0.0001 # resurect percent (per day) # # G = 0.1 # resurect percent (per day) A = 0.001 # destroy percent (per day) # solve the system dy/dt = f(y, t) def f(y, t): Si = y[0] Zi = y[1] Ri = y[2] # the model equations (see Munz et al. 2009) f0 = P - B*Si*Zi - d*Si f1 = B*Si*Zi + G*Ri - A*Si*Zi f2 = d*Si + A*Si*Zi - G*Ri return [f0, f1, f2] # initial conditions S0 = 500. # initial population Z0 = 0 # initial zombie population R0 = 0 # initial death population y0 = [S0, Z0, R0] # initial condition vector time_start = 0 time_finish = 10. N_points = 1000 t = np.linspace(time_start, time_finish, N_points) # time grid # solve the DEs soln = odeint(f, y0, t) S = soln[:, 0] Z = soln[:, 1] R = soln[:, 2] # plot results plt.figure() plt.plot(t, S,'g--', label='Living') plt.plot(t, Z,'r--', label='Zombies') plt.plot(t, R,'k-', label='Dead') plt.xlabel('Days from outbreak') plt.ylabel('Population') # plt.title('Zombie Apocalypse - No Init. Dead Pop.; No New Births.') plt.legend(loc=0) # In[1]: from IPython.html.widgets import interact from IPython.display import clear_output, display, HTML def zombie(P= 0., d = 0.0001, B=0.0095, G=0.001, A=0.001): # solve the system dy/dt = f(y, t) def f(y, t): Si = y[0] Zi = y[1] Ri = y[2] # the model equations (see Munz et al. 2009) f0 = P - B*Si*Zi - d*Si f1 = B*Si*Zi + G*Ri - A*Si*Zi f2 = d*Si + A*Si*Zi - G*Ri return [f0, f1, f2] # initial conditions S0 = 500. # initial population Z0 = 0 # initial zombie population R0 = 0 # initial death population y0 = [S0, Z0, R0] # initial condition vector time_start = 0 time_finish = 10. N_points = 1000 t = np.linspace(time_start, time_finish, N_points) # time grid # solve the DEs soln = odeint(f, y0, t) S = soln[:, 0] Z = soln[:, 1] R = soln[:, 2] # plot results plt.figure() plt.plot(t, S,'g--', label='Living') plt.plot(t, Z,'r--', label='Zombies') plt.plot(t, R,'k-', label='Dead') plt.xlabel('Days from outbreak') plt.ylabel('Population') # plt.title('Zombie Apocalypse - No Init. Dead Pop.; No New Births.') plt.legend(loc=0) interact(zombie, P = (0, 100, 5), A =(0.001,10**(-1),0.5*10**(-6)), B=(0.0095,10**(-2),0.5*10**(-5)) ) # # # # Problem 5: # # Calculate the functions $T(t), C_{A}(t)$ in a mixer: # # Stirred Tank reactor: # # $T_{in}, C_{A0} \to T_f, C_A$ # mixer # $q = \dot{V} = 100 m^3/hr$ # $V = 100 m^3$ # # # energy balance: # # $V \dfrac{dT}{dt} = q (T_{f} - T(t))$ # # mass balance: # # $V \dfrac{dC_A}{dt} = q (C_{f} - C_{A}(t))$ # # You need to give the program parameters # $C_{A0}=0, C_{Af} = 1$ # $T_0 = 350$, $T_f = 300$ # # you can call the python function: # # ```python # def mixer(X_list, t, Tf, Cf): # ... # return [dTdt, dCa/dt] # ``` # # solution could be found here: # # https://www.youtube.com/watch?v=8-V5T40aMEc # # # # # Additional problems : 2 # # $$\require{mhchem}$$ # Normal butane, C4H10, is to be isomerized to isobutane in a plug-flow reactor, because isobutane is worth more. This elementary reversible reaction is to be carried out adiabatically in the liquid phase under high pressure using trace amounts of a liquid catalyst which gives a specific reaction rate of 31.1/hr at 360K. The feed enters at 330K. # # Additional information: # * $\Delta H^\circ_{Rx} = -6900$ J/mol $n$-butane # * Activation energy $E_a = 65.7$ kJ/mol # * $K_C = 3.03$ at $60^\circ$ C # * $C_{P,n\text{-Butane}} = 141$ J/mol/K # * $C_{P,i\text{-Butane}} = 141$ J/mol/K # * $C_{P,n\text{-Pentane}} = 161$ J/mol/K # * $C_{A0} = 9.3$ mol/dm3 = 9.3 kmol/m3 feed concentration of n-butane # # a) Calculate the PFR volume necessary to process 100,000 gal/day (163 kmol/h) at 70% conversion of a mixture of 90 mol% n-butane and 10 mol% i-pentane, which is considered an inert. # # b) Plot and analyze $X, X_e, T$, and $-r_A$ down the length of the reactor. # # # You may use this code to use this code for your program. # You just need to understand and plot it. # # ```python # DHrx = -6900. # J/mol # Ea = 65700. # J/mol # R = 8.314 # J/mol/K # T0 = 330 # K # FA0 = 0.9 * 163e3 # mol/hr # CA0 = 9.3e3 # mol/m3 # # def Kc(T): # "Equilibrium constant, as a function of temperature" # return 3.03 * np.exp(( -1* DHrx / R) * (1./T - 1./(273.15+60)) ) # assert Kc(273.15+60) == 3.03 # assert Kc(273.15+65) < 3.03 # le Chatellier's principle # # def k(T): # "Specific reaction rate in 1/hour, as a function of temperature" # return 31.1 * np.exp((-1*Ea/R) * (1./T - 1./360.)) # assert k(360) == 31.1 # assert k(365) > 31.1 # # def Teb(X): # "Temperature from energy balance, as a function of conversion." # Cps = np.array([141, 141, 161]) # Thetas = np.array([1., 0., 0.1/0.9]) # return T0 - DHrx * X / sum(Cps*Thetas) # assert Teb(0) == T0 # assert Teb(0.5) > T0 # Exothermic # # def dXdV(X,V): # """ # dX/dV in a plug flow reactor is -rA/ FA0 # """ # T = Teb(X) # CA = CA0*(1.-X) # CB = CA0*X # rA = -k(T)*(CA - CB/Kc(T)) # return -rA/FA0 # ``` # # ``` # Xeq = Kc(Teb(X))/ (1+Kc(Teb(X))) # rate = k(Teb(X))*(CA0*(1.-X) - CA0*X/Kc(Teb(X))) # # #For Levenspiel plot you may use this: # # plt.plot(X, FA0/rate) # plt.ylim(0,10) # plt.title("Levenspiel Plot") # plt.xlabel("X") # plt.ylabel("$\\frac{F_{A0}}{-r_A}$") # plt.plot((Xeq[-1],Xeq[-1]),(0,FA0/rate[-1]), ':') # plt.show() # # ``` # In[ ]: DHrx = -6900. # J/mol Ea = 65700. # J/mol R = 8.314 # J/mol/K T0 = 330 # K FA0 = 0.9 * 163e3 # mol/hr CA0 = 9.3e3 # mol/m3 def Kc(T): "Equilibrium constant, as a function of temperature" return 3.03 * np.exp(( -1* DHrx / R) * (1./T - 1./(273.15+60)) ) assert Kc(273.15+60) == 3.03 assert Kc(273.15+65) < 3.03 # le Chatellier's principle def k(T): "Specific reaction rate in 1/hour, as a function of temperature" return 31.1 * np.exp((-1*Ea/R) * (1./T - 1./360.)) assert k(360) == 31.1 assert k(365) > 31.1 def Teb(X): "Temperature from energy balance, as a function of conversion." Cps = np.array([141, 141, 161]) Thetas = np.array([1., 0., 0.1/0.9]) return T0 - DHrx * X / sum(Cps*Thetas) assert Teb(0) == T0 assert Teb(0.5) > T0 # Exothermic def dXdV(X,V): """ dX/dV in a plug flow reactor is -rA/ FA0 """ T = Teb(X) CA = CA0*(1.-X) CB = CA0*X rA = -k(T)*(CA - CB/Kc(T)) return -rA/FA0 volumes = np.linspace(0,5,501) X = scipy.integrate.odeint(dXdV, 0., volumes) plt.plot(volumes,X,label="Conversion") Xeq = Kc(Teb(X))/ (1+Kc(Teb(X))) plt.plot(volumes, Xeq,label="Equilibrium conversion") plt.legend(loc="best") plt.xlabel("Volume") plt.ylabel("Conversion X") plt.show() rate = k(Teb(X))*(CA0*(1.-X) - CA0*X/Kc(Teb(X))) plt.plot(volumes, rate, label="Rate") plt.xlabel("Volume") plt.ylabel("Rate") plt.show() for volume, conversion in zip(volumes, X): if conversion>0.7: break print volume print conversion plt.plot(X, FA0/rate) plt.ylim(0,10) plt.title("Levenspiel Plot") plt.xlabel("X") plt.ylabel("$\\frac{F_{A0}}{-r_A}$") plt.plot((Xeq[-1],Xeq[-1]),(0,FA0/rate[-1]), ':') plt.show()