I've got a problem with my script for Python API. I'm searching for Bromine atom with distance 3.1-3.5 A from Oxygen atom. Search tool finds some results and the distance in constranints is ok (for first hit 3.1A) and result is compatible with the serch parameters. But when I acces atoms in single hits results and try to manually calculate the distance between them using MolecularDescriptors.atom_distance its different (9.9A). I've chcecked the atom coordinates in my hits results and in Mercury  for oxygen atom and they are different. I used the Python API 2017 and there were no such problem. How can I get good atom coordinates to calculate the distance in my script? Can you help me?

import ccdc.search
hal = ccdc.search.SMARTSSubstructure('Br[C,c]')
acc = ccdc.search.SMARTSSubstructure('C(=O)')
halogen_bond_search = ccdc.search.SubstructureSearch()
hal_id = halogen_bond_search.add_substructure(hal)
acc_id = halogen_bond_search.add_substructure(acc)
halogen_bond_search.add_distance_constraint('DIST',hal_id, 0,acc_id, 1,(3.1, 3.5),'Intermolecular')
hits = halogen_bond_search.search(max_hits_per_structure=1)
dist=[h.constraints['DIST']for h in hits]
print "Distance from Br to O for the first hit"
print dist[0]
print "Coordinates of Br and O for the first hit"
print "Br"
print X.coordinates
print "O"
print Y.coordinates
print "Calculated distance from Br to O for the first hit"
print ccdc.descriptors.MolecularDescriptors.atom_distance (X,Y)


Hi Anna,

this occurs because the search is performed over the all molecules of the crystal as can be seen by inspecting the symmetry operators of the match:

>>> print hits[0].match_symmetry_operators()[-1]

The atoms provided by match_atoms() are of the single, central molecule of the crystal. I do think that this is not ideal, and I would like to provide some method for capturing the actually matched atoms in some future release.

In the meantime you will can get the matched atoms in a rather roundabout way:

>>> sym_copy = hits[0].crystal.symmetric_molecule(hits[0].match_symmetry_operators()[-1])
>>> sym_Y = sym_copy.atom(Y.label)
>>> print ccdc.descriptors.MolecularDescriptors.atom_distance (X, sym_Y)

This will work as long as the atom labels of the molecule are distinct. If not the match will have to be done on the index of the atoms:

>>> sym_Y = [a for a in sym_copy.atoms if a.index == Y.index][0]

Best wishes



Thank you. It was very helpful.

Kindest regards



I've got some problems. For for example molecule HIZWAQ I've got an error. Could you tell me what does this mean?


TypeError('The symmetry operator %s is not applicable to this crystal' % symmop)
TypeError: The symmetry operator 1/3+y,2/3-x+y,1.66667-z is not applicable to this crystal

Hi Anna,

this did come as a bit of a surprise to me, but after looking at it a bit I think I am clear as to how it happened.

The symmetry operator returned by the  substructure search returns a composite of the unit cell translations and the underlying symmetry operator.  In this instance it represents the symmetry operator 1/3+y,2/3-x+y,2/3+z with a translation of (0, 0, 1).  When putting the translation component in to the operator we end up with 1 + 2/3 +z, which is rounded to 1.66667.  When applying the symetry operator to the crystal to create the symmetric_molecule, a check is made that the operator is one of the symmetry operators of the crystal, and 0.66667 is not exactly equal to 1/3.  

The solution here is very simple: you can force the symmetry operator to be applied:

>>> crystal = hits[0].crystal
>>> symmop = hits[0].match_symmetry_operators()[-1]
>>> symmetric_molecule = crystal.symmetric_molecule(symmop, force=True)

I shall consider whether there may be a better representation of symmetry operators, with the translation kept separate from the operator for some future release.

Thank you for raising this interesting case.

Best wishes

You must be signed in to post in this forum.