diff --git a/data/mmcif-field-names.csv b/data/mmcif-field-names.csv index 66b42958bbbaca08cccd734bccf042df4fcf1dcc..61649087a630b56eaacafc345338f2b8b37a9584 100644 --- a/data/mmcif-field-names.csv +++ b/data/mmcif-field-names.csv @@ -58,6 +58,11 @@ entity.pdbx_mutation entity.pdbx_fragment entity.pdbx_ec +entity_poly_seq.entity_id +entity_poly_seq.num +entity_poly_seq.mon_id +entity_poly_seq.hetero + entry.id exptl.entry_id diff --git a/src/apps/domain-annotation-server/mapping.ts b/src/apps/domain-annotation-server/mapping.ts index da87d9b4698b6e8db3014c4a8dd52e8d0717095e..0ab03c359bfa9e11acc8d7f0cd82bf0378586ca8 100644 --- a/src/apps/domain-annotation-server/mapping.ts +++ b/src/apps/domain-annotation-server/mapping.ts @@ -112,6 +112,6 @@ function getDomain(name: string, schema: any, allData: any) { return domains.length > 0 ? { name, domains: Table.ofRows({ ...S.Base, ...schema }, domains), - mappings: Table.ofRows(S.mapping, mappings) + mappings: Table.ofRows<S.mapping>(S.mapping, mappings) } : void 0; } \ No newline at end of file diff --git a/src/apps/structure-info/index.ts b/src/apps/structure-info/index.ts index 077bb8a10b98b7288a7ffa102494d3079b9cdebe..3fb2e6b313855163791c86e86ecb9906d66ce3ca 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); @@ -39,7 +63,7 @@ export function atomLabel(model: Model, aI: number) { } -function printBonds(structure: Structure) { +export function printBonds(structure: Structure) { const { units, elements } = structure; const unitIds = ElementSet.unitIndices(elements); @@ -67,24 +91,93 @@ function printBonds(structure: Structure) { } } -async function run(pdb: string) { - const mmcif = await getPdb(pdb) +export function printSequence(model: Model) { + console.log('Sequence\n============='); + const { byEntityKey } = model.sequence; + for (const key of Object.keys(byEntityKey)) { + const seq = byEntityKey[+key]; + console.log(`${seq.entityId} (${seq.num.value(0)}, ${seq.num.value(seq.num.rowCount - 1)}) (${seq.compId.value(0)}, ${seq.compId.value(seq.compId.rowCount - 1)})`); + // for (let i = 0; i < seq.compId.rowCount; i++) { + // console.log(`${seq.entityId} ${seq.num.value(i)} ${seq.compId.value(i)}`); + // } + } + console.log(); +} + +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/column.ts b/src/mol-data/db/column.ts index 03394b28d5329a8b101a0ecba41d0c407a775c8c..b50672afbb7cb0673c8ea8850db4976e18a74d13 100644 --- a/src/mol-data/db/column.ts +++ b/src/mol-data/db/column.ts @@ -132,6 +132,10 @@ namespace Column { return createFirstIndexMapOfColumn(column); } + export function createIndexer<T>(column: Column<T>) { + return createIndexerOfColumn(column); + } + export function mapToArray<T, S>(column: Column<T>, f: (v: T) => S, ctor?: ArrayCtor<S>): ArrayLike<S> { return mapToArrayImpl<T, S>(column, f, ctor || Array); } @@ -169,11 +173,20 @@ function createFirstIndexMapOfColumn<T>(c: Column<T>): Map<T, number> { const map = new Map<T, number>(); for (let i = 0, _i = c.rowCount; i < _i; i++) { const v = c.value(i); - if (!map.has(v)) return map.set(c.value(i), i); + if (!map.has(v)) map.set(c.value(i), i); } return map; } +function createIndexerOfColumn<T>(c: Column<T>): (value: T) => number { + const map = new Map<T, number>(); + for (let i = 0, _i = c.rowCount; i < _i; i++) { + const v = c.value(i); + if (!map.has(v)) map.set(c.value(i), i); + } + return v => map.has(v) ? map.get(v)! : -1; +} + function constColumn<T extends Column.Schema>(v: T['T'], rowCount: number, schema: T, valueKind: Column.ValueKind): Column<T['T']> { const value: Column<T['T']>['value'] = row => v; return { diff --git a/src/mol-data/db/table.ts b/src/mol-data/db/table.ts index cac162233387ca44e924942752b1b54c7b7298fc..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> = { @@ -58,7 +59,7 @@ namespace Table { return ret; } - export function ofRows<S extends Schema, R extends Table<S> = Table<S>>(schema: Schema, rows: ArrayLike<Row<S>>): R { + export function ofRows<S extends Schema, R extends Table<S> = Table<S>>(schema: Schema, rows: ArrayLike<Partial<Row<S>>>): R { const ret = Object.create(null); const rowCount = rows.length; const columns = Object.keys(schema); @@ -83,7 +84,7 @@ namespace Table { ret._columns = columns; ret._schema = schema; for (const k of columns) { - (ret as any)[k] = Column.ofArray({ array: arrays[k], schema: schema[k] }) + (ret as any)[k] = typeof arrays[k] !== 'undefined' ? Column.ofArray({ array: arrays[k], schema: schema[k] }) : Column.Undefined(ret._rowCount, schema[k]); } return ret as R; } @@ -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-geo/representation/structure/point.ts b/src/mol-geo/representation/structure/point.ts index c3e7d77bf1202f8e5947624ebafad8f78b2f5837..b95347fb17e7035214d95963c38d896bf2002262 100644 --- a/src/mol-geo/representation/structure/point.ts +++ b/src/mol-geo/representation/structure/point.ts @@ -26,7 +26,7 @@ export type PointProps = Partial<typeof DefaultPointProps> export function createPointVertices(unit: Unit, elementGroup: ElementGroup) { const elementCount = OrderedSet.size(elementGroup.elements) const vertices = new Float32Array(elementCount * 3) - const { x, y, z } = unit.model.conformation + const { x, y, z } = unit.model.atomSiteConformation for (let i = 0; i < elementCount; i++) { const e = OrderedSet.getAt(elementGroup.elements, i) const i3 = i * 3 diff --git a/src/mol-geo/representation/structure/spacefill.ts b/src/mol-geo/representation/structure/spacefill.ts index 05ac8cb750773bd098ac3f6c9cce59c2a04137af..6f994adee431d2f060fd375b62031bf9d47a67b0 100644 --- a/src/mol-geo/representation/structure/spacefill.ts +++ b/src/mol-geo/representation/structure/spacefill.ts @@ -32,7 +32,7 @@ function createSpacefillMesh(unit: Unit, elementGroup: ElementGroup, detail: num const v = Vec3.zero() const m = Mat4.identity() - const { x, y, z } = unit.model.conformation + const { x, y, z } = unit.model.atomSiteConformation const { type_symbol } = unit.model.hierarchy.atoms const elementCount = OrderedSet.size(elementGroup.elements) for (let i = 0; i < elementCount; i++) { diff --git a/src/mol-io/reader/cif/schema/mmcif.ts b/src/mol-io/reader/cif/schema/mmcif.ts index 8660f258d31a65fb55d6d151f5ce0580bdb240e8..373fa1ae8401ea1043707cebc355e0760cf321dc 100644 --- a/src/mol-io/reader/cif/schema/mmcif.ts +++ b/src/mol-io/reader/cif/schema/mmcif.ts @@ -86,6 +86,12 @@ export const mmCIF_Schema = { pdbx_fragment: str, pdbx_ec: List(',', x => x), }, + entity_poly_seq: { + entity_id: str, + num: int, + mon_id: str, + hetero: Aliased<'no' | 'n' | 'yes' | 'y'>(str) + }, entry: { id: str, }, diff --git a/src/mol-model/structure/export/mmcif.ts b/src/mol-model/structure/export/mmcif.ts index 9635dd78b7f07c8a90f82d0c370a247361fdce92..3958a39faf09c85e7d07a2daca46530a396674df 100644 --- a/src/mol-model/structure/export/mmcif.ts +++ b/src/mol-model/structure/export/mmcif.ts @@ -102,10 +102,10 @@ const atom_site: Encoder.CategoryDefinition<Element.Location> = { function entityProvider({ model }: Context): Encoder.CategoryInstance { return { - data: model.hierarchy.entities, - definition: Encoder.CategoryDefinition.ofTable('entity', model.hierarchy.entities), - keys: () => Iterator.Range(0, model.hierarchy.entities._rowCount - 1), - rowCount: model.hierarchy.entities._rowCount + data: model.entities.data, + definition: Encoder.CategoryDefinition.ofTable('entity', model.entities.data), + keys: () => Iterator.Range(0, model.entities.data._rowCount - 1), + rowCount: model.entities.data._rowCount } } diff --git a/src/mol-model/structure/model/formats/gro.ts b/src/mol-model/structure/model/formats/gro.ts index 09f57950ea5e7a27b04ac840a1d01157d9ba30c2..cb868ff96ee01927a9fb29f36640b62967e549bc 100644 --- a/src/mol-model/structure/model/formats/gro.ts +++ b/src/mol-model/structure/model/formats/gro.ts @@ -11,13 +11,16 @@ import { Atoms } from 'mol-io/reader/gro/schema' import Format from '../format' import Model from '../model' import * as Hierarchy from '../properties/hierarchy' -import Conformation from '../properties/conformation' +import AtomSiteConformation from '../properties/atom-site-conformation' import CoarseGrained from '../properties/coarse-grained' import findHierarchyKeys from '../utils/hierarchy-keys' import { guessElement } from '../utils/guess-element' import { ElementSymbol} from '../types' +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'; type HierarchyOffsets = { residues: ArrayLike<number>, chains: ArrayLike<number> } @@ -70,12 +73,11 @@ function createHierarchyData(atomsData: Atoms, offsets: HierarchyOffsets): Hiera // }); const chains = Table.ofUndefinedColumns(Hierarchy.ChainsSchema, 0); - const entities = Table.ofUndefinedColumns(Hierarchy.EntitySchema, 0); - return { atoms, residues, chains, entities }; + return { atoms, residues, chains }; } -function getConformation(atoms: Atoms): Conformation { +function getConformation(atoms: Atoms): AtomSiteConformation { return { id: UUID.create(), atomId: atoms.atomNumber, @@ -103,7 +105,7 @@ function createModel(format: gro_Format, modelNum: number, previous?: Model): Mo if (previous && isHierarchyDataEqual(previous.hierarchy, hierarchyData)) { return { ...previous, - conformation: getConformation(structure.atoms) + atomSiteConformation: getConformation(structure.atoms) }; } @@ -111,13 +113,27 @@ function createModel(format: gro_Format, modelNum: number, previous?: Model): Mo residueSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, bounds), chainSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, bounds), } - const hierarchyKeys = findHierarchyKeys(hierarchyData, hierarchySegments); + + // TODO: create a better mock entity + const entityTable = Table.ofRows<mmCIF['entity']>(mmCIF.entity, [{ + id: '0', + src_method: 'syn', + type: 'polymer', + pdbx_number_of_molecules: 1 + }]); + + const entities: Entities = { data: entityTable, getEntityIndex: Column.createIndexer(entityTable.id) }; + + const hierarchyKeys = findHierarchyKeys(hierarchyData, entities, hierarchySegments); + const hierarchy = { ...hierarchyData, ...hierarchyKeys, ...hierarchySegments }; return { id: UUID.create(), sourceData: format, modelNum, - hierarchy: { ...hierarchyData, ...hierarchyKeys, ...hierarchySegments }, - conformation: getConformation(structure.atoms), + hierarchy, + entities, + sequence: Sequence.fromHierarchy(hierarchy), + atomSiteConformation: getConformation(structure.atoms), coarseGrained: CoarseGrained.Empty, symmetry: { assemblies: [] }, 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 ff40bfa813f9ac23c8a7ac06463523e120053884..33c09a598b84df5e8fb06d7ce4a4c2fca7c949bb 100644 --- a/src/mol-model/structure/model/formats/mmcif.ts +++ b/src/mol-model/structure/model/formats/mmcif.ts @@ -10,14 +10,16 @@ import { Interval, Segmentation } from 'mol-data/int' import Format from '../format' import Model from '../model' import * as Hierarchy from '../properties/hierarchy' -import Conformation from '../properties/conformation' -import CoarseGrained from '../properties/coarse-grained' +import AtomSiteConformation from '../properties/atom-site-conformation' import Symmetry from '../properties/symmetry' import findHierarchyKeys from '../utils/hierarchy-keys' import { ElementSymbol} from '../types' 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; @@ -29,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]; @@ -62,10 +66,10 @@ function createHierarchyData({ data }: mmCIF_Format, bounds: Interval, offsets: Table.columnToArray(residues, 'label_seq_id', Int32Array); Table.columnToArray(residues, 'auth_seq_id', Int32Array); const chains = Table.view(atom_site, Hierarchy.ChainsSchema, offsets.chains); - return { atoms, residues, chains, entities: data.entity }; + return { atoms, residues, chains }; } -function getConformation({ data }: mmCIF_Format, bounds: Interval): Conformation { +function getConformation({ data }: mmCIF_Format, bounds: Interval): AtomSiteConformation { const start = Interval.start(bounds), end = Interval.end(bounds); const { atom_site } = data; return { @@ -97,7 +101,7 @@ function createModel(format: mmCIF_Format, bounds: Interval, previous?: Model): if (previous && isHierarchyDataEqual(previous.hierarchy, hierarchyData)) { return { ...previous, - conformation: getConformation(format, bounds) + atomSiteConformation: getConformation(format, bounds) }; } @@ -105,25 +109,38 @@ function createModel(format: mmCIF_Format, bounds: Interval, previous?: Model): residueSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, bounds), chainSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, bounds), } - const hierarchyKeys = findHierarchyKeys(hierarchyData, hierarchySegments); + + const entities: Entities = { data: format.data.entity, getEntityIndex: Column.createIndexer(format.data.entity.id) }; + + const hierarchyKeys = findHierarchyKeys(hierarchyData, entities, hierarchySegments); + + const hierarchy = { ...hierarchyData, ...hierarchyKeys, ...hierarchySegments }; + return { id: UUID.create(), sourceData: format, modelNum: format.data.atom_site.pdbx_PDB_model_num.value(Interval.start(bounds)), - hierarchy: { ...hierarchyData, ...hierarchyKeys, ...hierarchySegments }, - conformation: getConformation(format, bounds), - coarseGrained: CoarseGrained.Empty, + entities, + hierarchy, + sequence: getSequence(format.data, entities, hierarchy), + atomSiteConformation: getConformation(format, bounds), + 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 new file mode 100644 index 0000000000000000000000000000000000000000..d8590657156c97e3f38c1137792dcca2825a06aa --- /dev/null +++ b/src/mol-model/structure/model/formats/mmcif/ihm.ts @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @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/formats/mmcif/sequence.ts b/src/mol-model/structure/model/formats/mmcif/sequence.ts new file mode 100644 index 0000000000000000000000000000000000000000..ccba6849b3719825cbe067a8d4eb5a7e45c2816e --- /dev/null +++ b/src/mol-model/structure/model/formats/mmcif/sequence.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { mmCIF_Database as mmCIF } from 'mol-io/reader/cif/schema/mmcif' +import Sequence from '../../properties/sequence' +import { Column } from 'mol-data/db'; +import { Hierarchy } from '../../properties/hierarchy'; +import { Entities } from '../../properties/common'; + +export function getSequence(cif: mmCIF, entities: Entities, hierarchy: Hierarchy): Sequence { + if (!cif.entity_poly_seq._rowCount) return Sequence.fromHierarchy(hierarchy); + + const { entity_id, num, mon_id } = cif.entity_poly_seq; + + const byEntityKey: Sequence['byEntityKey'] = {}; + const count = entity_id.rowCount; + + let i = 0; + while (i < count) { + const start = i; + while (i < count - 1 && entity_id.areValuesEqual(i, i + 1)) i++; + i++; + + const id = entity_id.value(start); + byEntityKey[entities.getEntityIndex(id)] = { entityId: id, compId: Column.window(mon_id, start, i), num: Column.window(num, start, i) } + } + + return { byEntityKey }; +} \ No newline at end of file diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts index e7f0fe0a8b85b9bf3b0b32f869bdf970ccb87f55..e685af863b4467eeb50f4668607cff258db6c3f2 100644 --- a/src/mol-model/structure/model/model.ts +++ b/src/mol-model/structure/model/model.ts @@ -6,10 +6,12 @@ import UUID from 'mol-util/uuid' import Format from './format' +import Sequence from './properties/sequence' import Hierarchy from './properties/hierarchy' -import Conformation from './properties/conformation' +import AtomSiteConformation from './properties/atom-site-conformation' import Symmetry from './properties/symmetry' import CoarseGrained from './properties/coarse-grained' +import { Entities } from './properties/common'; import from_gro from './formats/gro' import from_mmCIF from './formats/mmcif' @@ -26,8 +28,11 @@ interface Model extends Readonly<{ sourceData: Format, + entities: Entities, + sequence: Sequence, + hierarchy: Hierarchy, - conformation: Conformation, + atomSiteConformation: AtomSiteConformation, symmetry: Symmetry, coarseGrained: CoarseGrained, diff --git a/src/mol-model/structure/model/properties/conformation.ts b/src/mol-model/structure/model/properties/atom-site-conformation.ts similarity index 100% rename from src/mol-model/structure/model/properties/conformation.ts rename to src/mol-model/structure/model/properties/atom-site-conformation.ts diff --git a/src/mol-model/structure/model/properties/coarse-grained.ts b/src/mol-model/structure/model/properties/coarse-grained.ts index 725493ff1d571801fa5cd9bd69cb3af4ac605c63..392f06a2ce24f82c7c59492d9597fa2a33f015ef 100644 --- a/src/mol-model/structure/model/properties/coarse-grained.ts +++ b/src/mol-model/structure/model/properties/coarse-grained.ts @@ -4,12 +4,50 @@ * @author David Sehnal <david.sehnal@gmail.com> */ +import { mmCIF_Database as mmCIF } from 'mol-io/reader/cif/schema/mmcif' +import { Tensor } from 'mol-math/linear-algebra'; +import { Column } from 'mol-data/db'; + interface CoarseGrained { - // TODO + isDefined: boolean, + modelList: mmCIF['ihm_model_list'], + spheres: CoarseGrained.Spheres, + gaussians: CoarseGrained.Gaussians } namespace CoarseGrained { - export const Empty: CoarseGrained = { }; + export const Empty: CoarseGrained = { isDefined: false } as any; + + export const enum ElementType { Sphere, Gaussian } + + export interface SiteBase { + asym_id: string, + seq_id_begin: number, + seq_id_end: number + } + + export interface Sphere extends SiteBase { + radius: number, + rmsf: number + } + + export interface Gaussian extends SiteBase { + weight: number, + covariance_matrix: Tensor.Data + } + + 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/model/properties/common.ts b/src/mol-model/structure/model/properties/common.ts new file mode 100644 index 0000000000000000000000000000000000000000..aa5d93aaec136b39e51f2b22b1b5de3d6bde2c65 --- /dev/null +++ b/src/mol-model/structure/model/properties/common.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { mmCIF_Database as mmCIF } from 'mol-io/reader/cif/schema/mmcif' + +interface Entities { + data: mmCIF['entity'], + getEntityIndex(id: string): number +} + +export { Entities } \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/hierarchy.ts b/src/mol-model/structure/model/properties/hierarchy.ts index 773e091d845fb23e1dda9c2c887df824d2b55c70..d25903d95d0e4bd865b1969152902deea071c966 100644 --- a/src/mol-model/structure/model/properties/hierarchy.ts +++ b/src/mol-model/structure/model/properties/hierarchy.ts @@ -40,15 +40,10 @@ export const ChainsSchema = { export type ChainsSchema = typeof ChainsSchema export interface Chains extends Table<ChainsSchema> { } -export const EntitySchema = mmCIF['entity'] -export type EntitySchema = typeof EntitySchema -export interface Entities extends Table<EntitySchema> { } - export interface Data { atoms: Atoms, residues: Residues, - chains: Chains, - entities: Entities + chains: Chains } export interface Segments { @@ -70,7 +65,6 @@ export interface Keys { // also index to the Entities table. entityKey: Column<number>, - findEntityKey(id: string): number, findChainKey(entityId: string, label_asym_id: string): number, findResidueKey(entityId: string, label_asym_id: string, label_comp_id: string, auth_seq_id: number, pdbx_PDB_ins_code: string): number } diff --git a/src/mol-model/structure/model/properties/sequence.ts b/src/mol-model/structure/model/properties/sequence.ts new file mode 100644 index 0000000000000000000000000000000000000000..d7aa43951ea85c75921b0e4c9879870b73a56faa --- /dev/null +++ b/src/mol-model/structure/model/properties/sequence.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Column } from 'mol-data/db' +import { Hierarchy } from './hierarchy'; + +interface Sequence { + readonly byEntityKey: { [key: number]: Sequence.Entity } +} + +namespace Sequence { + export interface Entity { + readonly entityId: string, + readonly num: Column<number> + // _entity_poly_seq.mon_id + readonly compId: Column<string> + } + + export function fromHierarchy(hierarchy: Hierarchy): Sequence { + // const { label_comp_id } = hierarchy.residues; + + throw 'not implemented'; + } +} + +export default Sequence \ 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 c09484245f80c577b7a948624a56cc469be9adc5..d70a26a94abdaeba5be60d137bccb7360d2b8820 100644 --- a/src/mol-model/structure/model/utils/hierarchy-keys.ts +++ b/src/mol-model/structure/model/utils/hierarchy-keys.ts @@ -7,6 +7,7 @@ import { Column } from 'mol-data/db' import { Data, Segments, Keys } from '../properties/hierarchy' import { Interval, Segmentation } from 'mol-data/int' +import { Entities } from '../properties/common'; function getResidueId(comp_id: string, seq_id: number, ins_code: string) { return `${comp_id} ${seq_id} ${ins_code}`; @@ -26,24 +27,26 @@ function getElementSubstructureKeyMap(map: Map<number, Map<string, number>>, key return ret; } -function createLookUp(entity: Map<string, number>, chain: Map<number, Map<string, number>>, residue: Map<number, Map<string, number>>) { - const findEntityKey: Keys['findEntityKey'] = (id) => entity.has(id) ? entity.get(id)! : -1; +function createLookUp(entities: Entities, chain: Map<number, Map<string, number>>, residue: Map<number, Map<string, number>>) { + const getEntKey = entities.getEntityIndex; const findChainKey: Keys['findChainKey'] = (e, c) => { - if (!entity.has(e)) return -1; - const cm = chain.get(entity.get(e)!)!; + let eKey = getEntKey(e); + if (eKey < 0) return -1; + const cm = chain.get(eKey)!; if (!cm.has(c)) return -1; return cm.get(c)!; } const findResidueKey: Keys['findResidueKey'] = (e, c, name, seq, ins) => { - if (!entity.has(e)) return -1; - const cm = chain.get(entity.get(e)!)!; + let eKey = getEntKey(e); + if (eKey < 0) return -1; + const cm = chain.get(eKey)!; if (!cm.has(c)) return -1; const rm = residue.get(cm.get(c)!)! const id = getResidueId(name, seq, ins); if (!rm.has(id)) return -1; return rm.get(id)!; } - return { findEntityKey, findChainKey, findResidueKey }; + return { findChainKey, findResidueKey }; } function checkMonotonous(xs: ArrayLike<number>) { @@ -55,10 +58,13 @@ function checkMonotonous(xs: ArrayLike<number>) { return true; } -function create(data: Data, segments: Segments): Keys { - const { chains, residues, entities } = data; +function missingEntity(k: string) { + throw new Error(`Missing entity entry for entity id '${k}'.`); +} + +function create(data: Data, entities: Entities, segments: Segments): Keys { + const { chains, residues } = data; - const entityMap = Column.createFirstIndexMap(entities.id); const chainMaps = new Map<number, Map<string, number>>(), chainCounter = { index: 0 }; const residueMaps = new Map<number, Map<string, number>>(), residueCounter = { index: 0 }; @@ -78,7 +84,8 @@ function create(data: Data, segments: Segments): Keys { const chainSegment = chainsIt.move(); const cI = chainSegment.index; - const eKey = entityMap.get(label_entity_id.value(cI)) || 0; + let eKey = entities.getEntityIndex(label_entity_id.value(cI)); + if (eKey < 0) missingEntity(label_entity_id.value(cI)); const chainMap = getElementSubstructureKeyMap(chainMaps, eKey); const cKey = getElementKey(chainMap, label_asym_id.value(cI), chainCounter); @@ -99,14 +106,13 @@ function create(data: Data, segments: Segments): Keys { } } - const { findEntityKey, findChainKey, findResidueKey } = createLookUp(entityMap, chainMaps, residueMaps); + const { findChainKey, findResidueKey } = createLookUp(entities, chainMaps, residueMaps); return { isMonotonous: isMonotonous && checkMonotonous(entityKey) && checkMonotonous(chainKey) && checkMonotonous(residueKey), residueKey: Column.ofIntArray(residueKey), chainKey: Column.ofIntArray(chainKey), entityKey: Column.ofIntArray(entityKey), - findEntityKey, findChainKey, findResidueKey }; 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 e119a345e490e42c5651792fdad7955b0fe9c947..8a766f9b5c6040a4fd7f7ebd8d23820f404bc7b7 100644 --- a/src/mol-model/structure/query/properties.ts +++ b/src/mol-model/structure/query/properties.ts @@ -2,11 +2,10 @@ * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> - * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import { Element, Unit } from '../structure' -import { VdwRadius, AtomWeight, AtomNumber } from '../model/properties/atomic'; +import CoarseGrained from '../model/properties/coarse-grained'; const constant = { true: Element.property(l => true), @@ -18,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), @@ -26,20 +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)), - - // Derived - vdw: Element.property(l => VdwRadius(l.unit.hierarchy.atoms.type_symbol.value(l.element))), - mass: Element.property(l => AtomWeight(l.unit.hierarchy.atoms.type_symbol.value(l.element))), - number: Element.property(l => AtomNumber(l.unit.hierarchy.atoms.type_symbol.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 = { @@ -61,21 +60,44 @@ 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 = { key: eK, - id: Element.property(l => l.unit.hierarchy.entities.id.value(eK(l))), - type: Element.property(l => l.unit.hierarchy.entities.type.value(eK(l))), - src_method: Element.property(l => l.unit.hierarchy.entities.src_method.value(eK(l))), - pdbx_description: Element.property(l => l.unit.hierarchy.entities.pdbx_description.value(eK(l))), - formula_weight: Element.property(l => l.unit.hierarchy.entities.formula_weight.value(eK(l))), - pdbx_number_of_molecules: Element.property(l => l.unit.hierarchy.entities.pdbx_number_of_molecules.value(eK(l))), - details: Element.property(l => l.unit.hierarchy.entities.details.value(eK(l))), - pdbx_mutation: Element.property(l => l.unit.hierarchy.entities.pdbx_mutation.value(eK(l))), - pdbx_fragment: Element.property(l => l.unit.hierarchy.entities.pdbx_fragment.value(eK(l))), - pdbx_ec: Element.property(l => l.unit.hierarchy.entities.pdbx_ec.value(eK(l))) + id: Element.property(l => l.unit.model.entities.data.id.value(eK(l))), + type: Element.property(l => l.unit.model.entities.data.type.value(eK(l))), + src_method: Element.property(l => l.unit.model.entities.data.src_method.value(eK(l))), + pdbx_description: Element.property(l => l.unit.model.entities.data.pdbx_description.value(eK(l))), + formula_weight: Element.property(l => l.unit.model.entities.data.formula_weight.value(eK(l))), + pdbx_number_of_molecules: Element.property(l => l.unit.model.entities.data.pdbx_number_of_molecules.value(eK(l))), + details: Element.property(l => l.unit.model.entities.data.details.value(eK(l))), + pdbx_mutation: Element.property(l => l.unit.model.entities.data.pdbx_mutation.value(eK(l))), + pdbx_fragment: Element.property(l => l.unit.model.entities.data.pdbx_fragment.value(eK(l))), + pdbx_ec: Element.property(l => l.unit.model.entities.data.pdbx_ec.value(eK(l))) } const unit = { @@ -89,7 +111,8 @@ const Properties = { residue, chain, entity, - unit + unit, + coarse_grained } type Properties = typeof Properties 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 index fb078c889fb924c543659b995eb6b6eea3e28eba..a75540509204cff433c98804911a2cb9bfe7961a 100644 --- a/src/mol-model/structure/structure/element/properties/bonds/group-compute.ts +++ b/src/mol-model/structure/structure/element/properties/bonds/group-compute.ts @@ -110,7 +110,7 @@ function computePerAtomBonds(atomA: number[], atomB: number[], _order: number[], function _computeBonds(unit: Unit.Atomic, atoms: ElementGroup, params: BondComputationParameters): GroupBonds { const MAX_RADIUS = 3; - const { x, y, z } = unit.model.conformation; + const { x, y, z } = unit.model.atomSiteConformation; const atomCount = ElementGroup.size(atoms); const { residueIndex } = unit; const { type_symbol, label_atom_id, label_alt_id } = unit.model.hierarchy.atoms; 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 2109cc10b299b6932a99f05acf410f86abdb45f9..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,18 +46,23 @@ namespace Unit { // Reference some commonly accessed things for faster access. readonly residueIndex: ArrayLike<number>, readonly chainIndex: ArrayLike<number>, - readonly conformation: Model['conformation'] + 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.conformation); + const { invariantPosition, position, x, y, z } = SymmetryOperator.createMapping(operator, model.atomSiteConformation); return { model, @@ -68,28 +72,43 @@ namespace Unit { residueIndex: h.residueSegments.segmentMap, chainIndex: h.chainSegments.segmentMap, hierarchy: model.hierarchy, - conformation: model.conformation, + conformation: model.atomSiteConformation, invariantPosition, position, x, y, z }; } - 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); } } export function getLookup3d(unit: Unit, group: ElementGroup) { if (group.__lookup3d__) return group.__lookup3d__; if (Unit.isAtomic(unit)) { - const { x, y, z } = unit.model.conformation; + const { x, y, z } = unit.model.atomSiteConformation; group.__lookup3d__ = GridLookup3D({ x, y, z, indices: group.elements }); return group.__lookup3d__; } diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts index 57a23cec6d93ba453392cf2c4bdb2fb1feb29f3c..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); @@ -356,11 +356,11 @@ export namespace PropertyAccess { // return; console.log('bs', baseline(models[0])); - console.log('sp', sumProperty(structures[0], l => l.unit.model.conformation.atomId.value(l.element))); - console.log(sumPropertySegmented(structures[0], l => l.unit.model.conformation.atomId.value(l.element))); + console.log('sp', sumProperty(structures[0], l => l.unit.model.atomSiteConformation.atomId.value(l.element))); + console.log(sumPropertySegmented(structures[0], l => l.unit.model.atomSiteConformation.atomId.value(l.element))); //console.log(sumPropertySegmentedMutable(structures[0], l => l.unit.model.conformation.atomId.value(l.element)); - console.log(sumPropertyAtomSetIt(structures[0], l => l.unit.model.conformation.atomId.value(l.element))); + console.log(sumPropertyAtomSetIt(structures[0], l => l.unit.model.atomSiteConformation.atomId.value(l.element))); //console.log(sumProperty(structures[0], Property.cachedAtomColumn(m => m.conformation.atomId))); //console.log(sumDirect(structures[0])); //console.log('r', sumPropertyResidue(structures[0], l => l.unit.hierarchy.residues.auth_seq_id.value(l.unit.residueIndex[l.atom])));