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

wip, model/structure

parent 3b8e1581
No related branches found
No related tags found
No related merge requests found
......@@ -18,7 +18,7 @@ describe('buckets', () => {
it('full range', () => {
const xs = [1, 1, 2, 2, 3, 1];
const range = createRangeArray(0, xs.length - 1);
const bs = makeBuckets(range, i => xs[i], false);
const bs = makeBuckets(range, i => xs[i]);
expect(reorder(range, xs)).toEqual([1, 1, 1, 2, 2, 3]);
expect(Array.from(bs)).toEqual([0, 3, 5, 6]);
......@@ -27,7 +27,7 @@ describe('buckets', () => {
it('sort', () => {
const xs = [3, 1, 2, 1, 2, 3];
const range = createRangeArray(0, xs.length - 1);
makeBuckets(range, i => xs[i], true);
makeBuckets(range, i => xs[i], { sort: true });
expect(reorder(range, xs)).toEqual([1, 1, 2, 2, 3, 3]);
});
......@@ -35,7 +35,7 @@ describe('buckets', () => {
it('subrange', () => {
const xs = [2, 1, 2, 1, 2, 3, 1];
const range = createRangeArray(0, xs.length - 1);
const bs = makeBuckets(range, i => xs[i], false, 1, 5);
const bs = makeBuckets(range, i => xs[i], { sort: false, start: 1, end: 5 });
expect(reorder(range, xs)).toEqual([2, 1, 1, 2, 2, 3, 1]);
expect(Array.from(bs)).toEqual([1, 3, 5]);
......
......@@ -4,13 +4,19 @@
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { sort, arraySwap } from './sort';
type Bucket = {
key: any,
count: number,
offset: number
}
function _makeBuckets(indices: Helpers.ArrayLike<number>, getKey: (i: number) => any, sort: boolean, start: number, end: number) {
function sortAsc(bs: Bucket[], i: number, j: number) { return bs[i].key < bs[j].key ? -1 : 1; }
function _makeBuckets(indices: Helpers.ArrayLike<number>,
getKey: (i: number) => any, sortBuckets: boolean, start: number, end: number) {
const buckets = new Map<any, Bucket>();
const bucketList: Bucket[] = [];
......@@ -33,7 +39,7 @@ function _makeBuckets(indices: Helpers.ArrayLike<number>, getKey: (i: number) =>
bucketOffsets[bucketList.length] = end;
let sorted = true;
if (sort) {
if (sortBuckets) {
for (let i = 1, _i = bucketList.length; i < _i; i++) {
if (bucketList[i - 1].key > bucketList[i].key) {
sorted = false;
......@@ -47,8 +53,8 @@ function _makeBuckets(indices: Helpers.ArrayLike<number>, getKey: (i: number) =>
return bucketOffsets;
}
if (sort && !sorted) {
bucketList.sort((x, y) => x.key <= y.key ? -1 : 1);
if (sortBuckets && !sorted) {
sort(bucketList, start, end, sortAsc, arraySwap);
}
let offset = 0;
......@@ -75,15 +81,24 @@ function _makeBuckets(indices: Helpers.ArrayLike<number>, getKey: (i: number) =>
return bucketOffsets;
}
export interface MakeBucketsOptions<K> {
// If specified, will be sorted
sort?: boolean,
// inclusive start indidex
start?: number,
// exclusive end index
end?: number
}
/**
* Reorders indices so that the same keys are next to each other, [start, end)
* Returns the offsets of buckets. So that [offsets[i], offsets[i + 1]) determines the range.
*/
export function makeBuckets<T>(indices: Helpers.ArrayLike<number>, getKey: (i: number) => string | number, sort: boolean, start?: number, end?: number): ArrayLike<number> {
const s = start || 0;
const e = typeof end === 'undefined' ? indices.length : end;
export function makeBuckets<K extends string | number>(
indices: Helpers.ArrayLike<number>, getKey: (i: number) => K, options?: MakeBucketsOptions<K>): ArrayLike<number> {
const s = (options && options.start) || 0;
const e = (options && options.end) || indices.length;
if (e - s <= 0) throw new Error('Can only bucket non-empty collections.');
return _makeBuckets(indices, getKey, sort, s, e);
return _makeBuckets(indices, getKey, !!(options && options.sort), s, e);
}
\ No newline at end of file
......@@ -27,13 +27,13 @@ import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif';
import mmCIF_Format = Format.mmCIF
type AtomSite = mmCIF_Database['atom_site']
function findModelBounds({ data }: mmCIF_Format, startIndex: number) {
function findModelEnd({ data }: mmCIF_Format, startIndex: number) {
const num = data.atom_site.pdbx_PDB_model_num;
const atomCount = num.rowCount;
if (!num.isDefined) return Interval.ofBounds(startIndex, atomCount);
if (!num.isDefined) return atomCount;
let endIndex = startIndex + 1;
while (endIndex < atomCount && num.areValuesEqual(startIndex, endIndex)) endIndex++;
return Interval.ofBounds(startIndex, endIndex);
return endIndex;
}
function findHierarchyOffsets(atom_site: AtomSite) {
......@@ -194,12 +194,11 @@ function buildModels(format: mmCIF_Format): Task<ReadonlyArray<Model>> {
const models: Model[] = [];
let modelStart = 0;
while (modelStart < atomCount) {
const bounds = findModelBounds(format, modelStart);
const atom_site = await sortAtomSite(ctx, format.data.atom_site, 0, Interval.end(bounds));
const modelEnd = findModelEnd(format, modelStart);
const atom_site = await sortAtomSite(ctx, format.data.atom_site, modelStart, modelEnd);
const model = createModel(format, atom_site, models.length > 0 ? models[models.length - 1] : void 0);
models.push(model);
modelStart = Interval.end(bounds);
modelStart = modelEnd;
}
return models;
});
......
......@@ -20,16 +20,16 @@ export async function sortAtomSite(ctx: RuntimeContext, atom_site: mmCIF_Databas
const indices = createRangeArray(start, end - 1);
const { label_entity_id, label_asym_id, label_seq_id } = atom_site;
const entityBuckets = makeBuckets(indices, label_entity_id.value, false);
const entityBuckets = makeBuckets(indices, label_entity_id.value);
if (ctx.shouldUpdate) await ctx.update();
for (let ei = 0, _eI = entityBuckets.length - 1; ei < _eI; ei++) {
const chainBuckets = makeBuckets(indices, label_asym_id.value, false, entityBuckets[ei], entityBuckets[ei + 1]);
const chainBuckets = makeBuckets(indices, label_asym_id.value, { start: entityBuckets[ei], end: entityBuckets[ei + 1] });
for (let cI = 0, _cI = chainBuckets.length - 1; cI < _cI; cI++) {
const aI = chainBuckets[cI];
// are we in HETATM territory?
if (label_seq_id.valueKind(aI) !== Column.ValueKind.Present) continue;
makeBuckets(indices, label_seq_id.value, true, aI, chainBuckets[cI + 1]);
makeBuckets(indices, label_seq_id.value, { sort: true, start: aI, end: chainBuckets[cI + 1] });
if (ctx.shouldUpdate) await ctx.update();
}
if (ctx.shouldUpdate) await ctx.update();
......
......@@ -43,6 +43,8 @@ interface Model extends Readonly<{
[customName: string]: any
},
// TODO: separate properties to "static" (propagated with trajectory) and "dynamic" (computed for each frame separately)
coarseHierarchy: CoarseHierarchy,
coarseConformation: CoarseConformation
}> {
......@@ -56,6 +58,18 @@ namespace Model {
case 'mmCIF': return from_mmCIF(format);
}
}
// TODO: figure the place to include this?
// export interface Property<T, K> { (model: Model, index: number): T, _kind: K }
// export interface AtomicProperty<T> extends Property<T, 'atomic'> { }
// export interface CoarseProperty<T> extends Property<T, 'coarse'> { }
// export interface SphereProperty<T> extends Property<T, 'sphere'> { }
// export interface GaussianProperty<T> extends Property<T, 'gaussian'> { }
// export function atomProp<T>(p: (model: Model, i: number) => T): AtomicProperty<T> { return p as any; }
// export function residueProp<T>(p: (model: Model, residueIndex: number) => T): AtomicProperty<T> {
// return p as any;
// }
}
export default Model
\ No newline at end of file
......@@ -49,13 +49,12 @@ export interface AtomicData {
export interface AtomicSegments {
residueSegments: Segmentation,
chainSegments: Segmentation
// TODO: include entity segments?
}
export interface AtomicKeys {
// indicate whether the keys form an increasing sequence and within each chain, sequence numbers
// are in increasing order.
// monotonous sequences enable for example faster secondary structure assignment.
isMonotonous: boolean,
// TODO: since Atoms must be sorted now, get rid of keys
// TODO: include (lazily computed) "entity/chain/residue" indices?
// assign a key to each residue index.
residueKey: ArrayLike<number>,
......
......@@ -9,11 +9,6 @@ import { Column } from 'mol-data/db'
import { Segmentation } from 'mol-data/int';
export interface CoarsedElementKeys {
// indicate whether the keys form an increasing sequence and within each chain, sequence numbers
// are in increasing order.
// monotonous sequences enable for example faster secondary structure assignment.
isMonotonous: boolean,
// assign a key to each element
chainKey: ArrayLike<number>,
// assign a key to each element, index to the Model.entities.data table
......
......@@ -48,15 +48,6 @@ function createLookUp(entities: Entities, chain: Map<number, Map<string, number>
return { findChainKey, findResidueKey };
}
function checkMonotonous(xs: ArrayLike<number>) {
for (let i = 1, _i = xs.length; i < _i; i++) {
if (xs[i] < xs[i - 1]) {
return false;
}
}
return true;
}
function missingEntity(k: string) {
throw new Error(`Missing entity entry for entity id '${k}'.`);
}
......@@ -76,8 +67,6 @@ export function getAtomicKeys(data: AtomicData, entities: Entities, segments: At
const atomSet = Interval.ofBounds(0, data.atoms._rowCount);
let isMonotonous = true;
const chainsIt = Segmentation.transientSegments(segments.chainSegments, atomSet);
while (chainsIt.hasNext) {
const chainSegment = chainsIt.move();
......@@ -93,13 +82,9 @@ export function getAtomicKeys(data: AtomicData, entities: Entities, segments: At
const residueMap = getElementSubstructureKeyMap(residueMaps, cKey);
const residuesIt = Segmentation.transientSegments(segments.residueSegments, atomSet, chainSegment);
let last_seq_id = Number.NEGATIVE_INFINITY;
while (residuesIt.hasNext) {
const residueSegment = residuesIt.move();
const rI = residueSegment.index;
const seq_id = auth_seq_id.value(rI);
if (seq_id < last_seq_id) isMonotonous = false;
last_seq_id = seq_id;
const residueId = getResidueId(label_comp_id.value(rI), auth_seq_id.value(rI), pdbx_PDB_ins_code.value(rI));
residueKey[rI] = getElementKey(residueMap, residueId, residueCounter);
}
......@@ -108,7 +93,6 @@ export function getAtomicKeys(data: AtomicData, entities: Entities, segments: At
const { findChainKey, findResidueKey } = createLookUp(entities, chainMaps, residueMaps);
return {
isMonotonous: isMonotonous && checkMonotonous(entityKey) && checkMonotonous(chainKey) && checkMonotonous(residueKey),
residueKey: residueKey,
chainKey: chainKey,
entityKey: entityKey,
......
......@@ -33,15 +33,6 @@ function createLookUp(entities: Entities, chain: Map<number, Map<string, number>
return { findChainKey };
}
function checkMonotonous(xs: ArrayLike<number>) {
for (let i = 1, _i = xs.length; i < _i; i++) {
if (xs[i] < xs[i - 1]) {
return false;
}
}
return true;
}
function missingEntity(k: string) {
throw new Error(`Missing entity entry for entity id '${k}'.`);
}
......@@ -75,7 +66,6 @@ export function getCoarseKeys(data: CoarseElementData, modelIndex: (id: number)
const { findChainKey } = createLookUp(entities, chainMaps);
return {
isMonotonous: checkMonotonous(entityKey) && checkMonotonous(chainKey),
chainKey: chainKey,
entityKey: entityKey,
modelKey: modelKey,
......
......@@ -23,8 +23,8 @@ namespace Element {
export function createEmptyArray(n: number): Element[] { return new Float64Array(n) as any; }
/** All the information required to access element properties */
export interface Location {
unit: Unit,
export interface Location<U = Unit> {
unit: U,
/** Index into element (atomic/coarse) properties of unit.model */
element: number
}
......@@ -40,6 +40,15 @@ namespace Element {
export function property<T>(p: Property<T>) { return p; }
function _wrongUnitKind(kind: string) { throw new Error(`Property only available for ${kind} models.`); }
export function atomicProperty<T>(p: (location: Location<Unit.Atomic>) => T) {
return property(l => Unit.isAtomic(l.unit) ? p(l as Location<Unit.Atomic>) : _wrongUnitKind('atomic') );
}
export function coarseProperty<T>(p: (location: Location<Unit.Spheres | Unit.Gaussians>) => T) {
return property(l => Unit.isCoarse(l.unit) ? p(l as Location<Unit.Spheres | Unit.Gaussians>) : _wrongUnitKind('coarse') );
}
/** Represents multiple element index locations */
export interface Loci {
readonly kind: 'element-loci',
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment