atomic.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. /**
  2. * Copyright (c) 2017-2019 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 { Column, Table } from '../../../mol-data/db';
  8. import { Interval, Segmentation } from '../../../mol-data/int';
  9. import { mmCIF_Database } from '../../../mol-io/reader/cif/schema/mmcif';
  10. import UUID from '../../../mol-util/uuid';
  11. import { ElementIndex } from '../../../mol-model/structure';
  12. import { Model } from '../../../mol-model/structure/model/model';
  13. import { AtomicConformation, AtomicData, AtomicHierarchy, AtomicSegments, AtomsSchema, ChainsSchema, ResiduesSchema } from '../../../mol-model/structure/model/properties/atomic';
  14. import { getAtomicIndex } from '../../../mol-model/structure/model/properties/utils/atomic-index';
  15. import { ElementSymbol } from '../../../mol-model/structure/model/types';
  16. import { Entities } from '../../../mol-model/structure/model/properties/common';
  17. import { getAtomicDerivedData } from '../../../mol-model/structure/model/properties/utils/atomic-derived';
  18. import { FormatData } from './parser';
  19. type AtomSite = mmCIF_Database['atom_site']
  20. function findHierarchyOffsets(atom_site: AtomSite) {
  21. if (atom_site._rowCount === 0) return { residues: [], chains: [] };
  22. const start = 0, end = atom_site._rowCount;
  23. const residues = [start as ElementIndex], chains = [start as ElementIndex];
  24. const { label_entity_id, label_asym_id, label_seq_id, auth_seq_id, pdbx_PDB_ins_code } = atom_site;
  25. for (let i = start + 1 as ElementIndex; i < end; i++) {
  26. const newChain = !label_entity_id.areValuesEqual(i - 1, i) || !label_asym_id.areValuesEqual(i - 1, i);
  27. const newResidue = newChain
  28. || !label_seq_id.areValuesEqual(i - 1, i)
  29. || !auth_seq_id.areValuesEqual(i - 1, i)
  30. || !pdbx_PDB_ins_code.areValuesEqual(i - 1, i);
  31. // not checking label_comp_id to allow for MICROHETEROGENEITY
  32. if (newResidue) residues[residues.length] = i as ElementIndex;
  33. if (newChain) chains[chains.length] = i as ElementIndex;
  34. }
  35. return { residues, chains };
  36. }
  37. function substUndefinedColumn<T extends Table<any>>(table: T, a: keyof T, b: keyof T) {
  38. if (!(table as any)[a].isDefined) {
  39. (table as any)[a] = (table as any)[b];
  40. }
  41. if (!(table as any)[b].isDefined) {
  42. (table as any)[b] = (table as any)[a];
  43. }
  44. }
  45. function createHierarchyData(atom_site: AtomSite, sourceIndex: Column<number>, offsets: { residues: ArrayLike<number>, chains: ArrayLike<number> }): AtomicData {
  46. const atoms = Table.ofColumns(AtomsSchema, {
  47. type_symbol: Column.ofArray({ array: Column.mapToArray(atom_site.type_symbol, ElementSymbol), schema: Column.Schema.Aliased<ElementSymbol>(Column.Schema.str) }),
  48. label_atom_id: atom_site.label_atom_id,
  49. auth_atom_id: atom_site.auth_atom_id,
  50. label_alt_id: atom_site.label_alt_id,
  51. pdbx_formal_charge: atom_site.pdbx_formal_charge,
  52. sourceIndex
  53. });
  54. const residues = Table.view(atom_site, ResiduesSchema, offsets.residues);
  55. // Optimize the numeric columns
  56. Table.columnToArray(residues, 'label_seq_id', Int32Array);
  57. Table.columnToArray(residues, 'auth_seq_id', Int32Array);
  58. const chains = Table.view(atom_site, ChainsSchema, offsets.chains);
  59. // Fix possibly missing auth_/label_ columns
  60. substUndefinedColumn(atoms, 'label_atom_id', 'auth_atom_id');
  61. substUndefinedColumn(residues, 'label_seq_id', 'auth_seq_id');
  62. substUndefinedColumn(residues, 'label_comp_id', 'auth_comp_id');
  63. substUndefinedColumn(chains, 'label_asym_id', 'auth_asym_id');
  64. return { atoms, residues, chains };
  65. }
  66. function getConformation(atom_site: AtomSite): AtomicConformation {
  67. return {
  68. id: UUID.create22(),
  69. atomId: atom_site.id,
  70. occupancy: atom_site.occupancy,
  71. B_iso_or_equiv: atom_site.B_iso_or_equiv,
  72. xyzDefined: atom_site.Cartn_x.isDefined && atom_site.Cartn_y.isDefined && atom_site.Cartn_z.isDefined,
  73. x: atom_site.Cartn_x.toArray({ array: Float32Array }),
  74. y: atom_site.Cartn_y.toArray({ array: Float32Array }),
  75. z: atom_site.Cartn_z.toArray({ array: Float32Array }),
  76. }
  77. }
  78. function isHierarchyDataEqual(a: AtomicData, b: AtomicData) {
  79. // TODO need to cast because of how TS handles type resolution for interfaces https://github.com/Microsoft/TypeScript/issues/15300
  80. return Table.areEqual(a.chains as Table<ChainsSchema>, b.chains as Table<ChainsSchema>)
  81. && Table.areEqual(a.residues as Table<ResiduesSchema>, b.residues as Table<ResiduesSchema>)
  82. && Table.areEqual(a.atoms as Table<AtomsSchema>, b.atoms as Table<AtomsSchema>)
  83. }
  84. function getAtomicHierarchy(atom_site: AtomSite, sourceIndex: Column<number>, entities: Entities, formatData: FormatData, previous?: Model) {
  85. const hierarchyOffsets = findHierarchyOffsets(atom_site);
  86. const hierarchyData = createHierarchyData(atom_site, sourceIndex, hierarchyOffsets);
  87. if (previous && isHierarchyDataEqual(previous.atomicHierarchy, hierarchyData)) {
  88. return {
  89. sameAsPrevious: true,
  90. hierarchy: previous.atomicHierarchy,
  91. };
  92. }
  93. const hierarchySegments: AtomicSegments = {
  94. residueAtomSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, Interval.ofBounds(0, atom_site._rowCount)),
  95. chainAtomSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, Interval.ofBounds(0, atom_site._rowCount)),
  96. }
  97. const index = getAtomicIndex(hierarchyData, entities, hierarchySegments);
  98. const derived = getAtomicDerivedData(hierarchyData, index, formatData.chemicalComponentMap);
  99. const hierarchy: AtomicHierarchy = { ...hierarchyData, ...hierarchySegments, index, derived };
  100. return { sameAsPrevious: false, hierarchy };
  101. }
  102. export function getAtomicHierarchyAndConformation(atom_site: AtomSite, sourceIndex: Column<number>, entities: Entities, formatData: FormatData, previous?: Model) {
  103. const { sameAsPrevious, hierarchy } = getAtomicHierarchy(atom_site, sourceIndex, entities, formatData, previous)
  104. const conformation = getConformation(atom_site)
  105. return { sameAsPrevious, hierarchy, conformation };
  106. }