{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1D Plug Flow Reactor Model with Surface Chemistry"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this model, we will illustrate the derivation of the governing differential equations and algebraic constraints, calculation of the initial conditions of the variables and their spatial derivatives and use the [scikits.odes.dae](http://scikits-odes.readthedocs.io/en/latest/guide.html#object-oriented-interface-ode-and-dae) IDA solver to solve this system of differential algebraic equations (DAE)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Runnning Cantera version: 2.4.0\n"
     ]
    }
   ],
   "source": [
    "from __future__ import print_function, division\n",
    "import numpy as np\n",
    "from scikits.odes import dae\n",
    "import cantera as ct\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "print('Runnning Cantera version: ' + ct.__version__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define gas species, bulk species, surface species and the interface"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here, we use a kinetic mechanism involving the chemical vapor deposition of silicon nitride (Si<sub>3</sub>N<sub>4</sub>) from SiF<sub>4</sub> and NH<sub>3</sub>. 25 gas species, 6 surface species and 2 bulk species mechanism is applied by [Richard S. Larson et al. 1996, SAND96-8211](https://github.com/yuj056/yuj056.github.io/blob/master/_posts/Sandia.pdf)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "#import the SiF4 + NH3 reaction mechanism\n",
    "mech = 'data/SiF4_NH3_mec.cti'\n",
    "#import the models for gas and bulk\n",
    "gas, bulk_Si, bulk_N = ct.import_phases(mech, ['gas', 'SiBulk', 'NBulk'])\n",
    "#import the model for gas-Si-N interface\n",
    "gas_Si_N_interface = ct.Interface(mech, 'SI3N4', [gas,bulk_Si,bulk_N])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Case 1: isothermal reactor\n",
    "## Define reactor conditions : temperature, pressure, fuel, and some important parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "T0 = 1713  # Kelvin\n",
    "p0 = 2 * ct.one_atm / 760.0  # Pa ~2 Torr\n",
    "gas.TPX = T0, p0, \"NH3:6, SiF4:1\"\n",
    "bulk_Si.TP = T0, p0\n",
    "bulk_N.TP = T0, p0\n",
    "gas_Si_N_interface.TP = T0, p0\n",
    "D = 5.08e-2  # diameter of the tube [m]\n",
    "Ac = np.pi * D**2/4  # cross section of the tube [m^2]\n",
    "mu = 5.7e-5  # kg/(m-s) dynamic viscosity\n",
    "perim = np.pi * D  # perimeter of the tube\n",
    "# calculate the site fractions of surface species at the entrance of the tube at steady state\n",
    "gas_Si_N_interface.advance_coverages(100.0)  # Here we assume after 100s, the system reaches the steady state\n",
    "Zk_0 = gas_Si_N_interface.coverages\n",
    "N = gas.n_species  # number of gas species\n",
    "M = gas_Si_N_interface.n_species  # number of surface species"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define a residual function for IDA solver"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For the isothermal tube with laminar flow, since the temperature of the flow and tube is constant, the energy conservation equation can be ignored. The governing equations include conservation of mass and species, momentum equation, equation of state, and the algebraic constraints that the net production rate of surface species by heterogeneous reactions are zero and that the sum of site fractions equals 1.\n",
    "\n",
    "Here we define a residual function, an equation which should always evaluate to the zero vector, as the input of IDA solver, which listed as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "\\begin{align}\n",
       "    R[0] &= u\\frac{d\\rho}{dz} + \\rho\\frac{du}{dz} - \\frac{p'}{A_c}\\sum^{K_g}\\dot{s}_{k,g}W_{k,g} \\\\\n",
       "    R[1] &= \\rho u A_c\\frac{dY_k}{dz} + Y_k p'\\sum^{K_g}\\dot{s}_{k,g}W_{k,g} - \\dot{\\omega_k}W_kA_c - \\dot{s}_{k,g}W_{k,g} p' \\\\\n",
       "    R[2] &= 2\\rho u \\frac{du}{dz} + u^2\\frac{d\\rho}{dz} + \\frac{dP}{dz} + \\frac{32u\\mu}{D^2}\\\\\n",
       "    R[3] &= P\\bar{W} - \\rho RT\\\\\n",
       "    R[4] &= \\dot{s}_{k,s} \\\\\n",
       "    R[5] &= \\sum_{phase}{Z_{k,s}} - 1\n",
       "\\end{align}     "
      ],
      "text/plain": [
       "<IPython.core.display.Latex object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%latex\n",
    "\\begin{align}\n",
    "    R[0] &= u\\frac{d\\rho}{dz} + \\rho\\frac{du}{dz} - \\frac{p'}{A_c}\\sum^{K_g}\\dot{s}_{k,g}W_{k,g} \\\\\n",
    "    R[1] &= \\rho u A_c\\frac{dY_k}{dz} + Y_k p'\\sum^{K_g}\\dot{s}_{k,g}W_{k,g} - \\dot{\\omega_k}W_kA_c - \\dot{s}_{k,g}W_{k,g} p' \\\\\n",
    "    R[2] &= 2\\rho u \\frac{du}{dz} + u^2\\frac{d\\rho}{dz} + \\frac{dP}{dz} + \\frac{32u\\mu}{D^2}\\\\\n",
    "    R[3] &= P\\bar{W} - \\rho RT\\\\\n",
    "    R[4] &= \\dot{s}_{k,s} \\\\\n",
    "    R[5] &= \\sum_{phase}{Z_{k,s}} - 1\n",
    "\\end{align}     "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The detailed derivation of the DAE system can be found in [my report](https://github.com/yuj056/yuj056.github.io/blob/master/Week1/yuj056_github_io.pdf)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def residual(z, vec, vecp, result):\n",
    "    \"\"\" we create the residual equations for the problem\n",
    "        vec = [u, rho, Yk, p, Zk]\n",
    "        vecp = [dudz, drhodz, dYkdz, dpdz, dZkdz]\n",
    "    \"\"\"\n",
    "    # temporary variables\n",
    "    u = vec[0]  # velocity\n",
    "    rho = vec[1]  # density\n",
    "    Y = vec[2:2+N]  # vector of mass fractions of all gas species\n",
    "    p = vec[2+N]  # pressure\n",
    "    Z = vec[3+N:]  # vector of site fractions of all surface species   \n",
    "\n",
    "    dudz = vecp[0]  # velocity spatial derivative\n",
    "    drhodz = vecp[1]  # density spatial derivative\n",
    "    dYdz = vecp[2:2+N]  # mass fraction spatial derivative\n",
    "    dpdz = vecp[2+N]  # pressure spatial derivative\n",
    "\n",
    "    # Use unnormalized mass fractions to avoid over-constraining the system\n",
    "    gas.set_unnormalized_mass_fractions(Y)\n",
    "    gas.TP = T0,p\n",
    "\n",
    "    bulk_Si.TP = T0,p\n",
    "    bulk_N.TP = T0,p\n",
    "\n",
    "    # Use unnormalized site fractions (coverages) to avoid over-constraining the system\n",
    "    gas_Si_N_interface.set_unnormalized_coverages(Z)\n",
    "    gas_Si_N_interface.TP = T0,p\n",
    "\n",
    "    # temporary variables (based on the given state)\n",
    "    coverages = gas_Si_N_interface.coverages  # site fraction vector\n",
    "    sdot_g = gas_Si_N_interface.net_production_rates[:N]  # heterogeneous production rate of gas species\n",
    "    sdot_s = gas_Si_N_interface.net_production_rates[-M:]\n",
    "    wdot_g = gas.net_production_rates  # homogeneous production rate of gas species\n",
    "    W_g = gas.molecular_weights  # vector of molecular weight of gas species\n",
    "\n",
    "    # mass continuity equation\n",
    "    result[0] = u*drhodz + rho*dudz - perim*np.sum(sdot_g*W_g)/Ac\n",
    "\n",
    "    # conservation of species\n",
    "    for k in range(N):\n",
    "        result[1+k] = (rho*u*Ac*dYdz[k] + Y[k]*perim*np.sum(sdot_g*W_g)\n",
    "                       - wdot_g[k]*W_g[k]*Ac\n",
    "                       - sdot_g[k]*W_g[k]*perim)\n",
    "    # conservation of momentum\n",
    "    result[1+N] = 2*rho*u*dudz + np.power(u,2)*drhodz + dpdz + 32*u*mu/D**2 \n",
    "\n",
    "    # equation of state\n",
    "    result[2+N] = gas.density - rho\n",
    "\n",
    "    # algebraic constraints\n",
    "    for j in range(M):\n",
    "        result[3+N+j] = sdot_s[j]\n",
    "\n",
    "    # replace the constraint with the condition sum(Zk) = 1 for the largest site fraction species\n",
    "    index = np.argmax(coverages)\n",
    "    result[3+N+index] = np.sum(coverages) - 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Determine the initial values of the spatial derivatives of the unknowns which need to be used as the initial conditions for the IDA solver"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The following linear equation system has been solved by [np.linalg.solve](https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.linalg.solve.html), a linear solver, to calculate the initial values of the spatial derivatives of the unknowns."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "\\begin{align}\n",
       "    u_0\\rho_0' + \\rho_0 u_0' - \\frac{p'}{A_c}\\sum^{K_g}\\dot{s}_{k,g}W_{k,g} &= 0\\\\\n",
       "    \\rho_0 u_0 A_c Y_{k,0}' + Y_{k,0} p'\\sum^{K_g}\\dot{s}_{k,g}W_{k,g} - \\dot{\\omega_k}W_kA_c - \\dot{s}_{k,g}W_{k,g} p' &=0 \\\\\n",
       "    2\\rho_0 u_0 u_0' + u_0^2\\rho_0' + P_0' + \\frac{32u_0 \\mu}{D^2} &=0\\\\\n",
       "    -RT\\rho_0' + \\bar{W_0}P_0' - P_0\\frac{\\sum^{K_g}Y_{k}'/W_{k,g}}{(\\sum^{K_g}Y_{k}/W_{k,g})^2} &= 0\n",
       "\\end{align}"
      ],
      "text/plain": [
       "<IPython.core.display.Latex object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%latex\n",
    "\\begin{align}\n",
    "    u_0\\rho_0' + \\rho_0 u_0' - \\frac{p'}{A_c}\\sum^{K_g}\\dot{s}_{k,g}W_{k,g} &= 0\\\\\n",
    "    \\rho_0 u_0 A_c Y_{k,0}' + Y_{k,0} p'\\sum^{K_g}\\dot{s}_{k,g}W_{k,g} - \\dot{\\omega_k}W_kA_c - \\dot{s}_{k,g}W_{k,g} p' &=0 \\\\\n",
    "    2\\rho_0 u_0 u_0' + u_0^2\\rho_0' + P_0' + \\frac{32u_0 \\mu}{D^2} &=0\\\\\n",
    "    -RT\\rho_0' + \\bar{W_0}P_0' - P_0\\frac{\\sum^{K_g}Y_{k}'/W_{k,g}}{(\\sum^{K_g}Y_{k}/W_{k,g})^2} &= 0\n",
    "\\end{align}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We assume the derivatives of the site fractions are equal to zero, although it is trivial for the IDA solver."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "######## Solve linear system for the initial vecp ###########\n",
    "\"\"\"\n",
    "   a = coefficient of [u', rho', Yk', P']\n",
    "   b = RHS constant of each conservation equations\n",
    "\"\"\"\n",
    "rho0 = gas.density  # initial density of the flow\n",
    "u0 = 11.53  # m/s initial velocity of the flow\n",
    "W = gas.molecular_weights\n",
    "W_avg = gas.mean_molecular_weight\n",
    "sdot = gas_Si_N_interface.net_production_rates  # heterogeneous molar production rate\n",
    "wdot = gas.net_production_rates  # homogeneous molar production rate\n",
    "################### a #########################\n",
    "a = np.zeros((3+N,3+N))\n",
    "a[0,:] = np.hstack((rho0, u0, np.zeros(1+N)))\n",
    "for i in range(N):\n",
    "    a[1+i,2+i] = rho0*u0*Ac\n",
    "a[1+N,:] = np.hstack((2*rho0*u0, u0**2, np.zeros(N), 1))\n",
    "coef = np.zeros(N)\n",
    "for j in range(N):\n",
    "    coef[j] = gas.P/W[j]/np.power(np.sum(gas.Y/W),2)\n",
    "a[2+N,:] = np.hstack((0, ct.gas_constant*T0, coef, -W_avg))\n",
    "################### b ###########################\n",
    "b = np.zeros(3+gas.n_species)\n",
    "b[0] = perim*np.sum(sdot[:N]*W)/Ac\n",
    "for i in range(gas.n_species):\n",
    "    b[1+i] = (wdot[i]*W[i]*Ac\n",
    "              + sdot[i]*W[i]*perim\n",
    "              - gas.Y[i]*perim*np.sum(sdot[:N]*W))\n",
    "b[1+gas.n_species] = -32*u0*mu/D**2\n",
    "b[2+gas.n_species] = 0\n",
    "part_vecp0 = np.linalg.solve(a,b)\n",
    "\n",
    "vecp0 = np.hstack((part_vecp0, np.zeros(M)))\n",
    "vec0 = np.hstack((11.53, gas.density, gas.Y, gas.P, Zk_0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Run the IDA solver to calculate the unknowns varying in the flow direction"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "solver = dae(\n",
    "    'ida',\n",
    "    residual, \n",
    "    first_step_size=1e-16,\n",
    "    atol=1e-8,  # absolute tolerance for solution\n",
    "    rtol=1e-8,  # relative tolerance for solution\n",
    "    # If the given problem is of type DAE, some items of the residual vector\n",
    "    # returned by the 'resfn' have to be treated as algebraic equations, and\n",
    "    # algebraic variables must be defined. These algebraic variables are\n",
    "    # denoted by the position (index) in the state vector y. All these\n",
    "    # indexes have to be specified in the 'algebraic_vars_idx' array.\n",
    "    algebraic_vars_idx=[np.arange(3+N,3+N+M,1)], \n",
    "    max_steps=5000,\n",
    "    old_api=False  # Forces use of new api (namedtuple)\n",
    ")\n",
    "\n",
    "times = np.arange(0,0.7,0.01)\n",
    "solution = solver.solve(times, vec0, vecp0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plot the results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 118632 characters]\n",
      "text/plain": [
       "<Figure size 864x864 with 6 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# plot velocity of gas along the flow direction\n",
    "f, ax = plt.subplots(3,2, figsize=(9,9), dpi=96)\n",
    "ax[0,0].plot(times, solution.values.y[:,0], color='C0')\n",
    "ax[0,0].set_xlabel('Distance (m)')\n",
    "ax[0,0].set_ylabel('Velocity (m/s)')\n",
    "\n",
    "# plot gas density along the flow direction\n",
    "ax[0,1].plot(times, solution.values.y[:,1], color='C1')\n",
    "ax[0,1].set_xlabel('Distance (m)')\n",
    "ax[0,1].set_ylabel('Density ($\\mathregular{kg/m^3}$)')\n",
    "ax[0,1].ticklabel_format(axis='y', style='sci', scilimits=(-2,2))  # scientific notation\n",
    "\n",
    "# plot major and minor gas species separately\n",
    "minor_idx = []\n",
    "major_idx = []\n",
    "for i,name in enumerate(gas.species_names): \n",
    "    mean = np.mean(solution.values.y[:,2+i])\n",
    "    if mean <= 0.01:\n",
    "        minor_idx.append(i) \n",
    "    else:\n",
    "        major_idx.append(i)\n",
    "\n",
    "# plot minor species\n",
    "for i in minor_idx:\n",
    "    style = '-' if i < 10 else '--'    \n",
    "    ax[1,0].plot(times, solution.values.y[:,2+i], label=gas.species_names[i], linestyle=style)\n",
    "ax[1,0].legend(fontsize=7, loc='upper right')\n",
    "ax[1,0].set_xlabel('Distance (m)')\n",
    "ax[1,0].set_ylabel('Mass Fraction')\n",
    "ax[1,0].ticklabel_format(axis='y', style='sci', scilimits=(-2,2))  # scientific notation\n",
    "\n",
    "# plot major species\n",
    "for j in major_idx:\n",
    "    ax[1,1].plot(times,solution.values.y[:,2+j], label=gas.species_names[j])\n",
    "ax[1,1].legend(loc='best')\n",
    "ax[1,1].set_xlabel('Distance (m)')\n",
    "ax[1,1].set_ylabel('Mass Fraction')\n",
    "\n",
    "# plot the pressure of the gas along the flow direction\n",
    "ax[2,0].plot(times, solution.values.y[:,2+N], color='C2')\n",
    "ax[2,0].set_xlabel('Distance (m)')\n",
    "ax[2,0].set_ylabel('Pressure (Pa)')\n",
    "\n",
    "# plot the site fraction of the surface species along the flow direction \n",
    "for i,name in enumerate(gas_Si_N_interface.species_names):\n",
    "    ax[2,1].plot(times, solution.values.y[:,3+N+i], label=name)\n",
    "ax[2,1].legend()\n",
    "ax[2,1].set_xlabel('Distance (m)')\n",
    "ax[2,1].set_ylabel('Site Fraction')\n",
    "f.tight_layout(pad=0.5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Case 2: Adiabatic reactor"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since the application of isothermal reactor is not prevalent, to improve the model for real use, the adiabatic reator is considered. Here, the energy balance equation is also considered.\n",
    "\n",
    "The heat flow rate into the system has two components. One is due to the heat flux $q_e$ from the surroundings to the outer tube wall (whose surface area per unit length is $a_e$) and accumulation of enthalpy in the bulk solid. The other is due to $q_i$, the heat flux to the gas from the inner tube wall, and accumulation of enthalpy in the surface species. The expression of energy balance equation for this problem is as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "\\begin{align}\n",
       "    \\rho u A_c c_p \\frac{dT}{dz} +A_c \\sum_{K_g}\\dot{\\omega}_k W_k h_k + p'\\sum_{K_g}h_k\\dot{s}_k W_k &= a_eq_e - p'\\sum^{K_b}_{bulk}\\dot{\\omega}_kh_k\\\\&=p'q_i + p'\\sum^{K_g}_{gas}\\dot{s_k}W_kh_k\n",
       "\\end{align}"
      ],
      "text/plain": [
       "<IPython.core.display.Latex object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%latex\n",
    "\\begin{align}\n",
    "    \\rho u A_c c_p \\frac{dT}{dz} +A_c \\sum_{K_g}\\dot{\\omega}_k W_k h_k + p'\\sum_{K_g}h_k\\dot{s}_k W_k &= a_eq_e - p'\\sum^{K_b}_{bulk}\\dot{\\omega}_kh_k\\\\&=p'q_i + p'\\sum^{K_g}_{gas}\\dot{s_k}W_kh_k\n",
    "\\end{align}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since the adiabatic reactor is considered, $q_e = 0$. Similar to the procedure for the isothermal reactor model, add the energy equation into the residual function and calculate the initial value of the spatial derivative of the temperature."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "############################### initial conditions ##################################################################\n",
    "# import the SiF4 + NH3 reaction mechanism\n",
    "mech = 'data/SiF4_NH3_mec.cti'\n",
    "# import the models for gas and bulk\n",
    "gas, bulk_Si, bulk_N = ct.import_phases(mech,['gas','SiBulk','NBulk'])\n",
    "\n",
    "# import the model for gas-Si-N interface\n",
    "gas_Si_N_interface = ct.Interface(mech, 'SI3N4',[gas,bulk_Si,bulk_N])\n",
    "T0 = 1713  # K\n",
    "p0 = 2 * ct.one_atm / 760.0  # Pa ~2Torr\n",
    "gas.TPX = T0, p0, \"NH3:6, SiF4:1\"\n",
    "bulk_Si.TP = T0, p0\n",
    "bulk_N.TP = T0, p0\n",
    "gas_Si_N_interface.TP = T0, p0\n",
    "D = 5.08e-2  # diameter of the tube [m]\n",
    "Ac = np.pi * D**2/4  # cross section of the tube [m]\n",
    "mu = 5.7e-5  # kg/(m-s) dynamic viscosity\n",
    "perim = np.pi * D  # perimeter of the tube\n",
    "# calculate the site fractions of surface species at the entrance of the tube at steady state\n",
    "gas_Si_N_interface.advance_coverages(100.0)\n",
    "Zk_0 = gas_Si_N_interface.coverages\n",
    "######################################## IDA solver ###################################################################\n",
    "def residual(z, vec, vecp, result):\n",
    "    \"\"\" we create the residual equations for the problem\n",
    "        vec = [u, rho, Yk, p, Zk, T]\n",
    "        vecp = [dudz, drhodz, dYkdz, dpdz, dZkdz, dTdz]\n",
    "    \"\"\"\n",
    "    # temporary variables\n",
    "    u = vec[0]  # velocity\n",
    "    rho = vec[1]  # density\n",
    "    Y = vec[2:2+N]  # vector of mass fractions of all gas species\n",
    "    p = vec[2+N]  # pressure\n",
    "    Z = vec[3+N:-1]  # vector of site fractions of all surface species\n",
    "    T = vec[-1]  # temperature\n",
    "    \n",
    "    dudz = vecp[0]  # velocity spatial derivative\n",
    "    drhodz = vecp[1]  # density spatial derivative\n",
    "    dYdz = vecp[2:2+N]  # mass fraction spatial derivative\n",
    "    dpdz = vecp[2+N]  # pressure spatial derivative\n",
    "    dTdz = vecp[-1]  # temperature spatial derivative\n",
    "    \n",
    "    h = gas.enthalpy_mass  # enthalpy of gas species per mass\n",
    "    h_Si = bulk_Si.enthalpy_mass  # enthalpy of Si per mass\n",
    "    h_N = bulk_N.enthalpy_mass  # enthalpy of N per mass\n",
    "    \n",
    "    # initial conditions\n",
    "    gas.set_unnormalized_mass_fractions(Y)\n",
    "    gas.TP = T, p\n",
    "    \n",
    "    bulk_Si.TP = T, p\n",
    "    bulk_N.TP = T, p\n",
    "    gas_Si_N_interface.set_unnormalized_coverages(Z)\n",
    "    gas_Si_N_interface.TP = T, p\n",
    "    \n",
    "    # temporary variables (based on the current system state)\n",
    "    coverages = gas_Si_N_interface.coverages  # site fraction vector\n",
    "    sdot_g = gas_Si_N_interface.net_production_rates[:N]  # heterogeneous production rate of gas species\n",
    "    sdot_s = gas_Si_N_interface.net_production_rates[-M:]  # molar production rate of surface speceis\n",
    "    wdot_g = gas.net_production_rates  # homogeneous production rate of gas species\n",
    "    W_g = gas.molecular_weights  # vector of molecular weight of gas species\n",
    "    W_Si_b = bulk_Si.molecular_weights\n",
    "    W_N_b = bulk_N.molecular_weights\n",
    "    bdot = gas_Si_N_interface.net_production_rates[gas.n_species:gas.n_species+2]  # bulk production rate\n",
    "   \n",
    "    # mass continuity equation\n",
    "    result[0] = u*drhodz+rho*dudz-perim*np.sum(sdot_g*W_g)/Ac\n",
    "    # conservation of species\n",
    "    for k in range(gas.n_species):\n",
    "        result[1+k] = (rho*u*Ac*dYdz[k] + Y[k]*perim*np.sum(sdot_g*W_g)\n",
    "                       - wdot_g[k]*W_g[k]*Ac\n",
    "                       - sdot_g[k]*W_g[k]*perim)\n",
    "    # conservation of momentum\n",
    "    result[1+gas.n_species] = 2*rho*u*dudz + np.power(u,2)*drhodz + dpdz + 32*u*mu/D**2\n",
    "\n",
    "    # equation of state\n",
    "    result[2+gas.n_species] = gas.density - rho\n",
    "\n",
    "    # algebraic constraints\n",
    "    for j in range(M):\n",
    "        result[3+N+j] = sdot_s[j]\n",
    "    \n",
    "    # replace the constraints with the condition sum(Zk) = 1 for the largest site fraction species\n",
    "    index = np.argmax(coverages)\n",
    "    result[3+N+index] = np.sum(coverages) - 1\n",
    "    \n",
    "    # energy equation\n",
    "    result[3+N+M] = (rho*u*Ac*gas.cp*dTdz\n",
    "        + Ac*np.sum(wdot_g*W_g*h)\n",
    "        + perim*np.sum(h*sdot_g*W_g)\n",
    "        + perim*(bdot[0]*W_Si_b*h_Si + bdot[1]*W_N_b*h_N))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "######## Solve linear system for the initial values of vecp ###########\n",
    "\"\"\"\n",
    "   a = coefficient of [u', rho', Yk', P',T]\n",
    "   b = RHS constant of each conservation equations\n",
    "\"\"\"\n",
    "rho0 = gas.density  # initial density of the flow\n",
    "u0 = 11.53  # m/s initial velocity of the flow\n",
    "W = gas.molecular_weights\n",
    "W_avg = gas.mean_molecular_weight\n",
    "sdot = gas_Si_N_interface.net_production_rates  # heterogeneous molar production rate\n",
    "wdot = gas.net_production_rates  # homogeneours molar production rate\n",
    "h = gas.enthalpy_mass\n",
    "h_Si = bulk_Si.enthalpy_mass\n",
    "h_N = bulk_N.enthalpy_mass\n",
    "W_Si = bulk_Si.molecular_weights\n",
    "W_N = bulk_N.molecular_weights\n",
    "################### a #########################\n",
    "a = np.zeros((4+N,4+N))\n",
    "a[0,:] = np.hstack((rho0, u0, np.zeros(2+N)))\n",
    "for i in range(N):\n",
    "    a[1+i,2+i] = rho0*u0*Ac\n",
    "a[1+N,:] = np.hstack((2*rho0*u0, u0**2, np.zeros(N), 1,0))\n",
    "coef = np.zeros(N)\n",
    "for j in range(N):\n",
    "    coef[j] = gas.P/W[j]/np.power(np.sum(gas.Y/W),2)\n",
    "a[2+N,:] = np.hstack((0, ct.gas_constant*T0, coef, -W_avg, 0))\n",
    "a[3+N,:] = np.hstack((np.zeros(3+N), rho0*u0*Ac*gas.cp))\n",
    "################### b ###########################\n",
    "b = np.zeros(4+gas.n_species)\n",
    "b[0] = perim*np.sum(sdot[:N]*W)/Ac\n",
    "for i in range(N):\n",
    "    b[1+i] = (wdot[i]*W[i]*Ac\n",
    "              + sdot[i]*W[i]*perim\n",
    "              - gas.Y[i]*perim*np.sum(sdot[:N]*W))\n",
    "b[1+gas.n_species] = -32*u0*mu/D**2\n",
    "b[2+gas.n_species] = 0\n",
    "b[3+gas.n_species] = (- Ac*np.sum(wdot*W*h)\n",
    "                      - perim*np.sum(h*sdot[:N]*W)\n",
    "                      - perim*np.sum(sdot[N]*W_Si*h_Si + sdot[N+1]*W_N*h_N))\n",
    "part_vecp0 = np.linalg.solve(a,b)\n",
    "\n",
    "vecp0 = np.hstack((part_vecp0[:-1], np.zeros(M), part_vecp0[-1]))\n",
    "vec0 = np.hstack((11.53, gas.density, gas.Y, gas.P, Zk_0, T0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "solver = dae(\n",
    "    'ida',\n",
    "    residual, \n",
    "    atol=1e-8,  # absolute tolerance for solution\n",
    "    rtol=1e-8,  # relative tolerance for solution\n",
    "    algebraic_vars_idx=[np.arange(3+N,3+N+M,1)], \n",
    "    max_steps=5000,\n",
    "    one_step_compute=True,\n",
    "    old_api=False\n",
    ")\n",
    "\n",
    "time = []\n",
    "solution = []\n",
    "state = solver.init_step(0.0, vec0, vecp0)\n",
    "while state.values.t < 0.7:\n",
    "    time.append(state.values.t)\n",
    "    solution.append(state.values.y)\n",
    "    state = solver.step(0.7)\n",
    "\n",
    "time = np.array(time)\n",
    "solution = np.array(solution)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 139164 characters]\n",
      "text/plain": [
       "<Figure size 864x1152 with 8 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "f, ax = plt.subplots(4,2, figsize=(9,12), dpi=96)\n",
    "\n",
    "# plot gas velocity along the flow direction\n",
    "ax[0,0].plot(time, solution[:,0], color='C0')\n",
    "ax[0,0].set_xlabel('Distance (m)')\n",
    "ax[0,0].set_ylabel('Velocity (m/s)')\n",
    "\n",
    "# plot gas density along the flow direction\n",
    "ax[0,1].plot(time, solution[:,1], color='C1')\n",
    "ax[0,1].set_xlabel('Distance (m)')\n",
    "ax[0,1].set_ylabel('Density ($\\mathregular{kg/m^3}$)')\n",
    "ax[0,1].ticklabel_format(axis='y', style='sci', scilimits=(-2,2))  # scientific notation\n",
    "\n",
    "# plot major and minor gas species separately\n",
    "minor_idx = []\n",
    "major_idx = []\n",
    "for i,name in enumerate(gas.species_names): \n",
    "    mean = np.mean(solution[:,2+i])\n",
    "    if mean <= 0.01:\n",
    "        minor_idx.append(i) \n",
    "    else:\n",
    "        major_idx.append(i)\n",
    "\n",
    "# plot minor gas species along the flow direction\n",
    "for i in minor_idx:\n",
    "    style = '-' if i < 10 else '--'\n",
    "    ax[1,0].plot(time, solution[:,2+i], label=gas.species_names[i], linestyle=style)\n",
    "ax[1,0].legend(fontsize=7.5, loc='best')\n",
    "ax[1,0].set_xlabel('Distance (m)')\n",
    "ax[1,0].set_ylabel('Mass Fraction')\n",
    "ax[1,0].ticklabel_format(axis='y', style='sci', scilimits=(-2,2))  # scientific notation\n",
    "\n",
    "# plot major gas species along the flow direction\n",
    "for j in major_idx:\n",
    "    ax[1,1].plot(time, solution[:,2+j], label=gas.species_names[j])\n",
    "ax[1,1].legend(fontsize=8, loc='best')\n",
    "ax[1,1].set_xlabel('Distance (m)')\n",
    "ax[1,1].set_ylabel('Mass Fraction')\n",
    "\n",
    "# plot the pressure of the gas along the flow direction\n",
    "ax[2,0].plot(time, solution[:,2+N], color='C2')\n",
    "ax[2,0].set_xlabel('Distance (m)')\n",
    "ax[2,0].set_ylabel('Pressure (Pa)')\n",
    "\n",
    "# plot the site fraction of the surface species along the flow direction\n",
    "for i,name in enumerate(gas_Si_N_interface.species_names):\n",
    "    ax[2,1].plot(time, solution[:,3+N+i], label=name)\n",
    "ax[2,1].legend(fontsize=8)\n",
    "ax[2,1].set_xlabel('Distance (m)')\n",
    "ax[2,1].set_ylabel('Site Fraction')\n",
    "\n",
    "# plot the temperature profile along the flow direction\n",
    "ax[3,0].plot(time, solution[:,-1], color='C3')\n",
    "ax[3,0].set_xlabel('Distance (m)')\n",
    "ax[3,0].set_ylabel('Temperature (K)')\n",
    "f.tight_layout(pad=0.5)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
