Browse Source

Implement basic structure of DNATCO Confal Pyramids extension

Michal Malý 4 years ago
parent
commit
5063e99761

+ 59 - 0
src/extensions/dnatco/confal-pyramids/behavior.ts

@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Michal Malý <michal.maly@ibt.cas.cz>
+ * @author Jiří Černý <jiri.cerny@ibt.cas.cz>
+ */
+
+import { ConfalPyramidsProvider } from './property';
+import { Loci } from '../../../mol-model/loci';
+import { PluginBehavior } from '../../../mol-plugin/behavior/behavior';
+import { ParamDefinition as PD } from '../../../mol-util/param-definition';
+
+export const DnatcoConfalPyramids = PluginBehavior.create<{ autoAttach: boolean, showToolTip: boolean }>({
+    name: 'dnatco-confal-pyramids-prop',
+    category: 'custom-props',
+    display: {
+        name: 'Confal Pyramids',
+        description: 'Schematic depiction of conformer class and confal value.',
+    },
+    ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showToolTip: boolean }> {
+
+        private provider = ConfalPyramidsProvider;
+
+        private labelConfalPyramids = {
+            label: (loci: Loci): string | undefined => {
+                if (!this.params.showToolTip) return void 0;
+
+                /* TODO: Implement this */
+                return void 0;
+            }
+        }
+
+        register(): void {
+            this.ctx.customModelProperties.register(this.provider, this.params.autoAttach);
+            this.ctx.managers.lociLabels.addProvider(this.labelConfalPyramids);
+
+            /* TODO: Add color and visual providers */
+        }
+
+        update(p: { autoAttach: boolean, showToolTip: boolean }) {
+            const updated = this.params.autoAttach !== p.autoAttach;
+            this.params.autoAttach = p.autoAttach;
+            this.params.showToolTip = p.showToolTip;
+            this.ctx.customModelProperties.setDefaultAutoAttach(this.provider.descriptor.name, this.params.autoAttach);
+            return updated;
+        }
+
+        unregister() {
+            this.ctx.customModelProperties.unregister(ConfalPyramidsProvider.descriptor.name);
+            this.ctx.managers.lociLabels.removeProvider(this.labelConfalPyramids);
+
+            /* TODO: Unregister color and visual providers */
+        }
+    },
+    params: () => ({
+        autoAttach: PD.Boolean(true),
+        showToolTip: PD.Boolean(true)
+    })
+});

+ 172 - 0
src/extensions/dnatco/confal-pyramids/property.ts

@@ -0,0 +1,172 @@
+/**
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Michal Malý <michal.maly@ibt.cas.cz>
+ * @author Jiří Černý <jiri.cerny@ibt.cas.cz>
+ */
+
+import { ConfalPyramidsTypes as CPT } from './types';
+import { Column, Table } from '../../../mol-data/db';
+import { toTable } from '../../../mol-io/reader/cif/schema';
+import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
+import { Model } from '../../../mol-model/structure';
+import { CustomProperty } from '../../../mol-model-props/common/custom-property';
+import { CustomModelProperty } from '../../../mol-model-props/common/custom-model-property';
+import { PropertyWrapper } from '../../../mol-model-props/common/wrapper';
+import { ParamDefinition as PD } from '../../../mol-util/param-definition';
+import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif';
+
+export type ConfalPyramids = PropertyWrapper<CPT.PyramidsData | undefined >;
+
+export namespace ConfalPyramids {
+    export const Schema = {
+        ndb_struct_ntc_step: {
+            id: Column.Schema.int,
+            name: Column.Schema.str,
+            PDB_model_number: Column.Schema.int,
+            label_entity_id_1: Column.Schema.int,
+            label_asym_id_1: Column.Schema.str,
+            label_seq_id_1: Column.Schema.int,
+            label_comp_id_1: Column.Schema.str,
+            label_alt_id_1: Column.Schema.str,
+            label_entity_id_2: Column.Schema.int,
+            label_asym_id_2: Column.Schema.str,
+            label_seq_id_2: Column.Schema.int,
+            label_comp_id_2: Column.Schema.str,
+            label_alt_id_2: Column.Schema.str,
+            auth_asym_id_1: Column.Schema.str,
+            auth_seq_id_1: Column.Schema.int,
+            auth_asym_id_2: Column.Schema.str,
+            auth_seq_id_2: Column.Schema.int,
+            PDB_ins_code_1: Column.Schema.str,
+            PDB_ins_code_2: Column.Schema.str,
+        },
+        ndb_struct_ntc_step_summary: {
+            step_id: Column.Schema.int,
+            assigned_CANA: Column.Schema.str,
+            assigned_NtC: Column.Schema.str,
+            confal_score: Column.Schema.int,
+            euclidean_distance_NtC_ideal: Column.Schema.float,
+            cartesian_rmsd_closest_NtC_representative: Column.Schema.float,
+            closest_CANA: Column.Schema.str,
+            closest_NtC: Column.Schema.str,
+            closest_step_golden: Column.Schema.str
+        }
+    };
+    export type Schema = typeof Schema;
+
+    export async function fromCif(ctx: CustomProperty.Context, model: Model, props: ConfalPyramidsProps): Promise<CustomProperty.Data<ConfalPyramids>> {
+        const info = PropertyWrapper.createInfo();
+        const data = getCifData(model);
+        if (data === undefined) return { value: { info, data: undefined } };
+
+        const fromCif = createPyramidsFromCif(model, data.steps, data.stepsSummary);
+        return { value: { info, data: fromCif } };
+    }
+
+    function getCifData(model: Model) {
+        if (!MmcifFormat.is(model.sourceData)) throw new Error('Data format must be mmCIF');
+        if (!hasNdbStructNtcCategories(model)) return undefined;
+        return {
+            steps: toTable(Schema.ndb_struct_ntc_step, model.sourceData.data.frame.categories.ndb_struct_ntc_step),
+            stepsSummary: toTable(Schema.ndb_struct_ntc_step_summary, model.sourceData.data.frame.categories.ndb_struct_ntc_step_summary)
+        };
+    }
+
+    function hasNdbStructNtcCategories(model: Model): boolean {
+        if (!MmcifFormat.is(model.sourceData)) throw new Error('Data format must be mmCIF');
+        const names = (model.sourceData).data.frame.categoryNames;
+        return names.includes('ndb_struct_ntc_step') && names.includes('ndb_struct_ntc_step_summary');
+    }
+
+    export function isApplicable(model?: Model): boolean {
+        return !!model && hasNdbStructNtcCategories(model);
+    }
+}
+
+export const ConfalPyramidsParams = {};
+export type ConfalPyramidsParams = typeof ConfalPyramidsParams;
+export type ConfalPyramidsProps = PD.Values<ConfalPyramidsParams>;
+
+export const ConfalPyramidsProvider: CustomModelProperty.Provider<ConfalPyramidsParams, ConfalPyramids> = CustomModelProperty.createProvider({
+    label: 'Confal Pyramids',
+    descriptor: CustomPropertyDescriptor({
+        name: 'confal_pyramids',
+    }),
+    type: 'static',
+    defaultParams: ConfalPyramidsParams,
+    getParams: (data: Model) => ConfalPyramidsParams,
+    isApplicable: (data: Model) => ConfalPyramids.isApplicable(data),
+    obtain: async (ctx: CustomProperty.Context, data: Model, props: Partial<ConfalPyramidsProps>) => {
+        const p = { ...PD.getDefaultValues(ConfalPyramidsParams), ...props };
+        return ConfalPyramids.fromCif(ctx, data, p);
+    }
+});
+
+type StepsSummaryTable = Table<typeof ConfalPyramids.Schema.ndb_struct_ntc_step_summary>;
+
+function createPyramidsFromCif(model: Model,
+    steps: Table<typeof ConfalPyramids.Schema.ndb_struct_ntc_step>,
+    stepsSummary: StepsSummaryTable): CPT.PyramidsData {
+    const pyramids = new Array<CPT.Pyramid>();
+    const names = new Map<string, number>();
+    const locations = new Array<CPT.Location>();
+    let hasMultipleModels = false;
+
+    const {
+        id, PDB_model_number, name,
+        auth_asym_id_1, auth_seq_id_1, label_comp_id_1, label_alt_id_1, PDB_ins_code_1,
+        auth_asym_id_2, auth_seq_id_2, label_comp_id_2, label_alt_id_2, PDB_ins_code_2,
+        _rowCount } = steps;
+
+    if (_rowCount !== stepsSummary._rowCount) throw new Error('Inconsistent mmCIF data');
+
+    for (let i = 0; i < _rowCount; i++) {
+        const model_num = PDB_model_number.value(i);
+        if (model_num !== model.modelNum) {
+            hasMultipleModels = true;
+            continue; // We are only interested in data for the current model
+        }
+
+        const { _NtC, _confal_score } = getNtCAndConfalScore(id.value(i), i, stepsSummary);
+
+        const pyramid = {
+            PDB_model_number: model_num,
+            name: name.value(i),
+            auth_asym_id_1: auth_asym_id_1.value(i),
+            auth_seq_id_1: auth_seq_id_1.value(i),
+            label_comp_id_1: label_comp_id_1.value(i),
+            label_alt_id_1: label_alt_id_1.value(i),
+            PDB_ins_code_1: PDB_ins_code_1.value(i),
+            auth_asym_id_2: auth_asym_id_2.value(i),
+            auth_seq_id_2: auth_seq_id_2.value(i),
+            label_comp_id_2: label_comp_id_2.value(i),
+            label_alt_id_2: label_alt_id_2.value(i),
+            PDB_ins_code_2: PDB_ins_code_2.value(i),
+            confal_score: _confal_score,
+            NtC: _NtC
+        };
+
+        pyramids.push(pyramid);
+        names.set(pyramid.name, pyramids.length - 1);
+
+        locations.push(CPT.Location(pyramid, false));
+        locations.push(CPT.Location(pyramid, true));
+    }
+
+    return { pyramids, names, locations, hasMultipleModels };
+}
+
+function getNtCAndConfalScore(id: number, i: number, stepsSummary: StepsSummaryTable) {
+    const { step_id, confal_score, assigned_NtC } = stepsSummary;
+
+    // Assume that step_ids in ntc_step_summary are in the same order as steps in ntc_step
+    for (let j = i; j < stepsSummary._rowCount; j++) {
+        if (id === step_id.value(j)) return { _NtC: assigned_NtC.value(j), _confal_score: confal_score.value(j) };
+    }
+    // Safety net for cases where the previous assumption is not met
+    for (let j = 0; j < i; j++) {
+        if (id === step_id.value(j)) return { _NtC: assigned_NtC.value(j), _confal_score: confal_score.value(j) };
+    }
+    throw new Error('Inconsistent mmCIF data');
+}

+ 60 - 0
src/extensions/dnatco/confal-pyramids/types.ts

@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Michal Malý <michal.maly@ibt.cas.cz>
+ * @author Jiří Černý <jiri.cerny@ibt.cas.cz>
+ */
+
+import { DataLocation } from '../../../mol-model/location';
+import { ElementIndex, Structure, StructureElement, Unit } from '../../../mol-model/structure';
+
+export namespace ConfalPyramidsTypes {
+    export type Pyramid = {
+        PDB_model_number: number,
+        name: string,
+        auth_asym_id_1: string,
+        auth_seq_id_1: number,
+        label_comp_id_1: string,
+        label_alt_id_1: string,
+        PDB_ins_code_1: string,
+        auth_asym_id_2: string,
+        auth_seq_id_2: number,
+        label_comp_id_2: string,
+        label_alt_id_2: string,
+        PDB_ins_code_2: string,
+        confal_score: number,
+        NtC: string
+    }
+
+    export interface PyramidsData {
+        pyramids: Array<Pyramid>,
+        names: Map<string, number>,
+        locations: Array<Location>,
+        hasMultipleModels: boolean
+    }
+
+    export interface LocationData {
+        readonly pyramid: Pyramid
+        readonly isLower: boolean;
+    }
+
+    export interface Element {
+        structure: Structure;
+        unit: Unit.Atomic;
+        element: ElementIndex;
+    }
+
+    export interface Location extends DataLocation<LocationData, Element> {}
+
+    export function Location(pyramid: Pyramid, isLower: boolean, structure?: Structure, unit?: Unit.Atomic, element?: ElementIndex) {
+        return DataLocation('pyramid', { pyramid, isLower }, { structure: structure as any, unit: unit as any, element: element as any });
+    }
+
+    export function isLocation(x: any): x is Location {
+        return !!x && x.kind === 'data-location' && x.tag === 'pyramid';
+    }
+
+    export function toElementLocation(location: Location) {
+        return StructureElement.Location.create(location.element.structure, location.element.unit, location.element.element);
+    }
+}

+ 8 - 0
src/extensions/dnatco/index.ts

@@ -0,0 +1,8 @@
+/**
+ * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Michal Malý <michal.maly@ibt.cas.cz>
+ * @author Jiří Černý <jiri.cerny@ibt.cas.cz>
+ */
+
+export { DnatcoConfalPyramids } from './confal-pyramids/behavior';