Skip to content

Commit

Permalink
Add basic support for ProtocolOutput to PMI2
Browse files Browse the repository at this point in the history
Allow attaching ProtocolOutput objects to PMI2
Systems. This will allow us to collect information
about a PMI2 modeling run, for example to deposit
the protocol as an mmCIF file. Relates #244.
  • Loading branch information
benmwebb committed Jun 22, 2018
1 parent 2c9a5c2 commit 4436041
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 3 deletions.
7 changes: 5 additions & 2 deletions pyext/src/mmcif.py
Original file line number Diff line number Diff line change
Expand Up @@ -941,9 +941,12 @@ def __init__(self, pmi_object, po):
# Point to the PMI object for this state. Use a weak reference
# since the state object typically points to us too, so we need
# to break the reference cycle. In PMI1 this will be a
# Representation object.
# Representation object; in PMI2 it is the PMI2 State object itself.
self._pmi_object = weakref.proxy(pmi_object)
self._pmi_state = pmi_object.state
if hasattr(pmi_object, 'state'):
self._pmi_state = pmi_object.state
else:
self._pmi_state = self._pmi_object
# Preserve PMI state name
old_name = self.name
super(_State, self).__init__(experiment_type='Fraction of bulk')
Expand Down
32 changes: 31 additions & 1 deletion pyext/src/topology/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class System(_SystemBase):
def __init__(self,mdl=None,name="System"):
_SystemBase.__init__(self,mdl)
self._number_of_states = 0
self._protocol_output = []
self.states = []
self.built=False

Expand Down Expand Up @@ -141,6 +142,17 @@ def build(self,**kwargs):
self.built=True
return self.hier

def add_protocol_output(self, p):
"""Capture details of the modeling protocol.
@param p an instance of IMP.pmi.output.ProtocolOutput or a subclass.
"""
self._protocol_output.append(p)
# p._each_metadata.append(self._metadata)
# p._file_datasets.append(self._file_dataset)
for state in self.states:
state._add_protocol_output(p, self)


#------------------------

class State(_SystemBase):
Expand All @@ -157,14 +169,24 @@ def __init__(self,system,state_index):
self.mdl = system.get_hierarchy().get_model()
self.system = system
self.hier = self._create_child(system.get_hierarchy())
self.hier.set_name("State_"+str(state_index))
self.short_name = self.long_name = "State_"+str(state_index)
self.hier.set_name(self.short_name)
self.molecules = defaultdict(list) # key is molecule name. value are the molecule copies!
IMP.atom.State.setup_particle(self.hier,state_index)
self.built = False
self._protocol_output = []
for p in system._protocol_output:
self._add_protocol_output(p, system)

def __repr__(self):
return self.system.__repr__()+'.'+self.hier.get_name()

def _add_protocol_output(self, p, system):
state = p._add_state(self)
self._protocol_output.append((p, state))
state.m = system.mdl
state.prot = self.hier

def get_molecules(self):
"""Return a dictionary where key is molecule name and value
are the list of all copies of that molecule in setup order"""
Expand Down Expand Up @@ -553,6 +575,9 @@ def add_representation(self,
ideal_helix,
color))

def _all_protocol_output(self):
return self.state._protocol_output

def build(self):
"""Create all parts of the IMP hierarchy
including Atoms, Residues, and Fragments/Representations and, finally, Copies
Expand All @@ -562,6 +587,11 @@ def build(self):
from the PDB file
"""
if not self.built:
# Add molecule name and sequence to any ProtocolOutput objects
name = self.hier.get_name()
for po, state in self._all_protocol_output():
po.create_component(state, name, True)
po.add_component_sequence(state, name, self.sequence)
# if requested, clone structure and representations BEFORE building original
if self.mol_to_clone is not None:
for nr,r in enumerate(self.mol_to_clone.residues):
Expand Down
50 changes: 50 additions & 0 deletions test/test_mmcif_pmi2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from __future__ import print_function

import ihm.format
import IMP.test
import IMP.pmi.topology
import IMP.pmi.mmcif
import sys

if sys.version_info[0] >= 3:
from io import StringIO
else:
from io import BytesIO as StringIO

class DummyPO(IMP.pmi.mmcif.ProtocolOutput):
def flush(self):
pass

class Tests(IMP.test.TestCase):

def test_entity(self):
"""Test EntityDump with PMI2-style init"""
m = IMP.Model()
s = IMP.pmi.topology.System(m)
po = DummyPO(None)
s.add_protocol_output(po)
state = s.create_state()
nup84 = state.create_molecule("Nup84", "MELS", "A")
nup84.add_representation(resolutions=[1])
hier = s.build()
fh = StringIO()
w = ihm.format.CifWriter(fh)
d = ihm.dumper._EntityDumper()
d.finalize(po.system)
d.dump(po.system, w)
out = fh.getvalue()
self.assertEqual(out, """#
loop_
_entity.id
_entity.type
_entity.src_method
_entity.pdbx_description
_entity.formula_weight
_entity.pdbx_number_of_molecules
_entity.details
1 polymer man Nup84 ? 1 .
#
""")

if __name__ == '__main__':
IMP.test.main()

0 comments on commit 4436041

Please sign in to comment.