diff --git a/data/mmcif-field-names.csv b/data/mmcif-field-names.csv index 61649087a630b56eaacafc345338f2b8b37a9584..bd1f2d51bdfe8257abd257a27b09b9f4b6afc390 100644 --- a/data/mmcif-field-names.csv +++ b/data/mmcif-field-names.csv @@ -1,3 +1,7 @@ +atom_sites.entry_id +atom_sites.fract_transf_matrix +atom_sites.fract_transf_vector + atom_site.group_PDB atom_site.id atom_site.type_symbol diff --git a/src/mol-io/reader/cif/schema/mmcif.ts b/src/mol-io/reader/cif/schema/mmcif.ts index 373fa1ae8401ea1043707cebc355e0760cf321dc..8f67d33bfa2b3c7ff56c2358ba004197450eb8ca 100644 --- a/src/mol-io/reader/cif/schema/mmcif.ts +++ b/src/mol-io/reader/cif/schema/mmcif.ts @@ -21,6 +21,11 @@ const Vector = Schema.Vector; const List = Schema.List; export const mmCIF_Schema = { + atom_sites: { + entry_id: str, + fract_transf_matrix: Matrix(3, 3), + fract_transf_vector: Vector(3) + }, atom_site: { auth_asym_id: str, auth_atom_id: str, diff --git a/src/mol-math/geometry/spacegroup/construction.ts b/src/mol-math/geometry/spacegroup/construction.ts index 65aa22e849283499982fbf6fefdd71ade328105b..0492fc297ba0ddf35af83d5a4757d5bc3ec0518f 100644 --- a/src/mol-math/geometry/spacegroup/construction.ts +++ b/src/mol-math/geometry/spacegroup/construction.ts @@ -5,11 +5,12 @@ */ import { Vec3, Mat4 } from '../../linear-algebra' -import { SpacegroupName, TransformData, GroupData, SpacegroupNumbers, SpacegroupNames, OperatorData } from './tables' +import { SpacegroupName, TransformData, GroupData, getSpacegroupIndex, OperatorData, SpacegroupNames } from './tables' import { SymmetryOperator } from '../../geometry'; interface SpacegroupCell { - readonly number: number, + // zero based spacegroup number + readonly index: number, readonly size: Vec3, readonly anglesInRadians: Vec3, /** Transfrom cartesian -> fractional coordinates within the cell */ @@ -26,16 +27,18 @@ interface Spacegroup { namespace SpacegroupCell { // Create a 'P 1' with cellsize [1, 1, 1] - export function zero() { - return create(0, Vec3.create(1, 1, 1), Vec3.create(Math.PI / 2, Math.PI / 2, Math.PI / 2)); - } + export const Zero: SpacegroupCell = create('P 1', Vec3.create(1, 1, 1), Vec3.create(Math.PI / 2, Math.PI / 2, Math.PI / 2)); // True if 'P 1' with cellsize [1, 1, 1] export function isZero(cell: SpacegroupCell) { - return cell.size[0] === 1 && cell.size[1] === 1 && cell.size[1] === 1; + return cell.index === 0 && cell.size[0] === 1 && cell.size[1] === 1 && cell.size[1] === 1; } - export function create(nameOrNumber: number | SpacegroupName, size: Vec3, anglesInRadians: Vec3): SpacegroupCell { + // returns Zero cell if the spacegroup does not exist + export function create(nameOrNumber: number | string | SpacegroupName, size: Vec3, anglesInRadians: Vec3): SpacegroupCell { + const index = getSpacegroupIndex(nameOrNumber); + if (index < 0) return Zero; + const alpha = anglesInRadians[0]; const beta = anglesInRadians[1]; const gamma = anglesInRadians[2]; @@ -58,23 +61,18 @@ namespace SpacegroupCell { ]); const toFractional = Mat4.invert(Mat4.zero(), fromFractional)!; - const num = typeof nameOrNumber === 'number' ? nameOrNumber : SpacegroupNumbers[nameOrNumber]; - return { number: num, size, anglesInRadians, toFractional, fromFractional }; + return { index, size, anglesInRadians, toFractional, fromFractional }; } } -namespace Spacegroup { - export function create(nameOrNumber: number | SpacegroupName, cell: SpacegroupCell): Spacegroup { - const num = typeof nameOrNumber === 'number' ? nameOrNumber : SpacegroupNumbers[nameOrNumber]; - const name = typeof nameOrNumber === 'number' ? SpacegroupNames[nameOrNumber] : nameOrNumber; - if (typeof num === 'undefined' || typeof name === 'undefined') { - throw new Error(`Spacegroup '${nameOrNumber}' is not defined.`); - } - - const operators = GroupData[num].map(i => getOperatorMatrix(OperatorData[i])); +namespace Spacegroup { + // P1 with [1, 1, 1] cell. + export const ZeroP1 = create(SpacegroupCell.Zero); - return { name, cell, operators }; + export function create(cell: SpacegroupCell): Spacegroup { + const operators = GroupData[cell.index].map(i => getOperatorMatrix(OperatorData[i])); + return { name: SpacegroupNames[cell.index], cell, operators }; } const _tempVec = Vec3.zero(), _tempMat = Mat4.zero(); @@ -89,6 +87,8 @@ namespace Spacegroup { export function getSymmetryOperator(spacegroup: Spacegroup, index: number, i: number, j: number, k: number): SymmetryOperator { const operator = updateOperatorMatrix(spacegroup, index, i, j, k, Mat4.zero()); + console.log(Mat4.makeTable(operator)); + console.log({ index, i, j, k }); return SymmetryOperator.create(`${index + 1}_${5 + i}${5 + j}${5 + k}`, operator, Vec3.create(i, j, k)); } diff --git a/src/mol-math/geometry/spacegroup/tables.ts b/src/mol-math/geometry/spacegroup/tables.ts index de3ec97927b441f1fc80b0a8d665650b628bf331..91ae4bc0273ed99cc361e047c64684c0cacea90f 100644 --- a/src/mol-math/geometry/spacegroup/tables.ts +++ b/src/mol-math/geometry/spacegroup/tables.ts @@ -985,7 +985,7 @@ export const GroupData = [ [0, 22, 57, 3, 159, 279, 654, 655, 158, 274, 656, 657, 29, 18, 25, 27, 284, 658, 262, 269, 280, 659, 257, 267], ]; -export const SpacegroupNumbers = { +export const ZeroBasedSpacegroupNumbers = { 'P 1': 0, 'P -1': 1, 'P 1 2 1': 2, @@ -1333,12 +1333,19 @@ export const SpacegroupNumbers = { 'I 2 3a': 265, }; -export type SpacegroupName = keyof typeof SpacegroupNumbers +export type SpacegroupName = keyof typeof ZeroBasedSpacegroupNumbers export const SpacegroupNames: { [num: number]: SpacegroupName } = (function () { const names = Object.create(null); - for (const n of Object.keys(SpacegroupNumbers)) { - names[(SpacegroupNumbers as any)[n]] = n; + for (const n of Object.keys(ZeroBasedSpacegroupNumbers)) { + names[(ZeroBasedSpacegroupNumbers as any)[n]] = n; } return names; -}()); \ No newline at end of file +}()); + +// return -1 if the spacegroup does not exist. +export function getSpacegroupIndex(nameOrNumber: number | string | SpacegroupName): number { + const index = typeof nameOrNumber === 'number' ? nameOrNumber - 1 : ZeroBasedSpacegroupNumbers[nameOrNumber as SpacegroupName]; + if (typeof index === 'undefined' || typeof SpacegroupNames[index] === 'undefined') return -1; + return index; +} \ No newline at end of file diff --git a/src/mol-math/linear-algebra/3d/common.ts b/src/mol-math/linear-algebra/3d/common.ts index eebaeb2901c60d1213d8e5963a5c44fb3fc2842f..4276dd8d60b1e254621bfb87e8dbd5302b023d0e 100644 --- a/src/mol-math/linear-algebra/3d/common.ts +++ b/src/mol-math/linear-algebra/3d/common.ts @@ -17,4 +17,8 @@ * furnished to do so, subject to the following conditions: */ -export const enum EPSILON { Value = 0.000001 } \ No newline at end of file +export const enum EPSILON { Value = 0.000001 } + +export function equalEps(a: number, b: number, eps: number) { + return Math.abs(a - b) <= eps; +} diff --git a/src/mol-math/linear-algebra/3d/mat4.ts b/src/mol-math/linear-algebra/3d/mat4.ts index 930eaf8518e425ff451b638830264510a8c5d97b..a23c5503950401af6c82307b0cbcbdce23bfbadd 100644 --- a/src/mol-math/linear-algebra/3d/mat4.ts +++ b/src/mol-math/linear-algebra/3d/mat4.ts @@ -17,7 +17,7 @@ * furnished to do so, subject to the following conditions: */ -import { EPSILON } from './common' +import { EPSILON, equalEps } from './common' import Vec3 from './vec3'; import Quat from './quat'; @@ -539,11 +539,11 @@ namespace Mat4 { a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], /* a30 = a[12], a31 = a[13], a32 = a[14],*/ a33 = a[15]; - if (a33 !== 1 || a03 !== 0 || a13 !== 0 || a23 !== 0) { + if (!equalEps(a33, 1, eps) || !equalEps(a03, 0, eps) || !equalEps(a13, 0, eps) || !equalEps(a23, 0, eps)) { return false; } const det3x3 = a00 * (a11 * a22 - a12 * a21) - a01 * (a10 * a22 - a12 * a20) + a02 * (a10 * a21 - a11 * a20); - if (det3x3 < 1 - eps || det3x3 > 1 + eps) { + if (!equalEps(det3x3, 1, eps)) { return false; } return true; diff --git a/src/mol-model/structure/model.ts b/src/mol-model/structure/model.ts index c53fd0986639873923cf30fbceba2d6010233741..1704ab09282eb09ec1e30a442532e52dc80def63 100644 --- a/src/mol-model/structure/model.ts +++ b/src/mol-model/structure/model.ts @@ -7,6 +7,6 @@ import Model from './model/model' import * as Types from './model/types' import Format from './model/format' -import ModelSymmetry from './model/properties/symmetry' +import { ModelSymmetry } from './model/properties/symmetry' export { Model, Types, Format, ModelSymmetry } \ No newline at end of file diff --git a/src/mol-model/structure/model/formats/gro.ts b/src/mol-model/structure/model/formats/gro.ts index d2348b1207db8147026b466363c4d593bfc0a89f..46a9cd1652087d59eb597918ac25fa29f34d68a1 100644 --- a/src/mol-model/structure/model/formats/gro.ts +++ b/src/mol-model/structure/model/formats/gro.ts @@ -10,8 +10,7 @@ import { Interval, Segmentation } from 'mol-data/int' import { Atoms } from 'mol-io/reader/gro/schema' import Format from '../format' import Model from '../model' -import * as AtomicHierarchy from '../properties/atomic/hierarchy' -import { AtomicConformation } from '../properties/atomic/conformation' +import { AtomicConformation, AtomicData, AtomsSchema, ResiduesSchema, ChainsSchema, AtomicSegments } from '../properties/atomic' import { CoarseGrainedHierarchy } from '../properties/coarse-grained/hierarchy' import findHierarchyKeys from '../utils/hierarchy-keys' import { guessElement } from '../utils/guess-element' @@ -21,6 +20,7 @@ import { mmCIF_Schema as mmCIF } from 'mol-io/reader/cif/schema/mmcif' import gro_Format = Format.gro import Sequence from '../properties/sequence'; import { Entities } from '../properties/common'; +import { ModelSymmetry } from '../properties/symmetry'; type HierarchyOffsets = { residues: ArrayLike<number>, chains: ArrayLike<number> } @@ -44,9 +44,9 @@ function guessElementSymbol (value: string) { return ElementSymbol(guessElement(value)); } -function createHierarchyData(atomsData: Atoms, offsets: HierarchyOffsets): AtomicHierarchy.AtomicData { +function createHierarchyData(atomsData: Atoms, offsets: HierarchyOffsets): AtomicData { console.log(atomsData.atomName) - const atoms = Table.ofColumns(AtomicHierarchy.AtomsSchema, { + const atoms = Table.ofColumns(AtomsSchema, { type_symbol: Column.ofArray({ array: Column.mapToArray(atomsData.atomName, guessElementSymbol), schema: Column.Schema.Aliased<ElementSymbol>(Column.Schema.str) }), label_atom_id: atomsData.atomName, auth_atom_id: atomsData.atomName, @@ -54,14 +54,14 @@ function createHierarchyData(atomsData: Atoms, offsets: HierarchyOffsets): Atomi pdbx_formal_charge: Column.Undefined(atomsData.count, Column.Schema.int) }); - const residues = Table.view(Table.ofColumns(AtomicHierarchy.ResiduesSchema, { + const residues = Table.view(Table.ofColumns(ResiduesSchema, { group_PDB: Column.Undefined(atomsData.count, Column.Schema.Aliased<'ATOM' | 'HETATM'>(Column.Schema.str)), label_comp_id: atomsData.residueName, auth_comp_id: atomsData.residueName, label_seq_id: atomsData.residueNumber, auth_seq_id: atomsData.residueNumber, pdbx_PDB_ins_code: Column.Undefined(atomsData.count, Column.Schema.str), - }), AtomicHierarchy.ResiduesSchema, offsets.residues); + }), ResiduesSchema, offsets.residues); // Optimize the numeric columns Table.columnToArray(residues, 'label_seq_id', Int32Array); Table.columnToArray(residues, 'auth_seq_id', Int32Array); @@ -72,7 +72,7 @@ function createHierarchyData(atomsData: Atoms, offsets: HierarchyOffsets): Atomi // label_entity_id: Column.Undefined(atomsData.count, Column.Schema.str) // }); - const chains = Table.ofUndefinedColumns(AtomicHierarchy.ChainsSchema, 0); + const chains = Table.ofUndefinedColumns(ChainsSchema, 0); return { atoms, residues, chains }; } @@ -89,10 +89,10 @@ function getConformation(atoms: Atoms): AtomicConformation { } } -function isHierarchyDataEqual(a: AtomicHierarchy.AtomicData, b: AtomicHierarchy.AtomicData) { +function isHierarchyDataEqual(a: AtomicData, b: AtomicData) { // need to cast because of how TS handles type resolution for interfaces https://github.com/Microsoft/TypeScript/issues/15300 - return Table.areEqual(a.residues as Table<AtomicHierarchy.ResiduesSchema>, b.residues as Table<AtomicHierarchy.ResiduesSchema>) - && Table.areEqual(a.atoms as Table<AtomicHierarchy.AtomsSchema>, b.atoms as Table<AtomicHierarchy.AtomsSchema>) + return Table.areEqual(a.residues as Table<ResiduesSchema>, b.residues as Table<ResiduesSchema>) + && Table.areEqual(a.atoms as Table<AtomsSchema>, b.atoms as Table<AtomsSchema>) } function createModel(format: gro_Format, modelNum: number, previous?: Model): Model { @@ -109,7 +109,7 @@ function createModel(format: gro_Format, modelNum: number, previous?: Model): Mo }; } - const hierarchySegments: AtomicHierarchy.AtomicSegments = { + const hierarchySegments: AtomicSegments = { residueSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, bounds), chainSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, bounds), } @@ -135,7 +135,7 @@ function createModel(format: gro_Format, modelNum: number, previous?: Model): Mo sequence: Sequence.fromAtomicHierarchy(hierarchy), atomSiteConformation: getConformation(structure.atoms), coarseGrained: CoarseGrainedHierarchy.Empty, - symmetry: { assemblies: [] }, + symmetry: ModelSymmetry.Default, atomCount: structure.atoms.count }; } diff --git a/src/mol-model/structure/model/formats/mmcif.ts b/src/mol-model/structure/model/formats/mmcif.ts index c9a962266f50706a26e346198420b01837db5212..22644e2fa2ef1fdbb7909b6a825275110fa5e24b 100644 --- a/src/mol-model/structure/model/formats/mmcif.ts +++ b/src/mol-model/structure/model/formats/mmcif.ts @@ -9,9 +9,8 @@ import { Column, Table } from 'mol-data/db' import { Interval, Segmentation } from 'mol-data/int' import Format from '../format' import Model from '../model' -import { AtomsSchema, ResiduesSchema, ChainsSchema, AtomicData, AtomicSegments } from '../properties/atomic/hierarchy' -import { AtomicConformation } from '../properties/atomic/conformation' -import Symmetry from '../properties/symmetry' +import { AtomsSchema, ResiduesSchema, ChainsSchema, AtomicData, AtomicSegments, AtomicConformation } from '../properties/atomic' +import { ModelSymmetry } from '../properties/symmetry' import findHierarchyKeys from '../utils/hierarchy-keys' import { ElementSymbol} from '../types' import createAssemblies from './mmcif/assembly' @@ -20,6 +19,8 @@ import mmCIF_Format = Format.mmCIF import { getSequence } from './mmcif/sequence'; import { Entities } from '../properties/common'; import { coarseGrainedFromIHM } from './mmcif/ihm'; +import { Spacegroup, SpacegroupCell } from 'mol-math/geometry'; +import { Vec3 } from 'mol-math/linear-algebra'; function findModelBounds({ data }: mmCIF_Format, startIndex: number) { const num = data.atom_site.pdbx_PDB_model_num; @@ -83,8 +84,29 @@ function getConformation({ data }: mmCIF_Format, bounds: Interval): AtomicConfor } } -function getSymmetry(format: mmCIF_Format): Symmetry { - return { assemblies: createAssemblies(format) }; +function getSymmetry(format: mmCIF_Format): ModelSymmetry { + const assemblies = createAssemblies(format); + const spacegroup = getSpacegroup(format); + const isNonStandardCrytalFrame = checkNonStandardCrystalFrame(format, spacegroup); + return { assemblies, spacegroup, isNonStandardCrytalFrame }; +} + +function checkNonStandardCrystalFrame(format: mmCIF_Format, spacegroup: Spacegroup) { + const { atom_sites } = format.data; + if (atom_sites._rowCount === 0) return false; + // TODO: parse atom_sites transform and check if it corresponds to the toFractional matrix + return false; +} + +function getSpacegroup(format: mmCIF_Format): Spacegroup { + const { symmetry, cell } = format.data; + if (symmetry._rowCount === 0 || cell._rowCount === 0) return Spacegroup.ZeroP1; + const groupName = symmetry['space_group_name_H-M'].value(0); + const spaceCell = SpacegroupCell.create(groupName, + Vec3.create(cell.length_a.value(0), cell.length_b.value(0), cell.length_c.value(0)), + Vec3.scale(Vec3.zero(), Vec3.create(cell.angle_alpha.value(0), cell.angle_beta.value(0), cell.angle_gamma.value(0)), Math.PI / 180)); + + return Spacegroup.create(spaceCell); } function isHierarchyDataEqual(a: AtomicData, b: AtomicData) { diff --git a/src/mol-model/structure/model/formats/mmcif/sequence.ts b/src/mol-model/structure/model/formats/mmcif/sequence.ts index 88da71446301e460c390e91de8771d54b093ca6f..3b30419696303332c6c91fec2fbc66b6de3f2451 100644 --- a/src/mol-model/structure/model/formats/mmcif/sequence.ts +++ b/src/mol-model/structure/model/formats/mmcif/sequence.ts @@ -7,7 +7,7 @@ import { mmCIF_Database as mmCIF } from 'mol-io/reader/cif/schema/mmcif' import Sequence from '../../properties/sequence' import { Column } from 'mol-data/db'; -import { AtomicHierarchy } from '../../properties/atomic/hierarchy'; +import { AtomicHierarchy } from '../../properties/atomic'; import { Entities } from '../../properties/common'; export function getSequence(cif: mmCIF, entities: Entities, hierarchy: AtomicHierarchy): Sequence { diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts index 8a0f009c2677eb068e8e2b69b4609d4ad3a13dd9..eb7d5024a4a0d524f59bee9a2328c2eae4cbeec2 100644 --- a/src/mol-model/structure/model/model.ts +++ b/src/mol-model/structure/model/model.ts @@ -7,9 +7,8 @@ import UUID from 'mol-util/uuid' import Format from './format' import Sequence from './properties/sequence' -import { AtomicHierarchy } from './properties/atomic/hierarchy' -import { AtomicConformation } from './properties/atomic/conformation' -import Symmetry from './properties/symmetry' +import { AtomicHierarchy, AtomicConformation } from './properties/atomic' +import { ModelSymmetry } from './properties/symmetry' import { CoarseGrainedHierarchy } from './properties/coarse-grained/hierarchy' import { Entities } from './properties/common'; @@ -28,7 +27,7 @@ interface Model extends Readonly<{ sourceData: Format, - symmetry: Symmetry, + symmetry: ModelSymmetry, entities: Entities, sequence: Sequence, diff --git a/src/mol-model/structure/model/properties/atomic.ts b/src/mol-model/structure/model/properties/atomic.ts new file mode 100644 index 0000000000000000000000000000000000000000..65b7bb2cbbf97f583911af6c54409be3f3e89718 --- /dev/null +++ b/src/mol-model/structure/model/properties/atomic.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +export * from './atomic/conformation' +export * from './atomic/hierarchy' +export * from './atomic/measures' \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/symmetry.ts b/src/mol-model/structure/model/properties/symmetry.ts index d459241b4a7ff1174933c8e410e4fa5e841eddad..3b2659e9e49541c16c1e47f8ae229905a0d1ca92 100644 --- a/src/mol-model/structure/model/properties/symmetry.ts +++ b/src/mol-model/structure/model/properties/symmetry.ts @@ -8,6 +8,7 @@ import { SymmetryOperator } from 'mol-math/geometry/symmetry-operator' import { arrayFind } from 'mol-data/util' import { Query } from '../../query' import { Model } from '../../model' +import { Spacegroup } from 'mol-math/geometry'; /** Determine an atom set and a list of operators that should be applied to that set */ export interface OperatorGroup { @@ -40,12 +41,14 @@ export namespace Assembly { } } -interface Symmetry { +interface ModelSymmetry { readonly assemblies: ReadonlyArray<Assembly>, + readonly spacegroup: Spacegroup, + readonly isNonStandardCrytalFrame: boolean } -namespace Symmetry { - export const Empty: Symmetry = { assemblies: [] }; +namespace ModelSymmetry { + export const Default: ModelSymmetry = { assemblies: [], spacegroup: Spacegroup.ZeroP1, isNonStandardCrytalFrame: false }; export function findAssembly(model: Model, id: string): Assembly | undefined { const _id = id.toLocaleLowerCase(); @@ -53,4 +56,4 @@ namespace Symmetry { } } -export default Symmetry \ No newline at end of file +export { ModelSymmetry } \ No newline at end of file diff --git a/src/mol-model/structure/model/utils/hierarchy-keys.ts b/src/mol-model/structure/model/utils/hierarchy-keys.ts index e825df9dd4a52de9158a946269434fd5fe8b5097..aabfe1b4a6bb9bed1633ab748d382aa935316588 100644 --- a/src/mol-model/structure/model/utils/hierarchy-keys.ts +++ b/src/mol-model/structure/model/utils/hierarchy-keys.ts @@ -5,7 +5,7 @@ */ import { Column } from 'mol-data/db' -import { AtomicData, AtomicSegments, AtomicKeys } from '../properties/atomic/hierarchy' +import { AtomicData, AtomicSegments, AtomicKeys } from '../properties/atomic' import { Interval, Segmentation } from 'mol-data/int' import { Entities } from '../properties/common'; diff --git a/src/mol-model/structure/query/properties.ts b/src/mol-model/structure/query/properties.ts index 6fe1eed352a7c9f8b52716522cf17f976401f4b5..fc2a066ad957730fe49ec9a884ffb47a822156e6 100644 --- a/src/mol-model/structure/query/properties.ts +++ b/src/mol-model/structure/query/properties.ts @@ -5,7 +5,7 @@ */ import { Element, Unit } from '../structure' -import { VdwRadius } from '../model/properties/atomic/measures'; +import { VdwRadius } from '../model/properties/atomic'; const constant = { true: Element.property(l => true), diff --git a/src/mol-model/structure/structure/symmetry.ts b/src/mol-model/structure/structure/symmetry.ts index 2bf71bda6742746edf52621bb930f204ac70418d..bf17c9013887a9f910257183ee3a2bfc99870826 100644 --- a/src/mol-model/structure/structure/symmetry.ts +++ b/src/mol-model/structure/structure/symmetry.ts @@ -11,15 +11,17 @@ import { Task } from 'mol-task'; import { SortedArray } from 'mol-data/int'; import Unit from './unit'; import { EquivalenceClasses, hash2 } from 'mol-data/util'; +import { Vec3 } from 'mol-math/linear-algebra'; +import { SymmetryOperator, Spacegroup, SpacegroupCell } from 'mol-math/geometry'; namespace StructureSymmetry { - export function buildAssembly(structure: Structure, name: string) { - return Task.create('Build Symmetry', async ctx => { + export function buildAssembly(structure: Structure, asmName: string) { + return Task.create('Build Assembly', 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], asmName); + if (!assembly) throw new Error(`Assembly '${asmName}' is not defined.`); const assembler = Structure.Builder(); @@ -41,6 +43,38 @@ namespace StructureSymmetry { }); } + export function buildSymmetryRange(structure: Structure, ijkMin: Vec3, ijkMax: Vec3) { + return Task.create('Build Assembly', async ctx => { + const models = Structure.getModels(structure); + if (models.length !== 1) throw new Error('Can only build symmetries from structures based on 1 model.'); + + const { spacegroup } = models[0].symmetry; + if (SpacegroupCell.isZero(spacegroup.cell)) return structure; + + const operators: SymmetryOperator[] = []; + for (let op = 0; op < spacegroup.operators.length; op++) { + for (let i = ijkMin[0]; i < ijkMax[0]; i++) { + for (let j = ijkMin[1]; j < ijkMax[1]; j++) { + for (let k = ijkMin[2]; k < ijkMax[2]; k++) { + operators[operators.length] = Spacegroup.getSymmetryOperator(spacegroup, op, i, j, k); + } + } + } + } + + const assembler = Structure.Builder(); + + const { units } = structure; + for (const oper of operators) { + for (const unit of units) { + assembler.addWithOperator(unit, oper); + } + } + + return assembler.getStructure(); + }); + } + function hashUnit(u: Unit) { return hash2(u.invariantId, SortedArray.hashCode(u.elements)); } diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts index d8e1037a964b2ba6083c2e33ab07365c1c1abdbd..4070cca0b782bfbc4a2d6f71ae7deb41b155f181 100644 --- a/src/perf-tests/structure.ts +++ b/src/perf-tests/structure.ts @@ -16,6 +16,7 @@ import { Structure, Model, Queries as Q, Element, Selection, StructureSymmetry, import to_mmCIF from 'mol-model/structure/export/mmcif' import { Run } from 'mol-task'; +import { Vec3 } from 'mol-math/linear-algebra'; //import { EquivalenceClasses } from 'mol-data/util'; require('util.promisify').shim(); @@ -302,6 +303,19 @@ export namespace PropertyAccess { console.log('exported'); } + export async function testSymmetry(id: string, s: Structure) { + console.time('symmetry') + const a = await Run(StructureSymmetry.buildSymmetryRange(s, Vec3.create(-1, -1, -1), Vec3.create(1, 1, 1))); + //const auth_comp_id = Q.props.residue.auth_comp_id; + //const q1 = Query(Q.generators.atoms({ residueTest: l => auth_comp_id(l) === 'ALA' })); + //const alas = await query(q1, a); + + console.timeEnd('symmetry') + fs.writeFileSync(`${DATA_DIR}/${id}_symm.bcif`, to_mmCIF(id, a, true)); + //fs.writeFileSync(`${DATA_DIR}/${id}_assembly.bcif`, to_mmCIF(id, Selection.unionStructure(alas), true)); + console.log('exported'); + } + // export async function testGrouping(structure: Structure) { // const { elements, units } = await Run(Symmetry.buildAssembly(structure, '1')); // console.log('grouping', units.length); @@ -329,7 +343,7 @@ export namespace PropertyAccess { //const { structures, models/*, mmcif*/ } = await getBcif('1cbs'); // const { structures, models } = await getBcif('3j3q'); - const { structures, models /*, mmcif*/ } = await readCIF('e:/test/quick/1hrv_updated.cif'); + const { structures, models /*, mmcif*/ } = await readCIF('e:/test/quick/1cbs_updated.cif'); //const { structures: s1, /*, mmcif*/ } = await readCIF('e:/test/quick/1tqn_updated.cif'); // testGrouping(structures[0]); @@ -340,7 +354,8 @@ export namespace PropertyAccess { //console.log(mmcif.pdbx_struct_oper_list.matrix.toArray()); // console.log(mmcif.pdbx_struct_oper_list.vector.toArray()); - await testAssembly('1hrv', structures[0]); + //await testAssembly('1hrv', structures[0]); + await testSymmetry('1cbs', structures[0]); // throw ''; // console.log(models[0].symmetry.assemblies);