intra-compute.ts 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /**
  2. * Copyright (c) 2017-2020 Mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  6. */
  7. import { BondType } from '../../../model/types';
  8. import { IntraUnitBonds } from './data';
  9. import Unit from '../../unit';
  10. import { IntAdjacencyGraph } from '../../../../../mol-math/graph';
  11. import { BondComputationProps, getElementIdx, MetalsSet, getElementThreshold, isHydrogen, getElementPairThreshold, DefaultBondComputationProps } from './common';
  12. import { SortedArray } from '../../../../../mol-data/int';
  13. import { getIntraBondOrderFromTable } from '../../../model/properties/atomic/bonds';
  14. import StructureElement from '../../element';
  15. import { IndexPairBonds } from '../../../../../mol-model-formats/structure/property/bonds/index-pair';
  16. import { ComponentBond } from '../../../../../mol-model-formats/structure/property/bonds/chem_comp';
  17. import { StructConn } from '../../../../../mol-model-formats/structure/property/bonds/struct_conn';
  18. import { Vec3 } from '../../../../../mol-math/linear-algebra';
  19. import { ElementIndex } from '../../../model/indexing';
  20. import { equalEps } from '../../../../../mol-math/linear-algebra/3d/common';
  21. function getGraph(atomA: StructureElement.UnitIndex[], atomB: StructureElement.UnitIndex[], _order: number[], _flags: number[], atomCount: number): IntraUnitBonds {
  22. const builder = new IntAdjacencyGraph.EdgeBuilder(atomCount, atomA, atomB);
  23. const flags = new Uint16Array(builder.slotCount);
  24. const order = new Int8Array(builder.slotCount);
  25. for (let i = 0, _i = builder.edgeCount; i < _i; i++) {
  26. builder.addNextEdge();
  27. builder.assignProperty(flags, _flags[i]);
  28. builder.assignProperty(order, _order[i]);
  29. }
  30. return builder.createGraph({ flags, order });
  31. }
  32. const tmpDistVecA = Vec3();
  33. const tmpDistVecB = Vec3();
  34. function getDistance(unit: Unit.Atomic, indexA: ElementIndex, indexB: ElementIndex) {
  35. unit.conformation.position(indexA, tmpDistVecA);
  36. unit.conformation.position(indexB, tmpDistVecB);
  37. return Vec3.distance(tmpDistVecA, tmpDistVecB);
  38. }
  39. const __structConnAdded = new Set<StructureElement.UnitIndex>();
  40. function _computeBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBonds {
  41. const MAX_RADIUS = 4;
  42. const { x, y, z } = unit.model.atomicConformation;
  43. const atomCount = unit.elements.length;
  44. const { elements: atoms, residueIndex, chainIndex } = unit;
  45. const { type_symbol, label_atom_id, label_alt_id, label_comp_id } = unit.model.atomicHierarchy.atoms;
  46. const { label_seq_id } = unit.model.atomicHierarchy.residues;
  47. const { index } = unit.model.atomicHierarchy;
  48. const { byEntityKey } = unit.model.sequence;
  49. const query3d = unit.lookup3d;
  50. const structConn = StructConn.Provider.get(unit.model);
  51. const component = ComponentBond.Provider.get(unit.model);
  52. const indexPairs = IndexPairBonds.Provider.get(unit.model);
  53. const atomA: StructureElement.UnitIndex[] = [];
  54. const atomB: StructureElement.UnitIndex[] = [];
  55. const flags: number[] = [];
  56. const order: number[] = [];
  57. let lastResidue = -1;
  58. let componentMap: Map<string, Map<string, { flags: number, order: number }>> | undefined = void 0;
  59. const structConnAdded = __structConnAdded;
  60. for (let _aI = 0 as StructureElement.UnitIndex; _aI < atomCount; _aI++) {
  61. const aI = atoms[_aI];
  62. if (!props.forceCompute && indexPairs) {
  63. const { edgeProps } = indexPairs;
  64. for (let i = indexPairs.offset[aI], il = indexPairs.offset[aI + 1]; i < il; ++i) {
  65. const bI = indexPairs.b[i];
  66. if (aI >= bI) continue;
  67. const _bI = SortedArray.indexOf(unit.elements, bI) as StructureElement.UnitIndex;
  68. if (_bI < 0) continue;
  69. if (type_symbol.value(aI) === 'H' && type_symbol.value(bI) === 'H') continue;
  70. const d = edgeProps.distance[i];
  71. if (d === -1 || equalEps(getDistance(unit, aI, bI), d, 0.5)) {
  72. atomA[atomA.length] = _aI;
  73. atomB[atomB.length] = _bI;
  74. order[order.length] = edgeProps.order[i];
  75. flags[flags.length] = edgeProps.flag[i];
  76. }
  77. }
  78. continue; // assume `indexPairs` supplies all bonds
  79. }
  80. const structConnEntries = props.forceCompute ? void 0 : structConn && structConn.byAtomIndex.get(aI);
  81. let hasStructConn = false;
  82. if (structConnEntries) {
  83. for (const se of structConnEntries) {
  84. const { partnerA, partnerB } = se;
  85. // symmetry must be the same for intra-unit bonds
  86. if (partnerA.symmetry !== partnerB.symmetry) continue;
  87. const p = partnerA.atomIndex === aI ? partnerB : partnerA;
  88. const _bI = SortedArray.indexOf(unit.elements, p.atomIndex) as StructureElement.UnitIndex;
  89. if (_bI < 0) continue;
  90. atomA[atomA.length] = _aI;
  91. atomB[atomB.length] = _bI;
  92. flags[flags.length] = se.flags;
  93. order[order.length] = se.order;
  94. if (!hasStructConn) structConnAdded.clear();
  95. hasStructConn = true;
  96. structConnAdded.add(_bI);
  97. }
  98. }
  99. const raI = residueIndex[aI];
  100. const compId = label_comp_id.value(aI);
  101. if (!props.forceCompute && raI !== lastResidue) {
  102. if (!!component && component.entries.has(compId)) {
  103. const entitySeq = byEntityKey[index.getEntityFromChain(chainIndex[aI])];
  104. if (entitySeq && entitySeq.sequence.microHet.has(label_seq_id.value(raI))) {
  105. // compute for sequence positions with micro-heterogeneity
  106. componentMap = void 0;
  107. } else {
  108. componentMap = component.entries.get(compId)!.map;
  109. }
  110. } else {
  111. componentMap = void 0;
  112. }
  113. }
  114. lastResidue = raI;
  115. const aeI = getElementIdx(type_symbol.value(aI));
  116. const atomIdA = label_atom_id.value(aI);
  117. const componentPairs = componentMap ? componentMap.get(atomIdA) : void 0;
  118. const { indices, count, squaredDistances } = query3d.find(x[aI], y[aI], z[aI], MAX_RADIUS);
  119. const isHa = isHydrogen(aeI);
  120. const thresholdA = getElementThreshold(aeI);
  121. const altA = label_alt_id.value(aI);
  122. const metalA = MetalsSet.has(aeI);
  123. for (let ni = 0; ni < count; ni++) {
  124. const _bI = indices[ni];
  125. if (hasStructConn && structConnAdded.has(_bI)) continue;
  126. const bI = atoms[_bI];
  127. if (bI <= aI) continue;
  128. const altB = label_alt_id.value(bI);
  129. if (altA && altB && altA !== altB) continue;
  130. const beI = getElementIdx(type_symbol.value(bI)!);
  131. const isHb = isHydrogen(beI);
  132. if (isHa && isHb) continue;
  133. const isMetal = (metalA || MetalsSet.has(beI)) && !(isHa || isHb);
  134. const rbI = residueIndex[bI];
  135. // handle "component dictionary" bonds.
  136. if (raI === rbI && componentPairs) {
  137. const e = componentPairs.get(label_atom_id.value(bI)!);
  138. if (e) {
  139. atomA[atomA.length] = _aI;
  140. atomB[atomB.length] = _bI;
  141. order[order.length] = e.order;
  142. let flag = e.flags;
  143. if (isMetal) {
  144. if (flag | BondType.Flag.Covalent) flag ^= BondType.Flag.Covalent;
  145. flag |= BondType.Flag.MetallicCoordination;
  146. }
  147. flags[flags.length] = flag;
  148. }
  149. continue;
  150. }
  151. const dist = Math.sqrt(squaredDistances[ni]);
  152. if (dist === 0) continue;
  153. const thresholdAB = getElementPairThreshold(aeI, beI);
  154. const pairingThreshold = thresholdAB > 0
  155. ? thresholdAB
  156. : beI < 0
  157. ? thresholdA
  158. : (thresholdA + getElementThreshold(beI)) / 2; // not sure if avg or min but max is too big
  159. if (dist <= pairingThreshold) {
  160. atomA[atomA.length] = _aI;
  161. atomB[atomB.length] = _bI;
  162. order[order.length] = getIntraBondOrderFromTable(compId, atomIdA, label_atom_id.value(bI));
  163. flags[flags.length] = (isMetal ? BondType.Flag.MetallicCoordination : BondType.Flag.Covalent) | BondType.Flag.Computed;
  164. }
  165. }
  166. }
  167. return getGraph(atomA, atomB, order, flags, atomCount);
  168. }
  169. function computeIntraUnitBonds(unit: Unit.Atomic, props?: Partial<BondComputationProps>) {
  170. const p = { ...DefaultBondComputationProps, ...props };
  171. if (p.noCompute) {
  172. // TODO add function that only adds bonds defined in structConn of chemCompBond
  173. // and avoid using unit.lookup
  174. return IntraUnitBonds.Empty;
  175. }
  176. return _computeBonds(unit, p);
  177. }
  178. export { computeIntraUnitBonds };