{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Transformation between internal and cartesian coordinates" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import chemcoord as cc\n", "import time\n", "import pandas as pd" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "water = cc.Cartesian.read_xyz('water_dimer.xyz', start_index=1)\n", "zwater = water.get_zmat()\n", "small = cc.Cartesian.read_xyz('MIL53_small.xyz', start_index=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Naming convention" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The table which defines the used references of each atom will be called\n", "**construction table**.\n", "\n", "The contruction table of the zmatrix of the water dimer can be seen here:" ] }, { "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", "
bad
1origine_ze_x
21e_ze_x
312e_x
4213
5421
6452
\n", "
" ], "text/plain": [ " b a d\n", "1 origin e_z e_x\n", "2 1 e_z e_x\n", "3 1 2 e_x\n", "4 2 1 3\n", "5 4 2 1\n", "6 4 5 2" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "zwater.loc[:, ['b', 'a', 'd']]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The absolute references are indicated by magic strings: ``['origin', 'e_x', 'e_y', 'e_z']``." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* The atom which is to be set in the reference of three other atoms, is denoted $i$.\n", "* The bond-defining atom is represented by $b$.\n", "* The angle-defining atom is represented by $a$.\n", "* The dihedral-defining atom is represented by $d$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Mathematical introduction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is advantageous to treat a zmatrix simply as recursive spherical coordinates.\n", "\n", "The $(n + 1)$-th atom uses three of the previous $n$ atoms as reference.\n", "Those three atoms ($b, a, d$) are spanning a coordinate system, if we require righthandedness.\n", "If we express the position of the atom $i$ in respect to this locally spanned coordinate system using\n", "spherical coordinates, we arrive at the usual definition of a zmatrix.\n", "\n", "\n", "PS: The question about right- or lefthandedness is equivalent to specifying a direction of rotation.\n", "Chemcoord uses of course the [IUPAC definition](https://goldbook.iupac.org/html/T/T06406.html)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Ideal case" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The ideal (and luckily most common) case is, that $\\vec{ib}$, $\\vec{ba}$, and $\\vec{ad}$ are linearly independent.\n", "In this case there exist a bijective mapping between spherical coordinates and cartesian coordinates and all angles, positions... are well defined." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![Ideal case](ideal_way.png \"Ideal case\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Linear angle" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One pathologic case appears, if $\\vec{ib}$ and $\\vec{ba}$ are linear dependent.\n", "\n", "This means, that the angle in the zmatrix is either $0^\\circ$ or $180^\\circ$.\n", "In this case there are infinitely many dihedral angles for the same configuration in cartesian space.\n", "Or to say it in a more analytical way:\n", "The transformation from spherical coordinates to cartesian coordinates is surjective, but not injective.\n", "\n", "For nearly all cases (e.g. expressing the potential hyper surface in terms of internal coordinates), the surjectivity property is sufficient.\n", "\n", "A lot of other problematic cases can be automatically solved by assigning a default value to the dihedral angle by definition ($0^\\circ$ in the case of chemcoord).\n", "\n", "Usually the user does not need to think about this case, which is automatically handled by chemcoord." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![Linear angle](linear_angle.png \"Linear angle\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Linear reference" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The real pathologic case appears, if the three reference atoms are linear. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![Linear dihedral](linear_dihedral.png \"Linear dihedral\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is important to note, that this is not a problem in the spherical coordinates of ``i``.\n", "The coordinate system itself which is spanned by ``b``, ``a`` and ``d`` is undefined.\n", "This means, that it is not visible directly from the values in the Zmatrix, if ``i`` uses an invalid reference.\n", "\n", "I will use the term valid Zmatrix if all atoms ``i`` have valid references. In this case the transformation to cartesian coordinates is well defined.\n", "\n", "Now there are two cases:\n", "\n", "##### Creation of a valid Zmatrix\n", "\n", "Chemcoord asserts, that the Zmatrix which is created from cartesian coordinates using ``get_zmat`` is a valid Zmatrix (or raises an explicit exception if it fails at finding valid references.) This is always done by choosing other references (instead of introducing dummy atoms.)\n", "\n", "\n", "##### Manipulation of a valid Zmatrix\n", "\n", "If a valid Zmatrix is manipulated after creation, it might occur because of an assignment, that ``b``, ``a``, and ``d`` are moved into a linear relationship. In this case a dummy atom is inserted which lies in the plane which was spanned by ``b``, ``a``, and ``d`` before the assignment. It uses the same references as the atom ``d``, so changes in the references of ``b``, ``a`` and ``d`` are also present in the position of the dummy atom ``X``.\n", "This is done using the safe assignment methods of chemcoord.\n", "\n", "\n", "### Example" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "water = water - water.loc[5, ['x', 'y', 'z']]\n", "\n", "zmolecule = water.get_zmat()\n", "c_table = zmolecule.loc[:, ['b', 'a', 'd']]\n", "c_table.loc[6, ['a', 'd']] = [2, 1]\n", "zmolecule1 = water.get_zmat(construction_table=c_table)\n", "zmolecule2 = zmolecule1.copy()\n", "zmolecule3 = water.get_zmat(construction_table=c_table)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Modifications on zmolecule1" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "angle_before_assignment = zmolecule1.loc[4, 'angle']" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/mcocdawc/code/chemcoord/src/chemcoord/internal_coordinates/_zmat_class_core.py:551: UserWarning: For the dihedral reference of atom 5 the dummy atom 7 was inserted\n", " warnings.warn(give_message(i=i, dummy_d=dummy_d), UserWarning)\n", "/home/mcocdawc/code/chemcoord/src/chemcoord/internal_coordinates/_zmat_class_core.py:551: UserWarning: For the dihedral reference of atom 6 the dummy atom 8 was inserted\n", " warnings.warn(give_message(i=i, dummy_d=dummy_d), UserWarning)\n" ] } ], "source": [ "zmolecule1.safe_loc[4, 'angle'] = 180" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "zmolecule1.safe_loc[5, 'dihedral'] = 90" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/mcocdawc/code/chemcoord/src/chemcoord/internal_coordinates/_zmat_class_core.py:618: UserWarning: The dummy atoms [5, 6] were removed\n", " warnings.warn('The dummy atoms {} were removed'.format(to_remove),\n" ] } ], "source": [ "zmolecule1.safe_loc[4, 'angle'] = angle_before_assignment" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "xyz1 = zmolecule1.get_cartesian()" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "xyz1.view()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Contextmanager" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With the following contextmanager we can switch the automatic insertion of dummy atoms of and look at the cartesian which is built after assignment of ``.safe_loc[4, 'angle'] = 180``. It is obvious from the structure, that the coordinate system spanned by ``O - H - O`` is undefined. This was the second pathological case in the mathematical introduction." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "with cc.DummyManipulation(False):\n", " try:\n", " zmolecule3.safe_loc[4, 'angle'] = 180\n", " except cc.exceptions.InvalidReference as e:\n", " e.already_built_cartesian.view()" ] }, { "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": 12, "metadata": {}, "outputs": [], "source": [ "import sympy\n", "sympy.init_printing()\n", "d = sympy.Symbol('d')" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "symb_water = zwater.copy()" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "symb_water.safe_loc[4, 'bond'] = d" ] }, { "cell_type": "code", "execution_count": 15, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Zmat
atombbondaangleddihedral
1Oorigin0.0e_z0.000000e_x0.000000
2H10.910922e_z56.385853e_x-0.000000
3H10.9109222107.000024e_x-0.000000
4O2$d$1132.4662983-16.755013
5H40.9109222132.4662981180.000000
6H40.9109225107.0000242163.244987
\n", "
" ], "text/plain": [ " atom b bond a angle d dihedral\n", "1 O origin 0.0 e_z 0.000000 e_x 0.000000\n", "2 H 1 0.910922 e_z 56.385853 e_x -0.000000\n", "3 H 1 0.910922 2 107.000024 e_x -0.000000\n", "4 O 2 d 1 132.466298 3 -16.755013\n", "5 H 4 0.910922 2 132.466298 1 180.000000\n", "6 H 4 0.910922 5 107.000024 2 163.244987" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symb_water" ] }, { "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Zmat
atombbondaangleddihedral
1Oorigin0.000000e_z0.000000e_x0.000000
2H10.910922e_z56.385853e_x-0.000000
3H10.9109222107.000024e_x-0.000000
4O22.0000001132.4662983-16.755013
5H40.9109222132.4662981180.000000
6H40.9109225107.0000242163.244987
\n", "
" ], "text/plain": [ " atom b bond a angle d dihedral\n", "1 O origin 0.000000 e_z 0.000000 e_x 0.000000\n", "2 H 1 0.910922 e_z 56.385853 e_x -0.000000\n", "3 H 1 0.910922 2 107.000024 e_x -0.000000\n", "4 O 2 2.000000 1 132.466298 3 -16.755013\n", "5 H 4 0.910922 2 132.466298 1 180.000000\n", "6 H 4 0.910922 5 107.000024 2 163.244987" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "symb_water.subs(d, 2)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "cc.xyz_functions.view([symb_water.subs(d, i).get_cartesian() for i in range(2, 5)])\n", "\n", "# If your viewer cannot open molden files you have to uncomment the following lines\n", "# for i in range(2, 5):\n", "# symb_water.subs(d, i).get_cartesian().view()\n", "# time.sleep(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Definition of the construction table" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The construction table in chemcoord is represented by a pandas DataFrame with the columns ``['b', 'a', 'd']`` which can be constructed manually." ] }, { "cell_type": "code", "execution_count": 18, "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", "
bad
0123
1456
2789
\n", "
" ], "text/plain": [ " b a d\n", "0 1 2 3\n", "1 4 5 6\n", "2 7 8 9" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]], columns=['b', 'a', 'd'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is possible to specify only the first $i$ rows of a Zmatrix, in order to compute the $i + 1$ to $n$ rows automatically.\n", "If the molecule consists of unconnected fragments, the construction tables are created independently for each fragment and connected afterwards.\n", "It is important to note, that an unfragmented, monolithic molecule is treated in the same way.\n", "It just consists of one fragment.\n", "This means that in several methods where a list of fragments is returned or taken,\n", "an one element list appears." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the Zmatrix is automatically created, the oxygen 1 is the first atom.\n", "Let's assume, that we want to change the order of fragments." ] }, { "cell_type": "code", "execution_count": 19, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Zmat
atombbondaangleddihedral
1Oorigin3.825100e_z97.575672e_x-172.422536
2H10.910922e_z13.716615e_x-146.597056
3H10.9109222107.000024e_x-12.723783
4O22.3512061132.4662983-16.755013
5H40.9109222132.4662981-180.000000
6H40.9109225107.0000242163.244987
\n", "
" ], "text/plain": [ " atom b bond a angle d dihedral\n", "1 O origin 3.825100 e_z 97.575672 e_x -172.422536\n", "2 H 1 0.910922 e_z 13.716615 e_x -146.597056\n", "3 H 1 0.910922 2 107.000024 e_x -12.723783\n", "4 O 2 2.351206 1 132.466298 3 -16.755013\n", "5 H 4 0.910922 2 132.466298 1 -180.000000\n", "6 H 4 0.910922 5 107.000024 2 163.244987" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "water.get_zmat()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's fragmentate the water" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "fragments = water.fragmentate()" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "c_table = water.get_construction_table(fragment_list=[fragments[1], fragments[0]])" ] }, { "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Zmat
atombbondaangleddihedral
4Oorigin0.910922e_z123.614147e_x-180.000000
5H40.910922e_z29.624299e_x-0.000000
6H40.9109225107.000024e_x-0.000000
2H42.3512066118.5611235165.988232
1O20.9109224132.466298618.293238
3H10.9109222107.0000244-16.755013
\n", "
" ], "text/plain": [ " atom b bond a angle d dihedral\n", "4 O origin 0.910922 e_z 123.614147 e_x -180.000000\n", "5 H 4 0.910922 e_z 29.624299 e_x -0.000000\n", "6 H 4 0.910922 5 107.000024 e_x -0.000000\n", "2 H 4 2.351206 6 118.561123 5 165.988232\n", "1 O 2 0.910922 4 132.466298 6 18.293238\n", "3 H 1 0.910922 2 107.000024 4 -16.755013" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "water.get_zmat(c_table)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we want to specify the order in the second fragment, so that it connects via the oxygen 1, it is important to note, that we have to specify the full row. **It is not possible to define just the order without the references**." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "frag_c_table = pd.DataFrame([[4, 6, 5], [1, 4, 6], [1, 2, 4]], columns=['b', 'a', 'd'], index=[1, 2, 3])" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "c_table2 = water.get_construction_table(fragment_list=[fragments[1], (fragments[0], frag_c_table)])" ] }, { "cell_type": "code", "execution_count": 25, "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", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Zmat
atombbondaangleddihedral
4Oorigin0.910922e_z123.614147e_x-180.000000
5H40.910922e_z29.624299e_x-0.000000
6H40.9109225107.000024e_x-0.000000
1O43.0413816106.3816535170.133373
2H10.910922434.7694256-163.300710
3H10.9109222107.0000244-16.755013
\n", "
" ], "text/plain": [ " atom b bond a angle d dihedral\n", "4 O origin 0.910922 e_z 123.614147 e_x -180.000000\n", "5 H 4 0.910922 e_z 29.624299 e_x -0.000000\n", "6 H 4 0.910922 5 107.000024 e_x -0.000000\n", "1 O 4 3.041381 6 106.381653 5 170.133373\n", "2 H 1 0.910922 4 34.769425 6 -163.300710\n", "3 H 1 0.910922 2 107.000024 4 -16.755013" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "water.get_zmat(c_table2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "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 }