struct_conn.ts 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  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 { Model } from '../../../../mol-model/structure/model/model'
  8. import { Structure } from '../../../../mol-model/structure'
  9. import { BondType } from '../../../../mol-model/structure/model/types'
  10. import { findEntityIdByAsymId, findAtomIndexByLabelName } from '../util'
  11. import { Column } from '../../../../mol-data/db'
  12. import { CustomPropertyDescriptor } from '../../../../mol-model/structure';
  13. import { mmCIF_Database } from '../../../../mol-io/reader/cif/schema/mmcif';
  14. import { SortedArray } from '../../../../mol-data/int';
  15. import { CifWriter } from '../../../../mol-io/writer/cif'
  16. import { ElementIndex, ResidueIndex } from '../../../../mol-model/structure/model/indexing';
  17. import { getInterBondOrderFromTable } from '../../../../mol-model/structure/model/properties/atomic/bonds';
  18. export interface StructConn {
  19. getAtomEntries(atomIndex: ElementIndex): ReadonlyArray<StructConn.Entry>,
  20. readonly entries: ReadonlyArray<StructConn.Entry>
  21. }
  22. export namespace StructConn {
  23. export const Descriptor: CustomPropertyDescriptor = {
  24. name: 'struct_conn',
  25. cifExport: {
  26. prefix: '',
  27. categories: [{
  28. name: 'struct_conn',
  29. instance(ctx) {
  30. const structure = ctx.structures[0], model = structure.model;
  31. const struct_conn = getStructConn(model);
  32. if (!struct_conn) return CifWriter.Category.Empty;
  33. const strConn = get(model);
  34. if (!strConn || strConn.entries.length === 0) return CifWriter.Category.Empty;
  35. const indices: number[] = [];
  36. for (const e of strConn.entries) {
  37. if (hasAtom(structure, e.partnerA.atomIndex) &&
  38. hasAtom(structure, e.partnerB.atomIndex)) {
  39. indices[indices.length] = e.rowIndex;
  40. }
  41. }
  42. return CifWriter.Category.ofTable(struct_conn, indices);
  43. }
  44. }]
  45. }
  46. }
  47. function hasAtom({ units }: Structure, element: ElementIndex) {
  48. for (let i = 0, _i = units.length; i < _i; i++) {
  49. if (SortedArray.indexOf(units[i].elements, element) >= 0) return true;
  50. }
  51. return false;
  52. }
  53. const _emptyEntry: Entry[] = [];
  54. class StructConnImpl implements StructConn {
  55. private _atomIndex: Map<number, StructConn.Entry[]> | undefined = void 0;
  56. private getAtomIndex() {
  57. if (this._atomIndex) return this._atomIndex;
  58. const m = this._atomIndex = new Map();
  59. for (const e of this.entries) {
  60. const { partnerA: { atomIndex: iA }, partnerB: { atomIndex: iB } } = e;
  61. if (m.has(iA)) m.get(iA)!.push(e);
  62. else m.set(iA, [e]);
  63. if (m.has(iB)) m.get(iB)!.push(e);
  64. else m.set(iB, [e]);
  65. }
  66. return this._atomIndex;
  67. }
  68. getAtomEntries(atomIndex: ElementIndex): ReadonlyArray<StructConn.Entry> {
  69. return this.getAtomIndex().get(atomIndex) || _emptyEntry;
  70. }
  71. constructor(public entries: StructConn.Entry[]) {
  72. }
  73. }
  74. export interface Entry {
  75. rowIndex: number,
  76. distance: number,
  77. order: number,
  78. flags: number,
  79. partnerA: { residueIndex: ResidueIndex, atomIndex: ElementIndex, symmetry: string },
  80. partnerB: { residueIndex: ResidueIndex, atomIndex: ElementIndex, symmetry: string }
  81. }
  82. export function attachFromMmCif(model: Model): boolean {
  83. if (model.customProperties.has(Descriptor)) return true;
  84. if (model.sourceData.kind !== 'mmCIF') return false;
  85. const { struct_conn } = model.sourceData.data;
  86. if (struct_conn._rowCount === 0) return false;
  87. model.customProperties.add(Descriptor);
  88. model._staticPropertyData.__StructConnData__ = struct_conn;
  89. return true;
  90. }
  91. function getStructConn(model: Model) {
  92. return model._staticPropertyData.__StructConnData__ as mmCIF_Database['struct_conn'];
  93. }
  94. export const PropName = '__StructConn__';
  95. export function get(model: Model): StructConn | undefined {
  96. if (model._staticPropertyData[PropName]) return model._staticPropertyData[PropName];
  97. if (!model.customProperties.has(Descriptor)) return void 0;
  98. const struct_conn = getStructConn(model);
  99. const { conn_type_id, pdbx_dist_value, pdbx_value_order } = struct_conn;
  100. const p1 = {
  101. label_asym_id: struct_conn.ptnr1_label_asym_id,
  102. label_seq_id: struct_conn.ptnr1_label_seq_id,
  103. auth_seq_id: struct_conn.ptnr1_auth_seq_id,
  104. label_atom_id: struct_conn.ptnr1_label_atom_id,
  105. label_alt_id: struct_conn.pdbx_ptnr1_label_alt_id,
  106. ins_code: struct_conn.pdbx_ptnr1_PDB_ins_code,
  107. symmetry: struct_conn.ptnr1_symmetry
  108. };
  109. const p2: typeof p1 = {
  110. label_asym_id: struct_conn.ptnr2_label_asym_id,
  111. label_seq_id: struct_conn.ptnr2_label_seq_id,
  112. auth_seq_id: struct_conn.ptnr2_auth_seq_id,
  113. label_atom_id: struct_conn.ptnr2_label_atom_id,
  114. label_alt_id: struct_conn.pdbx_ptnr2_label_alt_id,
  115. ins_code: struct_conn.pdbx_ptnr2_PDB_ins_code,
  116. symmetry: struct_conn.ptnr2_symmetry
  117. };
  118. const _p = (row: number, ps: typeof p1) => {
  119. if (ps.label_asym_id.valueKind(row) !== Column.ValueKind.Present) return void 0;
  120. const asymId = ps.label_asym_id.value(row);
  121. const residueIndex = model.atomicHierarchy.index.findResidue(
  122. findEntityIdByAsymId(model, asymId),
  123. asymId,
  124. ps.auth_seq_id.value(row),
  125. ps.ins_code.value(row)
  126. );
  127. if (residueIndex < 0) return void 0;
  128. const atomName = ps.label_atom_id.value(row);
  129. // turns out "mismat" records might not have atom name value
  130. if (!atomName) return void 0;
  131. const atomIndex = findAtomIndexByLabelName(model, residueIndex, atomName, ps.label_alt_id.value(row));
  132. if (atomIndex < 0) return void 0;
  133. return { residueIndex, atomIndex, symmetry: ps.symmetry.value(row) };
  134. }
  135. const entries: StructConn.Entry[] = [];
  136. for (let i = 0; i < struct_conn._rowCount; i++) {
  137. const partnerA = _p(i, p1)
  138. const partnerB = _p(i, p2)
  139. if (partnerA === undefined || partnerB === undefined) continue;
  140. const type = conn_type_id.value(i)
  141. const orderType = (pdbx_value_order.value(i) || '').toLowerCase();
  142. let flags = BondType.Flag.None;
  143. let order = 1;
  144. switch (orderType) {
  145. case 'sing': order = 1; break;
  146. case 'doub': order = 2; break;
  147. case 'trip': order = 3; break;
  148. case 'quad': order = 4; break;
  149. default:
  150. order = getInterBondOrderFromTable(
  151. struct_conn.ptnr1_label_comp_id.value(i),
  152. struct_conn.ptnr1_label_atom_id.value(i),
  153. struct_conn.ptnr2_label_comp_id.value(i),
  154. struct_conn.ptnr2_label_atom_id.value(i)
  155. )
  156. }
  157. switch (type) {
  158. case 'covale':
  159. flags = BondType.Flag.Covalent;
  160. break;
  161. case 'disulf': flags = BondType.Flag.Covalent | BondType.Flag.Disulfide; break;
  162. case 'hydrog':
  163. flags = BondType.Flag.HydrogenBond;
  164. break;
  165. case 'metalc': flags = BondType.Flag.MetallicCoordination; break;
  166. }
  167. entries.push({
  168. rowIndex: i, flags, order, distance: pdbx_dist_value.value(i), partnerA, partnerB
  169. });
  170. }
  171. const ret = new StructConnImpl(entries);
  172. model._staticPropertyData[PropName] = ret;
  173. return ret;
  174. }
  175. }