pdbe-molstar
Version:
Molstar implementation for PDBe
181 lines (180 loc) • 7.17 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.superposeBySequenceAlignment = superposeBySequenceAlignment;
const int_1 = require("molstar/lib/mol-data/int");
const minimize_rmsd_1 = require("molstar/lib/mol-math/linear-algebra/3d/minimize-rmsd");
const alignment_1 = require("molstar/lib/mol-model/sequence/alignment/alignment");
const structure_1 = require("molstar/lib/mol-model/structure");
const superposition_1 = require("molstar/lib/mol-model/structure/structure/util/superposition");
const helpers_1 = require("../../helpers");
/** Superpose structures based on the largest common component (measured the by number of residues), components being defined by `mappingsA` and `mappingsB`.
* Residue-residue correspondence is determined by sequence alignment. */
function superposeBySequenceAlignment(structA, structB, mappingsA, mappingsB) {
const sortedA = sortAccessionsAndUnits(structA, mappingsA);
const sortedB = sortAccessionsAndUnits(structB, mappingsB);
const bestMatch = bestMappingMatch(sortedA, sortedB);
if (!bestMatch) {
return undefined;
}
const accession = bestMatch.accession;
const lociA = helpers_1.QueryHelper.getInteractivityLoci(mappingsA[accession], structA);
const lociB = helpers_1.QueryHelper.getInteractivityLoci(mappingsB[accession], structB);
const superposition = alignAndSuperpose(lociA, lociB);
if (!isNaN(superposition.rmsd)) {
return Object.assign(Object.assign({}, superposition), { method: 'sequence-alignment', accession: bestMatch.accession });
}
else {
return undefined;
}
}
/** Sort units for each accession by decreasing size and sort accessions by decreasing biggest unit size. */
function sortAccessionsAndUnits(struct, mappings) {
const unitsByAccession = {};
for (const accession in mappings) {
const loci = helpers_1.QueryHelper.getInteractivityLoci(mappings[accession], struct);
const units = [];
for (const u of loci.elements) {
const unitId = u.unit.id.toString();
const elements = [];
int_1.OrderedSet.forEach(u.indices, elementUnitIndex => {
const elementIndex = u.unit.elements[elementUnitIndex];
if (int_1.SortedArray.has(u.unit.polymerElements, elementIndex))
elements.push(elementIndex);
});
units.push({ unitId, size: elements.length, elements: int_1.SortedArray.ofSortedArray(elements) });
}
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 bestMappingMatch(sortedA, sortedB) {
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;
for (const ub of unitsB) {
if (ub.size <= bestScore || ua.size <= bestScore)
break;
const score = Math.min(ua.size, ub.size);
if (score > bestScore) {
bestScore = score;
bestMatch = { accession, unitA: ua.unitId, unitB: ub.unitId, elementsA: ua.elements, elementsB: ub.elements, nMatchedElements: score };
}
}
}
}
return bestMatch;
}
const reProtein = /(polypeptide|cyclic-pseudo-peptide)/i;
function alignAndSuperpose(lociA, lociB) {
const location = structure_1.StructureElement.Loci.getFirstLocation(lociA);
const subtype = structure_1.StructureProperties.entity.subtype(location);
const substMatrix = subtype.match(reProtein) ? 'blosum62' : 'default';
const { matchedA, matchedB } = computeAlignment(lociA.elements[0], lociB.elements[0], { substMatrix });
const n = int_1.OrderedSet.size(matchedA.indices);
const coordsA = (0, superposition_1.getPositionTable)(structure_1.StructureElement.Loci(lociA.structure, [matchedA]), n);
const coordsB = (0, superposition_1.getPositionTable)(structure_1.StructureElement.Loci(lociB.structure, [matchedB]), n);
const superposition = minimize_rmsd_1.MinimizeRmsd.compute({ a: coordsA, b: coordsB });
return superposition;
}
function computeAlignment(a, b, options = {}) {
const seqA = getSequenceFromLoci(a);
const seqB = getSequenceFromLoci(b);
const { aliA, aliB, score } = (0, alignment_1.align)(seqA.sequence.map(getOneLetterCode), seqB.sequence.map(getOneLetterCode), options);
const indicesA = [];
const indicesB = [];
let seqIdxA = 0, seqIdxB = 0;
for (let i = 0, n = aliA.length; i < n; ++i) {
if (aliA[i] !== '-' && aliB[i] !== '-') {
indicesA.push(seqA.unitElements[seqIdxA]);
indicesB.push(seqB.unitElements[seqIdxB]);
}
if (aliA[i] !== '-')
seqIdxA += 1;
if (aliB[i] !== '-')
seqIdxB += 1;
}
return {
matchedA: { unit: a.unit, indices: int_1.OrderedSet.ofSortedArray(indicesA) },
matchedB: { unit: b.unit, indices: int_1.OrderedSet.ofSortedArray(indicesB) },
score,
};
}
/** Extract sequence and array of corresponding trace atoms. */
function getSequenceFromLoci(loci) {
const { unit, indices } = loci;
const unitElements = [];
const sequence = [];
int_1.OrderedSet.forEach(indices, elementUnitIndex => {
const elementIndex = unit.elements[elementUnitIndex];
if (int_1.OrderedSet.has(unit.polymerElements, elementIndex)) {
unitElements.push(elementUnitIndex);
const compId = unit.model.atomicHierarchy.atoms.label_comp_id.value(elementIndex);
sequence.push(compId);
}
});
return { sequence, unitElements };
}
function getOneLetterCode(compId) {
var _a;
return (_a = OneLetterCodes[compId]) !== null && _a !== void 0 ? _a : 'X';
}
// Copied from Molstar
const OneLetterCodes = {
'HIS': 'H',
'ARG': 'R',
'LYS': 'K',
'ILE': 'I',
'PHE': 'F',
'LEU': 'L',
'TRP': 'W',
'ALA': 'A',
'MET': 'M',
'PRO': 'P',
'CYS': 'C',
'ASN': 'N',
'VAL': 'V',
'GLY': 'G',
'SER': 'S',
'GLN': 'Q',
'TYR': 'Y',
'ASP': 'D',
'GLU': 'E',
'THR': 'T',
'SEC': 'U', // as per IUPAC definition
'PYL': 'O', // as per IUPAC definition
// charmm ff
'HSD': 'H', 'HSE': 'H', 'HSP': 'H',
'LSN': 'K',
'ASPP': 'D',
'GLUP': 'E',
// amber ff
'HID': 'H', 'HIE': 'H', 'HIP': 'H',
'LYN': 'K',
'ASH': 'D',
'GLH': 'E',
// DNA
'DA': 'A',
'DC': 'C',
'DG': 'G',
'DT': 'T',
'DU': 'U',
// RNA
'A': 'A',
'C': 'C',
'G': 'G',
'T': 'T',
'U': 'U',
};