ihmwg / python-modelcif

Python package for handling ModelCIF mmCIF and BinaryCIF files
MIT License
10 stars 1 forks source link

Add support for templates not deposited in PDB #1

Open benmwebb opened 2 years ago

benmwebb commented 2 years ago

Allow providing a PDB/mmCIF file for "customized" templates and fill out the ma_template_customized and ma_template_coord tables. Could maybe leverage some code from python-ihm that is used for starting models: startmodel.py

Likely low priority for now as structures in ModBase/SMR likely all have PDB templates, while most other structures have no templates at all.

bienchen commented 4 weeks ago

In SWISS-MODEL I wrote an additional dumper for user template uploads to have this:

class TemplateCustomized:
    """Base class for coordinates of custom templates.

    Use your own subclass with a `get_record_json` implementation, that yield
    atoms and template IDs.
    """

    # Written in the style of the original python-modelcif packae, Pylint
    # warnings about invalid names may be skipped.
    # pylint: disable=invalid-name
    def get_record_json(self):
        """Yield the template and a :class:`dict` that represents the
        template records.

        The dict needs following keys (and values):

        * group_PDB
        * type_symbol
        * label_atom_id
        * label_comp_id
        * label_seq_id
        * label_asym_id
        * Cartn_x
        * Cartn_y
        * Cartn_z
        * occupancy
        * label_entity_id
        * B_iso_or_equiv
        * pdb_model_num
        """
        raise NotImplementedError

    # pylint: enable=invalid-name

# pylint: enable=too-few-public-methods

class _TemplateCustomizedDumper(ihm.dumper.Dumper):
    """Write the _ma_template_customized category from a
    :class:`TemplateCustomized` object.
    """

    def dump(self, system, writer):
        """Actual writing happens here."""
        if len(system.custom_templates) == 0:
            return
        self.dump_ma_template_customize(system, writer)
        self.dump_ma_template_coord(system, writer)

    # Written in the style of the original python-modelcif packae, Pylint
    # warnings about invalid names may be skipped.
    # pylint: disable=invalid-name
    def dump_ma_template_customize(self, system, writer):
        """Write the _ma_template_customize category."""
        itl = ["template_id", "details"]
        with writer.loop("_ma_template_customized", itl) as lp:
            for templ in system.templates:
                # We have to access _id here like in the original
                # python-modelcif package.
                # pylint: disable=protected-access
                lp.write(template_id=templ._id, details="Provided by user")
                # pylint: enable=protected-access

    def dump_ma_template_coord(self, system, writer):
        """Write atom records for a customised template."""
        ordinal = itertools.count(1)
        itl = [
            "template_id",
            "group_PDB",
            "ordinal_id",
            "type_symbol",
            "label_atom_id",
            "label_comp_id",
            "label_seq_id",
            # auth_seq_id - skipped, not there in user templates
            "label_asym_id",
            "Cartn_x",
            "Cartn_y",
            "Cartn_z",
            "occupancy",
            "label_entity_id",
            # auth_asym_id - skipped, not there in user templates
            # auth_comp_id - skipped, not there in user templates
            "B_iso_or_equiv",
            # pdb_model_num - skipped
            # auth_atom_id - skipped, not there in user templates
            "formal_charge",
        ]
        with writer.loop("_ma_template_coord", itl) as lp:
            for custom_templ in system.custom_templates:
                for templ, atom in custom_templ.get_record_json():
                    # We have to access _id here like in the original
                    # python-modelcif package.
                    # pylint: disable=protected-access
                    lp.write(
                        template_id=templ._id, ordinal_id=next(ordinal), **atom
                    )
                    # pylint: enable=protected-access

    # pylint: enable=invalid-name