properties.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  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 { ChemicalComponent, MissingResidue, StructAsym } from '../../../mol-model/structure/model/properties/common';
  9. import { getMoleculeType, MoleculeType, getDefaultChemicalComponent } from '../../../mol-model/structure/model/types';
  10. import { SaccharideComponentMap, SaccharideComponent, SaccharidesSnfgMap, SaccharideCompIdMap, UnknownSaccharideComponent } from '../../../mol-model/structure/structure/carbohydrates/constants';
  11. import { memoize1 } from '../../../mol-util/memoize';
  12. import { BasicData } from './schema';
  13. import { Table } from '../../../mol-data/db';
  14. function getMissingResidues(data: BasicData): Model['properties']['missingResidues'] {
  15. const map = new Map<string, MissingResidue>();
  16. const getKey = (model_num: number, asym_id: string, seq_id: number) => {
  17. return `${model_num}|${asym_id}|${seq_id}`
  18. }
  19. const c = data.pdbx_unobs_or_zero_occ_residues
  20. for (let i = 0, il = c._rowCount; i < il; ++i) {
  21. const key = getKey(c.PDB_model_num.value(i), c.label_asym_id.value(i), c.label_seq_id.value(i))
  22. map.set(key, { polymer_flag: c.polymer_flag.value(i), occupancy_flag: c.occupancy_flag.value(i) })
  23. }
  24. return {
  25. has: (model_num: number, asym_id: string, seq_id: number) => {
  26. return map.has(getKey(model_num, asym_id, seq_id))
  27. },
  28. get: (model_num: number, asym_id: string, seq_id: number) => {
  29. return map.get(getKey(model_num, asym_id, seq_id))
  30. },
  31. size: map.size
  32. }
  33. }
  34. function getChemicalComponentMap(data: BasicData): Model['properties']['chemicalComponentMap'] {
  35. const map = new Map<string, ChemicalComponent>();
  36. if (data.chem_comp._rowCount > 0) {
  37. const { id } = data.chem_comp
  38. for (let i = 0, il = id.rowCount; i < il; ++i) {
  39. map.set(id.value(i), Table.getRow(data.chem_comp, i))
  40. }
  41. } else {
  42. const uniqueNames = getUniqueComponentNames(data);
  43. uniqueNames.forEach(n => {
  44. map.set(n, getDefaultChemicalComponent(n));
  45. });
  46. }
  47. return map
  48. }
  49. function getSaccharideComponentMap(data: BasicData): SaccharideComponentMap {
  50. const map = new Map<string, SaccharideComponent>();
  51. if (data.pdbx_chem_comp_identifier._rowCount > 0) {
  52. // note that `pdbx_chem_comp_identifier` does not contain
  53. // a 'SNFG CARBOHYDRATE SYMBOL' entry for 'Unknown' saccharide components
  54. // so we always need to check `chem_comp` for those
  55. const { comp_id, type, identifier } = data.pdbx_chem_comp_identifier
  56. for (let i = 0, il = comp_id.rowCount; i < il; ++i) {
  57. if (type.value(i) === 'SNFG CARBOHYDRATE SYMBOL' ||
  58. type.value(i) === 'SNFG CARB SYMBOL' // legacy, to be removed from mmCIF dictionary
  59. ) {
  60. const snfgName = identifier.value(i)
  61. const saccharideComp = SaccharidesSnfgMap.get(snfgName)
  62. if (saccharideComp) {
  63. map.set(comp_id.value(i), saccharideComp)
  64. } else {
  65. console.warn(`Unknown SNFG name '${snfgName}'`)
  66. }
  67. }
  68. }
  69. }
  70. if (data.chem_comp._rowCount > 0) {
  71. const { id, type } = data.chem_comp
  72. for (let i = 0, il = id.rowCount; i < il; ++i) {
  73. const _id = id.value(i)
  74. if (map.has(_id)) continue
  75. const _type = type.value(i)
  76. if (SaccharideCompIdMap.has(_id)) {
  77. map.set(_id, SaccharideCompIdMap.get(_id)!)
  78. } else if (getMoleculeType(_type, _id) === MoleculeType.Saccharide) {
  79. map.set(_id, UnknownSaccharideComponent)
  80. }
  81. }
  82. } else {
  83. const uniqueNames = getUniqueComponentNames(data)
  84. SaccharideCompIdMap.forEach((v, k) => {
  85. if (!map.has(k) && uniqueNames.has(k)) map.set(k, v)
  86. })
  87. }
  88. return map
  89. }
  90. const getUniqueComponentNames = memoize1((data: BasicData) => {
  91. const uniqueNames = new Set<string>()
  92. const { label_comp_id, auth_comp_id } = data.atom_site
  93. const comp_id = label_comp_id.isDefined ? label_comp_id : auth_comp_id;
  94. for (let i = 0, il = comp_id.rowCount; i < il; ++i) {
  95. uniqueNames.add(comp_id.value(i))
  96. }
  97. return uniqueNames
  98. })
  99. function getStructAsymMap(data: BasicData): Model['properties']['structAsymMap'] {
  100. const map = new Map<string, StructAsym>();
  101. const { label_asym_id, auth_asym_id, label_entity_id } = data.atom_site
  102. for (let i = 0, il = label_asym_id.rowCount; i < il; ++i) {
  103. const id = label_asym_id.value(i)
  104. if (!map.has(id)) {
  105. map.set(id, {
  106. id,
  107. auth_id: auth_asym_id.value(i),
  108. entity_id: label_entity_id.value(i)
  109. })
  110. }
  111. }
  112. if (data.struct_asym._rowCount > 0) {
  113. const { id, entity_id } = data.struct_asym
  114. for (let i = 0, il = id.rowCount; i < il; ++i) {
  115. const _id = id.value(i)
  116. if (!map.has(_id)) {
  117. map.set(_id, {
  118. id: _id,
  119. auth_id: '',
  120. entity_id: entity_id.value(i)
  121. })
  122. }
  123. }
  124. }
  125. return map
  126. }
  127. export function getProperties(data: BasicData): Model['properties'] {
  128. return {
  129. missingResidues: getMissingResidues(data),
  130. chemicalComponentMap: getChemicalComponentMap(data),
  131. saccharideComponentMap: getSaccharideComponentMap(data),
  132. structAsymMap: getStructAsymMap(data)
  133. }
  134. }