UNPKG

@rcsb/rcsb-saguaro-3d

Version:
357 lines (356 loc) 21.6 kB
"use strict"; /* * Copyright (c) 2021 RCSB PDB and contributors, licensed under MIT, See LICENSE file for more info. * @author Joan Segura Mora <joan.segura@rcsb.org> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.AlignmentRepresentationPresetProvider = void 0; const tslib_1 = require("tslib"); const representation_preset_1 = require("molstar/lib/mol-plugin-state/builder/structure/representation-preset"); const mol_state_1 = require("molstar/lib/mol-state"); const structure_1 = require("molstar/lib/mol-model/structure"); const builder_1 = require("molstar/lib/mol-script/language/builder"); const uniqid_1 = tslib_1.__importDefault(require("uniqid")); const plddt_1 = require("molstar/lib/extensions/model-archive/quality-assessment/color/plddt"); const selection_1 = require("@rcsb/rcsb-molstar/build/src/viewer/helpers/selection"); const param_definition_1 = require("molstar/lib/mol-util/param-definition"); const loci_1 = require("molstar/lib/mol-model/loci"); const superposition_1 = require("molstar/lib/mol-model/structure/structure/util/superposition"); const linear_algebra_1 = require("molstar/lib/mol-math/linear-algebra"); const TagDelimiter_1 = require("@rcsb/rcsb-api-tools/lib/RcsbUtils/TagDelimiter"); const AlignmentMapper_1 = require("../../../../Utils/AlignmentMapper"); const compiler_1 = require("molstar/lib/mol-script/runtime/query/compiler"); var reprBuilder = representation_preset_1.StructureRepresentationPresetProvider.reprBuilder; const mmcif_1 = require("molstar/lib/mol-model-formats/structure/mmcif"); const transform_1 = require("molstar/lib/mol-state/transform"); const model_1 = require("molstar/lib/mol-plugin-state/transforms/model"); var updateFocusRepr = representation_preset_1.StructureRepresentationPresetProvider.updateFocusRepr; const FocusColoring_1 = require("./FocusTheme/FocusColoring"); const prop_1 = require("molstar/lib/extensions/model-archive/quality-assessment/prop"); let refData = undefined; let refParams = undefined; exports.AlignmentRepresentationPresetProvider = (0, representation_preset_1.StructureRepresentationPresetProvider)({ id: 'alignment-to-reference', display: { name: 'Alignment to Reference' }, isApplicable: (structureRef, plugin) => true, params: (structureRef, plugin) => ({ pdb: param_definition_1.ParamDefinition.Value(undefined), targetAlignment: param_definition_1.ParamDefinition.Value(undefined), transform: param_definition_1.ParamDefinition.Value(undefined) }), apply: (structureRef, params, plugin) => tslib_1.__awaiter(void 0, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g, _h, _j; const structureCell = mol_state_1.StateObjectRef.resolveAndCheck(plugin.state.data, structureRef); if (!structureCell) return {}; const structure = structureCell.obj.data; const entryId = (_a = params.pdb) === null || _a === void 0 ? void 0 : _a.entryId; const entityId = params.pdb && "entityId" in params.pdb ? (_b = params.pdb) === null || _b === void 0 ? void 0 : _b.entityId : undefined; const instanceId = params.pdb && "instanceId" in params.pdb ? (_c = params.pdb) === null || _c === void 0 ? void 0 : _c.instanceId : undefined; const l = structure_1.StructureElement.Location.create(structure); let alignedEntityId; let alignedAsymId; let alignedOperatorName; let alignedType; const componentMap = {}; const representationMap = {}; for (const unit of structure.units) { structure_1.StructureElement.Location.set(l, structure, unit, unit.elements[0]); if (structure_1.StructureProperties.chain.label_entity_id(l) == entityId || structure_1.StructureProperties.chain.label_asym_id(l) == instanceId) { alignedEntityId = structure_1.StructureProperties.chain.label_entity_id(l); alignedAsymId = structure_1.StructureProperties.chain.label_asym_id(l); alignedOperatorName = structure_1.StructureProperties.unit.operator_name(l); alignedType = structure_1.StructureProperties.entity.type(l); const alignedOperators = structure_1.StructureProperties.unit.pdbx_struct_oper_list_ids(l); if (alignedOperators.length == 0) alignedOperators.push("0"); if (alignedType != "polymer") return {}; if (plugin.managers.structure.hierarchy.current.structures.length == 1) { refParams = { entryId: entryId, labelAsymId: alignedAsymId, operatorName: alignedOperatorName, targetAlignment: params.targetAlignment }; } if (refParams && params.pdb && !params.transform) { yield structuralAlignment(plugin, refParams, { entryId: entryId, labelAsymId: alignedAsymId, operatorName: alignedOperatorName, targetAlignment: params.targetAlignment }, structure); } else if ((_d = params.transform) === null || _d === void 0 ? void 0 : _d[0].transform) { yield matrixAlign(plugin, structureRef, (_e = params.transform) === null || _e === void 0 ? void 0 : _e[0].transform); } const comp = yield plugin.builders.structure.tryCreateComponentFromExpression(structureCell, builder_1.MolScriptBuilder.struct.generator.atomGroups({ 'chain-test': builder_1.MolScriptBuilder.core.logic.and([ builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.ammp('label_asym_id'), alignedAsymId]), builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.acp('operatorName'), alignedOperatorName]) ]) }), (0, uniqid_1.default)(`${entryId}${TagDelimiter_1.TagDelimiter.entity}${alignedEntityId}${TagDelimiter_1.TagDelimiter.instance}${alignedAsymId}${TagDelimiter_1.TagDelimiter.entity}${alignedOperators.join(",")}`), { label: `${entryId}${TagDelimiter_1.TagDelimiter.entity}${alignedEntityId}${TagDelimiter_1.TagDelimiter.instance}${alignedAsymId}${TagDelimiter_1.TagDelimiter.assembly}${alignedOperators.join(",")}${TagDelimiter_1.TagDelimiter.assembly}${alignedType}` }); componentMap["aligned"] = comp; //TODO This needs to be called after tryCreateComponentFromExpression const { update, builder } = reprBuilder(plugin, { ignoreHydrogens: true, ignoreLight: false, quality: "auto" }); representationMap["aligned"] = builder.buildRepresentation(update, comp, { color: structure.models.some(m => prop_1.QualityAssessment.isApplicable(m, 'pLDDT')) ? plddt_1.PLDDTConfidenceColorThemeProvider.name : "chain-id", type: "cartoon" }); yield update.commit({ revertOnError: false }); break; } } const expressions = []; const asymObserved = {}; for (const unit of structure.units) { structure_1.StructureElement.Location.set(l, structure, unit, unit.elements[0]); const asymId = structure_1.StructureProperties.chain.label_asym_id(l); const operatorName = structure_1.StructureProperties.unit.operator_name(l); if (asymId == alignedAsymId && operatorName == alignedOperatorName) continue; if (asymObserved[`${asymId}${TagDelimiter_1.TagDelimiter.assembly}${operatorName}`]) continue; asymObserved[`${asymId}${TagDelimiter_1.TagDelimiter.assembly}${operatorName}`] = true; const type = structure_1.StructureProperties.entity.type(l); if (type == "polymer") { expressions.push(builder_1.MolScriptBuilder.core.logic.and([ builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.ammp('label_asym_id'), asymId]), builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.acp('operatorName'), operatorName]) ])); } } const compId = `${entryId}${TagDelimiter_1.TagDelimiter.entity}${alignedEntityId}${TagDelimiter_1.TagDelimiter.assembly}${alignedType}`; const comp = yield plugin.builders.structure.tryCreateComponentFromExpression(structureCell, builder_1.MolScriptBuilder.struct.generator.atomGroups({ 'chain-test': builder_1.MolScriptBuilder.core.logic.or(expressions) }), (0, uniqid_1.default)(compId), { label: compId }); componentMap["polymer"] = comp; const { update, builder } = reprBuilder(plugin, { ignoreHydrogens: true, ignoreLight: false, quality: "auto" }); representationMap["polymer"] = builder.buildRepresentation(update, comp, { color: structure.models.some(m => prop_1.QualityAssessment.isApplicable(m, 'pLDDT')) ? plddt_1.PLDDTConfidenceColorThemeProvider.name : "chain-id", type: "cartoon" }, { initialState: { isHidden: true } }); if ((_f = comp === null || comp === void 0 ? void 0 : comp.cell) === null || _f === void 0 ? void 0 : _f.state) { transform_1.StateTransform.assignState((_g = comp === null || comp === void 0 ? void 0 : comp.cell) === null || _g === void 0 ? void 0 : _g.state, { isHidden: true }); } yield update.commit({ revertOnError: false }); for (const expression of (0, selection_1.createSelectionExpressions)(entryId)) { if (expression.tag == "polymer") continue; const comp = yield plugin.builders.structure.tryCreateComponentFromExpression(structureCell, expression.expression, (0, uniqid_1.default)(`${entryId}${TagDelimiter_1.TagDelimiter.entity}${alignedEntityId}${TagDelimiter_1.TagDelimiter.assembly}${expression.tag}`), { label: `${entryId}${TagDelimiter_1.TagDelimiter.entity}${alignedEntityId}${TagDelimiter_1.TagDelimiter.assembly}${expression.tag}` }); componentMap[expression.tag] = comp; //TODO This needs to be called after tryCreateComponentFromExpression const { update, builder } = reprBuilder(plugin, { ignoreHydrogens: true, ignoreLight: false, quality: "auto" }); representationMap[expression.tag] = builder.buildRepresentation(update, comp, { type: expression.type }, { initialState: { isHidden: true } }); if (expression.type !== "ball-and-stick") representationMap[expression.tag + "#ball-and-stick"] = builder.buildRepresentation(update, comp, { type: "ball-and-stick" }, { initialState: { isHidden: true } }); if ((_h = comp === null || comp === void 0 ? void 0 : comp.cell) === null || _h === void 0 ? void 0 : _h.state) { transform_1.StateTransform.assignState((_j = comp === null || comp === void 0 ? void 0 : comp.cell) === null || _j === void 0 ? void 0 : _j.state, { isHidden: true }); } yield update.commit({ revertOnError: false }); } structure.inheritedPropertyData.reprList = Object.values(representationMap).filter(repr => typeof repr != "undefined"); yield updateFocusRepr(plugin, structure, FocusColoring_1.FOCUS_RESIDUE_COLOR, {}); return { components: componentMap, representations: representationMap }; }) }); function matrixAlign(plugin, structureRef, matrix) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const trans = { transform: { name: 'matrix', params: { data: matrix, transpose: false } } }; const b = plugin.state.data.build().to(structureRef) .insert(model_1.TransformStructureConformation, trans); yield plugin.runTask(plugin.state.data.updateTree(b)); }); } function structuralAlignment(plugin, ref, pdb, structure) { return tslib_1.__awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d; if (ref.entryId == pdb.entryId) { refData = structure; } else { const pdbResIndexes = []; const refResIndexes = []; const pdbData = structure; const pdbUnit = yield findFirstInstanceUnit(pdbData, pdb.labelAsymId); const refUnit = refData ? yield findFirstInstanceUnit(refData, ref.labelAsymId) : undefined; if (pdbUnit && refUnit && ((_a = ref.targetAlignment) === null || _a === void 0 ? void 0 : _a.aligned_regions) && ((_b = pdb.targetAlignment) === null || _b === void 0 ? void 0 : _b.aligned_regions)) { const alignmentList = AlignmentMapper_1.AlignmentMapper.getAllTargetIntersections(ref.targetAlignment.aligned_regions, pdb.targetAlignment.aligned_regions); alignmentList.forEach(alignment => { const refRange = AlignmentMapper_1.AlignmentMapper.range(alignment[0].target_begin, alignment[0].target_end); const pdbRange = AlignmentMapper_1.AlignmentMapper.range(alignment[1].target_begin, alignment[1].target_end); refRange.forEach((refIndex, n) => { const pdbIndex = pdbRange[n]; const pdbLoci = residueToLoci(pdb, pdbIndex, pdbData); const refLoci = residueToLoci(refParams, refIndex, refData); if (!loci_1.Loci.isEmpty(pdbLoci) && !loci_1.Loci.isEmpty(refLoci) && checkLocalScore(pdbUnit.localScore, pdbIndex) && checkLocalScore(refUnit.localScore, refIndex)) { pdbResIndexes.push(pdbIndex); refResIndexes.push(refIndex); } }); }); } if (pdbData && pdbUnit && refData && refUnit) { const refLoci = residueListToLoci(refParams, refResIndexes, refData); const pdbLoci = residueListToLoci(pdb, pdbResIndexes, pdbData); if (structure_1.StructureElement.Loci.is(refLoci) && structure_1.StructureElement.Loci.is(pdbLoci)) { const pivot = plugin.managers.structure.hierarchy.findStructure(refLoci.structure); const coordinateSystem = (_d = (_c = pivot === null || pivot === void 0 ? void 0 : pivot.transform) === null || _c === void 0 ? void 0 : _c.cell.obj) === null || _d === void 0 ? void 0 : _d.data.coordinateSystem; const transforms = (0, superposition_1.superpose)([refLoci, pdbLoci]); const { bTransform } = transforms[0]; yield transform(plugin, plugin.helpers.substructureParent.get(pdbData), bTransform, coordinateSystem); } } } }); } function findFirstInstanceUnit(structure, labelAsymId) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const l = structure_1.StructureElement.Location.create(structure); for (const unit of structure.units) { structure_1.StructureElement.Location.set(l, structure, unit, unit.elements[0]); if (structure_1.StructureProperties.chain.label_asym_id(l) == labelAsymId) { const q = yield obtainQualityAssessment(unit.model); return { unit, localScore: q }; } } }); } function checkLocalScore(scoreMap, index) { if (scoreMap.size == 0) return true; return !!(scoreMap.get(index) && scoreMap.get(index) >= 70); } function obtainQualityAssessment(model) { return tslib_1.__awaiter(this, void 0, void 0, function* () { var _a; if (!model || !mmcif_1.MmcifFormat.is(model.sourceData)) return new Map(); const { ma_qa_metric, ma_qa_metric_local } = model.sourceData.data.db; const { model_id, label_seq_id, metric_id, metric_value } = ma_qa_metric_local; // for simplicity we assume names in ma_qa_metric for mode 'local' are unique const localMetrics = new Map(); const localNames = new Map(); for (let i = 0, il = ma_qa_metric._rowCount; i < il; i++) { if (ma_qa_metric.mode.value(i) !== 'local') continue; const name = ma_qa_metric.name.value(i); if (localMetrics.has(name)) { console.warn(`local ma_qa_metric with name '${name}' already added`); continue; } localMetrics.set(name, new Map()); localNames.set(ma_qa_metric.id.value(i), name); } for (let i = 0, il = ma_qa_metric_local._rowCount; i < il; i++) { if (model_id.value(i) !== model.modelNum) continue; const rI = label_seq_id.value(i); const name = localNames.get(metric_id.value(i)); localMetrics.get(name).set(rI, metric_value.value(i)); } return (_a = localMetrics.get('pLDDT')) !== null && _a !== void 0 ? _a : new Map(); }); } const SuperpositionTag = 'SuperpositionTransform'; function transform(plugin, s, matrix, coordinateSystem) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const r = mol_state_1.StateObjectRef.resolveAndCheck(plugin.state.data, s); if (!r) return; const o = plugin.state.data.selectQ(q => q.byRef(r.transform.ref).subtree().withTransformer(model_1.TransformStructureConformation))[0]; const transform = coordinateSystem && !linear_algebra_1.Mat4.isIdentity(coordinateSystem.matrix) ? linear_algebra_1.Mat4.mul((0, linear_algebra_1.Mat4)(), coordinateSystem.matrix, matrix) : matrix; const params = { transform: { name: 'matrix', params: { data: transform, transpose: false } } }; const b = o ? plugin.state.data.build().to(o).update(params) : plugin.state.data.build().to(s) .insert(model_1.TransformStructureConformation, params, { tags: SuperpositionTag }); yield plugin.runTask(plugin.state.data.updateTree(b)); }); } function residueToLoci(pdb, pdbIndex, structure) { const expression = builder_1.MolScriptBuilder.struct.generator.atomGroups({ 'chain-test': builder_1.MolScriptBuilder.core.logic.and([ builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.ammp('label_asym_id'), pdb.labelAsymId]), builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.acp('operatorName'), pdb.operatorName]), builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.acp('modelIndex'), 1]) ]), 'residue-test': builder_1.MolScriptBuilder.struct.atomProperty.ihm.hasSeqId([pdbIndex]), 'atom-test': builder_1.MolScriptBuilder.core.logic.and([ builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.ammp("label_atom_id"), "CA"]), builder_1.MolScriptBuilder.core.logic.or([builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.ammp("label_alt_id"), ""]), builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.ammp("label_alt_id"), "A"])]) ]) }); const query = (0, compiler_1.compile)(expression); const selection = query(new structure_1.QueryContext(structure)); return structure_1.StructureSelection.toLociWithSourceUnits(selection); } function residueListToLoci(pdb, indexList, structure) { const expression = builder_1.MolScriptBuilder.struct.generator.atomGroups({ 'chain-test': builder_1.MolScriptBuilder.core.logic.and([ builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.ammp('label_asym_id'), pdb.labelAsymId]), builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.acp('operatorName'), pdb.operatorName]), builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.acp('modelIndex'), 1]) ]), 'residue-test': builder_1.MolScriptBuilder.core.logic.or(indexList.map(index => builder_1.MolScriptBuilder.struct.atomProperty.ihm.hasSeqId([index]))), 'atom-test': builder_1.MolScriptBuilder.core.logic.and([ builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.ammp("label_atom_id"), "CA"]), builder_1.MolScriptBuilder.core.logic.or([builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.ammp("label_alt_id"), ""]), builder_1.MolScriptBuilder.core.rel.eq([builder_1.MolScriptBuilder.ammp("label_alt_id"), "A"])]) ]) }); const query = (0, compiler_1.compile)(expression); const selection = query(new structure_1.QueryContext(structure)); return structure_1.StructureSelection.toLociWithSourceUnits(selection); }