├── dev-requirements.txt ├── emojichem ├── version.py ├── __init__.py └── data.py ├── .pre-commit-config.yaml ├── setup.py ├── .github └── workflows │ ├── tests.yml │ └── build.yml ├── tests └── test_emojichem.py ├── README.md ├── .gitignore └── colab └── EmojiChem.ipynb /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | pre-commit 2 | pytest 3 | -------------------------------------------------------------------------------- /emojichem/version.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.3.0" 2 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v2.2.3 4 | hooks: 5 | - id: trailing-whitespace 6 | - id: check-yaml 7 | - id: end-of-file-fixer 8 | - id: mixed-line-ending 9 | - repo: https://github.com/psf/black 10 | rev: "22.3.0" 11 | hooks: 12 | - id: black 13 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from glob import glob 2 | 3 | from setuptools import setup 4 | 5 | exec(open("emojichem/version.py").read()) 6 | 7 | with open("README.md", "r", encoding="utf-8") as fh: 8 | long_description = fh.read() 9 | 10 | setup( 11 | name="emojichem", 12 | version=__version__, 13 | description="Replace elements with emojis in rdkit", 14 | author="Andrew White", 15 | author_email="andrew.white@rochester.edu", 16 | url="https://github.com/whitead/emojichem", 17 | license="MIT", 18 | packages=["emojichem"], 19 | install_requires=["rdkit", "skunk"], 20 | test_suite="tests", 21 | long_description=long_description, 22 | long_description_content_type="text/markdown", 23 | classifiers=[ 24 | "Programming Language :: Python :: 3", 25 | "License :: OSI Approved :: MIT License", 26 | "Operating System :: OS Independent", 27 | ], 28 | ) 29 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | 2 | name: test 3 | 4 | on: 5 | push: 6 | branches: [ main ] 7 | pull_request: 8 | branches: [ main ] 9 | 10 | jobs: 11 | test: 12 | 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | python-version: [3.7, 3.8, 3.9, "3.10"] 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Set up Python ${{ matrix.python-version }} 21 | uses: actions/setup-python@v2 22 | with: 23 | python-version: ${{ matrix.python-version }} 24 | - name: Install dependencies 25 | run: | 26 | sudo apt-get install libgraphviz-dev graphviz 27 | python -m pip install --upgrade pip 28 | if [ -f dev-requirements.txt ]; then pip install -r dev-requirements.txt; fi 29 | - name: Install 30 | run: | 31 | pip install .[all] 32 | - name: Run Test 33 | run: | 34 | pytest tests 35 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | 2 | name: publish 3 | 4 | on: 5 | release: 6 | types: 7 | - created 8 | workflow_dispatch: 9 | 10 | jobs: 11 | publish: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python "3.9" 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: "3.9" 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | if [ -f dev-requirements.txt ]; then pip install -r dev-requirements.txt; fi 25 | - name: Install 26 | run: | 27 | pip install .[all] 28 | - name: Run Test 29 | run: | 30 | pytest tests 31 | - name: Build a binary wheel and a source tarball 32 | run: | 33 | pip install build 34 | python -m build --sdist --wheel --outdir dist/ . 35 | - name: Publish distribution 📦 to PyPI 36 | uses: pypa/gh-action-pypi-publish@master 37 | with: 38 | password: ${{ secrets.PYPI_API_TOKEN }} 39 | -------------------------------------------------------------------------------- /tests/test_emojichem.py: -------------------------------------------------------------------------------- 1 | from rdkit import Chem 2 | import emojichem 3 | 4 | 5 | def test_emojichem(): 6 | mol = Chem.MolFromSmiles("O=C(NCC1CCCCC1N)C2=CC=CC=C2C3=CC=C(F)C=C3C(=O)NC4CCCCC4") 7 | emojichem.emoji_draw(mol, "wow.svg") 8 | 9 | 10 | def test_emojichem_str(): 11 | emojichem.emoji_draw("O=C(NCC1CCCCC1N)C2=CC=CC=C2C3=CC=C(F)C=C3C(=O)NC4CCCCC4") 12 | 13 | 14 | def test_emojichem_grid(): 15 | smiles = """Oc1ccc(N2CCNCC2)cc1 16 | Cc1n[nH]c(C)c1CCN 17 | C[C@H](C(=O)O)[C@H](C)C(=O)O 18 | CSCCCN 19 | COC(=O)c1cc(C=O)ccc1O 20 | CC(C)(N)C(=O)N1CCCCC1 21 | N[C@H]1CCCC(=O)C1 22 | NC(=O)CCOc1ccccc1N 23 | Cc1ncc(CO)s1 24 | CC(C)n1cncn1 25 | COC(=O)c1cc(N)cc2cn[nH]c12 26 | C[C@H](c1cnn(C)c1)n1nccc1N 27 | C[C@@H]1CCS1(=O)=O 28 | COCCOc1cnc(N)cn1 29 | CNc1cnc(C)cn1 30 | N=C(N)c1scc2c1OCCO2 31 | C[C@@H]1COC[C@@H](C)N1CCCN 32 | COC[C@H](N)[C@]1(O)CCS[C@H]1C 33 | CCN(CC)C(N)=O 34 | CCc1nsc(N)n1 35 | NC(=S)Nc1cnccn1 36 | Cc1nn(C)cc1/C=C/C(=O)O""".split( 37 | "\n" 38 | ) 39 | 40 | emojichem.emoji_grid(smiles) 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # emoji-chem [![build](https://github.com/whitead/emoji-chem/actions/workflows/tests.yml/badge.svg)](https://whitead.github.io/emoji-chem/)[![PyPI version](https://badge.fury.io/py/emoji-chem.svg)](https://badge.fury.io/py/emoji-chem) 2 | 3 | 4 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/whitead/emoji-chem/blob/main/colab/EmojiChem.ipynb) 5 | 6 | ## Install 7 | 8 | ```sh 9 | pip install emojichem 10 | ``` 11 | 12 | ## Quickstart 13 | 14 | ```py 15 | import emojichem 16 | emojichem.emoji_draw('CCN(CC)C(=O)[C@H]1CN([C@@H]2Cc3c[nH]c4c3c(ccc4)C2=C1)C') 17 | ``` 18 | 19 | ## Example 20 | This is how it should look 21 | 22 | ![image](https://user-images.githubusercontent.com/908389/206943965-f57686f9-554b-476d-9f8a-24121206fce4.png) 23 | 24 | You can also make a grid with `emoji_grid`: 25 | 26 | ![image](https://user-images.githubusercontent.com/908389/206966105-b90f7e20-6921-40d4-be50-11e444e31411.png) 27 | 28 | 29 | 30 | ## Credit 31 | Emoji list was taken (with minor modifications) from Nicola Ga-stan (@nicgaston) in [this tweet](https://twitter.com/nicgaston/status/914311195305193472) 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | *.svg 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | pip-wheel-metadata/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | .python-version 88 | 89 | # pipenv 90 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 91 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 92 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 93 | # install all needed dependencies. 94 | #Pipfile.lock 95 | 96 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 97 | __pypackages__/ 98 | 99 | # Celery stuff 100 | celerybeat-schedule 101 | celerybeat.pid 102 | 103 | # SageMath parsed files 104 | *.sage.py 105 | 106 | # Environments 107 | .env 108 | .venv 109 | env/ 110 | venv/ 111 | ENV/ 112 | env.bak/ 113 | venv.bak/ 114 | 115 | # Spyder project settings 116 | .spyderproject 117 | .spyproject 118 | 119 | # Rope project settings 120 | .ropeproject 121 | 122 | # mkdocs documentation 123 | /site 124 | 125 | # mypy 126 | .mypy_cache/ 127 | .dmypy.json 128 | dmypy.json 129 | 130 | # Pyre type checker 131 | .pyre/ 132 | -------------------------------------------------------------------------------- /colab/EmojiChem.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "id": "drcxuk2HPLbI" 7 | }, 8 | "source": [ 9 | "# Emoji Chem\n", 10 | "\n", 11 | "https://github.com/whitead/emoji-chem" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": { 18 | "cellView": "form", 19 | "collapsed": true, 20 | "id": "DGZ0I9Kg1UMA" 21 | }, 22 | "outputs": [], 23 | "source": [ 24 | "# @title Install Packages\n", 25 | "!pip install -U emojichem@https://github.com/whitead/emoji-chem/archive/refs/heads/main.zip" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "metadata": { 32 | "cellView": "form", 33 | "colab": { 34 | "base_uri": "https://localhost:8080/", 35 | "height": 221 36 | }, 37 | "id": "6SyUt3j11WV2", 38 | "outputId": "1b79ec5b-77a5-4fda-8a7d-424fe3185fd3" 39 | }, 40 | "outputs": [], 41 | "source": [ 42 | "smiles = \"O=C(NCC1CCCCC1N)C2=CC=CC=C2C3=CC=C(F)C=C3C(=O)NC4CCCCC4\" # @param {type:\"string\"}\n", 43 | "from rdkit import Chem\n", 44 | "import emojichem\n", 45 | "from IPython.display import display, HTML\n", 46 | "\n", 47 | "\n", 48 | "mol = Chem.MolFromSmiles(smiles)\n", 49 | "emojichem.emoji_draw(mol)" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": null, 55 | "metadata": { 56 | "cellView": "form", 57 | "colab": { 58 | "base_uri": "https://localhost:8080/", 59 | "height": 639 60 | }, 61 | "id": "dRMxu5m126_S", 62 | "outputId": "4e95a152-b5f5-4709-9d53-06cfa85c4a15" 63 | }, 64 | "outputs": [], 65 | "source": [ 66 | "# @title The emojis\n", 67 | "# @markdown Emoji list was taken (with minor modifications) from Nicola Ga-stan (@nicgaston) in [this tweet](https://twitter.com/nicgaston/status/914311195305193472)\n", 68 | "\n", 69 | "import tabulate\n", 70 | "\n", 71 | "table = []\n", 72 | "cols = 4\n", 73 | "N = len(emojichem.data.elements)\n", 74 | "for i, e in enumerate(emojichem.data.elements):\n", 75 | " j = i % (N // cols)\n", 76 | " if j >= len(table):\n", 77 | " table.append([e, emojichem.data.emoji_dict[e]])\n", 78 | " else:\n", 79 | " table[j % N].extend([\"|\", e, emojichem.data.emoji_dict[e]])\n", 80 | "display(HTML(tabulate.tabulate(table, tablefmt=\"html\")))" 81 | ] 82 | } 83 | ], 84 | "metadata": { 85 | "colab": { 86 | "name": "emoji-chem.ipynb", 87 | "provenance": [] 88 | }, 89 | "kernelspec": { 90 | "display_name": "Python 3.7.8 64-bit", 91 | "language": "python", 92 | "name": "python3" 93 | }, 94 | "language_info": { 95 | "name": "python", 96 | "version": "3.7.8" 97 | }, 98 | "vscode": { 99 | "interpreter": { 100 | "hash": "3e5a039a7a113538395a7d74f5574b0c5900118222149a18efb009bf03645fce" 101 | } 102 | } 103 | }, 104 | "nbformat": 4, 105 | "nbformat_minor": 0 106 | } 107 | -------------------------------------------------------------------------------- /emojichem/__init__.py: -------------------------------------------------------------------------------- 1 | from .data import elem_dict, emoji_dict 2 | from rdkit import Chem 3 | from rdkit.Chem import Draw 4 | import xml.etree.ElementTree as ET 5 | from .version import __version__ 6 | import skunk 7 | 8 | 9 | def emoji_grid(mols, labels=None, filename=None): 10 | svgs = [] 11 | for m in mols: 12 | if type(m) is str: 13 | m = Chem.MolFromSmiles(m) 14 | svgs.append( 15 | emoji_draw(m, filename=None, width=200, height=200, return_svg=True) 16 | ) 17 | svg_data = skunk.layout_svgs(svgs, labels=labels) 18 | if filename is not None: 19 | with open(filename, "w") as f: 20 | f.write(svg_data) 21 | # try to display with ipython 22 | try: 23 | from IPython.display import SVG 24 | 25 | return SVG(svg_data) 26 | except ImportError: 27 | return svg_data 28 | 29 | 30 | def emoji_draw(mol, filename=None, width=300, height=200, return_svg=False): 31 | if type(mol) is str: 32 | mol = Chem.MolFromSmiles(mol) 33 | drawer = Chem.Draw.rdMolDraw2D.MolDraw2DSVG(width, height) 34 | drawer.drawOptions().bgColor = None 35 | drawer.drawOptions().baseFontSize = 1.0 36 | drawer.drawOptions().drawMolsSameScale = False 37 | drawer.DrawMolecule(mol) 38 | drawer.FinishDrawing() 39 | sp = {} 40 | 41 | for a in Chem.AddHs(mol).GetAtoms(): 42 | sp[f"atom-{a.GetIdx()}"] = emoji_dict[elem_dict[a.GetSymbol()]] 43 | svg_data = rewrite_svg(drawer.GetDrawingText(), sp) 44 | if filename is not None: 45 | with open(filename, "w") as f: 46 | f.write(svg_data) 47 | # try to display with ipython 48 | if not return_svg: 49 | try: 50 | from IPython.display import SVG 51 | 52 | return SVG(svg_data) 53 | except ImportError: 54 | pass 55 | return svg_data 56 | 57 | 58 | def extract_atom_size(path): 59 | spath = path.split() 60 | x, y = [], [] 61 | for i in range(0, len(spath), 3): 62 | if spath[i] == "M" or spath[i] == "L": 63 | x.append(float(spath[i + 1])) 64 | y.append(float(spath[i + 2])) 65 | elif spath[i] == "Q": 66 | x.append(float(spath[i + 3])) 67 | y.append(float(spath[i + 4])) 68 | dx = max(x) - min(x) 69 | dy = max(y) - min(y) 70 | return min(x), min(y), dx, dy 71 | 72 | 73 | def extract_bond_size(path): 74 | spath = path.replace(",", " ").split() 75 | # just iterate through assuming M, x, y, L, x, y 76 | x0 = float(spath[1]) 77 | y0 = float(spath[2]) 78 | x1 = float(spath[4]) 79 | y1 = float(spath[5]) 80 | dx = x1 - x0 81 | dy = y1 - y0 82 | return x0, y0, dx, dy 83 | 84 | 85 | def rewrite_svg(svg, sp): 86 | root = ET.fromstring(svg) 87 | SVGNS = "http://www.w3.org/2000/svg" 88 | marked = {} 89 | update = [] 90 | bonds = {} 91 | 92 | # this code is to find bond positions related to an atom 93 | for p in root.findall(".//{http://www.w3.org/2000/svg}path"): 94 | try: 95 | name = p.attrib["class"] 96 | except KeyError: 97 | continue 98 | # check if it's a bond that matches our atoms 99 | if "bond" not in name: 100 | continue 101 | atoms = [] 102 | for s in sp: 103 | if s in name: 104 | atoms.append(s) 105 | if len(atoms) > 0: 106 | x, y, dx, dy = extract_bond_size(p.attrib["d"]) 107 | for a in atoms: 108 | if a in bonds: 109 | bonds[a].append((x, y, dx, dy)) 110 | else: 111 | bonds[a] = [(x, y, dx, dy)] 112 | # now find which atom element is closest to a bond (and mark it) 113 | scores = {n: [] for n in sp} 114 | for p in root.findall(".//{http://www.w3.org/2000/svg}path"): 115 | try: 116 | name = p.attrib["class"] 117 | except KeyError: 118 | continue 119 | if name in sp and name in bonds: 120 | # get min distance to bond 121 | x, y, _, _ = extract_atom_size(p.attrib["d"]) 122 | mind = 1000000 123 | for b in bonds[name]: 124 | bx, by, _, _ = b 125 | mind = min(mind, (bx - x) ** 2 + (by - y) ** 2) 126 | scores[name].append((mind, p)) 127 | # now mark the closest element 128 | for n in scores: 129 | if len(scores[n]) < 2: 130 | continue 131 | scores[n].sort(key=lambda x: x[0]) 132 | marked[n] = scores[n][0][1] 133 | for p in root.findall(".//{http://www.w3.org/2000/svg}path"): 134 | try: 135 | name = p.attrib["class"] 136 | except KeyError: 137 | continue 138 | if name in sp: 139 | if name in marked and p != marked[name]: 140 | # not closest heavy 141 | x, y, dx, dy = extract_atom_size(p.attrib["d"]) 142 | # estimate if it's an integer by looking for subscript 143 | x0, y0, dx0, dy0 = extract_atom_size(marked[name].attrib["d"]) 144 | # some heuristic for subscript 145 | if 1 < y - y0 and (y - y0 - dy0) ** 2 / dy0**2 < 0.1: 146 | # just adjust font size 147 | p.attrib["font-size"] = str(dy0 / 2) 148 | # and remove fill 149 | del p.attrib["fill"] 150 | continue 151 | t = ET.SubElement( 152 | root, 153 | "{http://www.w3.org/2000/svg}text", 154 | { 155 | "x": str(x + dx / 2), 156 | "y": str(y), 157 | "font-size": str(dy), 158 | "text-anchor": "middle", 159 | "dominant-baseline": "hanging", 160 | "textLength": str(dx), 161 | }, 162 | ) 163 | t.text = emoji_dict["hydrogen"] 164 | update.append(t) 165 | else: 166 | x, y, dx, dy = extract_atom_size(p.attrib["d"]) 167 | t = ET.SubElement( 168 | root, 169 | "{http://www.w3.org/2000/svg}text", 170 | { 171 | "x": str(x + dx / 2), 172 | "y": str(y), 173 | "font-size": str(dy), 174 | "text-anchor": "middle", 175 | "dominant-baseline": "hanging", 176 | "textLength": str(dx), 177 | }, 178 | ) 179 | t.text = sp[name] 180 | update.append(t) 181 | root.remove(p) 182 | # go through and get avg font size 183 | font_size = 0 184 | for t in update: 185 | font_size += float(t.attrib["font-size"]) 186 | for t in update: 187 | t.attrib["font-size"] = str(font_size / len(update)) 188 | ET.register_namespace("", "http://www.w3.org/2000/svg") 189 | return ET.tostring(root, encoding="unicode", method="xml") 190 | -------------------------------------------------------------------------------- /emojichem/data.py: -------------------------------------------------------------------------------- 1 | emoji_dict = { 2 | "hydrogen": "⭐️", 3 | "lithium": "🔋", 4 | "sodium": "🍟", 5 | "potassium": "🍌", 6 | "rubidium": "🚨", 7 | "cesium": "⏱", 8 | "francium": "🥐", 9 | "beryllium": "🛰", 10 | "magnesium": "💥", 11 | "calcium": "🥛", 12 | "strontium": "🎇", 13 | "barium": "💊", 14 | "radium": "⌚️", 15 | "scandium": "🚲", 16 | "yttrium": "📺", 17 | "lanthanum": "⚜️", 18 | "actinium": "🌀", 19 | "titanium": "🛳", 20 | "zirconium": "🚀", 21 | "hafnium": "📸", 22 | "rutherfordium": "🗺", 23 | "vanadium": "🎨", 24 | "niobium": "🎭", 25 | "cerium": "🚛", 26 | "tantalum": "🔍", 27 | "thorium": "⛈", 28 | "dubnium": "🤔", 29 | "chromium": "🍭", 30 | "molybdenum": "⛓", 31 | "praseodymium": "👥", 32 | "tungsten": "💎", 33 | "protactinium": "👩\u200d👦", 34 | "seaborgium": "🌊", 35 | "manganese": "🍖", 36 | "technetium": "⚙️", 37 | "neodymium": "🍷", 38 | "rhenium": "💻", 39 | "uranium": "☢️", 40 | "bohrium": "⚛️", 41 | "iron": "🥓", 42 | "ruthenium": "🦄", 43 | "promethium": "🚬", 44 | "osmium": "🖋", 45 | "neptunium": "🔱", 46 | "hassium": "⚖️", 47 | "cobalt": "📼", 48 | "rhodium": "🛣", 49 | "samarium": "💉", 50 | "iridium": "☄️", 51 | "plutonium": "💣", 52 | "meitnerium": "💃", 53 | "nickel": "🍴", 54 | "palladium": "⚗️", 55 | "europium": "🇪🇺", 56 | "platinum": "💰", 57 | "americium": "🌎", 58 | "darmstadtium": "🏰", 59 | "copper": "🥉", 60 | "silver": "🥈", 61 | "gadolinium": "🎰", 62 | "gold": "🥇", 63 | "curium": "👩🏻\u200d🔬", 64 | "roentgenium": "\U0001fa7b", 65 | "zinc": "🗝️", 66 | "cadmium": "🏭", 67 | "terbium": "🐳", 68 | "mercury": "🌡", 69 | "berkelium": "🎓", 70 | "copernicium": "💫", 71 | "boron": "🤹\u200d♀️", 72 | "aluminum": "✈️", 73 | "gallium": "🥄", 74 | "indium": "📱", 75 | "dysprosium": "💄", 76 | "thallium": "🐀", 77 | "californium": "🌞", 78 | "nihonium": "🗾", 79 | "carbon": "⛽️", 80 | "silicon": "🖥", 81 | "germanium": "🍺", 82 | "tin": "🤖", 83 | "holmium": "🌆", 84 | "lead": "🚰", 85 | "einsteinium": "🐼", 86 | "flerovium": "📝", 87 | "nitrogen": "🌱", 88 | "phosphorus": "🌋", 89 | "arsenic": "💀", 90 | "antimony": "👁", 91 | "erbium": "😬", 92 | "bismuth": "🌈", 93 | "fermium": "💯", 94 | "moscovium": "🏇", 95 | "oxygen": "🔥", 96 | "sulfur": "😷", 97 | "selenium": "💅", 98 | "tellurium": "🌌", 99 | "thulium": "🍒", 100 | "polonium": "🦅", 101 | "mendelevium": "🙇🏽", 102 | "livermorium": "💡", 103 | "fluorine": "🪥", 104 | "chlorine": "🤢", 105 | "bromine": "🥜", 106 | "iodine": "🐟", 107 | "ytterbium": "🌕", 108 | "astatine": "⚡️", 109 | "nobelium": "🏅", 110 | "tennessine": "🥃", 111 | "helium": "🎈", 112 | "neon": "🎃", 113 | "argon": "👻", 114 | "krypton": "🔫", 115 | "xenon": "🔮", 116 | "lutetium": "🤍", 117 | "radon": "🌪", 118 | "lawrencium": "🔁", 119 | "oganesson": "🥝", 120 | } 121 | elements = [ 122 | "hydrogen", 123 | "helium", 124 | "lithium", 125 | "beryllium", 126 | "boron", 127 | "carbon", 128 | "nitrogen", 129 | "oxygen", 130 | "fluorine", 131 | "neon", 132 | "sodium", 133 | "magnesium", 134 | "aluminum", 135 | "silicon", 136 | "phosphorus", 137 | "sulfur", 138 | "chlorine", 139 | "argon", 140 | "potassium", 141 | "calcium", 142 | "scandium", 143 | "titanium", 144 | "vanadium", 145 | "chromium", 146 | "manganese", 147 | "iron", 148 | "cobalt", 149 | "nickel", 150 | "copper", 151 | "silver", 152 | "zinc", 153 | "gallium", 154 | "germanium", 155 | "arsenic", 156 | "selenium", 157 | "bromine", 158 | "krypton", 159 | "rubidium", 160 | "strontium", 161 | "yttrium", 162 | "zirconium", 163 | "niobium", 164 | "molybdenum", 165 | "technetium", 166 | "ruthenium", 167 | "rhodium", 168 | "palladium", 169 | "silver", 170 | "cadmium", 171 | "indium", 172 | "tin", 173 | "antimony", 174 | "tellurium", 175 | "iodine", 176 | "xenon", 177 | "cesium", 178 | "barium", 179 | "lanthanum", 180 | "cerium", 181 | "praseodymium", 182 | "neodymium", 183 | "promethium", 184 | "samarium", 185 | "europium", 186 | "gadolinium", 187 | "terbium", 188 | "dysprosium", 189 | "holmium", 190 | "erbium", 191 | "thulium", 192 | "ytterbium", 193 | "lutetium", 194 | "hafnium", 195 | "tantalum", 196 | "tungsten", 197 | "rhenium", 198 | "osmium", 199 | "iridium", 200 | "platinum", 201 | "gold", 202 | "mercury", 203 | "thallium", 204 | "lead", 205 | "bismuth", 206 | "polonium", 207 | "astatine", 208 | "radon", 209 | "francium", 210 | "radium", 211 | "actinium", 212 | "thorium", 213 | "protactinium", 214 | "uranium", 215 | "neptunium", 216 | "plutonium", 217 | "americium", 218 | "curium", 219 | "berkelium", 220 | "californium", 221 | "einsteinium", 222 | "fermium", 223 | "mendelevium", 224 | "nobelium", 225 | "lawrencium", 226 | "rutherfordium", 227 | "dubnium", 228 | "seaborgium", 229 | "bohrium", 230 | "hassium", 231 | "meitnerium", 232 | "darmstadtium", 233 | "roentgenium", 234 | "copernicium", 235 | "nihonium", 236 | "flerovium", 237 | "moscovium", 238 | "livermorium", 239 | "tennessine", 240 | "oganesson", 241 | ] 242 | # mapping of element name to symbmol 243 | elem_dict = { 244 | "hydrogen": "H", 245 | "helium": "He", 246 | "lithium": "Li", 247 | "beryllium": "Be", 248 | "boron": "B", 249 | "carbon": "C", 250 | "nitrogen": "N", 251 | "oxygen": "O", 252 | "fluorine": "F", 253 | "neon": "Ne", 254 | "sodium": "Na", 255 | "magnesium": "Mg", 256 | "aluminum": "Al", 257 | "silicon": "Si", 258 | "phosphorus": "P", 259 | "sulfur": "S", 260 | "chlorine": "Cl", 261 | "argon": "Ar", 262 | "potassium": "K", 263 | "calcium": "Ca", 264 | "scandium": "Sc", 265 | "titanium": "Ti", 266 | "vanadium": "V", 267 | "chromium": "Cr", 268 | "manganese": "Mn", 269 | "iron": "Fe", 270 | "cobalt": "Co", 271 | "nickel": "Ni", 272 | "copper": "Cu", 273 | "zinc": "Zn", 274 | "gallium": "Ga", 275 | "germanium": "Ge", 276 | "arsenic": "As", 277 | "selenium": "Se", 278 | "bromine": "Br", 279 | "krypton": "Kr", 280 | "rubidium": "Rb", 281 | "strontium": "Sr", 282 | "yttrium": "Y", 283 | "zirconium": "Zr", 284 | "niobium": "Nb", 285 | "molybdenum": "Mo", 286 | "technetium": "Tc", 287 | "ruthenium": "Ru", 288 | "rhodium": "Rh", 289 | "palladium": "Pd", 290 | "silver": "Ag", 291 | "cadmium": "Cd", 292 | "indium": "In", 293 | "tin": "Sn", 294 | "antimony": "Sb", 295 | "tellurium": "Te", 296 | "iodine": "I", 297 | "xenon": "Xe", 298 | "cesium": "Cs", 299 | "barium": "Ba", 300 | "lanthanum": "La", 301 | "cerium": "Ce", 302 | "praseodymium": "Pr", 303 | "neodymium": "Nd", 304 | "promethium": "Pm", 305 | "samarium": "Sm", 306 | "europium": "Eu", 307 | "gadolinium": "Gd", 308 | "terbium": "Tb", 309 | "dysprosium": "Dy", 310 | "holmium": "Ho", 311 | "erbium": "Er", 312 | "thulium": "Tm", 313 | "ytterbium": "Yb", 314 | "lutetium": "Lu", 315 | "hafnium": "Hf", 316 | "tantalum": "Ta", 317 | "tungsten": "W", 318 | "rhenium": "Re", 319 | "osmium": "Os", 320 | "iridium": "Ir", 321 | "platinum": "Pt", 322 | "gold": "Au", 323 | "mercury": "Hg", 324 | "thallium": "Tl", 325 | "lead": "Pb", 326 | "bismuth": "Bi", 327 | "polonium": "Po", 328 | "astatine": "At", 329 | "radon": "Rn", 330 | "francium": "Fr", 331 | "radium": "Ra", 332 | "actinium": "Ac", 333 | "thorium": "Th", 334 | "protactinium": "Pa", 335 | "uranium": "U", 336 | "neptunium": "Np", 337 | "plutonium": "Pu", 338 | "americium": "Am", 339 | "curium": "Cm", 340 | "berkelium": "Bk", 341 | "californium": "Cf", 342 | "einsteinium": "Es", 343 | "fermium": "Fm", 344 | "mendelevium": "Md", 345 | "nobelium": "No", 346 | "lawrencium": "Lr", 347 | "rutherfordium": "Rf", 348 | "dubnium": "Db", 349 | "seaborgium": "Sg", 350 | "bohrium": "Bh", 351 | "hassium": "Hs", 352 | "meitnerium": "Mt", 353 | "darmstadtium": "Ds", 354 | "roentgenium": "Rg", 355 | "copernicium": "Cn", 356 | "nihonium": "Nh", 357 | "flerovium": "Fl", 358 | "moscovium": "Mc", 359 | "livermorium": "Lv", 360 | "tennessine": "Ts", 361 | "oganesson": "Og", 362 | } 363 | elem_dict = {v: k for k, v in elem_dict.items()} 364 | --------------------------------------------------------------------------------