diff --git a/src/apps/structure-info/index.ts b/src/apps/structure-info/index.ts index da2da68749825c05c27c7a9dca4e5272151ee724..f348f93fb14c1890473c754a97652b8b105370eb 100644 --- a/src/apps/structure-info/index.ts +++ b/src/apps/structure-info/index.ts @@ -5,14 +5,38 @@ */ import * as argparse from 'argparse' +import * as util from 'util' +import * as fs from 'fs' import fetch from 'node-fetch' require('util.promisify').shim(); // import { Table } from 'mol-data/db' import CIF from 'mol-io/reader/cif' -import { Model, Structure, ElementSet, Unit, ElementGroup } from 'mol-model/structure' +import { Model, Structure, Element, ElementSet, Unit, ElementGroup, Queries } from 'mol-model/structure' import { Run, Progress } from 'mol-task' import { OrderedSet } from 'mol-data/int'; +import { Table } from 'mol-data/db'; +import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif'; +import CoarseGrained from 'mol-model/structure/model/properties/coarse-grained'; + +const readFileAsync = util.promisify(fs.readFile); + +async function readFile(path: string) { + if (path.match(/\.bcif$/)) { + const input = await readFileAsync(path) + const data = new Uint8Array(input.byteLength); + for (let i = 0; i < input.byteLength; i++) data[i] = input[i]; + return data; + } else { + return readFileAsync(path, 'utf8'); + } +} + +async function readCif(path: string) { + const data = await readFile(path); + const parsed = await parseCif(data); + return CIF.schema.mmCIF(parsed.result.blocks[0]) +} async function parseCif(data: string|Uint8Array) { const comp = CIF.parse(data); @@ -68,6 +92,7 @@ export function printBonds(structure: Structure) { } export function printSequence(model: Model) { + console.log('Sequence\n============='); const { byEntityKey } = model.sequence; for (const key of Object.keys(byEntityKey)) { const seq = byEntityKey[+key]; @@ -76,27 +101,83 @@ export function printSequence(model: Model) { // console.log(`${seq.entityId} ${seq.num.value(i)} ${seq.compId.value(i)}`); // } } + console.log(); } -async function run(pdb: string) { - const mmcif = await getPdb(pdb) +export function printUnits(structure: Structure) { + console.log('Units\n============='); + const { elements, units } = structure; + const unitIds = ElementSet.unitIndices(elements); + const l = Element.Location(); + + for (let i = 0, _i = unitIds.length; i < _i; i++) { + const unitId = unitIds[i]; + l.unit = units[unitId]; + const set = ElementSet.groupAt(elements, i).elements; + const size = OrderedSet.size(set); + + if (Unit.isAtomic(l.unit)) { + console.log(`Atomic unit ${unitId}: ${size} elements`); + } else if (Unit.isCoarse(l.unit)) { + console.log(`Coarse unit ${unitId} (${l.unit.elementType === CoarseGrained.ElementType.Sphere ? 'spheres' : 'gaussians'}): ${size} elements.`); + + const props = Queries.props.coarse_grained; + const seq = l.unit.model.sequence; + + for (let j = 0, _j = Math.min(size, 10); j < _j; j++) { + l.element = OrderedSet.getAt(set, j); + + const residues: string[] = []; + const start = props.seq_id_begin(l), end = props.seq_id_end(l); + const compId = seq.byEntityKey[props.entityKey(l)].compId.value; + for (let e = start; e <= end; e++) residues.push(compId(e)); + console.log(`${props.asym_id(l)}:${start}-${end} (${residues.join('-')}) ${props.asym_id(l)} [${props.x(l).toFixed(2)}, ${props.y(l).toFixed(2)}, ${props.z(l).toFixed(2)}]`); + } + if (size > 10) console.log(`...`); + } + } +} + + +export function printIHMModels(model: Model) { + if (!model.coarseGrained.isDefined) return false; + console.log('IHM Models\n============='); + console.log(Table.formatToString(model.coarseGrained.modelList)); +} + +async function run(mmcif: mmCIF_Database) { const models = Model.create({ kind: 'mmCIF', data: mmcif }); - //const structure = Structure.ofModel(models[0]) - // console.log(structure) - // printBonds(structure) + const structure = Structure.ofModel(models[0]); printSequence(models[0]); + printIHMModels(models[0]); + printUnits(structure); +} + +async function runDL(pdb: string) { + const mmcif = await getPdb(pdb) + run(mmcif); +} + +async function runFile(filename: string) { + const mmcif = await readCif(filename); + run(mmcif); } const parser = new argparse.ArgumentParser({ addHelp: true, description: 'Print info about a structure, mainly to test and showcase the mol-model module' }); -parser.addArgument([ '--pdb', '-p' ], { +parser.addArgument([ '--download', '-d' ], { help: 'Pdb entry id' }); +parser.addArgument([ '--file', '-f' ], { + help: 'filename' +}); interface Args { - pdb: string + download?: string, + file?: string } const args: Args = parser.parseArgs(); -run(args.pdb) +if (args.download) runDL(args.download) +else if (args.file) runFile(args.file) diff --git a/src/mol-data/db/table.ts b/src/mol-data/db/table.ts index df96dacbe89704440f0b79ea526f1c3e96b5c1f0..6f0b9bcced4e182747e5031433a920be3127bb66 100644 --- a/src/mol-data/db/table.ts +++ b/src/mol-data/db/table.ts @@ -6,6 +6,7 @@ import Column from './column' import { sortArray } from '../util/sort' +import { StringBuilder } from 'mol-util'; /** A collection of columns */ type Table<Schema extends Table.Schema> = { @@ -188,6 +189,38 @@ namespace Table { } return ret; } + + export function formatToString<S extends Schema>(table: Table<S>) { + const sb = StringBuilder.create(); + + const { _columns: cols, _rowCount } = table; + + let headerLength = 1; + StringBuilder.write(sb, '|'); + for (let i = 0; i < cols.length; i++) { + StringBuilder.write(sb, cols[i]); + StringBuilder.write(sb, '|'); + headerLength += cols[i].length + 1; + } + StringBuilder.newline(sb); + StringBuilder.write(sb, new Array(headerLength + 1).join('-')); + StringBuilder.newline(sb); + + for (let r = 0; r < _rowCount; r++) { + StringBuilder.write(sb, '|'); + for (let i = 0; i < cols.length; i++) { + const c = table[cols[i]]; + if (c.valueKind(r) === Column.ValueKind.Present) { + StringBuilder.write(sb, c.value(r)); + StringBuilder.write(sb, '|'); + } else { + StringBuilder.write(sb, '.|'); + } + } + StringBuilder.newline(sb); + } + return StringBuilder.getString(sb); + } } export default Table \ No newline at end of file diff --git a/src/mol-model/structure/model/formats/mmcif.ts b/src/mol-model/structure/model/formats/mmcif.ts index dddb046275d41d5d8a553eb49b92c22e34d696e3..33c09a598b84df5e8fb06d7ce4a4c2fca7c949bb 100644 --- a/src/mol-model/structure/model/formats/mmcif.ts +++ b/src/mol-model/structure/model/formats/mmcif.ts @@ -11,7 +11,6 @@ import Format from '../format' import Model from '../model' import * as Hierarchy from '../properties/hierarchy' import AtomSiteConformation from '../properties/atom-site-conformation' -import CoarseGrained from '../properties/coarse-grained' import Symmetry from '../properties/symmetry' import findHierarchyKeys from '../utils/hierarchy-keys' import { ElementSymbol} from '../types' @@ -20,6 +19,7 @@ import createAssemblies from './mmcif/assembly' import mmCIF_Format = Format.mmCIF import { getSequence } from './mmcif/sequence'; import { Entities } from '../properties/common'; +import { coarseGrainedFromIHM } from './mmcif/ihm'; function findModelBounds({ data }: mmCIF_Format, startIndex: number) { const num = data.atom_site.pdbx_PDB_model_num; @@ -31,6 +31,8 @@ function findModelBounds({ data }: mmCIF_Format, startIndex: number) { } function findHierarchyOffsets({ data }: mmCIF_Format, bounds: Interval) { + if (Interval.size(bounds) === 0) return { residues: [], chains: [] }; + const start = Interval.start(bounds), end = Interval.end(bounds); const residues = [start], chains = [start]; @@ -122,18 +124,23 @@ function createModel(format: mmCIF_Format, bounds: Interval, previous?: Model): hierarchy, sequence: getSequence(format.data, entities, hierarchy), atomSiteConformation: getConformation(format, bounds), - coarseGrained: CoarseGrained.Empty, + coarseGrained: coarseGrainedFromIHM(format.data, entities), symmetry: getSymmetry(format), atomCount: Interval.size(bounds) }; } function buildModels(format: mmCIF_Format): ReadonlyArray<Model> { - const models: Model[] = []; const atomCount = format.data.atom_site._rowCount; + const isIHM = format.data.ihm_model_list._rowCount > 0; - if (atomCount === 0) return models; + if (atomCount === 0) { + return isIHM + ? [createModel(format, Interval.Empty, void 0)] + : []; + } + const models: Model[] = []; let modelStart = 0; while (modelStart < atomCount) { const bounds = findModelBounds(format, modelStart); diff --git a/src/mol-model/structure/model/formats/mmcif/ihm.ts b/src/mol-model/structure/model/formats/mmcif/ihm.ts index 879b4fb842e26b1581ef57647aa48c69ba9ab9f5..d8590657156c97e3f38c1137792dcca2825a06aa 100644 --- a/src/mol-model/structure/model/formats/mmcif/ihm.ts +++ b/src/mol-model/structure/model/formats/mmcif/ihm.ts @@ -4,3 +4,51 @@ * @author David Sehnal <david.sehnal@gmail.com> */ +import { mmCIF_Database as mmCIF, mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif' +import CoarseGrained from '../../properties/coarse-grained' +import { Entities } from '../../properties/common'; +import { Column } from 'mol-data/db'; + +function coarseGrainedFromIHM(data: mmCIF, entities: Entities): CoarseGrained { + if (data.ihm_model_list._rowCount === 0) return CoarseGrained.Empty; + + const { ihm_model_list, ihm_sphere_obj_site, ihm_gaussian_obj_site } = data; + const modelIndex = Column.createIndexer(ihm_model_list.model_id); + + return { + isDefined: true, + modelList: ihm_model_list, + spheres: getSpheres(ihm_sphere_obj_site, entities, modelIndex), + gaussians: getGaussians(ihm_gaussian_obj_site, entities, modelIndex) + }; +} + +function getSpheres(data: mmCIF['ihm_sphere_obj_site'], entities: Entities, modelIndex: (id: number) => number): CoarseGrained.Spheres { + const { Cartn_x, Cartn_y, Cartn_z, object_radius: radius, rmsf } = data; + const x = Cartn_x.toArray({ array: Float32Array }); + const y = Cartn_y.toArray({ array: Float32Array }); + const z = Cartn_z.toArray({ array: Float32Array }); + return { count: x.length, ...getCommonColumns(data, entities, modelIndex), x, y, z, radius, rmsf }; +} + +function getGaussians(data: mmCIF['ihm_gaussian_obj_site'], entities: Entities, modelIndex: (id: number) => number): CoarseGrained.Gaussians { + const { mean_Cartn_x, mean_Cartn_y, mean_Cartn_z, weight, covariance_matrix } = data; + const x = mean_Cartn_x.toArray({ array: Float32Array }); + const y = mean_Cartn_y.toArray({ array: Float32Array }); + const z = mean_Cartn_z.toArray({ array: Float32Array }); + return { count: x.length, ...getCommonColumns(data, entities, modelIndex), x, y, z, weight, covariance_matrix, matrix_space: mmCIF_Schema.ihm_gaussian_obj_site.covariance_matrix.space }; +} + +function getCommonColumns(data: mmCIF['ihm_sphere_obj_site'] | mmCIF['ihm_gaussian_obj_site'], entities: Entities, modelIndex: (id: number) => number) { + const { model_id, entity_id, seq_id_begin, seq_id_end, asym_id } = data; + + return { + entityKey: Column.mapToArray(entity_id, id => entities.getEntityIndex(id), Int32Array), + modelKey: Column.mapToArray(model_id, modelIndex, Int32Array), + asym_id, + seq_id_begin, + seq_id_end + }; +} + +export { coarseGrainedFromIHM } \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/coarse-grained.ts b/src/mol-model/structure/model/properties/coarse-grained.ts index 5787428eee35eb546b682f2320bd54e7873a2d4c..392f06a2ce24f82c7c59492d9597fa2a33f015ef 100644 --- a/src/mol-model/structure/model/properties/coarse-grained.ts +++ b/src/mol-model/structure/model/properties/coarse-grained.ts @@ -18,32 +18,36 @@ interface CoarseGrained { namespace CoarseGrained { export const Empty: CoarseGrained = { isDefined: false } as any; - interface Site { - // index to the Model.hierarchy.entities table - entityKey: number, - // index to the CoarseGrained.modelList table - modelKey: number, + export const enum ElementType { Sphere, Gaussian } + export interface SiteBase { asym_id: string, seq_id_begin: number, - seq_id_end: number, - x: number, - y: number, - z: number + seq_id_end: number } - export interface Sphere extends Site { + export interface Sphere extends SiteBase { radius: number, rmsf: number } - export interface Gaussian extends Site { + export interface Gaussian extends SiteBase { weight: number, - covarianceMatrix: Tensor.Data + covariance_matrix: Tensor.Data } - export type Spheres = { count: number} & { [P in keyof Sphere]: Column<Sphere[P]> } - export type Gaussians = { count: number} & { [P in keyof Gaussian]: Column<Gaussian[P]> } + type Common = { + count: number, + x: ArrayLike<number>, + y: ArrayLike<number>, + z: ArrayLike<number>, + modelKey: ArrayLike<number>, + entityKey: ArrayLike<number> + } + + export type SiteBases = Common & { [P in keyof SiteBase]: Column<SiteBase[P]> } + export type Spheres = Common & { [P in keyof Sphere]: Column<Sphere[P]> } + export type Gaussians = Common & { matrix_space: Tensor.Space } & { [P in keyof Gaussian]: Column<Gaussian[P]> } } export default CoarseGrained; \ 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 56beedae592d85f6436c39ba1593fc9f77a17e35..66c9f0415dd28f02c0f2ad74eac035ec09d305eb 100644 --- a/src/mol-model/structure/query/generators.ts +++ b/src/mol-model/structure/query/generators.ts @@ -7,7 +7,7 @@ import Query from './query' import Selection from './selection' import P from './properties' -import { Structure, ElementSet, Element } from '../structure' +import { Structure, ElementSet, Element, Unit } from '../structure' import { OrderedSet, Segmentation } from 'mol-data/int' export const all: Query.Provider = async (s, ctx) => Selection.Singletons(s, s.elements); @@ -79,6 +79,9 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A for (let i = 0, _i = unitIds.length; i < _i; i++) { const unitId = unitIds[i]; const unit = units[unitId]; + + if (unit.kind !== Unit.Kind.Atomic) continue; + l.unit = unit; const set = ElementSet.groupAt(elements, i).elements; @@ -171,6 +174,9 @@ function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, group for (let i = 0, _i = unitIds.length; i < _i; i++) { const unitId = unitIds[i]; const unit = units[unitId]; + + if (unit.kind !== Unit.Kind.Atomic) continue; + l.unit = unit; const set = ElementSet.groupAt(elements, i).elements; diff --git a/src/mol-model/structure/query/properties.ts b/src/mol-model/structure/query/properties.ts index 811c64a8996d4622de2b2ad40893790584ba99f0..8a766f9b5c6040a4fd7f7ebd8d23820f404bc7b7 100644 --- a/src/mol-model/structure/query/properties.ts +++ b/src/mol-model/structure/query/properties.ts @@ -5,6 +5,7 @@ */ import { Element, Unit } from '../structure' +import CoarseGrained from '../model/properties/coarse-grained'; const constant = { true: Element.property(l => true), @@ -16,6 +17,11 @@ function notAtomic(): never { throw 'Property only available for atomic models.'; } +function notCoarse(kind?: string): never { + if (!!kind) throw `Property only available for coarse models (${kind}).`; + throw `Property only available for coarse models.`; +} + const atom = { key: Element.property(l => l.element), @@ -24,15 +30,15 @@ const atom = { y: Element.property(l => l.unit.y(l.element)), z: Element.property(l => l.unit.z(l.element)), id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.conformation.atomId.value(l.element)), - occupancy: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.conformation.occupancy.value(l.element)), - B_iso_or_equiv: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.conformation.B_iso_or_equiv.value(l.element)), + occupancy: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.conformation.occupancy.value(l.element)), + B_iso_or_equiv: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.conformation.B_iso_or_equiv.value(l.element)), // Hierarchy - type_symbol: Element.property(l => l.unit.hierarchy.atoms.type_symbol.value(l.element)), - label_atom_id: Element.property(l => l.unit.hierarchy.atoms.label_atom_id.value(l.element)), - auth_atom_id: Element.property(l => l.unit.hierarchy.atoms.auth_atom_id.value(l.element)), - label_alt_id: Element.property(l => l.unit.hierarchy.atoms.label_alt_id.value(l.element)), - pdbx_formal_charge: Element.property(l => l.unit.hierarchy.atoms.pdbx_formal_charge.value(l.element)) + type_symbol: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.hierarchy.atoms.type_symbol.value(l.element)), + label_atom_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.hierarchy.atoms.label_atom_id.value(l.element)), + auth_atom_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.hierarchy.atoms.auth_atom_id.value(l.element)), + label_alt_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.hierarchy.atoms.label_alt_id.value(l.element)), + pdbx_formal_charge: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.hierarchy.atoms.pdbx_formal_charge.value(l.element)) } const residue = { @@ -54,6 +60,29 @@ const chain = { label_entity_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.hierarchy.chains.label_entity_id.value(l.unit.chainIndex[l.element])) } +const coarse_grained = { + modelKey: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.siteBases.modelKey[l.element]), + entityKey: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.siteBases.entityKey[l.element]), + + x: atom.x, + y: atom.y, + z: atom.z, + + asym_id: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.siteBases.asym_id.value(l.element)), + seq_id_begin: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.siteBases.seq_id_begin.value(l.element)), + seq_id_end: Element.property(l => !Unit.isCoarse(l.unit) ? notCoarse() : l.unit.siteBases.seq_id_end.value(l.element)), + + sphere_radius: Element.property(l => !Unit.isCoarse(l.unit) || l.unit.elementType !== CoarseGrained.ElementType.Sphere + ? notCoarse('spheres') : l.unit.spheres.radius.value(l.element)), + sphere_rmsf: Element.property(l => !Unit.isCoarse(l.unit) || l.unit.elementType !== CoarseGrained.ElementType.Sphere + ? notCoarse('spheres') : l.unit.spheres.rmsf.value(l.element)), + + gaussian_weight: Element.property(l => !Unit.isCoarse(l.unit) || l.unit.elementType !== CoarseGrained.ElementType.Gaussian + ? notCoarse('gaussians') : l.unit.gaussians.weight.value(l.element)), + gaussian_covariance_matrix: Element.property(l => !Unit.isCoarse(l.unit) || l.unit.elementType !== CoarseGrained.ElementType.Gaussian + ? notCoarse('gaussians') : l.unit.gaussians.covariance_matrix.value(l.element)), +} + function eK(l: Element.Location) { return !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.hierarchy.entityKey.value(l.unit.chainIndex[l.element]); } const entity = { @@ -82,7 +111,8 @@ const Properties = { residue, chain, entity, - unit + unit, + coarse_grained } type Properties = typeof Properties diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts index 710926b6138c69d9e2b85a8b3823fd32cee9e348..6572e03e1ea8988ac436a44c2259fc1223a988d9 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -12,6 +12,7 @@ import Unit from './unit' import ElementSet from './element/set' import ElementGroup from './element/group' import Element from './element' +import CoarseGrained from '../model/properties/coarse-grained'; // A structure is a pair of "units" and an element set. // Each unit contains the data and transformation of its corresponding elements. @@ -39,6 +40,20 @@ namespace Structure { builder.add(unit, unit.fullGroup); } + const cs = model.coarseGrained; + if (cs.isDefined) { + if (cs.spheres.count > 0) { + const group = ElementGroup.createNew(OrderedSet.ofBounds(0, cs.spheres.count)); + const unit = Unit.createCoarse(model, SymmetryOperator.Default, group, CoarseGrained.ElementType.Sphere); + builder.add(unit, unit.fullGroup); + } + if (cs.gaussians.count > 0) { + const group = ElementGroup.createNew(OrderedSet.ofBounds(0, cs.gaussians.count)); + const unit = Unit.createCoarse(model, SymmetryOperator.Default, group, CoarseGrained.ElementType.Gaussian); + builder.add(unit, unit.fullGroup); + } + } + return builder.getStructure(); } diff --git a/src/mol-model/structure/structure/unit.ts b/src/mol-model/structure/structure/unit.ts index 201002b587112b3e1aaef72bf7385dd984c86596..596dd3580cddfa4d32aaee7cfaae507e1d6925dd 100644 --- a/src/mol-model/structure/structure/unit.ts +++ b/src/mol-model/structure/structure/unit.ts @@ -9,6 +9,7 @@ import ElementGroup from './element/group' import { Model } from '../model' import { GridLookup3D } from 'mol-math/geometry' import { computeUnitBonds } from './element/properties/bonds/group-compute'; +import CoarseGrained from '../model/properties/coarse-grained'; // A building block of a structure that corresponds to an atomic or a coarse grained representation // 'conveniently grouped together'. @@ -29,9 +30,7 @@ namespace Unit { // Things like inter-unit bonds or spatial lookups // can be be implemented efficiently as "views" of the // full group. - readonly fullGroup: ElementGroup, - - readonly hierarchy: Model['hierarchy'], + readonly fullGroup: ElementGroup } // A bulding block of a structure that corresponds @@ -47,16 +46,21 @@ namespace Unit { // Reference some commonly accessed things for faster access. readonly residueIndex: ArrayLike<number>, readonly chainIndex: ArrayLike<number>, - readonly conformation: Model['atomSiteConformation'] + readonly conformation: Model['atomSiteConformation'], + readonly hierarchy: Model['hierarchy'] } // Coarse grained representations. - // TODO: can we use the ArrayMapping here? export interface Coarse extends Base { - readonly kind: Unit.Kind.Coarse + readonly kind: Unit.Kind.Coarse, + readonly elementType: CoarseGrained.ElementType, + + readonly siteBases: CoarseGrained.SiteBases, + readonly spheres: CoarseGrained.Spheres, + readonly gaussians: CoarseGrained.Gaussians } - export function createAtomic(model: Model, operator: SymmetryOperator, fullGroup: ElementGroup): Unit { + export function createAtomic(model: Model, operator: SymmetryOperator, fullGroup: ElementGroup): Unit.Atomic { const h = model.hierarchy; const { invariantPosition, position, x, y, z } = SymmetryOperator.createMapping(operator, model.atomSiteConformation); @@ -75,14 +79,29 @@ namespace Unit { }; } - export function createCoarse(model: Model, operator: SymmetryOperator, fullGroup: ElementGroup): Unit { - throw 'not implemented' + export function createCoarse(model: Model, operator: SymmetryOperator, fullGroup: ElementGroup, elementType: CoarseGrained.ElementType): Unit.Coarse { + const siteBases = elementType === CoarseGrained.ElementType.Sphere ? model.coarseGrained.spheres : model.coarseGrained.gaussians; + const { invariantPosition, position, x, y, z } = SymmetryOperator.createMapping(operator, siteBases); + + return { + model, + kind: Kind.Coarse, + elementType, + operator, + fullGroup, + siteBases, + spheres: model.coarseGrained.spheres, + gaussians: model.coarseGrained.gaussians, + invariantPosition, + position, + x, y, z + }; } export function withOperator(unit: Unit, operator: SymmetryOperator): Unit { switch (unit.kind) { case Kind.Atomic: return createAtomic(unit.model, SymmetryOperator.compose(unit.operator, operator), unit.fullGroup); - case Kind.Coarse: return createCoarse(unit.model, SymmetryOperator.compose(unit.operator, operator), unit.fullGroup); + case Kind.Coarse: return createCoarse(unit.model, SymmetryOperator.compose(unit.operator, operator), unit.fullGroup, unit.elementType); } } diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts index 1e0a9b9725c12a5a619167527f061837e70d6b18..03f04a2333d8fcfcc8af2b31f9b2b7cc02036255 100644 --- a/src/perf-tests/structure.ts +++ b/src/perf-tests/structure.ts @@ -148,7 +148,7 @@ export namespace PropertyAccess { let vA = 0, cC = 0, rC = 0; for (let i = 0, _i = unitIds.length; i < _i; i++) { - const unit = units[unitIds[i]]; + const unit = units[unitIds[i]] as Unit.Atomic; l.unit = unit; const set = ElementSet.groupAt(elements, i);