{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Thermo property packages"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A [Thermo](../Thermo.txt) object defines a thermodynamic property package. To build a `Thermo` object, we must first define all the chemicals involed. The following example will show how this can be done through the creation of a property package for the co-production of biodiesel and ethanol from lipid-cane [[1]](#References):"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Creating chemicals"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can first start by defining the common chemicals already in the data base:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import thermosteam as tmo\n",
    "Biodiesel = tmo.Chemical('Biodiesel',\n",
    "                         search_ID='Methyl oleate')\n",
    "lipidcane_chemicals = tmo.Chemicals(\n",
    "    ['Water', 'Methanol', 'Ethanol', 'Glycerol',\n",
    "     'Glucose', 'Sucrose', 'H3PO4', 'P4O10', 'CO2',\n",
    "     'Octane', 'O2', Biodiesel])\n",
    "\n",
    "(Water, Methanol, Ethanol,\n",
    " Glycerol, Glucose, Sucrose,\n",
    " H3PO4, P4O10, CO2, Octane, O2, Biodiesel) = lipidcane_chemicals"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can assume that CO2 and O2 will always remain a gas in the process by setting the state:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "O2.at_state(phase='g')\n",
    "CO2.at_state(phase='g')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Similarly, we can assume glucose, sucrose, and phosphoric acid all remain as solids:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "H3PO4.at_state(phase='s')\n",
    "P4O10.at_state(phase='s')\n",
    "Glucose.at_state(phase='s')\n",
    "Sucrose.at_state(phase='s')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can define the solids in the process (both soluble and insoluble). We can use the `Chemical.blank` method to create a \"blank\" `Chemical` object and add the thermo models ourselves:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def create_new_chemical(ID, phase='s', **constants):\n",
    "    # Create a new solid chemical without any data\n",
    "    solid = tmo.Chemical.blank(ID, phase=phase, **constants)\n",
    "    \n",
    "    # Add chemical to the Chemicals object\n",
    "    lipidcane_chemicals.append(solid)\n",
    "    \n",
    "    return solid\n",
    "\n",
    "# Cellulose and hemicellulose are modeled\n",
    "# as their monomer minus on H2O.\n",
    "Cellulose = create_new_chemical('Cellulose',\n",
    "                                formula=\"C6H10O5\", # Hydrolyzed glucose monomer\n",
    "                                Hf=-975708.8)\n",
    "Hemicellulose = create_new_chemical('Hemicellulose',\n",
    "                                    formula=\"C5H8O5\", # Hydrolyzed xylose monomer\n",
    "                                    Hf=-761906.4)\n",
    "Flocculant = create_new_chemical('Flocculant', MW=1.)\n",
    "# Lignin is modeled as vanillin.\n",
    "Lignin = create_new_chemical('Lignin',\n",
    "                             formula='C8H8O3', # Vanillin\n",
    "                             Hf=-452909.632)\n",
    "Ash = create_new_chemical('Ash', MW=1.)\n",
    "Solids = create_new_chemical('Solids', MW=1.)\n",
    "DryYeast = create_new_chemical('DryYeast', MW=1., CAS='Yeast')\n",
    "NaOCH3 = create_new_chemical('NaOCH3', formula='NaOCH3')\n",
    "CaO = create_new_chemical('CaO', formula='CaO')\n",
    "HCl = create_new_chemical('HCl', formula='HCl')\n",
    "NaOH = create_new_chemical('NaOH', formula='NaOH')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that we are still missing the lipid, modeled as Triolein. However, Triolein is not in the data bank, so let's start making it from scratch:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "Lipid = create_new_chemical(\n",
    "    'Lipid',\n",
    "    phase='l',\n",
    "    Hf=-2193.7e3,\n",
    "    formula = 'C57H104O6',\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Instead of creating new models based on external sources, here we will approximate Triolein with the properties of Tripalmitin (which does exist in the data bank):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "Tripalmitin = tmo.Chemical('Tripalmitin').at_state(phase='l', copy=True)\n",
    "Lipid.copy_models_from(Tripalmitin, ['V', 'sigma', 'kappa', 'Cn'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All what is left is to fill the chemical properties. This done through the `add_model` method of the chemical model handles. Let's begin with the solids using data from [[1-4]](#References):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.1"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from thermosteam import functional as fn\n",
    "\n",
    "# Assume a constant volume for lipid\n",
    "lipid_molar_volume = fn.rho_to_V(rho=900, MW=Lipid.MW)\n",
    "Lipid.V.add_model(lipid_molar_volume)\n",
    "\n",
    "# Insolubles occupy a significant volume\n",
    "insoluble_solids = (Ash, Cellulose, Hemicellulose,\n",
    "                    Flocculant, Lignin, Solids, DryYeast, P4O10)\n",
    "for chemical in insoluble_solids:\n",
    "    V = fn.rho_to_V(rho=1540, MW=chemical.MW)\n",
    "    chemical.V.add_model(V, top_priority=True)\n",
    "\n",
    "# Solubles don't occupy much volume\n",
    "soluble_solids = (CaO, HCl, NaOH, H3PO4, Sucrose, Glucose) \n",
    "for chemical in soluble_solids:\n",
    "    V = fn.rho_to_V(rho=1e5, MW=chemical.MW)\n",
    "    chemical.V.add_model(V, top_priority=True)\n",
    "\n",
    "\n",
    "# Assume sodium methoxide has some of the same properities as methanol\n",
    "LiquidMethanol = Methanol.at_state(phase='l', copy=True)\n",
    "NaOCH3.copy_models_from(LiquidMethanol, ['V', 'sigma','kappa', 'Cn'])\n",
    "\n",
    "# Add constant models for molar heat capacity of solids\n",
    "Ash.Cn.add_model(0.09 * 4.184 * Ash.MW) \n",
    "CaO.Cn.add_model(1.02388 * CaO.MW) \n",
    "Cellulose.Cn.add_model(1.364 * Cellulose.MW) \n",
    "Hemicellulose.Cn.add_model(1.364 * Hemicellulose.MW)\n",
    "Flocculant.Cn.add_model(4.184 * Flocculant.MW)\n",
    "Lignin.Cn.add_model(1.364 * Lignin.MW)\n",
    "Solids.Cn.add_model(1.100 * Solids.MW)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We don't care much about the rest of the properties (e.g. thermal conductivity), so we can default them to the values of water:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "for chemical in lipidcane_chemicals: chemical.default()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finalize the chemicals by compiling:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "lipidcane_chemicals.compile()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This enables methods such as `<CompiledChemicals>.array` to create chemical data ordered according to the IDs, as well as `<CompiledChemicals>.get_index` to get the index of a chemical:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2., 0., 2., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
       "       0., 0., 0., 0., 0., 0., 0.])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lipidcane_chemicals.array(['Water', 'Ethanol'], [2, 2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[0, 2]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "lipidcane_chemicals.get_index(('Water', 'Ethanol'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After compiling, it is possible to set synonyms for indexing:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Water\n"
     ]
    }
   ],
   "source": [
    "lipidcane_chemicals.set_synonym('Water', 'H2O')\n",
    "print(lipidcane_chemicals.H2O)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Mixture objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before creating a `Thermo` object, we must define the mixing rules to calculate mixture properties. A `Mixture` object is able to calculate mixture properties through functors. In this example we will use a function to create a `Mixture` object with ideal mixing rules:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Mixture(\n",
      "    rule='ideal mixing', ...\n",
      "    rigorous_energy_balance=True,\n",
      "    include_excess_energies=False\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "mixture = tmo.mixture.ideal_mixture(lipidcane_chemicals,\n",
    "                                    rigorous_energy_balance=True,\n",
    "                                    include_excess_energies=False)\n",
    "mixture"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can use the mixture for estimating mixture properties:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "694.8275427425033"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "array = lipidcane_chemicals.array\n",
    "mol = array(['Water', 'Ethanol'], [2, 2])\n",
    "mixture.H('l', mol, 300, 101325)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "376.3036205916924"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mol = array(['Water', 'Ethanol'], [2, 2])\n",
    "mixture.Cn('l', mol, 300)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also estimate multi-phase mixture properties through methods that start with \"x\" (e.g. `xCn`):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "574.6439104469846"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "mol_liquid = array(['Water', 'Ethanol'], [2, 2])\n",
    "mol_vapor = array(['Water', 'Ethanol'], [2, 2])\n",
    "phase_data = [('l', mol_liquid), ('g', mol_vapor)]\n",
    "mixture.xCn(phase_data, T=300)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note: To implement a your own Mixture object, you can request help on this process through https://github.com/BioSTEAMDevelopmentGroup/thermosteam."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Thermo objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once the chemicals and mixture objects are finalized, we can compile them into a Thermo object:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Thermo(\n",
      "    chemicals=CompiledChemicals([Water, Methanol, Ethanol, Glycerol, Glucose, Sucrose, H3PO4, P4O10, CO2, Octane, O2, Biodiesel, Cellulose, Hemicellulose, Flocculant, Lignin, Ash, Solids, DryYeast, NaOCH3, CaO, HCl, NaOH, Lipid]),\n",
      "    mixture=Mixture(\n",
      "        rule='ideal mixing', ...\n",
      "        rigorous_energy_balance=True,\n",
      "        include_excess_energies=False\n",
      "    ),\n",
      "    Gamma=DortmundActivityCoefficients,\n",
      "    Phi=IdealFugacityCoefficients,\n",
      "    PCF=IdealPoyintingCorrectionFactors\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "thermo = tmo.Thermo(lipidcane_chemicals, mixture)\n",
    "thermo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that a Thermo object contains `ActivityCoefficients`, `FugacityCoefficients`, and `PoyintingCorrectionFactors` subclasses to define fugacity estimation methods. By default, the Dortmund modified UNIFAC method for estimating activities is selected, while ideal values for (vapor phase) fugacity coefficients and poyinting correction factos are selected. Additionally, a `Thermo` object defaults to ideal mixing rules for estimating mixture properties, and neglects excess properties in the calculation of enthalpy and entropy:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Mixture(\n",
      "    rule='ideal mixing', ...\n",
      "    rigorous_energy_balance=True,\n",
      "    include_excess_energies=False\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "thermo = tmo.Thermo(lipidcane_chemicals)\n",
    "thermo.mixture"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### References"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='References'></a>\n",
    "\n",
    "1. Huang, H., Long, S., & Singh, V. (2016) “Techno-economic analysis of biodiesel and ethanol co-production from lipid-producing sugarcane” Biofuels, Bioproducts and Biorefining, 10(3), 299–315. https://doi.org/10.1002/bbb.1640\n",
    "\n",
    "2. Hatakeyama, T., Nakamura, K., & Hatakeyama, H. (1982). Studies on heat capacity of cellulose and lignin by differential scanning calorimetry. Polymer, 23(12), 1801–1804. https://doi.org/10.1016/0032-3861(82)90125-2\n",
    "\n",
    "3. Thybring, E. E. (2014). Explaining the heat capacity of wood constituents by molecular vibrations. Journal of Materials Science, 49(3), 1317–1327. https://doi.org/10.1007/s10853-013-7815-6\n",
    "\n",
    "4. Murphy W. K., and K. R. Masters. (1978). Gross heat of combustion of northern red oak (Quercus rubra) chemical components. Wood Sci. 10:139-141."
   ]
  }
 ],
 "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.7.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
