UNPKG

@rcsb/rcsb-saguaro-3d

Version:
354 lines 19.2 kB
/* * Copyright (c) 2021 RCSB PDB and contributors, licensed under MIT, See LICENSE file for more info. * @author Joan Segura Mora <joan.segura@rcsb.org> */ import { __awaiter } from "tslib"; import { StructureRepresentationPresetProvider } from "molstar/lib/mol-plugin-state/builder/structure/representation-preset"; import { StateObjectRef } from "molstar/lib/mol-state"; import { QueryContext, StructureElement, StructureProperties as SP, StructureSelection } from "molstar/lib/mol-model/structure"; import { MolScriptBuilder as MS } from "molstar/lib/mol-script/language/builder"; import uniqid from "uniqid"; import { PLDDTConfidenceColorThemeProvider } from "molstar/lib/extensions/model-archive/quality-assessment/color/plddt"; import { createSelectionExpressions } from "@rcsb/rcsb-molstar/build/src/viewer/helpers/selection"; import { ParamDefinition as PD } from "molstar/lib/mol-util/param-definition"; import { Loci } from "molstar/lib/mol-model/loci"; import { superpose } from "molstar/lib/mol-model/structure/structure/util/superposition"; import { Mat4 } from "molstar/lib/mol-math/linear-algebra"; import { TagDelimiter } from "@rcsb/rcsb-api-tools/lib/RcsbUtils/TagDelimiter"; import { AlignmentMapper as AM } from "../../../../Utils/AlignmentMapper"; import { compile } from 'molstar/lib/mol-script/runtime/query/compiler'; var reprBuilder = StructureRepresentationPresetProvider.reprBuilder; import { MmcifFormat } from "molstar/lib/mol-model-formats/structure/mmcif"; import { StateTransform } from "molstar/lib/mol-state/transform"; import { TransformStructureConformation } from "molstar/lib/mol-plugin-state/transforms/model"; var updateFocusRepr = StructureRepresentationPresetProvider.updateFocusRepr; import { FOCUS_RESIDUE_COLOR } from "./FocusTheme/FocusColoring"; import { QualityAssessment } from "molstar/lib/extensions/model-archive/quality-assessment/prop"; let refData = undefined; let refParams = undefined; export const AlignmentRepresentationPresetProvider = StructureRepresentationPresetProvider({ id: 'alignment-to-reference', display: { name: 'Alignment to Reference' }, isApplicable: (structureRef, plugin) => true, params: (structureRef, plugin) => ({ pdb: PD.Value(undefined), targetAlignment: PD.Value(undefined), transform: PD.Value(undefined) }), apply: (structureRef, params, plugin) => __awaiter(void 0, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g, _h, _j; const structureCell = 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 = StructureElement.Location.create(structure); let alignedEntityId; let alignedAsymId; let alignedOperatorName; let alignedType; const componentMap = {}; const representationMap = {}; for (const unit of structure.units) { StructureElement.Location.set(l, structure, unit, unit.elements[0]); if (SP.chain.label_entity_id(l) == entityId || SP.chain.label_asym_id(l) == instanceId) { alignedEntityId = SP.chain.label_entity_id(l); alignedAsymId = SP.chain.label_asym_id(l); alignedOperatorName = SP.unit.operator_name(l); alignedType = SP.entity.type(l); const alignedOperators = SP.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, MS.struct.generator.atomGroups({ 'chain-test': MS.core.logic.and([ MS.core.rel.eq([MS.ammp('label_asym_id'), alignedAsymId]), MS.core.rel.eq([MS.acp('operatorName'), alignedOperatorName]) ]) }), uniqid(`${entryId}${TagDelimiter.entity}${alignedEntityId}${TagDelimiter.instance}${alignedAsymId}${TagDelimiter.entity}${alignedOperators.join(",")}`), { label: `${entryId}${TagDelimiter.entity}${alignedEntityId}${TagDelimiter.instance}${alignedAsymId}${TagDelimiter.assembly}${alignedOperators.join(",")}${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 => QualityAssessment.isApplicable(m, 'pLDDT')) ? PLDDTConfidenceColorThemeProvider.name : "chain-id", type: "cartoon" }); yield update.commit({ revertOnError: false }); break; } } const expressions = []; const asymObserved = {}; for (const unit of structure.units) { StructureElement.Location.set(l, structure, unit, unit.elements[0]); const asymId = SP.chain.label_asym_id(l); const operatorName = SP.unit.operator_name(l); if (asymId == alignedAsymId && operatorName == alignedOperatorName) continue; if (asymObserved[`${asymId}${TagDelimiter.assembly}${operatorName}`]) continue; asymObserved[`${asymId}${TagDelimiter.assembly}${operatorName}`] = true; const type = SP.entity.type(l); if (type == "polymer") { expressions.push(MS.core.logic.and([ MS.core.rel.eq([MS.ammp('label_asym_id'), asymId]), MS.core.rel.eq([MS.acp('operatorName'), operatorName]) ])); } } const compId = `${entryId}${TagDelimiter.entity}${alignedEntityId}${TagDelimiter.assembly}${alignedType}`; const comp = yield plugin.builders.structure.tryCreateComponentFromExpression(structureCell, MS.struct.generator.atomGroups({ 'chain-test': MS.core.logic.or(expressions) }), uniqid(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 => QualityAssessment.isApplicable(m, 'pLDDT')) ? 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) { 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 createSelectionExpressions(entryId)) { if (expression.tag == "polymer") continue; const comp = yield plugin.builders.structure.tryCreateComponentFromExpression(structureCell, expression.expression, uniqid(`${entryId}${TagDelimiter.entity}${alignedEntityId}${TagDelimiter.assembly}${expression.tag}`), { label: `${entryId}${TagDelimiter.entity}${alignedEntityId}${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) { 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, FOCUS_RESIDUE_COLOR, {}); return { components: componentMap, representations: representationMap }; }) }); function matrixAlign(plugin, structureRef, matrix) { return __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(TransformStructureConformation, trans); yield plugin.runTask(plugin.state.data.updateTree(b)); }); } function structuralAlignment(plugin, ref, pdb, structure) { return __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 = AM.getAllTargetIntersections(ref.targetAlignment.aligned_regions, pdb.targetAlignment.aligned_regions); alignmentList.forEach(alignment => { const refRange = AM.range(alignment[0].target_begin, alignment[0].target_end); const pdbRange = AM.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.isEmpty(pdbLoci) && !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 (StructureElement.Loci.is(refLoci) && 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 = superpose([refLoci, pdbLoci]); const { bTransform } = transforms[0]; yield transform(plugin, plugin.helpers.substructureParent.get(pdbData), bTransform, coordinateSystem); } } } }); } function findFirstInstanceUnit(structure, labelAsymId) { return __awaiter(this, void 0, void 0, function* () { const l = StructureElement.Location.create(structure); for (const unit of structure.units) { StructureElement.Location.set(l, structure, unit, unit.elements[0]); if (SP.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 __awaiter(this, void 0, void 0, function* () { var _a; if (!model || !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 __awaiter(this, void 0, void 0, function* () { const r = StateObjectRef.resolveAndCheck(plugin.state.data, s); if (!r) return; const o = plugin.state.data.selectQ(q => q.byRef(r.transform.ref).subtree().withTransformer(TransformStructureConformation))[0]; const transform = coordinateSystem && !Mat4.isIdentity(coordinateSystem.matrix) ? Mat4.mul(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(TransformStructureConformation, params, { tags: SuperpositionTag }); yield plugin.runTask(plugin.state.data.updateTree(b)); }); } function residueToLoci(pdb, pdbIndex, structure) { const expression = MS.struct.generator.atomGroups({ 'chain-test': MS.core.logic.and([ MS.core.rel.eq([MS.ammp('label_asym_id'), pdb.labelAsymId]), MS.core.rel.eq([MS.acp('operatorName'), pdb.operatorName]), MS.core.rel.eq([MS.acp('modelIndex'), 1]) ]), 'residue-test': MS.struct.atomProperty.ihm.hasSeqId([pdbIndex]), 'atom-test': MS.core.logic.and([ MS.core.rel.eq([MS.ammp("label_atom_id"), "CA"]), MS.core.logic.or([MS.core.rel.eq([MS.ammp("label_alt_id"), ""]), MS.core.rel.eq([MS.ammp("label_alt_id"), "A"])]) ]) }); const query = compile(expression); const selection = query(new QueryContext(structure)); return StructureSelection.toLociWithSourceUnits(selection); } function residueListToLoci(pdb, indexList, structure) { const expression = MS.struct.generator.atomGroups({ 'chain-test': MS.core.logic.and([ MS.core.rel.eq([MS.ammp('label_asym_id'), pdb.labelAsymId]), MS.core.rel.eq([MS.acp('operatorName'), pdb.operatorName]), MS.core.rel.eq([MS.acp('modelIndex'), 1]) ]), 'residue-test': MS.core.logic.or(indexList.map(index => MS.struct.atomProperty.ihm.hasSeqId([index]))), 'atom-test': MS.core.logic.and([ MS.core.rel.eq([MS.ammp("label_atom_id"), "CA"]), MS.core.logic.or([MS.core.rel.eq([MS.ammp("label_alt_id"), ""]), MS.core.rel.eq([MS.ammp("label_alt_id"), "A"])]) ]) }); const query = compile(expression); const selection = query(new QueryContext(structure)); return StructureSelection.toLociWithSourceUnits(selection); } //# sourceMappingURL=AlignmentRepresentationPresetProvider.js.map