diff --git a/src/apps/structure-info/model.ts b/src/apps/structure-info/model.ts index 72557cb51b86b8d8110759100bcc660c65c49a64..7a9919b35299824e78876806371e3497397c96c9 100644 --- a/src/apps/structure-info/model.ts +++ b/src/apps/structure-info/model.ts @@ -9,7 +9,8 @@ import * as argparse from 'argparse' require('util.promisify').shim(); 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'; import { openCif, downloadCif } from './helpers'; @@ -164,7 +165,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 520505ca2df4992a7466a06491d39893321e8342..6025eea22076a5e71ed57bc3c2d5be2ded4be068 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-data/int/impl/segmentation.ts b/src/mol-data/int/impl/segmentation.ts index 63313431fc2322f165ff8950964ba8964e2313e1..a5534e52a2d412e9be7e288fe13b9a39ba51e704 100644 --- a/src/mol-data/int/impl/segmentation.ts +++ b/src/mol-data/int/impl/segmentation.ts @@ -54,7 +54,7 @@ export class SegmentIterator<T extends number = number> implements Iterator<Segs private segmentMin = 0; private segmentMax = 0; private setRange = Interval.Empty; - private value: Segs.Segment<T> = { index: 0, start: 0, end: 0 }; + private value: Segs.Segment<T> = { index: 0, start: 0 as T, end: 0 as T }; hasNext: boolean = false; @@ -75,8 +75,8 @@ export class SegmentIterator<T extends number = number> implements Iterator<Segs const segmentEnd = this.segments[this.segmentMin + 1]; // TODO: add optimized version for interval and array? const setEnd = OrderedSet.findPredecessorIndexInInterval(this.set, segmentEnd, this.setRange); - this.value.start = Interval.start(this.setRange); - this.value.end = setEnd; + this.value.start = Interval.start(this.setRange) as T; + this.value.end = setEnd as T; this.setRange = Interval.ofBounds(setEnd, Interval.end(this.setRange)); return setEnd > this.value.start; } diff --git a/src/mol-data/int/segmentation.ts b/src/mol-data/int/segmentation.ts index 8f7c58db6c2c644aaeae4b971180f645118a2caa..94abaaa0f1b823c16ff5b37e252e7d5b7c40af8e 100644 --- a/src/mol-data/int/segmentation.ts +++ b/src/mol-data/int/segmentation.ts @@ -9,7 +9,7 @@ import OrderedSet from './ordered-set' import * as Impl from './impl/segmentation' namespace Segmentation { - export interface Segment<T extends number = number> { index: number, start: number, end: number } + export interface Segment<T extends number = number> { index: number, start: T, end: T } export const create: <T extends number = number>(segs: ArrayLike<T>) => Segmentation<T> = Impl.create as any; export const ofOffsets: <T extends number = number>(offsets: ArrayLike<T>, bounds: Interval) => Segmentation<T> = Impl.ofOffsets as any; @@ -19,7 +19,7 @@ namespace Segmentation { export const projectValue: <T extends number = number>(segs: Segmentation<T>, set: OrderedSet<T>, value: T) => Interval = Impl.projectValue as any; // Segment iterator that mutates a single segment object to mark all the segments. - export const transientSegments: <T extends number = number>(segs: Segmentation<T>, set: OrderedSet<T>, segment?: Segment<T>) => Impl.SegmentIterator = Impl.segments as any; + export const transientSegments: <T extends number = number>(segs: Segmentation<T>, set: OrderedSet<T>, segment?: Segment<T>) => Impl.SegmentIterator<T> = Impl.segments as any; } interface Segmentation<T extends number = number> { diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts index d0bc2eb18a8188c3c9f739580d9e2266a53484a4..a93b138f23a159c47d25be664b8237d4ad1c646c 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 '..'; @@ -105,7 +105,7 @@ export function StructureUnitsRepresentation<P extends StructureProps>(visualCto return Task.create('Creating StructureRepresentation', async ctx => { if (!_structure) { - _groups = StructureSymmetry.getTransformGroups(structure); + _groups = structure.unitSymmetryGroups; for (let i = 0; i < _groups.length; i++) { const group = _groups[i]; const visual = visualCtor() @@ -116,7 +116,7 @@ export function StructureUnitsRepresentation<P extends StructureProps>(visualCto if (_structure.hashCode === structure.hashCode) { await update(_props) } else { - _groups = StructureSymmetry.getTransformGroups(structure); + _groups = structure.unitSymmetryGroups; const newGroups: Unit.SymmetryGroup[] = [] const oldUnitsVisuals = visuals visuals = 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 bc3cf8fd08490430a9421b9bb1cdcf76160bcc16..0b0e13114d7614db98054ca521166d4272f43693 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, createUniformColor } from '../../../util/color-data'; @@ -13,11 +13,12 @@ import { ColorScale } from 'mol-util/color'; function getAsymId(unit: Unit): Element.Property<string> { switch (unit.kind) { case Unit.Kind.Atomic: - return Queries.props.chain.label_asym_id + return StructureProperties.chain.label_asym_id case Unit.Kind.Spheres: case Unit.Kind.Gaussians: - return Queries.props.coarse.asym_id + return StructureProperties.coarse.asym_id } + throw new Error('unhandled unit kind') } export function chainIdColorData(props: StructureColorDataProps, locationFn: (l: Element.Location, renderElementIdx: number) => void, colorData?: ColorData) { diff --git a/src/mol-geo/theme/structure/size/physical.ts b/src/mol-geo/theme/structure/size/physical.ts index 84be4d33977008f9b1e4ad2812883602f7d636ca..34ac86d0de5e92e49b1c659a0ffa704d3489d500 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 0000000000000000000000000000000000000000..7ee77164560dbb31d8faf949370b1b250986595d --- /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/mmcif.ts b/src/mol-model/structure/export/mmcif.ts index ea699f649fe5e900813d315af5cad2717b39bbb5..f09ee2103f814b5f7be1408602c05a39a9b65fa7 100644 --- a/src/mol-model/structure/export/mmcif.ts +++ b/src/mol-model/structure/export/mmcif.ts @@ -7,53 +7,19 @@ 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'; -interface Context { +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) { - return ({ model }: Context) => { +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]; if (!table || !table._rowCount) return CifCategory.Empty; @@ -61,32 +27,20 @@ function copy_mmCif_cat(name: keyof mmCIF_Schema) { }; } -function _entity({ model, structure }: Context): CifCategory { +function _entity({ model, structure }: CifExportContext): CifCategory { const keys = Structure.getEntityKeys(structure); return CifCategory.ofTable('entity', model.entities.data, keys); } -function _atom_site({ structure }: Context): 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']); } @@ -104,11 +58,17 @@ export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structure: S if (models.length !== 1) throw 'Can\'t export stucture composed from multiple models.'; const model = models[0]; - const ctx: Context[] = [{ structure, model }]; + const ctx: CifExportContext[] = [{ structure, model }]; for (const cat of Categories) { encoder.writeCategory(cat, ctx); } + for (const customProp of model.customProperties.all) { + const cats = customProp.cifExport.categoryProvider(ctx[0]); + for (const cat of cats) { + encoder.writeCategory(cat, ctx); + } + } } function to_mmCIF(name: string, structure: Structure, asBinary = false) { diff --git a/src/mol-model/structure/model/formats/mmcif.ts b/src/mol-model/structure/model/formats/mmcif.ts index 81db623314986389b8098558935309ea31f1d5c0..924fc7ba96a5dbcb74030fba2ab2eef8aebc0198 100644 --- a/src/mol-model/structure/model/formats/mmcif.ts +++ b/src/mol-model/structure/model/formats/mmcif.ts @@ -24,8 +24,10 @@ import { getSequence } from './mmcif/sequence'; import { sortAtomSite } from './mmcif/sort'; import { mmCIF_Database, mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif'; import { Element } from '../../../structure' +import { CustomProperties } from '../properties/custom'; import mmCIF_Format = Format.mmCIF +import { ComponentBond } from './mmcif/bonds'; type AtomSite = mmCIF_Database['atom_site'] function findModelEnd({ data }: mmCIF_Format, startIndex: number) { @@ -208,6 +210,7 @@ function createModel(format: mmCIF_Format, atom_site: AtomSite, previous?: Model sourceData: format, modelNum: atom_site.pdbx_PDB_model_num.value(0), entities, + symmetry: getSymmetry(format), atomicHierarchy, sequence: getSequence(format.data, entities, atomicHierarchy, modifiedResidueNameMap), atomicConformation: getConformation(atom_site), @@ -218,10 +221,16 @@ function createModel(format: mmCIF_Format, atom_site: AtomSite, previous?: Model modifiedResidueNameMap, asymIdSerialMap }, - symmetry: getSymmetry(format) + customProperties: new CustomProperties(), + _staticPropertyData: Object.create(null), + _dynamicPropertyData: Object.create(null) }; } +function attachProps(model: Model) { + ComponentBond.attachFromMmCif(model); +} + function buildModels(format: mmCIF_Format): Task<ReadonlyArray<Model>> { return Task.create('Create mmCIF Model', async ctx => { const atomCount = format.data.atom_site._rowCount; @@ -239,6 +248,7 @@ function buildModels(format: mmCIF_Format): Task<ReadonlyArray<Model>> { const modelEnd = findModelEnd(format, modelStart); const atom_site = await sortAtomSite(ctx, format.data.atom_site, modelStart, modelEnd); const model = createModel(format, atom_site, models.length > 0 ? models[models.length - 1] : void 0); + attachProps(model); models.push(model); modelStart = modelEnd; } diff --git a/src/mol-model/structure/model/formats/mmcif/assembly.ts b/src/mol-model/structure/model/formats/mmcif/assembly.ts index 48720596c26741364f2acc6306ae47ba87ad3426..c86abd27cfd5b2d3a156268cc3ed5967e7639160 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 4eb62528687553ac5ba1ff9afb74d2ea0d31afa2..0f69970236251826f5c529d481726b6d2c82b8fc 100644 --- a/src/mol-model/structure/model/formats/mmcif/bonds.ts +++ b/src/mol-model/structure/model/formats/mmcif/bonds.ts @@ -5,271 +5,5 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import Model from '../../model' -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 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: number, 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.properties[PropName]) return model.properties[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.properties[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.properties[PropName]) return model.properties[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.properties[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 0000000000000000000000000000000000000000..fe0c4ab4ed029eab636dd4fdd63e888ec2512a87 --- /dev/null +++ b/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts @@ -0,0 +1,150 @@ +/** + * 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' +import { ModelPropertyDescriptor } from '../../../properties/custom'; +import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif'; +import { Structure, Unit, StructureProperties, Element } from '../../../../structure'; +import { Segmentation } from 'mol-data/int'; +import { CifWriter } from 'mol-io/writer/cif' + +export interface ComponentBond { + entries: Map<string, ComponentBond.Entry> +} + +export namespace ComponentBond { + export const Descriptor: ModelPropertyDescriptor = { + isStatic: true, + name: 'chem_comp_bond', + cifExport: { + categoryNames: ['chem_comp_bond'], + categoryProvider(ctx) { + const comp_names = getUniqueResidueNames(ctx.structure); + const chem_comp_bond = getChemCompBond(ctx.model); + + if (!chem_comp_bond) return []; + + const { comp_id, _rowCount } = chem_comp_bond; + const indices: number[] = []; + for (let i = 0; i < _rowCount; i++) { + if (comp_names.has(comp_id.value(i))) indices[indices.length] = i; + } + + return [ + () => CifWriter.Category.ofTable('chem_comp_bond', chem_comp_bond, indices) + ]; + } + } + } + + export function attachFromMmCif(model: Model): boolean { + if (model.sourceData.kind !== 'mmCIF') return false; + const { chem_comp_bond } = model.sourceData.data; + if (chem_comp_bond._rowCount === 0) return false; + model.customProperties.add(Descriptor); + model._staticPropertyData.__ComponentBondData__ = chem_comp_bond; + return true; + } + + 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) { + } + } + + function getChemCompBond(model: Model) { + return model._staticPropertyData.__ComponentBondData__ as mmCIF_Database['chem_comp_bond']; + } + + export const PropName = '__ComponentBond__'; + export function get(model: Model): ComponentBond | undefined { + if (model._staticPropertyData[PropName]) return model._staticPropertyData[PropName]; + if (!model.customProperties.has(Descriptor)) return void 0; + const chem_comp_bond = getChemCompBond(model); + + 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; + } + + function getUniqueResidueNames(s: Structure) { + const prop = StructureProperties.residue.label_comp_id; + const names = new Set<string>(); + const loc = Element.Location(); + for (const unit of s.units) { + if (!Unit.isAtomic(unit)) continue; + const residues = Segmentation.transientSegments(unit.model.atomicHierarchy.residueSegments, unit.elements); + loc.unit = unit; + while (residues.hasNext) { + const seg = residues.move(); + loc.element = seg.start; + names.add(prop(loc)); + } + } + return names; + } +} \ 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 0000000000000000000000000000000000000000..d0c0e5f07e0ab70bc4db546a0dcc68395a06b5d4 --- /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/model/formats/mmcif/util.ts b/src/mol-model/structure/model/formats/mmcif/util.ts index 1817e1c22868410ce6529934aa279e3096654189..2bccf1a97fb9209d8250ebc4b726447bb29214d3 100644 --- a/src/mol-model/structure/model/formats/mmcif/util.ts +++ b/src/mol-model/structure/model/formats/mmcif/util.ts @@ -5,6 +5,7 @@ */ import Model from '../../model' +import { Element } from '../../../structure' export function findEntityIdByAsymId(model: Model, asymId: string) { if (model.sourceData.kind !== 'mmCIF') return '' @@ -15,12 +16,12 @@ export function findEntityIdByAsymId(model: Model, asymId: string) { return '' } -export function findAtomIndexByLabelName(model: Model, residueIndex: number, atomName: string, altLoc: string | null) { +export function findAtomIndexByLabelName(model: Model, residueIndex: number, atomName: string, altLoc: string | null): Element { const { segmentMap, segments } = model.atomicHierarchy.residueSegments const idx = segmentMap[residueIndex] const { label_atom_id, label_alt_id } = model.atomicHierarchy.atoms; for (let i = segments[idx], n = segments[idx + 1]; i <= n; ++i) { - if (label_atom_id.value(i) === atomName && (!altLoc || label_alt_id.value(i) === altLoc)) return i; + if (label_atom_id.value(i) === atomName && (!altLoc || label_alt_id.value(i) === altLoc)) return i as Element; } - return -1; + return -1 as Element; } \ No newline at end of file diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts index 48a16e1339dc261cb8597046e7e75fd770d521bc..c76964e053f6f63e35f8409f59e64b263281173d 100644 --- a/src/mol-model/structure/model/model.ts +++ b/src/mol-model/structure/model/model.ts @@ -11,6 +11,7 @@ import { AtomicHierarchy, AtomicConformation } from './properties/atomic' import { ModelSymmetry } from './properties/symmetry' import { CoarseHierarchy, CoarseConformation } from './properties/coarse' import { Entities } from './properties/common'; +import { CustomProperties } from './properties/custom'; import { SecondaryStructure } from './properties/seconday-structure'; // import from_gro from './formats/gro' @@ -35,16 +36,22 @@ interface Model extends Readonly<{ atomicHierarchy: AtomicHierarchy, atomicConformation: AtomicConformation, - /** Various parts of the code can "cache" custom properties here */ properties: { + // secondary structure provided by the input file readonly secondaryStructure: SecondaryStructure, // maps modified residue name to its parent readonly modifiedResidueNameMap: Map<string, string>, - readonly asymIdSerialMap: Map<string, number>, - [customName: string]: any + readonly asymIdSerialMap: Map<string, number> }, - // TODO: separate properties to "static" (propagated with trajectory) and "dynamic" (computed for each frame separately) + customProperties: CustomProperties, + + /** + * Not to be accessed directly, each custom property descriptor + * defines property accessors that use this field to store the data. + */ + _staticPropertyData: { [name: string]: any }, + _dynamicPropertyData: { [name: string]: any }, coarseHierarchy: CoarseHierarchy, coarseConformation: CoarseConformation diff --git a/src/mol-model/structure/model/properties/custom.ts b/src/mol-model/structure/model/properties/custom.ts new file mode 100644 index 0000000000000000000000000000000000000000..d9a06eee1c7a5f4e19ae8cc462117427a020ca4a --- /dev/null +++ b/src/mol-model/structure/model/properties/custom.ts @@ -0,0 +1,8 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +export * from './custom/descriptor' +export * from './custom/collection' \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/custom/collection.ts b/src/mol-model/structure/model/properties/custom/collection.ts new file mode 100644 index 0000000000000000000000000000000000000000..371889b8f34034d25fbb6d84f0c59921b2c70303 --- /dev/null +++ b/src/mol-model/structure/model/properties/custom/collection.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { ModelPropertyDescriptor } from './descriptor' + +export class CustomProperties { + private _list: ModelPropertyDescriptor[] = []; + private _set = new Set<ModelPropertyDescriptor>(); + + get all(): ReadonlyArray<ModelPropertyDescriptor> { + return this._list; + } + + add(desc: ModelPropertyDescriptor) { + this._list.push(desc); + this._set.add(desc); + } + + has(desc: ModelPropertyDescriptor): boolean { + return this._set.has(desc); + } +} \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/custom/descriptor.ts b/src/mol-model/structure/model/properties/custom/descriptor.ts new file mode 100644 index 0000000000000000000000000000000000000000..89304d1972e5163eb24b03b44cd925de79a789dd --- /dev/null +++ b/src/mol-model/structure/model/properties/custom/descriptor.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { CifWriter } from 'mol-io/writer/cif' +import { CifExportContext } from '../../../export/mmcif'; + +interface ModelPropertyDescriptor { + readonly isStatic: boolean, + readonly name: string, + + cifExport: { + /** used category names that can be used for "filtering" by the writer */ + readonly categoryNames: ReadonlyArray<string>, + categoryProvider: (ctx: CifExportContext) => CifWriter.Category.Provider[] + } +} + +export { ModelPropertyDescriptor } \ No newline at end of file diff --git a/src/mol-model/structure/query.ts b/src/mol-model/structure/query.ts index b5886cbda873b29ebfbf23534c64609b8f1197b2..428d976d85872f423b3c4756cc77340d96844c7e 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 0650a17ae0690e5147a54239991436c8eda6fde9..290a8434117072f3439ab327cc212948021e1369 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 f6e6dfc32b80f5c1fc554974c59976a2d672a8c3..99ef9538902702dcc4435009327cb42dba429995 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 3299acaaee21a426e2e3069845768e132b63aca3..66298c56624811ef6ca5163c4ced45228d0ec026 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 bed8ae1fd045532d92ac1922e95cfdcd5b5afc04..e642278518140bb5e51cc26e31f11245e12a57da 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 0000000000000000000000000000000000000000..943c219c239e6d7a5c04d13fa5cb4f10a2de79da --- /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 90b1d3a5c3967c4bab0860b20f1ea8812076af8a..7202df6b7937c805104ed526a5c63e393c7d1c8f 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -14,9 +14,10 @@ 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 { CrossLinkRestraints, extractCrossLinkRestraints } from './unit/pair-restraints'; +import StructureSymmetry from './symmetry'; +import StructureProperties from './properties'; class Structure { readonly unitMap: IntMap<Unit>; @@ -77,6 +78,13 @@ class Structure { return this._crossLinkRestraints; } + 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>) { const map = IntMap.Mutable<Unit>(); let elementCount = 0; @@ -253,7 +261,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-model/structure/structure/symmetry.ts b/src/mol-model/structure/structure/symmetry.ts index a7836b7927788cc72d92e245d3f8cbaef3548958..5c71b11142568faa9c0fbbc5a49312cd60265170 100644 --- a/src/mol-model/structure/structure/symmetry.ts +++ b/src/mol-model/structure/structure/symmetry.ts @@ -68,7 +68,7 @@ namespace StructureSymmetry { return EquivalenceClasses<number, Unit>(hashUnit, areUnitsEquivalent); } - export function getTransformGroups(s: Structure): ReadonlyArray<Unit.SymmetryGroup> { + export function computeTransformGroups(s: Structure): ReadonlyArray<Unit.SymmetryGroup> { const groups = UnitEquivalenceBuilder(); for (const u of s.units) groups.add(u.id, u); diff --git a/src/mol-model/structure/structure/unit/links/inter-compute.ts b/src/mol-model/structure/structure/unit/links/inter-compute.ts index 3eeb8ecc97e0b78ed83e5842f07350aaa326fdac..40eb99a7a87d94f7da5d007def10c286b4db1590 100644 --- a/src/mol-model/structure/structure/unit/links/inter-compute.ts +++ b/src/mol-model/structure/structure/unit/links/inter-compute.ts @@ -11,6 +11,7 @@ import Unit from '../../unit'; import { getElementIdx, getElementPairThreshold, getElementThreshold, isHydrogen, LinkComputationParameters, MetalsSet } from './common'; import { InterUnitBonds } from './data'; import { UniqueArray } from 'mol-data/generic'; +import { SortedArray } from 'mol-data/int'; const MAX_RADIUS = 4; @@ -57,6 +58,19 @@ function findPairLinks(unitA: Unit.Atomic, unitB: Unit.Atomic, params: LinkCompu const metalA = MetalsSet.has(aeI); const structConnEntries = params.forceCompute ? void 0 : structConn && structConn.getAtomEntries(aI); + if (structConnEntries) { + for (const se of structConnEntries) { + if (se.distance < MAX_RADIUS) continue; + + for (const p of se.partners) { + const _bI = SortedArray.indexOf(unitB.elements, p.atomIndex); + if (_bI < 0) continue; + addLink(_aI, _bI, se.order, se.flags, state); + bondCount++; + } + } + } + for (let ni = 0; ni < count; ni++) { const _bI = indices[ni]; const bI = atomsB[_bI]; diff --git a/src/mol-model/structure/structure/unit/links/intra-compute.ts b/src/mol-model/structure/structure/unit/links/intra-compute.ts index 28ee7cc7d701f49d3fe91af6f6a96d40bc01a313..c17d8153972e6254db102ac5e02db46191671bda 100644 --- a/src/mol-model/structure/structure/unit/links/intra-compute.ts +++ b/src/mol-model/structure/structure/unit/links/intra-compute.ts @@ -10,6 +10,7 @@ import { StructConn, ComponentBond } from '../../../model/formats/mmcif/bonds' import Unit from '../../unit' import { IntAdjacencyGraph } from 'mol-math/graph'; import { LinkComputationParameters, getElementIdx, MetalsSet, getElementThreshold, isHydrogen, getElementPairThreshold } from './common'; +import { SortedArray } from 'mol-data/int'; function getGraph(atomA: number[], atomB: number[], _order: number[], _flags: number[], atomCount: number): IntraUnitLinks { const builder = new IntAdjacencyGraph.EdgeBuilder(atomCount, atomA, atomB); @@ -35,7 +36,7 @@ function _computeBonds(unit: Unit.Atomic, params: LinkComputationParameters): In const query3d = unit.lookup3d; const structConn = unit.model.sourceData.kind === 'mmCIF' ? StructConn.fromModel(unit.model) : void 0; - const component = unit.model.sourceData.kind === 'mmCIF' ? ComponentBond.fromModel(unit.model) : void 0; + const component = unit.model.sourceData.kind === 'mmCIF' ? ComponentBond.get(unit.model) : void 0; const atomA: number[] = []; const atomB: number[] = []; @@ -70,6 +71,21 @@ function _computeBonds(unit: Unit.Atomic, params: LinkComputationParameters): In const metalA = MetalsSet.has(aeI); const structConnEntries = params.forceCompute ? void 0 : structConn && structConn.getAtomEntries(aI); + if (structConnEntries) { + for (const se of structConnEntries) { + if (se.distance < MAX_RADIUS) continue; + + for (const p of se.partners) { + const _bI = SortedArray.indexOf(unit.elements, p.atomIndex); + if (_bI < 0) continue; + atomA[atomA.length] = _aI; + atomB[atomB.length] = _bI; + flags[flags.length] = se.flags; + order[order.length] = se.order; + } + } + } + for (let ni = 0; ni < count; ni++) { const _bI = indices[ni]; const bI = atoms[_bI]; diff --git a/src/mol-view/label.ts b/src/mol-view/label.ts index 3107afeb8c5bca913559c98cb482b43c6a3c0a4b..1623b27a95f0937b6b67060af6168bf0ca44bb17 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 22914398f3b5edfff51467026b3dc3e156cef832..ee8cae29c8f7465af1b6c26a340df099a41747b0 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 96820b82d903b31d9d61d9d9312ef6c84ed276c4..88830f6cd93311f75a0f404ed42315e3259e6223 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); }