{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f225380f",
   "metadata": {},
   "source": [
    "# Example: Departure functions from Peng-Robinson equation of state\n",
    "\n",
    "These are codes to calculate the enthalpy departure function from the complete, generalized Peng-Robinson equation of state as given in SIS 5th edition equations 6.4-2 and 6.7-1 to 6.7-4. \n",
    "\n",
    "We use it to calculate analyze an isenthalpic expansion of methane through a throttling process.\n",
    "\n",
    "Eric Furst  \n",
    "November 2024\n",
    "\n",
    "Version updates:  \n",
    "Revisions in layout, calculation of PR EOS (11/2025)\n",
    "\n",
    "----"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28594ad2",
   "metadata": {},
   "source": [
    "As ususal, we import the *numpy* and *matplotlib* libraries. We'll also use the *SciPy* constants library."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "8ba8e9f4",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from scipy import constants\n",
    "import scipy.optimize as opt\n",
    "\n",
    "R = constants.R # Set the gas constant to R"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8672870c-eb49-47d7-b862-437a5b5515d6",
   "metadata": {},
   "source": [
    "## Problem statement\n",
    "\n",
    "Methane gas undergoes a rapid, adiabatic expansion through a continous throttling process from upstream conditions ${\\text{13}^\\circ}\\text{C}$ and 184 bar to a lower pressure at ${\\text{-43}^\\circ}\\text{C}$ .\n",
    "\n",
    "Calculate the downstream gas pressure.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d2e66192-d13c-4c0d-9677-6a0e88410ff3",
   "metadata": {},
   "source": [
    "To solve this problem, we will calculate the enthalpy of methane such that it satisfies an isenthalpic condition. In terms of the departure function values, this is \n",
    "$$\\Delta \\underline{H} = \\Delta \\underline{H}^\\mathrm{IG} + \\Delta [ \\underline{H} - \\underline{H}^\\mathrm{IG}] = 0$$\n",
    "or\n",
    "$$\\Delta \\underline{H}^\\mathrm{IG} + [ \\underline{H} - \\underline{H}^\\mathrm{IG}]_2 - [ \\underline{H} - \\underline{H}^\\mathrm{IG}]_1 = 0$$\n",
    "The specific departure functions we will use are derived from the generalized Peng-Robinson equation of state. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9cacacf5",
   "metadata": {},
   "source": [
    "# Generalized Peng-Robinson Equation of State\n",
    "\n",
    "The Generalized Peng-Robinson equation of state is\n",
    "\n",
    "$$ P = \\frac{RT}{\\underline{V}-b} - \\frac{a(T)}{\\underline{V}(\\underline{V}+b) + b(\\underline{V}-b)} \\tag{Eq. 6.4-2}$$\n",
    "\n",
    "with\n",
    "\n",
    "$$ b = 0.07780 \\frac{RT_c}{P_c} \\tag{Eq. 6.7-2}$$\n",
    "$$ a(T) = a(T_c)\\alpha(T) = 0.45724 \\frac{R^2T_c^2}{P_c}\\alpha(T) \\tag{Eq. 6.7-1}$$\n",
    "$$ \\sqrt{\\alpha} = 1 + \\kappa \\left ( 1- \\sqrt{\\frac{T}{T_c}} \\right ) \\tag{Eq. 6.7-3}$$\n",
    "$$ \\kappa = 0.37464 + 1.54226\\omega − 0.26992\\omega^2 \\tag{Eq. 6.7-4}$$\n",
    "\n",
    "The acentric factor $\\omega$ and the crticial temperatures and pressures are given in SIS table 6.6-1.\n",
    "\n",
    "Calculating the pressure $P$ given $\\underline{V}$ and $T$ is straightforward, but to calculate the molar volume given $P$ and $T$, we need to solve the cubic equation of state of the form\n",
    "\n",
    "$$ Z^3 + \\alpha Z^2 + \\beta Z + \\gamma = 0 \\tag{Eq. 6.4-4}$$\n",
    "\n",
    "where $Z$ is the compressibility factor\n",
    "\n",
    "$$ Z = \\frac{P \\underbar{V{}}}{RT} $$\n",
    "\n",
    "For the Peng-Robinson EOS,\n",
    "\n",
    "$$ \\alpha = -1 + B $$\n",
    "$$ \\beta = A - 3B^2 -2B $$\n",
    "$$ \\gamma = -AB + B^2 + B^3 $$\n",
    "\n",
    "and \n",
    "\n",
    "$$ A = \\frac{aP}{(RT)^2} $$\n",
    "$$ B = \\frac{bP}{RT} $$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "f3d2f917",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Generalized Peng-Robinson EOS \n",
    "PR_pressure returns the pressure given V, T, Pc, Tc, omega\n",
    "PR_volume returns all molar volumes (real roots of EOS) given P, T, Pc, Tc, omega\n",
    "\"\"\"\n",
    "\n",
    "from scipy import constants\n",
    "from numpy.polynomial import Polynomial\n",
    "\n",
    "R = constants.R # Set the gas constant to R\n",
    "\n",
    "def calc_b(Pc,Tc):\n",
    "    return 0.07780*R*Tc/Pc\n",
    "\n",
    "def calc_a(T,Pc,Tc,omega):\n",
    "    kappa = 0.37464 + 1.54226*omega - 0.26992*omega**2\n",
    "    sqrtalpha = 1 + kappa*(1-np.sqrt(T/Tc))\n",
    "    return 0.45724*R**2*Tc**2/Pc*sqrtalpha**2\n",
    "\n",
    "# Calculate the presure given V, T for PR EOS\n",
    "def PR_pressure(V,T,Pc,Tc,omega):\n",
    "    a = calc_a(T,Pc,Tc,omega)\n",
    "    b = calc_b(Pc,Tc)\n",
    "\n",
    "    P = R*T/(V-b) - a/(V*(V+b)+b*(V-b))\n",
    "    return P\n",
    "\n",
    "# Solve for the compressibility factor given P, T for PR EOS in m^3/mol\n",
    "# Note that we can return multiple real roots (up to three)\n",
    "# The largest and smallest will be the vapor and liquid, respectively\n",
    "def PR_compressibility(P, T, Pc, Tc, omega):\n",
    "    # Calculate a, b, A, and B\n",
    "    a = calc_a(T, Pc, Tc, omega)\n",
    "    b = calc_b(Pc, Tc)\n",
    "    A = a*P/R**2/T**2\n",
    "    B = b*P/R/T\n",
    "\n",
    "    # Definitions of alpha, beta, gamma in SIS Table 6.4-3 for PR EOS\n",
    "    alpha = -1 + B\n",
    "    beta = A - 3*B**2 -2*B\n",
    "    gamma = -A*B + B**2 + B**3\n",
    "\n",
    "    # polynomial with coefficients in increasing order: c0 + c1 x + c2 x**2 + ...\n",
    "    p = Polynomial([ gamma, beta, alpha, 1 ])  \n",
    "\n",
    "    roots = p.roots()        # returns all (possibly complex) roots of Z\n",
    "    real_roots = roots.real[abs(roots.imag) < 1e-12]  # filter real ones\n",
    "\n",
    "    return real_roots"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "74031b60-3999-49f8-91a2-eebfeeb09d83",
   "metadata": {},
   "source": [
    "## Data for methane\n",
    "\n",
    "For methane, we need the critical pressure and temperature and Pitzer's acentric factor:\n",
    "\n",
    "$P_c = 4.6$ MPa   \n",
    "$T_c = 190.6$ K  \n",
    "$\\underline{V}_c = 0.099\\,\\mathrm{m^3/kmol} = 9.9\\times10^{-5}\\,\\mathrm{m^3/mol}$  \n",
    "$\\omega = 0.008$  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "7579e5f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Critical values for Methane and the accentric factor\n",
    "# pressure in Pa, temperature in K\n",
    "Pc = 4.6e6\n",
    "Tc = 190.6\n",
    "omega = 0.008\n",
    "\n",
    "# Known values in problem\n",
    "T1 = 286        # Inlet temperature in K\n",
    "P1 = 18.4e6     # Inlet pressure in Pa\n",
    "T2 = 230        # Outlet temperature in K"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad238854-9629-4ce9-9f2a-0c92edcecc92",
   "metadata": {},
   "source": [
    "# Enthalpy calculation\n",
    "\n",
    "## Start by calculating $Z$ for the inlet condition"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "0bdc3b7d-bbe1-4358-91a2-8aeaa0d33040",
   "metadata": {},
   "outputs": [],
   "source": [
    "TR1 = T1/Tc      # Reduced temperature\n",
    "PR1 = P1/Pc      # Reduced pressure\n",
    "\n",
    "# Use the PR_vol function to calculate the molar volume\n",
    "Z1 = np.max(PR_compressibility(P1,T1,Pc,Tc,omega))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "ca49e458-2711-465b-add8-198d80ff5131",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Z1 = 0.77\n"
     ]
    }
   ],
   "source": [
    "print(\"Z1 = {:.2f}\".format(Z1))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4133d8cd",
   "metadata": {},
   "source": [
    "## Calculate the departure function for the inlet condition\n",
    "The enthalpy departure function in $(P,T)$ control variables for the Peng-Robinson equuation of state is\n",
    "\n",
    "$$[\\underline{H} - \\underline{H}^\\mathrm{IG}] = RT(Z-1) + \\frac{T\\frac{da}{dT}-a}{2\\sqrt2 b}\\ln \\left [ \\frac{Z+(1+\\sqrt2)B}{Z+(1-\\sqrt2)B} \\right ] \\tag{Eq. 6.4-29}$$\n",
    "\n",
    "where we need to evaluate the compressibility factor,\n",
    "$$Z = \\frac{P\\underline{V}}{RT}$$\n",
    "the scaled value of $b$,\n",
    "$$B = \\frac{bP}{RT}$$\n",
    "and the deriviative of $a(T)$ with respect to temperature given in SIS (p. 264),\n",
    "$$\\frac{da}{dT} = - 0.45724 \\frac{R^2T_c^2}{P_c} \\kappa \\sqrt{\\frac{\\alpha}{T T_c}}$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "b9c7dd01-8aca-45eb-9d06-7eb0d1d1c084",
   "metadata": {},
   "outputs": [],
   "source": [
    "# With Z already calculated, we define a function to calculate the departure function\n",
    "\n",
    "\"\"\"\n",
    "Enthalpy departure function from the Peng-Robinson equation of state (eq. 6.4-29)\n",
    "For convenience, we recalculate b, kappa, sqrtalpha, and a(T) here, but we could\n",
    "use the earlier functions, too.\n",
    "\n",
    "Uses globals Tc, Pc, R\n",
    "\"\"\"\n",
    "def calc_depH(T,P,Z):\n",
    "    b = 0.07780*R*Tc/Pc\n",
    "    B = b*P/R/T\n",
    "    kappa = 0.37464 + 1.54226*omega - 0.26992*omega**2\n",
    "    sqrtalpha = 1 + kappa*(1-(T/Tc)**0.5)\n",
    "    a = 0.45724*R**2*Tc**2/Pc*sqrtalpha**2\n",
    "    dadT = -0.45724*R*R*Tc*Tc/Pc*kappa*sqrtalpha/(T*Tc)**0.5\n",
    "\n",
    "    depH = R*T*(Z-1)+(T*dadT-a)/(2*2**0.5*b)*np.log((Z+(1+2**0.5)*B)/(Z+(1-2**0.5)*B))\n",
    "    return depH"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "c3dc8ef7-f035-4079-8e90-bb4cc500fbf9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "*** depH1 = -3134 J/mol\n"
     ]
    }
   ],
   "source": [
    "depH1 = calc_depH(T1,P1,Z1)\n",
    "\n",
    "print(f\"*** depH1 = {depH1:.0f} J/mol\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "6132bf00-dc64-4bbf-99cc-4e749d5f676b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.93\n"
     ]
    }
   ],
   "source": [
    "# I'm calculating this number to compare with corresponding states later...\n",
    "print(f\"{-depH1/Tc/4.184:.2f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9b0c2408-14d8-4e9b-b593-414804278b6d",
   "metadata": {},
   "source": [
    "## Calculate the change in enthalpy in the ideal state\n",
    "$$\\Delta\\underline{H}^\\mathrm{IG} = \\int_{T_1}^{T_2}C_p^*(T)dT$$\n",
    "where we will use the ideal state heat capacities summarized in Appendix A.II,\n",
    "$$C^*_p(T) = A + BT + CT^2 + DT^3$$\n",
    "resulting in\n",
    "$$\\Delta\\underline{H}^\\mathrm{IG} = A(T_2-T_1) + \\frac{1}{2}B(T_2^2 - T_1^2)+ \\frac{1}{3}C(T_2^3 - T_1^3) + \\frac{1}{4}D(T_2^4 - T_1^4)$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "b587ff94-888c-455a-b216-f68a3a7ff462",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Heat capacity data for N2, O2, CO2, CH4, C2H6\n",
    "params = np.array([\n",
    "    [28.83, -0.157, 0.808, -2.871, 1800],\n",
    "    [25.46, 1.519, -0.715, 1.311, 1800],\n",
    "    [22.243, 5.977, -3.499, 7.464, 1800],\n",
    "    [19.875, 5.021, 1.268, -11.004, 1500],\n",
    "    [6.895, 17.255, -6.402, 7.280, 1500]\n",
    "    ])\n",
    "\n",
    "i = params[3,:]  # equation parameters for methane\n",
    "\n",
    "deltaHIG = i[0]*(T2-T1) + i[1]*10**-2*(T2**2-T1**2)/2 \\\n",
    "            + i[2]*10**-5*(T2**3-T1**3)/3 \\\n",
    "            + i[3]*10**-9*(T2**4-T1**4)/4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "bb8d3194-40ce-463c-9914-5eed37d4f7de",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Delta HIG = -1875 J/mol\n"
     ]
    }
   ],
   "source": [
    "print(\"Delta HIG = {:.0f} J/mol\".format(deltaHIG))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d72851d-90a5-43f0-9839-02f5f495744b",
   "metadata": {},
   "source": [
    "Now solve\n",
    "$$[ \\underline{H} - \\underline{H}^\\mathrm{IG}]_2 = [ \\underline{H} - \\underline{H}^\\mathrm{IG}]_1 - \\Delta \\underline{H}^\\mathrm{IG}$$\n",
    "\n",
    "We are looking for a value of the departure function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "9907b191-2e60-4a7b-8f04-e2655ed4474c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "depH2 must equal -1259 J/mol\n"
     ]
    }
   ],
   "source": [
    "print(\"depH2 must equal {:.0f} J/mol\".format(depH1-deltaHIG))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5cf19a3-a21e-4fc0-a034-30d48834da91",
   "metadata": {},
   "source": [
    "Do this by guessing an outlet pressure, calculating $[ \\underline{H} - \\underline{H}^\\mathrm{IG}]_2$ and iterating (guessing new values of $P_2$) until the condition above is met."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "04bda783-43d6-4fb6-8f69-e87dc1a5a2be",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "depH2 = -1259 J/mol, deltaH = 0 J/mol <-- should be zero\n"
     ]
    }
   ],
   "source": [
    "# Guess:\n",
    "P2 = 4.145e6  # guess a pressure\n",
    "\n",
    "# Use the PR_compressibility function to calculate the molar volume and compressibility factor\n",
    "Z2 = np.max(PR_compressibility(P2,T2,Pc,Tc,omega))\n",
    "\n",
    "depH2 = calc_depH(T2,P2,Z2)\n",
    "#print(P2/Pc)\n",
    "print(\"depH2 = {:.0f} J/mol, deltaH = {:.0f} J/mol <-- should be zero\".format(depH2,deltaHIG+depH2-depH1))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "acfea36c-cb4a-4a12-8577-059d4790340e",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "# Change in molar entropy\n",
    "\n",
    "Now we can calcuate the change in molar entropy of the methane,\n",
    "\n",
    "$$\\Delta \\underline{S} = \\Delta \\underline{S}^\\mathrm{IG} + \\Delta [ \\underline{S} - \\underline{S}^\\mathrm{IG}]$$\n",
    "\n",
    "Since the throttling process is adiabatic, this change represents the rate of entropy generation, $\\underline{\\dot{S}}_\\mathrm{gen}$.\n",
    "\n",
    "The entropy departure function for the Peng-Robinson equation is\n",
    "$$[ \\underline{S} - \\underline{S}^\\mathrm{IG}] = R\\ln(Z-B) + \\frac{da/dT}{2\\sqrt{2}b}\\ln \\left [ \\frac{Z+(1+\\sqrt2)B}{Z+(1-\\sqrt2)B} \\right ] \\tag{Eq. 6.4-30}$$\n",
    "and the entropy change of the real fluid in an ideal state with $P$ and $T$ control variables is\n",
    "$$\\Delta \\underline{S}^\\mathrm{IG} = \\int_{T_1}^{T_2}\\frac{C_p^*(T)}{T}dT - R\\ln\\frac{P_2}{P_1}$$\n",
    "with $C_p^*(T)$ given above, \n",
    "$$\\Delta \\underline{S}^\\mathrm{IG} = A\\ln\\frac{T_2}{T_1} + B(T_2-T_1) + \\frac{1}{2}C(T_2^2-T_1^2) + \\frac{1}{3}D(T_2^3-T_1^3) - R\\ln\\frac{P_2}{P_1}$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "cea15f29-14c8-4b4e-91bc-b070b56856d1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(12.433452523746052)"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "deltaSIG = i[0]*np.log(T2/T1) + i[1]*10**-2*(T2-T1) + i[2]*10**-5*(T2**2-T1**2)/2 \n",
    "+ i[3]*10**-9*(T2**3-T1**3)/3 - R*np.log(P2/P1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "0050af8f-231e-401a-8583-4e536511082a",
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Entropy departure function from the Peng-Robinson equation of state (eq. 6.4-30)\n",
    "For convenience, we recalculate b, kappa, sqrtalpha, and a(T) here, but we could\n",
    "use the earlier functions, too.\n",
    "\n",
    "Uses globals Tc, Pc, R\n",
    "\"\"\"\n",
    "def calc_depS(T,P,Z):\n",
    "    b = 0.07780*R*Tc/Pc\n",
    "    B = b*P/R/T\n",
    "    kappa = 0.37464 + 1.54226*omega - 0.26992*omega**2\n",
    "    sqrtalpha = 1 + kappa*(1-np.sqrt(T/Tc))\n",
    "    a = 0.45724*R**2*Tc**2/Pc*sqrtalpha**2\n",
    "    dadT = -0.45724*R*R*Tc*Tc/Pc*kappa*sqrtalpha/np.sqrt(T*Tc)\n",
    "\n",
    "    depS = R*(Z-B)+dadT/(2*2**0.5*b)*np.log((Z+(1+2**0.5)*B)/(Z+(1-2**0.5)*B))\n",
    "    return depS\n",
    "\n",
    "depS1 = calc_depS(T1, P1, Z1)\n",
    "depS2 = calc_depS(T2, P2, Z2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "f21e77c3-ac10-4596-bcd1-1e1124cffdd6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "T1: 286.0 K, P1: 1.84e+07 Pa, Z1: 0.77\n",
      "T2: 230.0 K, P2: 4.14e+06 Pa, Z2: 0.79\n",
      "\n",
      "delSIG= -7.33, depS2= 4.9, depS1= 1.6\n",
      "\n",
      "Change in molar entropy: -4.0 J/mol.K\n"
     ]
    }
   ],
   "source": [
    "print(\"T1: {:.1f} K, P1: {:0.2e} Pa, Z1: {:.2f}\".format(T1,P1,Z1))\n",
    "print(\"T2: {:.1f} K, P2: {:0.2e} Pa, Z2: {:.2f}\\n\".format(T2,P2,Z2))\n",
    "print(\"delSIG= {:.2f}, depS2= {:.1f}, depS1= {:.1f}\\n\".format(deltaSIG,depS2,depS1))\n",
    "print(\"Change in molar entropy: {:.1f} J/mol.K\".format(deltaSIG + depS2 - depS1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b2f846c5",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "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.12.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
