parser.ts 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  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 { Column, Table } from '../../../mol-data/db';
  8. import { RuntimeContext } from '../../../mol-task';
  9. import UUID from '../../../mol-util/uuid';
  10. import { Model } from '../../../mol-model/structure/model/model';
  11. import { Entities } from '../../../mol-model/structure/model/properties/common';
  12. import { CustomProperties } from '../../../mol-model/custom-property';
  13. import { getAtomicHierarchyAndConformation } from './atomic';
  14. import { getCoarse, EmptyCoarse, CoarseData } from './coarse';
  15. import { getSequence } from './sequence';
  16. import { sortAtomSite } from './sort';
  17. import { ModelFormat } from '../../format';
  18. import { getAtomicRanges } from '../../../mol-model/structure/model/properties/utils/atomic-ranges';
  19. import { AtomSite, BasicData } from './schema';
  20. import { getProperties } from './properties';
  21. import { getEntities } from './entities';
  22. import { getModelGroupName } from './util';
  23. import { ArrayTrajectory } from '../../../mol-model/structure/trajectory';
  24. export async function createModels(data: BasicData, format: ModelFormat, ctx: RuntimeContext) {
  25. const properties = getProperties(data);
  26. const models = data.ihm_model_list._rowCount > 0
  27. ? await readIntegrative(ctx, data, properties, format)
  28. : await readStandard(ctx, data, properties, format);
  29. for (let i = 0; i < models.length; i++) {
  30. Model.TrajectoryInfo.set(models[i], { index: i, size: models.length });
  31. }
  32. return new ArrayTrajectory(models);
  33. }
  34. /** Standard atomic model */
  35. function createStandardModel(data: BasicData, atom_site: AtomSite, sourceIndex: Column<number>, entities: Entities, properties: Model['properties'], format: ModelFormat, previous?: Model): Model {
  36. const atomic = getAtomicHierarchyAndConformation(atom_site, sourceIndex, entities, properties.chemicalComponentMap, format, previous);
  37. const modelNum = atom_site.pdbx_PDB_model_num.value(0);
  38. if (previous && atomic.sameAsPrevious) {
  39. return {
  40. ...previous,
  41. id: UUID.create22(),
  42. modelNum,
  43. atomicConformation: atomic.conformation,
  44. _dynamicPropertyData: Object.create(null)
  45. };
  46. }
  47. const coarse = EmptyCoarse;
  48. const sequence = getSequence(data, entities, atomic.hierarchy, coarse.hierarchy);
  49. const atomicRanges = getAtomicRanges(atomic.hierarchy, entities, atomic.conformation, sequence);
  50. const entry = data.entry.id.valueKind(0) === Column.ValueKind.Present
  51. ? data.entry.id.value(0) : format.name;
  52. const label: string[] = [];
  53. if (entry) label.push(entry);
  54. if (data.struct.title.valueKind(0) === Column.ValueKind.Present) label.push(data.struct.title.value(0));
  55. return {
  56. id: UUID.create22(),
  57. entryId: entry,
  58. label: label.join(' | '),
  59. entry,
  60. sourceData: format,
  61. modelNum,
  62. parent: undefined,
  63. entities,
  64. sequence,
  65. atomicHierarchy: atomic.hierarchy,
  66. atomicConformation: atomic.conformation,
  67. atomicRanges,
  68. atomicChainOperatorMappinng: atomic.chainOperatorMapping,
  69. coarseHierarchy: coarse.hierarchy,
  70. coarseConformation: coarse.conformation,
  71. properties,
  72. customProperties: new CustomProperties(),
  73. _staticPropertyData: Object.create(null),
  74. _dynamicPropertyData: Object.create(null)
  75. };
  76. }
  77. /** Integrative model with atomic/coarse parts */
  78. function createIntegrativeModel(data: BasicData, ihm: CoarseData, properties: Model['properties'], format: ModelFormat): Model {
  79. const atomic = getAtomicHierarchyAndConformation(ihm.atom_site, ihm.atom_site_sourceIndex, ihm.entities, properties.chemicalComponentMap, format);
  80. const coarse = getCoarse(ihm, properties);
  81. const sequence = getSequence(data, ihm.entities, atomic.hierarchy, coarse.hierarchy);
  82. const atomicRanges = getAtomicRanges(atomic.hierarchy, ihm.entities, atomic.conformation, sequence);
  83. const entry = data.entry.id.valueKind(0) === Column.ValueKind.Present
  84. ? data.entry.id.value(0) : format.name;
  85. const label: string[] = [];
  86. if (entry) label.push(entry);
  87. if (data.struct.title.valueKind(0) === Column.ValueKind.Present) label.push(data.struct.title.value(0));
  88. if (ihm.model_name) label.push(ihm.model_name);
  89. if (ihm.model_group_name) label.push(ihm.model_group_name);
  90. return {
  91. id: UUID.create22(),
  92. entryId: entry,
  93. label: label.join(' | '),
  94. entry,
  95. sourceData: format,
  96. modelNum: ihm.model_id,
  97. parent: undefined,
  98. entities: ihm.entities,
  99. sequence,
  100. atomicHierarchy: atomic.hierarchy,
  101. atomicConformation: atomic.conformation,
  102. atomicRanges,
  103. atomicChainOperatorMappinng: atomic.chainOperatorMapping,
  104. coarseHierarchy: coarse.hierarchy,
  105. coarseConformation: coarse.conformation,
  106. properties,
  107. customProperties: new CustomProperties(),
  108. _staticPropertyData: Object.create(null),
  109. _dynamicPropertyData: Object.create(null)
  110. };
  111. }
  112. function findModelEnd(num: Column<number>, startIndex: number) {
  113. const rowCount = num.rowCount;
  114. if (!num.isDefined) return rowCount;
  115. let endIndex = startIndex + 1;
  116. while (endIndex < rowCount && num.areValuesEqual(startIndex, endIndex)) endIndex++;
  117. return endIndex;
  118. }
  119. async function readStandard(ctx: RuntimeContext, data: BasicData, properties: Model['properties'], format: ModelFormat) {
  120. const models: Model[] = [];
  121. if (data.atom_site) {
  122. const atomCount = data.atom_site.id.rowCount;
  123. const entities = getEntities(data, properties);
  124. let modelStart = 0;
  125. while (modelStart < atomCount) {
  126. const modelEnd = findModelEnd(data.atom_site.pdbx_PDB_model_num, modelStart);
  127. const { atom_site, sourceIndex } = await sortAtomSite(ctx, data.atom_site, modelStart, modelEnd);
  128. const model = createStandardModel(data, atom_site, sourceIndex, entities, properties, format, models.length > 0 ? models[models.length - 1] : void 0);
  129. models.push(model);
  130. modelStart = modelEnd;
  131. }
  132. }
  133. return models;
  134. }
  135. function splitTable<T extends Table<any>>(table: T, col: Column<number>) {
  136. const ret = new Map<number, { table: T, start: number, end: number }>();
  137. const rowCount = table._rowCount;
  138. let modelStart = 0;
  139. while (modelStart < rowCount) {
  140. const modelEnd = findModelEnd(col, modelStart);
  141. const id = col.value(modelStart);
  142. ret.set(id, {
  143. table: Table.window(table, table._schema, modelStart, modelEnd) as T,
  144. start: modelStart,
  145. end: modelEnd
  146. });
  147. modelStart = modelEnd;
  148. }
  149. return ret;
  150. }
  151. async function readIntegrative(ctx: RuntimeContext, data: BasicData, properties: Model['properties'], format: ModelFormat) {
  152. const entities = getEntities(data, properties);
  153. // when `atom_site.ihm_model_id` is undefined fall back to `atom_site.pdbx_PDB_model_num`
  154. const atom_sites_modelColumn = data.atom_site.ihm_model_id.isDefined
  155. ? data.atom_site.ihm_model_id : data.atom_site.pdbx_PDB_model_num;
  156. const atom_sites = splitTable(data.atom_site, atom_sites_modelColumn);
  157. // TODO: will coarse IHM records require sorting or will we trust it?
  158. // ==> Probably implement a sort as as well and store the sourceIndex same as with atomSite
  159. // If the sorting is implemented, updated mol-model/structure/properties: atom.sourceIndex
  160. const sphere_sites = splitTable(data.ihm_sphere_obj_site, data.ihm_sphere_obj_site.model_id);
  161. const gauss_sites = splitTable(data.ihm_gaussian_obj_site, data.ihm_gaussian_obj_site.model_id);
  162. const models: Model[] = [];
  163. if (data.ihm_model_list) {
  164. const { model_id, model_name } = data.ihm_model_list;
  165. for (let i = 0; i < data.ihm_model_list._rowCount; i++) {
  166. const id = model_id.value(i);
  167. let atom_site, atom_site_sourceIndex;
  168. if (atom_sites.has(id)) {
  169. const e = atom_sites.get(id)!;
  170. // need to sort `data.atom_site` as `e.start` and `e.end` are indices into that
  171. const { atom_site: sorted, sourceIndex } = await sortAtomSite(ctx, data.atom_site, e.start, e.end);
  172. atom_site = sorted;
  173. atom_site_sourceIndex = sourceIndex;
  174. } else {
  175. atom_site = Table.window(data.atom_site, data.atom_site._schema, 0, 0);
  176. atom_site_sourceIndex = Column.ofIntArray([]);
  177. }
  178. const ihm: CoarseData = {
  179. model_id: id,
  180. model_name: model_name.value(i),
  181. model_group_name: getModelGroupName(id, data),
  182. entities: entities,
  183. atom_site,
  184. atom_site_sourceIndex,
  185. ihm_sphere_obj_site: sphere_sites.has(id) ? sphere_sites.get(id)!.table : Table.window(data.ihm_sphere_obj_site, data.ihm_sphere_obj_site._schema, 0, 0),
  186. ihm_gaussian_obj_site: gauss_sites.has(id) ? gauss_sites.get(id)!.table : Table.window(data.ihm_gaussian_obj_site, data.ihm_gaussian_obj_site._schema, 0, 0)
  187. };
  188. const model = createIntegrativeModel(data, ihm, properties, format);
  189. models.push(model);
  190. }
  191. }
  192. return models;
  193. }