UNPKG

molstar

Version:

A comprehensive macromolecular library.

250 lines (249 loc) 11.3 kB
/** * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * * Taken/adapted from DensityServer (https://github.com/dsehnal/DensityServer) * * @author David Sehnal <david.sehnal@gmail.com> */ import { __awaiter, __generator } from "tslib"; import * as DataFormat from '../../common/data-format'; import * as File from '../../common/file'; import * as Coords from '../algebra/coordinate'; import * as Box from '../algebra/box'; import { ConsoleLogger } from '../../../../mol-util/console-logger'; import { State } from '../state'; import { findUniqueBlocks } from './identify'; import { compose } from './compose'; import { encode } from './encode'; import { SpacegroupCell } from '../../../../mol-math/geometry'; import { Vec3 } from '../../../../mol-math/linear-algebra'; import { UUID } from '../../../../mol-util'; import { createTypedArray } from '../../../../mol-io/common/typed-array'; import { LimitsConfig } from '../../config'; import { fileHandleFromDescriptor } from '../../../common/file-handle'; export function execute(params, outputProvider) { return __awaiter(this, void 0, void 0, function () { var start, guid, sourceFile, _a, e_1; return __generator(this, function (_b) { switch (_b.label) { case 0: start = getTime(); State.pendingQueries++; guid = UUID.create22(); params.detail = Math.min(Math.max(0, params.detail | 0), LimitsConfig.maxOutputSizeInVoxelCountByPrecisionLevel.length - 1); ConsoleLogger.logId(guid, 'Info', "id=".concat(params.sourceId, ",encoding=").concat(params.asBinary ? 'binary' : 'text', ",detail=").concat(params.detail, ",").concat(queryBoxToString(params.box))); _b.label = 1; case 1: _b.trys.push([1, 4, 5, 6]); _a = fileHandleFromDescriptor; return [4 /*yield*/, File.openRead(params.sourceFilename)]; case 2: sourceFile = _a.apply(void 0, [_b.sent(), params.sourceFilename]); return [4 /*yield*/, _execute(sourceFile, params, guid, outputProvider)]; case 3: _b.sent(); return [2 /*return*/, true]; case 4: e_1 = _b.sent(); ConsoleLogger.errorId(guid, e_1); return [2 /*return*/, false]; case 5: if (sourceFile) sourceFile.close(); ConsoleLogger.logId(guid, 'Time', "".concat(Math.round(getTime() - start), "ms")); State.pendingQueries--; return [7 /*endfinally*/]; case 6: return [2 /*return*/]; } }); }); } function getTime() { var t = process.hrtime(); return t[0] * 1000 + t[1] / 1000000; } function blockDomain(domain, blockSize) { var delta = Coords.fractional(blockSize * domain.delta[0], blockSize * domain.delta[1], blockSize * domain.delta[2]); return Coords.domain('Block', { origin: domain.origin, dimensions: domain.dimensions, delta: delta, sampleCount: Coords.sampleCounts(domain.dimensions, delta) }); } function createSampling(header, index, dataOffset) { var sampling = header.sampling[index]; var dataDomain = Coords.domain('Data', { origin: Coords.fractional(header.origin[0], header.origin[1], header.origin[2]), dimensions: Coords.fractional(header.dimensions[0], header.dimensions[1], header.dimensions[2]), delta: Coords.fractional(header.dimensions[0] / sampling.sampleCount[0], header.dimensions[1] / sampling.sampleCount[1], header.dimensions[2] / sampling.sampleCount[2]), sampleCount: sampling.sampleCount }); return { index: index, rate: sampling.rate, byteOffset: sampling.byteOffset + dataOffset, dataDomain: dataDomain, blockDomain: blockDomain(dataDomain, header.blockSize) }; } function createDataContext(file) { return __awaiter(this, void 0, void 0, function () { var _a, header, dataOffset, origin, dimensions; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, DataFormat.readHeader(file)]; case 1: _a = _b.sent(), header = _a.header, dataOffset = _a.dataOffset; origin = Coords.fractional(header.origin[0], header.origin[1], header.origin[2]); dimensions = Coords.fractional(header.dimensions[0], header.dimensions[1], header.dimensions[2]); return [2 /*return*/, { file: file, header: header, spacegroup: SpacegroupCell.create(header.spacegroup.number, Vec3.ofArray(header.spacegroup.size), Vec3.scale(Vec3.zero(), Vec3.ofArray(header.spacegroup.angles), Math.PI / 180)), dataBox: { a: origin, b: Coords.add(origin, dimensions) }, sampling: header.sampling.map(function (s, i) { return createSampling(header, i, dataOffset); }) }]; } }); }); } function createQuerySampling(data, sampling, queryBox) { var fractionalBox = Box.gridToFractional(Box.expandGridBox(Box.fractionalToGrid(queryBox, sampling.dataDomain), 1)); var blocks = findUniqueBlocks(data, sampling, fractionalBox); var ret = { sampling: sampling, fractionalBox: fractionalBox, gridDomain: Box.fractionalToDomain(fractionalBox, 'Query', sampling.dataDomain.delta), blocks: blocks }; return ret; } function pickSampling(data, queryBox, forcedLevel, precision) { if (forcedLevel > 0) { return createQuerySampling(data, data.sampling[Math.min(data.sampling.length, forcedLevel) - 1], queryBox); } var sizeLimit = LimitsConfig.maxOutputSizeInVoxelCountByPrecisionLevel[precision] || (2 * 1024 * 1024); for (var _i = 0, _a = data.sampling; _i < _a.length; _i++) { var s = _a[_i]; var gridBox = Box.fractionalToGrid(queryBox, s.dataDomain); var approxSize = Box.volume(gridBox); if (approxSize <= sizeLimit) { var sampling = createQuerySampling(data, s, queryBox); if (sampling.blocks.length <= LimitsConfig.maxRequestBlockCount) { return sampling; } } } return createQuerySampling(data, data.sampling[data.sampling.length - 1], queryBox); } function emptyQueryContext(data, params, guid) { return { kind: 'Empty', guid: guid, params: params, data: data }; } function getQueryBox(data, queryBox) { switch (queryBox.kind) { case 'Cartesian': return Box.fractionalBoxReorderAxes(Box.cartesianToFractional(queryBox, data.spacegroup), data.header.axisOrder); case 'Fractional': return Box.fractionalBoxReorderAxes(queryBox, data.header.axisOrder); default: return data.dataBox; } } function allocateValues(domain, numChannels, valueType) { var values = []; for (var i = 0; i < numChannels; i++) { values[values.length] = createTypedArray(valueType, domain.sampleVolume); } return values; } function createQueryContext(data, params, guid) { var inputQueryBox = getQueryBox(data, params.box); var queryBox; if (!data.header.spacegroup.isPeriodic) { if (!Box.areIntersecting(data.dataBox, inputQueryBox)) { return emptyQueryContext(data, params, guid); } queryBox = Box.intersect(data.dataBox, inputQueryBox); } else { queryBox = inputQueryBox; } var dimensions = Box.dimensions(queryBox); if (dimensions.some(function (d) { return isNaN(d); })) { throw new Error('The query box is not defined.'); } if (dimensions[0] * dimensions[1] * dimensions[2] > LimitsConfig.maxFractionalBoxVolume) { throw new Error('The query box volume is too big.'); } var samplingInfo = pickSampling(data, queryBox, params.forcedSamplingLevel !== void 0 ? params.forcedSamplingLevel : 0, params.detail); if (samplingInfo.blocks.length === 0) return emptyQueryContext(data, params, guid); return { kind: 'Data', guid: guid, data: data, params: params, samplingInfo: samplingInfo, values: allocateValues(samplingInfo.gridDomain, data.header.channels.length, data.header.valueType) }; } function _execute(file, params, guid, outputProvider) { return __awaiter(this, void 0, void 0, function () { var output, data, query, e_2, query; return __generator(this, function (_a) { switch (_a.label) { case 0: output = void 0; _a.label = 1; case 1: _a.trys.push([1, 5, 6, 7]); return [4 /*yield*/, createDataContext(file)]; case 2: data = _a.sent(); query = createQueryContext(data, params, guid); if (!(query.kind === 'Data')) return [3 /*break*/, 4]; // Step 3b: Compose the result data return [4 /*yield*/, compose(query)]; case 3: // Step 3b: Compose the result data _a.sent(); _a.label = 4; case 4: // Step 4: Encode the result output = outputProvider(); encode(query, output); output.end(); return [3 /*break*/, 7]; case 5: e_2 = _a.sent(); query = { kind: 'Error', guid: guid, params: params, message: "".concat(e_2) }; try { if (!output) output = outputProvider(); encode(query, output); } catch (f) { throw f; } throw e_2; case 6: if (output) output.end(); return [7 /*endfinally*/]; case 7: return [2 /*return*/]; } }); }); } function roundCoord(c) { return Math.round(100000 * c) / 100000; } function queryBoxToString(queryBox) { switch (queryBox.kind) { case 'Cartesian': case 'Fractional': var a = queryBox.a, b = queryBox.b; var r = roundCoord; return "box-type=".concat(queryBox.kind, ",box-a=(").concat(r(a[0]), ",").concat(r(a[1]), ",").concat(r(a[2]), "),box-b=(").concat(r(b[0]), ",").concat(r(b[1]), ",").concat(r(b[2]), ")"); default: return "box-type=".concat(queryBox.kind); } }