{
"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",
" Cartesian\n",
"\n",
" \n",
" | \n",
" atom | \n",
" x | \n",
" y | \n",
" z | \n",
"
\n",
" \n",
" \n",
" \n",
" 1 | \n",
" O | \n",
" 0.000000 | \n",
" 0.0 | \n",
" 0.000000 | \n",
"
\n",
" \n",
" 2 | \n",
" H | \n",
" 0.758602 | \n",
" 0.0 | \n",
" 0.504284 | \n",
"
\n",
" \n",
" 3 | \n",
" H | \n",
" 0.260455 | \n",
" 0.0 | \n",
" -0.872893 | \n",
"
\n",
" \n",
" 4 | \n",
" O | \n",
" 3.000000 | \n",
" 0.5 | \n",
" 0.000000 | \n",
"
\n",
" \n",
" 5 | \n",
" H | \n",
" 3.758602 | \n",
" 0.5 | \n",
" 0.504284 | \n",
"
\n",
" \n",
" 6 | \n",
" H | \n",
" 3.260455 | \n",
" 0.5 | \n",
" -0.872893 | \n",
"
\n",
" \n",
"
\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",
" atom | \n",
" x | \n",
" y | \n",
" z | \n",
"
\n",
" \n",
" \n",
" \n",
" 1 | \n",
" True | \n",
" False | \n",
" False | \n",
" False | \n",
"
\n",
" \n",
" 2 | \n",
" True | \n",
" False | \n",
" False | \n",
" False | \n",
"
\n",
" \n",
" 3 | \n",
" True | \n",
" False | \n",
" False | \n",
" False | \n",
"
\n",
" \n",
" 4 | \n",
" True | \n",
" False | \n",
" False | \n",
" False | \n",
"
\n",
" \n",
" 5 | \n",
" True | \n",
" False | \n",
" False | \n",
" False | \n",
"
\n",
" \n",
" 6 | \n",
" True | \n",
" False | \n",
" False | \n",
" False | \n",
"
\n",
" \n",
"
\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",
" atom | \n",
" x | \n",
" y | \n",
" z | \n",
"
\n",
" \n",
" \n",
" \n",
" 1 | \n",
" True | \n",
" True | \n",
" True | \n",
" True | \n",
"
\n",
" \n",
" 2 | \n",
" True | \n",
" True | \n",
" True | \n",
" True | \n",
"
\n",
" \n",
" 3 | \n",
" True | \n",
" True | \n",
" True | \n",
" True | \n",
"
\n",
" \n",
" 4 | \n",
" True | \n",
" True | \n",
" True | \n",
" True | \n",
"
\n",
" \n",
" 5 | \n",
" True | \n",
" True | \n",
" True | \n",
" True | \n",
"
\n",
" \n",
" 6 | \n",
" True | \n",
" True | \n",
" True | \n",
" True | \n",
"
\n",
" \n",
"
\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",
" Cartesian\n",
"\n",
" \n",
" | \n",
" atom | \n",
" x | \n",
" y | \n",
" z | \n",
"
\n",
" \n",
" \n",
" \n",
" 1 | \n",
" O | \n",
" $x$ | \n",
" 0.0 | \n",
" 0.000000 | \n",
"
\n",
" \n",
" 2 | \n",
" H | \n",
" $x + 1$ | \n",
" 0.0 | \n",
" 0.504284 | \n",
"
\n",
" \n",
" 3 | \n",
" H | \n",
" $x + 2$ | \n",
" 0.0 | \n",
" -0.872893 | \n",
"
\n",
" \n",
" 4 | \n",
" O | \n",
" $x + 3$ | \n",
" 0.5 | \n",
" 0.000000 | \n",
"
\n",
" \n",
" 5 | \n",
" H | \n",
" $x + 4$ | \n",
" 0.5 | \n",
" 0.504284 | \n",
"
\n",
" \n",
" 6 | \n",
" H | \n",
" $x + 5$ | \n",
" 0.5 | \n",
" -0.872893 | \n",
"
\n",
" \n",
"
\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",
" Cartesian\n",
"\n",
" \n",
" | \n",
" atom | \n",
" x | \n",
" y | \n",
" z | \n",
"
\n",
" \n",
" \n",
" \n",
" 1 | \n",
" O | \n",
" 2.0 | \n",
" 0.0 | \n",
" 0.000000 | \n",
"
\n",
" \n",
" 2 | \n",
" H | \n",
" 3.0 | \n",
" 0.0 | \n",
" 0.504284 | \n",
"
\n",
" \n",
" 3 | \n",
" H | \n",
" 4.0 | \n",
" 0.0 | \n",
" -0.872893 | \n",
"
\n",
" \n",
" 4 | \n",
" O | \n",
" 5.0 | \n",
" 0.5 | \n",
" 0.000000 | \n",
"
\n",
" \n",
" 5 | \n",
" H | \n",
" 6.0 | \n",
" 0.5 | \n",
" 0.504284 | \n",
"
\n",
" \n",
" 6 | \n",
" H | \n",
" 7.0 | \n",
" 0.5 | \n",
" -0.872893 | \n",
"
\n",
" \n",
"
\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
}