{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "try:\n", " import google.colab\n", " %pip install -q ugropy\n", "except ImportError:\n", " pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Properties estimators\n", "\n", "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ipqa-research/ugropy/blob/main/docs/source/tutorial/properties_estimators.ipynb)\n", "\n", "Some group contribution models could be used to predict properties of pure\n", "substances. The `get_groups` function of PropertiesEstimator works the same\n", "as the `get_groups` function of the GibbsModels, for that, please refer to the\n", "GibbsModels section of the tutorial.\n", "\n", "### Joback model\n", "\n", "The [Joback model](https://doi.org/10.1080/00986448708960487) is a well known\n", "model to estimate properties of pure substances. We can use it independently of\n", "the Groups class by doing:" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'-CH3': 1, 'ring=CH-': 5, 'ring=C<': 1}\n" ] } ], "source": [ "from ugropy import joback\n", "\n", "toluene = joback.get_groups(\"toluene\")\n", "\n", "print(toluene.subgroups)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "-CH3: 1ring=CH-: 5ring=C<: 1" ], "text/plain": [ "" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "toluene.draw(width=500)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "41.144119209325225 bar\n", "319.5 centimeter ** 3 / mole\n", "598.0611700010388 kelvin\n", "0.25208296412216535 dimensionless\n", "195.07 kelvin\n" ] } ], "source": [ "print(toluene.critical_pressure)\n", "print(toluene.critical_volume)\n", "print(toluene.critical_temperature)\n", "print(toluene.acentric_factor)\n", "print(toluene.fusion_temperature)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You may notice that the attributes of the `Joback` class are numbers with \n", "units. This is possible thanks to the `pint` library. The `pint` library\n", "allows us to work with units in a very simple way. For example, we can\n", "convert the value of the `critical_pressure` attribute to pascal by doing:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "4114411.9209325225 pascal" ], "text/latex": [ "$4114411.9209325225\\ \\mathrm{pascal}$" ], "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "toluene.critical_pressure.to(\"Pa\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you want to obtain the raw numerical value without units, you can use the\n", "`magnitude` attribute:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "np.float64(41.144119209325225)" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "toluene.critical_pressure.magnitude" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or combine both functionalities:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "np.float64(30860.62289092802)" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "toluene.critical_pressure.to(\"mmHg\").magnitude" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For more information about the [pint library](https://github.com/hgrecco/pint),\n", "you can visit their documentation." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On the other hand, we can use the `Joback` class to estimate\n", "temperature-dependent properties of the molecule (also with `pint` units). In\n", "the API documentation, you can check the available methods to estimate\n", "temperature-dependent properties and how it's done." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.923433500943906 bar\n" ] } ], "source": [ "print(toluene.vapor_pressure(110 + 273.15))" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.0004848511681835698 pascal * second\n" ] } ], "source": [ "print(toluene.viscosity_liquid(25 + 273.15))" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "174.140191226778 joule / kelvin / mole\n" ] } ], "source": [ "print(toluene.heat_capacity_liquid(50 + 273.15))" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/html": [ "150.13004821343338 joule/(kelvin mole)" ], "text/latex": [ "$150.13004821343338\\ \\frac{\\mathrm{joule}}{\\left(\\mathrm{kelvin} \\cdot \\mathrm{mole}\\right)}$" ], "text/plain": [ "" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "toluene.heat_capacity_ideal_gas(150 + 273.15)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can check the API documentation of the `JobackFragmentationResult` class\n", "to see all the properties that can be estimated." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Joback model allows the user to provide the experimental normal boiling\n", "temperature of the substance to increase the accuracy of some properties\n", "estimations. This value will be used instead of the Joback estimated normal\n", "boiling temperature to calculate some properties, for example the critical\n", "temperature." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "toluene = joback.get_groups(\"toluene\", normal_boiling_point=(110.6 + 273.15))" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "593.8980798775972 kelvin\n" ] } ], "source": [ "print(toluene.critical_temperature)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.0132500000000007 bar\n" ] } ], "source": [ "print(toluene.vapor_pressure(110.6 + 273.15))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Abdulelah-Gani model\n", "\n", "https://github.com/PEESEgroup/Pure-Component-Property-Estimation\n", "\n", "The recently developed [Abdulelah-Gani model](https://doi.org/10.1002/aic.17469)\n", "is also available in `ugropy`. This model is in an early stage of development\n", "and it is not as complete as the original publication. Some of tertiary\n", "structures are not yet implemented and for that, the properties that depend on\n", "them are not available. Let's see how to use it:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "from ugropy import abdulelah_gani" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "adrenaline = abdulelah_gani.get_groups(\"adrenaline\")" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'CH3': 1, 'aCH': 3, 'aC-CH': 1, 'OH': 1, 'aC-OH': 2, 'CH2NH': 1}" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "adrenaline.primary.subgroups" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'CHm(OH)CHn(NHp) (m,n,p in 0..2)': 1,\n", " 'aC-CHn-OH (n in 1..2)': 1,\n", " 'AROMRINGs1s2s4': 1}" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "adrenaline.secondary.subgroups" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{}" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "adrenaline.tertiary.subgroups" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, The Abdulelah-Gani model includes different kinds of subgropus.\n", "The primary structures works the as the other models we have discussed before.\n", "The secondary and tertiary are additional structures that are used to increase\n", "the accuracy of the estimations. Also, with secondary and tertiary structures,\n", "the model differentiates isomers. For example:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "hexa23 = abdulelah_gani.get_groups(\"2,3-dimethylhexane\")\n", "hexa24 = abdulelah_gani.get_groups(\"2,4-dimethylhexane\")" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/html": [ "564.8418659474339 kelvin" ], "text/latex": [ "$564.8418659474339\\ \\mathrm{kelvin}$" ], "text/plain": [ "" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hexa23.critical_temperature" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ "549.3932758799738 kelvin" ], "text/latex": [ "$549.3932758799738\\ \\mathrm{kelvin}$" ], "text/plain": [ "" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hexa24.critical_temperature" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'CH3': 4, 'CH2': 2, 'CH': 2}\n", "{'CH3': 4, 'CH2': 2, 'CH': 2}\n" ] } ], "source": [ "print(hexa23.primary.subgroups)\n", "print(hexa24.primary.subgroups)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'(CH3)2CH': 1, 'CH(CH3)CH(CH3)': 1}\n", "{'(CH3)2CH': 1}\n" ] } ], "source": [ "print(hexa23.secondary.subgroups)\n", "print(hexa24.secondary.subgroups)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you can see, both molecules have the same primary structure but different\n", "secondary structures. This is why the properties are different. On a Property \n", "Estimator that only have primary structures like the Joback model we would get\n", "the same properties for both molecules." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "552.9339856842008 kelvin\n", "552.9339856842008 kelvin\n" ] } ], "source": [ "print(joback.get_groups(\"2,3-dimethylhexane\").critical_temperature)\n", "print(joback.get_groups(\"2,4-dimethylhexane\").critical_temperature)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "All the properties that can be estimated with the Abdulelah-Gani model\n", "(implemented on `ugropy`) are:" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "564.8418659474339 kelvin\n", "26.35038783694938 bar\n", "465.335 centimeter ** 3 / mole\n", "0.35672642267081217 dimensionless\n", "-214.82596509091175 kilojoule / mole\n", "15.59002996305599 kilojoule / mole\n" ] } ], "source": [ "print(hexa23.critical_temperature)\n", "print(hexa23.critical_pressure)\n", "print(hexa23.critical_volume)\n", "print(hexa23.acentric_factor)\n", "print(hexa23.ig_formation_enthalpy)\n", "print(hexa23.ig_formation_gibbs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you refer to the original publication you will notice that the \n", "Abdulelah-Gani model allows to estimate the properties by two methods:\n", "\n", "- GC-SIMPLE: Algebraic correlations\n", "- ML: trained neural networks\n", "\n", "`ugropy` evaluates the properties using the GC-SIMPLE method. The ML method\n", "is not provided in this library. To learn how evaluate the properties with the\n", "ML method, please refer to the original publication supplementary material:\n", "\n", "https://github.com/PEESEgroup/Pure-Component-Property-Estimation\n", "\n", "However, `ugropy` helps you a little to evaluate the trained neural networks.\n", "The fragmentation results give you the numpy array needed to evaluate the ML \n", "model ready to use:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[4, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", " 0, 0, 0, 0, 0, 0]])" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hexa23.ml_vector" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To visualize the fragmentation results you can use the `draw` method\n", "for each structure type:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "CH3: 4CH2: 2CH: 2" ], "text/plain": [ "" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hexa24.primary.draw(width=700)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "(CH3)2CH: 1" ], "text/plain": [ "" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hexa24.secondary.draw(width=700)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The example molecules doesn't have tertiary structures, let's get the solution\n", "for a molecule that has tertiary structures:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "fluorene = abdulelah_gani.get_groups(\"9H-Fluorene\")" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "aCH: 8aC fused with aromatic ring: 2aC fused with non-aromatic ring: 2CH2 (cyclic): 1" ], "text/plain": [ "" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fluorene.primary.draw(width=800)" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "aC-aC (different rings): 1aC-CHncyc (fused rings) (n in 0..1): 2AROM.FUSED[2]: 2" ], "text/plain": [ "" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fluorene.tertiary.draw(width=800)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, getting multiple solutions. Secondary and tertiary structures can\n", "be overlapped so they doesn't generate multiple solutions. But of course, the\n", "primary structures will generate multiple solutions. Let's see an example:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [], "source": [ "mol = abdulelah_gani.get_groups(\"COc1ccccc1N(=O)=O\", \"smiles\", search_multiple_solutions=True)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "CH3: 1aCH: 4aC-O: 1aC-NO2: 1" ], "text/plain": [ "" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mol[0].primary.draw(width=600)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "aCH: 4aC except as above: 1CH3O: 1aC-NO2: 1" ], "text/plain": [ "" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mol[1].primary.draw(width=600)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Both solutions have the same secondary and tertiary results, but different\n", "primary fragmentations. This is why the properties are different." ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/html": [ "750.949961574131 kelvin" ], "text/latex": [ "$750.949961574131\\ \\mathrm{kelvin}$" ], "text/plain": [ "" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mol[0].critical_temperature" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/html": [ "784.2891401277112 kelvin" ], "text/latex": [ "$784.2891401277112\\ \\mathrm{kelvin}$" ], "text/plain": [ "" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mol[1].critical_temperature" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What a difference!! In this case, the second solution provides a much better\n", "estimation of the critical temperature (782 K). You can check it by yourself on\n", "the publication original dataset." ] } ], "metadata": { "kernelspec": { "display_name": "ugropy", "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.13" } }, "nbformat": 4, "nbformat_minor": 2 }