diff --git a/documentation/tutorials/12-Using_View_Structures.ipynb b/documentation/tutorials/12-Using_View_Structures.ipynb new file mode 100644 index 0000000..75299fe --- /dev/null +++ b/documentation/tutorials/12-Using_View_Structures.ipynb @@ -0,0 +1,367 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 12 - Using View Structures:\n", + "\n", + "\n", + "### In this tutorial we will walk through many examples of how and what to use view structures for, and is meant as a hightlight of this function's ability.\n", + "\n", + "If you do not see a potential capability please let the developers know of your desire on github!!\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from architector import (view_structures,\n", + " build_complex,\n", + " convert_io_molecule)\n", + "from architector.io_calc import CalcExecutor # Run XTB calculations\n", + "from architector.io_samplers import random_sampler # Create distortions\n", + "from architector.vibrations_free_energy import vibration_analysis # Vibrational analysis to get normal modes\n", + "import architector.arch_context_manage as arch_context_manage # Temporary directory generation\n", + "from ase.vibrations import Vibrations # ASE implemenation of vibrational analysis\n", + "import numpy as np " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Even before running architector, view_structures can be used to visualize SMILES strings!\n", + "# In the background, openbabel is used to build these in 3D\n", + "view_structures(['C','CN','COC','CC=CC'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# To start from Architector, let's build complexes to visualize\n", + "inp = {'core':{'metal':'Fe','coreCN':6},\n", + " 'ligands':['bipy']*2+['chloride']+['water'],\n", + " 'parameters':{'skip_duplicate_tests':True,\n", + " 'assemble_method':'UFF',\n", + " 'full_method':'UFF',\n", + " 'relax':True}}\n", + "out = build_complex(inp)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# At it's base, view structures can be called on any number of types of structures.\n", + "# On Architector output dictionaries (e.g. out) it will pull out the mol2string fields to visualize.\n", + "# These are sorted by energy by default\n", + "view_structures(out)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# You can play with the size/shape of viewer grids:\n", + "view_structures(out,\n", + " columns=2, # Check the number of columns the viewer will use.\n", + " w=400, # Change the size of each viewer width in pixels. Default is 200\n", + " h=400 # Change the size of each viewer height in pixels. Default is 200\n", + " )\n", + "# This will thus make a 2X2 grid with larger versions of the molecules viewed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Now, we will pull out just the mol2strings to highlight other view_structures functionality\n", + "mol_list = [val['mol2string'] for key,val in out.items()]\n", + "energies = [val['energy'] for key,val in out.items()] # Pull out the energies\n", + "energy_labels = ['{0:.2f}'.format(x) for x in (np.array(energies)-min(energies))] # Format and make the energies relative to minimum energy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# We can now add the energies of the structures as labels.\n", + "view_structures(mol_list,\n", + " labels=energy_labels # Add labels to the centroid of the molecules with the relative energies\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# You can also add arbitrary strings or numbers as labels\n", + "view_structures(mol_list,\n", + " labels=np.arange(len(mol_list)) # Add labels corresponding to the indices of the structures\n", + " )\n", + "view_structures(mol_list,\n", + " labels=['zero','one','two',\n", + " 'three','four','five'][:len(mol_list)] # Add labels up to the number of structures produced\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Interatomic distances can also be visualized\n", + "view_structures(mol_list,\n", + " vis_distances=True)\n", + "# For much more parameters on how to edit and tweak visualization of \n", + "# interatomic distances please see tutorial 11-Distance_Analysis.ipynb!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Similar to labels, you can request to label the indices in each structure.\n", + "view_structures(mol_list,\n", + " w=400, # Make the viewer larger\n", + " h=400, # Make the viewer larger\n", + " columns=2, # Switch to two columns\n", + " labelinds=True, # Label the indices of the atoms in the molecule\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# For simplicity, we can select a single structure to view the indices of the atoms in the molecule\n", + "mol = convert_io_molecule(mol_list[1])\n", + "# Similar to labels, you can pass a list of values or strings to visualize on top of the indices.\n", + "view_structures(mol,\n", + " w=400, # Make the viewer larger\n", + " h=400, # Make the viewer larger\n", + " labelinds=(np.arange(len(mol.ase_atoms))/10).tolist(), # Label the indices of the atoms in the molecule\n", + " )\n", + "# Note that this is useful for visualizing charges\n", + "view_structures(mol,\n", + " w=400, # Make the viewer larger\n", + " h=400, # Make the viewer larger\n", + " labelinds=mol.ase_atoms.get_chemical_symbols(), # Add chemical symbols\n", + " )\n", + "labelinds = list(map(chr, range(97, 123))) * 3\n", + "view_structures(mol,\n", + " w=400, # Make the viewer larger\n", + " h=400, # Make the viewer larger\n", + " labelinds=labelinds[:len(mol.ase_atoms)], # Add arbitrary letters to each atom\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Note, this can all be done for lists of structures as well!!!\n", + "labelinds = list(map(chr, range(97, 123))) * 3\n", + "view_structures(mol_list,\n", + " labelinds=[labelinds[:len(mol.ase_atoms)]]*len(mol_list), # Add arbitrary letters to each atom for each structure in list\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Now, we can also play with the parameters for the visualization itself.\n", + "view_structures(mol,\n", + " representation='stick', # Switch to stick representation\n", + " stick_scale=0.5, # Make the sticks larger\n", + " )\n", + "view_structures(mol,\n", + " representation='sphere' # Switch to sphere representation\n", + " )\n", + "view_structures(mol,\n", + " sphere_scale=0.5, # Increase sphere scales\n", + " metal_scale=1.3, # Increase metal sphere scale even more.\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Now, we can distort and relax a structure with XTB with in-built routines:\n", + "distortion,_,_ = random_sampler(mol,\n", + " n=1, # Produce one distortion\n", + " max_rmsd=0.2 # Make sure it's not too different \n", + " ) \n", + "distortion = distortion[0]\n", + "view_structures([mol,distortion],\n", + " labels=['UFF Minima','Distorted'],\n", + " columns=2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Now, we can relax the distortion\n", + "xtb_relaxed = CalcExecutor(mol,\n", + " method='GFN2-xTB',\n", + " relax=True,\n", + " save_trajectories=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Now we can visulaze both the distortion and the relaxed structures\n", + "view_structures([distortion,xtb_relaxed.mol],\n", + " labels=['Distorted','XTB Relaxed'],\n", + " columns=2\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Further, we can animate the relaxation trajectory! \n", + "view_structures(xtb_relaxed.trajectory,\n", + " w=400,\n", + " h=400,\n", + " trajectory=True # Treat as a trajectory for animation\n", + " )\n", + "# Note that anything that is a list of structures that architector can read (ase atoms, xyz, mol2 ...)\n", + "# Can be treated as a trajectory" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Further, we can animate the relaxation trajectory! \n", + "view_structures(xtb_relaxed.trajectory,\n", + " w=400,\n", + " h=400,\n", + " interval=400, # Slow down the trajectory animation\n", + " trajectory=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Note that this block takes around 1 minute on an M2Max Macbook Pro.\n", + "calced = CalcExecutor(xtb_relaxed.mol,\n", + " method='GFN2-xTB') # Re-run single point to get vibrations\n", + "with arch_context_manage.make_temp_directory() as _:\n", + " vib_analysis = Vibrations(calced.mol.ase_atoms) # Run vibrational analysis using ASE\n", + " vib_analysis.run()\n", + " data = vib_analysis.get_vibrations() # Get the vibrations\n", + " hess = data.get_hessian_2d() # Get the hessian \n", + "# Calculate the normal modes, force constnaces, and vibrational energies.\n", + "vib_energies, modes, fconstants, _ , frequencies = vibration_analysis(calced.mol.ase_atoms,\n", + " hess)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# With the vibrational modes, we can now visualize mode vibrations\n", + "view_structures(calced.mol,\n", + " modes=[modes[7]] # Visualize just one mode (non-zero modes start a 7)\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# We can also visualize more than one mode\n", + "view_structures([calced.mol]*8,\n", + " modes=modes[30:(30+8)], # Visualize First 8 modes\n", + " labels=frequencies[30:(30+8)] # Add labels corresponding to the frequencies modes (cm^-1)\n", + " )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Conclusions to using View Structures\n", + "\n", + "In this tutorial we walked through several examples highlighting view structures capabilites.\n", + "\n", + "Although all of these examples were performed using Architector-generated structures,\n", + "\n", + "All these capabilities can also be done with local files or paths pointing to structure files including xyz, mol2 ..." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "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.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}