cmp_attr
- mdtools.structure.cmp_attr(ag, attr, weights=None, cmp=None, natms_per_cmp=None)[source]
Get attributes of an MDAnalysis
AtomGroup
compound-wise.Get arbitrary attributes (e.g.
masses
orcharges
) that are defined forAtoms
of an MDAnalysisAtomGroup
for each individual compound contained in theAtomGroup
.A compound is usually a chemically meaningful subgroup of an
AtomGroup
. This can e.g. be aSegment
,Residue
,fragment
or a singleAtom
. Refer to the MDAnalysis’ user guide for an explanation of these terms. Note that in any case, onlyAtoms
belonging to the inputAtomGroup
are taken into account, even if the compound might comprise additionalAtoms
that are not contained in the inputAtomGroup
.- Parameters:
ag (
MDAnalysis.core.groups.AtomGroup
) – The MDAnalysisAtomGroup
for which to get the compound-wise attribute.attr (
str
) – The attribute to get. In principle, this can be any attribute of the inputAtomGroup
. See the MDAnalysis documentation and the examples below for possible choices.weights (
str
orarray_like
orNone
or'total'
, optional) – The weights to use when calculating the compound-wise attribute. This can be either the name of an(other) attribute of the inputAtomGroup
ag, an array of shape(ag.n_atoms,)
assigning a weight to each atom in ag,None
or'total'
. If weights is an attribute of ag, the attribute must be an array of shape(ag.n_atoms,)
. If weights isNone
, all atoms are weighted equally. If weights is'total'
, the attributes of all atoms of a compound are simply summed up without taking any average. See examples below. If the weights of a compound sum up to zero, its attribute will benumpy.inf
.cmp (
{'group', 'segments', 'residues', 'molecules', 'fragments', 'atoms'}
, optional) – The compounds of ag for which to get the selected attribute. You must either provide cmp or natms_per_cmp. If both are given, cmp is ignored.The selected attribute can be calculated for each
Segment
,Residue
, molecule,fragment
orAtom
in the inputAtomGroup
or for the entireAtomGroup
itself. Refer to the MDAnalysis’ user guide for an explanation of these terms. Note that in any case, even if cmp is e.g.'residues'
, only theAtoms
belonging to ag are taken into account, even if the compound might comprise additionalAtoms
that are not contained in ag.If cmp is
'atoms'
, this function is equivalent togetattr(ag, attr).astype(np.float64)
.natms_per_cmp (
int
orarray_like
orNone
, optional) – Number ofAtoms
per compound. You must either provide cmp or natms_per_cmp. If both are given, cmp is ignored.natms_per_cmp can be a single integer or an array of integers. If a single integer is given, all compounds are assumed to contain the same number of
Atoms
. In this case, natms_per_cmp must be an integer divisor ofag.n_atoms
. If natms_per_cmp is an array of integers, it must contain the number ofAtoms
for each single compound. In this case,sum(natms_per_cmp)
must be equal toag.n_atoms
.Providing natms_per_cmp instead of cmp can speed up the calculation if this function is called multiple times. Internally, this function uses
mdtools.structure.natms_per_cmp()
to calculate natms_per_cmp if only cmp is given.
- Returns:
attr (
numpy.ndarray
ofdtype numpy.float64
) – The selected attribute for each compound in ag. The length of attr is equal to the number of compounds in ag. The number of dimensions of ag depends on the selected attribute. For instance, if the selected attribute is ‘charges’, the shape of attr will be(n_cmp,)
. If the selected attribute is ‘velocities’, the shape of attr will be(n_cmp, 3)
. The dtype of attr will always benumpy.float64
.
See also
mdtools.structure.natms_per_cmp()
Get the number of
Atoms
of each compound in an MDAnalysisAtomGroup
Examples
Create an MDAnalysis Universe from scratch for the following examples.
>>> import MDAnalysis as mda >>> # Number of segments. >>> n_seg = 1 >>> # Number of residues per segment. >>> n_res = [n+2 for n in range(n_seg)] >>> # Number of atoms per residue. >>> n_atms = [n+2 for n_r in n_res for n in range(n_r)] >>> u = mda.Universe.empty( ... n_atoms=sum(n_atms), ... n_residues=sum(n_res), ... n_segments=n_seg, ... atom_resindex=[ ... ix for ix, n_a in enumerate(n_atms) for _ in range(n_a) ... ], ... residue_segindex=[ ... ix for ix, n_r in enumerate(n_res) for _ in range(n_r) ... ], ... trajectory=True, ... velocities=True, ... forces=True, ... ) >>> bonds = [ ... (sum(n_atms[:i]), j) ... for i in range(len(n_atms)) ... for j in range(sum(n_atms[:i])+1, sum(n_atms[:i+1])) ... ] >>> u.add_TopologyAttr("bonds", bonds) >>> u.add_TopologyAttr("masses") >>> u.add_TopologyAttr("charges") >>> ag = u.atoms >>> # Fill AtomGroup attributes. >>> ag.positions = np.random.random(ag.positions.shape) * 10 - 5 >>> ag.velocities = np.random.random(ag.velocities.shape) * 2 - 1 >>> ag.forces = np.random.random(ag.forces.shape) * 5 - 2.5 >>> ag.masses = np.random.random(ag.masses.shape) * 10 >>> ag.charges = np.random.random(ag.charges.shape) * - 5
Center of geometry.
>>> # Center of geometry of each segment. >>> cog1 = mdt.strc.cmp_attr(ag, cmp="segments", attr="positions") >>> cog2 = [seg.atoms.center_of_geometry() for seg in ag.segments] >>> np.allclose(cog1, cog2, rtol=0) True >>> # Center of geometry of each residue. >>> cog1 = mdt.strc.cmp_attr(ag, cmp="residues", attr="positions") >>> cog2 = [res.atoms.center_of_geometry() for res in ag.residues] >>> np.allclose(cog1, cog2, rtol=0) True >>> # Center of geometry of each fragment. >>> cog1 = mdt.strc.cmp_attr(ag, cmp="fragments", attr="positions") >>> cog2 = [frg.atoms.center_of_geometry() for frg in ag.fragments] >>> np.allclose(cog1, cog2, rtol=0) True >>> # Center of geometry of each atom. >>> cog1 = mdt.strc.cmp_attr(ag, cmp="atoms", attr="positions") >>> cog2 = ag.positions >>> np.allclose(cog1, cog2, rtol=0) True >>> # Center of geometry of the entire group. >>> cog1 = mdt.strc.cmp_attr(ag, cmp="group", attr="positions") >>> cog2 = ag.center_of_geometry() >>> np.allclose(cog1, cog2, rtol=0) True
Instead of cmp one can also give natms_per_cmp, which can for instance be calculated using
mdtools.structure.natms_per_cmp()
.>>> # Center of geometry of the entire group. >>> cog1 = mdt.strc.cmp_attr( ... ag, attr="positions", natms_per_cmp=sum(n_atms) ... ) >>> cog2 = ag.center_of_geometry() >>> np.allclose(cog1, cog2, rtol=0) True >>> # Center of geometry of each atom. >>> cog1 = mdt.strc.cmp_attr(ag, attr="positions", natms_per_cmp=1) >>> cog2 = ag.positions >>> np.allclose(cog1, cog2, rtol=0) True >>> # Center of geometry of each residue. >>> cog1 = mdt.strc.cmp_attr( ... ag, attr="positions", natms_per_cmp=n_atms ... ) >>> cog2 = [res.atoms.center_of_geometry() for res in ag.residues] >>> np.allclose(cog1, cog2, rtol=0) True
Center of mass.
>>> com1 = mdt.strc.cmp_attr( ... ag, cmp="residues", attr="positions", weights="masses" ... ) >>> com2 = [res.atoms.center_of_mass() for res in ag.residues] >>> np.allclose(com1, com2, rtol=0) True
Center-of-mass velocity.
>>> com_vel1 = mdt.strc.cmp_attr( ... ag, cmp="residues", attr="velocities", weights="masses" ... ) >>> com_vel2 = [ ... np.sum( ... np.expand_dims( ... res.atoms.masses / np.sum(res.atoms.masses), ... axis=1 ... ) ... * res.atoms.velocities, ... axis=0 ... ) ... for res in ag.residues ... ] >>> np.allclose(com_vel1, com_vel2) True
Center of charge (the charges of each compound should not sum up to zero).
>>> coc1 = mdt.strc.cmp_attr( ... ag, cmp="residues", attr="positions", weights="charges" ... ) >>> coc2 = [ ... res.atoms.center(weights=res.atoms.charges) ... for res in ag.residues ... ] >>> np.allclose(coc1, coc2, rtol=0) True
Array of arbitrary weights (must have shape
(ag.n_atoms,)
).>>> coc_abs1 = mdt.strc.cmp_attr( ... ag, ... cmp="residues", ... attr="positions", ... weights=np.abs(ag.charges), ... ) >>> coc_abs2 = [ ... res.atoms.center(weights=np.abs(res.atoms.charges)) ... for res in ag.residues ... ] >>> np.allclose(coc_abs1, coc_abs2, rtol=0) True
weights="total"
: Return the sum of the selected attribute over all atoms of each compound without taking the (weighted) average.>>> # Total mass of each residue. >>> res_mass1 = mdt.strc.cmp_attr( ... ag, cmp="residues", attr="masses", weights="total" ... ) >>> res_mass2 = [sum(res.atoms.masses) for res in ag.residues] >>> np.allclose(res_mass1, res_mass2, rtol=0) True >>> # Total charge of each residue. >>> res_charges1 = mdt.strc.cmp_attr( ... ag, cmp="residues", attr="charges", weights="total" ... ) >>> res_charges2 = [sum(res.atoms.charges) for res in ag.residues] >>> np.allclose(res_charges1, res_charges2, rtol=0) True >>> # Total velocity of each residue. >>> res_vel1 = mdt.strc.cmp_attr( ... ag, cmp="residues", attr="velocities", weights="total" ... ) >>> res_vel2 = [ ... np.sum(res.atoms.velocities, axis=0) for res in ag.residues ... ] >>> np.allclose(res_vel1, res_vel2) True >>> # Center-of-mass force of each residue. >>> com_force1 = mdt.strc.cmp_attr( ... ag, cmp="residues", attr="forces", weights="total" ... ) >>> com_force2 = [ ... np.sum(res.atoms.forces, axis=0) for res in ag.residues ... ] >>> np.allclose(com_force1, com_force2) True
If the weights of all atoms belonging to the same compound sum up to zero, the compound’s attribute will be
numpy.inf
.>>> weights = np.array([-1, 1, -2, 0, 2]) >>> a = mdt.strc.cmp_attr( ... ag, cmp="residues", attr="positions", weights=weights, ... ) >>> np.abs(a) # Only for doctest: Convert -inf to inf array([[inf, inf, inf], [inf, inf, inf]])
mdtools.structure.cmp_attr()
only takes into accountAtoms
that belong to the inputAtomGroup
ag, even if the selected compound might comprise additionalAtoms
that are not contained in ag. Contrarily, ag.segments.atoms, ag.residues.atoms and ag.fragments.atoms contain allAtoms
that belong to the respective compound even if ag does not contain all theirAtoms
.>>> ag = ag[:-1] >>> cog1 = mdt.strc.cmp_attr(ag, cmp="residues", attr="positions") >>> cog2 = [res.atoms.center_of_geometry() for res in ag.residues] >>> np.allclose(cog1[0], cog2[0], rtol=0) True >>> np.allclose(cog1[1], cog2[1], rtol=0) False