01 - Motivation#

Engineers solve problems. You as a chemical engineer are likely excited to collaborate with others to use chemistry and physics together with other engineering principles to solve problems our society faces. This first discussion hopefully shows you the skills you’ll have by the end of the semester and how they can be used to solve problems.

Chemical Engineering Career Outlook#

Hopefully from the above you can see that BYU chemical engineers are unique and in demand. The skills you will learn in this class will help you to be successful in your career.

Current Worldwide Problems in Engineering#

Chemical Engineers are poised to help in each of these areas:

  • Energy

  • Chemical Production

    • We can continue to improve chemical production processes

    • Chemical and Engineering News

    • Argyle, Hedengren, Knotts, Nickerson, Seo, Tree, Wilding

  • Health

  • Water

  • Transportation - How do we better move people and goods around the world?

  • Environment - How do we efficiently continue improving our environment?

  • Security - How do we continue to adapt and peacefully protect people from threats?

  • Exploration, Food, and more…

You are the engineers that will work to improve societies’ and individuals’ quality of life around the world.

What tools will you use to solve these problems?#

  • You’ll stand on the backs of giants and be familiar with their successes.

  • You’ll collaborate with others.

  • You’ll utilize both:

    • Computational tools - to solve problems with computers

    • Experimental methods - to gain further insights.

    • Coupling both theoretical and experimental methods is a more efficient way to reach the best solution than either one alone.

This course is focused primarily on helping you become more familiar with computation tools in an applied engineering context.

Danger

The following example is not intended to be overwhelming but to show you an example of what I hope you’ll be able to do at the end of the course. I wanted to give you a vision of using the tools in an example.

Note

Foundational principles used in this and other examples in the lectures are also discussed in the Foundations section of the course.

Intro Demonstration#

Flames are critical to many processes that benefit us: cooking, heating, transportation, electricity, etc. The combustion products from burning fuels can present problems. However, we have been reducing those negative effects for decades. Below is a plot by the EPA showing that trend of improvement (also here):

Flames are Ubiquitous#

In this example we’ll measure the temperature of a butane flame with a thermocouple and a low-cost data acquisition unit (ESP32) with an amplifier(MAX6675). We’ll also predict the temperature of the flame using several models in python.

The below theoretical example problem calculates the adiabatic (no heat losses) flame temperature of butane (C4H10) and air mixture. The first method is completed with a model of the enthalpy of formation of the reactants and products and the heat of combustion. The second method uses the Cantera package to calculate the adiabatic flame temperature.

\[ \begin{equation} C_4H_{10} + 6.5\cdot O_2 + x\cdot N_2 => 4\cdot CO_2 + 5\cdot H_2O + x\cdot N_2 \end{equation} \]

where \(x\) is the number of moles of nitrogen which can be found from the nitrogen balance assuming that the amount of air in the atmosphere is 79% nitrogen and 21% oxygen. Thus x is simply the number of moles of oxygen divided by 0.21 multiplied by 0.79 (6.5*0.79/0.21 = 24.4).

First manually calculate the adiabatic flame temperature#

For butane combustion the below code calculates the adiabatic flame temperature using the enthalpy of formation of the reactants and products and the heat of combustion as well as the heat capacities of the products

# Adiabatic flame temperature of butane-air mixture from heats of formation

# First import the needed packages
import numpy as np 
import matplotlib.pyplot as plt
from scipy import integrate
import param

# Define the reaction:
# C4H10 + 6.5O2 + 24.4N2 => 4CO2 + 5H2O + 24.4N2

#define a class for the reactants and products
class species(param.Parameterized):
    H_rxn = param.Number(-1,bounds=(-1e10,0),doc='Heat of Reaction in kJ/mol')
    H_f = param.Number(-1,bounds=(-1e10,0),doc='Heat of Formation in kJ/mol')
    C_pgl = param.List([1,1,1,1,1],doc='Heat capacity coefficients in J/kmol/K')
    M_w = param.Number(1,bounds=(0,10000),doc='Molecular weight in g/mol')
    coeff = param.Number(1,bounds=(-100,100),doc='Stoichiometric coefficient')

    #define a function to calculate the ideal gas heat capacity
    def Cp(self,T):
        A = self.C_pgl[0]; B = self.C_pgl[1]; C = self.C_pgl[2]; D = self.C_pgl[3]; E = self.C_pgl[4]; 
        return A + B*((C/T)/np.sinh(C/T))**2 + D*((E/T)/np.cosh(E/T))**2 
    
    #define a function to calculate the enthalpy based on the ideal gas heat capacity
    def enthalpy(self,T,TO): #units of kJ/mol
        return self.coeff*(integrate.quad(self.Cp,TO,T)[0]/1e6)

#define reactants and products (estimated) for butane combustion
C4H10 = species(H_rxn=-2657.32,H_f=-125,C_pgl=[80152,162410,841,105747,2476],M_w=58.12,coeff=-1)
O2 = species(H_rxn=0,H_f=0,C_pgl=[29100,10040,2526,9356,1153],M_w=32.0,coeff=-6.5)
CO2 = species(H_rxn=0,H_f=-394,C_pgl=[29370,34540,1428,26400,588],M_w=44.01,coeff=4)
H2O = species(H_rxn=0,H_f=-242,C_pgl=[33363,26790,2610,8896,1169],M_w=18.02,coeff=5)
N2 = species(H_rxn=0,H_f=0,C_pgl=[29105,8615,1701,103,910],M_w=28.01,coeff=-24.4)
\[ \begin{equation} H_i = \int_{T_{ref}}^{T}{C_{p,i}dT} \end{equation} \]
\[ \begin{equation} \sum_{out}{n_iH_i(T_{ad})} + n_f\Delta H_{c} - \sum{n_i H_i(T_{in})} = 0 \end{equation} \]

where \(n_i\) is the number of moles of each species, \(H_i\) is the enthalpy of formation of each species, \(T_{ad}\) is the adiabatic flame temperature, \(n_f\) is the number of moles of fuel, \(\Delta H_{c}\) is the heat of combustion of the fuel, and \(T_{in}\) is the initial temperature of the reactants.

#define initial conditions
T0 = 298.0 #K

#define a function for the enthalpy of the reactants
def H_react(T):
    formation = C4H10.coeff*C4H10.H_f + O2.coeff*O2.H_f + O2.coeff*N2.H_f
    return C4H10.enthalpy(T,T0) + O2.enthalpy(T,T0) + N2.enthalpy(T,T0) + formation

#also define a function for the enthalpy of the products
def H_prod(T):
    return CO2.enthalpy(T,T0) + H2O.enthalpy(T,T0) - N2.enthalpy(T,T0)
# Use fsolve to find the addiabatic temperature from Equation 1
# first import the fsolve function
from scipy.optimize import fsolve
# next react the reactants to the products at T0 then heat the products to the adiabatic temperature
T_ad = fsolve(lambda T: H_react(T0) + C4H10.coeff*C4H10.H_rxn - H_prod(T),T0)[0]
print(f'The adiabatic flame temperature is {T_ad-273:.2f} C')
The adiabatic flame temperature is 2217.02 C

Second calculate the adiabatic flame temperature using Cantera#

Cantera is an open-source thermo-chemical-kinetics package that can be used to calculate the adiabatic flame temperature and other chemical engineering common problem solutions. The code below uses the Cantera package to calculate the adiabatic flame temperature. Furter info on Cantera is here.

import cantera as ct
gas1 = ct.Solution('sdmech.yaml')
gas1.TP = T0, ct.one_atm
gas1.set_equivalence_ratio(1.0, 'C4H10', 'O2:1, N2:3.76')
gas1.equilibrate('HP')
print(f'The equilibrium flame temperature is {gas1.T-273:.2f} C')
comps = gas1['H2O','CO2','N2','O2'].X
print(f'The eq. gas mole fractions for H2O, CO2, N2, O2 are {comps[0]:.2f},{comps[1]:.2f},{comps[2]:.2f},{comps[3]:.2f}, respectively')
gas1()
The equilibrium flame temperature is 2001.39 C
The eq. gas mole fractions for H2O, CO2, N2, O2 are 0.14,0.11,0.72,0.01, respectively

  gas:

       temperature   2274.4 K
          pressure   1.0132e+05 Pa
           density   0.15093 kg/m^3
  mean mol. weight   28.167 kg/kmol
   phase of matter   gas

                          1 kg             1 kmol     
                     ---------------   ---------------
          enthalpy       -1.3233e+05       -3.7274e+06  J
   internal energy       -8.0368e+05       -2.2638e+07  J
           entropy            9672.3        2.7244e+05  J/K
    Gibbs function       -2.2131e+07       -6.2337e+08  J
 heat capacity c_p            1471.7             41454  J/K
 heat capacity c_v            1176.5             33140  J/K

                      mass frac. Y      mole frac. X     chem. pot. / RT
                     ---------------   ---------------   ---------------
                N2           0.72011           0.72406           -27.696
                 H        1.6842e-05        0.00047062           -12.838
                O2         0.0077947         0.0068616           -34.213
                OH         0.0022466         0.0037209           -29.945
                 O        0.00020085        0.00035361           -17.106
                H2        0.00022096         0.0030872           -25.677
               H2O          0.091424           0.14295           -42.783
               HO2        8.1137e-07        6.9242e-07           -47.051
              H2O2        5.9634e-08        4.9384e-08           -59.889
                CO          0.012545          0.012616           -38.368
               CO2           0.16544           0.10589           -55.475
               HCO        1.2682e-09         1.231e-09           -51.206
              CH2O        1.6886e-11        1.5841e-11           -64.045
     [  +44 minor]        1.3908e-16        1.9315e-16  

Warning

You may get an error saying sdmech.yaml isn’t present in your system. As part of that error, it likely will tell you where to put it if you had it. You can download it from here: sdmech.yaml and put it in the location where it says it’s looking for it.

Cantera can also estimate the species and temperature in a 1-D flame#

# Simulation parameters
p = ct.one_atm  # pressure [Pa]
Tin = 300.0  # unburned gas temperature [K]
reactants = 'C4H10:1, O2:6.5, N2:24.4'  # premixed gas composition
width = 0.0005  # m
loglevel = 0  # amount of diagnostic output (0 to 8)

# Solution object used to compute mixture properties, set to the state of the
# upstream fuel-air mixture
gas = ct.Solution('sdmech.yaml')
gas.TPX = Tin, p, reactants

# Set up flame object
f = ct.FreeFlame(gas, width=width)
f.set_refine_criteria(ratio=3, slope=0.06, curve=0.12)
#f.show_solution()

# Solve with mixture-averaged transport model
f.transport_model = 'Mix'
f.solve(loglevel=loglevel, auto=True)
#f.show_solution()
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
Cell In[5], line 20
     16 #f.show_solution()
     17 
     18 # Solve with mixture-averaged transport model
     19 f.transport_model = 'Mix'
---> 20 f.solve(loglevel=loglevel, auto=True)
     21 #f.show_solution()

File ~/opt/anaconda3/envs/jupiterbook/lib/python3.9/site-packages/cantera/onedim.py:898, in FreeFlame.solve(self, loglevel, refine_grid, auto)
    896 for _ in range(12):
    897     try:
--> 898         super().solve(loglevel, refine_grid, auto)
    899         break
    900     except DomainTooNarrow:

File cantera/onedim.pyx:1221, in cantera._cantera.Sim1D.solve()

File cantera/func1.pyx:14, in cantera._cantera.func_callback()

File ~/opt/anaconda3/envs/jupiterbook/lib/python3.9/site-packages/cantera/interrupts.py:1, in no_op(t)
----> 1 def no_op(t):
      2     """
      3     This function does nothing. It is used as an interrupt in the 1D solver
      4     C++ loop where a pure Python function is needed in order for
      5     KeyboardInterrupt events to be captured.
      6     """
      7     return 0.0

KeyboardInterrupt: 
plt.plot(f.grid, f.T-273, label='T')
plt.xlabel('Distance (m)')
plt.ylabel('Temperature (C)')
plt.show()
../_images/2cb409d58aa83cb32793056d78ad46213f6c60b70da21c05dac1b7cabecb924d.png
plt.plot(f.grid, f.X[gas.species_index('C4H10')], label='C4H10')
plt.plot(f.grid, f.X[gas.species_index('O2')], label='O2')
plt.plot(f.grid, f.X[gas.species_index('CO2')], label='CO2')
plt.plot(f.grid, f.X[gas.species_index('H2O')], label='H2O')
plt.plot(f.grid, f.X[gas.species_index('N2')], label='N2')
plt.legend(loc='best')
plt.xlabel('Distance (m)')
plt.ylabel('Mole Fractions');
plt.show()
../_images/65e59774b09e04cd0cc9592336d029f532799657acfb2a4ac6b8d1c9e914dd71.png

Dr. Lignell discusses combustion processes and numerical methods further in his Combustion Principles course. Dr. Baxter has also taught an advanced combustion courses.

Experimental Estimate from a K type Thermocople#

An ESP32, MAX6675, and K type thermocouple are used to measure the temperature of the flame. The MAX6675 is a thermocouple amplifier that converts the thermocouple voltage to a digital signal that can be read by the ESP32. The ESP32 is a low-cost microcontroller that can be programmed with the Arduino IDE. The data collected by the ESP32 can be pushed to an andoid phone via classic bluetooth (or to a windows machine via bluetooth).

import pandas as pd #pandas is a data analysis library
#read in data from a txt file
data = pd.read_csv('supportfiles/flametempK.txt',delimiter=',',header=2)

The data file can be downloaded from the following link: data file

A lighter is used to heat a small bead K-type thermocouple. The rating of the K-type thermocouple is up to 1200 C. We’ll see how hot we can get the thermocouple.

data.head()
Small Thermocouple (C) Timestamp (seconds)
0 27.51 10.11
1 27.51 10.31
2 27.51 10.51
3 27.26 10.71
4 27.00 10.91
data.describe()
Small Thermocouple (C) Timestamp (seconds)
count 123.000000 123.000000
mean 424.777642 22.310000
std 415.927012 7.130217
min 27.000000 10.110000
25% 27.770000 16.210000
50% 183.670000 22.310000
75% 940.920000 28.410000
max 1043.200000 34.510000
data.columns
Index(['Small Thermocouple (C) ', ' Timestamp (seconds)'], dtype='object')
plt.plot(data[' Timestamp (seconds)'],data['Small Thermocouple (C) '],label='Experiment')
plt.xlabel('Time (s)'); plt.ylabel('Temperature (C)')
plt.show()
../_images/bd1dcb21adcd9869d9dffff3f0b2243fbcb3060c6f59049c828b6a11aa76e925.png

The figure shows the temperature rise immediately when inserted into the flame reaching temperatures above 1000 C. K type thermocouples are able to reach temperatures up to 1200 C, however, this data acquisition setup appears to max out at 1043 C. Once the thermocouple is removed from the flame the temperature drops rapidly. This temperature is significantly different from the adiabatic result as the flame is not adiabatic and there are significant heat losses to the surroundings. How could you estimate those heat losses?