├── .gitignore ├── README.md ├── environment-test.yml ├── environment.yml ├── mBuild_00_Getting_Started.ipynb ├── mBuild_01_Basic_Functionality.ipynb ├── mBuild_02_Reusing_Components.ipynb ├── mBuild_03_Connecting_Components_with_Ports.ipynb ├── mBuild_04_Constructing_Larger_Compounds.ipynb ├── mBuild_05_Creating_Flexible_Classes.ipynb ├── mBuild_06_Setting_Up_Bulk_Systems.ipynb ├── mBuild_07_Energy_Minimization.ipynb ├── mBuild_08_Building_Polymers.ipynb ├── mBuild_09_Surface_Functionalization.ipynb ├── mBuild_10_Constructing_Lattices.ipynb ├── nbval_sanitize.cfg ├── postBuild └── utils ├── cf2.pdb ├── ch2.pdb ├── cmim.mol2 └── hierarchical_design_image.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.ipynb_checkpoints 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mBuild: a hierarchical, component based molecule builder 2 | 3 | [![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/mosdef-hub/mbuild_tutorials/master) 4 | 5 | Contained in this repository are a series of tutorials to introduce new 6 | users to the [mBuild](https://github.com/mosdef-hub/mbuild) package for 7 | constructing molecular systems. The user is referred to 8 | [mBuild's documentation](http://mosdef-hub.github.io/mbuild/) for 9 | installation instructions. 10 | 11 | The tutorials contained within this repository will work with both the 12 | latest mBuild release as well as the development version. However, at 13 | this time, the user is strongly encouraged to use the development 14 | version of the software, as signficant updates to molecular visualization 15 | within the notebooks have been made. These changes will be reflected in 16 | an upcoming mBuild release at which time this message will be removed. 17 | 18 | ### How do I run these tutorials? 19 | 20 | The mBuild tutorials can be completed in two ways: 21 | 22 | 1. Creating the proper Python environment (the user is again referred to mBuild's installation instructions), cloning this repository, and launching a local Jupyter notebook server with `jupyter notebook` 23 | 24 | 2. Using our [Binder link](https://mybinder.org/v2/gh/mosdef-hub/mbuild_tutorials/master), where a Jupyter notebook server will be launched in the cloud with the proper Python environment pre-configured. 25 | -------------------------------------------------------------------------------- /environment-test.yml: -------------------------------------------------------------------------------- 1 | name: mbuild-tutorials 2 | 3 | channels: 4 | - conda-forge 5 | 6 | dependencies: 7 | - python>=3.6 8 | - numpy 9 | - scipy 10 | - packmol>=20 11 | - nglview>=3 12 | - parmed 13 | - mdtraj 14 | - gsd 15 | - openbabel 16 | - rdkit 17 | - jupyter 18 | - ipython=7.10.0 19 | - nbformat 20 | - mbuild 21 | - foyer 22 | - py3Dmol 23 | - pytest 24 | - nbval 25 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: mbuild-binder 2 | 3 | channels: 4 | - conda-forge 5 | 6 | dependencies: 7 | - python>=3.6 8 | - numpy 9 | - scipy 10 | - packmol>=20 11 | - nglview>=3 12 | - parmed 13 | - mdtraj 14 | - gsd 15 | - openbabel 16 | - rdkit 17 | - jupyter 18 | - nbformat 19 | - mbuild 20 | - foyer 21 | - py3Dmol 22 | -------------------------------------------------------------------------------- /mBuild_01_Basic_Functionality.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "# mBuild Tutorial 01: Basic Functionality\n", 10 | "\n", 11 | "This tutorial will cover the basic usage of mBuild's `Compound` class and the creation of static bonds between atoms." 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "As in the prior tutorial, we need to first import mbuild (here as `mb`)." 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 1, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "import warnings\n", 28 | "warnings.filterwarnings('ignore')\n", 29 | "\n", 30 | "import mbuild as mb" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "### The `Compound` class\n", 38 | "\n", 39 | "The base class of mBuild is the `Compound` class, which defines the primary building block used for constructing molecules. Molecules are constructed hierarchically; however, each level of the hierarchy inherits from the `Compound` class. This means that `Compounds` may contain other `Compounds`, and that the same methods and attributes are present for molecule components at any level of the hierarchy. mBuild `Compounds` feature [a variety of useful methods and attributes](http://mosdef-hub.github.io/mbuild/data_structures.html) to facilitate system construction.\n", 40 | "\n", 41 | "Here we will instantiate a `Compound` and view the available attributes and methods." 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 2, 47 | "metadata": {}, 48 | "outputs": [ 49 | { 50 | "data": { 51 | "text/plain": [ 52 | "['__class__',\n", 53 | " '__delattr__',\n", 54 | " '__dict__',\n", 55 | " '__dir__',\n", 56 | " '__doc__',\n", 57 | " '__eq__',\n", 58 | " '__format__',\n", 59 | " '__ge__',\n", 60 | " '__getattribute__',\n", 61 | " '__getitem__',\n", 62 | " '__gt__',\n", 63 | " '__hash__',\n", 64 | " '__init__',\n", 65 | " '__init_subclass__',\n", 66 | " '__le__',\n", 67 | " '__lt__',\n", 68 | " '__module__',\n", 69 | " '__ne__',\n", 70 | " '__new__',\n", 71 | " '__reduce__',\n", 72 | " '__reduce_ex__',\n", 73 | " '__repr__',\n", 74 | " '__setattr__',\n", 75 | " '__sizeof__',\n", 76 | " '__str__',\n", 77 | " '__subclasshook__',\n", 78 | " '__weakref__',\n", 79 | " '_box',\n", 80 | " '_charge',\n", 81 | " '_check_if_contains_rigid_bodies',\n", 82 | " '_clone',\n", 83 | " '_clone_bonds',\n", 84 | " '_contains_only_ports',\n", 85 | " '_contains_rigid',\n", 86 | " '_element',\n", 87 | " '_energy_minimize_openbabel',\n", 88 | " '_energy_minimize_openmm',\n", 89 | " '_increment_rigid_ids',\n", 90 | " '_kick',\n", 91 | " '_n_particles',\n", 92 | " '_particles',\n", 93 | " '_periodicity',\n", 94 | " '_pos',\n", 95 | " '_remove',\n", 96 | " '_remove_references',\n", 97 | " '_reorder_rigid_ids',\n", 98 | " '_rigid_id',\n", 99 | " '_update_port_locations',\n", 100 | " '_visualize_nglview',\n", 101 | " '_visualize_py3dmol',\n", 102 | " 'add',\n", 103 | " 'add_bond',\n", 104 | " 'all_ports',\n", 105 | " 'ancestors',\n", 106 | " 'available_ports',\n", 107 | " 'bond_graph',\n", 108 | " 'bonds',\n", 109 | " 'box',\n", 110 | " 'center',\n", 111 | " 'charge',\n", 112 | " 'children',\n", 113 | " 'contains_rigid',\n", 114 | " 'element',\n", 115 | " 'energy_minimization',\n", 116 | " 'energy_minimize',\n", 117 | " 'from_parmed',\n", 118 | " 'from_pybel',\n", 119 | " 'from_trajectory',\n", 120 | " 'generate_bonds',\n", 121 | " 'get_boundingbox',\n", 122 | " 'get_smiles',\n", 123 | " 'label_rigid_bodies',\n", 124 | " 'labels',\n", 125 | " 'max_rigid_id',\n", 126 | " 'maxs',\n", 127 | " 'min_periodic_distance',\n", 128 | " 'mins',\n", 129 | " 'n_bonds',\n", 130 | " 'n_particles',\n", 131 | " 'name',\n", 132 | " 'parent',\n", 133 | " 'particles',\n", 134 | " 'particles_by_element',\n", 135 | " 'particles_by_name',\n", 136 | " 'particles_in_range',\n", 137 | " 'periodicity',\n", 138 | " 'port_particle',\n", 139 | " 'pos',\n", 140 | " 'referenced_ports',\n", 141 | " 'referrers',\n", 142 | " 'remove',\n", 143 | " 'remove_bond',\n", 144 | " 'rigid_id',\n", 145 | " 'rigid_particles',\n", 146 | " 'root',\n", 147 | " 'rotate',\n", 148 | " 'save',\n", 149 | " 'spin',\n", 150 | " 'successors',\n", 151 | " 'to_intermol',\n", 152 | " 'to_networkx',\n", 153 | " 'to_parmed',\n", 154 | " 'to_pybel',\n", 155 | " 'to_trajectory',\n", 156 | " 'translate',\n", 157 | " 'translate_to',\n", 158 | " 'unlabel_rigid_bodies',\n", 159 | " 'update_coordinates',\n", 160 | " 'visualize',\n", 161 | " 'xyz',\n", 162 | " 'xyz_with_ports']" 163 | ] 164 | }, 165 | "execution_count": 2, 166 | "metadata": {}, 167 | "output_type": "execute_result" 168 | } 169 | ], 170 | "source": [ 171 | "my_compound = mb.Compound()\n", 172 | "dir(my_compound)" 173 | ] 174 | }, 175 | { 176 | "cell_type": "markdown", 177 | "metadata": {}, 178 | "source": [ 179 | "### Creating `Compounds`\n", 180 | "\n", 181 | "There are several ways that `Compounds` can be created with mBuild. The simplest is to construct them from `Particles`. The `Particle` class is used to define `Compounds` residing at the lowest level of the containment hierarchy. Standard mBuild protocol is to define `Particle` names according to their elemental symbol (e.g. `'C'`), or to preface `Particle` names by an underscore for non-atomistic particles (e.g. `'_CH4'` for a united-atom methane). This aids in the atomtyping and forcefield application process (using the Foyer package) which will be addressed in later tutorials.\n", 182 | "\n", 183 | "Now, lets create a simple carbon `Particle`. [Several arguments are available](http://mosdef-hub.github.io/mbuild/data_structures.html#mbuild.compound.Compound) to set various `Compound`/`Particle` attributes upon instantiation. Here, we'll use the `name` argument to specify the element of our `Particle` and the `pos` argument to specify the location of the `Particle` in Cartesian space.\n", 184 | "\n", 185 | "**Important Note:** mBuild expects all distance units to be in nanometers." 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": 3, 191 | "metadata": {}, 192 | "outputs": [ 193 | { 194 | "data": { 195 | "text/plain": [ 196 | "" 197 | ] 198 | }, 199 | "execution_count": 3, 200 | "metadata": {}, 201 | "output_type": "execute_result" 202 | } 203 | ], 204 | "source": [ 205 | "carbon = mb.Particle(name='C', pos=[1.0, 2.0, 3.0])\n", 206 | "carbon" 207 | ] 208 | }, 209 | { 210 | "cell_type": "markdown", 211 | "metadata": {}, 212 | "source": [ 213 | "Now, we will create a simple CH2 moiety. (Don't worry about the undercoordinated carbon; we'll be using this later to piece together an alkane chain.)\n", 214 | "\n", 215 | "It is often easiest to create systems by working top-down through the molecule hierarchy. This can be achieved by creating an empty `Compound` to serve as a container to add other `Compounds` to.\n", 216 | "\n", 217 | "Here, we will take this approach by creating an empty `Compound` to which we will add a carbon `Particle` and two hydrogen `Particles`." 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": 4, 223 | "metadata": {}, 224 | "outputs": [], 225 | "source": [ 226 | "ch2 = mb.Compound()" 227 | ] 228 | }, 229 | { 230 | "cell_type": "markdown", 231 | "metadata": {}, 232 | "source": [ 233 | "Note, that we can provide a name to this compound (beyond just the variable name ch2)." 234 | ] 235 | }, 236 | { 237 | "cell_type": "code", 238 | "execution_count": 5, 239 | "metadata": {}, 240 | "outputs": [], 241 | "source": [ 242 | "ch2 = mb.Compound(name='ch2 compound')" 243 | ] 244 | }, 245 | { 246 | "cell_type": "markdown", 247 | "metadata": {}, 248 | "source": [ 249 | "Now we need to create three `Particles`: one carbon and two hydrogens. We'll manually set the atomic positions such that they are reasonably representative of realistic atomic spacings." 250 | ] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": 6, 255 | "metadata": {}, 256 | "outputs": [], 257 | "source": [ 258 | "carbon = mb.Particle(pos=[0.0, 0.0, 0.0], name='C')\n", 259 | "hydrogen = mb.Particle(pos=[0.1, 0.0, 0.0], name='H')\n", 260 | "hydrogen2 = mb.Particle(pos=[-0.1, 0.0, 0.0], name='H')" 261 | ] 262 | }, 263 | { 264 | "cell_type": "markdown", 265 | "metadata": {}, 266 | "source": [ 267 | "Now we need to add our three `Particles` to the hierarchy of our CH2 `Compound`. This can be done using the `add` method. The `new_child` argument allows you to specify what child `Compound` should be added to the hierarchy of the parent `Compound`. We can also pass a list of `Compounds` through the `new_child` argument to add multiple `Compounds` to the hierarchy at once. We'll take that approach here to add the three `Particles` we've just created to the hierarchy of the ch2 `Compound`." 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 7, 273 | "metadata": {}, 274 | "outputs": [], 275 | "source": [ 276 | "ch2.add(new_child=[carbon, hydrogen, hydrogen2])" 277 | ] 278 | }, 279 | { 280 | "cell_type": "markdown", 281 | "metadata": {}, 282 | "source": [ 283 | "To view the children of a `Compound` (i.e. one level below in the molecule hierarchy) we can look at the `children` attribute." 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": 8, 289 | "metadata": {}, 290 | "outputs": [ 291 | { 292 | "data": { 293 | "text/plain": [ 294 | "OrderedSet(, , )" 295 | ] 296 | }, 297 | "execution_count": 8, 298 | "metadata": {}, 299 | "output_type": "execute_result" 300 | } 301 | ], 302 | "source": [ 303 | "ch2.children" 304 | ] 305 | }, 306 | { 307 | "cell_type": "markdown", 308 | "metadata": {}, 309 | "source": [ 310 | "We can use the `particles` method to view the `Particles` (i.e. those `Compounds` residing at the bottom of the molecule hierarchy) contained by a `Compound`. In this case, because our hierarchy only features two levels, this will be the same as `ch2.children`. Note that this method is written as a generator to conserve memory for large systems, so we'll need to convert to a `list`." 311 | ] 312 | }, 313 | { 314 | "cell_type": "code", 315 | "execution_count": 9, 316 | "metadata": {}, 317 | "outputs": [ 318 | { 319 | "data": { 320 | "text/plain": [ 321 | "[,\n", 322 | " ,\n", 323 | " ]" 324 | ] 325 | }, 326 | "execution_count": 9, 327 | "metadata": {}, 328 | "output_type": "execute_result" 329 | } 330 | ], 331 | "source": [ 332 | "list(ch2.particles())" 333 | ] 334 | }, 335 | { 336 | "cell_type": "markdown", 337 | "metadata": {}, 338 | "source": [ 339 | "As we can see, our carbon `Particle` and two hydrogen `Particles` are now contained within our CH2 `Compound`. Now let's visualize our `Compound` to confirm we built this correctly." 340 | ] 341 | }, 342 | { 343 | "cell_type": "code", 344 | "execution_count": 10, 345 | "metadata": {}, 346 | "outputs": [ 347 | { 348 | "data": { 349 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 350 | "text/html": [ 351 | "
\n", 352 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 353 | " jupyter labextension install jupyterlab_3dmol

\n", 354 | "
\n", 355 | "" 389 | ] 390 | }, 391 | "metadata": {}, 392 | "output_type": "display_data" 393 | }, 394 | { 395 | "data": { 396 | "text/plain": [ 397 | "" 398 | ] 399 | }, 400 | "execution_count": 10, 401 | "metadata": {}, 402 | "output_type": "execute_result" 403 | } 404 | ], 405 | "source": [ 406 | "ch2.visualize()" 407 | ] 408 | }, 409 | { 410 | "cell_type": "markdown", 411 | "metadata": {}, 412 | "source": [ 413 | "Although we've added our three `Particles` to the ch2 `Compound`, we have yet to define any bonds between them. To accomplish this, we can use the `Compound.add_bond()` method to specify our two C-H bonds." 414 | ] 415 | }, 416 | { 417 | "cell_type": "code", 418 | "execution_count": 11, 419 | "metadata": {}, 420 | "outputs": [ 421 | { 422 | "data": { 423 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 424 | "text/html": [ 425 | "
\n", 426 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 427 | " jupyter labextension install jupyterlab_3dmol

\n", 428 | "
\n", 429 | "" 463 | ] 464 | }, 465 | "metadata": {}, 466 | "output_type": "display_data" 467 | }, 468 | { 469 | "data": { 470 | "text/plain": [ 471 | "" 472 | ] 473 | }, 474 | "execution_count": 11, 475 | "metadata": {}, 476 | "output_type": "execute_result" 477 | } 478 | ], 479 | "source": [ 480 | "ch2.add_bond((carbon, hydrogen))\n", 481 | "ch2.add_bond((carbon, hydrogen2))\n", 482 | "ch2.visualize()" 483 | ] 484 | }, 485 | { 486 | "cell_type": "markdown", 487 | "metadata": {}, 488 | "source": [ 489 | "Visually, we now see that our ch2 `Compound` contains three `Particles` and two C-H bonds.\n", 490 | "\n", 491 | "Note that this tutorial was designed to introduce the basics of the `Compound` class and that in most cases you will not need to build `Compounds` by creating each particle one-by-one and manually adding bonds. The following tutorials will demonstrate how software such as Avogadro can be used to create small molecular moieites and how `Compounds` can be stitched together using `Ports` which can automatically create bonds for us." 492 | ] 493 | }, 494 | { 495 | "cell_type": "markdown", 496 | "metadata": { 497 | "collapsed": true 498 | }, 499 | "source": [ 500 | "## Recap\n", 501 | "\n", 502 | "The goal of this tutorial was to demonstrate the basics of mBuild's `Compound` class: how to instantiate `Compounds` and provide useful names, how to add `Compounds` to the molecule hierarchy of other `Compounds`, how to query which child `Compounds` and `Particles` reside in a `Compound`'s hierarchy, and how to define bonds between `Compounds`.\n", 503 | "\n", 504 | "In the next tutorial you will learn about wrapping routines for creating `Compounds` into classes for reusability." 505 | ] 506 | } 507 | ], 508 | "metadata": { 509 | "kernelspec": { 510 | "display_name": "Python 3", 511 | "language": "python", 512 | "name": "python3" 513 | }, 514 | "language_info": { 515 | "codemirror_mode": { 516 | "name": "ipython", 517 | "version": 3 518 | }, 519 | "file_extension": ".py", 520 | "mimetype": "text/x-python", 521 | "name": "python", 522 | "nbconvert_exporter": "python", 523 | "pygments_lexer": "ipython3", 524 | "version": "3.8.10" 525 | } 526 | }, 527 | "nbformat": 4, 528 | "nbformat_minor": 2 529 | } 530 | -------------------------------------------------------------------------------- /mBuild_02_Reusing_Components.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "# mBuild Tutorial 02: Reusing Components\n", 10 | "\n", 11 | "This tutorial demonstrates how to create reusable components in mBuild, through definition of custom classes that inherit from the mbuild `Compound` class." 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "As in the prior tutorials, we need to first import mbuild (here as `mb`)." 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 1, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "import warnings\n", 28 | "warnings.filterwarnings('ignore')\n", 29 | "\n", 30 | "import mbuild as mb" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "It would be quite tedious to have to perform each of the steps in prior tutorial (i.e. defining the particle positions and bonds) every time we wanted to create a new CH2 `Compound`. Fortunately, this problem is easily solved by wrapping these routines together into a class.\n", 38 | "\n", 39 | "Here, we'll create a class for our CH2 moiety using the same commands we used in prior tutorial so that we can easily reuse this piece when constructing more complex molecules." 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 2, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "class CH2(mb.Compound):\n", 49 | " def __init__(self):\n", 50 | " super(CH2, self).__init__()\n", 51 | " \n", 52 | " carbon = mb.Particle(pos=[0.0, 0.0, 0.0], name='C')\n", 53 | " hydrogen = mb.Particle(pos=[0.1, 0.0, 0.0], name='H')\n", 54 | " hydrogen2 = mb.Particle(pos=[-0.1, 0.0, 0.0], name='H')\n", 55 | " self.add([carbon, hydrogen, hydrogen2])\n", 56 | " self.add_bond((carbon, hydrogen))\n", 57 | " self.add_bond((carbon, hydrogen2))" 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "metadata": {}, 63 | "source": [ 64 | "As we can see, our class definition contains the same commands we just used in the prior tutorial the create the ch2 `Compound`; however, we have replaced `ch2` with `self` so that these commands will be performed on any instance of our `CH2` class. Additionally, since we want our class instance to be an mBuild `Compound`, we specify that our `CH2` class should inherit from `mb.Compound`.\n", 65 | "\n", 66 | "Now we'll create an instance of our `CH2` class and visualize it." 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": 3, 72 | "metadata": {}, 73 | "outputs": [ 74 | { 75 | "data": { 76 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 77 | "text/html": [ 78 | "
\n", 79 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 80 | " jupyter labextension install jupyterlab_3dmol

\n", 81 | "
\n", 82 | "" 116 | ] 117 | }, 118 | "metadata": {}, 119 | "output_type": "display_data" 120 | }, 121 | { 122 | "data": { 123 | "text/plain": [ 124 | "" 125 | ] 126 | }, 127 | "execution_count": 3, 128 | "metadata": {}, 129 | "output_type": "execute_result" 130 | } 131 | ], 132 | "source": [ 133 | "ch2 = CH2()\n", 134 | "ch2.visualize()" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "Because we have inherited from `mb.Compound`, our `CH2` class has access to all of the attributes and methods of `Compound`. We can thus perform actions such as querying the particles contained within our `CH2` instance." 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": 4, 147 | "metadata": {}, 148 | "outputs": [ 149 | { 150 | "data": { 151 | "text/plain": [ 152 | "[,\n", 153 | " ,\n", 154 | " ]" 155 | ] 156 | }, 157 | "execution_count": 4, 158 | "metadata": {}, 159 | "output_type": "execute_result" 160 | } 161 | ], 162 | "source": [ 163 | "list(ch2.particles())" 164 | ] 165 | }, 166 | { 167 | "cell_type": "markdown", 168 | "metadata": {}, 169 | "source": [ 170 | "We can also rename our `CH2` if we would like." 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": 5, 176 | "metadata": {}, 177 | "outputs": [ 178 | { 179 | "data": { 180 | "text/plain": [ 181 | "" 182 | ] 183 | }, 184 | "execution_count": 5, 185 | "metadata": {}, 186 | "output_type": "execute_result" 187 | } 188 | ], 189 | "source": [ 190 | "ch2.name = 'myCH2'\n", 191 | "ch2" 192 | ] 193 | }, 194 | { 195 | "cell_type": "markdown", 196 | "metadata": {}, 197 | "source": [ 198 | "While there are instances where creating `Compounds` particle-by-particle is useful, this process can become a bit tedious. It's much easier to create `Compounds` by loading pre-assembled building blocks. These can easily be created using software such as [Avogadro](https://avogadro.cc/).\n", 199 | "\n", 200 | "The `load` function can generate mBuild `Compounds` from a variety of common file formats (e.g. PDB, MOL2) that contain particle positions and bonds. Under the hood, mBuild uses readers from the [MDTraj package](http://mdtraj.org). Users are referred to the [MDTraj source](https://github.com/mdtraj/mdtraj/blob/b05b4637aaeda95fdf284a57ee2a37bc8e453ea1/mdtraj/core/trajectory.py#L77-L79) for a list of acceptable file formats. [Several additional formats](http://mdtraj.org/1.9.0/load_functions.html#format-specific-loading-functions) are also supported, but require an additional `topology` argument be specified.\n", 201 | "\n", 202 | "Let's take a look at the docstring for the `load` function." 203 | ] 204 | }, 205 | { 206 | "cell_type": "code", 207 | "execution_count": 6, 208 | "metadata": {}, 209 | "outputs": [ 210 | { 211 | "name": "stdout", 212 | "output_type": "stream", 213 | "text": [ 214 | "Help on function load in module mbuild.conversion:\n", 215 | "\n", 216 | "load(filename_or_object, relative_to_module=None, compound=None, coords_only=False, rigid=False, smiles=False, infer_hierarchy=True, backend=None, ignore_box_warn=False, **kwargs)\n", 217 | " Load a file or an existing topology into an mbuild Compound.\n", 218 | " \n", 219 | " Files are read using the MDTraj package unless the `use_parmed` argument is\n", 220 | " specified as True.\n", 221 | " Please refer to http://mdtraj.org/1.8.0/load_functions.html for formats\n", 222 | " supported by MDTraj and https://parmed.github.io/ParmEd/html/readwrite.html\n", 223 | " for formats supported by ParmEd.\n", 224 | " \n", 225 | " Parameters\n", 226 | " ----------\n", 227 | " filename_or_object : str, mdtraj.Trajectory, parmed.Structure,\n", 228 | " mbuild.Compound, pybel.Molecule,\n", 229 | " Name of the file or topology from which to load atom and bond\n", 230 | " information.\n", 231 | " relative_to_module : str, optional, default=None\n", 232 | " Instead of looking in the current working directory, look for the file\n", 233 | " where this module is defined. This is typically used in Compound\n", 234 | " classes that will be instantiated from a different directory (such as\n", 235 | " the Compounds located in mbuild.lib).\n", 236 | " compound : mb.Compound, optional, default=None\n", 237 | " Existing compound to load atom and bond information into. New structure\n", 238 | " will be added to the existing compound as a sub compound.\n", 239 | " coords_only : bool, optional, default=False\n", 240 | " Only load the coordinates into an existing compound.\n", 241 | " rigid : bool, optional, default=False\n", 242 | " Treat the compound as a rigid body\n", 243 | " backend : str, optional, default=None\n", 244 | " Backend used to load structure from file or string. If not specified, a\n", 245 | " default backend (extension specific) will be used.\n", 246 | " smiles: bool, optional, default=False\n", 247 | " Use RDKit or OpenBabel to parse filename as a SMILES string or file\n", 248 | " containing a SMILES string. If this is set to True, `rdkit` is the\n", 249 | " default backend.\n", 250 | " infer_hierarchy : bool, optional, default=True\n", 251 | " If True, infer hierarchy from chains and residues\n", 252 | " ignore_box_warn : bool, optional, default=False\n", 253 | " If True, ignore warning if no box is present. Defaults to True when\n", 254 | " loading from SMILES\n", 255 | " **kwargs : keyword arguments\n", 256 | " Key word arguments passed to mdTraj, RDKit, or pybel for loading.\n", 257 | " \n", 258 | " Returns\n", 259 | " -------\n", 260 | " compound : mb.Compound\n", 261 | " \n", 262 | " Notes\n", 263 | " -----\n", 264 | " If `smiles` is `True`, either `rdkit` (default) or `pybel` can be used, but\n", 265 | " RDkit is the only option of these that allows the user to specify a random\n", 266 | " number seed to reproducibly generate the same starting structure. This is\n", 267 | " NOT possible with `openbabel`, use `rdkit` if you need control over starting\n", 268 | " structure's position (recommended).\n", 269 | "\n" 270 | ] 271 | } 272 | ], 273 | "source": [ 274 | "help(mb.load)" 275 | ] 276 | }, 277 | { 278 | "cell_type": "markdown", 279 | "metadata": {}, 280 | "source": [ 281 | "Now we'll use `load` to again create a CH2 `Compound`, this time by loading from a PDB structure file." 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": 7, 287 | "metadata": {}, 288 | "outputs": [], 289 | "source": [ 290 | "class CH2(mb.Compound):\n", 291 | " def __init__(self):\n", 292 | " super(CH2, self).__init__()\n", 293 | " \n", 294 | " mb.load('utils/ch2.pdb', compound=self)" 295 | ] 296 | }, 297 | { 298 | "cell_type": "markdown", 299 | "metadata": {}, 300 | "source": [ 301 | "Note that by providing the `compound=self` argument to `mb.load` we will be loading the atom and bond information contained within our PDB file directly into our `CH2` class instance.\n", 302 | "\n", 303 | "**Note:** mBuild does not infer bonds (although you can achieve this with the [`generate_bonds`](http://mosdef-hub.github.io/mbuild/data_structures.html#mbuild.compound.Compound.generate_bonds) method). All bonds must be explicitly defined in your code or in a structure input file.\n", 304 | "\n", 305 | "If we take a look at an instance of this new `CH2` class, we will see that we get a similar result to when we constructed the `Compound` manually and that both particle positions and bond information have been loaded successfully." 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": 8, 311 | "metadata": {}, 312 | "outputs": [ 313 | { 314 | "data": { 315 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 316 | "text/html": [ 317 | "
\n", 318 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 319 | " jupyter labextension install jupyterlab_3dmol

\n", 320 | "
\n", 321 | "" 355 | ] 356 | }, 357 | "metadata": {}, 358 | "output_type": "display_data" 359 | }, 360 | { 361 | "data": { 362 | "text/plain": [ 363 | "" 364 | ] 365 | }, 366 | "execution_count": 8, 367 | "metadata": {}, 368 | "output_type": "execute_result" 369 | } 370 | ], 371 | "source": [ 372 | "ch2 = CH2()\n", 373 | "ch2.visualize()" 374 | ] 375 | }, 376 | { 377 | "cell_type": "markdown", 378 | "metadata": { 379 | "collapsed": true 380 | }, 381 | "source": [ 382 | "## Recap\n", 383 | "\n", 384 | "The goal of this tutorial was to demonstrate how routines for creating mBuild `Compounds` can be wrapped into classes, allowing for reusability. We also found that we can load information from structure files such as PDB or MOL2 to create `Compounds`.\n", 385 | "\n", 386 | "The next tutorial will demonstrate how another of mBuild's base classes, the `Port` class, can be used to create connections (bonds) between `Compounds` and move them in space." 387 | ] 388 | } 389 | ], 390 | "metadata": { 391 | "kernelspec": { 392 | "display_name": "Python 3", 393 | "language": "python", 394 | "name": "python3" 395 | }, 396 | "language_info": { 397 | "codemirror_mode": { 398 | "name": "ipython", 399 | "version": 3 400 | }, 401 | "file_extension": ".py", 402 | "mimetype": "text/x-python", 403 | "name": "python", 404 | "nbconvert_exporter": "python", 405 | "pygments_lexer": "ipython3", 406 | "version": "3.8.10" 407 | } 408 | }, 409 | "nbformat": 4, 410 | "nbformat_minor": 2 411 | } 412 | -------------------------------------------------------------------------------- /mBuild_03_Connecting_Components_with_Ports.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "# mBuild Tutorial 03: Connecting Components with Ports\n", 10 | "\n", 11 | "This tutorial demonstrates how to create `Ports` on `Compounds`, which aids in connecting them together by both creating bonds and moving the `Compounds` in space." 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "As in the prior tutorials, we need to first import mbuild (here as `mb`)." 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 1, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "import warnings\n", 28 | "warnings.filterwarnings('ignore')\n", 29 | "\n", 30 | "import mbuild as mb" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "We've already shown that `Particles` can be connected (i.e. bonded) by using the `add_bond` routine; however, this does not actually move the atoms in space, and it would become burdensome to need to manually update the position of each atom when connecting them together. Additionally, having to create fixed bonds for all connections would make mBuild less flexible; again, a key function of mBuild is the ability to exchange and swap `Compounds`, allowing arbitrary molecules to be constructed. \n", 38 | "\n", 39 | "This is where [mBuild's `Port` class](http://mosdef-hub.github.io/mbuild/data_structures.html#mbuild.port.Port) comes into play. `Ports`, in the most general sense, define a location (and direction) in space; however, in most cases these can be thought of as dangling bonds.\n", 40 | "\n", 41 | "Let's test this functionality by using `Ports` instead of `add_bond` to create a ch2 moiety. First, we'll create an empty `Compound` for CH2 that we will add three `Particles` to at unrealistic locations." 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 2, 47 | "metadata": {}, 48 | "outputs": [ 49 | { 50 | "data": { 51 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 52 | "text/html": [ 53 | "
\n", 54 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 55 | " jupyter labextension install jupyterlab_3dmol

\n", 56 | "
\n", 57 | "" 91 | ] 92 | }, 93 | "metadata": {}, 94 | "output_type": "display_data" 95 | }, 96 | { 97 | "data": { 98 | "text/plain": [ 99 | "" 100 | ] 101 | }, 102 | "execution_count": 2, 103 | "metadata": {}, 104 | "output_type": "execute_result" 105 | } 106 | ], 107 | "source": [ 108 | "ch2 = mb.Compound()\n", 109 | "carbon = mb.Particle(pos=[0.0, 0.0, 0.0], name='C')\n", 110 | "hydrogen = mb.Particle(pos=[1.0, 0.0, 0.0], name='H')\n", 111 | "hydrogen2 = mb.Particle(pos=[2.0, 0.0, 0.0], name='H')\n", 112 | "ch2.add([carbon, hydrogen, hydrogen2])\n", 113 | "ch2.visualize()" 114 | ] 115 | }, 116 | { 117 | "cell_type": "markdown", 118 | "metadata": {}, 119 | "source": [ 120 | "Now we'll create a `Port` instance and attach it to the carbon atom by using the `anchor` attribute. This allows mBuild to know which atoms to create bonds between when two `Ports` are connected (as well as providing a reference for any geometric transformations). We can also provide an `orientation` vector to give our `Port` a desired direction, and can use the `separation` argument to shift our `Port` from the position of the anchor `Particle`. Since we're going to be connecting to a hydrogen, we will shift our `Port` roughly half of a C-H bond length." 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 3, 126 | "metadata": {}, 127 | "outputs": [ 128 | { 129 | "data": { 130 | "text/plain": [ 131 | "" 132 | ] 133 | }, 134 | "execution_count": 3, 135 | "metadata": {}, 136 | "output_type": "execute_result" 137 | } 138 | ], 139 | "source": [ 140 | "port_C = mb.Port(anchor=carbon, orientation=[1, 0, 0], separation=0.05)\n", 141 | "port_C" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": {}, 147 | "source": [ 148 | "We now need to add this `Port` to the containment hierarchy of our ch2 `Compound`, again using the `add` method. We can also provide a descriptive label for our `Port` that we can use for easy access; here we will name this port `right`." 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 4, 154 | "metadata": {}, 155 | "outputs": [ 156 | { 157 | "data": { 158 | "text/plain": [ 159 | "" 160 | ] 161 | }, 162 | "execution_count": 4, 163 | "metadata": {}, 164 | "output_type": "execute_result" 165 | } 166 | ], 167 | "source": [ 168 | "ch2.add(port_C, label='right')\n", 169 | "ch2['right']" 170 | ] 171 | }, 172 | { 173 | "cell_type": "markdown", 174 | "metadata": {}, 175 | "source": [ 176 | "Now we need to add another `Port` to the carbon `Particle` and one `Port` to each hydrogen `Particle`, giving each of these distinct labels. We'll first add another `Port` to carbon (labeled `left`) and a `Port` on each of the hydrogens (labeled `H1` and `H2`). " 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": 5, 182 | "metadata": {}, 183 | "outputs": [ 184 | { 185 | "data": { 186 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 187 | "text/html": [ 188 | "
\n", 189 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 190 | " jupyter labextension install jupyterlab_3dmol

\n", 191 | "
\n", 192 | "" 226 | ] 227 | }, 228 | "metadata": {}, 229 | "output_type": "display_data" 230 | }, 231 | { 232 | "data": { 233 | "text/plain": [ 234 | "" 235 | ] 236 | }, 237 | "execution_count": 5, 238 | "metadata": {}, 239 | "output_type": "execute_result" 240 | } 241 | ], 242 | "source": [ 243 | "port2_C = mb.Port(anchor=carbon, orientation=[-1, 0, 0], separation=0.05)\n", 244 | "ch2.add(port2_C, label='left')\n", 245 | "\n", 246 | "port_H = mb.Port(anchor=hydrogen, orientation=[1, 0, 0], separation=0.05)\n", 247 | "ch2.add(port_H, label='H1')\n", 248 | "\n", 249 | "port2_H = mb.Port(anchor=hydrogen2, orientation=[1, 0, 0], separation=0.05)\n", 250 | "ch2.add(port2_H, label='H2')\n", 251 | "\n", 252 | "ch2.visualize(show_ports=True)" 253 | ] 254 | }, 255 | { 256 | "cell_type": "markdown", 257 | "metadata": {}, 258 | "source": [ 259 | "The `force_overlap` function is then used to force the overlap of two `Ports` by performing a coordinate transform on one of the two `Compounds` that are to be connected. This will also create a bond between the anchor `Particles` of each `Port`. We'll use this function here to connect each hydrogen to the carbon `Particle`." 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": 6, 265 | "metadata": {}, 266 | "outputs": [ 267 | { 268 | "data": { 269 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 270 | "text/html": [ 271 | "
\n", 272 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 273 | " jupyter labextension install jupyterlab_3dmol

\n", 274 | "
\n", 275 | "" 309 | ] 310 | }, 311 | "metadata": {}, 312 | "output_type": "display_data" 313 | }, 314 | { 315 | "data": { 316 | "text/plain": [ 317 | "" 318 | ] 319 | }, 320 | "execution_count": 6, 321 | "metadata": {}, 322 | "output_type": "execute_result" 323 | } 324 | ], 325 | "source": [ 326 | "mb.force_overlap(move_this=hydrogen,\n", 327 | " from_positions=ch2['H1'],\n", 328 | " to_positions=ch2['right'])\n", 329 | "\n", 330 | "mb.force_overlap(move_this=hydrogen2,\n", 331 | " from_positions=ch2['H2'],\n", 332 | " to_positions=ch2['left'])\n", 333 | "\n", 334 | "ch2.visualize(show_ports=True)" 335 | ] 336 | }, 337 | { 338 | "cell_type": "markdown", 339 | "metadata": {}, 340 | "source": [ 341 | "As can be seen above, the hydrogen atoms were appropriately translated and oriented to create the expected ch2 structure.\n", 342 | "\n", 343 | "Note that once two `Particles` are connected using `force_overlap` the `Ports` used to connect these `Particles` are removed. However, if we were to remove the bond between one of the hydrogen atoms and the carbon atoms, two new `Ports` will be created, one attached to each `Particle`, along the bond vector." 344 | ] 345 | }, 346 | { 347 | "cell_type": "code", 348 | "execution_count": 7, 349 | "metadata": {}, 350 | "outputs": [ 351 | { 352 | "data": { 353 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 354 | "text/html": [ 355 | "
\n", 356 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 357 | " jupyter labextension install jupyterlab_3dmol

\n", 358 | "
\n", 359 | "" 393 | ] 394 | }, 395 | "metadata": {}, 396 | "output_type": "display_data" 397 | }, 398 | { 399 | "data": { 400 | "text/plain": [ 401 | "" 402 | ] 403 | }, 404 | "execution_count": 7, 405 | "metadata": {}, 406 | "output_type": "execute_result" 407 | } 408 | ], 409 | "source": [ 410 | "ch2.remove_bond(particle_pair=(carbon, hydrogen))\n", 411 | "ch2.visualize(show_ports=True)" 412 | ] 413 | }, 414 | { 415 | "cell_type": "markdown", 416 | "metadata": {}, 417 | "source": [ 418 | "We could now remove the hydrogen we disconnected and connect something else to our CH moiety. To achieve this, we will need to use the `remove` method to remove both the hydrogen atom *and* the `Port` connected to this atom from our molecule hierarchy.\n", 419 | "\n", 420 | "First, however, we'll use the `available_ports` method to query the `Ports` present in our molecule, so that we can fine out the name of the `Port` we want to remove." 421 | ] 422 | }, 423 | { 424 | "cell_type": "code", 425 | "execution_count": 8, 426 | "metadata": {}, 427 | "outputs": [ 428 | { 429 | "data": { 430 | "text/plain": [ 431 | "[,\n", 432 | " ]" 433 | ] 434 | }, 435 | "execution_count": 8, 436 | "metadata": {}, 437 | "output_type": "execute_result" 438 | } 439 | ], 440 | "source": [ 441 | "ch2.available_ports()" 442 | ] 443 | }, 444 | { 445 | "cell_type": "markdown", 446 | "metadata": {}, 447 | "source": [ 448 | "Now we'll use `remove` to remove `port[1]` and the hydrogen atom from our molecule." 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "execution_count": 9, 454 | "metadata": {}, 455 | "outputs": [ 456 | { 457 | "data": { 458 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 459 | "text/html": [ 460 | "
\n", 461 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 462 | " jupyter labextension install jupyterlab_3dmol

\n", 463 | "
\n", 464 | "" 498 | ] 499 | }, 500 | "metadata": {}, 501 | "output_type": "display_data" 502 | }, 503 | { 504 | "data": { 505 | "text/plain": [ 506 | "" 507 | ] 508 | }, 509 | "execution_count": 9, 510 | "metadata": {}, 511 | "output_type": "execute_result" 512 | } 513 | ], 514 | "source": [ 515 | "ch2.remove([ch2['port[1]'], hydrogen])\n", 516 | "ch2.visualize(show_ports=True)" 517 | ] 518 | }, 519 | { 520 | "cell_type": "markdown", 521 | "metadata": {}, 522 | "source": [ 523 | "Now, we'll use the `clone` function to create a deep copy of the CH moiety and, again using `force_overlap` will connect the two CH's together. We'll then add both of these CH moieties to a parent `Compound` and visualize." 524 | ] 525 | }, 526 | { 527 | "cell_type": "code", 528 | "execution_count": 10, 529 | "metadata": {}, 530 | "outputs": [ 531 | { 532 | "data": { 533 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 534 | "text/html": [ 535 | "
\n", 536 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 537 | " jupyter labextension install jupyterlab_3dmol

\n", 538 | "
\n", 539 | "" 573 | ] 574 | }, 575 | "metadata": {}, 576 | "output_type": "display_data" 577 | }, 578 | { 579 | "data": { 580 | "text/plain": [ 581 | "" 582 | ] 583 | }, 584 | "execution_count": 10, 585 | "metadata": {}, 586 | "output_type": "execute_result" 587 | } 588 | ], 589 | "source": [ 590 | "ch2_copy = mb.clone(ch2)\n", 591 | "mb.force_overlap(move_this=ch2_copy,\n", 592 | " from_positions=ch2_copy['port[0]'],\n", 593 | " to_positions=ch2['port[0]'])\n", 594 | "parent = mb.Compound(subcompounds=[ch2, ch2_copy])\n", 595 | "parent.visualize(show_ports=True)" 596 | ] 597 | }, 598 | { 599 | "cell_type": "markdown", 600 | "metadata": {}, 601 | "source": [ 602 | "We now see that we have two CH's connected! However, because our `Ports` were based off of the `C-H` bond distance, the two carbon atoms are unrealistically close to one another. While mBuild does feature an `energy_minimization` function that could remedy this, we could also manually shift our CH2 copy a bit to the left using the `translate` method." 603 | ] 604 | }, 605 | { 606 | "cell_type": "code", 607 | "execution_count": 11, 608 | "metadata": {}, 609 | "outputs": [ 610 | { 611 | "data": { 612 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 613 | "text/html": [ 614 | "
\n", 615 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 616 | " jupyter labextension install jupyterlab_3dmol

\n", 617 | "
\n", 618 | "" 652 | ] 653 | }, 654 | "metadata": {}, 655 | "output_type": "display_data" 656 | }, 657 | { 658 | "data": { 659 | "text/plain": [ 660 | "" 661 | ] 662 | }, 663 | "execution_count": 11, 664 | "metadata": {}, 665 | "output_type": "execute_result" 666 | } 667 | ], 668 | "source": [ 669 | "ch2_copy.translate([0.05, 0, 0])\n", 670 | "parent.visualize(show_ports=True)" 671 | ] 672 | }, 673 | { 674 | "cell_type": "markdown", 675 | "metadata": {}, 676 | "source": [ 677 | "## Recap\n", 678 | "\n", 679 | "The goal of this tutorial was to demonstrate the use of mBuild's `Port` class to aid in connecting particles and molecules as well as moving them around in space. We also explored several additional methods of `Compound` including `translate`, `available_ports`, and `remove_bond`.\n", 680 | "\n", 681 | "The next tutorial will take the routines we've learned and use them to create a more complex molecule, an alkane chain." 682 | ] 683 | } 684 | ], 685 | "metadata": { 686 | "kernelspec": { 687 | "display_name": "Python 3", 688 | "language": "python", 689 | "name": "python3" 690 | }, 691 | "language_info": { 692 | "codemirror_mode": { 693 | "name": "ipython", 694 | "version": 3 695 | }, 696 | "file_extension": ".py", 697 | "mimetype": "text/x-python", 698 | "name": "python", 699 | "nbconvert_exporter": "python", 700 | "pygments_lexer": "ipython3", 701 | "version": "3.8.10" 702 | } 703 | }, 704 | "nbformat": 4, 705 | "nbformat_minor": 2 706 | } 707 | -------------------------------------------------------------------------------- /mBuild_04_Constructing_Larger_Compounds.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "# mBuild Tutorial 04: Constructing Larger Compounds\n", 10 | "\n", 11 | "This tutorial demonstrates how to create larger `Compounds` by taking advantage of the `Port` class we learned about in the previous tutorial. In particular, a linear alkane will be constructed by adding to the CH2 moiety class defined in a prior tutorial." 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "As in the prior tutorials, we need to first import mbuild (here as `mb`)." 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 1, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "import warnings\n", 28 | "warnings.filterwarnings('ignore')\n", 29 | "\n", 30 | "import mbuild as mb" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "In general, connecting atoms together using `Ports` to create small moieties is unnecessary as these can be loaded from structure files; however, this functionality is important for creating larger and more complex `Compounds`. Here, we will extend the CH2 class constructed in a prior tutorial, adding two `Ports` (labeled `up` and `down`) to the central carbon atom." 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": 2, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "class CH2(mb.Compound):\n", 47 | " def __init__(self):\n", 48 | " super(CH2, self).__init__()\n", 49 | " \n", 50 | " mb.load('utils/ch2.pdb', compound=self)\n", 51 | " carbon = list(self.particles_by_name('C'))[0]\n", 52 | " up_port = mb.Port(anchor=carbon, orientation=[0, 0, 1], separation=0.075)\n", 53 | " down_port = mb.Port(anchor=carbon, orientation=[0, 0, -1], separation=0.075)\n", 54 | " self.add(up_port, label='up')\n", 55 | " self.add(down_port, label='down')" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "Now we'll explore how `Ports` can be used to connect `Compounds` by connecting two CH2 groups to create a C2H4 group. We'll first use mBuild's `clone` function to create two deep copies of our CH2 `Compound`, similar to to what was done in the previous tutorial. \n", 63 | "\n", 64 | "We can use the `translate` function to move one copy so that they are not on top of one another. \n", 65 | "\n", 66 | "Note, in order to visualize the two compounds simultaneously, we'll again add these to a parent `Compound`. " 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": 3, 72 | "metadata": {}, 73 | "outputs": [ 74 | { 75 | "data": { 76 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 77 | "text/html": [ 78 | "
\n", 79 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 80 | " jupyter labextension install jupyterlab_3dmol

\n", 81 | "
\n", 82 | "" 116 | ] 117 | }, 118 | "metadata": {}, 119 | "output_type": "display_data" 120 | }, 121 | { 122 | "data": { 123 | "text/plain": [ 124 | "" 125 | ] 126 | }, 127 | "execution_count": 3, 128 | "metadata": {}, 129 | "output_type": "execute_result" 130 | } 131 | ], 132 | "source": [ 133 | "first_ch2 = CH2()\n", 134 | "second_ch2 = CH2()\n", 135 | "second_ch2.translate([1, 1, 1])\n", 136 | "parent = mb.Compound()\n", 137 | "parent.add((first_ch2, second_ch2))\n", 138 | "parent.visualize(show_ports=True)" 139 | ] 140 | }, 141 | { 142 | "cell_type": "markdown", 143 | "metadata": {}, 144 | "source": [ 145 | "These two CH2 `Compounds` can be trivially connected using the `force_overlap` command, just as was done when connecting the carbon and hydrogen atoms in the previous tutorial; mBuild is designed to be general and flexible and does not distinguish between a `Compound` that describes a single atom or a collection of atoms. " 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": 4, 151 | "metadata": {}, 152 | "outputs": [ 153 | { 154 | "data": { 155 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 156 | "text/html": [ 157 | "
\n", 158 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 159 | " jupyter labextension install jupyterlab_3dmol

\n", 160 | "
\n", 161 | "" 195 | ] 196 | }, 197 | "metadata": {}, 198 | "output_type": "display_data" 199 | }, 200 | { 201 | "data": { 202 | "text/plain": [ 203 | "" 204 | ] 205 | }, 206 | "execution_count": 4, 207 | "metadata": {}, 208 | "output_type": "execute_result" 209 | } 210 | ], 211 | "source": [ 212 | "mb.force_overlap(move_this=first_ch2,\n", 213 | " from_positions=first_ch2['up'],\n", 214 | " to_positions=second_ch2['down'])\n", 215 | "\n", 216 | "parent.visualize(show_ports=True)" 217 | ] 218 | }, 219 | { 220 | "cell_type": "markdown", 221 | "metadata": {}, 222 | "source": [ 223 | "### Building a linear alkane\n", 224 | "\n", 225 | "Now that we've explored the basics of creating mBuild `Compounds` and connecting them together using ports, we'll use this approach to create a slightly more complex molecule, a linear butane.\n", 226 | "\n", 227 | "We could approach our butane construction by connecting two CH2 moieties and two CH3 moieties. Alternatively, we could connect four CH2 moieties and cap the ends of the chain with hydrogen atoms. \n", 228 | "\n", 229 | "In this example, we will take the latter approach. As such, we'll need to also define a class for a hydrogen atom featuring a single `Port`." 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": 5, 235 | "metadata": {}, 236 | "outputs": [], 237 | "source": [ 238 | "class Hydrogen(mb.Compound):\n", 239 | " def __init__(self):\n", 240 | " super(Hydrogen, self).__init__()\n", 241 | " \n", 242 | " self.add(mb.Particle(name='H'))\n", 243 | " up_port = mb.Port(anchor=self[0], orientation=[0, 0, 1], separation=0.05)\n", 244 | " self.add(up_port, 'up')" 245 | ] 246 | }, 247 | { 248 | "cell_type": "markdown", 249 | "metadata": {}, 250 | "source": [ 251 | "We now have all of the pieces necessary to create a butane molecule. To begin, we'll instantiate an empty mBuild `Compound` to add our pieces to." 252 | ] 253 | }, 254 | { 255 | "cell_type": "code", 256 | "execution_count": 6, 257 | "metadata": {}, 258 | "outputs": [], 259 | "source": [ 260 | "butane = mb.Compound()" 261 | ] 262 | }, 263 | { 264 | "cell_type": "markdown", 265 | "metadata": {}, 266 | "source": [ 267 | "Now, we'll create one of our CH3 ends by connecting a CH2 group and a hydrogen atom. We'll then add these two `Compounds` to our butane, giving them each a label. Note that by providing `ch2[$]` as the label for our CH2 group, mBuild will create a list that any subsequent parts added to the `Compound` with the same label prefix will be appended to." 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 7, 273 | "metadata": {}, 274 | "outputs": [ 275 | { 276 | "data": { 277 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 278 | "text/html": [ 279 | "
\n", 280 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 281 | " jupyter labextension install jupyterlab_3dmol

\n", 282 | "
\n", 283 | "" 317 | ] 318 | }, 319 | "metadata": {}, 320 | "output_type": "display_data" 321 | }, 322 | { 323 | "data": { 324 | "text/plain": [ 325 | "" 326 | ] 327 | }, 328 | "execution_count": 7, 329 | "metadata": {}, 330 | "output_type": "execute_result" 331 | } 332 | ], 333 | "source": [ 334 | "hydrogen = Hydrogen()\n", 335 | "last_unit = CH2()\n", 336 | "mb.force_overlap(move_this=hydrogen,\n", 337 | " from_positions=hydrogen['up'],\n", 338 | " to_positions=last_unit['up'])\n", 339 | "butane.add(last_unit, label='ch2[$]')\n", 340 | "butane.add(hydrogen, label='up-cap')\n", 341 | "butane.visualize(show_ports=True)" 342 | ] 343 | }, 344 | { 345 | "cell_type": "markdown", 346 | "metadata": {}, 347 | "source": [ 348 | "To continue creating our butane molecule, we'll next attach three CH2 groups to the CH3 cap we've just created. This can be set up in a loop, where we'll use `force_overlap` to iteratively attach each new CH2 instantiation to the last unit on the chain." 349 | ] 350 | }, 351 | { 352 | "cell_type": "code", 353 | "execution_count": 8, 354 | "metadata": {}, 355 | "outputs": [ 356 | { 357 | "data": { 358 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 359 | "text/html": [ 360 | "
\n", 361 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 362 | " jupyter labextension install jupyterlab_3dmol

\n", 363 | "
\n", 364 | "" 398 | ] 399 | }, 400 | "metadata": {}, 401 | "output_type": "display_data" 402 | }, 403 | { 404 | "data": { 405 | "text/plain": [ 406 | "" 407 | ] 408 | }, 409 | "execution_count": 8, 410 | "metadata": {}, 411 | "output_type": "execute_result" 412 | } 413 | ], 414 | "source": [ 415 | "for _ in range(3):\n", 416 | " current_unit = CH2()\n", 417 | " mb.force_overlap(move_this=current_unit,\n", 418 | " from_positions=current_unit['up'],\n", 419 | " to_positions=last_unit['down'])\n", 420 | " butane.add(current_unit, label='ch2[$]')\n", 421 | " last_unit=current_unit\n", 422 | "\n", 423 | "butane.visualize(show_ports=True)" 424 | ] 425 | }, 426 | { 427 | "cell_type": "markdown", 428 | "metadata": {}, 429 | "source": [ 430 | "Finally, we need to cap the end of our molecule with a hydrogen atom to complete the creation of butane." 431 | ] 432 | }, 433 | { 434 | "cell_type": "code", 435 | "execution_count": 9, 436 | "metadata": {}, 437 | "outputs": [ 438 | { 439 | "data": { 440 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 441 | "text/html": [ 442 | "
\n", 443 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 444 | " jupyter labextension install jupyterlab_3dmol

\n", 445 | "
\n", 446 | "" 480 | ] 481 | }, 482 | "metadata": {}, 483 | "output_type": "display_data" 484 | }, 485 | { 486 | "data": { 487 | "text/plain": [ 488 | "" 489 | ] 490 | }, 491 | "execution_count": 9, 492 | "metadata": {}, 493 | "output_type": "execute_result" 494 | } 495 | ], 496 | "source": [ 497 | "hydrogen2 = Hydrogen()\n", 498 | "mb.force_overlap(move_this=hydrogen2,\n", 499 | " from_positions=hydrogen2['up'],\n", 500 | " to_positions=last_unit['down'])\n", 501 | "butane.add(hydrogen2, label='down-cap')\n", 502 | "butane.visualize(show_ports=True)" 503 | ] 504 | }, 505 | { 506 | "cell_type": "markdown", 507 | "metadata": {}, 508 | "source": [ 509 | "As shown previously, we can also wrap all of these commands into a reusable class." 510 | ] 511 | }, 512 | { 513 | "cell_type": "code", 514 | "execution_count": 10, 515 | "metadata": {}, 516 | "outputs": [], 517 | "source": [ 518 | "class Butane(mb.Compound):\n", 519 | " def __init__(self):\n", 520 | " super(Butane, self).__init__()\n", 521 | " \n", 522 | " hydrogen = Hydrogen()\n", 523 | " last_unit = CH2()\n", 524 | " mb.force_overlap(move_this=hydrogen,\n", 525 | " from_positions=hydrogen['up'],\n", 526 | " to_positions=last_unit['up'])\n", 527 | " self.add(last_unit, label='ch2[$]')\n", 528 | " self.add(hydrogen, label='up-cap')\n", 529 | " for _ in range(3):\n", 530 | " current_unit = CH2()\n", 531 | " mb.force_overlap(move_this=current_unit,\n", 532 | " from_positions=current_unit['up'],\n", 533 | " to_positions=last_unit['down'])\n", 534 | " self.add(current_unit, label='ch2[$]')\n", 535 | " last_unit=current_unit\n", 536 | " hydrogen = Hydrogen()\n", 537 | " mb.force_overlap(move_this=hydrogen,\n", 538 | " from_positions=hydrogen['up'],\n", 539 | " to_positions=last_unit['down'])\n", 540 | " self.add(hydrogen, label='down-cap')" 541 | ] 542 | }, 543 | { 544 | "cell_type": "code", 545 | "execution_count": 11, 546 | "metadata": {}, 547 | "outputs": [ 548 | { 549 | "data": { 550 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 551 | "text/html": [ 552 | "
\n", 553 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 554 | " jupyter labextension install jupyterlab_3dmol

\n", 555 | "
\n", 556 | "" 590 | ] 591 | }, 592 | "metadata": {}, 593 | "output_type": "display_data" 594 | }, 595 | { 596 | "data": { 597 | "text/plain": [ 598 | "" 599 | ] 600 | }, 601 | "execution_count": 11, 602 | "metadata": {}, 603 | "output_type": "execute_result" 604 | } 605 | ], 606 | "source": [ 607 | "butane = Butane()\n", 608 | "butane.visualize()" 609 | ] 610 | }, 611 | { 612 | "cell_type": "markdown", 613 | "metadata": { 614 | "collapsed": true 615 | }, 616 | "source": [ 617 | "## Recap\n", 618 | "\n", 619 | "The goal of this tutorial was to provide an example of how `Ports` along with `force_overlap` can be used to connect molecules in addition to atoms (as these are both instances of `Compound`). We also learned a useful trick where `force_overlap` can be used within a `for` loop to iteratively connect molecule copies.\n", 620 | "\n", 621 | "The next tutorial will teach you how to create mBuild `Compound` classes with arguments that allow you to tune the molecular chemistry upon instantiation." 622 | ] 623 | } 624 | ], 625 | "metadata": { 626 | "kernelspec": { 627 | "display_name": "Python 3", 628 | "language": "python", 629 | "name": "python3" 630 | }, 631 | "language_info": { 632 | "codemirror_mode": { 633 | "name": "ipython", 634 | "version": 3 635 | }, 636 | "file_extension": ".py", 637 | "mimetype": "text/x-python", 638 | "name": "python", 639 | "nbconvert_exporter": "python", 640 | "pygments_lexer": "ipython3", 641 | "version": "3.8.10" 642 | } 643 | }, 644 | "nbformat": 4, 645 | "nbformat_minor": 2 646 | } 647 | -------------------------------------------------------------------------------- /mBuild_05_Creating_Flexible_Classes.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "# mBuild Tutorial 05: Creating Flexible Classes\n", 10 | "\n", 11 | "This tutorial demonstrates how to write flexible classes that can be used to create families of `Compounds`. In particular, the previous class we wrote to create butane is extended to allow construction of linear alkanes of arbitrary chain length." 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "As in the prior tutorials, we need to first import mbuild (here as `mb`).\n", 19 | "\n", 20 | "We also introduce a filter for somewarnings to provide cleaner output." 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 1, 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "import warnings\n", 30 | "warnings.filterwarnings('ignore')\n", 31 | "\n", 32 | "import mbuild as mb" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "In the previous few tutorials we found that we could create reusable `Compounds` by wrapping routines for creating particles and bonds into a class. However, if we have to create a new class for each molecule we want to examine this would still be quite cumbersome, particularly if we desired to screen over a large chemical parameter space. Fortunately, this problem is easily solved by including additional arguments with the class constructor. In this way, one or more top-level variables describing the molecular chemistry can be used to create a whole family of molecules. \n", 40 | "\n", 41 | "We'll demonstrate that here by modifying the Butane class, defined in the previous tutorial, to now allow for the creation of any linear alkane by adding a `chain_length` argument.\n", 42 | "\n", 43 | "Note, rather than defining the CH2 and H classes again, we will use the classes for these that are included in the mBuild library." 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 2, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "from mbuild.lib.atoms import H\n", 53 | "from mbuild.lib.moieties import CH2\n", 54 | "\n", 55 | "class Alkane(mb.Compound):\n", 56 | " def __init__(self, chain_length):\n", 57 | " super(Alkane, self).__init__()\n", 58 | " \n", 59 | " hydrogen = H()\n", 60 | " last_unit = CH2()\n", 61 | " mb.force_overlap(move_this=hydrogen,\n", 62 | " from_positions=hydrogen['up'],\n", 63 | " to_positions=last_unit['up'])\n", 64 | " self.add(last_unit, label='ch2[$]')\n", 65 | " self.add(hydrogen, label='up-cap')\n", 66 | " for _ in range(chain_length - 1):\n", 67 | " current_unit = CH2()\n", 68 | " mb.force_overlap(move_this=current_unit,\n", 69 | " from_positions=current_unit['up'],\n", 70 | " to_positions=last_unit['down'])\n", 71 | " self.add(current_unit, label='ch2[$]')\n", 72 | " last_unit=current_unit\n", 73 | " hydrogen = H()\n", 74 | " mb.force_overlap(move_this=hydrogen,\n", 75 | " from_positions=hydrogen['up'],\n", 76 | " to_positions=last_unit['down'])\n", 77 | " self.add(hydrogen, label='down-cap')" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "We can now create any linear alkane by simply providing a different value for `chain_length` upon instantiation." 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 3, 90 | "metadata": {}, 91 | "outputs": [ 92 | { 93 | "data": { 94 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 95 | "text/html": [ 96 | "
\n", 97 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 98 | " jupyter labextension install jupyterlab_3dmol

\n", 99 | "
\n", 100 | "" 134 | ] 135 | }, 136 | "metadata": {}, 137 | "output_type": "display_data" 138 | }, 139 | { 140 | "data": { 141 | "text/plain": [ 142 | "" 143 | ] 144 | }, 145 | "execution_count": 3, 146 | "metadata": {}, 147 | "output_type": "execute_result" 148 | } 149 | ], 150 | "source": [ 151 | "ethane = Alkane(chain_length=2)\n", 152 | "ethane.visualize()" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 4, 158 | "metadata": {}, 159 | "outputs": [ 160 | { 161 | "data": { 162 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 163 | "text/html": [ 164 | "
\n", 165 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 166 | " jupyter labextension install jupyterlab_3dmol

\n", 167 | "
\n", 168 | "" 202 | ] 203 | }, 204 | "metadata": {}, 205 | "output_type": "display_data" 206 | }, 207 | { 208 | "data": { 209 | "text/plain": [ 210 | "" 211 | ] 212 | }, 213 | "execution_count": 4, 214 | "metadata": {}, 215 | "output_type": "execute_result" 216 | } 217 | ], 218 | "source": [ 219 | "hexane = Alkane(chain_length=6)\n", 220 | "hexane.visualize()" 221 | ] 222 | }, 223 | { 224 | "cell_type": "markdown", 225 | "metadata": {}, 226 | "source": [ 227 | "## Creation of an imidazole with an arbitrary side chain length" 228 | ] 229 | }, 230 | { 231 | "cell_type": "markdown", 232 | "metadata": { 233 | "collapsed": true 234 | }, 235 | "source": [ 236 | "The linear alkane class above can be trivially modified to create more complex molecules, e.g. by changing the chemistry of one or both of the capping groups. Here, we will replace one of the hydrogen caps with an imidazole ring. \n", 237 | "\n", 238 | "Let us first start by creating an `Imidazole` class, loading in the structure from a MOL2 file, and adding a `Port` to the appropriate location for the tail." 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": 5, 244 | "metadata": {}, 245 | "outputs": [], 246 | "source": [ 247 | "class Imidazole(mb.Compound):\n", 248 | " def __init__(self):\n", 249 | " super(Imidazole, self).__init__()\n", 250 | " mb.load('utils/cmim.mol2', compound=self)\n", 251 | " #define C_tail to be the N atom for which the carbon tail will attach. \n", 252 | " #This is the 4th entry in the datafile provided, hence self[3]\n", 253 | " C_tail = self[3] \n", 254 | " #add a port\n", 255 | " self.add(mb.Port(anchor=C_tail, orientation=[0, 1, 0], separation=0.04), 'up')" 256 | ] 257 | }, 258 | { 259 | "cell_type": "markdown", 260 | "metadata": {}, 261 | "source": [ 262 | "Next we will create a CMIM class that largely mimics the alkane class, but changes one capping hydrogen for an Imidazole. " 263 | ] 264 | }, 265 | { 266 | "cell_type": "code", 267 | "execution_count": 6, 268 | "metadata": {}, 269 | "outputs": [], 270 | "source": [ 271 | "from mbuild.lib.atoms import H\n", 272 | "from mbuild.lib.moieties import CH2\n", 273 | "\n", 274 | "class CMIM(mb.Compound):\n", 275 | " def __init__(self, chain_length):\n", 276 | " super(CMIM, self).__init__()\n", 277 | " \n", 278 | " hydrogen = H()\n", 279 | " last_unit = CH2()\n", 280 | " mb.force_overlap(move_this=hydrogen,\n", 281 | " from_positions=hydrogen['up'],\n", 282 | " to_positions=last_unit['up'])\n", 283 | " self.add(last_unit, label='ch2[$]')\n", 284 | " self.add(hydrogen, label='up-cap')\n", 285 | " for _ in range(chain_length - 1):\n", 286 | " current_unit = CH2()\n", 287 | " mb.force_overlap(move_this=current_unit,\n", 288 | " from_positions=current_unit['up'],\n", 289 | " to_positions=last_unit['down'])\n", 290 | " self.add(current_unit, label='ch2[$]')\n", 291 | " last_unit=current_unit\n", 292 | " imidazole = Imidazole()\n", 293 | " mb.force_overlap(move_this=imidazole,\n", 294 | " from_positions=imidazole['up'],\n", 295 | " to_positions=last_unit['down'])\n", 296 | " self.add(imidazole, label='down-cap')" 297 | ] 298 | }, 299 | { 300 | "cell_type": "markdown", 301 | "metadata": {}, 302 | "source": [ 303 | "Here, we can use this to create 1-Butyl-3-methylimidazolium (bmim), by passing a side chain length of 4." 304 | ] 305 | }, 306 | { 307 | "cell_type": "code", 308 | "execution_count": 7, 309 | "metadata": {}, 310 | "outputs": [ 311 | { 312 | "data": { 313 | "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", 314 | "text/html": [ 315 | "
\n", 316 | "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", 317 | " jupyter labextension install jupyterlab_3dmol

\n", 318 | "
\n", 319 | "" 353 | ] 354 | }, 355 | "metadata": {}, 356 | "output_type": "display_data" 357 | }, 358 | { 359 | "data": { 360 | "text/plain": [ 361 | "" 362 | ] 363 | }, 364 | "execution_count": 7, 365 | "metadata": {}, 366 | "output_type": "execute_result" 367 | } 368 | ], 369 | "source": [ 370 | "bmim = CMIM(chain_length=4)\n", 371 | "bmim.visualize()" 372 | ] 373 | }, 374 | { 375 | "cell_type": "markdown", 376 | "metadata": { 377 | "collapsed": true 378 | }, 379 | "source": [ 380 | "## Recap\n", 381 | "\n", 382 | "The goal of this tutorial was to demonstrate how to create `Compound` classes with arguments that can be used to tune the molecular chemistry.\n", 383 | "\n", 384 | "The next tutorial will teach you how to create systems of bulk molecules." 385 | ] 386 | } 387 | ], 388 | "metadata": { 389 | "kernelspec": { 390 | "display_name": "Python 3", 391 | "language": "python", 392 | "name": "python3" 393 | }, 394 | "language_info": { 395 | "codemirror_mode": { 396 | "name": "ipython", 397 | "version": 3 398 | }, 399 | "file_extension": ".py", 400 | "mimetype": "text/x-python", 401 | "name": "python", 402 | "nbconvert_exporter": "python", 403 | "pygments_lexer": "ipython3", 404 | "version": "3.8.10" 405 | } 406 | }, 407 | "nbformat": 4, 408 | "nbformat_minor": 2 409 | } 410 | -------------------------------------------------------------------------------- /mBuild_09_Surface_Functionalization.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "# mBuild Tutorial 09: Surface Functionalization\n", 10 | "\n", 11 | "This tutorial demonstrates the use of the surface functionalization routines included in mBuild. " 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "As in the prior tutorials, we need to first import mbuild (here as `mb`).\n", 19 | "\n", 20 | "We also introduce a filter for some (harmless) warnings to provide cleaner output." 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "import warnings\n", 30 | "warnings.simplefilter(action='ignore', category=FutureWarning)\n", 31 | "\n", 32 | "import mbuild as mb" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "metadata": {}, 38 | "source": [ 39 | "Here, we will examine the functionalization of surfaces with alkane chains. Since we will make use of `Polymer` class, we'll need to first define the CH2 moiety, as before." 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "class CH2(mb.Compound):\n", 49 | " def __init__(self):\n", 50 | " super(CH2, self).__init__()\n", 51 | " \n", 52 | " mb.load('utils/ch2.pdb', compound=self)\n", 53 | " carbon = list(self.particles_by_name('C'))[0]\n", 54 | " up_port = mb.Port(anchor=carbon, orientation=[0, 0, 1], separation=0.075)\n", 55 | " down_port = mb.Port(anchor=carbon, orientation=[0, 0, -1], separation=0.075)\n", 56 | " self.add(up_port, label='up')\n", 57 | " self.add(down_port, label='down')" 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "metadata": {}, 63 | "source": [ 64 | "mBuild features several functions to aid in the functionalization of surfaces. For example, the `Pattern.apply_to_compound` method allows one to connect copies of a 'guest' `Compound` to `Ports` located on a 'host' `Compound`. We'll explore how this can be useful for surface functionalization by considering a crystalline silica surface (featuring many `Ports`) as our host and a polymer chain as our guest.\n", 65 | "\n", 66 | "First we'll import our crystalline silica surface from mBuild's `surfaces` library. Note, this structure has surface binding sites already identified, with `Ports` placed at these locations. In the future we hope to add routines to mBuild to automatically detect surface sites and add `Ports` to them." 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": null, 72 | "metadata": {}, 73 | "outputs": [], 74 | "source": [ 75 | "from mbuild.lib.surfaces import Betacristobalite\n", 76 | "surface = Betacristobalite()\n", 77 | "surface.visualize(show_ports=True)" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "mBuild also features a recipe that creates a silica interface by carving from bulk amorphous silica." 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": null, 90 | "metadata": {}, 91 | "outputs": [], 92 | "source": [ 93 | "from mbuild.lib.bulk_materials import AmorphousSilicaBulk\n", 94 | "silica_interface = mb.recipes.SilicaInterface(bulk_silica=AmorphousSilicaBulk(), thickness=1.2)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "Lets visualize" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": {}, 108 | "outputs": [], 109 | "source": [ 110 | "silica_interface.visualize(show_ports=True)" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "Now, we will create the monolayer surface, via the following steps:\n", 118 | "- create prototypes for two polymer chains of different lengths\n", 119 | "- specify a random pattern of 30 points in 2D space\n", 120 | "- use `apply_to_compound` to stick copies of the first polymer on the surface, backfilling unused `Ports` with the shorter polymer \n", 121 | "\n", 122 | "In the mBuild nomenclature, `guests` are the `Compound` copies that have been added to the surface and `backfills` are an optional second `Compound` type that can be used to fill any leftover `Ports` in the host `Compound` after all points in the `Pattern` have been satisfied.\n", 123 | "\n", 124 | "Note, these chains are uncapped, but could be easily capped following the same procedures in the prior tutorials. " 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "metadata": {}, 131 | "outputs": [], 132 | "source": [ 133 | "# NBVAL_SKIP\n", 134 | "surface = silica_interface\n", 135 | "\n", 136 | "alkane_12 = mb.recipes.Polymer(monomers=CH2(), n=12, port_labels=('up', 'down'))\n", 137 | "alkane_3 = mb.recipes.Polymer(monomers=CH2(), n=3, port_labels=('up', 'down'))\n", 138 | "pattern = mb.Grid2DPattern(5, 5)\n", 139 | "guests, backfills = pattern.apply_to_compound(guest=alkane_12, host=surface, backfill=alkane_3, backfill_port_name='down')\n", 140 | "functionalized_surface = mb.Compound(subcompounds=[surface, guests, backfills])\n", 141 | "functionalized_surface.visualize()" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": {}, 147 | "source": [ 148 | "As we've seen, the `Pattern.apply_to_compound` method is a useful way to approach surface functionalization with mBuild. However, this can be done even easier by using `mbuild.Monolayer`, where the above steps have been wrapped into a class. Multi-component monolayers can be generated by simply passing a list of `Compounds` to the `chains` argument also with the `fractions` of each component." 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": null, 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | "# NBVAL_SKIP\n", 158 | "surface = mb.recipes.SilicaInterface(bulk_silica=AmorphousSilicaBulk())\n", 159 | "alkane_18 = mb.recipes.Polymer(monomers=CH2(), n=18, port_labels=('up', 'down'))\n", 160 | "alkane_12 = mb.recipes.Polymer(monomers=CH2(), n=12, port_labels=('up', 'down'))\n", 161 | "alkane_3 = mb.recipes.Polymer(monomers=CH2(), n=3, port_labels=('up', 'down'))\n", 162 | "\n", 163 | "monolayer = mb.recipes.Monolayer(surface=surface, chains=(alkane_3, alkane_12, alkane_18), fractions=(0.5, 0.4, 0.1))\n", 164 | "monolayer.visualize()" 165 | ] 166 | }, 167 | { 168 | "cell_type": "markdown", 169 | "metadata": {}, 170 | "source": [ 171 | "So far, the examples we have looked at have used surfaces already contained within mBuild that have `Ports` identified. However, while the surfaces available within mBuild are currently limited to crystalline and amorphous silica, additional surfaces can easily be created. For example, consider this example, where mBuild's `Lattice` class is utilized to generate a gold surface, which we will then functionalize with alkanethiols.\n", 172 | "\n", 173 | "First, we'll use `mb.Lattice` to create an face-centered cubic gold surface." 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": null, 179 | "metadata": {}, 180 | "outputs": [], 181 | "source": [ 182 | "gold = mb.Compound(name='Au')\n", 183 | "lattice_vectors = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]\n", 184 | "spacing = [.40782, .40782, .40782]\n", 185 | "locations = [[0., 0., 0.], [.5, .5, 0.],\n", 186 | " [.5, 0., .5], [0., .5, .5]]\n", 187 | "basis = {'Au': locations}\n", 188 | "lattice = mb.Lattice(lattice_spacing=spacing,\n", 189 | " lattice_vectors=lattice_vectors,\n", 190 | " lattice_points=basis)\n", 191 | "fcc_gold = lattice.populate(x=6, y=6, z=2,\n", 192 | " compound_dict={'Au': gold})\n", 193 | "fcc_gold.visualize()" 194 | ] 195 | }, 196 | { 197 | "cell_type": "markdown", 198 | "metadata": {}, 199 | "source": [ 200 | "Now, we'll identify the atoms at the surface and attach ports to them. To do this, we'll simply consider all atoms within 0.05nm of the atom with the largest *z* coordinate to be at the surface." 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": null, 206 | "metadata": {}, 207 | "outputs": [], 208 | "source": [ 209 | "gold_with_ports = mb.Compound()\n", 210 | "gold_with_ports.add(fcc_gold)\n", 211 | "\n", 212 | "buffer = 0.05\n", 213 | "surface_height = max(fcc_gold.xyz[:,2])\n", 214 | "surface_atoms = [atom for atom in fcc_gold if atom.pos[2] >= surface_height - buffer]\n", 215 | "for atom in surface_atoms:\n", 216 | " port = mb.Port(anchor=atom, orientation=[0, 0, 1], separation=0.12)\n", 217 | " gold_with_ports.add(port)\n", 218 | "\n", 219 | "gold_with_ports.visualize(show_ports=True)" 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "We'll now define a class for an alkanethiol chain. For this we will use the `Polymer` class from mBuild as well as define a class for a sulfur headgroup." 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": null, 232 | "metadata": {}, 233 | "outputs": [], 234 | "source": [ 235 | "# NBVAL_SKIP\n", 236 | "class S(mb.Compound):\n", 237 | " def __init__(self):\n", 238 | " super(S, self).__init__()\n", 239 | " \n", 240 | " self.add(mb.Particle(name='S'))\n", 241 | " up_port = mb.Port(anchor=self[0], orientation=[0, 0, 1], separation=0.1)\n", 242 | " self.add(up_port, 'up')\n", 243 | " down_port = mb.Port(anchor=self[0], orientation=[0, 0, -1], separation=0.1)\n", 244 | " self.add(down_port, 'down')\n", 245 | "\n", 246 | "class Alkanethiol(mb.Compound):\n", 247 | " def __init__(self, n):\n", 248 | " super(Alkanethiol, self).__init__()\n", 249 | " \n", 250 | " self.add(S(), 'headgroup')\n", 251 | " chain = mb.recipes.Alkane(n, cap_end=False)\n", 252 | " mb.force_overlap(move_this=chain,\n", 253 | " from_positions=chain['down'],\n", 254 | " to_positions=self['headgroup']['up'])\n", 255 | " self.add(chain)\n", 256 | " self.add(self['headgroup']['down'], 'down', containment=False)" 257 | ] 258 | }, 259 | { 260 | "cell_type": "markdown", 261 | "metadata": {}, 262 | "source": [ 263 | "We can now create a butanethiol by instantiating the `Alkanethiol` class with a chain length of 4." 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": null, 269 | "metadata": { 270 | "scrolled": true 271 | }, 272 | "outputs": [], 273 | "source": [ 274 | "# NBVAL_SKIP\n", 275 | "butanethiol = Alkanethiol(n=4)\n", 276 | "butanethiol.visualize(show_ports=True)" 277 | ] 278 | }, 279 | { 280 | "cell_type": "markdown", 281 | "metadata": {}, 282 | "source": [ 283 | "To attach copies of the butanethiol to the surface, we simply provide a call to `mb.Monolayer`." 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": null, 289 | "metadata": { 290 | "scrolled": false 291 | }, 292 | "outputs": [], 293 | "source": [ 294 | "# NBVAL_SKIP\n", 295 | "monolayer = mb.recipes.Monolayer(surface=gold_with_ports, chains=butanethiol)\n", 296 | "monolayer.visualize()" 297 | ] 298 | }, 299 | { 300 | "cell_type": "markdown", 301 | "metadata": { 302 | "collapsed": true 303 | }, 304 | "source": [ 305 | "## Recap\n", 306 | "\n", 307 | "The goal of this tutorial was to demonstrate how to functionalize surfaces using mBuild." 308 | ] 309 | } 310 | ], 311 | "metadata": { 312 | "kernelspec": { 313 | "display_name": "Python 3", 314 | "language": "python", 315 | "name": "python3" 316 | }, 317 | "language_info": { 318 | "codemirror_mode": { 319 | "name": "ipython", 320 | "version": 3 321 | }, 322 | "file_extension": ".py", 323 | "mimetype": "text/x-python", 324 | "name": "python", 325 | "nbconvert_exporter": "python", 326 | "pygments_lexer": "ipython3", 327 | "version": "3.8.10" 328 | } 329 | }, 330 | "nbformat": 4, 331 | "nbformat_minor": 2 332 | } 333 | -------------------------------------------------------------------------------- /mBuild_10_Constructing_Lattices.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [] 9 | } 10 | ], 11 | "metadata": { 12 | "kernelspec": { 13 | "display_name": "Python 3", 14 | "language": "python", 15 | "name": "python3" 16 | }, 17 | "language_info": { 18 | "codemirror_mode": { 19 | "name": "ipython", 20 | "version": 3 21 | }, 22 | "file_extension": ".py", 23 | "mimetype": "text/x-python", 24 | "name": "python", 25 | "nbconvert_exporter": "python", 26 | "pygments_lexer": "ipython3", 27 | "version": "3.7.6" 28 | } 29 | }, 30 | "nbformat": 4, 31 | "nbformat_minor": 2 32 | } 33 | -------------------------------------------------------------------------------- /nbval_sanitize.cfg: -------------------------------------------------------------------------------- 1 | [regex1] 2 | regex: \d{1,2}/\d{1,2}/\d{2,4} 3 | replace: DATE-STAMP 4 | 5 | [regex2] 6 | regex: \d{2}:\d{2}:\d{2} 7 | replace: TIME-STAMP 8 | -------------------------------------------------------------------------------- /postBuild: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir src 4 | cd src 5 | 6 | git clone https://github.com/mosdef-hub/mbuild.git 7 | cd mbuild 8 | pip install . 9 | cd .. 10 | 11 | git clone https://github.com/mosdef-hub/foyer.git 12 | cd foyer 13 | pip install . 14 | cd .. 15 | -------------------------------------------------------------------------------- /utils/cf2.pdb: -------------------------------------------------------------------------------- 1 | COMPND UNNAMED 2 | AUTHOR GENERATED BY OPEN BABEL 3 | ATOM 1 C LIG A 1 0.000 0.000 0.000 1.00 0.00 C 4 | ATOM 2 F LIG A 1 -1.400 0.000 0.000 1.00 0.00 F 5 | ATOM 3 F LIG A 1 1.400 0.000 0.000 1.00 0.00 F 6 | CONECT 1 2 3 7 | CONECT 2 1 8 | CONECT 3 1 9 | MASTER 0 0 0 0 0 0 0 0 3 0 3 0 10 | END 11 | -------------------------------------------------------------------------------- /utils/ch2.pdb: -------------------------------------------------------------------------------- 1 | COMPND UNNAMED 2 | AUTHOR GENERATED BY OPEN BABEL 3 | ATOM 1 C LIG A 1 0.000 0.000 0.000 1.00 0.00 C 4 | ATOM 2 H LIG A 1 -1.100 0.000 0.000 1.00 0.00 H 5 | ATOM 3 H LIG A 1 1.100 0.000 0.000 1.00 0.00 H 6 | CONECT 1 2 3 7 | CONECT 2 1 8 | CONECT 3 1 9 | MASTER 0 0 0 0 0 0 0 0 3 0 3 0 10 | END 11 | -------------------------------------------------------------------------------- /utils/cmim.mol2: -------------------------------------------------------------------------------- 1 | @MOLECULE 2 | ***** 3 | 12 12 0 0 0 4 | SMALL 5 | GASTEIGER 6 | 7 | @ATOM 8 | 1 C -0.0887 0.1082 0.0960 C.ar 1 LIG1 0.0454 9 | 2 C 0.3738 -1.2636 -0.3224 C.ar 1 LIG1 0.0237 10 | 3 N 1.6886 -1.3293 0.4360 N.ar 1 LIG1 -0.3399 11 | 4 N 1.1955 0.9102 -0.0683 N.ar 1 LIG1 -0.2441 12 | 5 C 2.6129 -2.4356 -0.0642 C.3 1 LIG1 0.0127 13 | 6 H 3.5569 -2.4297 0.5206 H 1 LIG1 0.0456 14 | 7 H 2.8564 -2.2825 -1.1374 H 1 LIG1 0.0456 15 | 8 H 2.1232 -3.4248 0.0587 H 1 LIG1 0.0456 16 | 9 C 2.2955 -0.0017 0.2752 C.ar 1 LIG1 0.0956 17 | 10 H -1.0216 0.4195 0.4087 H 1 LIG1 0.0845 18 | 11 H -0.0726 -1.9566 -0.9433 H 1 LIG1 0.0825 19 | 12 H 3.2927 0.2416 0.3819 H 1 LIG1 0.1028 20 | @BOND 21 | 1 1 2 ar 22 | 2 2 3 ar 23 | 3 1 4 ar 24 | 4 3 5 1 25 | 5 5 6 1 26 | 6 5 7 1 27 | 7 5 8 1 28 | 8 4 9 ar 29 | 9 9 3 ar 30 | 10 1 10 1 31 | 11 2 11 1 32 | 12 9 12 1 33 | -------------------------------------------------------------------------------- /utils/hierarchical_design_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mosdef-hub/mbuild_tutorials/f56a3ddf672a4a568648f39e0bfc114b05f9e2e7/utils/hierarchical_design_image.png --------------------------------------------------------------------------------