{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"markdown","source":"# 3D Visualization of Magnetic Interactions\n\nThere are three important factors, which determine the coupling constant:\n- bond distance between atoms (1J - 1 bond,  2J - 2 bonds,  3J  - bonds)\n- angle between atoms (e.g. torsion angle)\n<a href=\"https://ibb.co/3T5GJ8k\"><img src=\"https://i.ibb.co/6ycVdpN/2019-07-08-16-35-16.png\" alt=\"2019-07-08-16-35-16\" border=\"0\"></a>\n\n- electronegative substituents (e.g. F, N, 0)\n\n<a href=\"https://ibb.co/G5nrxXR\"><img src=\"https://i.ibb.co/S35Zs2B/2019-07-08-16-39-45.png\" alt=\"2019-07-08-16-39-45\" border=\"0\"></a>\n\n[Source: Organic chemistry, Stuart Warren](https://www.amazon.de/Organic-Chemistry-Jonathan-Clayden/dp/0198503466)"},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\n\nimport plotly.graph_objs as go\nfrom plotly.offline import init_notebook_mode, iplot\n\nstructures = pd.read_csv('../input/structures.csv')\ntrain = pd.read_csv('../input/train.csv')\n\n# initiate the plotly notebook mode\ninit_notebook_mode(connected=True)\n\n\ndef plot_interactions(molecule_name, structures_df, train_df):\n    \"\"\"Creates a 3D plot of the molecule\"\"\"\n    \n    atomic_radii = dict(C=0.77, F=0.71, H=0.38, N=0.75, O=0.73)  \n    cpk_colors = dict(C='black', F='green', H='white', N='blue', O='red')\n    \n    if molecule_name not in train_df.molecule_name.unique():\n        print(f'Molecule \"{molecule_name}\" is not in the training set!')\n        return\n    \n    molecule = structures[structures.molecule_name == molecule_name]\n    coordinates = molecule[['x', 'y', 'z']].values\n    x_coordinates = coordinates[:, 0]\n    y_coordinates = coordinates[:, 1]\n    z_coordinates = coordinates[:, 2]\n    elements = molecule.atom.tolist()\n    radii = [atomic_radii[element] for element in elements]\n    \n    data_train = train_df[train_df.molecule_name == molecule_name][['atom_index_0', 'atom_index_1', 'scalar_coupling_constant']]\n    interactions = data_train.groupby('atom_index_0')['atom_index_1'].apply(set).to_dict()\n    coupling_constants = data_train.set_index(['atom_index_0', 'atom_index_1']).round(2).to_dict()['scalar_coupling_constant']\n    \n    def get_bonds():\n        \"\"\"Generates a set of bonds from atomic cartesian coordinates\"\"\"\n        ids = np.arange(coordinates.shape[0])\n        bonds = dict()\n        coordinates_compare, radii_compare, ids_compare = coordinates, radii, ids\n        \n        for _ in range(len(ids)):\n            coordinates_compare = np.roll(coordinates_compare, -1, axis=0)\n            radii_compare = np.roll(radii_compare, -1, axis=0)\n            ids_compare = np.roll(ids_compare, -1, axis=0)\n            distances = np.linalg.norm(coordinates - coordinates_compare, axis=1)\n            bond_distances = (radii + radii_compare) * 1.3\n            mask = np.logical_and(distances > 0.1, distances <  bond_distances)\n            distances = distances.round(2)\n            new_bonds = {frozenset([i, j]): dist for i, j, dist in zip(ids[mask], ids_compare[mask], distances[mask])}\n            bonds.update(new_bonds)\n        return bonds      \n            \n    def atom_trace():\n        \"\"\"Creates an atom trace for the plot\"\"\"\n        colors = [cpk_colors[element] for element in elements]\n        markers = dict(color=colors, line=dict(color='lightgray', width=2), size=5, symbol='circle', opacity=0.8)\n        trace = go.Scatter3d(x=x_coordinates, y=y_coordinates, z=z_coordinates, mode='markers', marker=markers,\n                             text=elements, name='')\n        return trace\n\n    def bond_trace():\n        \"\"\"\"Creates a bond trace for the plot\"\"\"\n        trace = go.Scatter3d(x=[], y=[], z=[], hoverinfo='none', mode='lines',\n                             marker=dict(color='grey', size=7, opacity=1), line=dict(width=3))\n        for i, j in bonds.keys():\n            trace['x'] += (x_coordinates[i], x_coordinates[j], None)\n            trace['y'] += (y_coordinates[i], y_coordinates[j], None)\n            trace['z'] += (z_coordinates[i], z_coordinates[j], None)\n        return trace\n    \n    def interaction_trace(atom_id):\n        \"\"\"\"Creates an interaction trace for the plot\"\"\"\n        trace = go.Scatter3d(x=[], y=[], z=[], hoverinfo='none', mode='lines',\n                             marker=dict(color='pink', size=7, opacity=0.5),\n                            visible=False)\n        for i in interactions[atom_id]:\n            trace['x'] += (x_coordinates[atom_id], x_coordinates[i], None)\n            trace['y'] += (y_coordinates[atom_id], y_coordinates[i], None)\n            trace['z'] += (z_coordinates[atom_id], z_coordinates[i], None)\n        return trace\n    \n    bonds = get_bonds()\n    \n    zipped = zip(range(len(elements)), x_coordinates, y_coordinates, z_coordinates)\n    annotations_id = [dict(text=num, x=x, y=y, z=z, showarrow=False, yshift=15, font = dict(color = \"blue\"))\n                      for num, x, y, z in zipped]\n    \n    annotations_length = []\n    for (i, j), dist in bonds.items():\n        x_middle, y_middle, z_middle = (coordinates[i] + coordinates[j]) / 2\n        annotation = dict(text=dist, x=x_middle, y=y_middle, z=z_middle, showarrow=False, yshift=10)\n        annotations_length.append(annotation)\n    \n    annotations_interaction = []\n    for k, v in interactions.items():\n        annotations = []\n        for i in v:\n            x_middle, y_middle, z_middle = (coordinates[k] + coordinates[i]) / 2\n            constant = coupling_constants[(k, i)]\n            annotation = dict(text=constant, x=x_middle, y=y_middle, z=z_middle, showarrow=False, yshift=25,\n                              font = dict(color = \"hotpink\"))\n            annotations.append(annotation)\n        annotations_interaction.append(annotations)\n    \n    buttons = []\n    for num, i in enumerate(interactions.keys()):\n        mask = [False] * len(interactions)\n        mask[num] = True\n        button = dict(label=f'Atom {i}',\n                      method='update',\n                      args=[{'visible': [True] * 2 + mask},\n                            {'scene.annotations': annotations_id + annotations_length + annotations_interaction[num]}])\n        buttons.append(button)\n        \n    updatemenus = list([\n        dict(buttons = buttons,\n             direction = 'down',\n             xanchor = 'left',\n             yanchor = 'top'\n            )\n    ])\n    \n    data = [atom_trace(), bond_trace()]\n    \n    # add interaction traces\n    for num, i in enumerate(interactions.keys()):\n        trace = interaction_trace(i)\n        if num == 0:\n            trace.visible = True \n        data.append(trace)\n        \n    axis_params = dict(showgrid=False, showbackground=False, showticklabels=False, zeroline=False, titlefont=dict(color='white'))\n    layout = dict(scene=dict(xaxis=axis_params, yaxis=axis_params, zaxis=axis_params,\n                             annotations=annotations_id + annotations_length + annotations_interaction[0]),\n                  margin=dict(r=0, l=0, b=0, t=0), showlegend=False, updatemenus=updatemenus)\n\n    fig = go.Figure(data=data, layout=layout)\n    iplot(fig)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_interactions('dsgdb9nsd_000117', structures, train)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_interactions('dsgdb9nsd_000131', structures, train)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_interactions('dsgdb9nsd_000161', structures, train)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_interactions('dsgdb9nsd_000137', structures, train)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plot_interactions('dsgdb9nsd_099964', structures, train)","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":4}