From c1d54facc6d37c341bd752cae71156016c519606 Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Tue, 26 Jun 2018 11:30:14 +0200 Subject: [PATCH] moved Queries.props to StructureProperies, wip custom props --- src/apps/structure-info/model.ts | 4 +- .../ui/visualization/sequence-view.tsx | 6 +- src/mol-geo/representation/structure/index.ts | 6 +- src/mol-geo/theme/structure/color/chain-id.ts | 10 +- src/mol-geo/theme/structure/size/physical.ts | 6 +- .../structure/export/categories/atom_site.ts | 54 ++++ .../export/categories/chem_comp_bond.ts | 7 + src/mol-model/structure/export/mmcif.ts | 60 +--- .../structure/model/formats/mmcif/assembly.ts | 5 +- .../structure/model/formats/mmcif/bonds.ts | 273 +----------------- .../model/formats/mmcif/bonds/comp.ts | 93 ++++++ .../model/formats/mmcif/bonds/struct_conn.ts | 192 ++++++++++++ src/mol-model/structure/query.ts | 2 - src/mol-model/structure/query/generators.ts | 3 +- src/mol-model/structure/query/predicates.ts | 3 +- src/mol-model/structure/query/properties.ts | 118 +------- src/mol-model/structure/structure.ts | 3 +- .../structure/structure/properties.ts | 126 ++++++++ .../structure/structure/structure.ts | 14 +- src/mol-view/label.ts | 18 +- src/perf-tests/structure.ts | 24 +- src/servers/model/server/api.ts | 20 +- 22 files changed, 543 insertions(+), 504 deletions(-) create mode 100644 src/mol-model/structure/export/categories/atom_site.ts create mode 100644 src/mol-model/structure/export/categories/chem_comp_bond.ts create mode 100644 src/mol-model/structure/model/formats/mmcif/bonds/comp.ts create mode 100644 src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts create mode 100644 src/mol-model/structure/structure/properties.ts diff --git a/src/apps/structure-info/model.ts b/src/apps/structure-info/model.ts index d63913dca..d1252937c 100644 --- a/src/apps/structure-info/model.ts +++ b/src/apps/structure-info/model.ts @@ -10,7 +10,7 @@ require('util.promisify').shim(); // import { Table } from 'mol-data/db' import { CifFrame } from 'mol-io/reader/cif' -import { Model, Structure, Element, Unit, Queries, Format } from 'mol-model/structure' +import { Model, Structure, Element, Unit, Format, StructureProperties } from 'mol-model/structure' // import { Run, Progress } from 'mol-task' import { OrderedSet } from 'mol-data/int'; import { Table } from 'mol-data/db'; @@ -166,7 +166,7 @@ export function printUnits(structure: Structure) { } else if (Unit.isCoarse(l.unit)) { console.log(`Coarse unit ${unit.id} ${unit.conformation.operator.name} (${Unit.isSpheres(l.unit) ? 'spheres' : 'gaussians'}): ${size} elements.`); - const props = Queries.props.coarse; + const props = StructureProperties.coarse; const seq = l.unit.model.sequence; for (let j = 0, _j = Math.min(size, 3); j < _j; j++) { diff --git a/src/mol-app/ui/visualization/sequence-view.tsx b/src/mol-app/ui/visualization/sequence-view.tsx index 520505ca2..6025eea22 100644 --- a/src/mol-app/ui/visualization/sequence-view.tsx +++ b/src/mol-app/ui/visualization/sequence-view.tsx @@ -7,7 +7,7 @@ import * as React from 'react' import { View } from '../view'; import { SequenceViewController } from '../../controller/visualization/sequence-view'; -import { Structure, StructureSequence, Queries, Selection } from 'mol-model/structure'; +import { Structure, StructureSequence, Queries, Selection, StructureProperties } from 'mol-model/structure'; import { Context } from '../../context/context'; import { InteractivityEvents } from '../../event/basic'; import { SyncRuntimeContext } from 'mol-task/execution/synchronous'; @@ -27,8 +27,8 @@ export class SequenceView extends View<SequenceViewController, {}, {}> { function createQuery(entityId: string, label_seq_id: number) { return Queries.generators.atoms({ - entityTest: l => Queries.props.entity.id(l) === entityId, - residueTest: l => Queries.props.residue.label_seq_id(l) === label_seq_id + entityTest: l => StructureProperties.entity.id(l) === entityId, + residueTest: l => StructureProperties.residue.label_seq_id(l) === label_seq_id }); } diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts index 9d46deebc..fd8340667 100644 --- a/src/mol-geo/representation/structure/index.ts +++ b/src/mol-geo/representation/structure/index.ts @@ -5,7 +5,7 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Structure, StructureSymmetry, Unit } from 'mol-model/structure'; +import { Structure, Unit } from 'mol-model/structure'; import { Task } from 'mol-task' import { RenderObject } from 'mol-gl/render-object'; import { Representation, RepresentationProps, Visual } from '..'; @@ -43,7 +43,7 @@ export function StructureRepresentation<P extends StructureProps>(unitsVisualCto return Task.create('Creating StructureRepresentation', async ctx => { if (!_structure) { - _groups = structure.symmetryGroups; + _groups = structure.unitSymmetryGroups; for (let i = 0; i < _groups.length; i++) { const group = _groups[i]; const visual = unitsVisualCtor() @@ -59,7 +59,7 @@ export function StructureRepresentation<P extends StructureProps>(unitsVisualCto if (_structure.hashCode === structure.hashCode) { await update(_props) } else { - _groups = structure.symmetryGroups; + _groups = structure.unitSymmetryGroups; const newGroups: Unit.SymmetryGroup[] = [] const oldUnitsVisuals = unitsVisuals unitsVisuals = new Map() diff --git a/src/mol-geo/theme/structure/color/chain-id.ts b/src/mol-geo/theme/structure/color/chain-id.ts index 4c4b9687f..7afe56c46 100644 --- a/src/mol-geo/theme/structure/color/chain-id.ts +++ b/src/mol-geo/theme/structure/color/chain-id.ts @@ -4,7 +4,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Unit, Queries, Element } from 'mol-model/structure'; +import { Unit, StructureProperties, Element } from 'mol-model/structure'; import { StructureColorDataProps } from '.'; import { ColorData, createElementColor } from '../../../util/color-data'; @@ -49,9 +49,9 @@ export function chainIdColorData(props: StructureColorDataProps, locationFn: (l: let asym_id: Element.Property<string> if (Unit.isAtomic(unit)) { - asym_id = Queries.props.chain.label_asym_id + asym_id = StructureProperties.chain.label_asym_id } else if (Unit.isCoarse(unit)) { - asym_id = Queries.props.coarse.asym_id + asym_id = StructureProperties.coarse.asym_id } else { throw new Error('unhandled unit kind') } @@ -92,9 +92,9 @@ export function chainIdElementColorData(props: StructureColorDataProps, colorDat // let asym_id: Element.Property<string> // if (Unit.isAtomic(unit)) { - // asym_id = Queries.props.chain.label_asym_id + // asym_id = StructureProperties.chain.label_asym_id // } else if (Unit.isCoarse(unit)) { - // asym_id = Queries.props.coarse.asym_id + // asym_id = StructureProperties.coarse.asym_id // } // const l = Element.Location() diff --git a/src/mol-geo/theme/structure/size/physical.ts b/src/mol-geo/theme/structure/size/physical.ts index 84be4d339..34ac86d0d 100644 --- a/src/mol-geo/theme/structure/size/physical.ts +++ b/src/mol-geo/theme/structure/size/physical.ts @@ -4,15 +4,15 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Element, Unit, Queries } from 'mol-model/structure'; +import { Element, Unit, StructureProperties } from 'mol-model/structure'; import { StructureSizeDataProps } from '.'; import { createAttributeSize } from '../../../util/size-data'; export function getPhysicalRadius(unit: Unit): Element.Property<number> { if (Unit.isAtomic(unit)) { - return Queries.props.atom.vdw_radius + return StructureProperties.atom.vdw_radius } else if (Unit.isSpheres(unit)) { - return Queries.props.coarse.sphere_radius + return StructureProperties.coarse.sphere_radius } else { return () => 0 } diff --git a/src/mol-model/structure/export/categories/atom_site.ts b/src/mol-model/structure/export/categories/atom_site.ts new file mode 100644 index 000000000..7ee771645 --- /dev/null +++ b/src/mol-model/structure/export/categories/atom_site.ts @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { CifWriter } from 'mol-io/writer/cif'; +import { Element, Structure, StructureProperties as P } from '../../structure'; +import { CifExportContext } from '../mmcif'; +import CifField = CifWriter.Field +import CifCategory = CifWriter.Category +import E = CifWriter.Encodings + +const atom_site_fields: CifField<Element.Location>[] = [ + CifField.str('group_PDB', P.residue.group_PDB), + CifField.int('id', P.atom.id, { encoder: E.deltaRLE }), + CifField.str('type_symbol', P.atom.type_symbol as any), + CifField.str('label_atom_id', P.atom.label_atom_id), + CifField.str('label_alt_id', P.atom.label_alt_id), + + CifField.str('label_comp_id', P.residue.label_comp_id), + CifField.int('label_seq_id', P.residue.label_seq_id, { encoder: E.deltaRLE }), + CifField.str('pdbx_PDB_ins_code', P.residue.pdbx_PDB_ins_code), + + CifField.str('label_asym_id', P.chain.label_asym_id), + CifField.str('label_entity_id', P.chain.label_entity_id), + + CifField.float('Cartn_x', P.atom.x, { digitCount: 3, encoder: E.fixedPoint3 }), + CifField.float('Cartn_y', P.atom.y, { digitCount: 3, encoder: E.fixedPoint3 }), + CifField.float('Cartn_z', P.atom.z, { digitCount: 3, encoder: E.fixedPoint3 }), + CifField.float('occupancy', P.atom.occupancy, { digitCount: 2, encoder: E.fixedPoint2 }), + CifField.int('pdbx_formal_charge', P.atom.pdbx_formal_charge, { encoder: E.deltaRLE }), + + CifField.str('auth_atom_id', P.atom.auth_atom_id), + CifField.str('auth_comp_id', P.residue.auth_comp_id), + CifField.int('auth_seq_id', P.residue.auth_seq_id, { encoder: E.deltaRLE }), + CifField.str('auth_asym_id', P.chain.auth_asym_id), + + CifField.int('pdbx_PDB_model_num', P.unit.model_num, { encoder: E.deltaRLE }), + CifField.str<Element.Location, Structure>('operator_name', P.unit.operator_name, { + shouldInclude: structure => structure.units.some(u => !u.conformation.operator.isIdentity) + }) +]; + +export function _atom_site({ structure }: CifExportContext): CifCategory { + return { + data: structure, + name: 'atom_site', + fields: atom_site_fields, + rowCount: structure.elementCount, + keys: () => structure.elementLocations() + } +} \ No newline at end of file diff --git a/src/mol-model/structure/export/categories/chem_comp_bond.ts b/src/mol-model/structure/export/categories/chem_comp_bond.ts new file mode 100644 index 000000000..66298c566 --- /dev/null +++ b/src/mol-model/structure/export/categories/chem_comp_bond.ts @@ -0,0 +1,7 @@ +/** + * Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +// TODO \ 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 bf93893f0..4458ef154 100644 --- a/src/mol-model/structure/export/mmcif.ts +++ b/src/mol-model/structure/export/mmcif.ts @@ -7,52 +7,18 @@ import { CifWriter } from 'mol-io/writer/cif' import { mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif' -import { Structure, Element } from '../structure' +import { Structure } from '../structure' import { Model } from '../model' -import P from '../query/properties' +import { _atom_site } from './categories/atom_site'; export interface CifExportContext { structure: Structure, model: Model } -import CifField = CifWriter.Field import CifCategory = CifWriter.Category -import E = CifWriter.Encodings - -const atom_site_fields: CifField<Element.Location>[] = [ - CifField.str('group_PDB', P.residue.group_PDB), - CifField.int('id', P.atom.id, { encoder: E.deltaRLE }), - CifField.str('type_symbol', P.atom.type_symbol as any), - CifField.str('label_atom_id', P.atom.label_atom_id), - CifField.str('label_alt_id', P.atom.label_alt_id), - - CifField.str('label_comp_id', P.residue.label_comp_id), - CifField.int('label_seq_id', P.residue.label_seq_id, { encoder: E.deltaRLE }), - CifField.str('pdbx_PDB_ins_code', P.residue.pdbx_PDB_ins_code), - - CifField.str('label_asym_id', P.chain.label_asym_id), - CifField.str('label_entity_id', P.chain.label_entity_id), - - CifField.float('Cartn_x', P.atom.x, { digitCount: 3, encoder: E.fixedPoint3 }), - CifField.float('Cartn_y', P.atom.y, { digitCount: 3, encoder: E.fixedPoint3 }), - CifField.float('Cartn_z', P.atom.z, { digitCount: 3, encoder: E.fixedPoint3 }), - CifField.float('occupancy', P.atom.occupancy, { digitCount: 2, encoder: E.fixedPoint2 }), - CifField.int('pdbx_formal_charge', P.atom.pdbx_formal_charge, { encoder: E.deltaRLE }), - - CifField.str('auth_atom_id', P.atom.auth_atom_id), - CifField.str('auth_comp_id', P.residue.auth_comp_id), - CifField.int('auth_seq_id', P.residue.auth_seq_id, { encoder: E.deltaRLE }), - CifField.str('auth_asym_id', P.chain.auth_asym_id), - - CifField.int('pdbx_PDB_model_num', P.unit.model_num, { encoder: E.deltaRLE }), - CifField.str<Element.Location, Structure>('operator_name', P.unit.operator_name, { - shouldInclude: structure => structure.units.some(u => !u.conformation.operator.isIdentity) - }) -]; - -function copy_mmCif_cat(name: keyof mmCIF_Schema) { +function copy_mmCif_category(name: keyof mmCIF_Schema) { return ({ model }: CifExportContext) => { if (model.sourceData.kind !== 'mmCIF') return CifCategory.Empty; const table = model.sourceData.data[name]; @@ -66,27 +32,15 @@ function _entity({ model, structure }: CifExportContext): CifCategory { return CifCategory.ofTable('entity', model.entities.data, keys); } -function _atom_site({ structure }: CifExportContext): CifCategory { - return { - data: structure, - name: 'atom_site', - fields: atom_site_fields, - rowCount: structure.elementCount, - keys: () => structure.elementLocations() - } -} - const Categories = [ - copy_mmCif_cat('entry'), - copy_mmCif_cat('exptl'), - copy_mmCif_cat('cell'), - copy_mmCif_cat('symmetry'), + copy_mmCif_category('entry'), + copy_mmCif_category('exptl'), + copy_mmCif_category('cell'), + copy_mmCif_category('symmetry'), _entity, _atom_site ]; -mmCIF_Schema - namespace _Filters { export const AtomSitePositionsFieldNames = new Set<string>(<(keyof typeof mmCIF_Schema.atom_site)[]>['id', 'Cartn_x', 'Cartn_y', 'Cartn_z']); } diff --git a/src/mol-model/structure/model/formats/mmcif/assembly.ts b/src/mol-model/structure/model/formats/mmcif/assembly.ts index 48720596c..c86abd27c 100644 --- a/src/mol-model/structure/model/formats/mmcif/assembly.ts +++ b/src/mol-model/structure/model/formats/mmcif/assembly.ts @@ -11,6 +11,7 @@ import { Assembly, OperatorGroup, OperatorGroups } from '../../properties/symmet import { Queries as Q, Query } from '../../../query' import mmCIF_Format = Format.mmCIF +import { StructureProperties } from '../../../structure'; export function createAssemblies(format: mmCIF_Format): ReadonlyArray<Assembly> { const { pdbx_struct_assembly } = format.data; @@ -58,8 +59,8 @@ function operatorGroupsProvider(generators: Generator[], matrices: Matrices): () const operatorNames = expandOperators(operatorList); const operators = getAssemblyOperators(matrices, operatorNames, operatorOffset); const selector = Query(Q.generators.atoms({ chainTest: Q.pred.and( - Q.pred.eq(Q.props.unit.operator_name, SymmetryOperator.DefaultName), - Q.pred.inSet(Q.props.chain.label_asym_id, gen.asymIds) + Q.pred.eq(StructureProperties.unit.operator_name, SymmetryOperator.DefaultName), + Q.pred.inSet(StructureProperties.chain.label_asym_id, gen.asymIds) )})); groups[groups.length] = { selector, operators }; operatorOffset += operators.length; diff --git a/src/mol-model/structure/model/formats/mmcif/bonds.ts b/src/mol-model/structure/model/formats/mmcif/bonds.ts index 873ccda96..0f6997023 100644 --- a/src/mol-model/structure/model/formats/mmcif/bonds.ts +++ b/src/mol-model/structure/model/formats/mmcif/bonds.ts @@ -5,274 +5,5 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import Model from '../../model' -import { Element } from '../../../structure' -import { LinkType } from '../../types' -import { findEntityIdByAsymId, findAtomIndexByLabelName } from './util' -import { Column } from 'mol-data/db' - -// TODO: add dynamic property descriptor for this? - -export interface StructConn { - getResidueEntries(residueAIndex: number, residueBIndex: number): ReadonlyArray<StructConn.Entry> - getAtomEntries(atomIndex: number): ReadonlyArray<StructConn.Entry> -} - -export interface ComponentBond { - entries: Map<string, ComponentBond.Entry> -} - -export namespace StructConn { - function _resKey(rA: number, rB: number) { - if (rA < rB) return `${rA}-${rB}`; - return `${rB}-${rA}`; - } - const _emptyEntry: Entry[] = []; - - class StructConnImpl implements StructConn { - private _residuePairIndex: Map<string, StructConn.Entry[]> | undefined = void 0; - private _atomIndex: Map<number, StructConn.Entry[]> | undefined = void 0; - - private getResiduePairIndex() { - if (this._residuePairIndex) return this._residuePairIndex; - this._residuePairIndex = new Map(); - for (const e of this.entries) { - const ps = e.partners; - const l = ps.length; - for (let i = 0; i < l - 1; i++) { - for (let j = i + i; j < l; j++) { - const key = _resKey(ps[i].residueIndex, ps[j].residueIndex); - if (this._residuePairIndex.has(key)) { - this._residuePairIndex.get(key)!.push(e); - } else { - this._residuePairIndex.set(key, [e]); - } - } - } - } - return this._residuePairIndex; - } - - private getAtomIndex() { - if (this._atomIndex) return this._atomIndex; - this._atomIndex = new Map(); - for (const e of this.entries) { - for (const p of e.partners) { - const key = p.atomIndex; - if (this._atomIndex.has(key)) { - this._atomIndex.get(key)!.push(e); - } else { - this._atomIndex.set(key, [e]); - } - } - } - return this._atomIndex; - } - - - getResidueEntries(residueAIndex: number, residueBIndex: number): ReadonlyArray<StructConn.Entry> { - return this.getResiduePairIndex().get(_resKey(residueAIndex, residueBIndex)) || _emptyEntry; - } - - getAtomEntries(atomIndex: number): ReadonlyArray<StructConn.Entry> { - return this.getAtomIndex().get(atomIndex) || _emptyEntry; - } - - constructor(public entries: StructConn.Entry[]) { - } - } - - export interface Entry { - distance: number, - order: number, - flags: number, - partners: { residueIndex: number, atomIndex: Element, symmetry: string }[] - } - - type StructConnType = - | 'covale' - | 'covale_base' - | 'covale_phosphate' - | 'covale_sugar' - | 'disulf' - | 'hydrog' - | 'metalc' - | 'mismat' - | 'modres' - | 'saltbr' - - export const PropName = '__StructConn__'; - export function fromModel(model: Model): StructConn | undefined { - if (model._staticPropertyData[PropName]) return model._staticPropertyData[PropName]; - - if (model.sourceData.kind !== 'mmCIF') return; - const { struct_conn } = model.sourceData.data; - if (!struct_conn._rowCount) return void 0; - - const { conn_type_id, pdbx_dist_value, pdbx_value_order } = struct_conn; - const p1 = { - label_asym_id: struct_conn.ptnr1_label_asym_id, - label_comp_id: struct_conn.ptnr1_label_comp_id, - label_seq_id: struct_conn.ptnr1_label_seq_id, - label_atom_id: struct_conn.ptnr1_label_atom_id, - label_alt_id: struct_conn.pdbx_ptnr1_label_alt_id, - ins_code: struct_conn.pdbx_ptnr1_PDB_ins_code, - symmetry: struct_conn.ptnr1_symmetry - }; - const p2: typeof p1 = { - label_asym_id: struct_conn.ptnr2_label_asym_id, - label_comp_id: struct_conn.ptnr2_label_comp_id, - label_seq_id: struct_conn.ptnr2_label_seq_id, - label_atom_id: struct_conn.ptnr2_label_atom_id, - label_alt_id: struct_conn.pdbx_ptnr2_label_alt_id, - ins_code: struct_conn.pdbx_ptnr2_PDB_ins_code, - symmetry: struct_conn.ptnr2_symmetry - }; - - const _p = (row: number, ps: typeof p1) => { - if (ps.label_asym_id.valueKind(row) !== Column.ValueKind.Present) return void 0; - const asymId = ps.label_asym_id.value(row) - const residueIndex = model.atomicHierarchy.findResidueKey( - findEntityIdByAsymId(model, asymId), - ps.label_comp_id.value(row), - asymId, - ps.label_seq_id.value(row), - ps.ins_code.value(row) - ); - if (residueIndex < 0) return void 0; - const atomName = ps.label_atom_id.value(row); - // turns out "mismat" records might not have atom name value - if (!atomName) return void 0; - const atomIndex = findAtomIndexByLabelName(model, residueIndex, atomName, ps.label_alt_id.value(row)); - if (atomIndex < 0) return void 0; - return { residueIndex, atomIndex, symmetry: ps.symmetry.value(row) || '1_555' }; - } - - const _ps = (row: number) => { - const ret = []; - let p = _p(row, p1); - if (p) ret.push(p); - p = _p(row, p2); - if (p) ret.push(p); - return ret; - } - - const entries: StructConn.Entry[] = []; - for (let i = 0; i < struct_conn._rowCount; i++) { - const partners = _ps(i); - if (partners.length < 2) continue; - - const type = conn_type_id.value(i)! as StructConnType; - const orderType = (pdbx_value_order.value(i) || '').toLowerCase(); - let flags = LinkType.Flag.None; - let order = 1; - - switch (orderType) { - case 'sing': order = 1; break; - case 'doub': order = 2; break; - case 'trip': order = 3; break; - case 'quad': order = 4; break; - } - - switch (type) { - case 'covale': - case 'covale_base': - case 'covale_phosphate': - case 'covale_sugar': - case 'modres': - flags = LinkType.Flag.Covalent; - break; - case 'disulf': flags = LinkType.Flag.Covalent | LinkType.Flag.Sulfide; break; - case 'hydrog': flags = LinkType.Flag.Hydrogen; break; - case 'metalc': flags = LinkType.Flag.MetallicCoordination; break; - case 'saltbr': flags = LinkType.Flag.Ion; break; - } - - entries.push({ flags, order, distance: pdbx_dist_value.value(i), partners }); - } - - const ret = new StructConnImpl(entries); - model._staticPropertyData[PropName] = ret; - return ret; - } -} - -export namespace ComponentBond { - export class ComponentBondImpl implements ComponentBond { - entries: Map<string, ComponentBond.Entry> = new Map(); - - addEntry(id: string) { - let e = new Entry(id); - this.entries.set(id, e); - return e; - } - } - - export class Entry implements Entry { - map: Map<string, Map<string, { order: number, flags: number }>> = new Map(); - - add(a: string, b: string, order: number, flags: number, swap = true) { - let e = this.map.get(a); - if (e !== void 0) { - let f = e.get(b); - if (f === void 0) { - e.set(b, { order, flags }); - } - } else { - let map = new Map<string, { order: number, flags: number }>(); - map.set(b, { order, flags }); - this.map.set(a, map); - } - - if (swap) this.add(b, a, order, flags, false); - } - - constructor(public id: string) { - } - } - - export const PropName = '__ComponentBond__'; - export function fromModel(model: Model): ComponentBond | undefined { - if (model._staticPropertyData[PropName]) return model._staticPropertyData[PropName]; - - if (model.sourceData.kind !== 'mmCIF') return - const { chem_comp_bond } = model.sourceData.data; - if (!chem_comp_bond._rowCount) return void 0; - - let compBond = new ComponentBondImpl(); - - const { comp_id, atom_id_1, atom_id_2, value_order, pdbx_aromatic_flag, _rowCount: rowCount } = chem_comp_bond; - - let entry = compBond.addEntry(comp_id.value(0)!); - - for (let i = 0; i < rowCount; i++) { - - const id = comp_id.value(i)!; - const nameA = atom_id_1.value(i)!; - const nameB = atom_id_2.value(i)!; - const order = value_order.value(i)!; - const aromatic = pdbx_aromatic_flag.value(i) === 'Y'; - - if (entry.id !== id) { - entry = compBond.addEntry(id); - } - - let flags: number = LinkType.Flag.Covalent; - let ord = 1; - if (aromatic) flags |= LinkType.Flag.Aromatic; - switch (order.toLowerCase()) { - case 'doub': - case 'delo': - ord = 2; - break; - case 'trip': ord = 3; break; - case 'quad': ord = 4; break; - } - - entry.add(nameA, nameB, ord, flags); - } - - model._staticPropertyData[PropName] = compBond; - return compBond; - } -} \ No newline at end of file +export * from './bonds/comp' +export * from './bonds/struct_conn' \ No newline at end of file diff --git a/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts b/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts new file mode 100644 index 000000000..4b31456ab --- /dev/null +++ b/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2017-2018 Mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import Model from '../../../model' +import { LinkType } from '../../../types' + +export interface ComponentBond { + entries: Map<string, ComponentBond.Entry> +} + +export namespace ComponentBond { + export class ComponentBondImpl implements ComponentBond { + entries: Map<string, ComponentBond.Entry> = new Map(); + + addEntry(id: string) { + let e = new Entry(id); + this.entries.set(id, e); + return e; + } + } + + export class Entry implements Entry { + map: Map<string, Map<string, { order: number, flags: number }>> = new Map(); + + add(a: string, b: string, order: number, flags: number, swap = true) { + let e = this.map.get(a); + if (e !== void 0) { + let f = e.get(b); + if (f === void 0) { + e.set(b, { order, flags }); + } + } else { + let map = new Map<string, { order: number, flags: number }>(); + map.set(b, { order, flags }); + this.map.set(a, map); + } + + if (swap) this.add(b, a, order, flags, false); + } + + constructor(public id: string) { + } + } + + export const PropName = '__ComponentBond__'; + export function fromModel(model: Model): ComponentBond | undefined { + if (model._staticPropertyData[PropName]) return model._staticPropertyData[PropName]; + + if (model.sourceData.kind !== 'mmCIF') return + const { chem_comp_bond } = model.sourceData.data; + if (!chem_comp_bond._rowCount) return void 0; + + let compBond = new ComponentBondImpl(); + + const { comp_id, atom_id_1, atom_id_2, value_order, pdbx_aromatic_flag, _rowCount: rowCount } = chem_comp_bond; + + let entry = compBond.addEntry(comp_id.value(0)!); + + for (let i = 0; i < rowCount; i++) { + + const id = comp_id.value(i)!; + const nameA = atom_id_1.value(i)!; + const nameB = atom_id_2.value(i)!; + const order = value_order.value(i)!; + const aromatic = pdbx_aromatic_flag.value(i) === 'Y'; + + if (entry.id !== id) { + entry = compBond.addEntry(id); + } + + let flags: number = LinkType.Flag.Covalent; + let ord = 1; + if (aromatic) flags |= LinkType.Flag.Aromatic; + switch (order.toLowerCase()) { + case 'doub': + case 'delo': + ord = 2; + break; + case 'trip': ord = 3; break; + case 'quad': ord = 4; break; + } + + entry.add(nameA, nameB, ord, flags); + } + + model._staticPropertyData[PropName] = compBond; + return compBond; + } +} \ No newline at end of file diff --git a/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts b/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts new file mode 100644 index 000000000..d0c0e5f07 --- /dev/null +++ b/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2017-2018 Mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import Model from '../../../model' +import { Element } from '../../../../structure' +import { LinkType } from '../../../types' +import { findEntityIdByAsymId, findAtomIndexByLabelName } from '../util' +import { Column } from 'mol-data/db' + +export interface StructConn { + getResidueEntries(residueAIndex: number, residueBIndex: number): ReadonlyArray<StructConn.Entry> + getAtomEntries(atomIndex: number): ReadonlyArray<StructConn.Entry> +} + +export namespace StructConn { + function _resKey(rA: number, rB: number) { + if (rA < rB) return `${rA}-${rB}`; + return `${rB}-${rA}`; + } + const _emptyEntry: Entry[] = []; + + class StructConnImpl implements StructConn { + private _residuePairIndex: Map<string, StructConn.Entry[]> | undefined = void 0; + private _atomIndex: Map<number, StructConn.Entry[]> | undefined = void 0; + + private getResiduePairIndex() { + if (this._residuePairIndex) return this._residuePairIndex; + this._residuePairIndex = new Map(); + for (const e of this.entries) { + const ps = e.partners; + const l = ps.length; + for (let i = 0; i < l - 1; i++) { + for (let j = i + i; j < l; j++) { + const key = _resKey(ps[i].residueIndex, ps[j].residueIndex); + if (this._residuePairIndex.has(key)) { + this._residuePairIndex.get(key)!.push(e); + } else { + this._residuePairIndex.set(key, [e]); + } + } + } + } + return this._residuePairIndex; + } + + private getAtomIndex() { + if (this._atomIndex) return this._atomIndex; + this._atomIndex = new Map(); + for (const e of this.entries) { + for (const p of e.partners) { + const key = p.atomIndex; + if (this._atomIndex.has(key)) { + this._atomIndex.get(key)!.push(e); + } else { + this._atomIndex.set(key, [e]); + } + } + } + return this._atomIndex; + } + + + getResidueEntries(residueAIndex: number, residueBIndex: number): ReadonlyArray<StructConn.Entry> { + return this.getResiduePairIndex().get(_resKey(residueAIndex, residueBIndex)) || _emptyEntry; + } + + getAtomEntries(atomIndex: number): ReadonlyArray<StructConn.Entry> { + return this.getAtomIndex().get(atomIndex) || _emptyEntry; + } + + constructor(public entries: StructConn.Entry[]) { + } + } + + export interface Entry { + distance: number, + order: number, + flags: number, + partners: { residueIndex: number, atomIndex: Element, symmetry: string }[] + } + + type StructConnType = + | 'covale' + | 'covale_base' + | 'covale_phosphate' + | 'covale_sugar' + | 'disulf' + | 'hydrog' + | 'metalc' + | 'mismat' + | 'modres' + | 'saltbr' + + export const PropName = '__StructConn__'; + export function fromModel(model: Model): StructConn | undefined { + if (model._staticPropertyData[PropName]) return model._staticPropertyData[PropName]; + + if (model.sourceData.kind !== 'mmCIF') return; + const { struct_conn } = model.sourceData.data; + if (!struct_conn._rowCount) return void 0; + + const { conn_type_id, pdbx_dist_value, pdbx_value_order } = struct_conn; + const p1 = { + label_asym_id: struct_conn.ptnr1_label_asym_id, + label_comp_id: struct_conn.ptnr1_label_comp_id, + label_seq_id: struct_conn.ptnr1_label_seq_id, + label_atom_id: struct_conn.ptnr1_label_atom_id, + label_alt_id: struct_conn.pdbx_ptnr1_label_alt_id, + ins_code: struct_conn.pdbx_ptnr1_PDB_ins_code, + symmetry: struct_conn.ptnr1_symmetry + }; + const p2: typeof p1 = { + label_asym_id: struct_conn.ptnr2_label_asym_id, + label_comp_id: struct_conn.ptnr2_label_comp_id, + label_seq_id: struct_conn.ptnr2_label_seq_id, + label_atom_id: struct_conn.ptnr2_label_atom_id, + label_alt_id: struct_conn.pdbx_ptnr2_label_alt_id, + ins_code: struct_conn.pdbx_ptnr2_PDB_ins_code, + symmetry: struct_conn.ptnr2_symmetry + }; + + const _p = (row: number, ps: typeof p1) => { + if (ps.label_asym_id.valueKind(row) !== Column.ValueKind.Present) return void 0; + const asymId = ps.label_asym_id.value(row) + const residueIndex = model.atomicHierarchy.findResidueKey( + findEntityIdByAsymId(model, asymId), + ps.label_comp_id.value(row), + asymId, + ps.label_seq_id.value(row), + ps.ins_code.value(row) + ); + if (residueIndex < 0) return void 0; + const atomName = ps.label_atom_id.value(row); + // turns out "mismat" records might not have atom name value + if (!atomName) return void 0; + const atomIndex = findAtomIndexByLabelName(model, residueIndex, atomName, ps.label_alt_id.value(row)); + if (atomIndex < 0) return void 0; + return { residueIndex, atomIndex, symmetry: ps.symmetry.value(row) || '1_555' }; + } + + const _ps = (row: number) => { + const ret = []; + let p = _p(row, p1); + if (p) ret.push(p); + p = _p(row, p2); + if (p) ret.push(p); + return ret; + } + + const entries: StructConn.Entry[] = []; + for (let i = 0; i < struct_conn._rowCount; i++) { + const partners = _ps(i); + if (partners.length < 2) continue; + + const type = conn_type_id.value(i)! as StructConnType; + const orderType = (pdbx_value_order.value(i) || '').toLowerCase(); + let flags = LinkType.Flag.None; + let order = 1; + + switch (orderType) { + case 'sing': order = 1; break; + case 'doub': order = 2; break; + case 'trip': order = 3; break; + case 'quad': order = 4; break; + } + + switch (type) { + case 'covale': + case 'covale_base': + case 'covale_phosphate': + case 'covale_sugar': + case 'modres': + flags = LinkType.Flag.Covalent; + break; + case 'disulf': flags = LinkType.Flag.Covalent | LinkType.Flag.Sulfide; break; + case 'hydrog': flags = LinkType.Flag.Hydrogen; break; + case 'metalc': flags = LinkType.Flag.MetallicCoordination; break; + case 'saltbr': flags = LinkType.Flag.Ion; break; + } + + entries.push({ flags, order, distance: pdbx_dist_value.value(i), partners }); + } + + const ret = new StructConnImpl(entries); + model._staticPropertyData[PropName] = ret; + return ret; + } +} \ No newline at end of file diff --git a/src/mol-model/structure/query.ts b/src/mol-model/structure/query.ts index b5886cbda..428d976d8 100644 --- a/src/mol-model/structure/query.ts +++ b/src/mol-model/structure/query.ts @@ -8,13 +8,11 @@ import Selection from './query/selection' import Query from './query/query' import * as generators from './query/generators' import * as modifiers from './query/modifiers' -import props from './query/properties' import pred from './query/predicates' export const Queries = { generators, modifiers, - props, pred } diff --git a/src/mol-model/structure/query/generators.ts b/src/mol-model/structure/query/generators.ts index 0650a17ae..290a84341 100644 --- a/src/mol-model/structure/query/generators.ts +++ b/src/mol-model/structure/query/generators.ts @@ -6,8 +6,7 @@ import Query from './query' import Selection from './selection' -import P from './properties' -import { Element, Unit } from '../structure' +import { Element, Unit, StructureProperties as P } from '../structure' import { OrderedSet, Segmentation } from 'mol-data/int' import { LinearGroupingBuilder } from './utils/builders'; diff --git a/src/mol-model/structure/query/predicates.ts b/src/mol-model/structure/query/predicates.ts index f6e6dfc32..99ef95389 100644 --- a/src/mol-model/structure/query/predicates.ts +++ b/src/mol-model/structure/query/predicates.ts @@ -4,8 +4,7 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Element } from '../structure' -import P from './properties' +import { Element, StructureProperties as P } from '../structure' namespace Predicates { export interface SetLike<A> { has(v: A): boolean } diff --git a/src/mol-model/structure/query/properties.ts b/src/mol-model/structure/query/properties.ts index 3299acaae..66298c566 100644 --- a/src/mol-model/structure/query/properties.ts +++ b/src/mol-model/structure/query/properties.ts @@ -4,120 +4,4 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Element, Unit } from '../structure' -import { VdwRadius } from '../model/properties/atomic'; - -const constant = { - true: Element.property(l => true), - false: Element.property(l => false), - zero: Element.property(l => 0) -} - -function notAtomic(): never { - throw 'Property only available for atomic models.'; -} - -function notCoarse(kind?: string): never { - if (!!kind) throw `Property only available for coarse models (${kind}).`; - throw `Property only available for coarse models.`; -} - -const atom = { - key: Element.property(l => l.element), - - // Conformation - x: Element.property(l => l.unit.conformation.x(l.element)), - y: Element.property(l => l.unit.conformation.y(l.element)), - z: Element.property(l => l.unit.conformation.z(l.element)), - id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicConformation.atomId.value(l.element)), - occupancy: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicConformation.occupancy.value(l.element)), - B_iso_or_equiv: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicConformation.B_iso_or_equiv.value(l.element)), - - // Hierarchy - type_symbol: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.type_symbol.value(l.element)), - label_atom_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.label_atom_id.value(l.element)), - auth_atom_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.auth_atom_id.value(l.element)), - label_alt_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.label_alt_id.value(l.element)), - pdbx_formal_charge: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.pdbx_formal_charge.value(l.element)), - - // Derived - vdw_radius: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : VdwRadius(l.unit.model.atomicHierarchy.atoms.type_symbol.value(l.element))), -} - -const residue = { - key: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residueKey[l.unit.residueIndex[l.element]]), - - group_PDB: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.group_PDB.value(l.unit.residueIndex[l.element])), - label_comp_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.label_comp_id.value(l.unit.residueIndex[l.element])), - auth_comp_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.auth_comp_id.value(l.unit.residueIndex[l.element])), - label_seq_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.label_seq_id.value(l.unit.residueIndex[l.element])), - auth_seq_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.element])), - pdbx_PDB_ins_code: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.pdbx_PDB_ins_code.value(l.unit.residueIndex[l.element])), - - // 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]]), -} - -const chain = { - key: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chainKey[l.unit.chainIndex[l.element]]), - - label_asym_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chains.label_asym_id.value(l.unit.chainIndex[l.element])), - auth_asym_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chains.auth_asym_id.value(l.unit.chainIndex[l.element])), - label_entity_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chains.label_entity_id.value(l.unit.chainIndex[l.element])) -} - -const coarse = { - key: atom.key, - modelKey: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.modelKey[l.element]), - entityKey: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.entityKey[l.element]), - - x: atom.x, - y: atom.y, - z: atom.z, - - asym_id: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.asym_id.value(l.element)), - seq_id_begin: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.seq_id_begin.value(l.element)), - seq_id_end: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.seq_id_end.value(l.element)), - - sphere_radius: Element.property(l => !Unit.isSpheres(l.unit) ? notCoarse('spheres') : l.unit.coarseConformation.radius[l.element]), - sphere_rmsf: Element.property(l => !Unit.isSpheres(l.unit) ? notCoarse('spheres') : l.unit.coarseConformation.rmsf[l.element]), - - gaussian_weight: Element.property(l => !Unit.isGaussians(l.unit) ? notCoarse('gaussians') : l.unit.coarseConformation.weight[l.element]), - gaussian_covariance_matrix: Element.property(l => !Unit.isGaussians(l.unit) ? notCoarse('gaussians') : l.unit.coarseConformation.covariance_matrix[l.element]) -} - -function eK(l: Element.Location) { return !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.entityKey[l.unit.chainIndex[l.element]]; } - -const entity = { - key: eK, - - id: Element.property(l => l.unit.model.entities.data.id.value(eK(l))), - type: Element.property(l => l.unit.model.entities.data.type.value(eK(l))), - src_method: Element.property(l => l.unit.model.entities.data.src_method.value(eK(l))), - pdbx_description: Element.property(l => l.unit.model.entities.data.pdbx_description.value(eK(l))), - formula_weight: Element.property(l => l.unit.model.entities.data.formula_weight.value(eK(l))), - pdbx_number_of_molecules: Element.property(l => l.unit.model.entities.data.pdbx_number_of_molecules.value(eK(l))), - details: Element.property(l => l.unit.model.entities.data.details.value(eK(l))), - pdbx_mutation: Element.property(l => l.unit.model.entities.data.pdbx_mutation.value(eK(l))), - pdbx_fragment: Element.property(l => l.unit.model.entities.data.pdbx_fragment.value(eK(l))), - pdbx_ec: Element.property(l => l.unit.model.entities.data.pdbx_ec.value(eK(l))) -} - -const unit = { - operator_name: Element.property(l => l.unit.conformation.operator.name), - model_num: Element.property(l => l.unit.model.modelNum) -} - -const Properties = { - constant, - atom, - residue, - chain, - entity, - unit, - coarse -} - -type Properties = typeof Properties -export default Properties \ No newline at end of file +// TODO \ No newline at end of file diff --git a/src/mol-model/structure/structure.ts b/src/mol-model/structure/structure.ts index bed8ae1fd..e64227851 100644 --- a/src/mol-model/structure/structure.ts +++ b/src/mol-model/structure/structure.ts @@ -9,5 +9,6 @@ import Structure from './structure/structure' import Unit from './structure/unit' import StructureSymmetry from './structure/symmetry' import { Link } from './structure/unit/links' +import StructureProperties from './structure/properties' -export { Element, Link, Structure, Unit, StructureSymmetry } \ No newline at end of file +export { Element, Link, Structure, Unit, StructureSymmetry, StructureProperties } \ No newline at end of file diff --git a/src/mol-model/structure/structure/properties.ts b/src/mol-model/structure/structure/properties.ts new file mode 100644 index 000000000..943c219c2 --- /dev/null +++ b/src/mol-model/structure/structure/properties.ts @@ -0,0 +1,126 @@ +/** + * Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import Element from './element' +import Unit from './unit' +import { VdwRadius } from '../model/properties/atomic'; + +const constant = { + true: Element.property(l => true), + false: Element.property(l => false), + zero: Element.property(l => 0) +} + +function notAtomic(): never { + throw 'Property only available for atomic models.'; +} + +function notCoarse(kind?: string): never { + if (!!kind) throw `Property only available for coarse models (${kind}).`; + throw `Property only available for coarse models.`; +} + +// TODO: remove the type checks? + +const atom = { + key: Element.property(l => l.element), + + // Conformation + x: Element.property(l => l.unit.conformation.x(l.element)), + y: Element.property(l => l.unit.conformation.y(l.element)), + z: Element.property(l => l.unit.conformation.z(l.element)), + id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicConformation.atomId.value(l.element)), + occupancy: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicConformation.occupancy.value(l.element)), + B_iso_or_equiv: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicConformation.B_iso_or_equiv.value(l.element)), + + // Hierarchy + type_symbol: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.type_symbol.value(l.element)), + label_atom_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.label_atom_id.value(l.element)), + auth_atom_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.auth_atom_id.value(l.element)), + label_alt_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.label_alt_id.value(l.element)), + pdbx_formal_charge: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.atoms.pdbx_formal_charge.value(l.element)), + + // Derived + vdw_radius: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : VdwRadius(l.unit.model.atomicHierarchy.atoms.type_symbol.value(l.element))), +} + +const residue = { + key: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residueKey[l.unit.residueIndex[l.element]]), + + group_PDB: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.group_PDB.value(l.unit.residueIndex[l.element])), + label_comp_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.label_comp_id.value(l.unit.residueIndex[l.element])), + auth_comp_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.auth_comp_id.value(l.unit.residueIndex[l.element])), + label_seq_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.label_seq_id.value(l.unit.residueIndex[l.element])), + auth_seq_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.element])), + pdbx_PDB_ins_code: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.residues.pdbx_PDB_ins_code.value(l.unit.residueIndex[l.element])), + + // 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]]), +} + +const chain = { + key: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chainKey[l.unit.chainIndex[l.element]]), + + label_asym_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chains.label_asym_id.value(l.unit.chainIndex[l.element])), + auth_asym_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chains.auth_asym_id.value(l.unit.chainIndex[l.element])), + label_entity_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.chains.label_entity_id.value(l.unit.chainIndex[l.element])) +} + +const coarse = { + key: atom.key, + modelKey: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.modelKey[l.element]), + entityKey: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.entityKey[l.element]), + + x: atom.x, + y: atom.y, + z: atom.z, + + asym_id: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.asym_id.value(l.element)), + seq_id_begin: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.seq_id_begin.value(l.element)), + seq_id_end: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.coarseElements.seq_id_end.value(l.element)), + + sphere_radius: Element.property(l => !Unit.isSpheres(l.unit) ? notCoarse('spheres') : l.unit.coarseConformation.radius[l.element]), + sphere_rmsf: Element.property(l => !Unit.isSpheres(l.unit) ? notCoarse('spheres') : l.unit.coarseConformation.rmsf[l.element]), + + gaussian_weight: Element.property(l => !Unit.isGaussians(l.unit) ? notCoarse('gaussians') : l.unit.coarseConformation.weight[l.element]), + gaussian_covariance_matrix: Element.property(l => !Unit.isGaussians(l.unit) ? notCoarse('gaussians') : l.unit.coarseConformation.covariance_matrix[l.element]) +} + +function eK(l: Element.Location) { return !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.model.atomicHierarchy.entityKey[l.unit.chainIndex[l.element]]; } + +const entity = { + key: eK, + + id: Element.property(l => l.unit.model.entities.data.id.value(eK(l))), + type: Element.property(l => l.unit.model.entities.data.type.value(eK(l))), + src_method: Element.property(l => l.unit.model.entities.data.src_method.value(eK(l))), + pdbx_description: Element.property(l => l.unit.model.entities.data.pdbx_description.value(eK(l))), + formula_weight: Element.property(l => l.unit.model.entities.data.formula_weight.value(eK(l))), + pdbx_number_of_molecules: Element.property(l => l.unit.model.entities.data.pdbx_number_of_molecules.value(eK(l))), + details: Element.property(l => l.unit.model.entities.data.details.value(eK(l))), + pdbx_mutation: Element.property(l => l.unit.model.entities.data.pdbx_mutation.value(eK(l))), + pdbx_fragment: Element.property(l => l.unit.model.entities.data.pdbx_fragment.value(eK(l))), + pdbx_ec: Element.property(l => l.unit.model.entities.data.pdbx_ec.value(eK(l))) +} + +const unit = { + operator_name: Element.property(l => l.unit.conformation.operator.name), + model_num: Element.property(l => l.unit.model.modelNum) +} + +const StructureProperties = { + constant, + atom, + residue, + chain, + entity, + unit, + coarse +} + +type StructureProperties = typeof StructureProperties +export default StructureProperties \ No newline at end of file diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts index d36ea0a2b..5bca93fff 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -14,9 +14,9 @@ import Unit from './unit' import { StructureLookup3D } from './util/lookup3d'; import { CoarseElements } from '../model/properties/coarse'; import { StructureSubsetBuilder } from './util/subset-builder'; -import { Queries } from '../query'; import { InterUnitBonds, computeInterUnitBonds } from './unit/links'; import StructureSymmetry from './symmetry'; +import StructureProperties from './properties'; class Structure { readonly unitMap: IntMap<Unit>; @@ -69,11 +69,11 @@ class Structure { return this._links; } - private _symmetryGroups?: ReadonlyArray<Unit.SymmetryGroup> = void 0; - get symmetryGroups(): ReadonlyArray<Unit.SymmetryGroup> { - if (this._symmetryGroups) return this._symmetryGroups; - this._symmetryGroups = StructureSymmetry.computeTransformGroups(this); - return this._symmetryGroups; + private _unitSymmetryGroups?: ReadonlyArray<Unit.SymmetryGroup> = void 0; + get unitSymmetryGroups(): ReadonlyArray<Unit.SymmetryGroup> { + if (this._unitSymmetryGroups) return this._unitSymmetryGroups; + this._unitSymmetryGroups = StructureSymmetry.computeTransformGroups(this); + return this._unitSymmetryGroups; } constructor(units: ArrayLike<Unit>) { @@ -252,7 +252,7 @@ namespace Structure { const keys = UniqueArray.create<number, number>(); for (const unit of units) { - const prop = unit.kind === Unit.Kind.Atomic ? Queries.props.entity.key : Queries.props.coarse.entityKey; + const prop = unit.kind === Unit.Kind.Atomic ? StructureProperties.entity.key : StructureProperties.coarse.entityKey; l.unit = unit; const elements = unit.elements; diff --git a/src/mol-view/label.ts b/src/mol-view/label.ts index 3107afeb8..1623b27a9 100644 --- a/src/mol-view/label.ts +++ b/src/mol-view/label.ts @@ -5,7 +5,7 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Unit, Element, Queries } from 'mol-model/structure'; +import { Unit, Element, StructureProperties as Props } from 'mol-model/structure'; import { Loci } from 'mol-model/loci'; import { OrderedSet } from 'mol-data/int'; @@ -49,17 +49,17 @@ export function elementLabel(loc: Element.Location) { let element = '' if (Unit.isAtomic(loc.unit)) { - const asym_id = Queries.props.chain.auth_asym_id(loc) - const seq_id = Queries.props.residue.auth_seq_id(loc) - const comp_id = Queries.props.residue.auth_comp_id(loc) - const atom_id = Queries.props.atom.auth_atom_id(loc) + const asym_id = Props.chain.auth_asym_id(loc) + const seq_id = Props.residue.auth_seq_id(loc) + const comp_id = Props.residue.auth_comp_id(loc) + const atom_id = Props.atom.auth_atom_id(loc) element = `[${comp_id}]${seq_id}:${asym_id}.${atom_id}` } else if (Unit.isCoarse(loc.unit)) { - const asym_id = Queries.props.coarse.asym_id(loc) - const seq_id_begin = Queries.props.coarse.seq_id_begin(loc) - const seq_id_end = Queries.props.coarse.seq_id_end(loc) + const asym_id = Props.coarse.asym_id(loc) + const seq_id_begin = Props.coarse.seq_id_begin(loc) + const seq_id_end = Props.coarse.seq_id_end(loc) if (seq_id_begin === seq_id_end) { - const entityKey = Queries.props.coarse.entityKey(loc) + const entityKey = Props.coarse.entityKey(loc) const seq = loc.unit.model.sequence.byEntityKey[entityKey] const comp_id = seq.compId.value(seq_id_begin) element = `[${comp_id}]${seq_id_begin}:${asym_id}` diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts index 22914398f..ee8cae29c 100644 --- a/src/perf-tests/structure.ts +++ b/src/perf-tests/structure.ts @@ -11,7 +11,7 @@ import * as fs from 'fs' import fetch from 'node-fetch' import CIF from 'mol-io/reader/cif' -import { Structure, Model, Queries as Q, Element, Selection, StructureSymmetry, Query, Format } from 'mol-model/structure' +import { Structure, Model, Queries as Q, Element, Selection, StructureSymmetry, Query, Format, StructureProperties as SP } from 'mol-model/structure' //import { Segmentation, OrderedSet } from 'mol-data/int' import to_mmCIF from 'mol-model/structure/export/mmcif' @@ -293,7 +293,7 @@ export namespace PropertyAccess { export async function testAssembly(id: string, s: Structure) { console.time('assembly') const a = await StructureSymmetry.buildAssembly(s, '1').run(); - //const auth_comp_id = Q.props.residue.auth_comp_id; + //const auth_comp_id = SP.residue.auth_comp_id; //const q1 = Query(Q.generators.atoms({ residueTest: l => auth_comp_id(l) === 'ALA' })); //const alas = await query(q1, a); @@ -306,7 +306,7 @@ export namespace PropertyAccess { export async function testSymmetry(id: string, s: Structure) { console.time('symmetry') const a = await StructureSymmetry.buildSymmetryRange(s, Vec3.create(-1, -1, -1), Vec3.create(1, 1, 1)).run(); - //const auth_comp_id = Q.props.residue.auth_comp_id; + //const auth_comp_id = SP.residue.auth_comp_id; //const q1 = Query(Q.generators.atoms({ residueTest: l => auth_comp_id(l) === 'ALA' })); //const alas = await query(q1, a); @@ -322,7 +322,7 @@ export namespace PropertyAccess { const a = await StructureSymmetry.buildSymmetryRange(s, Vec3.create(-2, -2, -2), Vec3.create(2, 2, 2)).run(); //console.log(printUnits(a)); - const auth_comp_id = Q.props.residue.auth_comp_id, op = Q.props.unit.operator_name; + const auth_comp_id = SP.residue.auth_comp_id, op = SP.unit.operator_name; //const q1 = Q.generators.atoms({ residueTest: l => auth_comp_id(l) === 'REA' }); const q1 = Q.modifiers.includeSurroundings(Q.generators.atoms({ chainTest: l => op(l) === '1_555', @@ -345,7 +345,7 @@ export namespace PropertyAccess { // const it = surr.elementLocations(); // while (it.hasNext) { // const e = it.move(); - // console.log(`${Q.props.unit.operator_name(e)} ${Q.props.atom.id(e)}`); + // console.log(`${SP.unit.operator_name(e)} ${SP.atom.id(e)}`); // } //fs.writeFileSync(`${DATA_DIR}/${id}_surr.bcif`, to_mmCIF(id, a, true)); fs.writeFileSync(`${DATA_DIR}/${id}_surr.cif`, to_mmCIF(id, surr, false)); @@ -418,7 +418,7 @@ export namespace PropertyAccess { //console.log('r', sumPropertyResidue(structures[0], l => l.unit.hierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.atom]))); console.time('atom.x'); - console.log('atom.x', sumProperty(structures[0], Q.props.atom.x)); + console.log('atom.x', sumProperty(structures[0], SP.atom.x)); console.timeEnd('atom.x'); console.time('__x') //console.log('__x', sumProperty(structures[0], l => l.unit.conformation.x[l.atom])); @@ -426,16 +426,16 @@ export namespace PropertyAccess { //const authSeqId = Element.property(l => l.unit.hierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.atom])); - //const auth_seq_id = Q.props.residue.auth_seq_id; - const auth_comp_id = Q.props.residue.auth_comp_id; - //const auth_asym_id = Q.props.chain.auth_asym_id; + //const auth_seq_id = SP.residue.auth_seq_id; + const auth_comp_id = SP.residue.auth_comp_id; + //const auth_asym_id = SP.chain.auth_asym_id; //const set = new Set(['A', 'B', 'C', 'D']); //const q = Q.generators.atomGroups({ atomTest: l => auth_seq_id(l) < 3 }); - const q = Query(Q.generators.atoms({ atomTest: Q.pred.eq(Q.props.residue.auth_comp_id, 'ALA') })); - const P = Q.props + const q = Query(Q.generators.atoms({ atomTest: Q.pred.eq(SP.residue.auth_comp_id, 'ALA') })); + const P = SP //const q0 = Q.generators.atoms({ atomTest: l => auth_comp_id(l) === 'ALA' }); const q1 = Query(Q.generators.atoms({ residueTest: l => auth_comp_id(l) === 'ALA' })); - const q2 = Query(Q.generators.atoms({ residueTest: l => auth_comp_id(l) === 'ALA', groupBy: Q.props.residue.key })); + const q2 = Query(Q.generators.atoms({ residueTest: l => auth_comp_id(l) === 'ALA', groupBy: SP.residue.key })); const q3 = Query(Q.generators.atoms({ chainTest: Q.pred.inSet(P.chain.auth_asym_id, ['A', 'B', 'C', 'D']), residueTest: Q.pred.eq(P.residue.auth_comp_id, 'ALA') diff --git a/src/servers/model/server/api.ts b/src/servers/model/server/api.ts index 96820b82d..88830f6cd 100644 --- a/src/servers/model/server/api.ts +++ b/src/servers/model/server/api.ts @@ -4,7 +4,7 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Query, Queries, Structure, Element, StructureSymmetry } from 'mol-model/structure'; +import { Query, Queries, Structure, Element, StructureSymmetry, StructureProperties as Props } from 'mol-model/structure'; export enum QueryParamType { String, @@ -47,23 +47,23 @@ const AtomSiteParameters = { // function entityTest(params: any): Element.Predicate | undefined { // if (typeof params.entity_id === 'undefined') return void 0; -// const p = Queries.props.entity.id, id = '' + params.entityId; +// const p = Props.entity.id, id = '' + params.entityId; // return Element.property(l => p(l) === id); // } function entityTest1_555(params: any): Element.Predicate | undefined { if (typeof params.entity_id === 'undefined') return Element.property(l => l.unit.conformation.operator.isIdentity); - const p = Queries.props.entity.id, id = '' + params.entityId; + const p = Props.entity.id, id = '' + params.entityId; return Element.property(l => l.unit.conformation.operator.isIdentity && p(l) === id); } function chainTest(params: any): Element.Predicate | undefined { if (typeof params.label_asym_id !== 'undefined') { - const p = Queries.props.chain.label_asym_id, id = '' + params.label_asym_id; + const p = Props.chain.label_asym_id, id = '' + params.label_asym_id; return Element.property(l => p(l) === id); } if (typeof params.auth_asym_id !== 'undefined') { - const p = Queries.props.chain.auth_asym_id, id = '' + params.auth_asym_id; + const p = Props.chain.auth_asym_id, id = '' + params.auth_asym_id; return Element.property(l => p(l) === id); } return void 0; @@ -73,27 +73,27 @@ function residueTest(params: any): Element.Predicate | undefined { const props: Element.Property<any>[] = [], values: any[] = []; if (typeof params.label_seq_id !== 'undefined') { - props.push(Queries.props.residue.label_seq_id); + props.push(Props.residue.label_seq_id); values.push(+params.label_seq_id); } if (typeof params.auth_seq_id !== 'undefined') { - props.push(Queries.props.residue.auth_seq_id); + props.push(Props.residue.auth_seq_id); values.push(+params.auth_seq_id); } if (typeof params.label_comp_id !== 'undefined') { - props.push(Queries.props.residue.label_comp_id); + props.push(Props.residue.label_comp_id); values.push(params.label_comp_id); } if (typeof params.auth_comp_id !== 'undefined') { - props.push(Queries.props.residue.auth_comp_id); + props.push(Props.residue.auth_comp_id); values.push(params.auth_comp_id); } if (typeof params.pdbx_PDB_ins_code !== 'undefined') { - props.push(Queries.props.residue.pdbx_PDB_ins_code); + props.push(Props.residue.pdbx_PDB_ins_code); values.push(params.pdbx_PDB_ins_code); } -- GitLab