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])));