pdbe-molstar
Version:
Molstar implementation for PDBe
136 lines (135 loc) • 6.29 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.superposeStructuresByBiggestCommonChain = superposeStructuresByBiggestCommonChain;
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");
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;
}
function bestUniprotMatch(a, b) {
const sortedA = sortAccessionAndChains(a);
const sortedB = sortAccessionAndChains(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;
}
/** Sort units for each accession by decreasing size and sort accessions by decreasing biggest unit size. */
function sortAccessionAndChains(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,
};
}
/** 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;
}
function superposeStructuresByBiggestCommonChain(structA, structB, allowedComponentsA, allowedComponentsB) {
const indexA = extractUniprotIndex(structA, allowedComponentsA);
const indexB = extractUniprotIndex(structB, allowedComponentsB);
const bestMatch = bestUniprotMatch(indexA, indexB);
if (!bestMatch) {
return { status: 'zero-overlap', superposition: 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 { status: 'failed', superposition: undefined };
}
return { status: 'success', superposition: Object.assign(Object.assign({}, superposition), { nAlignedElements: bestMatch.nMatchedElements }) };
}