diff --git a/src/apps/render-test/state.ts b/src/apps/render-test/state.ts index f31133a57082d202a4cc658cc6fb22bd6a95f801..b262e21353b61941748cfc4c08c4ff9015ecfb10 100644 --- a/src/apps/render-test/state.ts +++ b/src/apps/render-test/state.ts @@ -45,7 +45,7 @@ export default class State { this.loading.next(true) const structures = await getStructuresFromPdbId(pdbId) - const struct = Symmetry.buildAssembly(structures[0], '1') + const struct = await Run(Symmetry.buildAssembly(structures[0], '1')) // const structPointRepr = StructureRepresentation(Point) // await Run(structPointRepr.create(struct)) diff --git a/src/apps/structure-info/index.ts b/src/apps/structure-info/index.ts index 6f606817361e4b7afd05269a9d2335d0cb7c7289..5233f459b883ed17617f53fff9391cb95ba1a944 100644 --- a/src/apps/structure-info/index.ts +++ b/src/apps/structure-info/index.ts @@ -10,8 +10,9 @@ require('util.promisify').shim(); // import { Table } from 'mol-data/db' import CIF from 'mol-io/reader/cif' -import { Model } from 'mol-model/structure' +import { Model, Structure, ElementSet, Unit, ElementGroup } from 'mol-model/structure' import { Run, Progress } from 'mol-task' +import { OrderedSet } from 'mol-data/int'; async function parseCif(data: string|Uint8Array) { const comp = CIF.parse(data); @@ -21,7 +22,8 @@ async function parseCif(data: string|Uint8Array) { } async function getPdb(pdb: string) { - const data = await fetch(`https://files.rcsb.org/download/${pdb}.cif`) + //const data = await fetch(`https://files.rcsb.org/download/${pdb}.cif`) + const data = await fetch(`http://www.ebi.ac.uk/pdbe/static/entry/${pdb}_updated.cif`); const parsed = await parseCif(await data.text()) return CIF.schema.mmCIF(parsed.result.blocks[0]) } @@ -37,24 +39,40 @@ export function atomLabel(model: Model, aI: number) { } -function printBonds(model: Model) { - // TODO: do bonds again - // const { count, offset, neighbor } = Model.bonds(model) - // for (let i = 0; i < count; ++i) { - // const start = offset[i]; - // const end = offset[i + 1]; - // for (let bI = start; bI < end; bI++) { - // console.log(`${atomLabel(model, i)} -- ${atomLabel(model, neighbor[bI])}`) - // } - // } +function printBonds(structure: Structure) { + const { units, elements } = structure; + const unitIds = ElementSet.unitIndices(elements); + + for (let i = 0, _i = OrderedSet.size(unitIds); i < _i; i++) { + const unit = units[OrderedSet.getAt(unitIds, i)]; + const group = ElementSet.groupFromUnitIndex(elements, OrderedSet.getAt(unitIds, i)); + + const { count, offset, neighbor } = Unit.getGroupBonds(unit, group); + const { model } = unit; + + if (!count) continue; + + for (let j = 0; j < offset.length - 1; ++j) { + const start = offset[j]; + const end = offset[j + 1]; + + if (end <= start) continue; + + const aI = ElementGroup.getAt(group, j); + for (let _bI = start; _bI < end; _bI++) { + const bI = ElementGroup.getAt(group, neighbor[_bI]) + console.log(`${atomLabel(model, aI)} -- ${atomLabel(model, bI)}`); + } + } + } } async function run(pdb: string) { const mmcif = await getPdb(pdb) const models = Model.create({ kind: 'mmCIF', data: mmcif }); - // const structure = Structure.ofModel(models[0]) + const structure = Structure.ofModel(models[0]) // console.log(structure) - printBonds(models[0]) + printBonds(structure) } const parser = new argparse.ArgumentParser({ diff --git a/src/mol-geo/representation/structure/index.ts b/src/mol-geo/representation/structure/index.ts index c2266895b070b890e93f5252d954c537c672cf82..092e2f7b7c8ab6b97cf9d6adfbbacaf28b31b3b6 100644 --- a/src/mol-geo/representation/structure/index.ts +++ b/src/mol-geo/representation/structure/index.ts @@ -40,10 +40,11 @@ export function StructureRepresentation<Props>(reprCtor: () => UnitsRepresentati (a, b) => a.unit.model.id === b.unit.model.id && OrderedSet.areEqual(a.group.elements, b.group.elements) ); - for (let i = 0, _i = ElementSet.unitCount(elements); i < _i; i++) { - const group = ElementSet.unitGetByIndex(elements, i); - const unitId = ElementSet.unitGetId(elements, i); - uniqueGroups.add(unitId, { unit: units[unitId], group }); + const unitIndices = ElementSet.unitIndices(elements); + for (let i = 0, _i = unitIndices.length; i < _i; i++) { + const unitIndex = unitIndices[i]; + const group = ElementSet.groupFromUnitIndex(elements, unitIndex); + uniqueGroups.add(unitIndex, { unit: units[unitIndex], group }); } for (let i = 0, _i = uniqueGroups.groups.length; i < _i; i++) { @@ -53,24 +54,13 @@ export function StructureRepresentation<Props>(reprCtor: () => UnitsRepresentati for (let j = 0, _j = group.length; j < _j; j++) { groupUnits.push(units[group[j]]) } - const elementGroup = ElementSet.unitGetByIndex(elements, group[0]) + const elementGroup = ElementSet.groupFromUnitIndex(elements, group[0]) const repr = reprCtor() unitReprs.push(repr) await ctx.update({ message: 'Building units...', current: i, max: _i }); await ctx.runChild(repr.create(groupUnits, elementGroup, props)); renderObjects.push(...repr.renderObjects) } - - // console.log(ElementSet.unitCount(elements)) - // console.log(uniqueGroups) - - // console.log({ elements, units }) - - // const repr = reprCtor() - // unitReprs.push(repr) - // await ctx.update('Building units...'); - // await ctx.runChild(repr.create(units, elements, props)); - // renderObjects.push(...repr.renderObjects) }); }, update(elements: ElementSet, props: RepresentationProps) { diff --git a/src/mol-math/geometry.ts b/src/mol-math/geometry.ts index f8134ddd0601834b1b8cbde0f79ef120664b041f..5754f04a380143ee983caaed3bea1b3d1028cc28 100644 --- a/src/mol-math/geometry.ts +++ b/src/mol-math/geometry.ts @@ -1,4 +1,4 @@ -/* +/** * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> diff --git a/src/mol-math/geometry/_spec/lookup3d.spec.ts b/src/mol-math/geometry/_spec/lookup3d.spec.ts index af2c30a463e8016d0efebf011cb47baffe8f8242..92760e92d874ac742f0cf8bc6f044f24f0916c06 100644 --- a/src/mol-math/geometry/_spec/lookup3d.spec.ts +++ b/src/mol-math/geometry/_spec/lookup3d.spec.ts @@ -1,8 +1,8 @@ -/* -* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. -* -* @author David Sehnal <david.sehnal@gmail.com> -*/ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ import { GridLookup3D } from '../../geometry'; import { sortArray } from 'mol-data/util'; diff --git a/src/mol-math/geometry/common.ts b/src/mol-math/geometry/common.ts index 195224635b1ef41049e0fafe83d8aa359186edd3..e203afdf228197b4b18a08bfd33ea0b50b4a455c 100644 --- a/src/mol-math/geometry/common.ts +++ b/src/mol-math/geometry/common.ts @@ -1,8 +1,8 @@ -/* -* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. -* -* @author David Sehnal <david.sehnal@gmail.com> -*/ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ import { OrderedSet } from 'mol-data/int' diff --git a/src/mol-math/geometry/lookup3d/common.ts b/src/mol-math/geometry/lookup3d/common.ts index 0aa58601dcc7f61d3fdfc5eb13520f1037846177..874735b9ca2659be52324834350cc595f5bc5024 100644 --- a/src/mol-math/geometry/lookup3d/common.ts +++ b/src/mol-math/geometry/lookup3d/common.ts @@ -1,4 +1,4 @@ -/* +/** * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> diff --git a/src/mol-math/geometry/lookup3d/grid.ts b/src/mol-math/geometry/lookup3d/grid.ts index 6d168df2c935433c0330cf4bd26ba3e111892290..7abb47781969df8deb176c5e1f9918f3cf2b532d 100644 --- a/src/mol-math/geometry/lookup3d/grid.ts +++ b/src/mol-math/geometry/lookup3d/grid.ts @@ -1,4 +1,4 @@ -/* +/** * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> diff --git a/src/mol-math/geometry/primitives/box3d.ts b/src/mol-math/geometry/primitives/box3d.ts index 21a5795165779a10f5223e2b2ccf928d31018ad7..90b6b241c113c05083456013b2ed3d7b6eb5fbcc 100644 --- a/src/mol-math/geometry/primitives/box3d.ts +++ b/src/mol-math/geometry/primitives/box3d.ts @@ -1,4 +1,4 @@ -/* +/** * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> diff --git a/src/mol-math/geometry/primitives/sphere3d.ts b/src/mol-math/geometry/primitives/sphere3d.ts index bd74a09a2a1fd500a5b1708265be56b7fec88423..3e280da055a57333215ebda7b540d309b50280b1 100644 --- a/src/mol-math/geometry/primitives/sphere3d.ts +++ b/src/mol-math/geometry/primitives/sphere3d.ts @@ -1,4 +1,4 @@ -/* +/** * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> diff --git a/src/mol-model/structure/_spec/atom-set.spec.ts b/src/mol-model/structure/_spec/atom-set.spec.ts index 582e7798d261e4aa7ff7345836eae323105adace..60b208053c028b902523853e1a01d0587c0b027c 100644 --- a/src/mol-model/structure/_spec/atom-set.spec.ts +++ b/src/mol-model/structure/_spec/atom-set.spec.ts @@ -26,7 +26,7 @@ describe('atom set', () => { expect(setToPairs(set)).toEqual([p(10, 11)]); expect(ElementSet.elementHas(set, p(10, 11))).toBe(true); expect(ElementSet.elementHas(set, p(11, 11))).toBe(false); - expect(ElementSet.elementGetAt(set, 0)).toBe(p(10, 11)); + expect(ElementSet.elementAt(set, 0)).toBe(p(10, 11)); expect(ElementSet.elementCount(set)).toBe(1); }); @@ -35,7 +35,7 @@ describe('atom set', () => { expect(setToPairs(set)).toEqual([p(10, 11)]); expect(ElementSet.elementHas(set, p(10, 11))).toBe(true); expect(ElementSet.elementHas(set, p(11, 11))).toBe(false); - expect(ElementSet.elementGetAt(set, 0)).toBe(p(10, 11)); + expect(ElementSet.elementAt(set, 0)).toBe(p(10, 11)); expect(ElementSet.elementCount(set)).toBe(1); }); @@ -51,7 +51,7 @@ describe('atom set', () => { expect(ElementSet.elementHas(set, p(3, 0))).toBe(true); expect(ElementSet.elementHas(set, p(1, 7))).toBe(true); for (let i = 0; i < ElementSet.elementCount(set); i++) { - expect(Element.areEqual(ElementSet.elementGetAt(set, i), ret[i])).toBe(true); + expect(Element.areEqual(ElementSet.elementAt(set, i), ret[i])).toBe(true); } }); @@ -62,8 +62,8 @@ describe('atom set', () => { gen.add(1, OrderedSet.ofSingleton(3)); const set = gen.getSet(); - expect(ElementSet.unitGetById(set, 0)).toBe(ElementSet.unitGetById(template, 0)); - expect(ElementSet.unitGetById(set, 1)).toBe(ElementSet.unitGetById(template, 1)); + expect(ElementSet.groupFromUnitIndex(set, 0)).toBe(ElementSet.groupFromUnitIndex(template, 0)); + expect(ElementSet.groupFromUnitIndex(set, 1)).toBe(ElementSet.groupFromUnitIndex(template, 1)); expect(set).toBe(template); }); @@ -74,8 +74,8 @@ describe('atom set', () => { gen.add(1, OrderedSet.ofSingleton(4)); const set = gen.getSet(); - expect(ElementSet.unitGetById(set, 0)).toBe(ElementSet.unitGetById(template, 0)); - expect(ElementSet.unitGetById(set, 1) === ElementSet.unitGetById(template, 1)).toBe(false); + expect(ElementSet.groupFromUnitIndex(set, 0)).toBe(ElementSet.groupFromUnitIndex(template, 0)); + expect(ElementSet.groupFromUnitIndex(set, 1) === ElementSet.groupFromUnitIndex(template, 1)).toBe(false); expect(set === template).toBe(false); }); @@ -89,9 +89,9 @@ describe('atom set', () => { const u0 = ElementSet.union([p01, p02, p06], template); const u1 = ElementSet.union([p01, p02, p06, p13], template); - expect(ElementSet.unitGetById(u0, 0)).toBe(ElementSet.unitGetById(template, 0)); - expect(ElementSet.unitGetById(u1, 0)).toBe(ElementSet.unitGetById(template, 0)); - expect(ElementSet.unitGetById(u1, 1)).toBe(ElementSet.unitGetById(template, 1)); + expect(ElementSet.groupFromUnitIndex(u0, 0)).toBe(ElementSet.groupFromUnitIndex(template, 0)); + expect(ElementSet.groupFromUnitIndex(u1, 0)).toBe(ElementSet.groupFromUnitIndex(template, 0)); + expect(ElementSet.groupFromUnitIndex(u1, 1)).toBe(ElementSet.groupFromUnitIndex(template, 1)); expect(u1).toBe(template); }); @@ -108,7 +108,7 @@ describe('atom set', () => { } const ms = gen.getSet(); for (let i = 0; i < control.length; i++) { - expect(Element.areEqual(ElementSet.elementGetAt(ms, i), control[i])).toBe(true); + expect(Element.areEqual(ElementSet.elementAt(ms, i), control[i])).toBe(true); } for (let i = 0; i < control.length; i++) { diff --git a/src/mol-model/structure/model/formats/mmcif/assembly.ts b/src/mol-model/structure/model/formats/mmcif/assembly.ts index a03ff07ae39d81a905646fdbaabb044861cda526..2315133d4cb83c7219207a9690f5b55ca148db9b 100644 --- a/src/mol-model/structure/model/formats/mmcif/assembly.ts +++ b/src/mol-model/structure/model/formats/mmcif/assembly.ts @@ -8,7 +8,7 @@ import { Mat4, Tensor } from 'mol-math/linear-algebra' import { SymmetryOperator } from 'mol-math/geometry/symmetry-operator' import Format from '../../format' import { Assembly, OperatorGroup, OperatorGroups } from '../../properties/symmetry' -import { Queries as Q } from '../../../query' +import { Queries as Q, Query } from '../../../query' import mmCIF_Format = Format.mmCIF @@ -57,10 +57,10 @@ function operatorGroupsProvider(generators: Generator[], matrices: Matrices): () const operatorList = parseOperatorList(gen.expression); const operatorNames = expandOperators(operatorList); const operators = getAssemblyOperators(matrices, operatorNames, operatorOffset); - const selector = Q.generators.atoms({ chainTest: Q.pred.and( + 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) - )}); + )})); 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 2a123a82d881f882362c9aaf27f2f001cb957964..139a7a8117647b2e14ff66dd0c9c5ea9f04bcbaf 100644 --- a/src/mol-model/structure/model/formats/mmcif/bonds.ts +++ b/src/mol-model/structure/model/formats/mmcif/bonds.ts @@ -6,12 +6,12 @@ */ import Model from '../../model' -import Bonds from '../../properties/bonds' import { BondType } from '../../types' import { findEntityIdByAsymId, findAtomIndexByLabelName } from './util' import { Column } from 'mol-data/db' +import { GroupBonds } from '../../../structure/element/properties/bonds/group-data'; -export class StructConn implements Bonds.StructConn { +export class StructConn implements GroupBonds.StructConn { private _residuePairIndex: Map<string, StructConn.Entry[]> | undefined = void 0; private _atomIndex: Map<number, StructConn.Entry[]> | undefined = void 0; @@ -71,7 +71,7 @@ export class StructConn implements Bonds.StructConn { } export namespace StructConn { - export interface Entry extends Bonds.StructConnEntry { + export interface Entry extends GroupBonds.StructConnEntry { distance: number, order: number, flags: number, @@ -181,7 +181,7 @@ export namespace StructConn { } } -export class ComponentBondInfo implements Bonds.ComponentBondInfo { +export class ComponentBondInfo implements GroupBonds.ComponentBondInfo { entries: Map<string, ComponentBondInfo.Entry> = new Map(); newEntry(id: string) { @@ -192,7 +192,7 @@ export class ComponentBondInfo implements Bonds.ComponentBondInfo { } export namespace ComponentBondInfo { - export class Entry implements Bonds.ComponentBondInfoEntry { + export class Entry implements GroupBonds.ComponentBondInfoEntry { map: Map<string, Map<string, { order: number, flags: number }>> = new Map(); add(a: string, b: string, order: number, flags: number, swap = true) { diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts index 99a3877aa1b99b03eb46d3d12aacabf58cffef1b..e7f0fe0a8b85b9bf3b0b32f869bdf970ccb87f55 100644 --- a/src/mol-model/structure/model/model.ts +++ b/src/mol-model/structure/model/model.ts @@ -14,7 +14,6 @@ import CoarseGrained from './properties/coarse-grained' import from_gro from './formats/gro' import from_mmCIF from './formats/mmcif' - /** * Interface to the "source data" of the molecule. * diff --git a/src/mol-model/structure/model/utils/compute-bonds.ts b/src/mol-model/structure/model/utils/compute-bonds.ts deleted file mode 100644 index 6db9047fd2017f7c78e20f7325069e7759e989d1..0000000000000000000000000000000000000000 --- a/src/mol-model/structure/model/utils/compute-bonds.ts +++ /dev/null @@ -1,253 +0,0 @@ -// /** -// * Copyright (c) 2017 MolQL contributors, licensed under MIT, See LICENSE file for more info. -// * -// * @author David Sehnal <david.sehnal@gmail.com> -// */ - -// import Mask from 'mol-util/mask' -// import Model from '../model' -// import { BondType, ElementSymbol } from '../types' -// import Bonds from '../properties/bonds' -// import { StructConn, ComponentBondInfo } from '../formats/mmcif/bonds' - -// export interface BondComputationParameters { -// maxHbondLength: number, -// forceCompute: boolean -// } - -// // H,D,T are all mapped to H -// const __ElementIndex: { [e: string]: number | undefined } = { 'H': 0, 'h': 0, 'D': 0, 'd': 0, 'T': 0, 't': 0, 'He': 2, 'HE': 2, 'he': 2, 'Li': 3, 'LI': 3, 'li': 3, 'Be': 4, 'BE': 4, 'be': 4, 'B': 5, 'b': 5, 'C': 6, 'c': 6, 'N': 7, 'n': 7, 'O': 8, 'o': 8, 'F': 9, 'f': 9, 'Ne': 10, 'NE': 10, 'ne': 10, 'Na': 11, 'NA': 11, 'na': 11, 'Mg': 12, 'MG': 12, 'mg': 12, 'Al': 13, 'AL': 13, 'al': 13, 'Si': 14, 'SI': 14, 'si': 14, 'P': 15, 'p': 15, 'S': 16, 's': 16, 'Cl': 17, 'CL': 17, 'cl': 17, 'Ar': 18, 'AR': 18, 'ar': 18, 'K': 19, 'k': 19, 'Ca': 20, 'CA': 20, 'ca': 20, 'Sc': 21, 'SC': 21, 'sc': 21, 'Ti': 22, 'TI': 22, 'ti': 22, 'V': 23, 'v': 23, 'Cr': 24, 'CR': 24, 'cr': 24, 'Mn': 25, 'MN': 25, 'mn': 25, 'Fe': 26, 'FE': 26, 'fe': 26, 'Co': 27, 'CO': 27, 'co': 27, 'Ni': 28, 'NI': 28, 'ni': 28, 'Cu': 29, 'CU': 29, 'cu': 29, 'Zn': 30, 'ZN': 30, 'zn': 30, 'Ga': 31, 'GA': 31, 'ga': 31, 'Ge': 32, 'GE': 32, 'ge': 32, 'As': 33, 'AS': 33, 'as': 33, 'Se': 34, 'SE': 34, 'se': 34, 'Br': 35, 'BR': 35, 'br': 35, 'Kr': 36, 'KR': 36, 'kr': 36, 'Rb': 37, 'RB': 37, 'rb': 37, 'Sr': 38, 'SR': 38, 'sr': 38, 'Y': 39, 'y': 39, 'Zr': 40, 'ZR': 40, 'zr': 40, 'Nb': 41, 'NB': 41, 'nb': 41, 'Mo': 42, 'MO': 42, 'mo': 42, 'Tc': 43, 'TC': 43, 'tc': 43, 'Ru': 44, 'RU': 44, 'ru': 44, 'Rh': 45, 'RH': 45, 'rh': 45, 'Pd': 46, 'PD': 46, 'pd': 46, 'Ag': 47, 'AG': 47, 'ag': 47, 'Cd': 48, 'CD': 48, 'cd': 48, 'In': 49, 'IN': 49, 'in': 49, 'Sn': 50, 'SN': 50, 'sn': 50, 'Sb': 51, 'SB': 51, 'sb': 51, 'Te': 52, 'TE': 52, 'te': 52, 'I': 53, 'i': 53, 'Xe': 54, 'XE': 54, 'xe': 54, 'Cs': 55, 'CS': 55, 'cs': 55, 'Ba': 56, 'BA': 56, 'ba': 56, 'La': 57, 'LA': 57, 'la': 57, 'Ce': 58, 'CE': 58, 'ce': 58, 'Pr': 59, 'PR': 59, 'pr': 59, 'Nd': 60, 'ND': 60, 'nd': 60, 'Pm': 61, 'PM': 61, 'pm': 61, 'Sm': 62, 'SM': 62, 'sm': 62, 'Eu': 63, 'EU': 63, 'eu': 63, 'Gd': 64, 'GD': 64, 'gd': 64, 'Tb': 65, 'TB': 65, 'tb': 65, 'Dy': 66, 'DY': 66, 'dy': 66, 'Ho': 67, 'HO': 67, 'ho': 67, 'Er': 68, 'ER': 68, 'er': 68, 'Tm': 69, 'TM': 69, 'tm': 69, 'Yb': 70, 'YB': 70, 'yb': 70, 'Lu': 71, 'LU': 71, 'lu': 71, 'Hf': 72, 'HF': 72, 'hf': 72, 'Ta': 73, 'TA': 73, 'ta': 73, 'W': 74, 'w': 74, 'Re': 75, 'RE': 75, 're': 75, 'Os': 76, 'OS': 76, 'os': 76, 'Ir': 77, 'IR': 77, 'ir': 77, 'Pt': 78, 'PT': 78, 'pt': 78, 'Au': 79, 'AU': 79, 'au': 79, 'Hg': 80, 'HG': 80, 'hg': 80, 'Tl': 81, 'TL': 81, 'tl': 81, 'Pb': 82, 'PB': 82, 'pb': 82, 'Bi': 83, 'BI': 83, 'bi': 83, 'Po': 84, 'PO': 84, 'po': 84, 'At': 85, 'AT': 85, 'at': 85, 'Rn': 86, 'RN': 86, 'rn': 86, 'Fr': 87, 'FR': 87, 'fr': 87, 'Ra': 88, 'RA': 88, 'ra': 88, 'Ac': 89, 'AC': 89, 'ac': 89, 'Th': 90, 'TH': 90, 'th': 90, 'Pa': 91, 'PA': 91, 'pa': 91, 'U': 92, 'u': 92, 'Np': 93, 'NP': 93, 'np': 93, 'Pu': 94, 'PU': 94, 'pu': 94, 'Am': 95, 'AM': 95, 'am': 95, 'Cm': 96, 'CM': 96, 'cm': 96, 'Bk': 97, 'BK': 97, 'bk': 97, 'Cf': 98, 'CF': 98, 'cf': 98, 'Es': 99, 'ES': 99, 'es': 99, 'Fm': 100, 'FM': 100, 'fm': 100, 'Md': 101, 'MD': 101, 'md': 101, 'No': 102, 'NO': 102, 'no': 102, 'Lr': 103, 'LR': 103, 'lr': 103, 'Rf': 104, 'RF': 104, 'rf': 104, 'Db': 105, 'DB': 105, 'db': 105, 'Sg': 106, 'SG': 106, 'sg': 106, 'Bh': 107, 'BH': 107, 'bh': 107, 'Hs': 108, 'HS': 108, 'hs': 108, 'Mt': 109, 'MT': 109, 'mt': 109 }; - -// const __ElementBondThresholds: { [e: number]: number | undefined } = { 0: 1.42, 1: 1.42, 3: 2.7, 4: 2.7, 6: 1.75, 7: 1.6, 8: 1.52, 11: 2.7, 12: 2.7, 13: 2.7, 14: 1.9, 15: 1.9, 16: 1.9, 17: 1.8, 19: 2.7, 20: 2.7, 21: 2.7, 22: 2.7, 23: 2.7, 24: 2.7, 25: 2.7, 26: 2.7, 27: 2.7, 28: 2.7, 29: 2.7, 30: 2.7, 31: 2.7, 33: 2.68, 37: 2.7, 38: 2.7, 39: 2.7, 40: 2.7, 41: 2.7, 42: 2.7, 43: 2.7, 44: 2.7, 45: 2.7, 46: 2.7, 47: 2.7, 48: 2.7, 49: 2.7, 50: 2.7, 55: 2.7, 56: 2.7, 57: 2.7, 58: 2.7, 59: 2.7, 60: 2.7, 61: 2.7, 62: 2.7, 63: 2.7, 64: 2.7, 65: 2.7, 66: 2.7, 67: 2.7, 68: 2.7, 69: 2.7, 70: 2.7, 71: 2.7, 72: 2.7, 73: 2.7, 74: 2.7, 75: 2.7, 76: 2.7, 77: 2.7, 78: 2.7, 79: 2.7, 80: 2.7, 81: 2.7, 82: 2.7, 83: 2.7, 87: 2.7, 88: 2.7, 89: 2.7, 90: 2.7, 91: 2.7, 92: 2.7, 93: 2.7, 94: 2.7, 95: 2.7, 96: 2.7, 97: 2.7, 98: 2.7, 99: 2.7, 100: 2.7, 101: 2.7, 102: 2.7, 103: 2.7, 104: 2.7, 105: 2.7, 106: 2.7, 107: 2.7, 108: 2.7, 109: 2.88 }; - -// const __ElementPairThresholds: { [e: number]: number | undefined } = { 0: 0.8, 20: 1.31, 27: 1.3, 35: 1.3, 44: 1.05, 54: 1, 60: 1.84, 72: 1.88, 84: 1.75, 85: 1.56, 86: 1.76, 98: 1.6, 99: 1.68, 100: 1.63, 112: 1.55, 113: 1.59, 114: 1.36, 129: 1.45, 144: 1.6, 170: 1.4, 180: 1.55, 202: 2.4, 222: 2.24, 224: 1.91, 225: 1.98, 243: 2.02, 269: 2, 293: 1.9, 480: 2.3, 512: 2.3, 544: 2.3, 612: 2.1, 629: 1.54, 665: 1, 813: 2.6, 854: 2.27, 894: 1.93, 896: 2.1, 937: 2.05, 938: 2.06, 981: 1.62, 1258: 2.68, 1309: 2.33, 1484: 1, 1763: 2.14, 1823: 2.48, 1882: 2.1, 1944: 1.72, 2380: 2.34, 3367: 2.44, 3733: 2.11, 3819: 2.6, 3821: 2.36, 4736: 2.75, 5724: 2.73, 5959: 2.63, 6519: 2.84, 6750: 2.87, 8991: 2.81 }; - -// const __DefaultBondingRadius = 2.001; - -// const MetalsSet = (function () { -// const metals = ['LI', 'NA', 'K', 'RB', 'CS', 'FR', 'BE', 'MG', 'CA', 'SR', 'BA', 'RA', 'AL', 'GA', 'IN', 'SN', 'TL', 'PB', 'BI', 'SC', 'TI', 'V', 'CR', 'MN', 'FE', 'CO', 'NI', 'CU', 'ZN', 'Y', 'ZR', 'NB', 'MO', 'TC', 'RU', 'RH', 'PD', 'AG', 'CD', 'LA', 'HF', 'TA', 'W', 'RE', 'OS', 'IR', 'PT', 'AU', 'HG', 'AC', 'RF', 'DB', 'SG', 'BH', 'HS', 'MT', 'CE', 'PR', 'ND', 'PM', 'SM', 'EU', 'GD', 'TB', 'DY', 'HO', 'ER', 'TM', 'YB', 'LU', 'TH', 'PA', 'U', 'NP', 'PU', 'AM', 'CM', 'BK', 'CF', 'ES', 'FM', 'MD', 'NO', 'LR']; -// const set = new Set<number>(); -// for (const m of metals) { -// set.add(__ElementIndex[m]!); -// } -// return set; -// })(); - -// function pair(a: number, b: number) { -// if (a < b) return (a + b) * (a + b + 1) / 2 + b; -// else return (a + b) * (a + b + 1) / 2 + a; -// } - -// function idx(e: ElementSymbol) { -// const i = __ElementIndex[e as any as string]; -// if (i === void 0) return -1; -// return i; -// } - -// function pairThreshold(i: number, j: number) { -// if (i < 0 || j < 0) return -1; -// const r = __ElementPairThresholds[pair(i, j)]; -// if (r === void 0) return -1; -// return r; -// } - -// function threshold(i: number) { -// if (i < 0) return __DefaultBondingRadius; -// const r = __ElementBondThresholds[i]; -// if (r === void 0) return __DefaultBondingRadius; -// return r; -// } - -// const H_ID = __ElementIndex['H']!; -// function isHydrogen(i: number) { -// return i === H_ID; -// } - -// function computePerAtomBonds(atomA: number[], atomB: number[], _order: number[], _flags: number[], atomCount: number) { -// const bucketSizes = new Int32Array(atomCount); -// const bucketOffsets = new Int32Array(atomCount + 1) as any as number[]; -// const bucketFill = new Int32Array(atomCount); - -// for (const i of atomA) bucketSizes[i]++; -// for (const i of atomB) bucketSizes[i]++; - -// let offset = 0; -// for (let i = 0; i < atomCount; i++) { -// bucketOffsets[i] = offset; -// offset += bucketSizes[i]; -// } -// bucketOffsets[atomCount] = offset; - -// const neighbor = new Int32Array(offset) as any as number[]; -// const flags = new Uint16Array(offset) as any as number[]; -// const order = new Int8Array(offset) as any as number[]; - -// for (let i = 0, _i = atomA.length; i < _i; i++) { -// const a = atomA[i], b = atomB[i], f = _flags[i], o = _order[i]; - -// const oa = bucketOffsets[a] + bucketFill[a]; -// const ob = bucketOffsets[b] + bucketFill[b]; - -// neighbor[oa] = b; -// flags[oa] = f; -// order[oa] = o; -// bucketFill[a]++; - -// neighbor[ob] = a; -// flags[ob] = f; -// order[ob] = o; -// bucketFill[b]++; -// } - -// return { -// offsets: bucketOffsets, -// neighbor, -// flags, -// order -// }; -// } - -// function _computeBonds(model: Model, params: BondComputationParameters): Bonds { -// const MAX_RADIUS = 3; - -// const { x, y, z } = model.conformation; -// const { atomCount } = model; -// const { residueKey } = model.hierarchy -// const { type_symbol, label_atom_id, label_alt_id } = model.hierarchy.atoms; -// const { label_comp_id } = model.hierarchy.residues; -// const query3d = Model.spatialLookup(model).find(Mask.always(atomCount)); - -// const structConn = model.sourceData.kind === 'mmCIF' ? StructConn.create(model) : void 0 -// const component = model.sourceData.kind === 'mmCIF' ? ComponentBondInfo.create(model) : void 0 - -// const atomA: number[] = []; -// const atomB: number[] = []; -// const flags: number[] = []; -// const order: number[] = []; - -// let lastResidue = -1; -// let componentMap: Map<string, Map<string, { flags: number, order: number }>> | undefined = void 0; - -// for (let aI = 0; aI < atomCount; aI++) { -// const raI = residueKey.value(aI); -// // const rowA = dataIndex[aI]; // TODO -// const rowA = aI; - -// if (!params.forceCompute && raI !== lastResidue) { -// const resn = label_comp_id.value(rowA)!; -// if (!!component && component.entries.has(resn)) { -// componentMap = component.entries.get(resn)!.map; -// } else { -// componentMap = void 0; -// } -// } -// lastResidue = raI; - -// const componentPairs = componentMap ? componentMap.get(label_atom_id.value(rowA)) : void 0; - -// const aeI = idx(type_symbol.value(rowA)!); - -// const { indices, count, squaredDistances } = query3d(x[aI], y[aI], z[aI], MAX_RADIUS); -// const isHa = isHydrogen(aeI); -// const thresholdA = threshold(aeI); -// const altA = label_alt_id.value(rowA); -// const metalA = MetalsSet.has(aeI); -// const structConnEntries = params.forceCompute ? void 0 : structConn && structConn.getAtomEntries(aI); - -// for (let ni = 0; ni < count; ni++) { -// const bI = indices[ni]; -// if (bI <= aI) continue; - -// // const rowB = dataIndex[bI]; // TODO -// const rowB = bI; - -// const altB = label_alt_id.value(rowB); -// if (altA && altB && altA !== altB) continue; - -// const beI = idx(type_symbol.value(rowB)!); -// const isMetal = metalA || MetalsSet.has(beI); - -// const rbI = residueKey.value(bI); -// // handle "component dictionary" bonds. -// if (raI === rbI && componentPairs) { -// const e = componentPairs.get(label_atom_id.value(rowB)!); -// if (e) { -// atomA[atomA.length] = aI; -// atomB[atomB.length] = bI; -// order[order.length] = e.order; -// let flag = e.flags; -// if (isMetal) { -// if (flag | BondType.Flag.Covalent) flag ^= BondType.Flag.Covalent; -// flag |= BondType.Flag.MetallicCoordination; -// } -// flags[flags.length] = flag; -// } -// continue; -// } - -// const isHb = isHydrogen(beI); -// if (isHa && isHb) continue; - -// const dist = Math.sqrt(squaredDistances[ni]); -// if (dist === 0) continue; - -// // handle "struct conn" bonds. -// if (structConnEntries && structConnEntries.length) { -// let added = false; -// for (const se of structConnEntries) { -// for (const p of se.partners) { -// if (p.atomIndex === bI) { -// atomA[atomA.length] = aI; -// atomB[atomB.length] = bI; -// flags[flags.length] = se.flags; -// order[order.length] = se.order; -// added = true; -// break; -// } -// } -// if (added) break; -// } -// if (added) continue; -// } - -// if (isHa || isHb) { -// if (dist < params.maxHbondLength) { -// atomA[atomA.length] = aI; -// atomB[atomB.length] = bI; -// order[order.length] = 1; -// flags[flags.length] = BondType.Flag.Covalent | BondType.Flag.Computed; // TODO: check if correct -// } -// continue; -// } - -// const thresholdAB = pairThreshold(aeI, beI); -// const pairingThreshold = thresholdAB > 0 -// ? thresholdAB -// : beI < 0 ? thresholdA : Math.max(thresholdA, threshold(beI)); - - -// if (dist <= pairingThreshold) { -// atomA[atomA.length] = aI; -// atomB[atomB.length] = bI; -// order[order.length] = 1; -// flags[flags.length] = (isMetal ? BondType.Flag.MetallicCoordination : BondType.Flag.Covalent) | BondType.Flag.Computed; -// } -// } -// } - -// const bonds = computePerAtomBonds(atomA, atomB, order, flags, atomCount); -// return { -// offset: bonds.offsets, -// neighbor: bonds.neighbor, -// flags: bonds.flags, -// order: bonds.order, -// count: atomA.length -// }; -// } - -// export default function computeBonds(model: Model, params?: Partial<BondComputationParameters>) { -// return _computeBonds(model, { -// maxHbondLength: (params && params.maxHbondLength) || 1.15, -// forceCompute: !!(params && params.forceCompute), -// }); -// } \ No newline at end of file diff --git a/src/mol-model/structure/query/generators.ts b/src/mol-model/structure/query/generators.ts index 53fff73ed4b567bdb202ba72a2b6a02b42df34fb..56beedae592d85f6436c39ba1593fc9f77a17e35 100644 --- a/src/mol-model/structure/query/generators.ts +++ b/src/mol-model/structure/query/generators.ts @@ -10,7 +10,7 @@ import P from './properties' import { Structure, ElementSet, Element } from '../structure' import { OrderedSet, Segmentation } from 'mol-data/int' -export const all: Query = s => Selection.Singletons(s, s.elements); +export const all: Query.Provider = async (s, ctx) => Selection.Singletons(s, s.elements); export interface AtomQueryParams { entityTest: Element.Predicate, @@ -27,7 +27,7 @@ export interface AtomGroupsQueryParams extends AtomQueryParams { export function residues(params?: Partial<AtomQueryParams>) { return atoms({ ...params, groupBy: P.residue.key }); } export function chains(params?: Partial<AtomQueryParams>) { return atoms({ ...params, groupBy: P.chain.key }); } -export function atoms(params?: Partial<AtomGroupsQueryParams>): Query { +export function atoms(params?: Partial<AtomGroupsQueryParams>): Query.Provider { if (!params || (!params.atomTest && !params.residueTest && !params.chainTest && !params.entityTest && !params.groupBy)) return all; if (!!params.atomTest && !params.residueTest && !params.chainTest && !params.entityTest && !params.groupBy) return atomGroupsLinear(params.atomTest); @@ -43,17 +43,17 @@ export function atoms(params?: Partial<AtomGroupsQueryParams>): Query { return atomGroupsGrouped(normalized); } -function atomGroupsLinear(atomTest: Element.Predicate): Query { - return structure => { +function atomGroupsLinear(atomTest: Element.Predicate): Query.Provider { + return async (structure, ctx) => { const { elements, units } = structure; - const unitIds = ElementSet.unitIds(elements); + const unitIds = ElementSet.unitIndices(elements); const l = Element.Location(); const builder = ElementSet.LinearBuilder(elements); for (let i = 0, _i = unitIds.length; i < _i; i++) { const unitId = unitIds[i]; l.unit = units[unitId]; - const set = ElementSet.unitGetByIndex(elements, i).elements; + const set = ElementSet.groupAt(elements, i).elements; builder.beginUnit(); for (let j = 0, _j = OrderedSet.size(set); j < _j; j++) { @@ -61,16 +61,18 @@ function atomGroupsLinear(atomTest: Element.Predicate): Query { if (atomTest(l)) builder.addToUnit(l.element); } builder.commitUnit(unitId); + + if (ctx.shouldUpdate) await ctx.update({ message: 'Atom Groups', current: 0, max: unitIds.length }); } return Selection.Singletons(structure, builder.getSet()); }; } -function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: AtomGroupsQueryParams): Query { - return structure => { +function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: AtomGroupsQueryParams): Query.Provider { + return async (structure, ctx) => { const { elements, units } = structure; - const unitIds = ElementSet.unitIds(elements); + const unitIds = ElementSet.unitIndices(elements); const l = Element.Location(); const builder = ElementSet.LinearBuilder(elements); @@ -78,7 +80,7 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A const unitId = unitIds[i]; const unit = units[unitId]; l.unit = unit; - const set = ElementSet.unitGetByIndex(elements, i).elements; + const set = ElementSet.groupAt(elements, i).elements; builder.beginUnit(); const chainsIt = Segmentation.transientSegments(unit.hierarchy.chainSegments, set); @@ -104,6 +106,8 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A } } builder.commitUnit(unitId); + + if (ctx.shouldUpdate) await ctx.update({ message: 'Atom Groups', current: 0, max: unitIds.length }); } return Selection.Singletons(structure, builder.getSet()); @@ -157,10 +161,10 @@ class LinearGroupingBuilder { constructor(private structure: Structure) { } } -function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, groupBy }: AtomGroupsQueryParams): Query { - return structure => { +function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, groupBy }: AtomGroupsQueryParams): Query.Provider { + return async (structure, ctx) => { const { elements, units } = structure; - const unitIds = ElementSet.unitIds(elements); + const unitIds = ElementSet.unitIndices(elements); const l = Element.Location(); const builder = new LinearGroupingBuilder(structure); @@ -168,7 +172,7 @@ function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, group const unitId = unitIds[i]; const unit = units[unitId]; l.unit = unit; - const set = ElementSet.unitGetByIndex(elements, i).elements; + const set = ElementSet.groupAt(elements, i).elements; const chainsIt = Segmentation.transientSegments(unit.hierarchy.chainSegments, set); const residuesIt = Segmentation.transientSegments(unit.hierarchy.residueSegments, set); @@ -192,6 +196,8 @@ function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, group } } } + + if (ctx.shouldUpdate) await ctx.update({ message: 'Atom Groups', current: 0, max: unitIds.length }); } return builder.getSelection(); diff --git a/src/mol-model/structure/query/query.ts b/src/mol-model/structure/query/query.ts index 001f1e0e79b3082011d3b60baecfa2a67e88befa..e8620e337b1b10f137b295adc148c43d57133166 100644 --- a/src/mol-model/structure/query/query.ts +++ b/src/mol-model/structure/query/query.ts @@ -4,10 +4,21 @@ * @author David Sehnal <david.sehnal@gmail.com> */ +import { Task, RuntimeContext } from 'mol-task' import { Structure } from '../structure' import Selection from './selection' // TODO: Query { (s: Structure): Computation<Selection> } -interface Query { (s: Structure): Selection } +interface Query { (s: Structure): Task<Selection>, provider: Query.Provider } +function Query(q: Query.Provider): Query { + const ret = (s => Task.create('Query', ctx => q(s, ctx))) as Query; + ret.provider = q; + return ret; +} + +namespace Query { + export interface Provider { (s: Structure, ctx: RuntimeContext): Promise<Selection> } +} + export default Query \ No newline at end of file diff --git a/src/mol-model/structure/query/selection.ts b/src/mol-model/structure/query/selection.ts index fb4f43f7dc10673043445fdacd30f8a0890ac183..f9f516710817ea7ae9d8c042457a7f81e2f53a2d 100644 --- a/src/mol-model/structure/query/selection.ts +++ b/src/mol-model/structure/query/selection.ts @@ -35,7 +35,7 @@ namespace Selection { export function getAt(sel: Selection, i: number): Structure { if (isSingleton(sel)) { - const atom = ElementSet.elementGetAt(sel.set, i); + const atom = ElementSet.elementAt(sel.set, i); return Structure.create(sel.structure.units, ElementSet.singleton(atom, sel.structure.elements)); } return Structure.create(sel.structure.units, sel.sets[i]); diff --git a/src/mol-model/structure/structure/element/group.ts b/src/mol-model/structure/structure/element/group.ts index ec7544470f6cf9c29a19ad606a83f904adf61934..e89b3d37e87b6611a0630fe4978fe1218ceaa76b 100644 --- a/src/mol-model/structure/structure/element/group.ts +++ b/src/mol-model/structure/structure/element/group.ts @@ -5,15 +5,17 @@ */ import { OrderedSet } from 'mol-data/int' -import { Lookup3D, GridLookup3D } from 'mol-math/geometry'; +import { Lookup3D } from 'mol-math/geometry' import Unit from '../unit' +import { GroupBonds } from './properties/bonds/group-data'; interface ElementGroup { elements: OrderedSet, // Unique identifier of the group, usable as partial key for various "caches". key: number, - __lookup3d__?: Lookup3D + __lookup3d__?: Lookup3D, + __bonds__?: GroupBonds } namespace ElementGroup { @@ -64,17 +66,6 @@ namespace ElementGroup { return createNew(set); } - export function getLookup3d(unit: Unit, group: ElementGroup) { - if (group.__lookup3d__) return group.__lookup3d__; - if (Unit.isAtomic(unit)) { - const { x, y, z } = unit.model.conformation; - group.__lookup3d__ = GridLookup3D({ x, y, z, indices: group.elements }); - return group.__lookup3d__; - } - - throw 'not implemented'; - } - let _id = 0; function nextKey() { const ret = _id; diff --git a/src/mol-model/structure/structure/element/impl/set.ts b/src/mol-model/structure/structure/element/impl/set.ts index cae6746058e0c0ecd8925fc326586c71cf2e09cb..2aaf3182e5f1ea62f0c313357d3b48d6b8a5dd4c 100644 --- a/src/mol-model/structure/structure/element/impl/set.ts +++ b/src/mol-model/structure/structure/element/impl/set.ts @@ -9,10 +9,19 @@ import { sortArray } from 'mol-data/util/sort' import { hash1 } from 'mol-data/util/hash-functions' import Element from '../../element' import ElementGroup from '../group' +import { ElementSetLookup3D } from '../../util/lookup3d' +import Structure from '../../structure'; /** Long and painful implementation starts here */ -export type ElementSetImpl = { groups: IntMap<ElementGroup>, offsets: Int32Array, hashCode: number, keys: SortedArray } +export type ElementSetImpl = { + groups: IntMap<ElementGroup>, + offsets: Int32Array, + hashCode: number, + keys: SortedArray, + + __lookup3d__?: ElementSetLookup3D +} export const Empty: ElementSetImpl = { groups: IntMap.Empty, offsets: new Int32Array(1), hashCode: 0, keys: SortedArray.Empty }; @@ -270,6 +279,13 @@ export function Generator() { return new AtomSetGenerator(); } +export function getLookup3d(s: Structure) { + const set = s.elements as any as ElementSetImpl; + if (set.__lookup3d__) return set.__lookup3d__; + set.__lookup3d__ = ElementSetLookup3D.create(s); + return set.__lookup3d__; +} + /** When adding groups, compare them to existing ones. If they all match, return the whole original set. */ class ChildGenerator { private keys: number[] = []; diff --git a/src/mol-model/structure/structure/element/properties/bonds/group-compute.ts b/src/mol-model/structure/structure/element/properties/bonds/group-compute.ts new file mode 100644 index 0000000000000000000000000000000000000000..fb078c889fb924c543659b995eb6b6eea3e28eba --- /dev/null +++ b/src/mol-model/structure/structure/element/properties/bonds/group-compute.ts @@ -0,0 +1,253 @@ +/** + * Copyright (c) 2017 MolQL contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { BondType, ElementSymbol } from '../../../../model/types' +import { GroupBonds } from './group-data' +import { StructConn, ComponentBondInfo } from '../../../../model/formats/mmcif/bonds' +import Unit from '../../../unit'; +import ElementGroup from '../../group'; + +export interface BondComputationParameters { + maxHbondLength: number, + forceCompute: boolean +} + +// H,D,T are all mapped to H +const __ElementIndex: { [e: string]: number | undefined } = { 'H': 0, 'h': 0, 'D': 0, 'd': 0, 'T': 0, 't': 0, 'He': 2, 'HE': 2, 'he': 2, 'Li': 3, 'LI': 3, 'li': 3, 'Be': 4, 'BE': 4, 'be': 4, 'B': 5, 'b': 5, 'C': 6, 'c': 6, 'N': 7, 'n': 7, 'O': 8, 'o': 8, 'F': 9, 'f': 9, 'Ne': 10, 'NE': 10, 'ne': 10, 'Na': 11, 'NA': 11, 'na': 11, 'Mg': 12, 'MG': 12, 'mg': 12, 'Al': 13, 'AL': 13, 'al': 13, 'Si': 14, 'SI': 14, 'si': 14, 'P': 15, 'p': 15, 'S': 16, 's': 16, 'Cl': 17, 'CL': 17, 'cl': 17, 'Ar': 18, 'AR': 18, 'ar': 18, 'K': 19, 'k': 19, 'Ca': 20, 'CA': 20, 'ca': 20, 'Sc': 21, 'SC': 21, 'sc': 21, 'Ti': 22, 'TI': 22, 'ti': 22, 'V': 23, 'v': 23, 'Cr': 24, 'CR': 24, 'cr': 24, 'Mn': 25, 'MN': 25, 'mn': 25, 'Fe': 26, 'FE': 26, 'fe': 26, 'Co': 27, 'CO': 27, 'co': 27, 'Ni': 28, 'NI': 28, 'ni': 28, 'Cu': 29, 'CU': 29, 'cu': 29, 'Zn': 30, 'ZN': 30, 'zn': 30, 'Ga': 31, 'GA': 31, 'ga': 31, 'Ge': 32, 'GE': 32, 'ge': 32, 'As': 33, 'AS': 33, 'as': 33, 'Se': 34, 'SE': 34, 'se': 34, 'Br': 35, 'BR': 35, 'br': 35, 'Kr': 36, 'KR': 36, 'kr': 36, 'Rb': 37, 'RB': 37, 'rb': 37, 'Sr': 38, 'SR': 38, 'sr': 38, 'Y': 39, 'y': 39, 'Zr': 40, 'ZR': 40, 'zr': 40, 'Nb': 41, 'NB': 41, 'nb': 41, 'Mo': 42, 'MO': 42, 'mo': 42, 'Tc': 43, 'TC': 43, 'tc': 43, 'Ru': 44, 'RU': 44, 'ru': 44, 'Rh': 45, 'RH': 45, 'rh': 45, 'Pd': 46, 'PD': 46, 'pd': 46, 'Ag': 47, 'AG': 47, 'ag': 47, 'Cd': 48, 'CD': 48, 'cd': 48, 'In': 49, 'IN': 49, 'in': 49, 'Sn': 50, 'SN': 50, 'sn': 50, 'Sb': 51, 'SB': 51, 'sb': 51, 'Te': 52, 'TE': 52, 'te': 52, 'I': 53, 'i': 53, 'Xe': 54, 'XE': 54, 'xe': 54, 'Cs': 55, 'CS': 55, 'cs': 55, 'Ba': 56, 'BA': 56, 'ba': 56, 'La': 57, 'LA': 57, 'la': 57, 'Ce': 58, 'CE': 58, 'ce': 58, 'Pr': 59, 'PR': 59, 'pr': 59, 'Nd': 60, 'ND': 60, 'nd': 60, 'Pm': 61, 'PM': 61, 'pm': 61, 'Sm': 62, 'SM': 62, 'sm': 62, 'Eu': 63, 'EU': 63, 'eu': 63, 'Gd': 64, 'GD': 64, 'gd': 64, 'Tb': 65, 'TB': 65, 'tb': 65, 'Dy': 66, 'DY': 66, 'dy': 66, 'Ho': 67, 'HO': 67, 'ho': 67, 'Er': 68, 'ER': 68, 'er': 68, 'Tm': 69, 'TM': 69, 'tm': 69, 'Yb': 70, 'YB': 70, 'yb': 70, 'Lu': 71, 'LU': 71, 'lu': 71, 'Hf': 72, 'HF': 72, 'hf': 72, 'Ta': 73, 'TA': 73, 'ta': 73, 'W': 74, 'w': 74, 'Re': 75, 'RE': 75, 're': 75, 'Os': 76, 'OS': 76, 'os': 76, 'Ir': 77, 'IR': 77, 'ir': 77, 'Pt': 78, 'PT': 78, 'pt': 78, 'Au': 79, 'AU': 79, 'au': 79, 'Hg': 80, 'HG': 80, 'hg': 80, 'Tl': 81, 'TL': 81, 'tl': 81, 'Pb': 82, 'PB': 82, 'pb': 82, 'Bi': 83, 'BI': 83, 'bi': 83, 'Po': 84, 'PO': 84, 'po': 84, 'At': 85, 'AT': 85, 'at': 85, 'Rn': 86, 'RN': 86, 'rn': 86, 'Fr': 87, 'FR': 87, 'fr': 87, 'Ra': 88, 'RA': 88, 'ra': 88, 'Ac': 89, 'AC': 89, 'ac': 89, 'Th': 90, 'TH': 90, 'th': 90, 'Pa': 91, 'PA': 91, 'pa': 91, 'U': 92, 'u': 92, 'Np': 93, 'NP': 93, 'np': 93, 'Pu': 94, 'PU': 94, 'pu': 94, 'Am': 95, 'AM': 95, 'am': 95, 'Cm': 96, 'CM': 96, 'cm': 96, 'Bk': 97, 'BK': 97, 'bk': 97, 'Cf': 98, 'CF': 98, 'cf': 98, 'Es': 99, 'ES': 99, 'es': 99, 'Fm': 100, 'FM': 100, 'fm': 100, 'Md': 101, 'MD': 101, 'md': 101, 'No': 102, 'NO': 102, 'no': 102, 'Lr': 103, 'LR': 103, 'lr': 103, 'Rf': 104, 'RF': 104, 'rf': 104, 'Db': 105, 'DB': 105, 'db': 105, 'Sg': 106, 'SG': 106, 'sg': 106, 'Bh': 107, 'BH': 107, 'bh': 107, 'Hs': 108, 'HS': 108, 'hs': 108, 'Mt': 109, 'MT': 109, 'mt': 109 }; + +const __ElementBondThresholds: { [e: number]: number | undefined } = { 0: 1.42, 1: 1.42, 3: 2.7, 4: 2.7, 6: 1.75, 7: 1.6, 8: 1.52, 11: 2.7, 12: 2.7, 13: 2.7, 14: 1.9, 15: 1.9, 16: 1.9, 17: 1.8, 19: 2.7, 20: 2.7, 21: 2.7, 22: 2.7, 23: 2.7, 24: 2.7, 25: 2.7, 26: 2.7, 27: 2.7, 28: 2.7, 29: 2.7, 30: 2.7, 31: 2.7, 33: 2.68, 37: 2.7, 38: 2.7, 39: 2.7, 40: 2.7, 41: 2.7, 42: 2.7, 43: 2.7, 44: 2.7, 45: 2.7, 46: 2.7, 47: 2.7, 48: 2.7, 49: 2.7, 50: 2.7, 55: 2.7, 56: 2.7, 57: 2.7, 58: 2.7, 59: 2.7, 60: 2.7, 61: 2.7, 62: 2.7, 63: 2.7, 64: 2.7, 65: 2.7, 66: 2.7, 67: 2.7, 68: 2.7, 69: 2.7, 70: 2.7, 71: 2.7, 72: 2.7, 73: 2.7, 74: 2.7, 75: 2.7, 76: 2.7, 77: 2.7, 78: 2.7, 79: 2.7, 80: 2.7, 81: 2.7, 82: 2.7, 83: 2.7, 87: 2.7, 88: 2.7, 89: 2.7, 90: 2.7, 91: 2.7, 92: 2.7, 93: 2.7, 94: 2.7, 95: 2.7, 96: 2.7, 97: 2.7, 98: 2.7, 99: 2.7, 100: 2.7, 101: 2.7, 102: 2.7, 103: 2.7, 104: 2.7, 105: 2.7, 106: 2.7, 107: 2.7, 108: 2.7, 109: 2.88 }; + +const __ElementPairThresholds: { [e: number]: number | undefined } = { 0: 0.8, 20: 1.31, 27: 1.3, 35: 1.3, 44: 1.05, 54: 1, 60: 1.84, 72: 1.88, 84: 1.75, 85: 1.56, 86: 1.76, 98: 1.6, 99: 1.68, 100: 1.63, 112: 1.55, 113: 1.59, 114: 1.36, 129: 1.45, 144: 1.6, 170: 1.4, 180: 1.55, 202: 2.4, 222: 2.24, 224: 1.91, 225: 1.98, 243: 2.02, 269: 2, 293: 1.9, 480: 2.3, 512: 2.3, 544: 2.3, 612: 2.1, 629: 1.54, 665: 1, 813: 2.6, 854: 2.27, 894: 1.93, 896: 2.1, 937: 2.05, 938: 2.06, 981: 1.62, 1258: 2.68, 1309: 2.33, 1484: 1, 1763: 2.14, 1823: 2.48, 1882: 2.1, 1944: 1.72, 2380: 2.34, 3367: 2.44, 3733: 2.11, 3819: 2.6, 3821: 2.36, 4736: 2.75, 5724: 2.73, 5959: 2.63, 6519: 2.84, 6750: 2.87, 8991: 2.81 }; + +const __DefaultBondingRadius = 2.001; + +const MetalsSet = (function () { + const metals = ['LI', 'NA', 'K', 'RB', 'CS', 'FR', 'BE', 'MG', 'CA', 'SR', 'BA', 'RA', 'AL', 'GA', 'IN', 'SN', 'TL', 'PB', 'BI', 'SC', 'TI', 'V', 'CR', 'MN', 'FE', 'CO', 'NI', 'CU', 'ZN', 'Y', 'ZR', 'NB', 'MO', 'TC', 'RU', 'RH', 'PD', 'AG', 'CD', 'LA', 'HF', 'TA', 'W', 'RE', 'OS', 'IR', 'PT', 'AU', 'HG', 'AC', 'RF', 'DB', 'SG', 'BH', 'HS', 'MT', 'CE', 'PR', 'ND', 'PM', 'SM', 'EU', 'GD', 'TB', 'DY', 'HO', 'ER', 'TM', 'YB', 'LU', 'TH', 'PA', 'U', 'NP', 'PU', 'AM', 'CM', 'BK', 'CF', 'ES', 'FM', 'MD', 'NO', 'LR']; + const set = new Set<number>(); + for (const m of metals) { + set.add(__ElementIndex[m]!); + } + return set; +})(); + +function pair(a: number, b: number) { + if (a < b) return (a + b) * (a + b + 1) / 2 + b; + else return (a + b) * (a + b + 1) / 2 + a; +} + +function idx(e: ElementSymbol) { + const i = __ElementIndex[e as any as string]; + if (i === void 0) return -1; + return i; +} + +function pairThreshold(i: number, j: number) { + if (i < 0 || j < 0) return -1; + const r = __ElementPairThresholds[pair(i, j)]; + if (r === void 0) return -1; + return r; +} + +function threshold(i: number) { + if (i < 0) return __DefaultBondingRadius; + const r = __ElementBondThresholds[i]; + if (r === void 0) return __DefaultBondingRadius; + return r; +} + +const H_ID = __ElementIndex['H']!; +function isHydrogen(i: number) { + return i === H_ID; +} + +function computePerAtomBonds(atomA: number[], atomB: number[], _order: number[], _flags: number[], atomCount: number) { + const bucketSizes = new Int32Array(atomCount); + const bucketOffsets = new Int32Array(atomCount + 1) as any as number[]; + const bucketFill = new Int32Array(atomCount); + + for (const i of atomA) bucketSizes[i]++; + for (const i of atomB) bucketSizes[i]++; + + let offset = 0; + for (let i = 0; i < atomCount; i++) { + bucketOffsets[i] = offset; + offset += bucketSizes[i]; + } + bucketOffsets[atomCount] = offset; + + const neighbor = new Int32Array(offset) as any as number[]; + const flags = new Uint16Array(offset) as any as number[]; + const order = new Int8Array(offset) as any as number[]; + + for (let i = 0, _i = atomA.length; i < _i; i++) { + const a = atomA[i], b = atomB[i], f = _flags[i], o = _order[i]; + + const oa = bucketOffsets[a] + bucketFill[a]; + const ob = bucketOffsets[b] + bucketFill[b]; + + neighbor[oa] = b; + flags[oa] = f; + order[oa] = o; + bucketFill[a]++; + + neighbor[ob] = a; + flags[ob] = f; + order[ob] = o; + bucketFill[b]++; + } + + return { + offsets: bucketOffsets, + neighbor, + flags, + order + }; +} + +function _computeBonds(unit: Unit.Atomic, atoms: ElementGroup, params: BondComputationParameters): GroupBonds { + const MAX_RADIUS = 3; + + const { x, y, z } = unit.model.conformation; + const atomCount = ElementGroup.size(atoms); + const { residueIndex } = unit; + const { type_symbol, label_atom_id, label_alt_id } = unit.model.hierarchy.atoms; + const { label_comp_id } = unit.model.hierarchy.residues; + const query3d = Unit.getLookup3d(unit, atoms); + + const structConn = unit.model.sourceData.kind === 'mmCIF' ? StructConn.create(unit.model) : void 0 + const component = unit.model.sourceData.kind === 'mmCIF' ? ComponentBondInfo.create(unit.model) : void 0 + + const atomA: number[] = []; + const atomB: number[] = []; + const flags: number[] = []; + const order: number[] = []; + + let lastResidue = -1; + let componentMap: Map<string, Map<string, { flags: number, order: number }>> | undefined = void 0; + + for (let _aI = 0; _aI < atomCount; _aI++) { + const aI = ElementGroup.getAt(atoms, _aI); + const raI = residueIndex[aI]; + + if (!params.forceCompute && raI !== lastResidue) { + const resn = label_comp_id.value(raI)!; + if (!!component && component.entries.has(resn)) { + componentMap = component.entries.get(resn)!.map; + } else { + componentMap = void 0; + } + } + lastResidue = raI; + + const componentPairs = componentMap ? componentMap.get(label_atom_id.value(aI)) : void 0; + + const aeI = idx(type_symbol.value(aI)!); + + const { indices, count, squaredDistances } = query3d.find(x[aI], y[aI], z[aI], MAX_RADIUS); + const isHa = isHydrogen(aeI); + const thresholdA = threshold(aeI); + const altA = label_alt_id.value(aI); + const metalA = MetalsSet.has(aeI); + const structConnEntries = params.forceCompute ? void 0 : structConn && structConn.getAtomEntries(aI); + + for (let ni = 0; ni < count; ni++) { + const _bI = indices[ni]; + const bI = ElementGroup.getAt(atoms, _bI); + if (bI <= aI) continue; + + const altB = label_alt_id.value(bI); + if (altA && altB && altA !== altB) continue; + + const beI = idx(type_symbol.value(bI)!); + const isMetal = metalA || MetalsSet.has(beI); + + const rbI = residueIndex[bI]; + // handle "component dictionary" bonds. + if (raI === rbI && componentPairs) { + const e = componentPairs.get(label_atom_id.value(bI)!); + if (e) { + atomA[atomA.length] = _aI; + atomB[atomB.length] = _bI; + order[order.length] = e.order; + let flag = e.flags; + if (isMetal) { + if (flag | BondType.Flag.Covalent) flag ^= BondType.Flag.Covalent; + flag |= BondType.Flag.MetallicCoordination; + } + flags[flags.length] = flag; + } + continue; + } + + const isHb = isHydrogen(beI); + if (isHa && isHb) continue; + + const dist = Math.sqrt(squaredDistances[ni]); + if (dist === 0) continue; + + // handle "struct conn" bonds. + if (structConnEntries && structConnEntries.length) { + let added = false; + for (const se of structConnEntries) { + for (const p of se.partners) { + if (p.atomIndex === bI) { + atomA[atomA.length] = _aI; + atomB[atomB.length] = _bI; + flags[flags.length] = se.flags; + order[order.length] = se.order; + added = true; + break; + } + } + if (added) break; + } + if (added) continue; + } + + if (isHa || isHb) { + if (dist < params.maxHbondLength) { + atomA[atomA.length] = _aI; + atomB[atomB.length] = _bI; + order[order.length] = 1; + flags[flags.length] = BondType.Flag.Covalent | BondType.Flag.Computed; // TODO: check if correct + } + continue; + } + + const thresholdAB = pairThreshold(aeI, beI); + const pairingThreshold = thresholdAB > 0 + ? thresholdAB + : beI < 0 ? thresholdA : Math.max(thresholdA, threshold(beI)); + + + if (dist <= pairingThreshold) { + atomA[atomA.length] = _aI; + atomB[atomB.length] = _bI; + order[order.length] = 1; + flags[flags.length] = (isMetal ? BondType.Flag.MetallicCoordination : BondType.Flag.Covalent) | BondType.Flag.Computed; + } + } + } + + const bonds = computePerAtomBonds(atomA, atomB, order, flags, atomCount); + + return { + offset: bonds.offsets, + neighbor: bonds.neighbor, + flags: bonds.flags, + order: bonds.order, + count: atomA.length + }; +} + +function computeUnitBonds(unit: Unit.Atomic, atoms: ElementGroup, params?: Partial<BondComputationParameters>) { + return _computeBonds(unit, atoms, { + maxHbondLength: (params && params.maxHbondLength) || 1.15, + forceCompute: !!(params && params.forceCompute), + }); +} + +export { computeUnitBonds } \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/bonds.ts b/src/mol-model/structure/structure/element/properties/bonds/group-data.ts similarity index 88% rename from src/mol-model/structure/model/properties/bonds.ts rename to src/mol-model/structure/structure/element/properties/bonds/group-data.ts index 706202d4d4693a3df4d235969a5e8f7a8fafeaae..0406f05d326db903c081be750001b069b4a89578 100644 --- a/src/mol-model/structure/model/properties/bonds.ts +++ b/src/mol-model/structure/structure/element/properties/bonds/group-data.ts @@ -5,9 +5,9 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { BondType } from '../types' +import { BondType } from '../../../../model/types' -interface Bonds { +interface GroupBonds { /** * Where bonds for atom A start and end. * Start offset at idx, end at idx + 1 @@ -21,8 +21,8 @@ interface Bonds { count: number } -namespace Bonds { - export function createEmpty(): Bonds { +namespace GroupBonds { + export function createEmpty(): GroupBonds { return { offset: [], neighbor: [], order: [], flags: [], count: 0 } } export function isCovalent(flags: number) { @@ -46,6 +46,4 @@ namespace Bonds { } } - - -export default Bonds \ No newline at end of file +export { GroupBonds } \ No newline at end of file diff --git a/src/mol-model/structure/structure/element/set.ts b/src/mol-model/structure/structure/element/set.ts index 0ecb481cd92560f156c9155ff75768de1403aabc..dddebe8bfd28b73505e0c93d18840a68087d83d4 100644 --- a/src/mol-model/structure/structure/element/set.ts +++ b/src/mol-model/structure/structure/element/set.ts @@ -9,26 +9,32 @@ import Element from '../element' import ElementGroup from './group' import * as Impl from './impl/set' import * as Builders from './impl/set-builder' +import { ElementSetLookup3D } from '../util/lookup3d'; +import Structure from '../structure'; -/** A map-like representation of grouped atom set */ +/** + * A map-like representation of grouped atom set + * + * Essentially corresponds to the type { [unitId: number]: ElementGroup }. + */ namespace ElementSet { export const Empty: ElementSet = Impl.Empty as any; export const ofAtoms: (elements: ArrayLike<Element>, template: ElementSet) => ElementSet = Impl.ofElements as any; export const singleton: (element: Element, template: ElementSet) => ElementSet = Impl.singleton as any; - export const unitCount: (set: ElementSet) => number = Impl.keyCount as any; - export const unitIds: (set: ElementSet) => SortedArray = Impl.getKeys as any; - export const unitHas: (set: ElementSet, id: number) => boolean = Impl.hasKey as any; - export const unitGetId: (set: ElementSet, i: number) => number = Impl.getKey as any; + export const unitIndices: (set: ElementSet) => SortedArray = Impl.getKeys as any; + export const unitHas: (set: ElementSet, index: number) => boolean = Impl.hasKey as any; - export const unitGetById: (set: ElementSet, key: number) => ElementGroup = Impl.getByKey as any; - export const unitGetByIndex: (set: ElementSet, i: number) => ElementGroup = Impl.getByIndex as any; + export const groupCount: (set: ElementSet) => number = Impl.keyCount as any; + export const groupUnitIndex: (set: ElementSet, index: number) => number = Impl.getKey as any; + export const groupFromUnitIndex: (set: ElementSet, unitId: number) => ElementGroup = Impl.getByKey as any; + export const groupAt: (set: ElementSet, index: number) => ElementGroup = Impl.getByIndex as any; export const elementCount: (set: ElementSet) => number = Impl.size as any; export const elementHas: (set: ElementSet, x: Element) => boolean = Impl.hasAtom as any; export const elementIndexOf: (set: ElementSet, x: Element) => number = Impl.indexOf as any; - export const elementGetAt: (set: ElementSet, i: number) => Element = Impl.getAt as any; + export const elementAt: (set: ElementSet, i: number) => Element = Impl.getAt as any; export const elements: (set: ElementSet) => Iterator<Element> = Impl.values as any; export const hashCode: (set: ElementSet) => number = Impl.hashCode as any; @@ -49,12 +55,13 @@ namespace ElementSet { export interface TemplateGenerator { add(unit: number, set: OrderedSet): void, getSet(): ElementSet } export const TemplateGenerator: (template: ElementSet) => TemplateGenerator = Impl.TemplateGenerator as any - // TODO: bounding sphere + export const getLookup3d: (s: Structure) => ElementSetLookup3D = Impl.getLookup3d; + // TODO: distance, areWithIn? // TODO: check connected // TODO: add "parent" property? how to avoid using too much memory? Transitive parents? Parent unlinking? } -interface ElementSet { '@type': 'element-set' | Element['@type'] } +interface ElementSet { '@type': 'element-set' } export default ElementSet \ 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 5dbcd62ec20c11c7be36c782638bb13a5d70d544..710926b6138c69d9e2b85a8b3823fd32cee9e348 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -12,15 +12,12 @@ import Unit from './unit' import ElementSet from './element/set' import ElementGroup from './element/group' import Element from './element' -import { StructureLookup3D } from './util/lookup3d'; // A structure is a pair of "units" and an element set. // Each unit contains the data and transformation of its corresponding elements. interface Structure { readonly units: ReadonlyArray<Unit>, - readonly elements: ElementSet, - - __lookup3d__?: StructureLookup3D + readonly elements: ElementSet } namespace Structure { @@ -77,7 +74,7 @@ namespace Structure { export function getModels(s: Structure) { const { units, elements } = s; const arr = UniqueArray.create<Model['id'], Model>(); - const ids = ElementSet.unitIds(elements); + const ids = ElementSet.unitIndices(elements); for (let i = 0; i < ids.length; i++) { const u = units[ids[i]]; UniqueArray.add(arr, u.model.id, u.model); @@ -86,9 +83,7 @@ namespace Structure { } export function getLookup3d(s: Structure) { - if (s.__lookup3d__) return s.__lookup3d__; - s.__lookup3d__ = StructureLookup3D.create(s); - return s.__lookup3d__; + return ElementSet.getLookup3d(s); } export function getBoundary(s: Structure) { diff --git a/src/mol-model/structure/structure/symmetry.ts b/src/mol-model/structure/structure/symmetry.ts index 179a058aca6aaeea502a28bc7749d5f55a458555..7fe2fb2bb874e1402537df5ec312c3df5677e5ed 100644 --- a/src/mol-model/structure/structure/symmetry.ts +++ b/src/mol-model/structure/structure/symmetry.ts @@ -9,36 +9,39 @@ import ElementSet from './element/set' import Unit from './unit' import { Selection } from '../query' import { ModelSymmetry } from '../model' +import { Task } from 'mol-task'; namespace Symmetry { - export const buildAssembly = buildAssemblyImpl; + export const buildAssembly = buildAssemblyImpl; } export default Symmetry; function buildAssemblyImpl(structure: Structure, name: string) { - const models = Structure.getModels(structure); - if (models.length !== 1) throw new Error('Can only build assemblies from structures based on 1 model.'); + return Task.create('Build Symmetry', async ctx => { + const models = Structure.getModels(structure); + if (models.length !== 1) throw new Error('Can only build assemblies from structures based on 1 model.'); - const assembly = ModelSymmetry.findAssembly(models[0], name); - if (!assembly) throw new Error(`Assembly '${name}' is not defined.`); + const assembly = ModelSymmetry.findAssembly(models[0], name); + if (!assembly) throw new Error(`Assembly '${name}' is not defined.`); - const assembler = Structure.Builder(); + const assembler = Structure.Builder(); - for (const g of assembly.operatorGroups) { - const selection = g.selector(structure); - if (Selection.structureCount(selection) === 0) continue; - const { units, elements } = Selection.unionStructure(selection); + for (const g of assembly.operatorGroups) { + const selection = await ctx.runChild(g.selector(structure)); + if (Selection.structureCount(selection) === 0) continue; + const { units, elements } = Selection.unionStructure(selection); - const unitIds = ElementSet.unitIds(elements); + const unitIds = ElementSet.unitIndices(elements); - for (const oper of g.operators) { - for (let uI = 0, _uI = unitIds.length; uI < _uI; uI++) { - const unit = units[unitIds[uI]]; - assembler.add(Unit.withOperator(unit, oper), ElementSet.unitGetByIndex(elements, uI)); + for (const oper of g.operators) { + for (let uI = 0, _uI = unitIds.length; uI < _uI; uI++) { + const unit = units[unitIds[uI]]; + assembler.add(Unit.withOperator(unit, oper), ElementSet.groupAt(elements, uI)); + } } } - } - return assembler.getStructure(); + return assembler.getStructure(); + }); } \ No newline at end of file diff --git a/src/mol-model/structure/structure/unit.ts b/src/mol-model/structure/structure/unit.ts index 76accfeaf0fd69c0f787362deb99388daf9c1d4d..2109cc10b299b6932a99f05acf410f86abdb45f9 100644 --- a/src/mol-model/structure/structure/unit.ts +++ b/src/mol-model/structure/structure/unit.ts @@ -7,6 +7,8 @@ import { SymmetryOperator } from 'mol-math/geometry/symmetry-operator' import ElementGroup from './element/group' import { Model } from '../model' +import { GridLookup3D } from 'mol-math/geometry' +import { computeUnitBonds } from './element/properties/bonds/group-compute'; // A building block of a structure that corresponds to an atomic or a coarse grained representation // 'conveniently grouped together'. @@ -83,6 +85,27 @@ namespace Unit { case Kind.Coarse: return createCoarse(unit.model, SymmetryOperator.compose(unit.operator, operator), unit.fullGroup); } } + + export function getLookup3d(unit: Unit, group: ElementGroup) { + if (group.__lookup3d__) return group.__lookup3d__; + if (Unit.isAtomic(unit)) { + const { x, y, z } = unit.model.conformation; + group.__lookup3d__ = GridLookup3D({ x, y, z, indices: group.elements }); + return group.__lookup3d__; + } + + throw 'not implemented'; + } + + export function getGroupBonds(unit: Unit, group: ElementGroup) { + if (group.__bonds__) return group.__bonds__; + if (Unit.isAtomic(unit)) { + group.__bonds__ = computeUnitBonds(unit, group); + return group.__bonds__; + } + + throw 'not implemented'; + } } export default Unit; \ No newline at end of file diff --git a/src/mol-model/structure/structure/util/boundary.ts b/src/mol-model/structure/structure/util/boundary.ts index 83655a247c9ee8a2289b29d942bf68b7f7c2cf7b..547d3828eb2f892bcdb9b8b620cebe36fc0a0610 100644 --- a/src/mol-model/structure/structure/util/boundary.ts +++ b/src/mol-model/structure/structure/util/boundary.ts @@ -20,9 +20,9 @@ function computeStructureBoundary(s: Structure): { box: Box3D, sphere: Sphere3D let radiusSq = 0; let size = 0; - for (let i = 0, _i = ElementSet.unitCount(elements); i < _i; i++) { - const group = ElementSet.unitGetByIndex(elements, i); - const { x, y, z } = units[ElementSet.unitGetId(elements, i)]; + for (let i = 0, _i = ElementSet.groupCount(elements); i < _i; i++) { + const group = ElementSet.groupAt(elements, i); + const { x, y, z } = units[ElementSet.groupUnitIndex(elements, i)]; size += ElementGroup.size(group); for (let j = 0, _j = ElementGroup.size(group); j < _j; j++) { @@ -48,9 +48,9 @@ function computeStructureBoundary(s: Structure): { box: Box3D, sphere: Sphere3D cz /= size; } - for (let i = 0, _i = ElementSet.unitCount(elements); i < _i; i++) { - const group = ElementSet.unitGetByIndex(elements, i); - const { x, y, z } = units[ElementSet.unitGetId(elements, i)]; + for (let i = 0, _i = ElementSet.groupCount(elements); i < _i; i++) { + const group = ElementSet.groupAt(elements, i); + const { x, y, z } = units[ElementSet.groupUnitIndex(elements, i)]; size += ElementGroup.size(group); for (let j = 0, _j = ElementGroup.size(group); j < _j; j++) { diff --git a/src/mol-model/structure/structure/util/lookup3d.ts b/src/mol-model/structure/structure/util/lookup3d.ts index 7434852bcb15496839eb74017aa6212041dbd12d..bb7230600205a39c78beeff7ac044b2d99a95d8d 100644 --- a/src/mol-model/structure/structure/util/lookup3d.ts +++ b/src/mol-model/structure/structure/util/lookup3d.ts @@ -7,15 +7,15 @@ import Structure from '../structure' import Element from '../element' import { Lookup3D, GridLookup3D, Result, Box3D, Sphere3D } from 'mol-math/geometry'; -import { ElementGroup, ElementSet } from '../../structure'; +import { ElementSet, Unit } from '../../structure'; import { Vec3 } from 'mol-math/linear-algebra'; import { OrderedSet } from 'mol-data/int'; import { computeStructureBoundary } from './boundary'; -interface StructureLookup3D extends Lookup3D<Element> {} +interface ElementSetLookup3D extends Lookup3D<Element> {} -namespace StructureLookup3D { - class Impl implements StructureLookup3D { +namespace ElementSetLookup3D { + class Impl implements ElementSetLookup3D { private unitLookup: Lookup3D; private result = Result.create<Element>(); private pivot = Vec3.zero(); @@ -28,14 +28,14 @@ namespace StructureLookup3D { for (let t = 0, _t = closeUnits.count; t < _t; t++) { const i = closeUnits.indices[t]; - const unitId = ElementSet.unitGetId(elements, i); - const group = ElementSet.unitGetByIndex(elements, i); + const unitId = ElementSet.groupUnitIndex(elements, i); + const group = ElementSet.groupAt(elements, i); const unit = units[unitId]; Vec3.set(this.pivot, x, y, z); if (!unit.operator.isIdentity) { Vec3.transformMat4(this.pivot, this.pivot, unit.operator.inverse); } - const groupLookup = ElementGroup.getLookup3d(unit, group); + const groupLookup = Unit.getLookup3d(unit, group); const groupResult = groupLookup.find(this.pivot[0], this.pivot[1], this.pivot[2], radius); for (let j = 0, _j = groupResult.count; j < _j; j++) { Result.add(this.result, Element.create(unitId, groupResult.indices[j]), groupResult.squaredDistances[j]); @@ -52,14 +52,14 @@ namespace StructureLookup3D { for (let t = 0, _t = closeUnits.count; t < _t; t++) { const i = closeUnits.indices[t]; - const unitId = ElementSet.unitGetId(elements, i); - const group = ElementSet.unitGetByIndex(elements, i); + const unitId = ElementSet.groupUnitIndex(elements, i); + const group = ElementSet.groupAt(elements, i); const unit = units[unitId]; Vec3.set(this.pivot, x, y, z); if (!unit.operator.isIdentity) { Vec3.transformMat4(this.pivot, this.pivot, unit.operator.inverse); } - const groupLookup = ElementGroup.getLookup3d(unit, group); + const groupLookup = Unit.getLookup3d(unit, group); if (groupLookup.check(this.pivot[0], this.pivot[1], this.pivot[2], radius)) return true; } @@ -70,7 +70,7 @@ namespace StructureLookup3D { constructor(private structure: Structure) { const { units, elements } = structure; - const unitCount = ElementSet.unitCount(elements); + const unitCount = ElementSet.groupCount(elements); const xs = new Float32Array(unitCount); const ys = new Float32Array(unitCount); const zs = new Float32Array(unitCount); @@ -78,9 +78,9 @@ namespace StructureLookup3D { const center = Vec3.zero(); for (let i = 0; i < unitCount; i++) { - const group = ElementSet.unitGetByIndex(elements, i); - const unit = units[ElementSet.unitGetId(elements, i)]; - const lookup = ElementGroup.getLookup3d(unit, group); + const group = ElementSet.groupAt(elements, i); + const unit = units[ElementSet.groupUnitIndex(elements, i)]; + const lookup = Unit.getLookup3d(unit, group); const s = lookup.boundary.sphere; Vec3.transformMat4(center, s.center, unit.operator.matrix); @@ -96,9 +96,9 @@ namespace StructureLookup3D { } } - export function create(s: Structure): StructureLookup3D { + export function create(s: Structure): ElementSetLookup3D { return new Impl(s); } } -export { StructureLookup3D } \ No newline at end of file +export { ElementSetLookup3D } \ No newline at end of file diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts index e27ae3f04b84dab91609b1a63219f888f7736d96..57a23cec6d93ba453392cf2c4bdb2fb1feb29f3c 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, ElementGroup, ElementSet, Selection, Symmetry, Unit } from 'mol-model/structure' +import { Structure, Model, Queries as Q, Element, ElementGroup, ElementSet, Selection, Symmetry, Unit, Query } from 'mol-model/structure' import { Segmentation, OrderedSet } from 'mol-data/int' import to_mmCIF from 'mol-model/structure/export/mmcif' @@ -120,14 +120,14 @@ export namespace PropertyAccess { function sumProperty(structure: Structure, p: Element.Property<number>) { const { elements, units } = structure; - const unitIds = ElementSet.unitIds(elements); + const unitIds = ElementSet.unitIndices(elements); const l = Element.Location(); let s = 0; for (let i = 0, _i = unitIds.length; i < _i; i++) { l.unit = units[unitIds[i]]; - const set = ElementSet.unitGetByIndex(elements, i); + const set = ElementSet.groupAt(elements, i); for (let j = 0, _j = ElementGroup.size(set); j < _j; j++) { @@ -141,7 +141,7 @@ export namespace PropertyAccess { function sumPropertySegmented(structure: Structure, p: Element.Property<number>) { const { elements, units } = structure; - const unitIds = ElementSet.unitIds(elements); + const unitIds = ElementSet.unitIndices(elements); const l = Element.Location(); let s = 0; @@ -150,7 +150,7 @@ export namespace PropertyAccess { for (let i = 0, _i = unitIds.length; i < _i; i++) { const unit = units[unitIds[i]]; l.unit = unit; - const set = ElementSet.unitGetByIndex(elements, i); + const set = ElementSet.groupAt(elements, i); const chainsIt = Segmentation.transientSegments(unit.hierarchy.chainSegments, set.elements); const residues = unit.hierarchy.residueSegments; @@ -294,16 +294,16 @@ export namespace PropertyAccess { console.log(to_mmCIF('test', s)); } - export function testAssembly(id: string, s: Structure) { + export async function testAssembly(id: string, s: Structure) { console.time('assembly') - const a = Symmetry.buildAssembly(s, '1'); + const a = await Run(Symmetry.buildAssembly(s, '1')); console.timeEnd('assembly') fs.writeFileSync(`${DATA_DIR}/${id}_assembly.bcif`, to_mmCIF(id, a, true)); console.log('exported'); } - export function testGrouping(structure: Structure) { - const { elements, units } = Symmetry.buildAssembly(structure, '1'); + export async function testGrouping(structure: Structure) { + const { elements, units } = await Run(Symmetry.buildAssembly(structure, '1')); console.log('grouping', units.length); console.log('built asm'); @@ -312,15 +312,19 @@ export namespace PropertyAccess { (a, b) => a.unit.model.id === b.unit.model.id && (a.group.key === b.group.key && OrderedSet.areEqual(a.group.elements, b.group.elements)) ); - for (let i = 0, _i = ElementSet.unitCount(elements); i < _i; i++) { - const group = ElementSet.unitGetByIndex(elements, i); - const unitId = ElementSet.unitGetId(elements, i); + for (let i = 0, _i = ElementSet.groupCount(elements); i < _i; i++) { + const group = ElementSet.groupAt(elements, i); + const unitId = ElementSet.groupUnitIndex(elements, i); uniqueGroups.add(unitId, { unit: units[unitId], group }); } console.log('group count', uniqueGroups.groups.length); } + function query(q: Query, s: Structure) { + return Run((q(s))); + } + export async function run() { //const { structures, models/*, mmcif*/ } = await getBcif('1cbs'); // const { structures, models } = await getBcif('3j3q'); @@ -375,26 +379,26 @@ export namespace PropertyAccess { //const auth_asym_id = Q.props.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 = Q.generators.atoms({ atomTest: Q.pred.eq(Q.props.residue.auth_comp_id, 'ALA') }); + const q = Query(Q.generators.atoms({ atomTest: Q.pred.eq(Q.props.residue.auth_comp_id, 'ALA') })); const P = Q.props //const q0 = Q.generators.atoms({ atomTest: l => auth_comp_id(l) === 'ALA' }); - const q1 = Q.generators.atoms({ residueTest: l => auth_comp_id(l) === 'ALA' }); - const q2 = Q.generators.atoms({ residueTest: l => auth_comp_id(l) === 'ALA', groupBy: Q.props.residue.key }); - const q3 = Q.generators.atoms({ + 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 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') - }); - q(structures[0]); + })); + await query(q, structures[0]); //console.log(to_mmCIF('test', Selection.union(q0r))); console.time('q1') - q1(structures[0]); + await query(q1, structures[0]); console.timeEnd('q1') console.time('q1') - q1(structures[0]); + await query(q1, structures[0]); console.timeEnd('q1') console.time('q2') - const q2r = q2(structures[0]); + const q2r = await query(q2, structures[0]); console.timeEnd('q2') console.log(Selection.structureCount(q2r)); //console.log(q1(structures[0])); @@ -404,8 +408,8 @@ export namespace PropertyAccess { suite //.add('test q', () => q1(structures[0])) //.add('test q', () => q(structures[0])) - .add('test q1', () => q1(structures[0])) - .add('test q3', () => q3(structures[0])) + .add('test q1', async () => await q1(structures[0])) + .add('test q3', async () => await q3(structures[0])) //.add('test int', () => sumProperty(structures[0], l => col(l.element)) // .add('sum residue', () => sumPropertyResidue(structures[0], l => l.unit.hierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.atom])))