Browse Source

Properties

David Sehnal 7 years ago
parent
commit
aa4fae98a7

+ 0 - 3
src/mol-data/model.ts

@@ -29,9 +29,6 @@ interface Model extends Readonly<{
     hierarchy: HierarchyProperties,
     conformation: ConformationProperties,
 
-    // used for diffing.
-    version: Version,
-
     atomCount: number
 }> { }
 

+ 4 - 5
src/mol-data/model/builders/mmcif.ts

@@ -64,7 +64,8 @@ function getConformation(data: mmCIF, bounds: Interval): Conformation {
     const start = Interval.start(bounds), end = Interval.end(bounds);
     const { atom_site } = data;
     return {
-        id: Column.window(atom_site.id, start, end),
+        id: newUUID(),
+        atomId: Column.window(atom_site.id, start, end),
         occupancy: Column.window(atom_site.occupancy, start, end),
         B_iso_or_equiv: Column.window(atom_site.B_iso_or_equiv, start, end),
         x: atom_site.Cartn_x.toArray({ array: Float32Array, start, end }),
@@ -87,8 +88,7 @@ function createModel(raw: RawData, data: mmCIF, bounds: Interval, previous?: Mod
     if (previous && isHierarchyDataEqual(previous.hierarchy, hierarchyData)) {
         return {
             ...previous,
-            conformation: getConformation(data, bounds),
-            version: { hierarchy: previous.version.hierarchy, conformation: newUUID() }
+            conformation: getConformation(data, bounds)
         };
     }
 
@@ -104,12 +104,11 @@ function createModel(raw: RawData, data: mmCIF, bounds: Interval, previous?: Mod
         model_num: data.atom_site.pdbx_PDB_model_num.value(Interval.start(bounds)),
         hierarchy: { ...hierarchyData, ...hierarchyKeys, ...hierarchySegments },
         conformation: getConformation(data, bounds),
-        version: { hierarchy: newUUID(), conformation: newUUID() },
         atomCount: Interval.size(bounds)
     };
 }
 
-function buildModels(data: mmCIF): ArrayLike<Model> {
+function buildModels(data: mmCIF): ReadonlyArray<Model> {
     const raw: RawData = { source: 'mmCIF', data };
     const models: Model[] = [];
     const atomCount = data.atom_site._rowCount;

+ 4 - 1
src/mol-data/model/properties/conformation.ts

@@ -5,12 +5,15 @@
  */
 
 import Column from '../../../mol-base/collections/column'
+import UUID from '../../../mol-base/utils/uuid'
 
 interface Conformation {
+    id: UUID,
+
     // ID is part of conformation because mmCIF is a leaky abstraction
     // thats assigns different atom ids to corresponding atoms in different models
     // ... go figure.
-    id: Column<number>,
+    atomId: Column<number>,
 
     occupancy: Column<number>,
     B_iso_or_equiv: Column<number>

+ 2 - 1
src/mol-data/structure/atom-set.ts

@@ -4,6 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
+import SortedArray from '../../mol-base/collections/integer/sorted-array'
 import OrderedSet from '../../mol-base/collections/integer/ordered-set'
 import Iterator from '../../mol-base/collections/iterator'
 import Atom from './atom'
@@ -17,7 +18,7 @@ namespace AtomSet {
     export const create: (data: Atom | ArrayLike<Atom> | { [unitId: number]: OrderedSet }) => AtomSet = Base.create as any;
 
     export const unitCount: (set: AtomSet) => number = Base.keyCount as any;
-    export const unitIds: (set: AtomSet) => OrderedSet = Base.getKeys as any;
+    export const unitIds: (set: AtomSet) => SortedArray = Base.getKeys as any;
     export const unitHas: (set: AtomSet, id: number) => boolean = Base.hasKey as any;
     export const unitGetId: (set: AtomSet, i: number) => number = Base.getKey as any;
 

+ 3 - 1
src/mol-data/structure/property.ts

@@ -25,7 +25,7 @@ namespace Property {
         chainIndex: number
     }
 
-    export function create(structure?: Structure, unit?: Unit): Location {
+    export function createLocation(structure?: Structure, unit?: Unit): Location {
         return {
             structure: structure!,
             unit: unit!,
@@ -45,6 +45,8 @@ namespace Property {
         l.chainIndex = u.chainIndex[i];
     }
 
+    export function naive<T>(p: Property<T>) { return p; }
+
     export function cachedAtomColumn<T>(col: (model: Model) => Column<T>): Property<T> {
         let lastUnit: Unit | undefined = void 0;
         let cached: ((row: number) => T) | undefined = void 0;

+ 25 - 3
src/mol-io/reader/cif/binary/field.ts

@@ -10,6 +10,10 @@ import { EncodedColumn } from './encoding'
 import decode from './decoder'
 import { parseInt as fastParseInt, parseFloat as fastParseFloat } from '../../common/text/number-parser'
 
+function wrap(o: Data.Field) {
+    return { ...o };
+}
+
 export default function Field(column: EncodedColumn): Data.Field {
     const mask = column.mask ? decode(column.mask) as number[] : void 0;
     const data = decode(column.data);
@@ -37,7 +41,7 @@ export default function Field(column: EncodedColumn): Data.Field {
 
     const rowCount = data.length;
 
-    return {
+    return wrap({
         '@array': data,
         isDefined: true,
         rowCount,
@@ -53,5 +57,23 @@ export default function Field(column: EncodedColumn): Data.Field {
         toFloatArray: isNumeric
             ? params => ColumnHelpers.typedArrayWindow(data, params)
             : params => ColumnHelpers.createAndFillArray(rowCount, float, params)
-    };
-}
+    });
+}
+
+// return wrap({
+//     '@array': data,
+//     isDefined: true,
+//     rowCount,
+//     str: str as any,
+//     int,
+//     float,
+//     valueKind,
+//     areValuesEqual: (rowA, rowB) => data[rowA] === data[rowB],
+//     toStringArray: params => ColumnHelpers.createAndFillArray(rowCount, str, params),
+//     toIntArray: isNumeric
+//         ? params => ColumnHelpers.typedArrayWindow(data, params)
+//         : params => ColumnHelpers.createAndFillArray(rowCount, int, params),
+//     toFloatArray: isNumeric
+//         ? params => ColumnHelpers.typedArrayWindow(data, params)
+//         : params => ColumnHelpers.createAndFillArray(rowCount, float, params)
+// });

+ 5 - 1
src/mol-io/reader/cif/binary/parser.ts

@@ -20,12 +20,16 @@ function checkVersions(min: number[], current: number[]) {
 
 function Category(data: Encoding.EncodedCategory): Data.Category {
     const map = Object.create(null);
+    const cache = Object.create(null);
     for (const col of data.columns) map[col.name] = col;
     return {
         rowCount: data.rowCount,
         getField(name) {
             const col = map[name];
-            return col ? Field(col) : void 0;
+            if (!col) return void 0;
+            if (!!cache[name]) return cache[name];
+            cache[name] = Field(col);
+            return cache[name];
         }
     }
 }

+ 168 - 0
src/perf-tests/structure.ts

@@ -0,0 +1,168 @@
+/**
+ * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import * as B from 'benchmark'
+
+import * as util from 'util'
+import * as fs from 'fs'
+import CIF from '../mol-io/reader/cif'
+
+import buildModels from '../mol-data/model/builders/mmcif'
+import { ofModel } from '../mol-data/structure/base'
+import Property from '../mol-data/structure/property'
+import Model from '../mol-data/Model'
+import Structure from '../mol-data/structure'
+import OrdSet from '../mol-base/collections/integer/ordered-set'
+import AtomSet from '../mol-data/structure/atom-set'
+
+require('util.promisify').shim();
+const readFileAsync = util.promisify(fs.readFile);
+
+async function readData(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');
+    }
+}
+
+export async function readCIF(path: string) {
+    const input = await readData(path)
+    const comp = typeof input === 'string' ? CIF.parseText(input) : CIF.parseBinary(input);
+    const parsed = await comp();
+    if (parsed.isError) {
+        throw parsed;
+    }
+
+    const data = parsed.result.blocks[0];
+    const mmcif = CIF.schema.mmCIF(data);
+    const models = buildModels(mmcif);
+    const structures = models.map(ofModel);
+
+    return { mmcif, models, structures };
+}
+
+export namespace PropertyAccess {
+    function baselineRaw(model: Model) {
+        const atom_site = model.sourceData.data._frame.categories['_atom_site'];
+        const id = atom_site.getField('id')!.int;
+        let s = 0;
+        for (let i = 0, _i = atom_site.rowCount; i < _i; i++) {
+            s += id(i);
+        }
+        return s;
+    }
+
+    function baseline(model: Model) {
+        const atom_site = model.sourceData.data.atom_site;
+        const id = atom_site.id.value;
+        let s = 0;
+        for (let i = 0, _i = atom_site._rowCount; i < _i; i++) {
+            s += id(i);
+        }
+        return s;
+    }
+
+    function sumProperty(structure: Structure, p: Property<number>, initial: number) {
+        const { atoms, units } = structure;
+        const unitIds = AtomSet.unitIds(atoms);
+        const l = Property.createLocation(structure);
+
+        let s = initial;
+
+        for (let i = 0, _i = unitIds.length; i < _i; i++) {
+            const unitId = unitIds[i];
+            l.unit = units[unitId];
+            const set = AtomSet.unitGetByIndex(atoms, i);
+            const { residueIndex, chainIndex } = l.unit;
+
+            for (let j = 0, _j = OrdSet.size(set); j < _j; j++) {
+                const aI = OrdSet.getAt(set, j);
+                l.atomIndex = aI;
+                l.residueIndex = residueIndex[aI];
+                l.chainIndex = chainIndex[aI];
+                s += p(l);
+            }
+        }
+
+        return s;
+    }
+
+    function sumDirect(structure: Structure) {
+        const { atoms, units } = structure;
+        const unitIds = AtomSet.unitIds(atoms);
+
+        let s = 0;
+
+        for (let i = 0, _i = unitIds.length; i < _i; i++) {
+            const unitId = unitIds[i];
+            const unit = units[unitId];
+            const set = AtomSet.unitGetByIndex(atoms, i);
+            //const { residueIndex, chainIndex } = unit;
+            const p = unit.model.conformation.atomId.value;
+            for (let j = 0, _j = OrdSet.size(set); j < _j; j++) {
+                const aI = OrdSet.getAt(set, j);
+                s += p(aI);
+            }
+        }
+
+        return s;
+    }
+
+    // function concatProperty(structure: Structure, p: Property<string>) {
+    //     const { atoms, units } = structure;
+    //     const unitIds = AtomSet.unitIds(atoms);
+    //     const l = Property.createLocation(structure);
+
+    //     let s = [];
+
+    //     for (let i = 0, _i = unitIds.length; i < _i; i++) {
+    //         const unitId = unitIds[i];
+    //         l.unit = units[unitId];
+    //         const set = AtomSet.unitGetByIndex(atoms, i);
+    //         const { residueIndex, chainIndex } = l.unit;
+
+    //         for (let j = 0, _j = OrdSet.size(set); j < _j; j++) {
+    //             const aI = OrdSet.getAt(set, j);
+    //             l.atomIndex = aI;
+    //             l.residueIndex = residueIndex[aI];
+    //             l.chainIndex = chainIndex[aI];
+    //             s[s.length] = p(l);
+    //         }
+    //     }
+
+    //     return s;
+    // }
+
+    export async function run() {
+        const { structures, models } = await readCIF('./examples/1cbs_full.bcif');
+
+        console.log(baseline(models[0]));
+        console.log(baselineRaw(models[0]));
+        console.log(sumProperty(structures[0], l => l.unit.model.conformation.atomId.value(l.atomIndex), 0));
+        console.log(sumProperty(structures[0], Property.cachedAtomColumn(m => m.conformation.atomId), 0));
+        console.log(sumDirect(structures[0]));
+
+        const col = models[0].conformation.atomId.value;
+        const suite = new B.Suite();
+        suite
+            .add('baseline raw', () =>  baselineRaw(models[0]))
+            .add('baseline', () =>  baseline(models[0]))
+            .add('direct', () =>  sumDirect(structures[0]))
+            .add('normal int', () => sumProperty(structures[0], l => l.unit.model.conformation.atomId.value(l.atomIndex), 0))
+            .add('test int', () => sumProperty(structures[0], l => col(l.atomIndex), 0))
+            .add('cached int', () => sumProperty(structures[0], Property.cachedAtomColumn(m => m.conformation.atomId), 0))
+            //.add('concat str', () => concatProperty(structures[0], l => l.unit.model.hierarchy.atoms.auth_atom_id.value(l.atomIndex)))
+            //.add('cached concat str', () => concatProperty(structures[0], Property.cachedAtomColumn(m => m.hierarchy.atoms.auth_atom_id)))
+            .on('cycle', (e: any) => console.log(String(e.target)))
+            .run();
+    }
+}
+
+PropertyAccess.run();

+ 1 - 1
src/script.ts

@@ -119,7 +119,7 @@ async function runCIF(input: string | Uint8Array) {
     console.timeEnd('createModels');
 
     for (let i = 0; i < models.length; i++) {
-        console.log(models[i].version);
+        console.log(models[i].id);
     }
 
     // console.log(models[0].hierarchy.isMonotonous);