From 1e9bd97121256e7da2617d008bb96e89bbe3a2e1 Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Thu, 5 Jul 2018 15:29:35 +0200 Subject: [PATCH] mmCIF secondary structure export --- .../export/categories/secondary-structure.ts | 115 ++++++++++++++++++ src/mol-model/structure/export/mmcif.ts | 4 +- .../formats/mmcif/secondary-structure.ts | 4 +- 3 files changed, 119 insertions(+), 4 deletions(-) diff --git a/src/mol-model/structure/export/categories/secondary-structure.ts b/src/mol-model/structure/export/categories/secondary-structure.ts index de7200ac1..cc27294ab 100644 --- a/src/mol-model/structure/export/categories/secondary-structure.ts +++ b/src/mol-model/structure/export/categories/secondary-structure.ts @@ -3,3 +3,118 @@ * * @author David Sehnal <david.sehnal@gmail.com> */ + +import { Segmentation } from 'mol-data/int'; +import { CifWriter } from 'mol-io/writer/cif'; +import { SecondaryStructure } from '../../model/properties/seconday-structure'; +import { Element, Unit, StructureProperties as P } from '../../structure'; +import { CifExportContext } from '../mmcif'; + +import CifField = CifWriter.Field +import CifCategory = CifWriter.Category +import { Column } from 'mol-data/db'; + +export function _struct_conf(ctx: CifExportContext): CifCategory { + const elements = findElements(ctx, 'helix'); + return { + data: elements, + name: 'struct_conf', + fields: struct_conf_fields, + rowCount: elements.length + }; +} + +export function _struct_sheet_range(ctx: CifExportContext): CifCategory { + const elements = findElements(ctx, 'sheet'); + return { + data: elements, + name: 'struct_sheet_range', + fields: struct_sheet_range_fields, + rowCount: elements.length + }; +} + +const struct_conf_fields: CifField[] = [ + CifField.str<number, SSElement<SecondaryStructure.Helix>[]>('conf_type_id', (i, data) => data[i].element.type_id), + CifField.str<number, SSElement<SecondaryStructure.Helix>[]>('conf_type_id', (i, data, idx) => `${data[i].element.type_id}${idx + 1}`), + ...residueIdFields('beg_', e => e.start), + ...residueIdFields('end_', e => e.end), + CifField.str<number, SSElement<SecondaryStructure.Helix>[]>('pdbx_PDB_helix_class', (i, data) => data[i].element.helix_class), + CifField.str<number, SSElement<SecondaryStructure.Helix>[]>('details', (i, data) => data[i].element.details || '', { + valueKind: (i, d) => !!d[i].element.details ? Column.ValueKind.Present : Column.ValueKind.Unknown + }), + CifField.int<number, SSElement<SecondaryStructure.Helix>[]>('pdbx_PDB_helix_class', (i, data) => data[i].length) +]; + +const struct_sheet_range_fields: CifField[] = [ + CifField.index('id'), + CifField.str<number, SSElement<SecondaryStructure.Sheet>[]>('sheet_id', (i, data) => data[i].element.sheet_id), + ...residueIdFields('beg_', e => e.start), + ...residueIdFields('end_', e => e.end), + CifField.str('symmetry', (i, data) => '', { valueKind: (i, d) => Column.ValueKind.Unknown }) +]; + +function residueIdFields(prefix: string, loc: (e: SSElement<any>) => Element.Location): CifField<number, SSElement<SecondaryStructure.Helix>[]>[] { + return [ + CifField.str(`${prefix}label_comp_id`, (i, d) => P.residue.label_comp_id(loc(d[i]))), + CifField.int(`${prefix}label_seq_id`, (i, d) => P.residue.label_seq_id(loc(d[i]))), + CifField.str(`pdbx_${prefix}PDB_ins_code`, (i, d) => P.residue.pdbx_PDB_ins_code(loc(d[i]))), + CifField.str(`${prefix}label_asym_id`, (i, d) => P.chain.label_asym_id(loc(d[i]))), + CifField.str(`${prefix}_entity_id`, (i, d) => P.chain.label_entity_id(loc(d[i]))), + CifField.str(`${prefix}auth_comp_id`, (i, d) => P.residue.auth_comp_id(loc(d[i]))), + CifField.int(`${prefix}auth_seq_id`, (i, d) => P.residue.auth_seq_id(loc(d[i]))), + CifField.str(`${prefix}auth_asym_id`, (i, d) => P.chain.auth_asym_id(loc(d[i]))) + ]; +} + +interface SSElement<T extends SecondaryStructure.Element> { + start: Element.Location, + end: Element.Location, + length: number, + element: T +} + +function findElements<T extends SecondaryStructure.Element>(ctx: CifExportContext, kind: SecondaryStructure.Element['kind']) { + const { index, elements } = ctx.model.properties.secondaryStructure; + + const ssElements: SSElement<any>[] = []; + + for (const unit of ctx.structure.units) { + // currently can only support this for "identity" operators. + if (!Unit.isAtomic(unit) || !unit.conformation.operator.isIdentity) continue; + + const segs = unit.model.atomicHierarchy.residueSegments; + const residues = Segmentation.transientSegments(segs, unit.elements); + + let current: Segmentation.Segment<Element>, move = true; + while (residues.hasNext) { + if (move) current = residues.move(); + + const start = current!.index; + const startIdx = index[start]; + const element = elements[startIdx]; + if (element.kind !== kind) { + move = true; + continue; + } + + let prev = start; + while (residues.hasNext) { + prev = current!.index; + current = residues.move(); + if (startIdx !== index[current.index]) { + move = false; + ssElements[ssElements.length] = { + start: Element.Location(unit, segs.segments[start]), + end: Element.Location(unit, segs.segments[prev]), + length: prev - start + 1, + element + } + break; + } + } + } + } + + return ssElements as SSElement<T>[]; +} \ No newline at end of file diff --git a/src/mol-model/structure/export/mmcif.ts b/src/mol-model/structure/export/mmcif.ts index 8a33f9851..c783b552e 100644 --- a/src/mol-model/structure/export/mmcif.ts +++ b/src/mol-model/structure/export/mmcif.ts @@ -17,6 +17,7 @@ export interface CifExportContext { } import CifCategory = CifWriter.Category +import { _struct_conf, _struct_sheet_range } from './categories/secondary-structure'; function copy_mmCif_category(name: keyof mmCIF_Schema) { return ({ model }: CifExportContext) => { @@ -48,7 +49,8 @@ const Categories = [ copy_mmCif_category('pdbx_struct_oper_list'), // Secondary structure - // TODO + _struct_conf, + _struct_sheet_range, // Misc // TODO: filter for actual present residues? 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 69e6f8f42..15ff80bb2 100644 --- a/src/mol-model/structure/model/formats/mmcif/secondary-structure.ts +++ b/src/mol-model/structure/model/formats/mmcif/secondary-structure.ts @@ -56,9 +56,7 @@ function addHelices(cat: mmCIF['struct_conf'], map: SecondaryStructureMap, eleme 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', + type_id: 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 }; -- GitLab