atomic-index.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. /**
  2. * Copyright (c) 2017-2021 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 { AtomicData, AtomicSegments } from '../atomic';
  8. import { Interval, Segmentation, SortedArray } from '../../../../../mol-data/int';
  9. import { Entities } from '../common';
  10. import { ChainIndex, ResidueIndex, EntityIndex, ElementIndex } from '../../indexing';
  11. import { AtomicIndex, AtomicHierarchy } from '../atomic/hierarchy';
  12. import { cantorPairing } from '../../../../../mol-data/util';
  13. import { Column } from '../../../../../mol-data/db';
  14. import { ElementSymbol } from '../../types';
  15. function getResidueId(seq_id: number, ins_code: string) {
  16. if (!ins_code) return seq_id;
  17. if (ins_code.length === 1) return cantorPairing(ins_code.charCodeAt(0), seq_id);
  18. if (ins_code.length === 2) return cantorPairing(ins_code.charCodeAt(0), cantorPairing(ins_code.charCodeAt(1), seq_id));
  19. return `${seq_id} ${ins_code}`;
  20. }
  21. function updateMapMapIndex<K, I extends number>(map: Map<K, Map<string, I>>, key0: K, key1: string, index: I) {
  22. if (map.has(key0)) {
  23. const submap = map.get(key0)!;
  24. if (!submap.has(key1)) {
  25. submap.set(key1, index);
  26. }
  27. } else {
  28. const submap = new Map<string, I>();
  29. map.set(key0, submap);
  30. submap.set(key1, index);
  31. }
  32. }
  33. function missingEntity(k: string) {
  34. throw new Error(`Missing entity entry for entity id '${k}'.`);
  35. }
  36. interface Mapping {
  37. entities: Entities,
  38. label_seq_id: SortedArray,
  39. label_atom_id: Column<string>,
  40. auth_atom_id: Column<string>,
  41. label_alt_id: Column<string>,
  42. type_symbol: Column<ElementSymbol>,
  43. segments: AtomicSegments,
  44. chain_index_entity_index: EntityIndex[],
  45. entity_index_label_asym_id: Map<EntityIndex, Map<string, ChainIndex>>,
  46. chain_index_label_seq_id: Map<ChainIndex, Map<string | number, ResidueIndex>>,
  47. auth_asym_id_auth_seq_id: Map<string, Map<number, ChainIndex>>,
  48. chain_index_auth_seq_id: Map<ChainIndex, Map<string | number, ResidueIndex>>,
  49. label_asym_id: Map<string, EntityIndex>,
  50. }
  51. function createMapping(entities: Entities, data: AtomicData, segments: AtomicSegments): Mapping {
  52. return {
  53. entities,
  54. segments,
  55. label_seq_id: SortedArray.ofSortedArray(data.residues.label_seq_id.toArray({ array: Int32Array })),
  56. label_atom_id: data.atoms.label_atom_id,
  57. auth_atom_id: data.atoms.auth_atom_id,
  58. label_alt_id: data.atoms.label_alt_id,
  59. type_symbol: data.atoms.type_symbol,
  60. chain_index_entity_index: new Int32Array(data.chains._rowCount) as any,
  61. entity_index_label_asym_id: new Map(),
  62. chain_index_label_seq_id: new Map(),
  63. auth_asym_id_auth_seq_id: new Map(),
  64. chain_index_auth_seq_id: new Map(),
  65. label_asym_id: new Map(),
  66. };
  67. }
  68. const _tempResidueKey = AtomicIndex.EmptyResidueKey();
  69. class Index implements AtomicIndex {
  70. private entityIndex: Entities['getEntityIndex'];
  71. private residueOffsets: ArrayLike<ElementIndex>;
  72. getEntityFromChain(cI: ChainIndex): EntityIndex {
  73. return this.map.chain_index_entity_index[cI];
  74. }
  75. findEntity(label_asym_id: string): EntityIndex {
  76. const entityIndex = this.map.label_asym_id.get(label_asym_id);
  77. return entityIndex !== undefined ? entityIndex : -1 as EntityIndex;
  78. }
  79. findChainLabel(key: AtomicIndex.ChainLabelKey): ChainIndex {
  80. const eI = this.entityIndex(key.label_entity_id);
  81. if (eI < 0 || !this.map.entity_index_label_asym_id.has(eI)) return -1 as ChainIndex;
  82. const cm = this.map.entity_index_label_asym_id.get(eI);
  83. if (!cm) return -1 as ChainIndex;
  84. return cm.has(key.label_asym_id) ? cm.get(key.label_asym_id)! : -1 as ChainIndex;
  85. }
  86. findChainAuth(key: AtomicIndex.ChainAuthKey): ChainIndex {
  87. if (!this.map.auth_asym_id_auth_seq_id.has(key.auth_asym_id)) return -1 as ChainIndex;
  88. const rm = this.map.auth_asym_id_auth_seq_id.get(key.auth_asym_id)!;
  89. return rm.has(key.auth_seq_id) ? rm.get(key.auth_seq_id)! : -1 as ChainIndex;
  90. }
  91. findResidue(label_entity_id: string, label_asym_id: string, auth_seq_id: number, pdbx_PDB_ins_code?: string): ResidueIndex
  92. findResidue(key: AtomicIndex.ResidueKey): ResidueIndex
  93. findResidue(label_entity_id_or_key: string | AtomicIndex.ResidueKey, label_asym_id?: string, auth_seq_id?: number, pdbx_PDB_ins_code?: string): ResidueIndex {
  94. let key: AtomicIndex.ResidueKey;
  95. if (arguments.length === 1) {
  96. key = label_entity_id_or_key as AtomicIndex.ResidueKey;
  97. } else {
  98. _tempResidueKey.label_entity_id = label_entity_id_or_key as string;
  99. _tempResidueKey.label_asym_id = label_asym_id!;
  100. _tempResidueKey.auth_seq_id = auth_seq_id!;
  101. _tempResidueKey.pdbx_PDB_ins_code = pdbx_PDB_ins_code;
  102. key = _tempResidueKey;
  103. }
  104. const cI = this.findChainLabel(key);
  105. if (cI < 0) return -1 as ResidueIndex;
  106. const rm = this.map.chain_index_auth_seq_id.get(cI)!;
  107. const id = getResidueId(key.auth_seq_id, key.pdbx_PDB_ins_code || '');
  108. return rm.has(id) ? rm.get(id)! : -1 as ResidueIndex;
  109. }
  110. findResidueAuth(key: AtomicIndex.ResidueAuthKey): ResidueIndex {
  111. const cI = this.findChainAuth(key);
  112. if (cI < 0) return -1 as ResidueIndex;
  113. const rm = this.map.chain_index_auth_seq_id.get(cI)!;
  114. const id = getResidueId(key.auth_seq_id, key.pdbx_PDB_ins_code || '');
  115. return rm.has(id) ? rm.get(id)! : -1 as ResidueIndex;
  116. }
  117. findResidueInsertion(key: AtomicIndex.ResidueLabelKey): ResidueIndex {
  118. const cI = this.findChainLabel(key);
  119. if (cI < 0) return -1 as ResidueIndex;
  120. const rm = this.map.chain_index_label_seq_id.get(cI)!;
  121. const id = getResidueId(key.label_seq_id, key.pdbx_PDB_ins_code || '');
  122. if (rm.has(id)) return rm.get(id)!;
  123. const idx = SortedArray.findPredecessorIndex(this.map.label_seq_id, key.label_seq_id) as ResidueIndex;
  124. const start = AtomicHierarchy.chainStartResidueIndex(this.map.segments, cI);
  125. if (idx < start) return start;
  126. const end = AtomicHierarchy.chainEndResidueIndexExcl(this.map.segments, cI) - 1 as ResidueIndex;
  127. if (idx >= end) return end;
  128. return idx;
  129. }
  130. findAtom(key: AtomicIndex.AtomKey): ElementIndex {
  131. const rI = this.findResidue(key);
  132. if (rI < 0) return -1 as ElementIndex;
  133. if (typeof key.label_alt_id === 'undefined') {
  134. return findAtomByName(this.residueOffsets[rI], this.residueOffsets[rI + 1], this.map.label_atom_id, key.label_atom_id);
  135. }
  136. return findAtomByNameAndAltLoc(this.residueOffsets[rI], this.residueOffsets[rI + 1], this.map.label_atom_id, this.map.label_alt_id, key.label_atom_id, key.label_alt_id);
  137. }
  138. findAtomAuth(key: AtomicIndex.AtomAuthKey): ElementIndex {
  139. const rI = this.findResidueAuth(key);
  140. if (rI < 0) return -1 as ElementIndex;
  141. if (typeof key.label_alt_id === 'undefined') {
  142. return findAtomByName(this.residueOffsets[rI], this.residueOffsets[rI + 1], this.map.auth_atom_id, key.auth_atom_id);
  143. }
  144. return findAtomByNameAndAltLoc(this.residueOffsets[rI], this.residueOffsets[rI + 1], this.map.auth_atom_id, this.map.label_alt_id, key.auth_atom_id, key.label_alt_id);
  145. }
  146. findAtomOnResidue(rI: ResidueIndex, label_atom_id: string, label_alt_id?: string) {
  147. if (typeof label_alt_id === 'undefined') {
  148. return findAtomByName(this.residueOffsets[rI], this.residueOffsets[rI + 1], this.map.label_atom_id, label_atom_id);
  149. }
  150. return findAtomByNameAndAltLoc(this.residueOffsets[rI], this.residueOffsets[rI + 1], this.map.label_atom_id, this.map.label_alt_id, label_atom_id, label_alt_id);
  151. }
  152. findAtomsOnResidue(rI: ResidueIndex, label_atom_ids: Set<string>) {
  153. return findAtomByNames(this.residueOffsets[rI], this.residueOffsets[rI + 1], this.map.label_atom_id, label_atom_ids);
  154. }
  155. findElementOnResidue(rI: ResidueIndex, type_symbol: ElementSymbol) {
  156. return findAtomByElement(this.residueOffsets[rI], this.residueOffsets[rI + 1], this.map.type_symbol, type_symbol);
  157. }
  158. constructor(private map: Mapping) {
  159. this.entityIndex = map.entities.getEntityIndex;
  160. this.residueOffsets = this.map.segments.residueAtomSegments.offsets;
  161. }
  162. }
  163. function findAtomByName(start: ElementIndex, end: ElementIndex, data: Column<string>, atomName: string): ElementIndex {
  164. for (let i = start; i < end; i++) {
  165. if (data.value(i) === atomName) return i;
  166. }
  167. return -1 as ElementIndex;
  168. }
  169. function findAtomByNames(start: ElementIndex, end: ElementIndex, data: Column<string>, atomNames: Set<string>): ElementIndex {
  170. for (let i = start; i < end; i++) {
  171. if (atomNames.has(data.value(i))) return i;
  172. }
  173. return -1 as ElementIndex;
  174. }
  175. function findAtomByNameAndAltLoc(start: ElementIndex, end: ElementIndex, nameData: Column<string>, altLocData: Column<string>,
  176. atomName: string, altLoc: string): ElementIndex {
  177. for (let i = start; i < end; i++) {
  178. if (nameData.value(i) === atomName && altLocData.value(i) === altLoc) return i;
  179. }
  180. return -1 as ElementIndex;
  181. }
  182. function findAtomByElement(start: ElementIndex, end: ElementIndex, data: Column<ElementSymbol>, typeSymbol: ElementSymbol): ElementIndex {
  183. for (let i = start; i < end; i++) {
  184. if (data.value(i) === typeSymbol) return i;
  185. }
  186. return -1 as ElementIndex;
  187. }
  188. export function getAtomicIndex(data: AtomicData, entities: Entities, segments: AtomicSegments): AtomicIndex {
  189. const map = createMapping(entities, data, segments);
  190. const { label_seq_id, auth_seq_id, pdbx_PDB_ins_code } = data.residues;
  191. const { label_entity_id, label_asym_id, auth_asym_id } = data.chains;
  192. const atomSet = Interval.ofBounds(0, data.atoms._rowCount);
  193. const chainsIt = Segmentation.transientSegments(segments.chainAtomSegments, atomSet);
  194. while (chainsIt.hasNext) {
  195. const chainSegment = chainsIt.move();
  196. const chainIndex = chainSegment.index;
  197. const entityIndex = entities.getEntityIndex(label_entity_id.value(chainIndex));
  198. if (entityIndex < 0) missingEntity(label_entity_id.value(chainIndex));
  199. map.chain_index_entity_index[chainIndex] = entityIndex;
  200. const authAsymId = auth_asym_id.value(chainIndex);
  201. let auth_asym_id_auth_seq_id = map.auth_asym_id_auth_seq_id.get(authAsymId);
  202. if (!auth_asym_id_auth_seq_id) {
  203. auth_asym_id_auth_seq_id = new Map<number, ChainIndex>();
  204. map.auth_asym_id_auth_seq_id.set(authAsymId, auth_asym_id_auth_seq_id);
  205. }
  206. const labelAsymId = label_asym_id.value(chainIndex);
  207. if (!map.label_asym_id.has(labelAsymId)) map.label_asym_id.set(labelAsymId, entityIndex);
  208. updateMapMapIndex(map.entity_index_label_asym_id, entityIndex, labelAsymId, chainIndex);
  209. const chain_index_label_seq_id = new Map<string | number, ResidueIndex>();
  210. const chain_index_auth_seq_id = new Map<string | number, ResidueIndex>();
  211. map.chain_index_label_seq_id.set(chainIndex, chain_index_label_seq_id);
  212. map.chain_index_auth_seq_id.set(chainIndex, chain_index_auth_seq_id);
  213. const residuesIt = Segmentation.transientSegments(segments.residueAtomSegments, atomSet, chainSegment);
  214. while (residuesIt.hasNext) {
  215. const residueSegment = residuesIt.move();
  216. const rI = residueSegment.index;
  217. const authSeqId = auth_seq_id.value(rI);
  218. const insCode = pdbx_PDB_ins_code.value(rI);
  219. chain_index_label_seq_id.set(getResidueId(label_seq_id.value(rI), insCode), rI);
  220. chain_index_auth_seq_id.set(getResidueId(authSeqId, insCode), rI);
  221. auth_asym_id_auth_seq_id.set(authSeqId, chainIndex);
  222. }
  223. }
  224. return new Index(map);
  225. }