secondary-structure.ts 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /**
  2. * Copyright (c) 2018-2022 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 { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
  8. import { SecondaryStructureType } from '../../../mol-model/structure/model/types';
  9. import { AtomicHierarchy } from '../../../mol-model/structure/model/properties/atomic';
  10. import { SecondaryStructure } from '../../../mol-model/structure/model/properties/seconday-structure';
  11. import { Column, Table } from '../../../mol-data/db';
  12. import { ChainIndex, ResidueIndex } from '../../../mol-model/structure/model/indexing';
  13. import { FormatPropertyProvider } from '../common/property';
  14. import { CustomPropertyDescriptor } from '../../../mol-model/custom-property';
  15. export { ModelSecondaryStructure };
  16. type StructConf = Table<mmCIF_Schema['struct_conf']>
  17. type StructSheetRange = Table<mmCIF_Schema['struct_sheet_range']>
  18. type CoordinateType = 'label' | 'auth';
  19. namespace ModelSecondaryStructure {
  20. export const Descriptor: CustomPropertyDescriptor = {
  21. name: 'model_secondary_structure',
  22. };
  23. export const Provider = FormatPropertyProvider.create<SecondaryStructure>(Descriptor);
  24. export function fromStruct(conf: StructConf, sheetRange: StructSheetRange, hierarchy: AtomicHierarchy): SecondaryStructure {
  25. const map: SecondaryStructureMap = new Map();
  26. const elements: SecondaryStructure.Element[] = [{ kind: 'none' }];
  27. const coordinates = getCoordinateType(conf, sheetRange);
  28. addHelices(conf, coordinates, map, elements);
  29. // must add Helices 1st because of 'key' value assignment.
  30. addSheets(sheetRange, coordinates, map, conf._rowCount, elements);
  31. const n = hierarchy.residues._rowCount;
  32. const getIndex = (rI: ResidueIndex) => rI;
  33. const secStruct: SecondaryStructureData = {
  34. type: new Int32Array(n) as any,
  35. key: new Int32Array(n) as any,
  36. elements
  37. };
  38. if (map.size > 0) assignSecondaryStructureRanges(hierarchy, coordinates, map, secStruct);
  39. return SecondaryStructure(secStruct.type, secStruct.key, secStruct.elements, getIndex);
  40. }
  41. }
  42. function getCoordinateType(conf: StructConf, sheetRange: StructSheetRange): CoordinateType {
  43. if (conf._rowCount > 0) {
  44. if (conf.beg_label_seq_id.valueKind(0) !== Column.ValueKinds.Present || conf.end_label_seq_id.valueKind(0) !== Column.ValueKinds.Present) return 'auth';
  45. } else if (sheetRange) {
  46. if (sheetRange.beg_label_seq_id.valueKind(0) !== Column.ValueKinds.Present || sheetRange.end_label_seq_id.valueKind(0) !== Column.ValueKinds.Present) return 'auth';
  47. }
  48. return 'label';
  49. }
  50. type SecondaryStructureEntry = {
  51. startSeqId: number,
  52. startInsCode: string | null,
  53. endSeqId: number,
  54. endInsCode: string | null,
  55. type: SecondaryStructureType,
  56. key: number
  57. }
  58. type SecondaryStructureMap = Map<string, Map<number, SecondaryStructureEntry[]>>
  59. type SecondaryStructureData = { type: SecondaryStructureType[], key: number[], elements: SecondaryStructure.Element[] }
  60. function addHelices(cat: StructConf, coordinates: CoordinateType, map: SecondaryStructureMap, elements: SecondaryStructure.Element[]) {
  61. if (!cat._rowCount) return;
  62. const { beg_label_asym_id, beg_label_seq_id, beg_auth_seq_id, pdbx_beg_PDB_ins_code } = cat;
  63. const { end_label_seq_id, end_auth_seq_id, pdbx_end_PDB_ins_code } = cat;
  64. const { pdbx_PDB_helix_class, conf_type_id, details } = cat;
  65. const beg_seq_id = coordinates === 'label' ? beg_label_seq_id : beg_auth_seq_id;
  66. const end_seq_id = coordinates === 'label' ? end_label_seq_id : end_auth_seq_id;
  67. for (let i = 0, _i = cat._rowCount; i < _i; i++) {
  68. const type = SecondaryStructureType.create(pdbx_PDB_helix_class.valueKind(i) === Column.ValueKinds.Present
  69. ? SecondaryStructureType.SecondaryStructurePdb[pdbx_PDB_helix_class.value(i)]
  70. : conf_type_id.valueKind(i) === Column.ValueKinds.Present
  71. ? SecondaryStructureType.SecondaryStructureMmcif[conf_type_id.value(i)]
  72. : SecondaryStructureType.Flag.NA);
  73. const element: SecondaryStructure.Helix = {
  74. kind: 'helix',
  75. flags: type,
  76. type_id: conf_type_id.valueKind(i) === Column.ValueKinds.Present ? conf_type_id.value(i) : 'helx_p',
  77. helix_class: pdbx_PDB_helix_class.value(i),
  78. details: details.valueKind(i) === Column.ValueKinds.Present ? details.value(i) : void 0
  79. };
  80. const entry: SecondaryStructureEntry = {
  81. startSeqId: beg_seq_id.value(i),
  82. startInsCode: pdbx_beg_PDB_ins_code.value(i),
  83. endSeqId: end_seq_id.value(i),
  84. endInsCode: pdbx_end_PDB_ins_code.value(i),
  85. type,
  86. key: elements.length
  87. };
  88. elements[elements.length] = element;
  89. const asymId = beg_label_asym_id.value(i)!;
  90. if (map.has(asymId)) {
  91. const entries = map.get(asymId)!;
  92. if (entries.has(entry.startSeqId)) {
  93. entries.get(entry.startSeqId)!.push(entry);
  94. } else {
  95. entries.set(entry.startSeqId, [entry]);
  96. }
  97. } else {
  98. map.set(asymId, new Map([[entry.startSeqId, [entry]]]));
  99. }
  100. }
  101. }
  102. function addSheets(cat: StructSheetRange, coordinates: CoordinateType, map: SecondaryStructureMap, sheetCount: number, elements: SecondaryStructure.Element[]) {
  103. if (!cat._rowCount) return;
  104. const { beg_label_asym_id, beg_label_seq_id, beg_auth_seq_id, pdbx_beg_PDB_ins_code } = cat;
  105. const { end_label_seq_id, end_auth_seq_id, pdbx_end_PDB_ins_code } = cat;
  106. const { sheet_id } = cat;
  107. const beg_seq_id = coordinates === 'label' ? beg_label_seq_id : beg_auth_seq_id;
  108. const end_seq_id = coordinates === 'label' ? end_label_seq_id : end_auth_seq_id;
  109. const sheet_id_key = new Map<string, number>();
  110. let currentKey = sheetCount + 1;
  111. for (let i = 0, _i = cat._rowCount; i < _i; i++) {
  112. const id = sheet_id.value(i);
  113. let key: number;
  114. if (sheet_id_key.has(id)) key = sheet_id_key.get(id)!;
  115. else {
  116. key = currentKey++;
  117. sheet_id_key.set(id, key);
  118. }
  119. const type = SecondaryStructureType.create(SecondaryStructureType.Flag.Beta | SecondaryStructureType.Flag.BetaSheet);
  120. const element: SecondaryStructure.Sheet = {
  121. kind: 'sheet',
  122. flags: type,
  123. sheet_id: id,
  124. symmetry: void 0
  125. };
  126. const entry: SecondaryStructureEntry = {
  127. startSeqId: beg_seq_id.value(i),
  128. startInsCode: pdbx_beg_PDB_ins_code.value(i),
  129. endSeqId: end_seq_id.value(i),
  130. endInsCode: pdbx_end_PDB_ins_code.value(i),
  131. type,
  132. key: elements.length
  133. };
  134. elements[elements.length] = element;
  135. const asymId = beg_label_asym_id.value(i)!;
  136. if (map.has(asymId)) {
  137. const entries = map.get(asymId)!;
  138. if (entries.has(entry.startSeqId)) {
  139. entries.get(entry.startSeqId)!.push(entry);
  140. } else {
  141. entries.set(entry.startSeqId, [entry]);
  142. }
  143. } else {
  144. map.set(asymId, new Map([[entry.startSeqId, [entry]]]));
  145. }
  146. }
  147. return;
  148. }
  149. function assignSecondaryStructureEntry(hierarchy: AtomicHierarchy, coordinates: CoordinateType, entry: SecondaryStructureEntry, resStart: ResidueIndex, resEnd: ResidueIndex, data: SecondaryStructureData) {
  150. const { auth_seq_id, label_seq_id, pdbx_PDB_ins_code } = hierarchy.residues;
  151. const { endSeqId, endInsCode, key, type } = entry;
  152. const seq_id = coordinates === 'label' ? label_seq_id : auth_seq_id;
  153. let rI = resStart;
  154. while (rI < resEnd) {
  155. const seqNumber = seq_id.value(rI);
  156. data.type[rI] = type;
  157. data.key[rI] = key;
  158. if ((seqNumber > endSeqId) ||
  159. (seqNumber === endSeqId && pdbx_PDB_ins_code.value(rI) === endInsCode)) {
  160. break;
  161. }
  162. rI++;
  163. }
  164. }
  165. function assignSecondaryStructureRanges(hierarchy: AtomicHierarchy, coordinates: CoordinateType, map: SecondaryStructureMap, data: SecondaryStructureData) {
  166. const { count: chainCount } = hierarchy.chainAtomSegments;
  167. const { label_asym_id } = hierarchy.chains;
  168. const { auth_seq_id, label_seq_id, pdbx_PDB_ins_code } = hierarchy.residues;
  169. const seq_id = coordinates === 'label' ? label_seq_id : auth_seq_id;
  170. for (let cI = 0 as ChainIndex; cI < chainCount; cI++) {
  171. const resStart = AtomicHierarchy.chainStartResidueIndex(hierarchy, cI), resEnd = AtomicHierarchy.chainEndResidueIndexExcl(hierarchy, cI);
  172. const asymId = label_asym_id.value(cI);
  173. if (map.has(asymId)) {
  174. const entries = map.get(asymId)!;
  175. for (let rI = resStart; rI < resEnd; rI++) {
  176. const seqId = seq_id.value(rI);
  177. if (entries.has(seqId)) {
  178. const entryList = entries.get(seqId)!;
  179. for (const entry of entryList) {
  180. const insCode = pdbx_PDB_ins_code.value(rI);
  181. if (entry.startInsCode !== insCode) continue;
  182. assignSecondaryStructureEntry(hierarchy, coordinates, entry, rI, resEnd, data);
  183. }
  184. }
  185. }
  186. }
  187. }
  188. }