Measuring Distances and Angles¶
We have already seen that we can measure the lengths of bonds or
sizes of angles using the functions of the
Bond
, ~sire.mm.Angle
, ~sire.mm.Dihedral
and Improper
classes.
For example, load up the aladip
system…
>>> import sire as sr
>>> mols = sr.load(sr.expand(sr.tutorial_url, ["ala.top", "ala.crd"]))
>>> mol = mols[0]
and we could then find the lengths of all of the carbon-oxygen bonds using
the lengths()
function;
>>> print(mols.bonds("element carbon", "element oxygen").lengths())
[1.20803 Å, 1.24385 Å]
Similarly, you could get the size of the first five hydrogen-oxygen-hydrogen
angles using the sizes()
function;
>>> print(mols.angles("element H", "element O", "element H")[0:5].sizes())
[104.491°, 104.491°, 104.491°, 104.491°, 104.491°]
Sire uses the synonym measure
for both lengths and sizes. This lets you
use the same function name for measuring bond lengths, angle sizes or
dihedral torsion sizes. For example, you could use
measures()
in place of
lengths()
above, e.g.
>>> print(mols.bonds("element carbon", "element oxygen").measures())
[1.20803 Å, 1.24385 Å]
or measures()
in place of
sizes()
,
>>> print(mols.angles("element H", "element O", "element H")[0:5].measures())
[104.491°, 104.491°, 104.491°, 104.491°, 104.491°]
Note
You can use measures
instead of sizes
to measure dihedral
angles and improper angles too.
For individual bond, angle, dihedral, or improper objects, you could
use measure
instead of length
or size
.
Making measurements between atoms¶
So far, you have used length
, size
or measure
for measuring
actual class:Bond
, ~sire.mm.Angle
, ~sire.mm.Dihedral
or Improper
objects.
There are many cases where you want to measure the distance or angles between arbitrary atoms.
To do this, you use the sire.measure()
function. For example,
we could measure the distance between the oxygen atoms of the first
two water molecules using;
>>> oxygens = mols["water and element O"]
>>> print(sr.measure(oxygens[0], oxygens[1]))
18.5067 Å
The measurement returned depends on the number of items passed to the
measure()
function. Passing two items, as above, will measure
and return the distance. Passing three items will measure and return
the angle, so
>>> print(sr.measure(oxygens[0], oxygens[1], oxygens[2]))
53.3414°
has returned the angle between the oxygens of the first three water molecules.
Passing in four items will measure the dihedral (torsion) angle, i.e.
>>> print(sr.measure(oxygens[0], oxygens[1], oxygens[2], oxygens[3]))
60.0107°
measures the torsion angle between the oxygens of the first four water molecules.
Improper angles are also measured between four items. Set
improper_angle
to True
to get the improper angle instead;
>>> print(sr.measure(oxygens[0], oxygens[1], oxygens[2], oxygens[3],
... improper_angle=True))
-44.0118°
Passing in only a single item will call the .measure()
function
on that item. This means that this will only work for individual
Bond
, ~sire.mm.Angle
, ~sire.mm.Dihedral
or Improper
objects;
>>> bond = mols[0].bonds()[0]
>>> print(bond, bond.measure())
Bond( HH31:1 => CH3:2 ) 1.09 Å
>>> print(sr.measure(bond))
1.09 Å
Making measurements between arbitray views¶
The measure()
function calls the .coordinates()
function
on the items that are passed. This means that you can pass in any
object that has a .coordinates()
function. For example, you can
calculate the distance between the first two water molecules using
>>> waters = mols["water"]
>>> print(sr.measure(waters[0], waters[1]))
18.4583 Å
This is not the same as the distance between the oxygens of
these water molecules. This is because the .coordinates()
function on a molecule returns the molecule’s center of mass.
If you wanted to return the distance between the molecules’ centers of geometry you would use
>>> print(sr.measure(waters[0].evaluate().center_of_geometry(),
... waters[1].evaluate().center_of_geometry()))
18.0674 Å
You can calculate distances between the centers of mass or geometry of arbitray views. For example, here we calculate the distance between the centers of mass of the first two residues of the first molecule;
>>> res = mols[0].residues()
>>> print(sr.measure(res[0], res[1]))
3.24294 Å
or, to get the distance between the centers of geometry
>>> print(sr.measure(res[0].evaluate().center_of_geometry(),
... res[1].evaluate().center_of_geometry()))
3.79671 Å
The same would work for angles, dihedrals or improper angles, e.g.
>>> print(sr.measure(res[0], res[1], res[2]))
148.946°
You can also pass in a list of views, e.g.
>>> print(sr.measure([res[0], res[1], res[2]]))
148.946°
or
>>> print(sr.measure(res[0:3]))
148.946°
Measuring against points in space¶
The actual coordinates of individual atoms, or of the centers of geometry
or mass of molecular views, are represented as sire.maths.Vector
objects. These are simple objects that hold three double precision numbers
that represent the x, y, and z coordinates of a point in 3D space.
For example, here is the Vector
that corresponds
to the center of mass of the first molecule.
>>> print(mols[0].coordinates())
( 16.5471 Å, 4.50102 Å, 15.6589 Å )
You access the individual x, y, and z components either via the
x()
, y()
and z()
functions, or by treating the
Vector
as a container, e.g.
>>> v = mols[0].coordinates()
>>> print(v.x(), v.y(), v.z())
16.5471 Å 4.50102 Å 15.6589 Å
>>> print(v[0], v[1], v[2])
16.5471 Å 4.50102 Å 15.6589 Å
You construct Vector
objects by passing in the
values of the x, y, and z components. For example, here we calculate
the distance between two points in space;
>>> print(sr.measure(sr.maths.Vector(0, 0, 0),
... sr.maths.Vector(5, 0, 0)))
5 Å
Notice how the distance is returned in angstroms. This is because the units of distance, if unspecified, are in the default length unit that has been set (this defaults to angstrom).
You can change the default length unit using, e.g.
>>> from sire.units import picometer
>>> sr.units.set_length_unit(picometer)
>>> print(sr.measure(sr.maths.Vector(0, 0, 0),
... sr.maths.Vector(5, 0, 0)))
5 pm
You can change to a full set of SI units using
>>> sr.units.set_si_units()
>>> print(sr.measure(sr.maths.Vector(0, 0, 0),
... sr.maths.Vector(5, 0, 0)))
5 nm
As you can see, sire uses nanometers as the SI unit of length. You can
find the default units for any dimension using the get_default()
function on each unit, e.g.
>>> picometer.get_default()
1 nm
This shows that the current default unit of length is one nanometer.
You can reset to the default units for sire using
>>> sr.units.set_internal_units()
These use angstroms for length,
>>> picometer.get_default()
1 Å
You can always specify the units if something other than the default is desired, or you want to make sure that your script is robust to changes in the default.
>>> print(sr.measure(sr.maths.Vector(0, 0, 0),
... sr.maths.Vector(5 * picometer, 0, 0)))
0.05 Å
You can also pass in a tuple or list of three values, e.g.
>>> print(sr.measure( (0,0,0), (5,0,0) ))
5 Å
>>> print(sr.measure( (0,0,0), (5*picometer,0,0) ))
0.05 Å
Using Vector
enables you to calculate distances,
angles etc. between atoms or molecule views to arbitrary points in space.
For example, here is the distance from the origin to the center of first molecule
>>> print(sr.measure( (0,0,0), mols[0] ))
23.2221 Å
Or the angle between the oxygen in the first water molecule and the x axis
>>> print(sr.measure( (0,0,0), (1,0,0), mols["water and element O"][0] ))
135.775°