interactions.ts 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  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. */
  6. import { ParamDefinition as PD } from '../../../mol-util/param-definition';
  7. import { Structure, Unit } from '../../../mol-model/structure';
  8. import { RuntimeContext } from '../../../mol-task';
  9. import { addUnitHydrogenDonors, addUnitWeakHydrogenDonors, addUnitHydrogenAcceptors, addUnitHydrogenBonds, HydrogenBondsParams, addStructureHydrogenBonds } from './hydrogen-bonds';
  10. import { Features, FeaturesBuilder } from './features';
  11. import { ValenceModelProvider } from '../valence-model';
  12. import { InteractionsIntraLinks, InteractionsInterLinks, InteractionType } from './common';
  13. import { IntraLinksBuilder, InterLinksBuilder } from './builder';
  14. import { IntMap } from '../../../mol-data/int';
  15. import { Vec3 } from '../../../mol-math/linear-algebra';
  16. export { Interactions }
  17. interface Interactions {
  18. /** Features of each unit */
  19. unitsFeatures: IntMap<Features>
  20. /** Interactions of each unit */
  21. unitsLinks: IntMap<InteractionsIntraLinks>
  22. /** Interactions between units */
  23. links: InteractionsInterLinks
  24. }
  25. namespace Interactions {
  26. export interface Location {
  27. readonly kind: 'interaction-location'
  28. interactions: Interactions
  29. unitA: Unit
  30. /** Index into features of unitA */
  31. indexA: number
  32. unitB: Unit
  33. /** Index into features of unitB */
  34. indexB: number
  35. }
  36. export function Location(interactions?: Interactions, unitA?: Unit, indexA?: number, unitB?: Unit, indexB?: number): Location {
  37. return { kind: 'interaction-location', interactions: interactions as any, unitA: unitA as any, indexA: indexA as any, unitB: unitB as any, indexB: indexB as any };
  38. }
  39. export function isLocation(x: any): x is Location {
  40. return !!x && x.kind === 'interaction-location';
  41. }
  42. export function areLocationsEqual(locA: Location, locB: Location) {
  43. return (
  44. locA.interactions === locB.interactions &&
  45. locA.indexA === locB.indexA && locA.indexB === locB.indexB &&
  46. locA.unitA === locB.unitA && locA.unitB === locB.unitB
  47. )
  48. }
  49. export interface Loci {
  50. readonly kind: 'interaction-loci'
  51. readonly structure: Structure
  52. readonly interactions: Interactions
  53. readonly links: ReadonlyArray<{
  54. unitA: Unit
  55. /** Index into features of unitA */
  56. indexA: number
  57. unitB: Unit
  58. /** Index into features of unitB */
  59. indexB: number
  60. }>
  61. }
  62. export function Loci(structure: Structure, interactions: Interactions, links: Loci['links']): Loci {
  63. return { kind: 'interaction-loci', structure, interactions, links };
  64. }
  65. export function isLoci(x: any): x is Loci {
  66. return !!x && x.kind === 'interaction-loci';
  67. }
  68. export function areLociEqual(a: Loci, b: Loci) {
  69. if (a.structure !== b.structure) return false
  70. if (a.interactions !== b.interactions) return false
  71. if (a.links.length !== b.links.length) return false
  72. for (let i = 0, il = a.links.length; i < il; ++i) {
  73. const linkA = a.links[i]
  74. const linkB = b.links[i]
  75. if (linkA.unitA !== linkB.unitA) return false
  76. if (linkA.unitB !== linkB.unitB) return false
  77. if (linkA.indexA !== linkB.indexA) return false
  78. if (linkA.indexB !== linkB.indexB) return false
  79. }
  80. return true
  81. }
  82. export function isLociEmpty(loci: Loci) {
  83. return loci.links.length === 0 ? true : false
  84. }
  85. export function typeLabel(type: InteractionType): string {
  86. switch (type) {
  87. case InteractionType.HydrogenBond:
  88. case InteractionType.WaterHydrogenBond:
  89. case InteractionType.BackboneHydrogenBond:
  90. return 'Hydrogen Bond'
  91. case InteractionType.Hydrophobic:
  92. return 'Hydrophobic Contact'
  93. case InteractionType.HalogenBond:
  94. return 'Halogen Bond'
  95. case InteractionType.IonicInteraction:
  96. return 'Ionic Interaction'
  97. case InteractionType.MetalCoordination:
  98. return 'Metal Coordination'
  99. case InteractionType.CationPi:
  100. return 'Cation-Pi Interaction'
  101. case InteractionType.PiStacking:
  102. return 'Pi-Pi Stacking'
  103. case InteractionType.WeakHydrogenBond:
  104. return 'Weak Hydrogen Bond'
  105. case InteractionType.Unknown:
  106. return 'Unknown Interaction'
  107. }
  108. }
  109. }
  110. export const InteractionsParams = {
  111. ...HydrogenBondsParams,
  112. }
  113. export type InteractionsParams = typeof InteractionsParams
  114. export type InteractionsProps = PD.Values<InteractionsParams>
  115. export async function computeInteractions(runtime: RuntimeContext, structure: Structure, props: Partial<InteractionsProps>) {
  116. const p = { ...PD.getDefaultValues(InteractionsParams), ...props }
  117. await ValenceModelProvider.attach(structure).runInContext(runtime)
  118. const unitsFeatures = IntMap.Mutable<Features>()
  119. const unitsLinks = IntMap.Mutable<InteractionsIntraLinks>()
  120. for (let i = 0, il = structure.units.length; i < il; ++i) {
  121. const u = structure.units[i]
  122. const d = findIntraUnitLinksAndFeatures(structure, u, p)
  123. unitsFeatures.set(u.id, d.features)
  124. unitsLinks.set(u.id, d.links)
  125. }
  126. const links = findInterUnitLinks(structure, unitsFeatures, p)
  127. return { unitsFeatures, unitsLinks, links }
  128. }
  129. function findIntraUnitLinksAndFeatures(structure: Structure, unit: Unit, props: InteractionsProps) {
  130. const featuresBuilder = FeaturesBuilder.create()
  131. if (Unit.isAtomic(unit)) {
  132. addUnitHydrogenDonors(structure, unit, featuresBuilder)
  133. addUnitWeakHydrogenDonors(structure, unit, featuresBuilder)
  134. addUnitHydrogenAcceptors(structure, unit, featuresBuilder)
  135. }
  136. const features = featuresBuilder.getFeatures()
  137. const linksBuilder = IntraLinksBuilder.create(features, unit.elements.length)
  138. if (Unit.isAtomic(unit)) {
  139. addUnitHydrogenBonds(structure, unit, features, linksBuilder, props)
  140. }
  141. return { features, links: linksBuilder.getLinks() }
  142. }
  143. const MAX_RADIUS = 5
  144. function findInterUnitLinks(structure: Structure, unitsFeatures: IntMap<Features>, props: InteractionsProps) {
  145. const builder = InterLinksBuilder.create()
  146. const lookup = structure.lookup3d;
  147. const imageCenter = Vec3.zero();
  148. for (const unitA of structure.units) {
  149. if (!Unit.isAtomic(unitA)) continue;
  150. const featuresA = unitsFeatures.get(unitA.id)
  151. const bs = unitA.lookup3d.boundary.sphere;
  152. Vec3.transformMat4(imageCenter, bs.center, unitA.conformation.operator.matrix);
  153. const closeUnits = lookup.findUnitIndices(imageCenter[0], imageCenter[1], imageCenter[2], bs.radius + MAX_RADIUS);
  154. for (let i = 0; i < closeUnits.count; i++) {
  155. const unitB = structure.units[closeUnits.indices[i]];
  156. if (!Unit.isAtomic(unitB) || unitA.id >= unitB.id || !Structure.validUnitPair(structure, unitA, unitB)) continue;
  157. const featuresB = unitsFeatures.get(unitB.id)
  158. if (unitB.elements.length >= unitA.elements.length) {
  159. addStructureHydrogenBonds(structure, unitA, featuresA, unitB, featuresB, builder, props)
  160. } else {
  161. addStructureHydrogenBonds(structure, unitB, featuresB, unitA, featuresA, builder, props)
  162. }
  163. }
  164. }
  165. return builder.getLinks()
  166. }