interactions.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  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 { Features, FeaturesBuilder } from './features';
  10. import { ValenceModelProvider } from '../valence-model';
  11. import { InteractionsIntraContacts, InteractionsInterContacts, FeatureType } from './common';
  12. import { IntraContactsBuilder, InterContactsBuilder } from './contacts-builder';
  13. import { IntMap } from '../../../mol-data/int';
  14. import { Vec3 } from '../../../mol-math/linear-algebra';
  15. import { addUnitContacts, ContactTester, addStructureContacts, ContactProvider, ContactsParams, ContactsProps } from './contacts';
  16. import { HalogenDonorProvider, HalogenAcceptorProvider, HalogenBondsProvider } from './halogen-bonds';
  17. import { HydrogenDonorProvider, WeakHydrogenDonorProvider, HydrogenAcceptorProvider, HydrogenBondsProvider, WeakHydrogenBondsProvider } from './hydrogen-bonds';
  18. import { NegativChargeProvider, PositiveChargeProvider, AromaticRingProvider, IonicProvider, PiStackingProvider, CationPiProvider } from './charged';
  19. import { HydrophobicAtomProvider, HydrophobicProvider } from './hydrophobic';
  20. import { SetUtils } from '../../../mol-util/set';
  21. import { MetalCoordinationProvider, MetalProvider, MetalBindingProvider } from './metal';
  22. import { refineInteractions } from './refine';
  23. import { Result } from '../../../mol-math/geometry';
  24. export { Interactions }
  25. interface Interactions {
  26. /** Features of each unit */
  27. unitsFeatures: IntMap<Features>
  28. /** Interactions of each unit */
  29. unitsContacts: IntMap<InteractionsIntraContacts>
  30. /** Interactions between units */
  31. contacts: InteractionsInterContacts
  32. }
  33. namespace Interactions {
  34. export interface Location {
  35. readonly kind: 'interaction-location'
  36. interactions: Interactions
  37. unitA: Unit
  38. /** Index into features of unitA */
  39. indexA: Features.FeatureIndex
  40. unitB: Unit
  41. /** Index into features of unitB */
  42. indexB: Features.FeatureIndex
  43. }
  44. export function Location(interactions?: Interactions, unitA?: Unit, indexA?: Features.FeatureIndex, unitB?: Unit, indexB?: Features.FeatureIndex): Location {
  45. return { kind: 'interaction-location', interactions: interactions as any, unitA: unitA as any, indexA: indexA as any, unitB: unitB as any, indexB: indexB as any };
  46. }
  47. export function isLocation(x: any): x is Location {
  48. return !!x && x.kind === 'interaction-location';
  49. }
  50. export function areLocationsEqual(locA: Location, locB: Location) {
  51. return (
  52. locA.interactions === locB.interactions &&
  53. locA.indexA === locB.indexA && locA.indexB === locB.indexB &&
  54. locA.unitA === locB.unitA && locA.unitB === locB.unitB
  55. )
  56. }
  57. export interface Loci {
  58. readonly kind: 'interaction-loci'
  59. readonly structure: Structure
  60. readonly interactions: Interactions
  61. readonly links: ReadonlyArray<{
  62. unitA: Unit
  63. /** Index into features of unitA */
  64. indexA: Features.FeatureIndex
  65. unitB: Unit
  66. /** Index into features of unitB */
  67. indexB: Features.FeatureIndex
  68. }>
  69. }
  70. export function Loci(structure: Structure, interactions: Interactions, links: Loci['links']): Loci {
  71. return { kind: 'interaction-loci', structure, interactions, links };
  72. }
  73. export function isLoci(x: any): x is Loci {
  74. return !!x && x.kind === 'interaction-loci';
  75. }
  76. export function areLociEqual(a: Loci, b: Loci) {
  77. if (a.structure !== b.structure) return false
  78. if (a.interactions !== b.interactions) return false
  79. if (a.links.length !== b.links.length) return false
  80. for (let i = 0, il = a.links.length; i < il; ++i) {
  81. const linkA = a.links[i]
  82. const linkB = b.links[i]
  83. if (linkA.unitA !== linkB.unitA) return false
  84. if (linkA.unitB !== linkB.unitB) return false
  85. if (linkA.indexA !== linkB.indexA) return false
  86. if (linkA.indexB !== linkB.indexB) return false
  87. }
  88. return true
  89. }
  90. export function isLociEmpty(loci: Loci) {
  91. return loci.links.length === 0 ? true : false
  92. }
  93. }
  94. const FeatureProviders = [
  95. HydrogenDonorProvider, WeakHydrogenDonorProvider, HydrogenAcceptorProvider,
  96. NegativChargeProvider, PositiveChargeProvider, AromaticRingProvider,
  97. HalogenDonorProvider, HalogenAcceptorProvider,
  98. HydrophobicAtomProvider,
  99. MetalProvider, MetalBindingProvider,
  100. ]
  101. const ContactProviders = {
  102. 'ionic': IonicProvider,
  103. 'pi-stacking': PiStackingProvider,
  104. 'cation-pi': CationPiProvider,
  105. 'halogen-bonds': HalogenBondsProvider,
  106. 'hydrogen-bonds': HydrogenBondsProvider,
  107. 'weak-hydrogen-bonds': WeakHydrogenBondsProvider,
  108. 'hydrophobic': HydrophobicProvider,
  109. 'metal-coordination': MetalCoordinationProvider,
  110. }
  111. type ContactProviders = typeof ContactProviders
  112. function getProvidersParams() {
  113. const params: { [k in keyof ContactProviders]: PD.Group<ContactProviders[k]['params']> } = Object.create(null)
  114. Object.keys(ContactProviders).forEach(k => {
  115. (params as any)[k] = PD.Group(ContactProviders[k as keyof ContactProviders].params)
  116. })
  117. return params
  118. }
  119. export const InteractionsParams = {
  120. types: PD.MultiSelect([
  121. 'ionic',
  122. 'cation-pi',
  123. 'pi-stacking',
  124. 'hydrogen-bonds',
  125. 'halogen-bonds',
  126. // 'hydrophobic',
  127. 'metal-coordination',
  128. // 'weak-hydrogen-bonds',
  129. ], PD.objectToOptions(ContactProviders)),
  130. contacts: PD.Group(ContactsParams, { isFlat: true }),
  131. ...getProvidersParams()
  132. }
  133. export type InteractionsParams = typeof InteractionsParams
  134. export type InteractionsProps = PD.Values<InteractionsParams>
  135. export async function computeInteractions(runtime: RuntimeContext, structure: Structure, props: Partial<InteractionsProps>): Promise<Interactions> {
  136. const p = { ...PD.getDefaultValues(InteractionsParams), ...props }
  137. await ValenceModelProvider.attach(structure).runInContext(runtime)
  138. const contactProviders: ContactProvider<any>[] = []
  139. Object.keys(ContactProviders).forEach(k => {
  140. if (p.types.includes(k)) contactProviders.push(ContactProviders[k as keyof typeof ContactProviders])
  141. })
  142. const contactTesters = contactProviders.map(l => l.createTester(p[l.name as keyof InteractionsProps]))
  143. const requiredFeatures = new Set<FeatureType>()
  144. contactTesters.forEach(l => SetUtils.add(requiredFeatures, l.requiredFeatures))
  145. const featureProviders = FeatureProviders.filter(f => SetUtils.areIntersecting(requiredFeatures, f.types))
  146. const unitsFeatures = IntMap.Mutable<Features>()
  147. const unitsContacts = IntMap.Mutable<InteractionsIntraContacts>()
  148. for (let i = 0, il = structure.unitSymmetryGroups.length; i < il; ++i) {
  149. const group = structure.unitSymmetryGroups[i]
  150. if (runtime.shouldUpdate) {
  151. await runtime.update({ message: 'computing interactions', current: i, max: il })
  152. }
  153. const features = findUnitFeatures(structure, group.units[0], featureProviders)
  154. const intraUnitContacts = findIntraUnitContacts(structure, group.units[0], features, contactTesters, p.contacts)
  155. for (let j = 0, jl = group.units.length; j < jl; ++j) {
  156. const u = group.units[j]
  157. unitsFeatures.set(u.id, features)
  158. unitsContacts.set(u.id, intraUnitContacts)
  159. }
  160. }
  161. const contacts = findInterUnitContacts(structure, unitsFeatures, contactTesters, p.contacts)
  162. const interactions = { unitsFeatures, unitsContacts, contacts }
  163. refineInteractions(structure, interactions)
  164. return interactions
  165. }
  166. function findUnitFeatures(structure: Structure, unit: Unit, featureProviders: Features.Provider[]) {
  167. const count = unit.elements.length
  168. const featuresBuilder = FeaturesBuilder.create(count, count / 2)
  169. if (Unit.isAtomic(unit)) {
  170. for (const fp of featureProviders) {
  171. fp.add(structure, unit, featuresBuilder)
  172. }
  173. }
  174. return featuresBuilder.getFeatures(count)
  175. }
  176. function findIntraUnitContacts(structure: Structure, unit: Unit, features: Features, contactTesters: ReadonlyArray<ContactTester>, props: ContactsProps) {
  177. const builder = IntraContactsBuilder.create(features, unit.elements.length)
  178. if (Unit.isAtomic(unit)) {
  179. addUnitContacts(structure, unit, features, builder, contactTesters, props)
  180. }
  181. return builder.getContacts()
  182. }
  183. function findInterUnitContacts(structure: Structure, unitsFeatures: IntMap<Features>, contactTesters: ReadonlyArray<ContactTester>, props: ContactsProps) {
  184. const builder = InterContactsBuilder.create()
  185. const maxDistance = Math.max(...contactTesters.map(t => t.maxDistance))
  186. const lookup = structure.lookup3d;
  187. const imageCenter = Vec3.zero();
  188. const closeUnits = Result.create()
  189. for (const unitA of structure.units) {
  190. if (!Unit.isAtomic(unitA)) continue;
  191. const featuresA = unitsFeatures.get(unitA.id)
  192. const bs = unitA.lookup3d.boundary.sphere;
  193. Vec3.transformMat4(imageCenter, bs.center, unitA.conformation.operator.matrix);
  194. Result.copy(closeUnits, lookup.findUnitIndices(imageCenter[0], imageCenter[1], imageCenter[2], bs.radius + maxDistance));
  195. for (let i = 0; i < closeUnits.count; i++) {
  196. const unitB = structure.units[closeUnits.indices[i]];
  197. if (!Unit.isAtomic(unitB) || unitA.id >= unitB.id || !Structure.validUnitPair(structure, unitA, unitB)) continue;
  198. const featuresB = unitsFeatures.get(unitB.id)
  199. if (unitB.elements.length >= unitA.elements.length) {
  200. addStructureContacts(structure, unitA, featuresA, unitB, featuresB, builder, contactTesters, props)
  201. } else {
  202. addStructureContacts(structure, unitB, featuresB, unitA, featuresA, builder, contactTesters, props)
  203. }
  204. }
  205. }
  206. return builder.getContacts(unitsFeatures)
  207. }