halogen-bonds.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. /**
  2. * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. * @author Fred Ludlow <Fred.Ludlow@astx.com>
  6. *
  7. * based in part on NGL (https://github.com/arose/ngl)
  8. */
  9. import { ParamDefinition as PD } from '../../../mol-util/param-definition';
  10. import { Structure, Unit, StructureElement } from '../../../mol-model/structure';
  11. import { calcAngles } from '../chemistry/geometry';
  12. import { FeaturesBuilder, Features } from './features';
  13. import { ElementSymbol } from '../../../mol-model/structure/model/types';
  14. import { typeSymbol, eachBondedAtom } from '../chemistry/util';
  15. import { Elements } from '../../../mol-model/structure/model/properties/atomic/types';
  16. import { degToRad } from '../../../mol-math/misc';
  17. import { FeatureType, FeatureGroup, InteractionType } from './common';
  18. import { LinkProvider } from './links';
  19. const HalogenBondsParams = {
  20. distanceMax: PD.Numeric(4.0, { min: 1, max: 5, step: 0.1 }),
  21. angleMax: PD.Numeric(30, { min: 0, max: 60, step: 1 }),
  22. }
  23. type HalogenBondsParams = typeof HalogenBondsParams
  24. type HalogenBondsProps = PD.Values<HalogenBondsParams>
  25. const halBondElements = [Elements.CL, Elements.BR, Elements.I, Elements.AT] as ElementSymbol[]
  26. /**
  27. * Halogen bond donors (X-C, with X one of Cl, Br, I or At) not F!
  28. */
  29. function addUnitHalogenDonors(structure: Structure, unit: Unit.Atomic, builder: FeaturesBuilder) {
  30. const { elements } = unit
  31. const { x, y, z } = unit.model.atomicConformation
  32. for (let i = 0 as StructureElement.UnitIndex, il = elements.length; i < il; ++i) {
  33. const element = typeSymbol(unit, i)
  34. if (halBondElements.includes(element)) {
  35. builder.add(FeatureType.HalogenDonor, FeatureGroup.None, x[elements[i]], y[elements[i]], z[elements[i]], i)
  36. }
  37. }
  38. }
  39. const X = [Elements.N, Elements.O, Elements.S] as ElementSymbol[]
  40. const Y = [Elements.C, Elements.N, Elements.P, Elements.S] as ElementSymbol[]
  41. /**
  42. * Halogen bond acceptors (Y-{O|N|S}, with Y=C,P,N,S)
  43. */
  44. function addUnitHalogenAcceptors(structure: Structure, unit: Unit.Atomic, builder: FeaturesBuilder) {
  45. const { elements } = unit
  46. const { x, y, z } = unit.model.atomicConformation
  47. for (let i = 0 as StructureElement.UnitIndex, il = elements.length; i < il; ++i) {
  48. const element = typeSymbol(unit, i)
  49. if (X.includes(element)) {
  50. let flag = false
  51. eachBondedAtom(structure, unit, i, (unitB, indexB) => {
  52. if (Y.includes(typeSymbol(unitB, indexB))) {
  53. flag = true
  54. }
  55. })
  56. if (flag) {
  57. builder.add(FeatureType.HalogenAcceptor, FeatureGroup.None, x[elements[i]], y[elements[i]], z[elements[i]], i)
  58. }
  59. }
  60. }
  61. }
  62. function isHalogenBond(ti: FeatureType, tj: FeatureType) {
  63. return (
  64. (ti === FeatureType.HalogenAcceptor && tj === FeatureType.HalogenDonor) ||
  65. (ti === FeatureType.HalogenDonor && tj === FeatureType.HalogenAcceptor)
  66. )
  67. }
  68. // http://www.pnas.org/content/101/48/16789.full
  69. const OptimalHalogenAngle = degToRad(180) // adjusted from 165 to account for spherical statistics
  70. const OptimalAcceptorAngle = degToRad(120)
  71. function getOptions(props: HalogenBondsProps) {
  72. return {
  73. angleMax: degToRad(props.angleMax),
  74. }
  75. }
  76. type Options = ReturnType<typeof getOptions>
  77. function testHalogenBond(structure: Structure, infoA: Features.Info, infoB: Features.Info, opts: Options): InteractionType | undefined {
  78. const typeA = infoA.types[infoA.feature]
  79. const typeB = infoB.types[infoB.feature]
  80. if (!isHalogenBond(typeA, typeB)) return
  81. const [don, acc] = typeA === FeatureType.HalogenDonor ? [infoA, infoB] : [infoB, infoA]
  82. const donIndex = don.members[don.offsets[don.feature]]
  83. const accIndex = acc.members[acc.offsets[acc.feature]]
  84. const halogenAngles = calcAngles(structure, don.unit, donIndex, acc.unit, accIndex)
  85. // Singly bonded halogen only (not bromide ion for example)
  86. if (halogenAngles.length !== 1) return
  87. if (OptimalHalogenAngle - halogenAngles[0] > opts.angleMax) return
  88. const acceptorAngles = calcAngles(structure, acc.unit, accIndex, don.unit, donIndex)
  89. // Angle must be defined. Excludes water as acceptor. Debatable
  90. if (acceptorAngles.length === 0) return
  91. if (acceptorAngles.some(acceptorAngle => OptimalAcceptorAngle - acceptorAngle > opts.angleMax)) return
  92. return InteractionType.HalogenBond
  93. }
  94. //
  95. export const HalogenDonorProvider = Features.Provider([FeatureType.HalogenDonor], addUnitHalogenDonors)
  96. export const HalogenAcceptorProvider = Features.Provider([FeatureType.HalogenAcceptor], addUnitHalogenAcceptors)
  97. export const HalogenBondsProvider: LinkProvider<HalogenBondsParams> = {
  98. name: 'halogen-bonds',
  99. params: HalogenBondsParams,
  100. createTester: (props: HalogenBondsProps) => {
  101. const opts = getOptions(props)
  102. return {
  103. maxDistance: props.distanceMax,
  104. requiredFeatures: new Set([FeatureType.HalogenDonor, FeatureType.HalogenAcceptor]),
  105. getType: (structure, infoA, infoB) => testHalogenBond(structure, infoA, infoB, opts)
  106. }
  107. }
  108. }