Moving Atoms and Molecules#

Moving atoms and molecules is very simple. At the most basic, you can move atoms by simply updating their coordinates property (just as you would update any other property). For example, let’s load up the aladip system and translate the first molecule by 1 Å along the x axis.

First, lets get the molecule and print the coordinates as they are from the input file…

>>> mols = sr.load(sr.expand(sr.tutorial_url, ["ala.top", "ala.crd"]))
>>> mol = mols[0]
>>> print(mol.property("coordinates"))
AtomCoords( size=22
0: ( 18.4532 Å, 3.49423 Å, 12.4365 Å )
1: ( 18.9818 Å, 3.44823 Å, 13.3886 Å )
2: ( 20.0513 Å, 3.63293 Å, 13.2874 Å )
3: ( 18.798 Å, 2.43076 Å, 13.7337 Å )
4: ( 18.4805 Å, 4.54971 Å, 14.3514 Å )
...
17: ( 15.3407 Å, 5.44815 Å, 17.9626 Å )
18: ( 13.8341 Å, 3.93668 Å, 18.3509 Å )
19: ( 14.3525 Å, 3.40994 Å, 19.1521 Å )
20: ( 13.1933 Å, 4.59022 Å, 18.9428 Å )
21: ( 13.2149 Å, 3.33301 Å, 17.6874 Å )
)

Now let’s edit the coordinates. The coordinates are just another property of the molecule, so can be edited using a cursor.

>>> c = mol.cursor()
>>> for atom in c.atoms():
...     atom["coordinates"] = atom["coordinates"] + (1,0,0)
>>> mol = c.commit()
>>> print(mol.property("coordinates"))
AtomCoords( size=22
0: ( 19.4532 Å, 3.49423 Å, 12.4365 Å )
1: ( 19.9818 Å, 3.44823 Å, 13.3886 Å )
2: ( 21.0513 Å, 3.63293 Å, 13.2874 Å )
3: ( 19.798 Å, 2.43076 Å, 13.7337 Å )
4: ( 19.4805 Å, 4.54971 Å, 14.3514 Å )
...
17: ( 16.3407 Å, 5.44815 Å, 17.9626 Å )
18: ( 14.8341 Å, 3.93668 Å, 18.3509 Å )
19: ( 15.3525 Å, 3.40994 Å, 19.1521 Å )
20: ( 14.1933 Å, 4.59022 Å, 18.9428 Å )
21: ( 14.2149 Å, 3.33301 Å, 17.6874 Å )
)

Note

Note that the tuple, (1,0,0) is automatically converted to a Vector when added to the coordinates. The units will be the current default length units.

In this case, we have translated every atom by 1 Å along the x-axis. You could set the coordinates directly. For example, here we set the coordinates of the hydrogen atoms to (0,0,0).

>>> for atom in c.atoms("element H"):
...     atom["coordinates"] = (0, 0, 0)
>>> mol = c.commit()
>>> print(mol.property("coordinates"))
AtomCoords( size=22
0: ( 0 , 0 , 0  )
1: ( 19.9818 Å, 3.44823 Å, 13.3886 Å )
2: ( 0 , 0 , 0  )
3: ( 0 , 0 , 0  )
4: ( 19.4805 Å, 4.54971 Å, 14.3514 Å )
...
17: ( 0 , 0 , 0  )
18: ( 14.8341 Å, 3.93668 Å, 18.3509 Å )
19: ( 0 , 0 , 0  )
20: ( 0 , 0 , 0  )
21: ( 0 , 0 , 0  )
)

Or, more directly

>>> c.atoms("element H")["coordinates"] = (0,0,0)
>>> mol = c.commit()
>>> print(mol.property("coordinates"))
AtomCoords( size=22
0: ( 0 , 0 , 0  )
1: ( 19.9818 Å, 3.44823 Å, 13.3886 Å )
2: ( 0 , 0 , 0  )
3: ( 0 , 0 , 0  )
4: ( 19.4805 Å, 4.54971 Å, 14.3514 Å )
...
17: ( 0 , 0 , 0  )
18: ( 14.8341 Å, 3.93668 Å, 18.3509 Å )
19: ( 0 , 0 , 0  )
20: ( 0 , 0 , 0  )
21: ( 0 , 0 , 0  )
)

Note

Setting the property in a cursor collection sets the value for all cursors contained in that collection.

Or, you can set the coordinates of the atoms to different values, if you pass in a list of coordinates that has the same size as the number of cursors, e.g.

>>> c.atoms("element H")[0:3]["coordinates"] = [(1, 1, 1), (2, 2, 2), (3, 3, 3)]
>>> mol = c.commit()
>>> print(mol.property("coordinates"))
AtomCoords( size=22
0: ( 1 Å, 1 Å, 1 Å )
1: ( 19.9818 Å, 3.44823 Å, 13.3886 Å )
2: ( 2 Å, 2 Å, 2 Å )
3: ( 3 Å, 3 Å, 3 Å )
4: ( 19.4805 Å, 4.54971 Å, 14.3514 Å )
...
17: ( 0 , 0 , 0  )
18: ( 14.8341 Å, 3.93668 Å, 18.3509 Å )
19: ( 0 , 0 , 0  )
20: ( 0 , 0 , 0  )
21: ( 0 , 0 , 0  )
)

sets the coordinates of the first three hydrogen atoms to (1,1,1), (2,2,2) and (3,3,3).

Translation using a Cursor#

The Cursor has additional functions that simplify the process of translating, rotating and moving atoms and molecules.

For example, this is how you can use a cursor to translate the first molecule by 1 Å along the x axis.

>>> mol = mols[0]
>>> mol = mol.cursor().translate( (1,0,0) ).commit()
>>> print(mol.property("coordinates"))
AtomCoords( size=22
0: ( 19.4532 Å, 3.49423 Å, 12.4365 Å )
1: ( 19.9818 Å, 3.44823 Å, 13.3886 Å )
2: ( 21.0513 Å, 3.63293 Å, 13.2874 Å )
3: ( 19.798 Å, 2.43076 Å, 13.7337 Å )
4: ( 19.4805 Å, 4.54971 Å, 14.3514 Å )
...
17: ( 16.3407 Å, 5.44815 Å, 17.9626 Å )
18: ( 14.8341 Å, 3.93668 Å, 18.3509 Å )
19: ( 15.3525 Å, 3.40994 Å, 19.1521 Å )
20: ( 14.1933 Å, 4.59022 Å, 18.9428 Å )
21: ( 14.2149 Å, 3.33301 Å, 17.6874 Å )
)

The translate() function translates all of the atoms selected by the cursor by the passed vector (or passed x, y and z components).

For example, you could translate all of the hydrogen atoms by the vector (1, 2, 3) using

>>> cursor = mol.cursor()
>>> cursor["element H"].translate(1, 2, 3)
>>> mol = cursor.commit()
>>> print(mol.property("coordinates"))
AtomCoords( size=22
0: ( 20.4532 Å, 5.49423 Å, 15.4365 Å )
1: ( 19.9818 Å, 3.44823 Å, 13.3886 Å )
2: ( 22.0513 Å, 5.63293 Å, 16.2874 Å )
3: ( 20.798 Å, 4.43076 Å, 16.7337 Å )
4: ( 19.4805 Å, 4.54971 Å, 14.3514 Å )
...
17: ( 17.3407 Å, 7.44815 Å, 20.9626 Å )
18: ( 14.8341 Å, 3.93668 Å, 18.3509 Å )
19: ( 16.3525 Å, 5.40994 Å, 22.1521 Å )
20: ( 15.1933 Å, 6.59022 Å, 21.9428 Å )
21: ( 15.2149 Å, 5.33301 Å, 20.6874 Å )
)

Note

You can pass in the vector to translate either as arguments, e.g. translate(1, 2, 3), or as a Vector, e.g. translate(sr.maths.Vector(1,2,3)) or translate((1,2,3)). As for the rest of Sire, the default units are Å, which can be changed using, e.g. sr.units.set_length_unit. You can also specify the units yourself, e.g. translate(1*sr.units.angstrom, 0, 0) or translate(sr.maths.Vector(1*sr.units.angstrom,0, 0)).

You can even translate all of the molecules that have been loaded, using the cursor for the whole system.

>>> cursor = mols.cursor()
>>> cursor.translate(3,4,5)
>>> mols = cursor.commit()
>>> print(mols[1].property("coordinates"))
AtomCoords( size=3
0: ( 28.6397 Å, 12.5028 Å, 27.4241 Å )
1: ( 28.8521 Å, 12.6656 Å, 26.505 Å )
2: ( 29.3373 Å, 11.9243 Å, 27.7322 Å )
)

Rotation using a Cursor#

You can rotate molecules using a cursor’s rotate() function.

>>> mol = mols[0]
>>> cursor = mol.cursor()
>>> cursor.rotate(5)
>>> mol = cursor.commit()
>>> print(mol.property("coordinates"))
AtomCoords( size=22
0: ( 21.5337 Å, 7.66419 Å, 17.4365 Å )
1: ( 22.0643 Å, 7.66444 Å, 18.3886 Å )
2: ( 23.1136 Å, 7.94164 Å, 18.2874 Å )
3: ( 21.9699 Å, 6.63481 Å, 18.7337 Å )
4: ( 21.4689 Å, 8.71803 Å, 19.3514 Å )
...
17: ( 18.2627 Å, 9.33939 Å, 22.9626 Å )
18: ( 16.8936 Å, 7.70237 Å, 23.3509 Å )
19: ( 17.456 Å, 7.22282 Å, 24.1521 Å )
20: ( 16.1983 Å, 8.29758 Å, 23.9428 Å )
21: ( 16.3293 Å, 7.04702 Å, 22.6874 Å )
)

In this case, we rotated the molecule by 5° about the z-axis of the molecule, around its center of mass.

You can specify the units yourself, e.g. 5 * sr.units.degrees, and can also specify the axis and centers of rotation as additional arguments, e.g.

>>> cursor.rotate(0.1*sr.units.radians, axis=(1,0,0))
>>> print(cursor["coordinates"])
AtomCoords( size=22
0: ( 21.5337 Å, 7.99007 Å, 17.3691 Å )
1: ( 22.0643 Å, 7.89526 Å, 18.3165 Å )
2: ( 23.1136 Å, 8.18119 Å, 18.2434 Å )
3: ( 21.9699 Å, 6.83634 Å, 18.557 Å )
4: ( 21.4689 Å, 8.84748 Å, 19.3796 Å )
...
17: ( 18.2627 Å, 9.10522 Å, 23.0348 Å )
18: ( 16.8936 Å, 7.43761 Å, 23.2577 Å )
19: ( 17.456 Å, 6.88047 Å, 24.007 Å )
20: ( 16.1983 Å, 7.97075 Å, 23.9061 Å )
21: ( 16.3293 Å, 6.85177 Å, 22.5321 Å )
)

rotates by 0.1 radians about the x-axis ((1,0,0)) around the molecule’s center of mass, while

>>> cursor.rotate(10*sr.units.degrees, axis=(0,1,0), center=(0,0,0))
>>> print(cursor["coordinates"])
AtomCoords( size=22
0: ( 24.2227 Å, 7.99007 Å, 13.3659 Å )
1: ( 24.9098 Å, 7.89526 Å, 14.2068 Å )
2: ( 25.9304 Å, 8.18119 Å, 13.9526 Å )
3: ( 24.8585 Å, 6.83634 Å, 14.46 Å )
4: ( 24.508 Å, 8.84748 Å, 15.3571 Å )
...
17: ( 21.9852 Å, 9.10522 Å, 19.5136 Å )
18: ( 20.6756 Å, 7.43761 Å, 19.9708 Å )
19: ( 21.3596 Å, 6.88047 Å, 20.6111 Å )
20: ( 20.1035 Å, 7.97075 Å, 20.7301 Å )
21: ( 19.9939 Å, 6.85177 Å, 19.3542 Å )
)

rotates by 10° about the y-axis with the rotation centered on the origin ((0,0,0)).

You can also specify the rotations directly via rotation matrices (sire.maths.Matrix) or quaternions (sire.maths.Quaternion).

To do this, pass in the matrix or quaternion that represents the rotation, e.g.

>>> cursor.rotate(sr.maths.Quaternion(5*sr.units.degrees,
...                                   sr.maths.Vector(1,0,0)))
>>> print(cursor["coordinates"])
AtomCoords( size=22
0: ( 24.2227 Å, 8.30445 Å, 13.335 Å )
1: ( 24.9098 Å, 8.13672 Å, 14.1644 Å )
2: ( 25.9304 Å, 8.44371 Å, 13.9361 Å )
3: ( 24.8585 Å, 7.05975 Å, 14.3244 Å )
4: ( 24.508 Å, 8.98505 Å, 15.3934 Å )
...
17: ( 21.9852 Å, 8.87955 Å, 19.5565 Å )
18: ( 20.6756 Å, 7.17844 Å, 19.8666 Å )
19: ( 21.3596 Å, 6.56761 Å, 20.4559 Å )
20: ( 20.1035 Å, 7.64338 Å, 20.6695 Å )
21: ( 19.9939 Å, 6.64857 Å, 19.2014 Å )
)

or

>>> rotmat = sr.maths.Matrix(1,0,0,
...                          0,0.984808,-0.173648,
...                          0,0.173648,0.984808)
>>> cursor.rotate(rotmat)
>>> print(cursor["coordinates"])
AtomCoords( size=22
0: ( 24.2227 Å, 8.9353 Å, 13.3558 Å )
1: ( 24.9098 Å, 8.62609 Å, 14.1435 Å )
2: ( 25.9304 Å, 8.96806 Å, 13.972 Å )
3: ( 24.8585 Å, 7.53769 Å, 14.114 Å )
4: ( 24.508 Å, 9.24812 Å, 15.5011 Å )
...
17: ( 21.9852 Å, 8.42131 Å, 19.5826 Å )
18: ( 20.6756 Å, 6.69218 Å, 19.5927 Å )
19: ( 21.3596 Å, 5.98831 Å, 20.0669 Å )
20: ( 20.1035 Å, 7.01065 Å, 20.464 Å )
21: ( 19.9939 Å, 6.28589 Å, 18.8455 Å )
)

Note

The above rotation matrix rotates by 10° about the x-axis. If was generated using the to_matrix() function of the Quaternion that represented this rotation.

As before, the center of rotation defaults to the center of mass of the molecule. You can specify the center of rotation via the center keyword argument. For example,

>>> cursor.rotate(rotmat, center=(0,0,0))
>>> print(cursor["coordinates"])
AtomCoords( size=22
0: ( 24.2227 Å, 6.48035 Å, 14.7045 Å )
1: ( 24.9098 Å, 6.03905 Å, 15.4265 Å )
2: ( 25.9304 Å, 6.40561 Å, 15.317 Å )
3: ( 24.8585 Å, 4.9723 Å, 15.2085 Å )
4: ( 24.508 Å, 6.41589 Å, 16.8715 Å )
...
17: ( 21.9852 Å, 4.89289 Å, 20.7475 Å )
18: ( 20.6756 Å, 3.18828 Å, 20.4571 Å )
19: ( 21.3596 Å, 2.41276 Å, 20.8019 Å )
20: ( 20.1035 Å, 3.3506 Å, 21.3705 Å )
21: ( 19.9939 Å, 2.91791 Å, 19.6507 Å )
)

rotates using the passed rotation matrix, centered on the origin.

Visualising movement by saving trajectory frames#

You can visualise the movements you are performing by saving trajectory frames, and then using the trajectory visualisation and/or analysis functions.

For example, here we will rotate a molecule 360° around the z-axis. We do this in 25 steps, calling sire.mol.Cursor.save_frame() after each rotation to save the coordinates as a new frame in the molecular trajectory.

>>> mols = sr.load(sr.expand(sr.tutorial_url, ["ala.top", "ala.crd"]))
>>> mol = mols[0]
>>> cursor = mol.cursor()
>>> for i in range(0, 25):
...     cursor.rotate(360 / 25, (0,0,1))
...     cursor.save_frame()
>>> mol = cursor.commit()

Note

Note how the axis of rotation is passed in as the second positional argument of rotate. You can pass it in either as the second positional argument or by using the axis keyword, e.g. cursor.rotate(360 / 25, axis=(0,0,1)).

If you are in a Jupyter notebook (or similar) then you can view this as a movie via;

>>> mol.view()
Still from a movie of the aladip molecule being rotated.