Ver código fonte

VolumeData and various tweaks

David Sehnal 7 anos atrás
pai
commit
6f17707e74

+ 0 - 0
src/mol-io/reader/cif/schema/density.ts → src/apps/structure-info/density.ts


+ 43 - 0
src/apps/structure-info/helpers.ts

@@ -0,0 +1,43 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import * as util from 'util'
+import * as fs from 'fs'
+import fetch from 'node-fetch'
+require('util.promisify').shim();
+
+import CIF from 'mol-io/reader/cif'
+import { Run, Progress } from 'mol-task'
+
+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 parseCif(data: string|Uint8Array) {
+    const comp = CIF.parse(data);
+    const parsed = await Run(comp, p => console.log(Progress.format(p)), 250);
+    if (parsed.isError) throw parsed;
+    return parsed
+}
+
+export async function openCif(path: string) {
+    const data = await readFile(path);
+    return parseCif(data);
+}
+
+export async function downloadCif(url: string, isBinary: boolean) {
+    const data = await fetch(url);
+    return parseCif(isBinary ? new Uint8Array(await data.arrayBuffer()) : await data.text());
+}

+ 12 - 34
src/apps/structure-info/index.ts

@@ -2,54 +2,32 @@
  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ * @author David Sehnal <david.sehnal@gmail.com>
  */
 
 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, 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 { 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';
+import { openCif, downloadCif } from './helpers';
 
-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);
-    const parsed = await Run(comp, p => console.log(Progress.format(p)), 250);
-    if (parsed.isError) throw parsed;
-    return parsed
+async function downloadFromPdb(pdb: string) {
+    // `https://files.rcsb.org/download/${pdb}.cif`
+    const parsed = await downloadCif(`http://www.ebi.ac.uk/pdbe/static/entry/${pdb}_updated.cif`, false);
+    return CIF.schema.mmCIF(parsed.result.blocks[0]);
 }
 
-async function getPdb(pdb: string) {
-    // const data = await fetch(`https://files.rcsb.org/download/${pdb}.cif`)
-    const data = await fetch(`http://www.ebi.ac.uk/pdbe/static/entry/${pdb}_updated.cif`);
-    const parsed = await parseCif(await data.text())
-    return CIF.schema.mmCIF(parsed.result.blocks[0])
+async function readPdbFile(path: string) {
+    const parsed = await openCif(path);
+    return CIF.schema.mmCIF(parsed.result.blocks[0]);
 }
 
 export function atomLabel(model: Model, aI: number) {
@@ -154,12 +132,12 @@ async function run(mmcif: mmCIF_Database) {
 }
 
 async function runDL(pdb: string) {
-    const mmcif = await getPdb(pdb)
+    const mmcif = await downloadFromPdb(pdb)
     run(mmcif);
 }
 
 async function runFile(filename: string) {
-    const mmcif = await readCif(filename);
+    const mmcif = await readPdbFile(filename);
     run(mmcif);
 }
 

+ 4 - 4
src/mol-data/db/column.ts

@@ -34,7 +34,7 @@ namespace Column {
         export type Float = { '@type': 'float', T: number } & Base<'float'>
         export type Coordinate = { '@type': 'coord', T: number } & Base<'float'>
 
-        export type Tensor = { '@type': 'tensor', T: Tensors.Data, space: Tensors.Space } & Base<'tensor'>
+        export type Tensor = { '@type': 'tensor', T: Tensors.Data, space: Tensors.Space, baseType: Int | Float } & Base<'tensor'>
         export type Aliased<T> = { '@type': 'aliased', T: T } & Base<'str' | 'int'>
         export type List<T extends number|string> = { '@type': 'list', T: T[], separator: string, itemParse: (x: string) => T } & Base<'list'>
 
@@ -46,9 +46,9 @@ namespace Column {
         export function Str(defaultValue = ''): Str { return { '@type': 'str', T: defaultValue, valueType: 'str' } };
         export function Int(defaultValue = 0): Int { return { '@type': 'int', T: defaultValue, valueType: 'int' } };
         export function Float(defaultValue = 0): Float { return { '@type': 'float', T: defaultValue, valueType: 'float' } };
-        export function Tensor(space: Tensors.Space): Tensor { return { '@type': 'tensor', T: space.create(), space, valueType: 'tensor' }; }
-        export function Vector(dim: number): Tensor { return Tensor(Tensors.Vector(dim)); }
-        export function Matrix(rows: number, cols: number): Tensor { return Tensor(Tensors.ColumnMajorMatrix(rows, cols)); }
+        export function Tensor(space: Tensors.Space, baseType: Int | Float = float): Tensor { return { '@type': 'tensor', T: space.create(), space, valueType: 'tensor', baseType }; }
+        export function Vector(dim: number, baseType: Int | Float = float): Tensor { return Tensor(Tensors.Vector(dim), baseType); }
+        export function Matrix(rows: number, cols: number, baseType: Int | Float = float): Tensor { return Tensor(Tensors.ColumnMajorMatrix(rows, cols), baseType); }
 
         export function Aliased<T>(t: Str | Int, defaultValue?: T): Aliased<T> {
             if (typeof defaultValue !== 'undefined') return { ...t, T: defaultValue } as any as Aliased<T>;

+ 68 - 0
src/mol-io/reader/cif/schema/density-server.ts

@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { Database, Column } from 'mol-data/db'
+
+import Schema = Column.Schema
+
+const str = Schema.str;
+const int = Schema.int;
+const float = Schema.float;
+
+const Aliased = Schema.Aliased;
+const Vector = Schema.Vector;
+
+export const DensityServer_Header_Schema = {
+    density_server_result: {
+        'server_version': str,
+        'datetime_utc': str,
+        'guid': str,
+        'is_empty': Aliased<'no' | 'n' | 'yes' | 'y'>(str),
+        'has_error': Aliased<'no' | 'n' | 'yes' | 'y'>(str),
+        'error': str,
+        'query_source_id': str,
+        'query_type': Aliased<'box' | 'cell'>(str),
+        'query_box_type': Aliased<'cartesian' | 'fractional'>(str),
+        'query_box_a': Vector(3),
+        'query_box_b': Vector(3)
+    }
+}
+
+export const DensityServer_Data_Schema = {
+    volume_data_3d_info: {
+        'name': str,
+        // zero indexed axis order of the data
+        'axis_order': Vector(3, int),
+        // Origin in fractional coords
+        'origin': Vector(3),
+        // Dimension in fractional coords
+        'dimensions': Vector(3),
+        'sample_rate': int,
+        // number of samples along each axis
+        'sample_count': Vector(3, int),
+        'spacegroup_number': int,
+        'spacegroup_cell_size': Vector(3),
+        // angles in degrees
+        'spacegroup_cell_angles': Vector(3),
+        'mean_source': float,
+        'mean_sampled': float,
+        'sigma_source': float,
+        'sigma_sampled': float,
+        'min_source': float,
+        'min_sampled': float,
+        'max_source': float,
+        'max_sampled': float
+    },
+    volume_data_3d: {
+        values: float
+    }
+}
+
+export type DensityServer_Header_Schema = typeof DensityServer_Header_Schema;
+export interface DensityServer_Header_Database extends Database<DensityServer_Header_Schema> {}
+
+export type DensityServer_Data_Schema = typeof DensityServer_Data_Schema;
+export interface DensityServer_Data_Database extends Database<DensityServer_Data_Schema> {}

+ 6 - 0
src/mol-math/geometry/primitives/box3d.ts

@@ -11,6 +11,8 @@ import { OrderedSet } from 'mol-data/int';
 interface Box3D { min: Vec3, max: Vec3 }
 
 namespace Box3D {
+    export function create(min: Vec3, max: Vec3): Box3D { return { min, max }; }
+    
     export function computeBounding(data: PositionData): Box3D {
         const min = [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE];
         const max = [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE];
@@ -28,6 +30,10 @@ namespace Box3D {
         return { min: Vec3.create(min[0], min[1], min[2]), max: Vec3.create(max[0], max[1], max[2]) }
     }
 
+    export function size(box: Box3D) {
+        return Vec3.sub(Vec3.zero(), box.max, box.min);
+    }
+
     export function expand(box: Box3D, delta: Vec3): Box3D {
         return {
             min: Vec3.sub(Vec3.zero(), box.min, delta),

+ 23 - 5
src/mol-math/geometry/spacegroup/construction.ts

@@ -9,23 +9,34 @@ import { SpacegroupName, TransformData, GroupData, SpacegroupNumbers, Spacegroup
 import { SymmetryOperator } from '../../geometry';
 
 interface SpacegroupCell {
+    readonly number: number,
     readonly size: Vec3,
     readonly anglesInRadians: Vec3,
     /** Transfrom cartesian -> fractional coordinates within the cell */
     readonly toFractional: Mat4,
     /** Transfrom fractional coordinates within the cell -> cartesian */
-    readonly fromFractional: Mat4
+    readonly fromFractional: Mat4,
+    readonly fractionalBasis: Vec3[]
 }
 
 interface Spacegroup {
-    readonly number: number,
     readonly name: string,
     readonly cell: SpacegroupCell,
     readonly operators: ReadonlyArray<Mat4>
 }
 
 namespace SpacegroupCell {
-    export function create(size: Vec3, anglesInRadians: Vec3): SpacegroupCell {
+    // Create a 'P 1' with cellsize [1, 1, 1]
+    export function zero() {
+        return create(0, Vec3.create(1, 1, 1), Vec3.create(Math.PI / 2, Math.PI / 2, Math.PI / 2));
+    }
+
+    // True if 'P 1' with cellsize [1, 1, 1]
+    export function isZero(cell: SpacegroupCell) {
+        return cell.size[0] === 1 && cell.size[1] === 1 && cell.size[1] === 1;
+    }
+
+    export function create(nameOrNumber: number | SpacegroupName, size: Vec3, anglesInRadians: Vec3): SpacegroupCell {
         const alpha = anglesInRadians[0];
         const beta = anglesInRadians[1];
         const gamma = anglesInRadians[2];
@@ -48,7 +59,14 @@ namespace SpacegroupCell {
         ]);
         const toFractional = Mat4.invert(Mat4.zero(), fromFractional)!;
 
-        return { size, anglesInRadians, toFractional, fromFractional };
+        const fractionalBasis = [
+            Vec3.transformMat4(Vec3.zero(), Vec3.create(1, 0, 0), toFractional),
+            Vec3.transformMat4(Vec3.zero(), Vec3.create(0, 1, 0), toFractional),
+            Vec3.transformMat4(Vec3.zero(), Vec3.create(0, 0, 1), toFractional)
+        ];
+
+        const num = typeof nameOrNumber === 'number' ? nameOrNumber : SpacegroupNumbers[nameOrNumber];
+        return { number: num, size, anglesInRadians, toFractional, fromFractional, fractionalBasis };
     }
 }
 
@@ -63,7 +81,7 @@ namespace Spacegroup {
 
         const operators = GroupData[num].map(i => getOperatorMatrix(OperatorData[i]));
 
-        return { number: num, name, cell, operators };
+        return { name, cell, operators };
     }
 
     const _tempVec = Vec3.zero(), _tempMat = Mat4.zero();

+ 17 - 0
src/mol-math/linear-algebra/3d/mat4.ts

@@ -79,6 +79,11 @@ namespace Mat4 {
         return mat;
     }
 
+    export function setZero(mat: Mat4): Mat4 {
+        for (let i = 0; i < 16; i++) mat[i] = 0;
+        return mat;
+    }
+
     export function ofRows(rows: number[][]): Mat4 {
         const out = zero();
         for (let i = 0; i < 4; i++) {
@@ -741,6 +746,18 @@ namespace Mat4 {
 
         return out;
     }
+
+    /**
+     * Perm is 0-indexed permutation
+     */
+    export function fromPermutation(out: Mat4, perm: number[]) {
+        setZero(out);
+        for (let i = 0; i < 4; i++) {
+            const p = perm[i];
+            setValue(out, i, p, 1);
+        }
+        return out;
+    }
 }
 
 export default Mat4

+ 24 - 0
src/mol-math/linear-algebra/_spec/mat4.spec.ts

@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { Mat4, Vec3 } from '../3d'
+
+describe('Mat4', () => {
+    it('permutation', () => {
+        expect(Mat4.areEqual(Mat4.fromPermutation(Mat4.zero(), [0, 1, 2, 3]), Mat4.identity(), 1e-6)).toBe(true);
+
+        expect(Mat4.areEqual(Mat4.fromPermutation(Mat4.zero(), [1, 0, 2, 3]), Mat4.ofRows([
+            [0, 1, 0, 0],
+            [1, 0, 0, 0],
+            [0, 0, 1, 0],
+            [0, 0, 0, 1],
+        ]), 1e-6)).toBe(true);
+
+        const perm = Mat4.fromPermutation(Mat4.zero(), [1, 2, 0, 3]);
+
+        expect(Vec3.transformMat4(Vec3.zero(), Vec3.create(1, 2, 3), perm)).toEqual(Vec3.create(2, 3, 1));
+    });
+});

+ 2 - 0
src/mol-math/linear-algebra/tensor.ts

@@ -49,6 +49,8 @@ export namespace Tensor {
         return { rank: dimensions.length, dimensions, axisOrderSlowToFast, create: creator(layout), get, set };
     }
 
+    export function Data1(values: ArrayLike<number>): Data { return values as Data; }
+
     export function Vector(d: number, ctor?: ArrayCtor) { return Space([d], [0], ctor); }
     export function ColumnMajorMatrix(rows: number, cols: number, ctor?: ArrayCtor) { return Space([rows, cols], [1, 0], ctor); }
     export function RowMajorMatrix(rows: number, cols: number, ctor?: ArrayCtor) { return Space([rows, cols], [0, 1], ctor); }

+ 2 - 0
src/mol-model/sequence/TODO

@@ -0,0 +1,2 @@
+- Support for FASTA etc..
+- Mapping/properties for 'structure'

+ 0 - 0
src/mol-model/volume/TODO → src/mol-model/volume.ts


+ 6 - 0
src/mol-model/volume/_spec/data.spec.ts

@@ -0,0 +1,6 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+

+ 39 - 0
src/mol-model/volume/data.ts

@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { SpacegroupCell, Box3D } from 'mol-math/geometry'
+import { Tensor, Mat4, Vec3 } from 'mol-math/linear-algebra'
+
+interface VolumeData {
+    // The basic unit cell that contains the data.
+    readonly cell: SpacegroupCell,
+    readonly fractionalBox: Box3D,
+    readonly data: Tensor,
+    readonly dataStats: Readonly<{
+        min: number,
+        max: number,
+        mean: number,
+        sigma: number
+    }>
+}
+
+namespace VolumeData {
+    const _scale = Mat4.zero(), _translate = Mat4.zero(), _perm = Mat4.zero();
+    export function getGridToCartesianTransform(volume: VolumeData) {
+        const { data: { space } } = volume;
+        const scale = Mat4.fromScaling(_scale, Vec3.div(Vec3.zero(), Box3D.size(volume.fractionalBox), Vec3.ofArray(space.dimensions)));
+        const translate = Mat4.fromTranslation(_translate, volume.fractionalBox.min);
+        const ret = Mat4.mul3(Mat4.zero(), volume.cell.fromFractional, translate, scale);
+
+        const [x, y, z] = space.axisOrderSlowToFast;
+        if (x !== 0 || y !== 1 || z !== 2) {
+            Mat4.mul(ret, Mat4.fromPermutation(_perm, [x, y, z, 3]), ret);
+        }
+        return ret;
+    }
+}
+
+export { VolumeData }

+ 37 - 0
src/mol-model/volume/formats/density-server.ts

@@ -0,0 +1,37 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ */
+
+import { DensityServer_Data_Database } from 'mol-io/reader/cif/schema/density-server'
+import { VolumeData } from '../data'
+import { Task } from 'mol-task';
+import { SpacegroupCell, Box3D } from 'mol-math/geometry';
+import { Tensor, Vec3 } from 'mol-math/linear-algebra';
+
+function parseDensityServerData(source: DensityServer_Data_Database): Task<VolumeData> {
+    return Task.create<VolumeData>('Parse Volume Data', async ctx => {
+        const { volume_data_3d_info: info, volume_data_3d: values } = source;
+        const cell = SpacegroupCell.create(info.spacegroup_number.value(0), Vec3.ofArray(info.spacegroup_cell_size.value(0)), Vec3.ofArray(info.spacegroup_cell_angles.value(0)));
+
+        const tensorSpace = Tensor.Space(info.sample_count.value(0), info.axis_order.value(0), Float32Array);
+        const data = Tensor.create(tensorSpace, Tensor.Data1(values.values.toArray()));
+
+        const origin = Vec3.ofArray(info.origin.value(0)), dimensions = Vec3.ofArray(info.dimensions.value(0));
+
+        return {
+            cell,
+            fractionalBox: Box3D.create(origin, Vec3.add(Vec3.zero(), origin, dimensions)),
+            data,
+            dataStats: {
+                min: info.min_sampled.value(0),
+                max: info.max_sampled.value(0),
+                mean: info.mean_sampled.value(0),
+                sigma: info.sigma_sampled.value(0)
+            }
+        };
+    });
+}
+
+export { parseDensityServerData }