secondary-structure.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { Segmentation } from '../../../../mol-data/int';
  7. import { CifWriter } from '../../../../mol-io/writer/cif';
  8. import { SecondaryStructure } from '../../model/properties/seconday-structure';
  9. import { StructureElement, Unit } from '../../structure';
  10. import { CifExportContext } from '../mmcif';
  11. import CifField = CifWriter.Field
  12. import CifCategory = CifWriter.Category
  13. import { Column } from '../../../../mol-data/db';
  14. import { residueIdFields } from './atom_site';
  15. import { ModelSecondaryStructure } from '../../../../mol-model-formats/structure/property/secondary-structure';
  16. export const _struct_conf: CifCategory<CifExportContext> = {
  17. name: 'struct_conf',
  18. instance(ctx) {
  19. const elements = findElements(ctx, 'helix');
  20. return {
  21. fields: struct_conf_fields(),
  22. source: [{ data: elements, rowCount: elements.length }]
  23. };
  24. }
  25. };
  26. export const _struct_sheet_range: CifCategory<CifExportContext> = {
  27. name: 'struct_sheet_range',
  28. instance(ctx) {
  29. const elements = (findElements(ctx, 'sheet') as SSElement<SecondaryStructure.Sheet>[]).sort(compare_ssr);
  30. return {
  31. fields: struct_sheet_range_fields(),
  32. source: [{ data: elements, rowCount: elements.length }]
  33. };
  34. }
  35. };
  36. function compare_ssr(x: SSElement<SecondaryStructure.Sheet>, y: SSElement<SecondaryStructure.Sheet>) {
  37. const a = x.element, b = y.element;
  38. return a.sheet_id < b.sheet_id ? -1 : a.sheet_id === b.sheet_id ? x.start.element - y.start.element : 1;
  39. };
  40. const struct_conf_fields = (): CifField[] => [
  41. CifField.str<number, SSElement<SecondaryStructure.Helix>[]>('conf_type_id', (i, data) => data[i].element.type_id),
  42. CifField.str<number, SSElement<SecondaryStructure.Helix>[]>('id', (i, data, idx) => `${data[i].element.type_id}${idx + 1}`),
  43. ...residueIdFields<number, SSElement<SecondaryStructure.Helix>[]>((i, e) => e[i].start, { prefix: 'beg' }),
  44. ...residueIdFields<number, SSElement<SecondaryStructure.Helix>[]>((i, e) => e[i].end, { prefix: 'end' }),
  45. CifField.str<number, SSElement<SecondaryStructure.Helix>[]>('pdbx_PDB_helix_class', (i, data) => data[i].element.helix_class),
  46. CifField.str<number, SSElement<SecondaryStructure.Helix>[]>('details', (i, data) => data[i].element.details || '', {
  47. valueKind: (i, d) => !!d[i].element.details ? Column.ValueKind.Present : Column.ValueKind.Unknown
  48. }),
  49. CifField.int<number, SSElement<SecondaryStructure.Helix>[]>('pdbx_PDB_helix_length', (i, data) => data[i].length)
  50. ];
  51. const struct_sheet_range_fields = (): CifField[] => [
  52. CifField.str<number, SSElement<SecondaryStructure.Sheet>[]>('sheet_id', (i, data) => data[i].element.sheet_id),
  53. CifField.index('id'),
  54. ...residueIdFields<number, SSElement<SecondaryStructure.Sheet>[]>((i, e) => e[i].start, { prefix: 'beg' }),
  55. ...residueIdFields<number, SSElement<SecondaryStructure.Sheet>[]>((i, e) => e[i].end, { prefix: 'end' }),
  56. CifField.str('symmetry', (i, data) => '', { valueKind: (i, d) => Column.ValueKind.Unknown })
  57. ];
  58. interface SSElement<T extends SecondaryStructure.Element> {
  59. start: StructureElement.Location,
  60. end: StructureElement.Location,
  61. length: number,
  62. element: T
  63. }
  64. function findElements<T extends SecondaryStructure.Element>(ctx: CifExportContext, kind: SecondaryStructure.Element['kind']) {
  65. // TODO: encode secondary structure for different models?
  66. const secondaryStructure = ModelSecondaryStructure.Provider.get(ctx.firstModel);
  67. if (!secondaryStructure) return [] as SSElement<T>[];
  68. const { key, elements } = secondaryStructure;
  69. const ssElements: SSElement<any>[] = [];
  70. const structure = ctx.structures[0];
  71. for (const unit of structure.unitSymmetryGroups) {
  72. const u = unit.units[0];
  73. if (!Unit.isAtomic(u)) continue;
  74. const segs = u.model.atomicHierarchy.residueAtomSegments;
  75. const residues = Segmentation.transientSegments(segs, u.elements);
  76. let current: Segmentation.Segment, move = true;
  77. while (residues.hasNext) {
  78. if (move) current = residues.move();
  79. const start = current!.index;
  80. const startIdx = key[start];
  81. const element = elements[startIdx];
  82. if (element.kind !== kind) {
  83. move = true;
  84. continue;
  85. }
  86. let prev = start;
  87. while (residues.hasNext) {
  88. prev = current!.index;
  89. current = residues.move();
  90. if (startIdx !== key[current.index]) {
  91. move = false;
  92. ssElements[ssElements.length] = {
  93. start: StructureElement.Location.create(structure, u, segs.offsets[start]),
  94. end: StructureElement.Location.create(structure, u, segs.offsets[prev]),
  95. length: prev - start + 1,
  96. element
  97. };
  98. break;
  99. }
  100. }
  101. }
  102. }
  103. return ssElements as SSElement<T>[];
  104. }