Browse Source

mmCIF secondary structure export

David Sehnal 6 years ago
parent
commit
1e9bd97121

+ 115 - 0
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>[];
+}

+ 3 - 1
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?

+ 1 - 3
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
         };