molstar
Version:
A comprehensive macromolecular library.
490 lines • 23.9 kB
JavaScript
"use strict";
/**
* Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ClashesProvider = exports.ValidationReportProvider = exports.ValidationReportParams = exports.ValidationReport = void 0;
var tslib_1 = require("tslib");
var param_definition_1 = require("../../../mol-util/param-definition");
var structure_1 = require("../../../mol-model/structure");
var custom_model_property_1 = require("../../../mol-model-props/common/custom-model-property");
var model_1 = require("../../../mol-model/structure/model");
var graph_1 = require("../../../mol-math/graph");
var custom_structure_property_1 = require("../../../mol-model-props/common/custom-structure-property");
var inter_unit_graph_1 = require("../../../mol-math/graph/inter-unit-graph");
var int_1 = require("../../../mol-data/int");
var array_1 = require("../../../mol-util/array");
var common_1 = require("../../../mol-math/linear-algebra/3d/common");
var linear_algebra_1 = require("../../../mol-math/linear-algebra");
var compiler_1 = require("../../../mol-script/runtime/query/compiler");
var symbol_1 = require("../../../mol-script/language/symbol");
var type_1 = require("../../../mol-script/language/type");
var assets_1 = require("../../../mol-util/assets");
var custom_property_1 = require("../../../mol-model/custom-property");
var ValidationReport;
(function (ValidationReport) {
var Tag;
(function (Tag) {
Tag["DensityFit"] = "rcsb-density-fit";
Tag["GeometryQuality"] = "rcsb-geometry-quality";
Tag["RandomCoilIndex"] = "rcsb-random-coil-index";
Tag["Clashes"] = "rcsb-clashes";
})(Tag = ValidationReport.Tag || (ValidationReport.Tag = {}));
ValidationReport.DefaultBaseUrl = '//ftp.rcsb.org/pub/pdb/validation_reports';
function getEntryUrl(pdbId, baseUrl) {
var id = pdbId.toLowerCase();
return baseUrl + "/" + id.substr(1, 2) + "/" + id + "/" + id + "_validation.xml.gz";
}
ValidationReport.getEntryUrl = getEntryUrl;
function isApplicable(model) {
return !!model && model_1.Model.isFromPdbArchive(model);
}
ValidationReport.isApplicable = isApplicable;
function fromXml(xml, model) {
return parseValidationReportXml(xml, model);
}
ValidationReport.fromXml = fromXml;
function fetch(ctx, model, props) {
return (0, tslib_1.__awaiter)(this, void 0, void 0, function () {
var url, xml;
return (0, tslib_1.__generator)(this, function (_a) {
switch (_a.label) {
case 0:
url = assets_1.Asset.getUrlAsset(ctx.assetManager, getEntryUrl(model.entryId, props.baseUrl));
return [4 /*yield*/, ctx.assetManager.resolve(url, 'xml').runInContext(ctx.runtime)];
case 1:
xml = _a.sent();
return [2 /*return*/, { value: fromXml(xml.data, model), assets: [xml] }];
}
});
});
}
ValidationReport.fetch = fetch;
function open(ctx, model, props) {
return (0, tslib_1.__awaiter)(this, void 0, void 0, function () {
var xml;
return (0, tslib_1.__generator)(this, function (_a) {
switch (_a.label) {
case 0:
if (props.input === null)
throw new Error('No file given');
return [4 /*yield*/, ctx.assetManager.resolve(props.input, 'xml').runInContext(ctx.runtime)];
case 1:
xml = _a.sent();
return [2 /*return*/, { value: fromXml(xml.data, model), assets: [xml] }];
}
});
});
}
ValidationReport.open = open;
function obtain(ctx, model, props) {
return (0, tslib_1.__awaiter)(this, void 0, void 0, function () {
return (0, tslib_1.__generator)(this, function (_a) {
switch (props.source.name) {
case 'file': return [2 /*return*/, open(ctx, model, props.source.params)];
case 'server': return [2 /*return*/, fetch(ctx, model, props.source.params)];
}
return [2 /*return*/];
});
});
}
ValidationReport.obtain = obtain;
ValidationReport.symbols = {
hasClash: compiler_1.QuerySymbolRuntime.Dynamic((0, symbol_1.CustomPropSymbol)('rcsb', 'validation-report.has-clash', type_1.Type.Bool), function (ctx) {
var _a = ctx.element, unit = _a.unit, element = _a.element;
if (!structure_1.Unit.isAtomic(unit))
return 0;
var validationReport = exports.ValidationReportProvider.get(unit.model).value;
return validationReport && validationReport.clashes.getVertexEdgeCount(element) > 0;
}),
issueCount: compiler_1.QuerySymbolRuntime.Dynamic((0, symbol_1.CustomPropSymbol)('rcsb', 'validation-report.issue-count', type_1.Type.Num), function (ctx) {
var _a;
var _b = ctx.element, unit = _b.unit, element = _b.element;
if (!structure_1.Unit.isAtomic(unit))
return 0;
var validationReport = exports.ValidationReportProvider.get(unit.model).value;
return ((_a = validationReport === null || validationReport === void 0 ? void 0 : validationReport.geometryIssues.get(unit.residueIndex[element])) === null || _a === void 0 ? void 0 : _a.size) || 0;
}),
};
})(ValidationReport || (ValidationReport = {}));
exports.ValidationReport = ValidationReport;
var FileSourceParams = {
input: param_definition_1.ParamDefinition.File({ accept: '.xml,.gz,.zip' })
};
var ServerSourceParams = {
baseUrl: param_definition_1.ParamDefinition.Text(ValidationReport.DefaultBaseUrl, { description: 'Base URL to directory tree' })
};
exports.ValidationReportParams = {
source: param_definition_1.ParamDefinition.MappedStatic('server', {
'file': param_definition_1.ParamDefinition.Group(FileSourceParams, { label: 'File', isFlat: true }),
'server': param_definition_1.ParamDefinition.Group(ServerSourceParams, { label: 'Server', isFlat: true }),
}, { options: [['file', 'File'], ['server', 'Server']] })
};
exports.ValidationReportProvider = custom_model_property_1.CustomModelProperty.createProvider({
label: 'Validation Report',
descriptor: (0, custom_property_1.CustomPropertyDescriptor)({
name: 'rcsb_validation_report',
symbols: ValidationReport.symbols
}),
type: 'dynamic',
defaultParams: exports.ValidationReportParams,
getParams: function (data) { return exports.ValidationReportParams; },
isApplicable: function (data) { return ValidationReport.isApplicable(data); },
obtain: function (ctx, data, props) { return (0, tslib_1.__awaiter)(void 0, void 0, void 0, function () {
var p;
return (0, tslib_1.__generator)(this, function (_a) {
switch (_a.label) {
case 0:
p = (0, tslib_1.__assign)((0, tslib_1.__assign)({}, param_definition_1.ParamDefinition.getDefaultValues(exports.ValidationReportParams)), props);
return [4 /*yield*/, ValidationReport.obtain(ctx, data, p)];
case 1: return [2 /*return*/, _a.sent()];
}
});
}); }
});
function createInterUnitClashes(structure, clashes) {
var builder = new inter_unit_graph_1.InterUnitGraph.Builder();
var a = clashes.a, b = clashes.b, _a = clashes.edgeProps, id = _a.id, magnitude = _a.magnitude, distance = _a.distance;
var pA = (0, linear_algebra_1.Vec3)(), pB = (0, linear_algebra_1.Vec3)();
structure_1.Structure.eachUnitPair(structure, function (unitA, unitB) {
var elementsA = unitA.elements;
var elementsB = unitB.elements;
builder.startUnitPair(unitA.id, unitB.id);
for (var i = 0, il = clashes.edgeCount * 2; i < il; ++i) {
// TODO create lookup
var indexA = int_1.SortedArray.indexOf(elementsA, a[i]);
var indexB = int_1.SortedArray.indexOf(elementsB, b[i]);
if (indexA !== -1 && indexB !== -1) {
unitA.conformation.position(a[i], pA);
unitB.conformation.position(b[i], pB);
// check actual distance to avoid clashes between unrelated chain instances
if ((0, common_1.equalEps)(distance[i], linear_algebra_1.Vec3.distance(pA, pB), 0.1)) {
builder.add(indexA, indexB, {
id: id[i],
magnitude: magnitude[i],
distance: distance[i],
});
}
}
}
builder.finishUnitPair();
}, {
maxRadius: (0, array_1.arrayMax)(clashes.edgeProps.distance),
validUnit: function (unit) { return structure_1.Unit.isAtomic(unit); },
validUnitPair: function (unitA, unitB) { return unitA.model === unitB.model; }
});
return new inter_unit_graph_1.InterUnitGraph(builder.getMap());
}
function createIntraUnitClashes(unit, clashes) {
var aIndices = [];
var bIndices = [];
var ids = [];
var magnitudes = [];
var distances = [];
var pA = (0, linear_algebra_1.Vec3)(), pB = (0, linear_algebra_1.Vec3)();
var elements = unit.elements;
var a = clashes.a, b = clashes.b, edgeCount = clashes.edgeCount, edgeProps = clashes.edgeProps;
for (var i = 0, il = edgeCount * 2; i < il; ++i) {
// TODO create lookup
var indexA = int_1.SortedArray.indexOf(elements, a[i]);
var indexB = int_1.SortedArray.indexOf(elements, b[i]);
if (indexA !== -1 && indexB !== -1) {
unit.conformation.position(a[i], pA);
unit.conformation.position(b[i], pB);
// check actual distance to avoid clashes between unrelated chain instances
if ((0, common_1.equalEps)(edgeProps.distance[i], linear_algebra_1.Vec3.distance(pA, pB), 0.1)) {
aIndices.push(indexA);
bIndices.push(indexB);
ids.push(edgeProps.id[i]);
magnitudes.push(edgeProps.magnitude[i]);
distances.push(edgeProps.distance[i]);
}
}
}
var builder = new graph_1.IntAdjacencyGraph.EdgeBuilder(elements.length, aIndices, bIndices);
var id = new Int32Array(builder.slotCount);
var magnitude = new Float32Array(builder.slotCount);
var distance = new Float32Array(builder.slotCount);
for (var i = 0, _i = builder.edgeCount; i < _i; i++) {
builder.addNextEdge();
builder.assignProperty(id, ids[i]);
builder.assignProperty(magnitude, magnitudes[i]);
builder.assignProperty(distance, distances[i]);
}
return builder.createGraph({ id: id, magnitude: magnitude, distance: distance });
}
function createClashes(structure, clashes) {
var intraUnit = int_1.IntMap.Mutable();
for (var i = 0, il = structure.unitSymmetryGroups.length; i < il; ++i) {
var group = structure.unitSymmetryGroups[i];
if (!structure_1.Unit.isAtomic(group.units[0]))
continue;
var intraClashes = createIntraUnitClashes(group.units[0], clashes);
for (var j = 0, jl = group.units.length; j < jl; ++j) {
intraUnit.set(group.units[j].id, intraClashes);
}
}
return {
interUnit: createInterUnitClashes(structure, clashes),
intraUnit: intraUnit
};
}
exports.ClashesProvider = custom_structure_property_1.CustomStructureProperty.createProvider({
label: 'Clashes',
descriptor: (0, custom_property_1.CustomPropertyDescriptor)({
name: 'rcsb_clashes',
// TODO `cifExport` and `symbol`
}),
type: 'local',
defaultParams: {},
getParams: function (data) { return ({}); },
isApplicable: function (data) { return true; },
obtain: function (ctx, data) { return (0, tslib_1.__awaiter)(void 0, void 0, void 0, function () {
var validationReport;
return (0, tslib_1.__generator)(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, exports.ValidationReportProvider.attach(ctx, data.models[0])];
case 1:
_a.sent();
validationReport = exports.ValidationReportProvider.get(data.models[0]).value;
return [2 /*return*/, {
value: createClashes(data, validationReport.clashes)
}];
}
});
}); }
});
//
function getItem(a, name) {
var item = a.getNamedItem(name);
return item !== null ? item.value : '';
}
function hasAttr(a, name, value) {
var item = a.getNamedItem(name);
return item !== null && item.value === value;
}
function getMogInfo(a) {
return {
mean: parseFloat(getItem(a, 'mean')),
obs: parseFloat(getItem(a, 'obsval')),
stdev: parseFloat(getItem(a, 'stdev')),
z: parseFloat(getItem(a, 'Zscore')),
};
}
function getMolInfo(a) {
return {
mean: parseFloat(getItem(a, 'mean')),
obs: parseFloat(getItem(a, 'obs')),
stdev: parseFloat(getItem(a, 'stdev')),
z: parseInt(getItem(a, 'z')),
};
}
function addIndex(index, element, map) {
if (map.has(element))
map.get(element).push(index);
else
map.set(element, [index]);
}
function ClashesBuilder(elementsCount) {
var aIndices = [];
var bIndices = [];
var ids = [];
var magnitudes = [];
var distances = [];
var seen = new Map();
return {
add: function (element, id, magnitude, distance, isSymop) {
var hash = id + "|" + (isSymop ? 's' : '');
var other = seen.get(hash);
if (other !== undefined) {
aIndices[aIndices.length] = element;
bIndices[bIndices.length] = other;
ids[ids.length] = id;
magnitudes[magnitudes.length] = magnitude;
distances[distances.length] = distance;
}
else {
seen.set(hash, element);
}
},
get: function () {
var builder = new graph_1.IntAdjacencyGraph.EdgeBuilder(elementsCount, aIndices, bIndices);
var id = new Int32Array(builder.slotCount);
var magnitude = new Float32Array(builder.slotCount);
var distance = new Float32Array(builder.slotCount);
for (var i = 0, _i = builder.edgeCount; i < _i; i++) {
builder.addNextEdge();
builder.assignProperty(id, ids[i]);
builder.assignProperty(magnitude, magnitudes[i]);
builder.assignProperty(distance, distances[i]);
}
return builder.createGraph({ id: id, magnitude: magnitude, distance: distance });
}
};
}
function parseValidationReportXml(xml, model) {
var rsrz = new Map();
var rscc = new Map();
var rci = new Map();
var geometryIssues = new Map();
var bondOutliers = {
index: new Map(),
data: []
};
var angleOutliers = {
index: new Map(),
data: []
};
var clashesBuilder = ClashesBuilder(model.atomicHierarchy.atoms._rowCount);
var index = model.atomicHierarchy.index;
var entries = xml.getElementsByTagName('Entry');
if (entries.length === 1) {
var chemicalShiftLists = entries[0].getElementsByTagName('chemical_shift_list');
if (chemicalShiftLists.length === 1) {
var randomCoilIndices = chemicalShiftLists[0].getElementsByTagName('random_coil_index');
for (var j = 0, jl = randomCoilIndices.length; j < jl; ++j) {
var attributes = randomCoilIndices[j].attributes;
var value = parseFloat(getItem(attributes, 'value'));
var auth_asym_id = getItem(attributes, 'chain');
var auth_comp_id = getItem(attributes, 'rescode');
var auth_seq_id = parseInt(getItem(attributes, 'resnum'));
var rI = index.findResidueAuth({ auth_asym_id: auth_asym_id, auth_comp_id: auth_comp_id, auth_seq_id: auth_seq_id });
if (rI !== -1)
rci.set(rI, value);
}
}
}
var groups = xml.getElementsByTagName('ModelledSubgroup');
for (var i = 0, il = groups.length; i < il; ++i) {
var g = groups[i];
var ga = g.attributes;
var pdbx_PDB_model_num = parseInt(getItem(ga, 'model'));
if (model.modelNum !== pdbx_PDB_model_num)
continue;
var auth_asym_id = getItem(ga, 'chain');
var auth_comp_id = getItem(ga, 'resname');
var auth_seq_id = parseInt(getItem(ga, 'resnum'));
var pdbx_PDB_ins_code = getItem(ga, 'icode').trim() || undefined;
var label_alt_id = getItem(ga, 'altcode').trim() || undefined;
var rI = index.findResidueAuth({ auth_asym_id: auth_asym_id, auth_comp_id: auth_comp_id, auth_seq_id: auth_seq_id, pdbx_PDB_ins_code: pdbx_PDB_ins_code });
// continue if no residue index is found
if (rI === -1)
continue;
if (ga.getNamedItem('rsrz') !== null)
rsrz.set(rI, parseFloat(getItem(ga, 'rsrz')));
if (ga.getNamedItem('rscc') !== null)
rscc.set(rI, parseFloat(getItem(ga, 'rscc')));
var isPolymer = getItem(ga, 'seq') !== '.';
var issues = new Set();
if (isPolymer) {
var molBondOutliers = g.getElementsByTagName('bond-outlier');
if (molBondOutliers.length)
issues.add('bond-outlier');
for (var j = 0, jl = molBondOutliers.length; j < jl; ++j) {
var bo = molBondOutliers[j].attributes;
var idx = bondOutliers.data.length;
var atomA = index.findAtomOnResidue(rI, getItem(bo, 'atom0'));
var atomB = index.findAtomOnResidue(rI, getItem(bo, 'atom1'));
addIndex(idx, atomA, bondOutliers.index);
addIndex(idx, atomB, bondOutliers.index);
bondOutliers.data.push((0, tslib_1.__assign)({ tag: 'bond-outlier', atomA: atomA, atomB: atomB }, getMolInfo(bo)));
}
var molAngleOutliers = g.getElementsByTagName('angle-outlier');
if (molAngleOutliers.length)
issues.add('angle-outlier');
for (var j = 0, jl = molAngleOutliers.length; j < jl; ++j) {
var ao = molAngleOutliers[j].attributes;
var idx = bondOutliers.data.length;
var atomA = index.findAtomOnResidue(rI, getItem(ao, 'atom0'));
var atomB = index.findAtomOnResidue(rI, getItem(ao, 'atom1'));
var atomC = index.findAtomOnResidue(rI, getItem(ao, 'atom2'));
addIndex(idx, atomA, angleOutliers.index);
addIndex(idx, atomB, angleOutliers.index);
addIndex(idx, atomC, angleOutliers.index);
angleOutliers.data.push((0, tslib_1.__assign)({ tag: 'angle-outlier', atomA: atomA, atomB: atomB, atomC: atomC }, getMolInfo(ao)));
}
var planeOutliers = g.getElementsByTagName('plane-outlier');
if (planeOutliers.length)
issues.add('plane-outlier');
if (hasAttr(ga, 'rota', 'OUTLIER'))
issues.add('rotamer-outlier');
if (hasAttr(ga, 'rama', 'OUTLIER'))
issues.add('ramachandran-outlier');
if (hasAttr(ga, 'RNApucker', 'outlier'))
issues.add('RNApucker-outlier');
}
else {
var mogBondOutliers = g.getElementsByTagName('mog-bond-outlier');
if (mogBondOutliers.length)
issues.add('mog-bond-outlier');
for (var j = 0, jl = mogBondOutliers.length; j < jl; ++j) {
var mbo = mogBondOutliers[j].attributes;
var atoms = getItem(mbo, 'atoms').split(',');
var idx = bondOutliers.data.length;
var atomA = index.findAtomOnResidue(rI, atoms[0]);
var atomB = index.findAtomOnResidue(rI, atoms[1]);
addIndex(idx, atomA, bondOutliers.index);
addIndex(idx, atomB, bondOutliers.index);
bondOutliers.data.push((0, tslib_1.__assign)({ tag: 'mog-bond-outlier', atomA: atomA, atomB: atomB }, getMogInfo(mbo)));
}
var mogAngleOutliers = g.getElementsByTagName('mog-angle-outlier');
if (mogAngleOutliers.length)
issues.add('mog-angle-outlier');
for (var j = 0, jl = mogAngleOutliers.length; j < jl; ++j) {
var mao = mogAngleOutliers[j].attributes;
var atoms = getItem(mao, 'atoms').split(',');
var idx = angleOutliers.data.length;
var atomA = index.findAtomOnResidue(rI, atoms[0]);
var atomB = index.findAtomOnResidue(rI, atoms[1]);
var atomC = index.findAtomOnResidue(rI, atoms[2]);
addIndex(idx, atomA, angleOutliers.index);
addIndex(idx, atomB, angleOutliers.index);
addIndex(idx, atomC, angleOutliers.index);
angleOutliers.data.push((0, tslib_1.__assign)({ tag: 'mog-angle-outlier', atomA: atomA, atomB: atomB, atomC: atomC }, getMogInfo(mao)));
}
}
var clashes_1 = g.getElementsByTagName('clash');
if (clashes_1.length)
issues.add('clash');
for (var j = 0, jl = clashes_1.length; j < jl; ++j) {
var ca = clashes_1[j].attributes;
var id = parseInt(getItem(ca, 'cid'));
var magnitude = parseFloat(getItem(ca, 'clashmag'));
var distance = parseFloat(getItem(ca, 'dist'));
var label_atom_id = getItem(ca, 'atom');
var element = index.findAtomOnResidue(rI, label_atom_id, label_alt_id);
if (element !== -1) {
clashesBuilder.add(element, id, magnitude, distance, false);
}
}
var symmClashes = g.getElementsByTagName('symm-clash');
if (symmClashes.length)
issues.add('symm-clash');
for (var j = 0, jl = symmClashes.length; j < jl; ++j) {
var sca = symmClashes[j].attributes;
var id = parseInt(getItem(sca, 'scid'));
var magnitude = parseFloat(getItem(sca, 'clashmag'));
var distance = parseFloat(getItem(sca, 'dist'));
var label_atom_id = getItem(sca, 'atom');
var element = index.findAtomOnResidue(rI, label_atom_id, label_alt_id);
if (element !== -1) {
clashesBuilder.add(element, id, magnitude, distance, true);
}
}
geometryIssues.set(rI, issues);
}
var clashes = clashesBuilder.get();
var validationReport = {
rsrz: rsrz,
rscc: rscc,
rci: rci,
geometryIssues: geometryIssues,
bondOutliers: bondOutliers,
angleOutliers: angleOutliers,
clashes: clashes
};
return validationReport;
}
//# sourceMappingURL=prop.js.map