UNPKG

molstar

Version:

A comprehensive macromolecular library.

254 lines (253 loc) 11.8 kB
"use strict"; /** * 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> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.execute = void 0; var tslib_1 = require("tslib"); var DataFormat = tslib_1.__importStar(require("../../common/data-format")); var File = tslib_1.__importStar(require("../../common/file")); var Coords = tslib_1.__importStar(require("../algebra/coordinate")); var Box = tslib_1.__importStar(require("../algebra/box")); var console_logger_1 = require("../../../../mol-util/console-logger"); var state_1 = require("../state"); var identify_1 = require("./identify"); var compose_1 = require("./compose"); var encode_1 = require("./encode"); var geometry_1 = require("../../../../mol-math/geometry"); var linear_algebra_1 = require("../../../../mol-math/linear-algebra"); var mol_util_1 = require("../../../../mol-util"); var typed_array_1 = require("../../../../mol-io/common/typed-array"); var config_1 = require("../../config"); var file_handle_1 = require("../../../common/file-handle"); function execute(params, outputProvider) { return tslib_1.__awaiter(this, void 0, void 0, function () { var start, guid, sourceFile, _a, e_1; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: start = getTime(); state_1.State.pendingQueries++; guid = mol_util_1.UUID.create22(); params.detail = Math.min(Math.max(0, params.detail | 0), config_1.LimitsConfig.maxOutputSizeInVoxelCountByPrecisionLevel.length - 1); console_logger_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 = file_handle_1.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(); console_logger_1.ConsoleLogger.errorId(guid, e_1); return [2 /*return*/, false]; case 5: if (sourceFile) sourceFile.close(); console_logger_1.ConsoleLogger.logId(guid, 'Time', "".concat(Math.round(getTime() - start), "ms")); state_1.State.pendingQueries--; return [7 /*endfinally*/]; case 6: return [2 /*return*/]; } }); }); } exports.execute = execute; 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 tslib_1.__awaiter(this, void 0, void 0, function () { var _a, header, dataOffset, origin, dimensions; return tslib_1.__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: geometry_1.SpacegroupCell.create(header.spacegroup.number, linear_algebra_1.Vec3.ofArray(header.spacegroup.size), linear_algebra_1.Vec3.scale(linear_algebra_1.Vec3.zero(), linear_algebra_1.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 = (0, identify_1.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 = config_1.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 <= config_1.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] = (0, typed_array_1.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] > config_1.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 tslib_1.__awaiter(this, void 0, void 0, function () { var output, data, query, e_2, query; return tslib_1.__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*/, (0, compose_1.compose)(query)]; case 3: // Step 3b: Compose the result data _a.sent(); _a.label = 4; case 4: // Step 4: Encode the result output = outputProvider(); (0, encode_1.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(); (0, encode_1.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); } }