Skip to content

Commit

Permalink
Check for missing translation or rotation
Browse files Browse the repository at this point in the history
When writing out transformations (for geometric
objects or datasets) report an error if the
transformation is missing either a rotation or
a translation. Skip this check if so requested.
  • Loading branch information
benmwebb committed Oct 19, 2024
1 parent 739c711 commit 65fee40
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 4 deletions.
17 changes: 13 additions & 4 deletions ihm/dumper.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,15 @@ def dump(self, system, writer):
def _get_transform(rot_matrix, tr_vector):
"""Return a dict encoding a transform, suitable for passing to
loop.write()"""
# mmCIF writer usually outputs floats to 3 decimal
# places, but we need more precision for rotation
# matrices
rm = [["%.6f" % e for e in rot_matrix[i]] for i in range(3)]
if rot_matrix in (None, ihm.unknown):
rm = [[rot_matrix for _ in range(3)] for _ in range(3)]
else:
# mmCIF writer usually outputs floats to 3 decimal
# places, but we need more precision for rotation
# matrices
rm = [["%.6f" % e for e in rot_matrix[i]] for i in range(3)]
if tr_vector in (None, ihm.unknown):
tr_vector = [tr_vector for _ in range(3)]

return {'rot_matrix11': rm[0][0], 'rot_matrix21': rm[1][0],
'rot_matrix31': rm[2][0], 'rot_matrix12': rm[0][1],
Expand Down Expand Up @@ -1242,6 +1247,8 @@ def dump_related_transform(self, system, writer):
"rot_matrix[1][3]", "rot_matrix[2][3]", "rot_matrix[3][3]",
"tr_vector[1]", "tr_vector[2]", "tr_vector[3]"]) as lp:
for t in self._transform_by_id:
if self._check:
util._check_transform(t)
lp.write(id=t._dtid,
**_get_transform(t.rot_matrix, t.tr_vector))

Expand Down Expand Up @@ -2085,6 +2092,8 @@ def dump_transformations(self, writer):
"rot_matrix[1][3]", "rot_matrix[2][3]", "rot_matrix[3][3]",
"tr_vector[1]", "tr_vector[2]", "tr_vector[3]"]) as lp:
for t in self._transformations_by_id:
if self._check:
util._check_transform(t)
lp.write(id=t._id, **_get_transform(t.rot_matrix, t.tr_vector))

def dump_generic(self, writer):
Expand Down
7 changes: 7 additions & 0 deletions ihm/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ def _check_residue(r):
% (r.seq_id, r.entity, len(r.entity.sequence)))


def _check_transform(t):
if t.rot_matrix in (None, ihm.unknown):
raise ValueError("Transformation %s is missing rotation" % t)
if t.tr_vector in (None, ihm.unknown):
raise ValueError("Transformation %s is missing translation" % t)


def _invert_ranges(ranges, end, start=1):
"""Given a sorted list of non-overlapping ranges, yield a new list which
contains every range in the range start-end which was not in the
Expand Down
56 changes: 56 additions & 0 deletions test/test_dumper.py
Original file line number Diff line number Diff line change
Expand Up @@ -1694,6 +1694,29 @@ def test_dataset_dumper_dump(self):
#
""")

def test_dataset_dumper_dump_invalid_transform(self):
"""Test DatasetDumper.dump() with invalid transformed dataset"""
system = ihm.System()

loc = ihm.location.PDBLocation('1cdf', version='foo', details='bar')
loc._id = 1
dst = ihm.dataset.Dataset(loc, details='baz')
t = ihm.geometry.Transformation(
rot_matrix=None, tr_vector=[1., 2., 3.])
td = ihm.dataset.TransformedDataset(dst, transform=t)

loc = ihm.location.InputFileLocation(repo='foo', path='bar')
loc._id = 2
ds2 = ihm.dataset.CXMSDataset(loc)
ds2.parents.append(td)
system.orphan_datasets.append(ds2)

d = ihm.dumper._DatasetDumper()
d.finalize(system) # Assign IDs
self.assertRaises(ValueError, _get_dumper_output, d, system)
# OK if checks are disabled
_ = _get_dumper_output(d, system, check=False)

def test_model_representation_dump(self):
"""Test ModelRepresentationDumper"""
system = ihm.System()
Expand Down Expand Up @@ -3576,6 +3599,39 @@ def test_geometric_object_dumper(self):
#
""")

def test_geometric_object_dumper_invalid_rotation(self):
"""Test GeometricObjectDumper with invalid rotation"""
system = ihm.System()
center = ihm.geometry.Center(1., 2., 3.)
trans = ihm.geometry.Transformation(None, [1., 2., 3.])
sphere = ihm.geometry.Sphere(center=center, transformation=trans,
radius=2.2, name='my sphere',
description='a test sphere')
system.orphan_geometric_objects.append(sphere)

dumper = ihm.dumper._GeometricObjectDumper()
dumper.finalize(system)
self.assertRaises(ValueError, _get_dumper_output, dumper, system)
# OK if checks are disabled
_ = _get_dumper_output(dumper, system, check=False)

def test_geometric_object_dumper_invalid_translation(self):
"""Test GeometricObjectDumper with invalid translation"""
system = ihm.System()
center = ihm.geometry.Center(1., 2., 3.)
trans = ihm.geometry.Transformation([[1, 0, 0], [0, 1, 0], [0, 0, 1]],
ihm.unknown)
sphere = ihm.geometry.Sphere(center=center, transformation=trans,
radius=2.2, name='my sphere',
description='a test sphere')
system.orphan_geometric_objects.append(sphere)

dumper = ihm.dumper._GeometricObjectDumper()
dumper.finalize(system)
self.assertRaises(ValueError, _get_dumper_output, dumper, system)
# OK if checks are disabled
_ = _get_dumper_output(dumper, system, check=False)

def test_feature_dumper(self):
"""Test FeatureDumper"""
system = ihm.System()
Expand Down

0 comments on commit 65fee40

Please sign in to comment.