Skip to content

Commit

Permalink
Squashed 'modules/core/dependency/python-ihm/' changes from f49b8e991…
Browse files Browse the repository at this point in the history
…7..a8122418ca

a8122418ca Prepare for 0.41 release
fb63400f75 Add more info on steps
1584b66a82 Remove unused variable
fd16469460 Use seq_id internally for branched entities
65405a13dd Account for new atom_site.auth_comp_id in examples
c98b9c755f Add atom_site.auth_comp_id
c22c2988ac Add _pdbx_branch_scheme.pdb_mon_id
3ed7b65da3 Skip pdbx_entity_nonpoly table for branched entity
5d69a98b33 Sort output to make a little more readable

git-subtree-dir: modules/core/dependency/python-ihm
git-subtree-split: a8122418ca30d3341d2dcbbd98b465a2fbe1a829
  • Loading branch information
benmwebb committed Oct 11, 2023
1 parent 7b1dce5 commit 4313a61
Show file tree
Hide file tree
Showing 13 changed files with 77 additions and 48 deletions.
7 changes: 7 additions & 0 deletions modules/core/dependency/python-ihm/ChangeLog.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
0.41 - 2023-10-02
=================
- More complete support for oligosaccharides, in particular correct
numbering for atoms in `atom_site`, and the addition of some
data items to the output which are required for full
dictionary compliance.

0.40 - 2023-09-25
=================
- Basic support for oligosaccharides is now provided. New classes are
Expand Down
2 changes: 1 addition & 1 deletion modules/core/dependency/python-ihm/MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ include examples/*
include util/make-mmcif.py
include src/ihm_format.h
include src/ihm_format.i
include src/ihm_format_wrap_0.40.c
include src/ihm_format_wrap_0.41.c
4 changes: 2 additions & 2 deletions modules/core/dependency/python-ihm/ihm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import json
from . import util

__version__ = '0.40'
__version__ = '0.41'


class __UnknownValue(object):
Expand Down Expand Up @@ -1341,7 +1341,7 @@ def __call__(self, seq_id_begin, seq_id_end):
return EntityRange(self, seq_id_begin, seq_id_end)

def __get_seq_id_range(self):
if self.is_polymeric():
if self.is_polymeric() or self.is_branched():
return (1, len(self.sequence))
else:
# Nonpolymers don't have the concept of seq_id
Expand Down
3 changes: 2 additions & 1 deletion modules/core/dependency/python-ihm/ihm/dictionary.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ def _check_linked_items(self):
# the chem_comp_* categories don't need to be fully populated
if cat in self.dictionary.categories \
and not cat.startswith('chem_comp_'):
missing = self._seen_ids[child] - self._seen_ids[parent]
missing = sorted(self._seen_ids[child]
- self._seen_ids[parent])
self.errors.append(
"The following IDs referenced by %s "
"were not defined in the parent category (%s): %s"
Expand Down
21 changes: 13 additions & 8 deletions modules/core/dependency/python-ihm/ihm/dumper.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ def dump(self, system, writer):
with writer.loop("_pdbx_entity_nonpoly",
["entity_id", "name", "comp_id"]) as lp:
for entity in system.entities:
if entity.is_polymeric():
if entity.is_polymeric() or entity.is_branched():
continue
lp.write(entity_id=entity._id, name=entity.description,
comp_id=entity.sequence[0].id)
Expand Down Expand Up @@ -734,7 +734,7 @@ def dump(self, system, writer):
with writer.loop("_pdbx_branch_scheme",
["asym_id", "entity_id", "mon_id", "num",
"pdb_seq_num", "auth_seq_num",
"auth_mon_id", "pdb_asym_id"]) as lp:
"auth_mon_id", "pdb_mon_id", "pdb_asym_id"]) as lp:
for asym in system.asym_units:
entity = asym.entity
if not entity.is_branched():
Expand All @@ -747,8 +747,8 @@ def dump(self, system, writer):
num=num + 1,
pdb_seq_num=auth_seq_num,
auth_seq_num=auth_seq_num,
mon_id=comp.id,
auth_mon_id=comp.id)
mon_id=comp.id, auth_mon_id=comp.id,
pdb_mon_id=comp.id)


class _BranchDescriptorDumper(Dumper):
Expand Down Expand Up @@ -1460,6 +1460,9 @@ def __call__(self, obj):
else:
type_check = self._type_check_atom
seq_id_range = (obj.seq_id, obj.seq_id)
# Allow seq_id to be either 1 or None for ligands
if obj.seq_id == 1 and asym.entity.type == 'non-polymer':
seq_id_range = (None, None)
self._check_duplicate_atom(obj)
self._check_assembly(obj, asym, seq_id_range)
self._check_representation(obj, asym, type_check, seq_id_range)
Expand Down Expand Up @@ -1591,7 +1594,7 @@ def dump_atoms(self, system, writer, add_ihm=True):
"label_alt_id", "label_comp_id", "label_seq_id", "auth_seq_id",
"pdbx_PDB_ins_code", "label_asym_id", "Cartn_x", "Cartn_y",
"Cartn_z", "occupancy", "label_entity_id", "auth_asym_id",
"B_iso_or_equiv", "pdbx_PDB_model_num"]
"auth_comp_id", "B_iso_or_equiv", "pdbx_PDB_model_num"]
if add_ihm:
it.append("ihm_model_id")
with writer.loop("_atom_site", it) as lp:
Expand All @@ -1600,8 +1603,10 @@ def dump_atoms(self, system, writer, add_ihm=True):
for atom in model.get_atoms():
rngcheck(atom)
seq_id = 1 if atom.seq_id is None else atom.seq_id
label_seq_id = atom.seq_id
if not atom.asym_unit.entity.is_polymeric():
label_seq_id = None
comp = atom.asym_unit.sequence[seq_id - 1]
water = isinstance(atom.asym_unit, ihm.WaterAsymUnit)
seen_types[atom.type_symbol] = None
auth_seq_id, ins = \
atom.asym_unit._get_auth_seq_id_ins_code(seq_id)
Expand All @@ -1612,8 +1617,8 @@ def dump_atoms(self, system, writer, add_ihm=True):
label_comp_id=comp.id,
label_asym_id=atom.asym_unit._id,
label_entity_id=atom.asym_unit.entity._id,
label_seq_id=None if water else atom.seq_id,
auth_seq_id=auth_seq_id,
label_seq_id=label_seq_id,
auth_seq_id=auth_seq_id, auth_comp_id=comp.id,
pdbx_PDB_ins_code=ins or ihm.unknown,
auth_asym_id=atom.asym_unit.strand_id,
Cartn_x=atom.x, Cartn_y=atom.y, Cartn_z=atom.z,
Expand Down
4 changes: 3 additions & 1 deletion modules/core/dependency/python-ihm/ihm/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ class Atom(object):
:type asym_unit: :class:`ihm.AsymUnit`
:param int seq_id: The sequence ID of the residue represented by this
atom. This should generally be a number starting at 1 for any
polymer chain or water, or None for a ligand.
polymer chain, water, or oligosaccharide. For ligands, a seq_id
is not needed (as a given asym can only contain a single ligand),
so either 1 or None can be used.
:param str atom_id: The name of the atom in the residue
:param str type_symbol: Element name
:param float x: x coordinate of the atom
Expand Down
18 changes: 10 additions & 8 deletions modules/core/dependency/python-ihm/ihm/reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -1994,8 +1994,9 @@ def __init__(self, *args):
self._missing_sequence = collections.defaultdict(dict)
self._seq_id_map = {}

def _get_water_seq_id(self, auth_seq_id, pdbx_pdb_ins_code, asym):
"""Get an internal seq_id for a water, given author-provided info"""
def _get_seq_id_from_auth(self, auth_seq_id, pdbx_pdb_ins_code, asym):
"""Get an internal seq_id for something not a polymer (nonpolymer,
water, branched), given author-provided info"""
if asym._id not in self._seq_id_map:
m = {}
# Make reverse mapping from author-provided info to seq_id
Expand Down Expand Up @@ -2028,23 +2029,24 @@ def __call__(self, pdbx_pdb_model_num, label_asym_id, b_iso_or_equiv,
else:
asym = self.sysr.asym_units.get_by_id(label_asym_id)
auth_seq_id = self.get_int_or_string(auth_seq_id)
water = isinstance(asym, ihm.WaterAsymUnit)
if water:
if seq_id is None:
# Fill in our internal seq_id if possible
seq_id = self._get_water_seq_id(auth_seq_id, pdbx_pdb_ins_code,
asym)
our_seq_id = self._get_seq_id_from_auth(
auth_seq_id, pdbx_pdb_ins_code, asym)
else:
our_seq_id = seq_id
biso = self.get_float(b_iso_or_equiv)
occupancy = self.get_float(occupancy)
group = 'ATOM' if group_pdb is None else group_pdb
a = ihm.model.Atom(
asym_unit=asym, seq_id=seq_id, atom_id=label_atom_id,
asym_unit=asym, seq_id=our_seq_id, atom_id=label_atom_id,
type_symbol=type_symbol, x=float(cartn_x), y=float(cartn_y),
z=float(cartn_z), het=group != 'ATOM', biso=biso,
occupancy=occupancy)
model.add_atom(a)

# Note any residues that have different seq_id and auth_seq_id
if (auth_seq_id is not None and not water and
if (auth_seq_id is not None and seq_id is not None and
(seq_id != auth_seq_id
or pdbx_pdb_ins_code not in (None, ihm.unknown))):
if asym.auth_seq_id_map == 0:
Expand Down
4 changes: 2 additions & 2 deletions modules/core/dependency/python-ihm/make-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
# First, do
# - Update AuditConformDumper to match latest IHM dictionary if necessary
# - Run util/validate-outputs.py to make sure all example outputs validate
# (cd util; PYTHONPATH=.. python3 ./validate-outputs.py)
# - Update ChangeLog.rst and util/python-ihm.spec with the release number
# - Update release number in ihm/__init__.py, MANIFEST.in, and setup.py
# - Commit, tag, and push
# - Make release on GitHub
# - Upload the release tarball from
# https://github.com/ihmwg/python-ihm/releases to Zenodo as a new release
# - Build SRPM and upload to COPR
# - Make sure there are no extraneous .py files (setup.py will include them
# in the pypi package)

Expand All @@ -23,4 +23,4 @@ python3 setup.py sdist
rm -f "src/ihm_format_wrap_${VERSION}.c"

echo "Now use 'twine upload dist/ihm-${VERSION}.tar.gz' to publish the release on PyPi."
echo "Then, update the conda-forge and Homebrew packages to match."
echo "Then, update the conda-forge, COPR, and Homebrew packages to match."
2 changes: 1 addition & 1 deletion modules/core/dependency/python-ihm/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import sys
import os

VERSION = "0.40"
VERSION = "0.41"

copy_args = sys.argv[1:]

Expand Down
41 changes: 24 additions & 17 deletions modules/core/dependency/python-ihm/test/test_dumper.py
Original file line number Diff line number Diff line change
Expand Up @@ -805,7 +805,9 @@ def test_entity_nonpoly_dumper(self):
# Non-polymeric entity
e2 = ihm.Entity([ihm.NonPolymerChemComp('HEM')], description='heme')
e3 = ihm.Entity([ihm.WaterChemComp()])
system.entities.extend((e1, e2, e3))
# Branched entity
e4 = ihm.Entity([ihm.SaccharideChemComp('NAG')])
system.entities.extend((e1, e2, e3, e4))

ed = ihm.dumper._EntityDumper()
ed.finalize(system) # Assign entity IDs
Expand Down Expand Up @@ -2101,10 +2103,13 @@ def test_range_checker_repr_seq_id(self):
x=1.0, y=2.0, z=3.0, radius=4.0)
self.assertRaises(ValueError, rngcheck, sphere)

# Atom in a nonpolymer must have no seq_id
# Atom in a nonpolymer must have no seq_id (or seq_id==1)
atom = ihm.model.Atom(asym_unit=asym_nonpol, seq_id=None, atom_id='C',
type_symbol='C', x=1.0, y=2.0, z=3.0)
rngcheck(atom)
atom = ihm.model.Atom(asym_unit=asym_nonpol, seq_id=1, atom_id='C',
type_symbol='C', x=1.0, y=2.0, z=3.0)
rngcheck(atom)
atom = ihm.model.Atom(asym_unit=asym2, seq_id=None, atom_id='C',
type_symbol='C', x=1.0, y=2.0, z=3.0)
self.assertRaises(ValueError, rngcheck, atom)
Expand Down Expand Up @@ -2328,12 +2333,13 @@ def test_model_dumper_atoms(self):
_atom_site.occupancy
_atom_site.label_entity_id
_atom_site.auth_asym_id
_atom_site.auth_comp_id
_atom_site.B_iso_or_equiv
_atom_site.pdbx_PDB_model_num
_atom_site.ihm_model_id
ATOM 1 C C . ALA 1 1 ? X 1.000 2.000 3.000 . 9 X . 1 1
HETATM 2 C CA . ALA 1 1 ? X 10.000 20.000 30.000 . 9 X . 1 1
ATOM 3 N N . CYS 2 2 ? X 4.000 5.000 6.000 0.200 9 X 42.000 1 1
ATOM 1 C C . ALA 1 1 ? X 1.000 2.000 3.000 . 9 X ALA . 1 1
HETATM 2 C CA . ALA 1 1 ? X 10.000 20.000 30.000 . 9 X ALA . 1 1
ATOM 3 N N . CYS 2 2 ? X 4.000 5.000 6.000 0.200 9 X CYS 42.000 1 1
#
#
loop_
Expand All @@ -2352,19 +2358,19 @@ def test_model_dumper_atoms(self):
asym.auth_seq_id_map = -1
out = _get_dumper_output(dumper, system)
self.assertEqual(
out.split('\n')[43:46:2],
["ATOM 1 C C . ALA 1 0 ? X 1.000 2.000 3.000 . 9 X . 1 1",
out.split('\n')[44:47:2],
["ATOM 1 C C . ALA 1 0 ? X 1.000 2.000 3.000 . 9 X ALA . 1 1",
"ATOM 3 N N . CYS 2 1 ? X 4.000 5.000 6.000 "
"0.200 9 X 42.000 1 1"])
"0.200 9 X CYS 42.000 1 1"])

# With auth_seq_id map
asym.auth_seq_id_map = {1: 42, 2: 99}
out = _get_dumper_output(dumper, system)
self.assertEqual(
out.split('\n')[43:46:2],
["ATOM 1 C C . ALA 1 42 ? X 1.000 2.000 3.000 . 9 X . 1 1",
out.split('\n')[44:47:2],
["ATOM 1 C C . ALA 1 42 ? X 1.000 2.000 3.000 . 9 X ALA . 1 1",
"ATOM 3 N N . CYS 2 99 ? X 4.000 5.000 6.000 "
"0.200 9 X 42.000 1 1"])
"0.200 9 X CYS 42.000 1 1"])

def test_model_dumper_water_atoms(self):
"""Test ModelDumper with water atoms"""
Expand Down Expand Up @@ -2394,10 +2400,10 @@ def test_model_dumper_water_atoms(self):

out = _get_dumper_output(dumper, system)
self.assertEqual(
out.split('\n')[43:46],
['HETATM 1 O O . HOH . 42 ? X 1.000 2.000 3.000 . 9 X . 1 1',
'HETATM 2 O O . HOH . 99 ? X 4.000 5.000 6.000 . 9 X . 1 1',
'HETATM 3 O O . HOH . 3 ? X 7.000 8.000 9.000 . 9 X . 1 1'])
out.split('\n')[44:47],
['HETATM 1 O O . HOH . 42 ? X 1.000 2.000 3.000 . 9 X HOH . 1 1',
'HETATM 2 O O . HOH . 99 ? X 4.000 5.000 6.000 . 9 X HOH . 1 1',
'HETATM 3 O O . HOH . 3 ? X 7.000 8.000 9.000 . 9 X HOH . 1 1'])

def test_ensemble_dumper(self):
"""Test EnsembleDumper"""
Expand Down Expand Up @@ -4515,9 +4521,10 @@ def test_branch_scheme_dumper(self):
_pdbx_branch_scheme.pdb_seq_num
_pdbx_branch_scheme.auth_seq_num
_pdbx_branch_scheme.auth_mon_id
_pdbx_branch_scheme.pdb_mon_id
_pdbx_branch_scheme.pdb_asym_id
A 1 NAG 1 1 1 NAG A
B 2 FUC 1 6 6 FUC B
A 1 NAG 1 1 1 NAG NAG A
B 2 FUC 1 6 6 FUC FUC B
#
""")

Expand Down
4 changes: 2 additions & 2 deletions modules/core/dependency/python-ihm/test/test_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def test_simple_docking_example(self):
# can read it
with open(os.path.join(tmpdir, 'output.cif')) as fh:
contents = fh.readlines()
self.assertEqual(len(contents), 317)
self.assertEqual(len(contents), 318)
with open(os.path.join(tmpdir, 'output.cif')) as fh:
s, = ihm.reader.read(fh)

Expand Down Expand Up @@ -70,7 +70,7 @@ def test_ligands_water_example(self):
# can read it
with open(out) as fh:
contents = fh.readlines()
self.assertEqual(len(contents), 253)
self.assertEqual(len(contents), 254)
with open(out) as fh:
s, = ihm.reader.read(fh)
# Make sure that resulting Python objects are picklable
Expand Down
10 changes: 6 additions & 4 deletions modules/core/dependency/python-ihm/test/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,10 @@ def test_entity(self):
self.assertNotEqual(e1, e3)
self.assertEqual(e1.seq_id_range, (1, 4))
self.assertEqual(e3.seq_id_range, (1, 5))
# seq_id does not exist for nonpolymers or branched entities
# seq_id does not exist for nonpolymers
self.assertEqual(heme.seq_id_range, (None, None))
self.assertEqual(sugar.seq_id_range, (None, None))
# We do have an internal seq_id_range for branched entities
self.assertEqual(sugar.seq_id_range, (1, 1))

def test_entity_weight(self):
"""Test Entity.formula_weight"""
Expand Down Expand Up @@ -466,9 +467,10 @@ def test_asym_range(self):
asugar = ihm.AsymUnit(sugar)
a._id = 42
self.assertEqual(a.seq_id_range, (1, 6))
# seq_id is not defined for nonpolymers or branched entities
# seq_id is not defined for nonpolymers
self.assertEqual(aheme.seq_id_range, (None, None))
self.assertEqual(asugar.seq_id_range, (None, None))
# We use seq_id internally for branched entities
self.assertEqual(asugar.seq_id_range, (1, 1))
r = a(3, 4)
self.assertEqual(r.seq_id_range, (3, 4))
self.assertEqual(r._id, 42)
Expand Down
5 changes: 4 additions & 1 deletion modules/core/dependency/python-ihm/util/python-ihm.spec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Name: python3-ihm
License: MIT
Group: Applications/Engineering
Version: 0.40
Version: 0.41
Release: 1%{?dist}
Summary: Package for handling IHM mmCIF and BinaryCIF files
Packager: Ben Webb <[email protected]>
Expand Down Expand Up @@ -36,6 +36,9 @@ sed -i -e "s/install_requires=\['msgpack'\]/#/" setup.py
%defattr(-,root,root)

%changelog
* Mon Oct 02 2023 Ben Webb <[email protected]> 0.41-1
- Update to latest upstream.

* Mon Sep 25 2023 Ben Webb <[email protected]> 0.40-1
- Update to latest upstream.

Expand Down

0 comments on commit 4313a61

Please sign in to comment.