Atom Properties¶
Many of the properties you will use will be atomic properties. These are properties that have one value per atom in the molecule.
For example, it is very common that molecules will have atomic coordinates.
By default, these are placed into a property called coordinates
.
>>> 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 Å )
)
The coordinates are held in a sire.mol.AtomCoords
object,
which is an example of a AtomProperty
object. This behaves like
a python list, e.g.
>>> coords = mol.property("coordinates")
>>> print(coords[0])
( 18.4532 Å, 3.49423 Å, 12.4365 Å )
>>> print(coords[0:5])
[( 18.4532 Å, 3.49423 Å, 12.4365 Å ), ( 18.9818 Å, 3.44823 Å, 13.3886 Å ),
( 20.0513 Å, 3.63293 Å, 13.2874 Å ), ( 18.798 Å, 2.43076 Å, 13.7337 Å ),
( 18.4805 Å, 4.54971 Å, 14.3514 Å )]
>>> for coord in coords[0:3]:
... print(coord)
( 18.4532 Å, 3.49423 Å, 12.4365 Å )
( 18.9818 Å, 3.44823 Å, 13.3886 Å )
( 20.0513 Å, 3.63293 Å, 13.2874 Å )
The coordinates themselves are sire.maths.Vector
objects. You
can get the x
, y
, and z
components using the corresponding functions;
>>> coord = coords[0]
>>> print(coord.x())
18.4532 Å
>>> print(coord.y(), coord.z())
3.49423 Å 12.4365 Å
Accessing an atom property via the molecule will return the complete
AtomCoords
object, containing the coordinates for
all of the atoms in the molecule.
You can get the coordinates
properties for an individual atom using
the property
function on the atom. For example, to get the coordinates
on the first atom in the molecule you could use;
>>> atom = mol[0]
>>> print(atom.property("coordinates"))
( 18.4532 Å, 3.49423 Å, 12.4365 Å )
To get the charge on the CH3
atom in residue number 1
you could use
>>> print(mol["resnum 1"]["CH3"].property("charge"))
-0.3662 |e|
Convenience functions¶
To reduce the amount of typing, there are shorthand, convenience functions
that can be used via the Atom
view to access commonly-used properties.
The coordinates
property can be accessed via the coordinates
or
coords
functions, e.g.
>>> print(atom.coordinates())
( 18.4532 Å, 3.49423 Å, 12.4365 Å )
>>> print(atom.coords())
( 18.4532 Å, 3.49423 Å, 12.4365 Å )
You can also get the x
, y
, and z
components
of the coordinates directly, e.g.
>>> print(atom.x(), atom.y(), atom.z())
18.4532 Å 3.49423 Å 12.4365 Å
Properties that can be accessed this way are charge
, coordinates
,
element
, lj
(Lennard Jones parameters) and mass
.
>>> print(atom.charge(), atom.element(), atom.lj(), atom.mass())
0.1123 |e| Hydrogen (H, 1)
LJ( sigma = 2.64953 Å, epsilon = 0.0157 kcal mol-1 ) 1.008 g mol-1
These convenience functions can also be used for larger views. However,
in these cases they evaluate a single value that represents that
view from all of the values of the atoms in that view. Calling the mass()
on a molecule will return the total mass of all atoms in that molecule;
>>> print(mol.mass())
144.176 g mol-1
Similarly, calling charge()
on a residue will return the total
charge on that residue;
>>> print(mol["resnum 1"].charge())
5.48778e-10 |e|
while calling coordinates()
or coords()
on a view will return
the center of mass of that view
>>> print(mol["resnum 1"].coords())
( 18.9264 Å, 4.47803 Å, 14.1498 Å )
This works for any view into a molecule, e.g. the total mass of the first five atoms could be calculated via
>>> print(mol[0:5].mass())
27.044 g mol-1
Accessing atom properties via views¶
While the above convenience functions are useful, there are times when
you will want to get the individual atom properties for all atoms in
a view. You can do this by calling the property()
function on that
view.
For example, to get the elements of all of the atoms in the first residue you would use
>>> residue = mol["resnum 1"]
>>> print(residue.property("element"))
[Hydrogen (H, 1), Carbon (C, 6), Hydrogen (H, 1),
Hydrogen (H, 1), Carbon (C, 6), Oxygen (O, 8)]
or, more directly
>>> print(mol["resnum 1"].property("element"))
[Hydrogen (H, 1), Carbon (C, 6), Hydrogen (H, 1),
Hydrogen (H, 1), Carbon (C, 6), Oxygen (O, 8)]
This works for collections of views, e.g. to get all of the coordinates on the first five atoms of the molecule, you would use
>>> print(mol[0:5].property("coordinates"))
[( 18.4532 Å, 3.49423 Å, 12.4365 Å ), ( 18.9818 Å, 3.44823 Å, 13.3886 Å ),
( 20.0513 Å, 3.63293 Å, 13.2874 Å ), ( 18.798 Å, 2.43076 Å, 13.7337 Å ),
( 18.4805 Å, 4.54971 Å, 14.3514 Å )]
or you could get the Lennard Jones parameters of all of the carbon atoms using
>>> print(mol["element C"].property("LJ"))
[LJ( sigma = 3.39967 Å, epsilon = 0.1094 kcal mol-1 ),
LJ( sigma = 3.39967 Å, epsilon = 0.086 kcal mol-1 ),
LJ( sigma = 3.39967 Å, epsilon = 0.1094 kcal mol-1 ),
LJ( sigma = 3.39967 Å, epsilon = 0.1094 kcal mol-1 ),
LJ( sigma = 3.39967 Å, epsilon = 0.086 kcal mol-1 ),
LJ( sigma = 3.39967 Å, epsilon = 0.1094 kcal mol-1 )]
Note
Note how the Lennard Jones property is called LJ
(using
capital letters), while the .lj()
convenience function on
Atom
uses lower case letters. This is because functions are
named using the snake_case
convention.
Using apply to get the properties of views in a container¶
Another route to getting the properties is to use the apply
function.
The apply
function will call the passed function on all views
within a molecular container. For example, calling the charge
function
on mol.atoms()
will return the total charge on the molecule,
>>> print(mol.atoms().charge())
-5.48778e-10 |e|
To call the charge
function on each atom in the mol.atoms()
container,
we would use apply
, e.g.
>>> print(mol.atoms().apply("charge"))
[ 0.1123 |e|, -0.3662 |e|, 0.1123 |e|, 0.1123 |e|, 0.5972 |e|, -0.5679 |e|,
-0.4157 |e|, 0.2719 |e|, 0.0337 |e|, 0.0823 |e|, -0.1825 |e|, 0.0603 |e|,
0.0603 |e|, 0.0603 |e|, 0.5973 |e|, -0.5679 |e|, -0.4157 |e|, 0.2719 |e|,
-0.149 |e|, 0.0976 |e|, 0.0976 |e|, 0.0976 |e|]
Apply calls the specified function on each view in a container, returning
the result as a list. You can either pass in the name of the function
you want to apply, or you can pass in a function yourself. In this case,
we will use apply
with a lambda expression to get the x coordinates
of all of the atoms in the first residue;
>>> print(mol["resnum 1"].apply(lambda atom: atom.x()))
[18.4532 Å, 18.9818 Å, 20.0513 Å, 18.798 Å, 18.4805 Å, 19.1866 Å]
You can pass in positional and named arguments to the applied function
as arguments to apply
. For example, here we will ask for the mass
property on each atom by calling the property
function via an apply
;
>>> print(mol.atoms().apply("property", "mass"))
[1.008 g mol-1, 12.01 g mol-1, 1.008 g mol-1, 1.008 g mol-1, 12.01 g mol-1,
16 g mol-1, 14.01 g mol-1, 1.008 g mol-1, 12.01 g mol-1, 1.008 g mol-1,
12.01 g mol-1, 1.008 g mol-1, 1.008 g mol-1, 1.008 g mol-1, 12.01 g mol-1,
16 g mol-1, 14.01 g mol-1, 1.008 g mol-1, 12.01 g mol-1, 1.008 g mol-1,
1.008 g mol-1, 1.008 g mol-1]
If you want to reduce the results of an apply
back to a single value,
then you can use the apply_reduce
function. By default, this will
reduce using addition, e.g. the total mass of the oxygen atoms could
be calculated using
>>> print(mol.atoms("element O").apply_reduce("mass"))
32 g mol-1
You can pass in a reduction function as the second argument. For example, to find the maximum residue mass you could type
>>> print(mol.residues().apply_reduce("mass", max))
71.08 g mol-1
By combining the two you could get the maximum atom mass per residue, e.g.
>>> print(mol.residues().apply("apply_reduce", "mass", max))
[16 g mol-1, 16 g mol-1, 14.01 g mol-1]
or, written using a lambda expression,
>>> print(mol.residues().apply(lambda res: res.apply_reduce("mass", max)))
[16 g mol-1, 16 g mol-1, 14.01 g mol-1]