UNPKG

molstar

Version:

A comprehensive macromolecular library.

490 lines 23.9 kB
"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