atomic.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. /**
  2. * Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { Column, Table } from 'mol-data/db';
  7. import { Interval, Segmentation } from 'mol-data/int';
  8. import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif';
  9. import UUID from 'mol-util/uuid';
  10. import { ElementIndex } from 'mol-model/structure';
  11. import { Model } from 'mol-model/structure/model/model';
  12. import { AtomicConformation, AtomicData, AtomicHierarchy, AtomicSegments, AtomsSchema, ChainsSchema, ResiduesSchema } from 'mol-model/structure/model/properties/atomic';
  13. import { getAtomicIndex } from 'mol-model/structure/model/properties/utils/atomic-index';
  14. import { ElementSymbol } from 'mol-model/structure/model/types';
  15. import { Entities } from 'mol-model/structure/model/properties/common';
  16. import { getAtomicRanges } from 'mol-model/structure/model/properties/utils/atomic-ranges';
  17. import { getAtomicDerivedData } from 'mol-model/structure/model/properties/utils/atomic-derived';
  18. import { ModelFormat } from '../format';
  19. import mmCIF_Format = ModelFormat.mmCIF
  20. import { FormatData } from './parser';
  21. type AtomSite = mmCIF_Database['atom_site']
  22. function findHierarchyOffsets(atom_site: AtomSite) {
  23. if (atom_site._rowCount === 0) return { residues: [], chains: [] };
  24. const start = 0, end = atom_site._rowCount;
  25. const residues = [start as ElementIndex], chains = [start as ElementIndex];
  26. const { label_entity_id, label_asym_id, label_seq_id, auth_seq_id, pdbx_PDB_ins_code, label_comp_id } = atom_site;
  27. for (let i = start + 1 as ElementIndex; i < end; i++) {
  28. const newChain = !label_entity_id.areValuesEqual(i - 1, i) || !label_asym_id.areValuesEqual(i - 1, i);
  29. const newResidue = newChain
  30. || !label_seq_id.areValuesEqual(i - 1, i)
  31. || !auth_seq_id.areValuesEqual(i - 1, i)
  32. || !pdbx_PDB_ins_code.areValuesEqual(i - 1, i)
  33. || !label_comp_id.areValuesEqual(i - 1, i);
  34. if (newResidue) residues[residues.length] = i as ElementIndex;
  35. if (newChain) chains[chains.length] = i as ElementIndex;
  36. }
  37. return { residues, chains };
  38. }
  39. function createHierarchyData(atom_site: AtomSite, offsets: { residues: ArrayLike<number>, chains: ArrayLike<number> }): AtomicData {
  40. const atoms = Table.ofColumns(AtomsSchema, {
  41. type_symbol: Column.ofArray({ array: Column.mapToArray(atom_site.type_symbol, ElementSymbol), schema: Column.Schema.Aliased<ElementSymbol>(Column.Schema.str) }),
  42. label_atom_id: atom_site.label_atom_id,
  43. auth_atom_id: atom_site.auth_atom_id,
  44. label_alt_id: atom_site.label_alt_id,
  45. pdbx_formal_charge: atom_site.pdbx_formal_charge
  46. });
  47. const residues = Table.view(atom_site, ResiduesSchema, offsets.residues);
  48. // Optimize the numeric columns
  49. Table.columnToArray(residues, 'label_seq_id', Int32Array);
  50. Table.columnToArray(residues, 'auth_seq_id', Int32Array);
  51. const chains = Table.view(atom_site, ChainsSchema, offsets.chains);
  52. return { atoms, residues, chains };
  53. }
  54. function getConformation(atom_site: AtomSite): AtomicConformation {
  55. return {
  56. id: UUID.create22(),
  57. atomId: atom_site.id,
  58. occupancy: atom_site.occupancy,
  59. B_iso_or_equiv: atom_site.B_iso_or_equiv,
  60. x: atom_site.Cartn_x.toArray({ array: Float32Array }),
  61. y: atom_site.Cartn_y.toArray({ array: Float32Array }),
  62. z: atom_site.Cartn_z.toArray({ array: Float32Array }),
  63. }
  64. }
  65. function isHierarchyDataEqual(a: AtomicData, b: AtomicData) {
  66. // TODO need to cast because of how TS handles type resolution for interfaces https://github.com/Microsoft/TypeScript/issues/15300
  67. return Table.areEqual(a.chains as Table<ChainsSchema>, b.chains as Table<ChainsSchema>)
  68. && Table.areEqual(a.residues as Table<ResiduesSchema>, b.residues as Table<ResiduesSchema>)
  69. && Table.areEqual(a.atoms as Table<AtomsSchema>, b.atoms as Table<AtomsSchema>)
  70. }
  71. export function getAtomicHierarchyAndConformation(format: mmCIF_Format, atom_site: AtomSite, entities: Entities, formatData: FormatData, previous?: Model) {
  72. const hierarchyOffsets = findHierarchyOffsets(atom_site);
  73. const hierarchyData = createHierarchyData(atom_site, hierarchyOffsets);
  74. if (previous && isHierarchyDataEqual(previous.atomicHierarchy, hierarchyData)) {
  75. return {
  76. sameAsPrevious: true,
  77. hierarchy: previous.atomicHierarchy,
  78. conformation: getConformation(atom_site)
  79. };
  80. }
  81. const conformation = getConformation(atom_site)
  82. const hierarchySegments: AtomicSegments = {
  83. residueAtomSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, Interval.ofBounds(0, atom_site._rowCount)),
  84. chainAtomSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, Interval.ofBounds(0, atom_site._rowCount)),
  85. }
  86. const index = getAtomicIndex(hierarchyData, entities, hierarchySegments);
  87. const derived = getAtomicDerivedData(hierarchyData, index, formatData.chemicalComponentMap);
  88. const hierarchyRanges = getAtomicRanges(hierarchyData, hierarchySegments, conformation, index, derived.residue.moleculeType);
  89. const hierarchy: AtomicHierarchy = { ...hierarchyData, ...hierarchySegments, ...hierarchyRanges, index, derived };
  90. return { sameAsPrevious: false, hierarchy, conformation };
  91. }