molstar
Version:
A comprehensive macromolecular library.
169 lines • 7.42 kB
JavaScript
/**
* Copyright (c) 2017 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 { __awaiter, __generator } from "tslib";
import { Column } from '../../../mol-data/db';
import { Tokenizer } from '../common/text/tokenizer';
import { FixedColumnProvider as FixedColumn } from '../common/text/column/fixed';
import { ReaderResult as Result } from '../result';
import { Task } from '../../../mol-task';
function createEmptyHeader() {
return {
title: '',
timeInPs: 0,
hasVelocities: false,
precision: { position: 0, velocity: 0 },
box: [0, 0, 0]
};
}
function State(tokenizer, runtimeCtx) {
return {
tokenizer: tokenizer,
header: createEmptyHeader(),
numberOfAtoms: 0,
runtimeCtx: runtimeCtx
};
}
/**
* title string (free format string, optional time in ps after 't=')
*/
function handleTitleString(state) {
var tokenizer = state.tokenizer, header = state.header;
var line = Tokenizer.readLine(tokenizer);
// skip potential empty lines...
if (line.trim().length === 0) {
line = Tokenizer.readLine(tokenizer);
}
var timeOffset = line.lastIndexOf('t=');
if (timeOffset >= 0) {
header.timeInPs = parseFloat(line.substring(timeOffset + 2));
header.title = line.substring(0, timeOffset).trim();
if (header.title && header.title[header.title.length - 1] === ',') {
header.title = header.title.substring(0, header.title.length - 1);
}
}
else {
header.title = line;
}
}
/**
* number of atoms (free format integer)
*/
function handleNumberOfAtoms(state) {
var tokenizer = state.tokenizer;
Tokenizer.markLine(tokenizer);
var line = Tokenizer.getTokenString(tokenizer);
state.numberOfAtoms = parseInt(line);
}
/**
* This format is fixed, ie. all columns are in a fixed position.
* Optionally (for now only yet with trjconv) you can write gro files
* with any number of decimal places, the format will then be n+5
* positions with n decimal places (n+1 for velocities) in stead
* of 8 with 3 (with 4 for velocities). Upon reading, the precision
* will be inferred from the distance between the decimal points
* (which will be n+5). Columns contain the following information
* (from left to right):
* residue number (5 positions, integer)
* residue name (5 characters)
* atom name (5 characters)
* atom number (5 positions, integer)
* position (in nm, x y z in 3 columns, each 8 positions with 3 decimal places)
* velocity (in nm/ps (or km/s), x y z in 3 columns, each 8 positions with 4 decimal places)
*/
function handleAtoms(state) {
return __awaiter(this, void 0, void 0, function () {
var tokenizer, numberOfAtoms, lines, positionSample, precisions, hasVelocities, pO, pW, vO, vW, col, undef, ret;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
tokenizer = state.tokenizer, numberOfAtoms = state.numberOfAtoms;
return [4 /*yield*/, Tokenizer.readLinesAsync(tokenizer, numberOfAtoms, state.runtimeCtx, 100000)];
case 1:
lines = _a.sent();
positionSample = tokenizer.data.substring(lines.indices[0], lines.indices[1]).substring(20);
precisions = positionSample.match(/\.\d+/g);
hasVelocities = precisions.length === 6;
state.header.hasVelocities = hasVelocities;
state.header.precision.position = precisions[0].length - 1;
state.header.precision.velocity = hasVelocities ? precisions[3].length - 1 : 0;
pO = 20;
pW = state.header.precision.position + 5;
vO = pO + 3 * pW;
vW = state.header.precision.velocity + 4;
col = FixedColumn(lines);
undef = Column.Undefined(state.numberOfAtoms, Column.Schema.float);
ret = {
count: state.numberOfAtoms,
residueNumber: col(0, 5, Column.Schema.int),
residueName: col(5, 5, Column.Schema.str),
atomName: col(10, 5, Column.Schema.str),
atomNumber: col(15, 5, Column.Schema.int),
x: col(pO, pW, Column.Schema.float),
y: col(pO + pW, pW, Column.Schema.float),
z: col(pO + 2 * pW, pW, Column.Schema.float),
vx: hasVelocities ? col(vO, vW, Column.Schema.float) : undef,
vy: hasVelocities ? col(vO + vW, vW, Column.Schema.float) : undef,
vz: hasVelocities ? col(vO + 2 * vW, vW, Column.Schema.float) : undef,
};
return [2 /*return*/, ret];
}
});
});
}
/**
* box vectors (free format, space separated reals), values:
* v1(x) v2(y) v3(z) v1(y) v1(z) v2(x) v2(z) v3(x) v3(y),
* the last 6 values may be omitted (they will be set to zero).
* Gromacs only supports boxes with v1(y)=v1(z)=v2(z)=0.
*/
function handleBoxVectors(state) {
var tokenizer = state.tokenizer;
var values = Tokenizer.readLine(tokenizer).trim().split(/\s+/g);
state.header.box = [+values[0], +values[1], +values[2]];
}
function parseInternal(data, ctx) {
return __awaiter(this, void 0, void 0, function () {
var tokenizer, structures, state, atoms, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
tokenizer = Tokenizer(data);
return [4 /*yield*/, ctx.update({ message: 'Parsing...', current: 0, max: data.length })];
case 1:
_a.sent();
structures = [];
_a.label = 2;
case 2:
if (!(tokenizer.position < data.length)) return [3 /*break*/, 4];
state = State(tokenizer, ctx);
handleTitleString(state);
handleNumberOfAtoms(state);
return [4 /*yield*/, handleAtoms(state)];
case 3:
atoms = _a.sent();
handleBoxVectors(state);
structures.push({ header: state.header, atoms: atoms });
return [3 /*break*/, 2];
case 4:
result = { structures: structures };
return [2 /*return*/, Result.success(result)];
}
});
});
}
export function parseGRO(data) {
var _this = this;
return Task.create('Parse GRO', function (ctx) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, parseInternal(data, ctx)];
case 1: return [2 /*return*/, _a.sent()];
}
});
}); });
}
//# sourceMappingURL=parser.js.map