{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Introduction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Welcome to the tutorial for ChemCoord (http://chemcoord.readthedocs.org/)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The manipulation of the coordinates is a lot easier, if you can view them on the fly.\n", "So please install a molecule viewer, which opens xyz-files. \n", "A non complete list includes:\n", "[molcas gv](http://www.molcas.org/GV/),\n", "[avogadro](https://avogadro.cc/),\n", "[vmd](http://www.ks.uiuc.edu/Research/vmd/), and\n", "[pymol](https://www.pymol.org/)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Cartesian" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import chemcoord as cc\n", "from chemcoord.xyz_functions import get_rotation_matrix\n", "import numpy as np\n", "import time" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "water = cc.Cartesian.read_xyz('water_dimer.xyz', start_index=1)\n", "small = cc.Cartesian.read_xyz('MIL53_small.xyz', start_index=1)\n", "middle = cc.Cartesian.read_xyz('MIL53_middle.xyz', start_index=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's have a look at it:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Cartesian
atomxyz
1O0.0000000.00.000000
2H0.7586020.00.504284
3H0.2604550.0-0.872893
4O3.0000000.50.000000
5H3.7586020.50.504284
6H3.2604550.5-0.872893
\n", "
" ], "text/plain": [ " atom x y z\n", "1 O 0.000000 0.0 0.000000\n", "2 H 0.758602 0.0 0.504284\n", "3 H 0.260455 0.0 -0.872893\n", "4 O 3.000000 0.5 0.000000\n", "5 H 3.758602 0.5 0.504284\n", "6 H 3.260455 0.5 -0.872893" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "water" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is also possible to open it with an external viewer. I use Molcas ``gv.exe`` so you have to change it accordingly to your program of choice." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "water.view(viewer='gv.exe')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To make this setting permament, execute:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "cc.settings['defaults']['viewer'] = 'gv.exe' # replace by your viewer of choice" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Slicing" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The slicing operations are the same as for ``pandas.DataFrames``. (http://pandas.pydata.org/pandas-docs/stable/indexing.html)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the ``'x'`` axis is of particular interest you can slice it out with:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1 0.000000\n", "2 0.758602\n", "3 0.260455\n", "4 3.000000\n", "5 3.758602\n", "6 3.260455\n", "Name: x, dtype: float64" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "water['x']\n", "# or explicit label based indexing\n", "water.loc[:, 'x']\n", "# or explicit integer based indexing\n", "water.iloc[:, 1]\n", "# or selection directly as an attribute\n", "# this is only possible, if no other conflicting method/attribute exists\n", "water.x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With boolean slicing it is very easy to cut all the oxygens away:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "water[water.atom != 'O'].view()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This can be combined with other selections:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "water[(water.atom != 'O') & (water.x < 1)].view()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Returned type" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The indexing behaves like Indexing and Selecting data in\n", "[Pandas]().\n", "You can slice with `Cartesian.loc[key]`, `Cartesian.iloc[keys]`, and `Cartesian[key]`.\n", "The only question is about the return type.\n", "If the information in the columns is enough to draw a molecule,\n", "an instance of the own class (e.g. `Cartesian`)\n", "is returned.\n", "If the information in the columns is not enough to draw a molecule, there \n", "are two cases to consider:\n", "\n", "* A `pandas.Series` instance is returned for one dimensional slices\n", "* A `pandas.DataFrame` instance is returned in all other cases:\n", "\n", " molecule.loc[:, ['atom', 'x', 'y', 'z']] returns a `Cartesian`.\n", "\n", " molecule.loc[:, ['atom', 'x']]`` returns a `pandas.DataFrame`.\n", "\n", " molecule.loc[:, 'atom']`` returns a `pandas.Series`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Sideeffects and Method chaining" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Two general rules are: \n", "1. **All functions are sideeffect free unless stated otherwise in the documentation**.\n", "2. **Where possible the methods return an instance of the own class, to allow method chaining**." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Have a look at the unmodified molecule" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "middle.view()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Chain the methods:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "middle.cut_sphere(radius=5, preserve_bonds=False).view()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The molecule itself remains unchanged." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "middle.view()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Chemical bonds" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One really important method is ``get_bonds()``. \n", "It returns a connectivity table, which is represented by a dictionary.\n", "Each index points to set of indices, that are connected to it." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{1: {2, 3}, 2: {1}, 3: {1}, 4: {5, 6}, 5: {4}, 6: {4}}" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "water.get_bonds()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now the focus switches to another molecule (MIL53_middle)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's explore the coordinationsphere of the Cr atom with the index 7." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "for i in range(3):\n", " middle.get_coordination_sphere(13, n_sphere=i, only_surface=False).view()\n", " time.sleep(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Binary operators" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Mathematical Operations:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Binary operators are supported in the logic of the scipy stack, but you need\n", "python3.x for using the matrix multiplication operator ``@``.\n", "\n", "The general rule is that mathematical operations using the binary operators\n", "``(+ - * / @)`` and the unary operatos ``(+ - abs)``\n", "are only applied to the ``['x', 'y', 'z']`` columns.\n", "\n", "**Addition/Subtraction/Multiplication/Division**:\n", "If you add a scalar to a Cartesian it is added elementwise onto the\n", "``['x', 'y', 'z']`` columns.\n", "If you add a 3-dimensional vector, list, tuple... the first element of this\n", "vector is added elementwise to the ``'x'`` column of the\n", "Cartesian instance and so on.\n", "The last possibility is to add a matrix with\n", "``shape=(len(Cartesian), 3)`` which is again added elementwise.\n", "The same rules are true for subtraction, division and multiplication.\n", "\n", "**Matrixmultiplication**:\n", "Only leftsided multiplication with a matrix of ``shape=(n, 3)``,\n", "where ``n`` is a natural number, is supported.\n", "The usual usecase is for example\n", "``np.diag([1, 1, -1]) @ cartesian_instance``\n", "to mirror on the x-y plane.\n", "Note that if ``A`` is the matrix which is multiplied from the left, and ``X`` is the ``shape=(n, 3)``-matrix\n", "consisting of the ``['x', 'y', 'z']`` columns. The actual calculation is:\n", "``(A @ X.T).T``" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "(water + 3).view()" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "(get_rotation_matrix([1, 0, 0], np.radians(90)) @ water).view()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Comparison:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The comparison operators ``==`` and ``!=`` are supported and require molecules indexed in the same way:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In some cases it is better to test for numerical equality $ |a - b| < \\epsilon$. This is done using\n", "``allclose`` or ``isclose`` (elementwise)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
atomxyz
1TrueFalseFalseFalse
2TrueFalseFalseFalse
3TrueFalseFalseFalse
4TrueFalseFalseFalse
5TrueFalseFalseFalse
6TrueFalseFalseFalse
\n", "
" ], "text/plain": [ " atom x y z\n", "1 True False False False\n", "2 True False False False\n", "3 True False False False\n", "4 True False False False\n", "5 True False False False\n", "6 True False False False" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "water == water + 1e-15" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
atomxyz
1TrueTrueTrueTrue
2TrueTrueTrueTrue
3TrueTrueTrueTrue
4TrueTrueTrueTrue
5TrueTrueTrueTrue
6TrueTrueTrueTrue
\n", "
" ], "text/plain": [ " atom x y z\n", "1 True True True True\n", "2 True True True True\n", "3 True True True True\n", "4 True True True True\n", "5 True True True True\n", "6 True True True True" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cc.xyz_functions.isclose(water, water + 1e-15)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cc.xyz_functions.allclose(water, water + 1e-15)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Symbolic evaluation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is possible to use symbolic expressions from sympy." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "import sympy\n", "sympy.init_printing()\n", "x = sympy.Symbol('x')" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "symb_water = water.copy()" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "symb_water['x'] = [x + i for i in range(len(symb_water))]" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Cartesian
atomxyz
1O$x$0.00.000000
2H$x + 1$0.00.504284
3H$x + 2$0.0-0.872893
4O$x + 3$0.50.000000
5H$x + 4$0.50.504284
6H$x + 5$0.5-0.872893
\n", "
" ], "text/plain": [ " atom x y z\n", "1 O x 0.0 0.000000\n", "2 H x + 1 0.0 0.504284\n", "3 H x + 2 0.0 -0.872893\n", "4 O x + 3 0.5 0.000000\n", "5 H x + 4 0.5 0.504284\n", "6 H x + 5 0.5 -0.872893" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symb_water" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Cartesian
atomxyz
1O2.00.00.000000
2H3.00.00.504284
3H4.00.0-0.872893
4O5.00.50.000000
5H6.00.50.504284
6H7.00.5-0.872893
\n", "
" ], "text/plain": [ " atom x y z\n", "1 O 2.0 0.0 0.000000\n", "2 H 3.0 0.0 0.504284\n", "3 H 4.0 0.0 -0.872893\n", "4 O 5.0 0.5 0.000000\n", "5 H 6.0 0.5 0.504284\n", "6 H 7.0 0.5 -0.872893" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symb_water.subs(x, 2)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "symb_water.subs(x, 2).view()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Alignment" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "moved = get_rotation_matrix([1, 2, 3], 1.1) @ middle + 15" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "moved.view()" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "m1, m2 = middle.align(moved)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "cc.xyz_functions.view([m1, m2])\n", "# If your viewer of choice does not support molden files, you have to call separately:\n", "# m1.view()\n", "# m2.view()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Symmetry" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is possible to detect the point group and symmetrize a molecule.\n", "Let's distort a $C_{2,v}$ symmetric molecule and symmetrize it back:" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [], "source": [ "np.random.seed(77)\n", "dist_molecule = small.copy()\n", "dist_molecule += np.random.randn(len(dist_molecule), 3) / 25" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "C1" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dist_molecule.get_pointgroup(tolerance=0.1)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "eq = dist_molecule.symmetrize(max_n=25, tolerance=0.3, epsilon=1e-5)" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "C2v" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "eq['sym_mol'].get_pointgroup(tolerance=0.1)" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [], "source": [ "a, b = small.align(dist_molecule)\n", "a, c = small.align(eq['sym_mol'])\n", "d1 = (a - b).get_distance_to()\n", "d2 = (a - c).get_distance_to()" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "cc.xyz_functions.view([a, b, c])\n", "# If your viewer of choice does not support molden files, you have to call separately:\n", "# a.view()\n", "# b.view()\n", "# c.view()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As we can see, the symmetrised molecule is a lot more similar to the original molecule.\n", "The average deviation from the original positions decreased by 35 %." ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/latex": [ "$\\displaystyle 0.346090793590717$" ], "text/plain": [ "0.34609079359071687" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(d1['distance'].sum() - d2['distance'].sum()) / d1['distance'].sum()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": {}, "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.8.10" } }, "nbformat": 4, "nbformat_minor": 4 }