From 0b9f31b45e79fd5d3931894cf8e36ce1382cc7f1 Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Fri, 8 Jun 2018 12:58:51 +0200 Subject: [PATCH] Mod res support --- src/apps/structure-info/model.ts | 12 ++++ .../structure/model/formats/mmcif.ts | 17 ++++- .../model/formats/mmcif/modified-residues.ts | 65 +++++++++++++++++++ src/mol-model/structure/model/model.ts | 7 +- .../model/properties/atomic/hierarchy.ts | 4 +- 5 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 src/mol-model/structure/model/formats/mmcif/modified-residues.ts diff --git a/src/apps/structure-info/model.ts b/src/apps/structure-info/model.ts index 85f118dea..384758e57 100644 --- a/src/apps/structure-info/model.ts +++ b/src/apps/structure-info/model.ts @@ -126,6 +126,17 @@ export function printSequence(model: Model) { console.log(); } +export function printModRes(model: Model) { + console.log('\nModified Residues\n============='); + const { residueIndices, keys, data } = model.properties.modifiedResidues; + const { label_comp_id } = model.atomicHierarchy.residues; + for (let i = 0; i < residueIndices.length; i++) { + const rI = residueIndices[i], k = keys[i]; + console.log(`Idx: ${rI} ${data.parent_comp_id.value(k)} -> ${label_comp_id.value(rI)}`); + } + console.log(); +} + export function printRings(structure: Structure) { console.log('\nRings\n============='); for (const unit of structure.units) { @@ -186,6 +197,7 @@ async function run(mmcif: mmCIF_Database) { printUnits(structure); printRings(structure); printLinks(structure, false, true); + printModRes(models[0]); //printSecStructure(models[0]); } diff --git a/src/mol-model/structure/model/formats/mmcif.ts b/src/mol-model/structure/model/formats/mmcif.ts index a50d29311..7cbaedb7d 100644 --- a/src/mol-model/structure/model/formats/mmcif.ts +++ b/src/mol-model/structure/model/formats/mmcif.ts @@ -11,7 +11,7 @@ import { Vec3 } from 'mol-math/linear-algebra'; import UUID from 'mol-util/uuid'; import Format from '../format'; import Model from '../model'; -import { AtomicConformation, AtomicData, AtomicSegments, AtomsSchema, ChainsSchema, ResiduesSchema } from '../properties/atomic'; +import { AtomicConformation, AtomicData, AtomicSegments, AtomsSchema, ChainsSchema, ResiduesSchema, AtomicHierarchy } from '../properties/atomic'; import { Entities } from '../properties/common'; import { ModelSymmetry } from '../properties/symmetry'; import { getAtomicKeys } from '../properties/utils/atomic-keys'; @@ -23,6 +23,7 @@ import { getSequence } from './mmcif/sequence'; import mmCIF_Format = Format.mmCIF import { Task } from 'mol-task'; import { getSecondaryStructureMmCif } from './mmcif/secondary-structure'; +import { ModifiedResidues } from './mmcif/modified-residues'; function findModelBounds({ data }: mmCIF_Format, startIndex: number) { const num = data.atom_site.pdbx_PDB_model_num; @@ -118,6 +119,15 @@ function isHierarchyDataEqual(a: AtomicData, b: AtomicData) { && Table.areEqual(a.atoms as Table<AtomsSchema>, b.atoms as Table<AtomsSchema>) } +function modResProvider(format: mmCIF_Format, hierarchy: AtomicHierarchy, entities: Entities) { + let modres: ModifiedResidues | undefined = void 0; + return () => { + if (modres) return modres; + modres = new ModifiedResidues(format.data.pdbx_struct_mod_residue, hierarchy, entities); + return modres; + } +} + function createModel(format: mmCIF_Format, bounds: Interval, previous?: Model): Model { const hierarchyOffsets = findHierarchyOffsets(format, bounds); const hierarchyData = createHierarchyData(format, bounds, hierarchyOffsets); @@ -146,6 +156,8 @@ function createModel(format: mmCIF_Format, bounds: Interval, previous?: Model): ? format.data.entry.id.value(0) : format.data._name; + const modRes = modResProvider(format, atomicHierarchy, entities); + return { id: UUID.create(), label, @@ -158,7 +170,8 @@ function createModel(format: mmCIF_Format, bounds: Interval, previous?: Model): coarseHierarchy: coarse.hierarchy, coarseConformation: coarse.conformation, properties: { - secondaryStructure: getSecondaryStructureMmCif(format.data, atomicHierarchy) + secondaryStructure: getSecondaryStructureMmCif(format.data, atomicHierarchy), + get modifiedResidues() { return modRes() } }, symmetry: getSymmetry(format) }; diff --git a/src/mol-model/structure/model/formats/mmcif/modified-residues.ts b/src/mol-model/structure/model/formats/mmcif/modified-residues.ts new file mode 100644 index 000000000..64f182b2b --- /dev/null +++ b/src/mol-model/structure/model/formats/mmcif/modified-residues.ts @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif' +import { AtomicHierarchy } from '../../properties/atomic'; +import { Entities } from '../../properties/common'; + +class ModifiedResidues { + private residueIndexMap = new Map<number, number>(); + + /** All residue indices within the given model that are modified. */ + readonly residueIndices: ReadonlyArray<number>; + /** Indexed same as residueIndex and is key to ModifiedResidues.data table */ + readonly keys: ReadonlyArray<number>; + + /** Index into the data table. -1 if the residue is not modified. */ + getKey(residueIndex: number): number { + return this.residueIndexMap.has(residueIndex) ? this.residueIndexMap.get(residueIndex)! : -1; + } + + constructor(public data: mmCIF_Database['pdbx_struct_mod_residue'], hierarchy: AtomicHierarchy, entities: Entities) { + if (data._rowCount === 0) { + this.residueIndices = []; + this.keys = []; + return; + } + + const { PDB_ins_code, auth_seq_id, _rowCount } = data; + + const asym_id = data.label_asym_id.isDefined ? data.label_asym_id : data.auth_asym_id; + const comp_id = data.label_comp_id.isDefined ? data.label_comp_id : data.auth_comp_id; + + const entityIds = entities.data.id.toArray(); + + const residueIndices: number[] = []; + const keys: number[] = []; + + for (let i = 0; i < _rowCount; i++) { + const aId = asym_id.value(i); + const eIdx = getEntityId(hierarchy, entityIds, aId); + if (eIdx < 0) continue; + const key = hierarchy.findResidueKey(entityIds[eIdx], aId, comp_id.value(i), auth_seq_id.value(i), PDB_ins_code.value(i)); + if (key >= 0) { + this.residueIndexMap.set(key, i); + residueIndices[residueIndices.length] = key; + keys[keys.length] = i; + } + } + + this.residueIndices = residueIndices; + this.keys = keys; + } +} + +function getEntityId(hierarchy: AtomicHierarchy, ids: ArrayLike<string>, asym_id: string) { + for (let i = 0, _i = ids.length; i < _i; i++) { + if (hierarchy.findChainKey(ids[i], asym_id) >= 0) return i; + } + return -1; +} + +export { ModifiedResidues } \ No newline at end of file diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts index a20eaf796..8f60531e6 100644 --- a/src/mol-model/structure/model/model.ts +++ b/src/mol-model/structure/model/model.ts @@ -15,6 +15,7 @@ import { SecondaryStructure } from './properties/seconday-structure'; //import from_gro from './formats/gro' import from_mmCIF from './formats/mmcif' +import { ModifiedResidues } from './formats/mmcif/modified-residues'; /** * Interface to the "source data" of the molecule. @@ -37,7 +38,11 @@ interface Model extends Readonly<{ atomicConformation: AtomicConformation, /** Various parts of the code can "cache" custom properties here */ - properties: { readonly secondaryStructure: SecondaryStructure } & { [customName: string]: any }, + properties: { + readonly secondaryStructure: SecondaryStructure, + readonly modifiedResidues: ModifiedResidues, + [customName: string]: any + }, coarseHierarchy: CoarseHierarchy, coarseConformation: CoarseConformation diff --git a/src/mol-model/structure/model/properties/atomic/hierarchy.ts b/src/mol-model/structure/model/properties/atomic/hierarchy.ts index fe50e990b..4327d22d4 100644 --- a/src/mol-model/structure/model/properties/atomic/hierarchy.ts +++ b/src/mol-model/structure/model/properties/atomic/hierarchy.ts @@ -65,7 +65,9 @@ export interface AtomicKeys { // also index to the Entities table. entityKey: ArrayLike<number>, - findChainKey(entityId: string, label_asym_id: string): number + findChainKey(entityId: string, label_asym_id: string): number, + + /** Unique number for each of the residue. Also the index of the 1st occurence of this residue. */ findResidueKey(entityId: string, label_asym_id: string, label_comp_id: string, auth_seq_id: number, pdbx_PDB_ins_code: string): number } -- GitLab