Skip to content
Snippets Groups Projects
Commit e3a350de authored by David Sehnal's avatar David Sehnal
Browse files

Basic IHM support

parent 3babfb55
No related branches found
No related tags found
No related merge requests found
...@@ -5,14 +5,38 @@ ...@@ -5,14 +5,38 @@
*/ */
import * as argparse from 'argparse' import * as argparse from 'argparse'
import * as util from 'util'
import * as fs from 'fs'
import fetch from 'node-fetch' import fetch from 'node-fetch'
require('util.promisify').shim(); require('util.promisify').shim();
// import { Table } from 'mol-data/db' // import { Table } from 'mol-data/db'
import CIF from 'mol-io/reader/cif' 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 { Run, Progress } from 'mol-task'
import { OrderedSet } from 'mol-data/int'; 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) { async function parseCif(data: string|Uint8Array) {
const comp = CIF.parse(data); const comp = CIF.parse(data);
...@@ -68,6 +92,7 @@ export function printBonds(structure: Structure) { ...@@ -68,6 +92,7 @@ export function printBonds(structure: Structure) {
} }
export function printSequence(model: Model) { export function printSequence(model: Model) {
console.log('Sequence\n=============');
const { byEntityKey } = model.sequence; const { byEntityKey } = model.sequence;
for (const key of Object.keys(byEntityKey)) { for (const key of Object.keys(byEntityKey)) {
const seq = byEntityKey[+key]; const seq = byEntityKey[+key];
...@@ -76,27 +101,83 @@ export function printSequence(model: Model) { ...@@ -76,27 +101,83 @@ export function printSequence(model: Model) {
// console.log(`${seq.entityId} ${seq.num.value(i)} ${seq.compId.value(i)}`); // console.log(`${seq.entityId} ${seq.num.value(i)} ${seq.compId.value(i)}`);
// } // }
} }
console.log();
} }
async function run(pdb: string) { export function printUnits(structure: Structure) {
const mmcif = await getPdb(pdb) 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 models = Model.create({ kind: 'mmCIF', data: mmcif });
//const structure = Structure.ofModel(models[0]) const structure = Structure.ofModel(models[0]);
// console.log(structure)
// printBonds(structure)
printSequence(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({ const parser = new argparse.ArgumentParser({
addHelp: true, addHelp: true,
description: 'Print info about a structure, mainly to test and showcase the mol-model module' 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' help: 'Pdb entry id'
}); });
parser.addArgument([ '--file', '-f' ], {
help: 'filename'
});
interface Args { interface Args {
pdb: string download?: string,
file?: string
} }
const args: Args = parser.parseArgs(); const args: Args = parser.parseArgs();
run(args.pdb) if (args.download) runDL(args.download)
else if (args.file) runFile(args.file)
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
import Column from './column' import Column from './column'
import { sortArray } from '../util/sort' import { sortArray } from '../util/sort'
import { StringBuilder } from 'mol-util';
/** A collection of columns */ /** A collection of columns */
type Table<Schema extends Table.Schema> = { type Table<Schema extends Table.Schema> = {
...@@ -188,6 +189,38 @@ namespace Table { ...@@ -188,6 +189,38 @@ namespace Table {
} }
return ret; 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 export default Table
\ No newline at end of file
...@@ -11,7 +11,6 @@ import Format from '../format' ...@@ -11,7 +11,6 @@ import Format from '../format'
import Model from '../model' import Model from '../model'
import * as Hierarchy from '../properties/hierarchy' import * as Hierarchy from '../properties/hierarchy'
import AtomSiteConformation from '../properties/atom-site-conformation' import AtomSiteConformation from '../properties/atom-site-conformation'
import CoarseGrained from '../properties/coarse-grained'
import Symmetry from '../properties/symmetry' import Symmetry from '../properties/symmetry'
import findHierarchyKeys from '../utils/hierarchy-keys' import findHierarchyKeys from '../utils/hierarchy-keys'
import { ElementSymbol} from '../types' import { ElementSymbol} from '../types'
...@@ -20,6 +19,7 @@ import createAssemblies from './mmcif/assembly' ...@@ -20,6 +19,7 @@ import createAssemblies from './mmcif/assembly'
import mmCIF_Format = Format.mmCIF import mmCIF_Format = Format.mmCIF
import { getSequence } from './mmcif/sequence'; import { getSequence } from './mmcif/sequence';
import { Entities } from '../properties/common'; import { Entities } from '../properties/common';
import { coarseGrainedFromIHM } from './mmcif/ihm';
function findModelBounds({ data }: mmCIF_Format, startIndex: number) { function findModelBounds({ data }: mmCIF_Format, startIndex: number) {
const num = data.atom_site.pdbx_PDB_model_num; const num = data.atom_site.pdbx_PDB_model_num;
...@@ -31,6 +31,8 @@ function findModelBounds({ data }: mmCIF_Format, startIndex: number) { ...@@ -31,6 +31,8 @@ function findModelBounds({ data }: mmCIF_Format, startIndex: number) {
} }
function findHierarchyOffsets({ data }: mmCIF_Format, bounds: Interval) { 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 start = Interval.start(bounds), end = Interval.end(bounds);
const residues = [start], chains = [start]; const residues = [start], chains = [start];
...@@ -122,18 +124,23 @@ function createModel(format: mmCIF_Format, bounds: Interval, previous?: Model): ...@@ -122,18 +124,23 @@ function createModel(format: mmCIF_Format, bounds: Interval, previous?: Model):
hierarchy, hierarchy,
sequence: getSequence(format.data, entities, hierarchy), sequence: getSequence(format.data, entities, hierarchy),
atomSiteConformation: getConformation(format, bounds), atomSiteConformation: getConformation(format, bounds),
coarseGrained: CoarseGrained.Empty, coarseGrained: coarseGrainedFromIHM(format.data, entities),
symmetry: getSymmetry(format), symmetry: getSymmetry(format),
atomCount: Interval.size(bounds) atomCount: Interval.size(bounds)
}; };
} }
function buildModels(format: mmCIF_Format): ReadonlyArray<Model> { function buildModels(format: mmCIF_Format): ReadonlyArray<Model> {
const models: Model[] = [];
const atomCount = format.data.atom_site._rowCount; 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; let modelStart = 0;
while (modelStart < atomCount) { while (modelStart < atomCount) {
const bounds = findModelBounds(format, modelStart); const bounds = findModelBounds(format, modelStart);
......
...@@ -4,3 +4,51 @@ ...@@ -4,3 +4,51 @@
* @author David Sehnal <david.sehnal@gmail.com> * @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
...@@ -18,32 +18,36 @@ interface CoarseGrained { ...@@ -18,32 +18,36 @@ interface CoarseGrained {
namespace CoarseGrained { namespace CoarseGrained {
export const Empty: CoarseGrained = { isDefined: false } as any; export const Empty: CoarseGrained = { isDefined: false } as any;
interface Site { export const enum ElementType { Sphere, Gaussian }
// index to the Model.hierarchy.entities table
entityKey: number,
// index to the CoarseGrained.modelList table
modelKey: number,
export interface SiteBase {
asym_id: string, asym_id: string,
seq_id_begin: number, seq_id_begin: number,
seq_id_end: number, seq_id_end: number
x: number,
y: number,
z: number
} }
export interface Sphere extends Site { export interface Sphere extends SiteBase {
radius: number, radius: number,
rmsf: number rmsf: number
} }
export interface Gaussian extends Site { export interface Gaussian extends SiteBase {
weight: number, weight: number,
covarianceMatrix: Tensor.Data covariance_matrix: Tensor.Data
} }
export type Spheres = { count: number} & { [P in keyof Sphere]: Column<Sphere[P]> } type Common = {
export type Gaussians = { count: number} & { [P in keyof Gaussian]: Column<Gaussian[P]> } 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; export default CoarseGrained;
\ No newline at end of file
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
import Query from './query' import Query from './query'
import Selection from './selection' import Selection from './selection'
import P from './properties' import P from './properties'
import { Structure, ElementSet, Element } from '../structure' import { Structure, ElementSet, Element, Unit } from '../structure'
import { OrderedSet, Segmentation } from 'mol-data/int' import { OrderedSet, Segmentation } from 'mol-data/int'
export const all: Query.Provider = async (s, ctx) => Selection.Singletons(s, s.elements); export const all: Query.Provider = async (s, ctx) => Selection.Singletons(s, s.elements);
...@@ -79,6 +79,9 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A ...@@ -79,6 +79,9 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A
for (let i = 0, _i = unitIds.length; i < _i; i++) { for (let i = 0, _i = unitIds.length; i < _i; i++) {
const unitId = unitIds[i]; const unitId = unitIds[i];
const unit = units[unitId]; const unit = units[unitId];
if (unit.kind !== Unit.Kind.Atomic) continue;
l.unit = unit; l.unit = unit;
const set = ElementSet.groupAt(elements, i).elements; const set = ElementSet.groupAt(elements, i).elements;
...@@ -171,6 +174,9 @@ function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, group ...@@ -171,6 +174,9 @@ function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, group
for (let i = 0, _i = unitIds.length; i < _i; i++) { for (let i = 0, _i = unitIds.length; i < _i; i++) {
const unitId = unitIds[i]; const unitId = unitIds[i];
const unit = units[unitId]; const unit = units[unitId];
if (unit.kind !== Unit.Kind.Atomic) continue;
l.unit = unit; l.unit = unit;
const set = ElementSet.groupAt(elements, i).elements; const set = ElementSet.groupAt(elements, i).elements;
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
*/ */
import { Element, Unit } from '../structure' import { Element, Unit } from '../structure'
import CoarseGrained from '../model/properties/coarse-grained';
const constant = { const constant = {
true: Element.property(l => true), true: Element.property(l => true),
...@@ -16,6 +17,11 @@ function notAtomic(): never { ...@@ -16,6 +17,11 @@ function notAtomic(): never {
throw 'Property only available for atomic models.'; 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 = { const atom = {
key: Element.property(l => l.element), key: Element.property(l => l.element),
...@@ -24,15 +30,15 @@ const atom = { ...@@ -24,15 +30,15 @@ const atom = {
y: Element.property(l => l.unit.y(l.element)), y: Element.property(l => l.unit.y(l.element)),
z: Element.property(l => l.unit.z(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)), 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)), 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)), B_iso_or_equiv: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.conformation.B_iso_or_equiv.value(l.element)),
// Hierarchy // Hierarchy
type_symbol: Element.property(l => 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 => l.unit.hierarchy.atoms.label_atom_id.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 => l.unit.hierarchy.atoms.auth_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 => l.unit.hierarchy.atoms.label_alt_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 => l.unit.hierarchy.atoms.pdbx_formal_charge.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 = { const residue = {
...@@ -54,6 +60,29 @@ const chain = { ...@@ -54,6 +60,29 @@ const chain = {
label_entity_id: Element.property(l => !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.hierarchy.chains.label_entity_id.value(l.unit.chainIndex[l.element])) 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]); } function eK(l: Element.Location) { return !Unit.isAtomic(l.unit) ? notAtomic() : l.unit.hierarchy.entityKey.value(l.unit.chainIndex[l.element]); }
const entity = { const entity = {
...@@ -82,7 +111,8 @@ const Properties = { ...@@ -82,7 +111,8 @@ const Properties = {
residue, residue,
chain, chain,
entity, entity,
unit unit,
coarse_grained
} }
type Properties = typeof Properties type Properties = typeof Properties
......
...@@ -12,6 +12,7 @@ import Unit from './unit' ...@@ -12,6 +12,7 @@ import Unit from './unit'
import ElementSet from './element/set' import ElementSet from './element/set'
import ElementGroup from './element/group' import ElementGroup from './element/group'
import Element from './element' import Element from './element'
import CoarseGrained from '../model/properties/coarse-grained';
// A structure is a pair of "units" and an element set. // A structure is a pair of "units" and an element set.
// Each unit contains the data and transformation of its corresponding elements. // Each unit contains the data and transformation of its corresponding elements.
...@@ -39,6 +40,20 @@ namespace Structure { ...@@ -39,6 +40,20 @@ namespace Structure {
builder.add(unit, unit.fullGroup); 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(); return builder.getStructure();
} }
......
...@@ -9,6 +9,7 @@ import ElementGroup from './element/group' ...@@ -9,6 +9,7 @@ import ElementGroup from './element/group'
import { Model } from '../model' import { Model } from '../model'
import { GridLookup3D } from 'mol-math/geometry' import { GridLookup3D } from 'mol-math/geometry'
import { computeUnitBonds } from './element/properties/bonds/group-compute'; 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 // A building block of a structure that corresponds to an atomic or a coarse grained representation
// 'conveniently grouped together'. // 'conveniently grouped together'.
...@@ -29,9 +30,7 @@ namespace Unit { ...@@ -29,9 +30,7 @@ namespace Unit {
// Things like inter-unit bonds or spatial lookups // Things like inter-unit bonds or spatial lookups
// can be be implemented efficiently as "views" of the // can be be implemented efficiently as "views" of the
// full group. // full group.
readonly fullGroup: ElementGroup, readonly fullGroup: ElementGroup
readonly hierarchy: Model['hierarchy'],
} }
// A bulding block of a structure that corresponds // A bulding block of a structure that corresponds
...@@ -47,16 +46,21 @@ namespace Unit { ...@@ -47,16 +46,21 @@ namespace Unit {
// Reference some commonly accessed things for faster access. // Reference some commonly accessed things for faster access.
readonly residueIndex: ArrayLike<number>, readonly residueIndex: ArrayLike<number>,
readonly chainIndex: ArrayLike<number>, readonly chainIndex: ArrayLike<number>,
readonly conformation: Model['atomSiteConformation'] readonly conformation: Model['atomSiteConformation'],
readonly hierarchy: Model['hierarchy']
} }
// Coarse grained representations. // Coarse grained representations.
// TODO: can we use the ArrayMapping here?
export interface Coarse extends Base { 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 h = model.hierarchy;
const { invariantPosition, position, x, y, z } = SymmetryOperator.createMapping(operator, model.atomSiteConformation); const { invariantPosition, position, x, y, z } = SymmetryOperator.createMapping(operator, model.atomSiteConformation);
...@@ -75,14 +79,29 @@ namespace Unit { ...@@ -75,14 +79,29 @@ namespace Unit {
}; };
} }
export function createCoarse(model: Model, operator: SymmetryOperator, fullGroup: ElementGroup): Unit { export function createCoarse(model: Model, operator: SymmetryOperator, fullGroup: ElementGroup, elementType: CoarseGrained.ElementType): Unit.Coarse {
throw 'not implemented' 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 { export function withOperator(unit: Unit, operator: SymmetryOperator): Unit {
switch (unit.kind) { switch (unit.kind) {
case Kind.Atomic: return createAtomic(unit.model, SymmetryOperator.compose(unit.operator, operator), unit.fullGroup); 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);
} }
} }
......
...@@ -148,7 +148,7 @@ export namespace PropertyAccess { ...@@ -148,7 +148,7 @@ export namespace PropertyAccess {
let vA = 0, cC = 0, rC = 0; let vA = 0, cC = 0, rC = 0;
for (let i = 0, _i = unitIds.length; i < _i; i++) { 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; l.unit = unit;
const set = ElementSet.groupAt(elements, i); const set = ElementSet.groupAt(elements, i);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment