From dc59cb88dc4666260c7241f031cb7c7d7b5cd9b6 Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Thu, 5 Jul 2018 14:25:15 +0200 Subject: [PATCH] wip, seconadry structure export, updated sec. struct data model --- src/apps/structure-info/model.ts | 14 ++--- .../export/categories/secondary-structure.ts | 5 ++ src/mol-model/structure/export/mmcif.ts | 12 ++++ .../formats/mmcif/secondary-structure.ts | 58 +++++++++++++------ .../model/properties/seconday-structure.ts | 31 +++++++++- .../structure/structure/properties.ts | 2 +- 6 files changed, 90 insertions(+), 32 deletions(-) create mode 100644 src/mol-model/structure/export/categories/secondary-structure.ts diff --git a/src/apps/structure-info/model.ts b/src/apps/structure-info/model.ts index 17f1b6373..a4f8ddf66 100644 --- a/src/apps/structure-info/model.ts +++ b/src/apps/structure-info/model.ts @@ -13,8 +13,6 @@ import { Model, Structure, Element, Unit, Format, StructureProperties } from 'mo // import { Run, Progress } from 'mol-task' import { OrderedSet } from 'mol-data/int'; import { openCif, downloadCif } from './helpers'; -import { BitFlags } from 'mol-util'; -import { SecondaryStructureType } from 'mol-model/structure/model/types'; import { UnitRings } from 'mol-model/structure/structure/unit/rings'; import { Vec3 } from 'mol-math/linear-algebra'; @@ -51,21 +49,17 @@ export function residueLabel(model: Model, rI: number) { export function printSecStructure(model: Model) { console.log('\nSecondary Structure\n============='); const { residues } = model.atomicHierarchy; - const { type, key } = model.properties.secondaryStructure; + const { index, elements } = model.properties.secondaryStructure; const count = residues._rowCount; let rI = 0; while (rI < count) { let start = rI; - while (rI < count && key[start] === key[rI]) rI++; + while (rI < count && index[start] === index[rI]) rI++; rI--; - if (BitFlags.has(type[start], SecondaryStructureType.Flag.Beta)) { - console.log(`Sheet: ${residueLabel(model, start)} - ${residueLabel(model, rI)} (key ${key[start]})`); - } else if (BitFlags.has(type[start], SecondaryStructureType.Flag.Helix)) { - console.log(`Helix: ${residueLabel(model, start)} - ${residueLabel(model, rI)} (key ${key[start]})`); - } - + const e = elements[index[start]]; + if (e.kind !== 'none') console.log(`${e.kind}: ${residueLabel(model, start)} - ${residueLabel(model, rI)}`); rI++; } } diff --git a/src/mol-model/structure/export/categories/secondary-structure.ts b/src/mol-model/structure/export/categories/secondary-structure.ts new file mode 100644 index 000000000..de7200ac1 --- /dev/null +++ b/src/mol-model/structure/export/categories/secondary-structure.ts @@ -0,0 +1,5 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ diff --git a/src/mol-model/structure/export/mmcif.ts b/src/mol-model/structure/export/mmcif.ts index 3a5f900c8..8a33f9851 100644 --- a/src/mol-model/structure/export/mmcif.ts +++ b/src/mol-model/structure/export/mmcif.ts @@ -33,17 +33,29 @@ function _entity({ model, structure }: CifExportContext): CifCategory { } const Categories = [ + // Basics copy_mmCif_category('entry'), copy_mmCif_category('exptl'), _entity, + + // Symmetry copy_mmCif_category('cell'), copy_mmCif_category('symmetry'), + + // Assemblies copy_mmCif_category('pdbx_struct_assembly'), copy_mmCif_category('pdbx_struct_assembly_gen'), copy_mmCif_category('pdbx_struct_oper_list'), + + // Secondary structure + // TODO + + // Misc // TODO: filter for actual present residues? copy_mmCif_category('chem_comp'), copy_mmCif_category('atom_sites'), + + // Atoms _atom_site ]; diff --git a/src/mol-model/structure/model/formats/mmcif/secondary-structure.ts b/src/mol-model/structure/model/formats/mmcif/secondary-structure.ts index ed07db38b..69e6f8f42 100644 --- a/src/mol-model/structure/model/formats/mmcif/secondary-structure.ts +++ b/src/mol-model/structure/model/formats/mmcif/secondary-structure.ts @@ -13,13 +13,15 @@ import { Column } from 'mol-data/db'; export function getSecondaryStructureMmCif(data: mmCIF_Database, hierarchy: AtomicHierarchy): SecondaryStructure { const map: SecondaryStructureMap = new Map(); - addHelices(data.struct_conf, map); + const elements: SecondaryStructure.Element[] = [{ kind: 'none' }]; + addHelices(data.struct_conf, map, elements); // must add Helices 1st because of 'key' value assignment. - addSheets(data.struct_sheet_range, map, data.struct_conf._rowCount); + addSheets(data.struct_sheet_range, map, data.struct_conf._rowCount, elements); const secStruct: SecondaryStructureData = { type: new Int32Array(hierarchy.residues._rowCount) as any, - key: new Int32Array(hierarchy.residues._rowCount) as any + index: new Int32Array(hierarchy.residues._rowCount) as any, + elements }; if (map.size > 0) assignSecondaryStructureRanges(hierarchy, map, secStruct); @@ -32,34 +34,45 @@ type SecondaryStructureEntry = { endSeqNumber: number, endInsCode: string | null, type: SecondaryStructureType, - key: number + index: number } type SecondaryStructureMap = Map<string, Map<number, SecondaryStructureEntry>> -type SecondaryStructureData = { type: SecondaryStructureType[], key: number[] } +type SecondaryStructureData = { type: SecondaryStructureType[], index: number[], elements: SecondaryStructure.Element[] } -function addHelices(cat: mmCIF['struct_conf'], map: SecondaryStructureMap) { +function addHelices(cat: mmCIF['struct_conf'], map: SecondaryStructureMap, elements: SecondaryStructure.Element[]) { if (!cat._rowCount) return; const { beg_label_asym_id, beg_label_seq_id, pdbx_beg_PDB_ins_code } = cat; const { end_label_seq_id, pdbx_end_PDB_ins_code } = cat; - const { pdbx_PDB_helix_class, conf_type_id } = cat; + const { pdbx_PDB_helix_class, conf_type_id, details } = cat; for (let i = 0, _i = cat._rowCount; i < _i; i++) { - const type = pdbx_PDB_helix_class.valueKind(i) === Column.ValueKind.Present + const type = SecondaryStructureType.create(pdbx_PDB_helix_class.valueKind(i) === Column.ValueKind.Present ? SecondaryStructureType.SecondaryStructurePdb[pdbx_PDB_helix_class.value(i)] : conf_type_id.valueKind(i) === Column.ValueKind.Present ? SecondaryStructureType.SecondaryStructureMmcif[conf_type_id.value(i)] - : SecondaryStructureType.Flag.NA - + : SecondaryStructureType.Flag.NA); + + const element: SecondaryStructure.Helix = { + kind: 'helix', + flags: type, + type_id: pdbx_PDB_helix_class.valueKind(i) === Column.ValueKind.Present + ? pdbx_PDB_helix_class.value(i) : conf_type_id.valueKind(i) === Column.ValueKind.Present + ? conf_type_id.value(i) : 'HELIX_P', + helix_class: pdbx_PDB_helix_class.value(i), + details: details.valueKind(i) === Column.ValueKind.Present ? details.value(i) : void 0 + }; const entry: SecondaryStructureEntry = { startSeqNumber: beg_label_seq_id.value(i), startInsCode: pdbx_beg_PDB_ins_code.value(i), endSeqNumber: end_label_seq_id.value(i), endInsCode: pdbx_end_PDB_ins_code.value(i), - type: SecondaryStructureType.create(type), - key: i + 1 + type, + index: elements.length }; + elements[elements.length] = element; + const asymId = beg_label_asym_id.value(i)!; if (map.has(asymId)) { map.get(asymId)!.set(entry.startSeqNumber, entry); @@ -69,7 +82,7 @@ function addHelices(cat: mmCIF['struct_conf'], map: SecondaryStructureMap) { } } -function addSheets(cat: mmCIF['struct_sheet_range'], map: SecondaryStructureMap, sheetCount: number) { +function addSheets(cat: mmCIF['struct_sheet_range'], map: SecondaryStructureMap, sheetCount: number, elements: SecondaryStructure.Element[]) { if (!cat._rowCount) return; const { beg_label_asym_id, beg_label_seq_id, pdbx_beg_PDB_ins_code } = cat; @@ -88,15 +101,25 @@ function addSheets(cat: mmCIF['struct_sheet_range'], map: SecondaryStructureMap, sheet_id_key.set(id, key); } + const type = SecondaryStructureType.create(SecondaryStructureType.Flag.Beta | SecondaryStructureType.Flag.BetaSheet); + const element: SecondaryStructure.Sheet = { + kind: 'sheet', + flags: type, + sheet_id: id, + symmetry: void 0 + } const entry: SecondaryStructureEntry = { startSeqNumber: beg_label_seq_id.value(i), startInsCode: pdbx_beg_PDB_ins_code.value(i), endSeqNumber: end_label_seq_id.value(i), endInsCode: pdbx_end_PDB_ins_code.value(i), - type: SecondaryStructureType.create(SecondaryStructureType.Flag.Beta | SecondaryStructureType.Flag.BetaSheet), - key + type, + index: elements.length }; + elements[elements.length] = element; + + const asymId = beg_label_asym_id.value(i)!; if (map.has(asymId)) { map.get(asymId)!.set(entry.startSeqNumber, entry); @@ -110,14 +133,13 @@ function addSheets(cat: mmCIF['struct_sheet_range'], map: SecondaryStructureMap, function assignSecondaryStructureEntry(hierarchy: AtomicHierarchy, entry: SecondaryStructureEntry, resStart: number, resEnd: number, data: SecondaryStructureData) { const { label_seq_id, pdbx_PDB_ins_code } = hierarchy.residues; - const { endSeqNumber, endInsCode, type, key } = entry; + const { endSeqNumber, endInsCode, index, type } = entry; let rI = resStart; while (rI < resEnd) { const seqNumber = label_seq_id.value(rI); - data.type[rI] = type; - data.key[rI] = key; + data.index[rI] = index; if ((seqNumber > endSeqNumber) || (seqNumber === endSeqNumber && pdbx_PDB_ins_code.value(rI) === endInsCode)) { diff --git a/src/mol-model/structure/model/properties/seconday-structure.ts b/src/mol-model/structure/model/properties/seconday-structure.ts index dfde631a3..5ecfb439c 100644 --- a/src/mol-model/structure/model/properties/seconday-structure.ts +++ b/src/mol-model/structure/model/properties/seconday-structure.ts @@ -8,10 +8,35 @@ import { SecondaryStructureType } from '../types'; /** Secondary structure "indexed" by residues. */ interface SecondaryStructure { - // assign flags to each residue readonly type: ArrayLike<SecondaryStructureType>, - /** unique value for each "element". This is because single sheet is speficied by multiple records. */ - readonly key: ArrayLike<number> + + /** index into the elements array */ + readonly index: ArrayLike<number>, + /** indexed by key */ + readonly elements: ReadonlyArray<SecondaryStructure.Element> +} + +namespace SecondaryStructure { + export type Element = None | Helix | Sheet + + export interface None { + kind: 'none' + } + + export interface Helix { + kind: 'helix', + flags: SecondaryStructureType, + type_id: string, // TODO: use aliased type? + helix_class: string, + details?: string + } + + export interface Sheet { + kind: 'sheet', + flags: SecondaryStructureType, + sheet_id: string, + symmetry?: string + } } export { SecondaryStructure } \ No newline at end of file diff --git a/src/mol-model/structure/structure/properties.ts b/src/mol-model/structure/structure/properties.ts index d13f5ddd8..7a3e8f789 100644 --- a/src/mol-model/structure/structure/properties.ts +++ b/src/mol-model/structure/structure/properties.ts @@ -59,7 +59,7 @@ const residue = { // Properties secondary_structure_type: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.properties.secondaryStructure.type[l.unit.residueIndex[l.element]]), - secondary_structure_key: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.properties.secondaryStructure.key[l.unit.residueIndex[l.element]]), + secondary_structure_key: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.properties.secondaryStructure.index[l.unit.residueIndex[l.element]]), } const chain = { -- GitLab