4. Geometry tools

4.1. Atomic positions

_images/atompos.png

Atomic positions are accessible both as vectors and scalars:

>>> mol = mdt.from_smiles('CCC')
>>> atom = mol.atoms[0]
>>> atom.position
<Quantity([ 0.99016981  0.04194212  0.10581842], 'ang')>
>>> atom.x
<Quantity(0.990169808818, 'ang')>
>>> atom.z
<Quantity(0.105818418471, 'ang')>

These quantities can be updated, not just read. Everything is kept automatically in sync - changing atom.position will automatically change atom.x, atom.y and atom.z, and vice versa:

>>> atom.x = 5.0 * u.angstrom
>>> atom.position
<Quantity([ 5.          0.04194212  0.10581842], 'ang')>
>>> atom.position = [0,0,0] * u.angstrom
>>> atom.x, atom.y, atom.z
(<Quantity(0.0, 'ang')>, <Quantity(0.0, 'ang')>, <Quantity(0.0, 'ang')>)

4.2. Internal coordinates

The moldesign.geom module contains a variety of methods for measuring (and manipulate geometry).

You can get the distance between any two atoms with the atom.distance method.

>>> atom1.distance(atom2)
[...] angstrom

Bond angles and dihedral (twist) angles can be measured using the moldesign.angle() and moldesign.dihedral() methods:

>>> moldesign.angle(atom1, atom2, atom3)
[...] radians
>>> moldesign.dihedral(atom1, atom2, atom3, atom4)
[...] radians

Measuring a dihedral (or twist) angle can be a pain, because they are defined as a function of four atomic positions. For quick measurements, you can pass in only the two central atoms in the dihedral, and MDT will infer the others. Any of the following call signatures will work:

>>> moldesign.dihedral(a1, a2, a3, a4)
>>> moldesign.dihedral(a1, a2)
>>> moldesign.dihedral(bond)

For applications that require derivatives, the gradients of these quantites are also available using the moldesign.distance_gradient(), moldesign.angle_gradient(), and moldesign.dihedral_gradient().

4.3. Manipulating coordinates

Internal coordinates can also be manipulated using the moldesign.set_distance(), moldesign.set_angle(), and moldesign.set_dihedral() functions.

Note that these functions can work in two ways. We’ll illustrate this with ethylene:

>>> ethylene = mdt.from_smiles('C=C')
>>> ethylene.draw3d()
_images/ethyl_init.png

By default, MDT will move all atoms affected by the change (equivalent to manipulating the molecule’s internal coordinates). Here, although we only explicitly change the distance between the two carbons, the methylene groups move as well:

>>> mdt.set_distance(ethylene.atoms[0], ethylene.atoms[1], 3.0 * u.angstrom)
_images/ethyl_after.png

However, if the keyword adjustmol is set to False, only the specified atoms will be moved; the hydrogens stay in their original positions:

>>> ethylene = mdt.from_smiles('C=C')
>>> mdt.set_distance(ethylene.atoms[0], ethylene.atoms[1],
>>>                  3.0 * u.angstrom,
>>>                  adjustmol=False)
>>> ethylene.draw3d()
_images/ethyl_noadjust.png

In notebooks, these quantities can be changed interactively using the GeometryBuilder widget:

>>> mdt.GeometryBuilder(ethylene)
_images/geometrybuilder.png

4.4. Monitor coordinates

Geometry monitor classes can simplify the process of tracking and manipulating internal coordinates.

>>> ethylene = mdt.from_smiles('C=C')
>>> distance_monitor = mdt.DistanceMonitor(ethylene.atoms[0], ethylene.atoms[1])
>>> distance_monitor.value
<Quantity(1.51205701815, 'ang')>
>>> ethylene.draw3d()
_images/ethyl_init.png

Changing the monitor’s value will change the molecular geometry:

>>> distance_monitor.value = 3.0 * u.angstrom
>>> ethylene.draw()
_images/ethyl_after.png

4.5. Groups of atoms

Most groups of atoms in MDT - including molecules, residues, and even arbitrary lists of atoms in AtomLists - have built-in geometric analysis and manipulation methods (inherited from the AtomContainer class).

Analysis methods include calculating a center of mass,

>>> mol = mdt.from_pdb('1YU8')
>>> mol.center_of_mass
<Quantity([ 10.59496153  15.55064957  21.02801831], 'ang')>
>>> mol.residues[0].center_of_mass
<Quantity([ 10.24884993  27.76183367  19.85926777], 'ang')>

the distance between two groups of atoms (i.e., the minimum distance between any pair of atoms in the two objects),

>>> r1 = mol.residues[0]
>>> r2 = mol.residues[1]
>>> r1.distance(r2)
<Quantity(1.32810161367, 'ang')>

and arrays of distances between the atoms in two objects:

>>> array = r1.calc_distance_array(r2)
>>> array
<Quantity([[ 3.49805684  4.74654325  ...]
           [ 2.43405388  3.8018831   ...]
             ...], 'ang')>

where array[i,j] == r1.atoms[i].distance(r2.atoms[j]).

In addition, groups of atoms can be moved using their translate and rotate methods.

4.6. Analyze dynamics

WIP

4.7. Constrain geometry

Geometric constraints - coordinates that are held constant during dynamics or minimizations - can be created using Molecule methods:

>>> dist_constraint = mol.constrain_distance(mol.atoms[0], mol.atoms[1])
>>> ang_constraint = mol.constrain_angle(mol.atoms[0], mol.atoms[1], mol.atoms[2])

These constraint <moldesign.geom.constraints.GeometryConstraint objects can be queried:

>>> dist_constraint.error()
<Quantity(0.0, 'ang')>
>>> dist_constraint.satisfied()
True

and can be manipulated via their value and tolerance attributes.

For convenience, constraints can also be created from Geometry monitor objects:

>>> distance = mdt.DistanceMonitor(mol.atoms[0], mol.atoms[1])
>>> constraint = distance.constrain()

Molecular constraints are stored in a list at molecule.constraints. To remove all constraints from a molecule, use the clear_constraints method.