diff --git a/src/extensions/dnatco/confal-pyramids/behavior.ts b/src/extensions/dnatco/confal-pyramids/behavior.ts new file mode 100644 index 0000000000000000000000000000000000000000..b88a2c9daf8b4069048018c898c8ab675cff52b2 --- /dev/null +++ b/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) + }) +}); diff --git a/src/extensions/dnatco/confal-pyramids/property.ts b/src/extensions/dnatco/confal-pyramids/property.ts new file mode 100644 index 0000000000000000000000000000000000000000..00dcf95cc5787b736bca1ad6016d664c3bb188db --- /dev/null +++ b/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'); +} diff --git a/src/extensions/dnatco/confal-pyramids/types.ts b/src/extensions/dnatco/confal-pyramids/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..b7a94099885cb2aa5e37094cb6bb100c355b2392 --- /dev/null +++ b/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); + } +} diff --git a/src/extensions/dnatco/index.ts b/src/extensions/dnatco/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..1be8f0e39ba10650fe31fe539c80d38e89e7a4f2 --- /dev/null +++ b/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';