molstar
Version:
A comprehensive macromolecular library.
263 lines • 12.9 kB
JavaScript
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author Fred Ludlow <Fred.Ludlow@astx.com>
*
* based in part on NGL (https://github.com/arose/ngl)
*/
import { __assign } from "tslib";
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { AtomGeometryAngles, calcAngles, calcPlaneAngle } from '../chemistry/geometry';
import { Features } from './features';
import { typeSymbol, bondToElementCount, bondCount, formalCharge, compId, atomId } from '../chemistry/util';
import { ValenceModelProvider } from '../valence-model';
import { degToRad } from '../../../mol-math/misc';
import { ProteinBackboneAtoms } from '../../../mol-model/structure/model/types';
var GeometryParams = {
distanceMax: PD.Numeric(3.5, { min: 1, max: 5, step: 0.1 }),
backbone: PD.Boolean(true, { description: 'Include backbone-to-backbone hydrogen bonds' }),
accAngleDevMax: PD.Numeric(45, { min: 0, max: 180, step: 1 }, { description: 'Max deviation from ideal acceptor angle' }),
donAngleDevMax: PD.Numeric(45, { min: 0, max: 180, step: 1 }, { description: 'Max deviation from ideal donor angle' }),
accOutOfPlaneAngleMax: PD.Numeric(90, { min: 0, max: 180, step: 1 }),
donOutOfPlaneAngleMax: PD.Numeric(45, { min: 0, max: 180, step: 1 }),
};
var HydrogenBondsParams = __assign(__assign({}, GeometryParams), { water: PD.Boolean(false, { description: 'Include water-to-water hydrogen bonds' }), sulfurDistanceMax: PD.Numeric(4.1, { min: 1, max: 5, step: 0.1 }) });
var WeakHydrogenBondsParams = __assign({}, GeometryParams);
//
// Geometric characteristics of hydrogen bonds involving sulfur atoms in proteins
// https://doi.org/10.1002/prot.22327
// Satisfying Hydrogen Bonding Potential in Proteins (HBPLUS)
// https://doi.org/10.1006/jmbi.1994.1334
// http://www.csb.yale.edu/userguides/datamanip/hbplus/hbplus_descrip.html
function getUnitValenceModel(structure, unit) {
var valenceModel = ValenceModelProvider.get(structure).value;
if (!valenceModel)
throw Error('expected valence model to be available');
var unitValenceModel = valenceModel.get(unit.id);
if (!unitValenceModel)
throw Error('expected valence model for unit to be available');
return unitValenceModel;
}
/**
* Potential hydrogen donor
*/
function addUnitHydrogenDonors(structure, unit, builder) {
var totalH = getUnitValenceModel(structure, unit).totalH;
var elements = unit.elements;
var _a = unit.model.atomicConformation, x = _a.x, y = _a.y, z = _a.z;
var elementAromaticRingIndices = unit.rings.elementAromaticRingIndices;
for (var i = 0, il = elements.length; i < il; ++i) {
if (elementAromaticRingIndices.has(i))
continue;
var element = typeSymbol(unit, i);
if ((
// include both nitrogen atoms in histidine due to
// their often ambiguous protonation assignment
isHistidineNitrogen(unit, i)) || (totalH[i] > 0 &&
(element === "N" /* N */ || element === "O" /* O */ || element === "S" /* S */))) {
builder.add(4 /* HydrogenDonor */, 0 /* None */, x[elements[i]], y[elements[i]], z[elements[i]], i);
}
}
}
/**
* Weak hydrogen donor.
*/
function addUnitWeakHydrogenDonors(structure, unit, builder) {
var totalH = getUnitValenceModel(structure, unit).totalH;
var elements = unit.elements;
var _a = unit.model.atomicConformation, x = _a.x, y = _a.y, z = _a.z;
for (var i = 0, il = elements.length; i < il; ++i) {
if (typeSymbol(unit, i) === "C" /* C */ &&
totalH[i] > 0 &&
(bondToElementCount(structure, unit, i, "N" /* N */) > 0 ||
bondToElementCount(structure, unit, i, "O" /* O */) > 0 ||
inAromaticRingWithElectronNegativeElement(unit, i))) {
builder.add(9 /* WeakHydrogenDonor */, 0 /* None */, x[elements[i]], y[elements[i]], z[elements[i]], i);
}
}
}
function inAromaticRingWithElectronNegativeElement(unit, index) {
var _a = unit.rings, elementAromaticRingIndices = _a.elementAromaticRingIndices, all = _a.all;
var ringIndices = elementAromaticRingIndices.get(index);
if (ringIndices === undefined)
return false;
for (var i = 0, il = ringIndices.length; i < il; ++i) {
var ring = all[ringIndices[i]];
for (var j = 0, jl = ring.length; j < jl; ++j) {
var element = typeSymbol(unit, ring[j]);
if (element === "N" /* N */ || element === "O" /* O */) {
return true;
}
}
}
return false;
}
/**
* Potential hydrogen acceptor
*/
function addUnitHydrogenAcceptors(structure, unit, builder) {
var _a = getUnitValenceModel(structure, unit), charge = _a.charge, implicitH = _a.implicitH, idealGeometry = _a.idealGeometry;
var elements = unit.elements;
var _b = unit.model.atomicConformation, x = _b.x, y = _b.y, z = _b.z;
var elementAromaticRingIndices = unit.rings.elementAromaticRingIndices;
var add = function (i) {
builder.add(5 /* HydrogenAcceptor */, 0 /* None */, x[elements[i]], y[elements[i]], z[elements[i]], i);
};
for (var i = 0, il = elements.length; i < il; ++i) {
if (elementAromaticRingIndices.has(i))
continue;
var element = typeSymbol(unit, i);
if (element === "O" /* O */) {
// Basically assume all oxygen atoms are acceptors!
add(i);
}
else if (element === "N" /* N */) {
if (isHistidineNitrogen(unit, i)) {
// include both nitrogen atoms in histidine due to
// their often ambiguous protonation assignment
add(i);
}
else if (charge[i] < 1) {
// Neutral nitrogen might be an acceptor
// It must have at least one lone pair not conjugated
var totalBonds = bondCount(structure, unit, i) + implicitH[i];
var ig = idealGeometry[i];
if ((ig === 4 /* Tetrahedral */ && totalBonds < 4) ||
(ig === 3 /* Trigonal */ && totalBonds < 3) ||
(ig === 2 /* Linear */ && totalBonds < 2)) {
add(i);
}
}
}
else if (element === "S" /* S */) {
var resname = compId(unit, i);
if (resname === 'CYS' || resname === 'MET' || formalCharge(unit, i) === -1) {
add(i);
}
}
}
}
function isWater(unit, index) {
return unit.model.atomicHierarchy.derived.residue.moleculeType[unit.residueIndex[unit.elements[index]]] === 2 /* Water */;
}
function isBackbone(unit, index) {
return ProteinBackboneAtoms.has(atomId(unit, index));
}
function isRing(unit, index) {
return unit.rings.elementRingIndices.has(index);
}
function isHistidineNitrogen(unit, index) {
return compId(unit, index) === 'HIS' && typeSymbol(unit, index) === "N" /* N */ && isRing(unit, index);
}
function isBackboneHydrogenBond(unitA, indexA, unitB, indexB) {
return isBackbone(unitA, indexA) && isBackbone(unitB, indexB);
}
function isWaterHydrogenBond(unitA, indexA, unitB, indexB) {
return isWater(unitA, indexA) && isWater(unitB, indexB);
}
function isHydrogenBond(ti, tj) {
return ((ti === 5 /* HydrogenAcceptor */ && tj === 4 /* HydrogenDonor */) ||
(ti === 4 /* HydrogenDonor */ && tj === 5 /* HydrogenAcceptor */));
}
function isWeakHydrogenBond(ti, tj) {
return ((ti === 9 /* WeakHydrogenDonor */ && tj === 5 /* HydrogenAcceptor */) ||
(ti === 5 /* HydrogenAcceptor */ && tj === 9 /* WeakHydrogenDonor */));
}
function getGeometryOptions(props) {
return {
includeBackbone: props.backbone,
maxAccAngleDev: degToRad(props.accAngleDevMax),
maxDonAngleDev: degToRad(props.donAngleDevMax),
maxAccOutOfPlaneAngle: degToRad(props.accOutOfPlaneAngleMax),
maxDonOutOfPlaneAngle: degToRad(props.donOutOfPlaneAngleMax),
};
}
function getHydrogenBondsOptions(props) {
return __assign(__assign({}, getGeometryOptions(props)), { includeWater: props.water, maxSulfurDistSq: props.sulfurDistanceMax * props.sulfurDistanceMax, maxDistSq: props.distanceMax * props.distanceMax });
}
var deg120InRad = degToRad(120);
function checkGeometry(structure, don, acc, opts) {
var donIndex = don.members[don.offsets[don.feature]];
var accIndex = acc.members[acc.offsets[acc.feature]];
if (!opts.includeBackbone && isBackboneHydrogenBond(don.unit, donIndex, acc.unit, accIndex))
return;
var donAngles = calcAngles(structure, don.unit, donIndex, acc.unit, accIndex);
var idealDonAngle = AtomGeometryAngles.get(don.idealGeometry[donIndex]) || deg120InRad;
if (donAngles.some(function (donAngle) { return Math.abs(idealDonAngle - donAngle) > opts.maxDonAngleDev; }))
return;
if (don.idealGeometry[donIndex] === 3 /* Trigonal */) {
var outOfPlane = calcPlaneAngle(structure, don.unit, donIndex, acc.unit, accIndex);
if (outOfPlane !== undefined && outOfPlane > opts.maxDonOutOfPlaneAngle)
return;
}
var accAngles = calcAngles(structure, acc.unit, accIndex, don.unit, donIndex);
var idealAccAngle = AtomGeometryAngles.get(acc.idealGeometry[accIndex]) || deg120InRad;
// Do not limit large acceptor angles
if (accAngles.some(function (accAngle) { return idealAccAngle - accAngle > opts.maxAccAngleDev; }))
return;
if (acc.idealGeometry[accIndex] === 3 /* Trigonal */) {
var outOfPlane = calcPlaneAngle(structure, acc.unit, accIndex, don.unit, donIndex);
if (outOfPlane !== undefined && outOfPlane > opts.maxAccOutOfPlaneAngle)
return;
}
return true;
}
function testHydrogenBond(structure, infoA, infoB, distanceSq, opts) {
var typeA = infoA.types[infoA.feature];
var typeB = infoB.types[infoB.feature];
if (!isHydrogenBond(typeA, typeB))
return;
var _a = typeB === 5 /* HydrogenAcceptor */ ? [infoA, infoB] : [infoB, infoA], don = _a[0], acc = _a[1];
var donIndex = don.members[don.offsets[don.feature]];
var accIndex = acc.members[acc.offsets[acc.feature]];
// check if distance is ok depending on non-sulfur-containing hbond
var maxDistSq = typeSymbol(don.unit, donIndex) === "S" /* S */ || typeSymbol(acc.unit, accIndex) === "S" /* S */ ? opts.maxSulfurDistSq : opts.maxDistSq;
if (distanceSq > maxDistSq)
return;
if (!opts.includeWater && isWaterHydrogenBond(don.unit, donIndex, acc.unit, accIndex))
return;
if (!checkGeometry(structure, don, acc, opts))
return;
return 4 /* HydrogenBond */;
}
function testWeakHydrogenBond(structure, infoA, infoB, distanceSq, opts) {
var typeA = infoA.types[infoA.feature];
var typeB = infoB.types[infoB.feature];
if (!isWeakHydrogenBond(typeA, typeB))
return;
var _a = typeB === 5 /* HydrogenAcceptor */ ? [infoA, infoB] : [infoB, infoA], don = _a[0], acc = _a[1];
if (!checkGeometry(structure, don, acc, opts))
return;
return 8 /* WeakHydrogenBond */;
}
//
export var HydrogenDonorProvider = Features.Provider([4 /* HydrogenDonor */], addUnitHydrogenDonors);
export var WeakHydrogenDonorProvider = Features.Provider([9 /* WeakHydrogenDonor */], addUnitWeakHydrogenDonors);
export var HydrogenAcceptorProvider = Features.Provider([5 /* HydrogenAcceptor */], addUnitHydrogenAcceptors);
export var HydrogenBondsProvider = {
name: 'hydrogen-bonds',
params: HydrogenBondsParams,
createTester: function (props) {
var maxDistance = Math.max(props.distanceMax, props.sulfurDistanceMax);
var opts = getHydrogenBondsOptions(props);
return {
maxDistance: maxDistance,
requiredFeatures: new Set([4 /* HydrogenDonor */, 5 /* HydrogenAcceptor */]),
getType: function (structure, infoA, infoB, distanceSq) { return testHydrogenBond(structure, infoA, infoB, distanceSq, opts); }
};
}
};
export var WeakHydrogenBondsProvider = {
name: 'weak-hydrogen-bonds',
params: WeakHydrogenBondsParams,
createTester: function (props) {
var opts = getGeometryOptions(props);
return {
maxDistance: props.distanceMax,
requiredFeatures: new Set([9 /* WeakHydrogenDonor */, 5 /* HydrogenAcceptor */]),
getType: function (structure, infoA, infoB, distanceSq) { return testWeakHydrogenBond(structure, infoA, infoB, distanceSq, opts); }
};
}
};
//# sourceMappingURL=hydrogen-bonds.js.map