diff options
| author | Steffen Moeller <moeller@debian.org> | 2025-10-03 16:07:11 +0100 |
|---|---|---|
| committer | git-ubuntu importer <ubuntu-devel-discuss@lists.ubuntu.com> | 2025-10-28 22:36:18 +0000 |
| commit | 8ed8247f0973f900898167c0eebf94825504b2ce (patch) | |
| tree | 9ab3f04e1ae81543bbd7fdce37786a2cc34f35c7 /examples | |
1.5-1 (patches unapplied)HEADimport/1.5-1ubuntu/resolute-proposedubuntu/resolute-develubuntu/resoluteubuntu/develdebian/sid
Imported using git-ubuntu import.
Notes
Notes:
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/README.md | 24 | ||||
| -rw-r--r-- | examples/associated.py | 50 | ||||
| -rw-r--r-- | examples/convert_bcif.py | 15 | ||||
| -rw-r--r-- | examples/input/ligands.cif | 323 | ||||
| -rw-r--r-- | examples/ligands.py | 98 | ||||
| -rw-r--r-- | examples/mkmodbase.py | 216 | ||||
| -rwxr-xr-x | examples/validate_mmcif.py | 27 | ||||
| -rw-r--r-- | examples/validate_modbase.py | 36 |
8 files changed, 789 insertions, 0 deletions
diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..6e8b2b9 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,24 @@ +These examples demonstrate some of the functionality of the python-modelcif +software. See [the documentation](https://python-modelcif.readthedocs.io/) +for more information. + + - [convert\_bcif.py](convert_bcif.py) converts an mmCIF file containing + ModelCIF data to [BinaryCIF](https://github.com/molstar/BinaryCIF) format. + - [ligands.py](ligands.py) demonstrates writing an mmCIF file of a typical + single-template homology or comparative model, including a ligand. + - [mkmodbase.py](mkmodbase.py) demonstrates using the library to make an mmCIF + file of a typical single-template single-chain homology or comparative + model, similar to those generated by + [MODELLER](https://salilab.org/modeller/) and deposited in the + [ModBase database](https://modbase.compbio.ucsf.edu/). + - [associated.py](associated.py) demonstrates reading a deposition from + [ModelArchive](https://www.modelarchive.org/) that has been split into + multiple mmCIF files. + - [validate\_mmcif.py](validate_mmcif.py) demonstrates the use of the + library's validator to validate a user-provided input mmCIF file against the + [ModelCIF dictionary](https://mmcif.wwpdb.org/dictionaries/mmcif_ma.dic/Index/). + - [validate\_modbase.py](validate_modbase.py) demonstrates downloading an + mmCIF structure from the [ModBase database](https://modbase.compbio.ucsf.edu/) + and validating it against the + [ModelCIF dictionary](https://mmcif.wwpdb.org/dictionaries/mmcif_ma.dic/Index/) + for compliance. diff --git a/examples/associated.py b/examples/associated.py new file mode 100644 index 0000000..00ae002 --- /dev/null +++ b/examples/associated.py @@ -0,0 +1,50 @@ +# This example demonstrates reading ModelCIF "associated" files. +# Some repositories, such as ModelArchive, split the deposited data into +# multiple mmCIF files, putting some of the quality score information not +# into the main mmCIF file but into a separate "associated" file. +# These associated files are referenced in the main file +# (see System.repositories) so we can programmatically download and +# extract them. +# This example requires Python 3. + +import urllib.request +import zipfile +import tempfile +import shutil +import modelcif.reader + + +# Get any associated files containing pairwise QA scores +def _get_zip_scores_files(s): + for repo in s.repositories: + for f in repo.files: + if isinstance(f, modelcif.associated.ZipFile): + for zf in f.files: + if isinstance( + zf, modelcif.associated.QAMetricsFile): + yield zf, f, repo + + +# Download entry ma-bak-cepc-0944 directly from ModelArchive +url = "https://www.modelarchive.org/doi/10.5452/ma-bak-cepc-0944.cif" +with urllib.request.urlopen(url) as fh: + s, = modelcif.reader.read(fh) + + +# Get any referenced associated files containing QA scores. For ModelArchive, +# these are stored in an mmCIF file that is then compressed into a zip file +for scores, archive, repo in _get_zip_scores_files(s): + url = repo.get_url(archive) + # Download the referenced zip file directly from ModelArchive + with urllib.request.urlopen(repo.get_url(archive)) as f_url: + with tempfile.NamedTemporaryFile() as f_zip: + shutil.copyfileobj(f_url, f_zip) + # Extract the scores file from the zip file + with zipfile.ZipFile(f_zip) as zf: + with zf.open(scores.path) as f_scores: + # Add scores in the file to our existing System + modelcif.reader.read(f_scores, add_to_system=s) + +for mg in s.model_groups: + for m in mg: + print("Model %s contains %d QA metrics" % (m, len(m.qa_metrics))) diff --git a/examples/convert_bcif.py b/examples/convert_bcif.py new file mode 100644 index 0000000..2e109f8 --- /dev/null +++ b/examples/convert_bcif.py @@ -0,0 +1,15 @@ +# This example demonstrates using the library to convert an mmCIF file +# containing ModelCIF data to BinaryCIF format. + +# Import used classes. +import modelcif +import modelcif.dumper +import modelcif.reader + +# Read in an existing mmCIF file: +with open('input/ligands.cif') as fh: + systems = modelcif.reader.read(fh, format='mmCIF') + +# Write a new BinaryCIF file containing the same data: +with open('ligands.bcif', 'wb') as fh: + modelcif.dumper.write(fh, systems, format='BCIF') diff --git a/examples/input/ligands.cif b/examples/input/ligands.cif new file mode 100644 index 0000000..782c305 --- /dev/null +++ b/examples/input/ligands.cif @@ -0,0 +1,323 @@ +data_model +_entry.id model +_struct.entry_id model +_struct.pdbx_model_details . +_struct.pdbx_structure_determination_methodology computational +_struct.title 'Ligand example' +_audit_conform.dict_location https://raw.githubusercontent.com/ihmwg/ModelCIF/557bda7/base/mmcif_ma-core.dic +_audit_conform.dict_name mmcif_ma.dic +_audit_conform.dict_version 1.4.1 +# +loop_ +_chem_comp.id +_chem_comp.type +_chem_comp.name +_chem_comp.formula +_chem_comp.formula_weight +_chem_comp.ma_provenance +ALA 'L-peptide linking' ALANINE 'C3 H7 N O2' 89.094 'CCD Core' +ASN 'L-peptide linking' ASPARAGINE 'C4 H8 N2 O3' 132.119 'CCD Core' +ASP 'L-peptide linking' 'ASPARTIC ACID' 'C4 H7 N O4' 133.103 'CCD Core' +CYS 'L-peptide linking' CYSTEINE 'C3 H7 N O2 S' 121.154 'CCD Core' +ILE 'L-peptide linking' ISOLEUCINE 'C6 H13 N O2' 131.175 'CCD Core' +LYS 'L-peptide linking' LYSINE 'C6 H15 N2 O2 1' 147.198 'CCD Core' +PHE 'L-peptide linking' PHENYLALANINE 'C9 H11 N O2' 165.192 'CCD Core' +SER 'L-peptide linking' SERINE 'C3 H7 N O3' 105.093 'CCD Core' +SF4 non-polymer 'IRON/SULFUR CLUSTER' 'Fe4 S4' 351.620 'CCD Core' +THR 'L-peptide linking' THREONINE 'C4 H9 N O3' 119.120 'CCD Core' +TYR 'L-peptide linking' TYROSINE 'C9 H11 N O3' 181.191 'CCD Core' +VAL 'L-peptide linking' VALINE 'C5 H11 N O2' 117.148 'CCD Core' +# +# +loop_ +_entity.id +_entity.type +_entity.src_method +_entity.pdbx_description +_entity.formula_weight +_entity.pdbx_number_of_molecules +_entity.details +1 polymer man 'Model subunit' 1230.346 1 . +2 non-polymer man 'IRON/SULFUR CLUSTER' 351.620 1 . +# +# +loop_ +_entity_poly.entity_id +_entity_poly.type +_entity_poly.nstd_linkage +_entity_poly.nstd_monomer +_entity_poly.pdbx_strand_id +_entity_poly.pdbx_seq_one_letter_code +_entity_poly.pdbx_seq_one_letter_code_can +1 polypeptide(L) no no A AYVINDSCIA AYVINDSCIA +# +# +loop_ +_pdbx_entity_nonpoly.entity_id +_pdbx_entity_nonpoly.name +_pdbx_entity_nonpoly.comp_id +_pdbx_entity_nonpoly.ma_model_mode +2 'IRON/SULFUR CLUSTER' SF4 implicit +# +# +loop_ +_entity_poly_seq.entity_id +_entity_poly_seq.num +_entity_poly_seq.mon_id +_entity_poly_seq.hetero +1 1 ALA . +1 2 TYR . +1 3 VAL . +1 4 ILE . +1 5 ASN . +1 6 ASP . +1 7 SER . +1 8 CYS . +1 9 ILE . +1 10 ALA . +# +# +loop_ +_struct_asym.id +_struct_asym.entity_id +_struct_asym.details +A 1 'Model subunit A' +B 2 'Model subunit B' +# +# +loop_ +_pdbx_poly_seq_scheme.asym_id +_pdbx_poly_seq_scheme.entity_id +_pdbx_poly_seq_scheme.seq_id +_pdbx_poly_seq_scheme.mon_id +_pdbx_poly_seq_scheme.pdb_seq_num +_pdbx_poly_seq_scheme.auth_seq_num +_pdbx_poly_seq_scheme.pdb_mon_id +_pdbx_poly_seq_scheme.auth_mon_id +_pdbx_poly_seq_scheme.pdb_strand_id +_pdbx_poly_seq_scheme.pdb_ins_code +A 1 1 ALA 1 1 ALA ALA A . +A 1 2 TYR 2 2 TYR TYR A . +A 1 3 VAL 3 3 VAL VAL A . +A 1 4 ILE 4 4 ILE ILE A . +A 1 5 ASN 5 5 ASN ASN A . +A 1 6 ASP 6 6 ASP ASP A . +A 1 7 SER 7 7 SER SER A . +A 1 8 CYS 8 8 CYS CYS A . +A 1 9 ILE 9 9 ILE ILE A . +A 1 10 ALA 10 10 ALA ALA A . +# +# +loop_ +_pdbx_nonpoly_scheme.asym_id +_pdbx_nonpoly_scheme.entity_id +_pdbx_nonpoly_scheme.mon_id +_pdbx_nonpoly_scheme.ndb_seq_num +_pdbx_nonpoly_scheme.pdb_seq_num +_pdbx_nonpoly_scheme.auth_seq_num +_pdbx_nonpoly_scheme.auth_mon_id +_pdbx_nonpoly_scheme.pdb_strand_id +_pdbx_nonpoly_scheme.pdb_ins_code +B 2 SF4 1 1 1 SF4 B . +# +# +loop_ +_ma_data.id +_ma_data.name +_ma_data.content_type +_ma_data.content_type_other_details +1 'Template polymer' 'template structure' . +2 'Template ligand' 'template structure' . +3 'Model subunit' target . +4 'IRON/SULFUR CLUSTER' target . +5 'Modeling alignment' 'target-template alignment' . +6 'Best scoring model' 'model coordinates' . +# +# +loop_ +_ma_data_group.ordinal_id +_ma_data_group.group_id +_ma_data_group.data_id +1 1 5 +2 2 6 +# +# +loop_ +_ma_target_entity.entity_id +_ma_target_entity.data_id +_ma_target_entity.origin +1 3 designed +2 4 designed +# +# +loop_ +_ma_target_entity_instance.asym_id +_ma_target_entity_instance.entity_id +_ma_target_entity_instance.details +A 1 'Model subunit A' +B 2 'Model subunit B' +# +# +loop_ +_ma_template_trans_matrix.id +_ma_template_trans_matrix.rot_matrix[1][1] +_ma_template_trans_matrix.rot_matrix[2][1] +_ma_template_trans_matrix.rot_matrix[3][1] +_ma_template_trans_matrix.rot_matrix[1][2] +_ma_template_trans_matrix.rot_matrix[2][2] +_ma_template_trans_matrix.rot_matrix[3][2] +_ma_template_trans_matrix.rot_matrix[1][3] +_ma_template_trans_matrix.rot_matrix[2][3] +_ma_template_trans_matrix.rot_matrix[3][3] +_ma_template_trans_matrix.tr_vector[1] +_ma_template_trans_matrix.tr_vector[2] +_ma_template_trans_matrix.tr_vector[3] +1 1.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 +1.000000 0 0 0 +# +# +loop_ +_ma_template_details.ordinal_id +_ma_template_details.template_id +_ma_template_details.template_origin +_ma_template_details.template_entity_type +_ma_template_details.template_trans_matrix_id +_ma_template_details.template_data_id +_ma_template_details.target_asym_id +_ma_template_details.template_label_asym_id +_ma_template_details.template_label_entity_id +_ma_template_details.template_model_num +_ma_template_details.template_auth_asym_id +1 1 'reference database' polymer 1 1 A A . 1 A +2 2 'reference database' non-polymer 1 2 B B . 1 B +# +# +loop_ +_ma_template_poly.template_id +_ma_template_poly.seq_one_letter_code +_ma_template_poly.seq_one_letter_code_can +1 AFVVTDNCIKCK AFVVTDNCIKCK +# +# +loop_ +_ma_template_poly_segment.id +_ma_template_poly_segment.template_id +_ma_template_poly_segment.residue_number_begin +_ma_template_poly_segment.residue_number_end +1 1 1 12 +# +# +loop_ +_ma_template_non_poly.template_id +_ma_template_non_poly.comp_id +_ma_template_non_poly.details +2 SF4 'IRON/SULFUR CLUSTER' +# +# +loop_ +_ma_template_ref_db_details.template_id +_ma_template_ref_db_details.db_name +_ma_template_ref_db_details.db_name_other_details +_ma_template_ref_db_details.db_accession_code +_ma_template_ref_db_details.db_version_date +1 PDB . 5fd1 . +2 PDB . 5fd1 . +# +# +loop_ +_ma_target_template_poly_mapping.id +_ma_target_template_poly_mapping.template_segment_id +_ma_target_template_poly_mapping.target_asym_id +_ma_target_template_poly_mapping.target_seq_id_begin +_ma_target_template_poly_mapping.target_seq_id_end +1 1 A 1 10 +# +# +loop_ +_ma_alignment_info.alignment_id +_ma_alignment_info.data_id +_ma_alignment_info.software_group_id +_ma_alignment_info.alignment_length +_ma_alignment_info.alignment_type +_ma_alignment_info.alignment_mode +1 5 . 12 'target-template pairwise alignment' global +# +# +loop_ +_ma_alignment_details.ordinal_id +_ma_alignment_details.alignment_id +_ma_alignment_details.template_segment_id +_ma_alignment_details.target_asym_id +_ma_alignment_details.score_type +_ma_alignment_details.score_type_other_details +_ma_alignment_details.score_value +_ma_alignment_details.percent_sequence_identity +_ma_alignment_details.sequence_identity_denominator +_ma_alignment_details.sequence_identity_denominator_other_details +1 1 1 A 'BLAST e-value' . 1e-15 45.000 'Length of the shorter sequence' . +# +# +loop_ +_ma_alignment.ordinal_id +_ma_alignment.alignment_id +_ma_alignment.target_template_flag +_ma_alignment.sequence +1 1 1 AYVINDSC--IA +2 1 2 AFVVTDNCIKCK +# +# +loop_ +_ma_protocol_step.ordinal_id +_ma_protocol_step.protocol_id +_ma_protocol_step.step_id +_ma_protocol_step.method_type +_ma_protocol_step.step_name +_ma_protocol_step.details +_ma_protocol_step.software_group_id +_ma_protocol_step.input_data_group_id +_ma_protocol_step.output_data_group_id +1 1 1 modeling . . . 1 2 +# +# +loop_ +_ma_model_list.ordinal_id +_ma_model_list.model_id +_ma_model_list.model_group_id +_ma_model_list.model_name +_ma_model_list.model_group_name +_ma_model_list.data_id +_ma_model_list.model_type +_ma_model_list.model_type_other_details +1 1 1 'Best scoring model' 'All models' 6 'Homology model' . +# +# +loop_ +_atom_site.group_PDB +_atom_site.id +_atom_site.type_symbol +_atom_site.label_atom_id +_atom_site.label_alt_id +_atom_site.label_comp_id +_atom_site.label_seq_id +_atom_site.auth_seq_id +_atom_site.pdbx_PDB_ins_code +_atom_site.label_asym_id +_atom_site.Cartn_x +_atom_site.Cartn_y +_atom_site.Cartn_z +_atom_site.occupancy +_atom_site.label_entity_id +_atom_site.auth_asym_id +_atom_site.B_iso_or_equiv +_atom_site.pdbx_PDB_model_num +ATOM 1 C CA . ALA 1 1 ? A 1.000 2.000 3.000 . 1 A . 1 +ATOM 2 C CA . TYR 2 2 ? A 4.000 5.000 6.000 . 1 A . 1 +ATOM 3 C CA . VAL 3 3 ? A 7.000 8.000 9.000 . 1 A . 1 +HETATM 4 FE FE . SF4 . 1 ? B 10.000 10.000 10.000 . 2 B . 1 +# +# +loop_ +_atom_type.symbol +C +FE +# diff --git a/examples/ligands.py b/examples/ligands.py new file mode 100644 index 0000000..244961e --- /dev/null +++ b/examples/ligands.py @@ -0,0 +1,98 @@ +# This example demonstrates writing an mmCIF file of a typical +# single-template homology or comparative model, including a ligand. +# +# This is very similar to the mkmodbase.py example; see that example for +# more details. + +# Import used classes +import modelcif +import modelcif.model +import modelcif.dumper +import modelcif.reference +import modelcif.protocol +import modelcif.alignment +from modelcif.alignment import ShorterSequenceIdentity as SequenceIdentity +import ihm + +system = modelcif.System(title='Ligand example') + +# Describe the amino acid (polymer) sequences as Entity objects, for both +# template and model: +template_e = modelcif.Entity('AFVVTDNCIKCK', description='Template subunit') +model_e = modelcif.Entity('AYVINDSCIA', description='Model subunit') + +# For non-polymers (e.g. ligands) we need to describe the chemistry of the +# ligand as a chemical component object, then create an Entity using that +# component. We only need to do this once because the ligand is the same +# in both template and model: +sf4 = ihm.NonPolymerChemComp("SF4", name='IRON/SULFUR CLUSTER', + formula='Fe4 S4') +ligand_e = modelcif.Entity([sf4], description='IRON/SULFUR CLUSTER') + +# Create a Template for each chain (amino acids in chain A, ligand in chain B) +# and point to the original PDB, 5fd1: +s = modelcif.reference.PDB('5fd1') +templateA = modelcif.Template( + entity=template_e, asym_id='A', model_num=1, name="Template polymer", + transformation=modelcif.Transformation.identity(), references=[s]) +templateB = modelcif.Template( + entity=ligand_e, asym_id='B', model_num=1, name='Template ligand', + transformation=modelcif.Transformation.identity(), references=[s]) + +# Define the model assembly, as two AsymUnits. NonPolymerFromTemplate is a +# subclass of AsymUnit that additionally notes the Template from which it +# was derived. In this case we state that the ligand was simply copied from +# the template into the target (explicit=False): +asymA = modelcif.AsymUnit(model_e, details='Model subunit A', id='A') +asymB = modelcif.NonPolymerFromTemplate(template=templateB, explicit=False, + details='Model subunit B', id='B') +modeled_assembly = modelcif.Assembly((asymA, asymB), name='Modeled assembly') + + +# For the amino acid chain, add the modeling alignment, just as in the +# mkmodbase.py example: +class Alignment(modelcif.alignment.Global, modelcif.alignment.Pairwise): + pass + + +p = modelcif.alignment.Pair( + template=templateA.segment("AFVVTDNCIKCK", 1, 12), + target=asymA.segment("AYVINDSC--IA", 1, 10), + score=modelcif.alignment.BLASTEValue(1e-15), + identity=SequenceIdentity(45.0)) +aln = Alignment(name="Modeling alignment", + pairs=[p]) +system.alignments.append(aln) + +# Add model coordinates, similarly to the mkmodbase.py example. +# Note that nonpolymers are not "sequences" and so seq_id=None. +atoms = [('A', 1, 'C', 'CA', 1., 2., 3.), + ('A', 2, 'C', 'CA', 4., 5., 6.), + ('A', 3, 'C', 'CA', 7., 8., 9.), + ('B', None, 'FE', 'FE', 10., 10., 10.)] + + +class MyModel(modelcif.model.HomologyModel): + asym_unit_map = {'A': asymA, 'B': asymB} + + def get_atoms(self): + for asym, seq_id, type_symbol, atom_id, x, y, z in atoms: + yield modelcif.model.Atom( + asym_unit=self.asym_unit_map[asym], type_symbol=type_symbol, + seq_id=seq_id, atom_id=atom_id, x=x, y=y, z=z, + het=seq_id is None) + + +# Add the model and modeling protocol to the file and write them out: +model = MyModel(assembly=modeled_assembly, name='Best scoring model') + +model_group = modelcif.model.ModelGroup([model], name='All models') +system.model_groups.append(model_group) + +protocol = modelcif.protocol.Protocol() +protocol.steps.append(modelcif.protocol.ModelingStep( + input_data=aln, output_data=model)) +system.protocols.append(protocol) + +with open('output.cif', 'w') as fh: + modelcif.dumper.write(fh, [system]) diff --git a/examples/mkmodbase.py b/examples/mkmodbase.py new file mode 100644 index 0000000..780b535 --- /dev/null +++ b/examples/mkmodbase.py @@ -0,0 +1,216 @@ +# This example demonstrates using the library to make an mmCIF file of a +# typical single-template single-chain homology or comparative model, similar +# to those generated by MODELLER (https://salilab.org/modeller/) and deposited +# in the ModBase database (https://modbase.compbio.ucsf.edu/) + +# For a more complete (but less documented) script to convert a complete +# ModBase PDB file into a corresponding mmCIF or BinaryCIF file, see +# https://github.com/salilab/modbase_utils/blob/main/modbase_pdb_to_cif.py + +# Import used classes. +import modelcif +import modelcif.protocol +import modelcif.model +import modelcif.dumper +import modelcif.reference +import modelcif.qa_metric +import modelcif.alignment +# Different methods measure "sequence identity" in different ways, so import +# the class that matches the way Modeller understands it (number of identical +# aligned residues, divided by the length of the shorter sequence) +from modelcif.alignment import ShorterSequenceIdentity as SequenceIdentity +import ihm.citations +import modelcif.reader + +# First, we create a system, which contains everything we know about the +# modeling. A single mmCIF file can contain multiple Systems, but in most +# cases we use just one: +system = modelcif.System(title='S54091 hypothetical protein YPR070w') + +# List the authors of this file (here these are the ModBase authors) +system.authors.extend(('Pieper U', 'Webb B', 'Narayanan E', 'Sali A')) + +# Describe the software that was used in the modeling +modpipe_software = modelcif.Software( + name='ModPipe', classification='comparative modeling', + location='https://salilab.org/modpipe/', type='program', + version='SVN.r1703', description='Comparative modeling pipeline') +# Every object we create must ultimately be linked to the System, which +# maintains simple lists for each type of object. For example, there is a +# list system.software (like system.authors above) which can be used for +# any Software object not referenced by any other object. But in this case +# we're going to use these Software objects further on in the script, so +# don't need to explicitly add them here. + +modeller_software = modelcif.Software( + name='MODELLER', classification='comparative modeling', + location='https://salilab.org/modeller/', type='program', + version='SVN', citation=ihm.citations.modeller, + description='Comparative modeling by satisfaction of spatial restraints') + +# Next, we define "entities", unique sequences in the system, as Entity +# objects. First, the template sequence: +template_e = modelcif.Entity('DMACDTFIKCC', description='Template subunit') + +# Next, the target (model) sequence, together with a link to the reference +# sequence (in UniProt): +s = modelcif.reference.UniProt(code='MED1_YEAST', accession='Q12321', + sequence='DSYVETLDCC') +model_e = modelcif.Entity('DSYVETLDCC', description='Model subunit', + references=[s]) + +# Next, we define asymmetric units for everything we modeled. +# These roughly correspond to chains in a traditional PDB file. Multiple +# asymmetric units may map to the same entity (for example if there are +# several copies of a given protein). +asymA = modelcif.AsymUnit(model_e, details='Model subunit A', id='A') + +# Next, we group asymmetric units into assemblies. +modeled_assembly = modelcif.Assembly((asymA,), name='Modeled assembly') + +# In a similar fashion, we declare a Template for each chain that we used +# as a template structure, with a link to the reference structure database +# (PDB). +s = modelcif.reference.PDB('3nc1') +template = modelcif.Template( + entity=template_e, asym_id='A', model_num=1, name="Template Structure", + transformation=modelcif.Transformation.identity(), + references=[s]) + + +# Now, we describe the alignment between target and template. +# python-ma provides various subclasses to use here. All ModBase structures +# use a simple pairwise global alignment between target and template, so +# declare a suitable class: +class Alignment(modelcif.alignment.Global, modelcif.alignment.Pairwise): + pass + + +# An alignment consists of a list of aligned target-template segments. +# Here we provide the residue ranges and the actual alignment, including gaps, +# between the two, together with the sequence identity and any score available +# for the alignment (here we have the BLAST e-value): +p = modelcif.alignment.Pair( + template=template.segment("DMACDTFIK", 1, 9), + target=asymA.segment("DSYV-ETLD", 1, 8), + score=modelcif.alignment.BLASTEValue(1e-15), + identity=SequenceIdentity(45.0)) +aln = Alignment(name="Modeling alignment", software=modpipe_software, + pairs=[p]) +# Alignments aren't used by any objects; they should be added directly +# to the System: +system.alignments.append(aln) + +# For the actual model coordinates, we must subclass a suitable class and +# override the get_atoms() method to return a list of Atom objects. This design +# avoids having a separate copy of every atom in memory. +# Modeller models are comparative or homology models, so we subclass +# HomologyModel. For the purposes of this example, we just return a simple +# static list of atoms: + +atoms = [('A', 1, 'C', 'CA', 1., 2., 3.), + ('A', 2, 'C', 'CA', 4., 5., 6.), + ('A', 3, 'C', 'CA', 7., 8., 9.), + ('A', 4, 'C', 'CA', 10., 11., 12.)] + + +class MyModel(modelcif.model.HomologyModel): + # Map our asym unit names to ModelCIF asym_unit objects: + asym_unit_map = {'A': asymA} + + def get_atoms(self): + for asym, seq_id, type_symbol, atom_id, x, y, z in atoms: + yield modelcif.model.Atom( + asym_unit=self.asym_unit_map[asym], type_symbol=type_symbol, + seq_id=seq_id, atom_id=atom_id, x=x, y=y, z=z) + + +# Link the model to the Assembly that describes all subunits +model = MyModel(assembly=modeled_assembly, name='Best scoring model') + +# Next, we describe the modeling protocol: +protocol = modelcif.protocol.Protocol() +protocol.steps.append(modelcif.protocol.TemplateSearchStep( + name='ModPipe Seq-Prf (0001)', software=modpipe_software, + input_data=model_e, output_data=aln)) +protocol.steps.append(modelcif.protocol.ModelingStep( + software=modeller_software, input_data=aln, output_data=model)) +protocol.steps.append(modelcif.protocol.ModelSelectionStep( + software=modpipe_software, input_data=model, output_data=model)) +# Protocols aren't used by any other objects; they should be added directly +# to the System: +system.protocols.append(protocol) + + +# We can also attach quality scores to our model(s). To do this we must +# first define the scores by creating subclasses using a MetricMode +# (e.g. global, per-residue) and a MetricType (e.g. distance, z-score). +# Here we define the quality scores used by the ModPipe pipeline that is used +# by ModBase. Note that one score (MPQS) uses a custom metric type, while +# another (zDOPE) is a simple global z-score: +class MPQSMetricType(modelcif.qa_metric.MetricType): + """composite score, values >1.1 are considered reliable""" + + +class MPQS(modelcif.qa_metric.Global, MPQSMetricType): + """ModPipe Quality Score""" + software = modpipe_software + + +class zDOPE(modelcif.qa_metric.Global, modelcif.qa_metric.ZScore): + """Normalized DOPE""" + software = modeller_software + + +class TSVModRMSD(modelcif.qa_metric.Global, modelcif.qa_metric.Distance): + """TSVMod predicted RMSD (MSALL)""" + software = None + + +class TSVModNO35(modelcif.qa_metric.Global, + modelcif.qa_metric.NormalizedScore): + """TSVMod predicted native overlap (MSALL)""" + software = None + + +# Add qa metrics to the model +model.qa_metrics.extend((MPQS(0.853452), zDOPE(0.31), TSVModRMSD(12.996), + TSVModNO35(0.143))) + +# All ModBase QA metrics are global, but the library also supports per-residue +# or pairwise (between two residues) scores. Here's a fictional example for a +# z-score on the 4th residue of the first chain in the model, and a distance +# score between the 1st and 3rd residues: + + +class SomeLocalScore(modelcif.qa_metric.Local, modelcif.qa_metric.ZScore): + """A per-residue z-score""" + software = None + + +class SomePairScore(modelcif.qa_metric.LocalPairwise, + modelcif.qa_metric.Distance): + """A distance score between two residues""" + software = None + + +model.qa_metrics.append(SomeLocalScore(asymA.residue(4), -0.1)) +model.qa_metrics.append(SomePairScore(asymA.residue(1), asymA.residue(3), 1.0)) + +# Models should be grouped together using ModelGroup and then added to the +# top-level System. Here we only have a single model in the group: +model_group = modelcif.model.ModelGroup([model], name='All models') +system.model_groups.append(model_group) + +# Once the system is complete, we can write it out to an mmCIF file: +with open('output.cif', 'w') as fh: + modelcif.dumper.write(fh, [system]) + +# We can also *read* an mmCIF file and create a set of Python objects from it. +# Here we read in the file we just created: +with open('output.cif') as fh: + s, = modelcif.reader.read(fh) +for t in s.templates: + print(t.name, "-".join(c.id for c in t.entity.sequence)) +for e in s.entities: + print(e.description, "-".join(c.id for c in e.sequence)) diff --git a/examples/validate_mmcif.py b/examples/validate_mmcif.py new file mode 100755 index 0000000..32a5fba --- /dev/null +++ b/examples/validate_mmcif.py @@ -0,0 +1,27 @@ +# This example demonstrates the use of the Python IHM library's validator +# to validate a user-provided input mmCIF file. +# See also validate_modbase.py for a more detailed example. + +import sys +import ihm.dictionary +import urllib.request + + +if len(sys.argv) != 2: + print("Usage: %s input.cif" % sys.argv[0], file=sys.stderr) + sys.exit(1) +fname = sys.argv[1] + +# Read in the ModelCIF and PDBx dictionary from https://mmcif.wwpdb.org/ +with urllib.request.urlopen( + 'https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_ma.dic') as fh: + pdbx_mcif = ihm.dictionary.read(fh) + +# Validate the mmCIF file assuming it is UTF8 encoded. +# See validate_modbase.py for code to fallback to ASCII for non-UTF8 files. +with open(fname, encoding='UTF-8') as fh: + pdbx_mcif.validate(fh) + +# Similarly, to validate a BinaryCIF file, use: +# with open(fname, 'rb') as fh: +# pdbx_mcif.validate(fh, format='BCIF') diff --git a/examples/validate_modbase.py b/examples/validate_modbase.py new file mode 100644 index 0000000..1c80531 --- /dev/null +++ b/examples/validate_modbase.py @@ -0,0 +1,36 @@ +# This example demonstrates the use of the Python IHM library's validator. +# A structure is downloaded from the ModBase database and checked against +# the ModelCIF dictionary for compliance. This validator can be used +# to perform basic integrity checking against any mmCIF dictionary. +# See also validate_mmcif.py for a simpler script to validate a +# user-provided mmCIF file. + +import io +import ihm.dictionary +import urllib.request + +# Read in the ModelCIF dictionary from wwPDB as a Dictionary object. +# Note that the ModelCIF dictionary also includes the PDBx dictionary, +# so we don't need to read that in separately +fh = urllib.request.urlopen( + 'https://mmcif.wwpdb.org/dictionaries/ascii/mmcif_ma.dic') +pdbx_mc = ihm.dictionary.read(fh) +fh.close() + +# Validate a structure against PDBx+ModelCIF. +# A correct structure here should result in no output; an invalid structure +# will result in a ValidatorError Python exception. +# Here, a structure from ModBase (which should be valid) is used. +acc = 'P21812' +cif = urllib.request.urlopen('https://salilab.org/modbase/retrieve' + '?databaseID=%s&format=mmcif' % acc).read() + +# The encoding for mmCIF files isn't strictly defined, so first try UTF-8 +# and if that fails, strip out any non-ASCII characters. This ensures that +# we handle accented characters in string fields correctly. +try: + fh = io.StringIO(cif.decode('utf-8')) +except UnicodeDecodeError: + fh = io.StringIO(cif.decode('ascii', errors='ignore')) + +pdbx_mc.validate(fh) |
