UNPKG

pdbe-molstar

Version:
141 lines (140 loc) 6.59 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.superposeByBiggestCommonChain = superposeByBiggestCommonChain; const minimize_rmsd_1 = require("molstar/lib/mol-math/linear-algebra/3d/minimize-rmsd"); const mmcif_1 = require("molstar/lib/mol-model-formats/structure/mmcif"); /** Superpose structures based on the largest common component (measured the by number of residues) with a UniProt mapping (taken from atom_site mmCIF category). * Residue-residue correspondence is determined by UniProt residue numbers. * This differs from UniProt-based superposition in core Molstar (`alignAndSuperposeWithSIFTSMapping`) which takes all components (regardless of their spacial arrangement). */ function superposeByBiggestCommonChain(structA, structB, allowedComponentsA, allowedComponentsB) { const indexA = extractUniprotIndex(structA, allowedComponentsA); const indexB = extractUniprotIndex(structB, allowedComponentsB); const bestMatch = bestUniprotMatch(indexA, indexB); if (!bestMatch) { return undefined; } const unitA = structA.unitMap.get(Number(bestMatch.unitA)); const unitB = structB.unitMap.get(Number(bestMatch.unitB)); const unitIndexA = indexA[bestMatch.accession][bestMatch.unitA]; const unitIndexB = indexB[bestMatch.accession][bestMatch.unitB]; const positionsA = minimize_rmsd_1.MinimizeRmsd.Positions.empty(bestMatch.nMatchedElements); const positionsB = minimize_rmsd_1.MinimizeRmsd.Positions.empty(bestMatch.nMatchedElements); let i = 0; for (const unpNum in unitIndexA.atomMap) { const iAtomB = unitIndexB.atomMap[unpNum]; if (iAtomB === undefined) continue; const iAtomA = unitIndexA.atomMap[unpNum]; positionsA.x[i] = unitA.conformation.coordinates.x[iAtomA]; positionsA.y[i] = unitA.conformation.coordinates.y[iAtomA]; positionsA.z[i] = unitA.conformation.coordinates.z[iAtomA]; positionsB.x[i] = unitB.conformation.coordinates.x[iAtomB]; positionsB.y[i] = unitB.conformation.coordinates.y[iAtomB]; positionsB.z[i] = unitB.conformation.coordinates.z[iAtomB]; i++; } const superposition = minimize_rmsd_1.MinimizeRmsd.compute({ a: positionsA, b: positionsB }); if (!isNaN(superposition.rmsd)) { return Object.assign(Object.assign({}, superposition), { method: 'uniprot-numbering', accession: bestMatch.accession }); } else { return undefined; } } function extractUniprotIndex(structure, allowedAccessions) { var _a, _b, _c; var _d, _e; const allowedAccessionsSet = allowedAccessions ? new Set(allowedAccessions) : undefined; const seenUnitInvariantIds = new Set(); const out = {}; for (const unit of structure.units) { if (seenUnitInvariantIds.has(unit.invariantId)) continue; else seenUnitInvariantIds.add(unit.invariantId); const src = structure.model.sourceData; if (!mmcif_1.MmcifFormat.is(src)) throw new Error('Source data must be mmCIF/BCIF'); const h = unit.model.atomicHierarchy; const { pdbx_sifts_xref_db_acc, pdbx_sifts_xref_db_name, pdbx_sifts_xref_db_num } = src.data.db.atom_site; const atoms = unit.polymerElements; const nAtoms = atoms.length; for (let i = 0; i < nAtoms; i++) { const iAtom = atoms[i]; const srcIAtom = h.atomSourceIndex.value(iAtom); const dbName = pdbx_sifts_xref_db_name.value(srcIAtom); if (dbName !== 'UNP') continue; const dbAcc = pdbx_sifts_xref_db_acc.value(srcIAtom); if (allowedAccessionsSet && !allowedAccessionsSet.has(dbAcc)) continue; const dbNum = pdbx_sifts_xref_db_num.value(srcIAtom); const structMapping = (_a = out[dbAcc]) !== null && _a !== void 0 ? _a : (out[dbAcc] = {}); const chainMapping = (_b = structMapping[_d = unit.id]) !== null && _b !== void 0 ? _b : (structMapping[_d] = { label_asym_id: h.chains.label_asym_id.value(h.chainAtomSegments.index[atoms[0]]), auth_asym_id: h.chains.auth_asym_id.value(h.chainAtomSegments.index[atoms[0]]), atomMap: {}, }); (_c = (_e = chainMapping.atomMap)[dbNum]) !== null && _c !== void 0 ? _c : (_e[dbNum] = iAtom); } } return out; } /** Sort units for each accession by decreasing size and sort accessions by decreasing biggest unit size. */ function sortAccessionsAndUnits(uniprotIndex) { const unitsByAccession = {}; for (const accession in uniprotIndex) { const unitIds = uniprotIndex[accession]; const units = []; for (const unitId in unitIds) { const size = Object.keys(unitIds[unitId].atomMap).length; units.push({ unitId, size }); } units.sort((a, b) => b.size - a.size); unitsByAccession[accession] = units; } return { /** Accessions sorted by decreasing biggest unit size */ accessions: Object.keys(unitsByAccession).sort((a, b) => unitsByAccession[b][0].size - unitsByAccession[a][0].size), /** Units per accession, sorted by decreasing unit size */ units: unitsByAccession, }; } function bestUniprotMatch(a, b) { const sortedA = sortAccessionsAndUnits(a); const sortedB = sortAccessionsAndUnits(b); let bestMatch = undefined; let bestScore = 0; for (const accession of sortedA.accessions) { const unitsA = sortedA.units[accession]; const unitsB = sortedB.units[accession]; if (!unitsB) continue; for (const ua of unitsA) { if (ua.size <= bestScore) break; const unitA = a[accession][ua.unitId]; for (const ub of unitsB) { if (ub.size <= bestScore || ua.size <= bestScore) break; const unitB = b[accession][ub.unitId]; const score = objectKeyOverlap(unitA.atomMap, unitB.atomMap); if (score > bestScore) { bestScore = score; bestMatch = { accession, unitA: ua.unitId, unitB: ub.unitId, nMatchedElements: score }; } } } } return bestMatch; } /** Return number of keys common to objects `a` and `b` */ function objectKeyOverlap(a, b) { let overlap = 0; for (const key in a) { if (key in b) { overlap++; } } return overlap; }