mmcif.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  6. */
  7. import { CifWriter } from 'mol-io/writer/cif'
  8. import { mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif'
  9. import { Structure } from '../structure'
  10. import { Model } from '../model'
  11. import { _atom_site } from './categories/atom_site';
  12. import CifCategory = CifWriter.Category
  13. import { _struct_conf, _struct_sheet_range } from './categories/secondary-structure';
  14. import { _pdbx_struct_mod_residue } from './categories/modified-residues';
  15. export interface CifExportContext {
  16. structure: Structure,
  17. model: Model,
  18. cache: any
  19. }
  20. export namespace CifExportContext {
  21. export function create(structure: Structure, model: Model): CifExportContext {
  22. return { structure, model, cache: Object.create(null) };
  23. }
  24. }
  25. function copy_mmCif_category(name: keyof mmCIF_Schema): CifCategory<CifExportContext> {
  26. return {
  27. name,
  28. instance({ model }) {
  29. if (model.sourceData.kind !== 'mmCIF') return CifCategory.Empty;
  30. const table = model.sourceData.data[name];
  31. if (!table || !table._rowCount) return CifCategory.Empty;
  32. return CifCategory.ofTable(table);
  33. }
  34. };
  35. }
  36. const _entity: CifCategory<CifExportContext> = {
  37. name: 'entity',
  38. instance({ structure, model}) {
  39. const keys = Structure.getEntityKeys(structure);
  40. return CifCategory.ofTable(model.entities.data, keys);
  41. }
  42. }
  43. const Categories = [
  44. // Basics
  45. copy_mmCif_category('entry'),
  46. copy_mmCif_category('exptl'),
  47. _entity,
  48. // Symmetry
  49. copy_mmCif_category('cell'),
  50. copy_mmCif_category('symmetry'),
  51. // Assemblies
  52. copy_mmCif_category('pdbx_struct_assembly'),
  53. copy_mmCif_category('pdbx_struct_assembly_gen'),
  54. copy_mmCif_category('pdbx_struct_oper_list'),
  55. // Secondary structure
  56. _struct_conf,
  57. _struct_sheet_range,
  58. // Sequence
  59. copy_mmCif_category('struct_asym'), // TODO: filter only present chains?
  60. copy_mmCif_category('entity_poly'),
  61. copy_mmCif_category('entity_poly_seq'),
  62. // Branch
  63. copy_mmCif_category('pdbx_entity_branch'),
  64. copy_mmCif_category('pdbx_entity_branch_link'),
  65. copy_mmCif_category('pdbx_branch_scheme'),
  66. // Misc
  67. // TODO: filter for actual present residues?
  68. copy_mmCif_category('chem_comp'),
  69. copy_mmCif_category('pdbx_chem_comp_identifier'),
  70. copy_mmCif_category('atom_sites'),
  71. _pdbx_struct_mod_residue,
  72. // Atoms
  73. _atom_site
  74. ];
  75. namespace _Filters {
  76. export const AtomSitePositionsFieldNames = new Set<string>(<(keyof typeof mmCIF_Schema.atom_site)[]>['id', 'Cartn_x', 'Cartn_y', 'Cartn_z']);
  77. }
  78. export const mmCIF_Export_Filters = {
  79. onlyPositions: <CifWriter.Category.Filter>{
  80. includeCategory(name) { return name === 'atom_site'; },
  81. includeField(cat, field) { return _Filters.AtomSitePositionsFieldNames.has(field); }
  82. }
  83. }
  84. /** Doesn't start a data block */
  85. export function encode_mmCIF_categories(encoder: CifWriter.Encoder, structure: Structure, params?: { skipCategoryNames?: Set<string>, exportCtx?: CifExportContext }) {
  86. const models = structure.models;
  87. if (models.length !== 1) throw 'Can\'t export stucture composed from multiple models.';
  88. const model = models[0];
  89. const _params = params || { };
  90. const ctx: CifExportContext[] = [_params.exportCtx ? _params.exportCtx : CifExportContext.create(structure, model)];
  91. for (const cat of Categories) {
  92. if (_params.skipCategoryNames && _params.skipCategoryNames.has(cat.name)) continue;
  93. encoder.writeCategory(cat, ctx);
  94. }
  95. for (const customProp of model.customProperties.all) {
  96. if (!customProp.cifExport || customProp.cifExport.categories.length === 0) continue;
  97. const prefix = customProp.cifExport.prefix;
  98. const cats = customProp.cifExport.categories;
  99. for (const cat of cats) {
  100. if (_params.skipCategoryNames && _params.skipCategoryNames.has(cat.name)) continue;
  101. if (cat.name.indexOf(prefix) !== 0) throw new Error(`Custom category '${cat.name}' name must start with prefix '${prefix}.'`);
  102. encoder.writeCategory(cat, ctx);
  103. }
  104. }
  105. }
  106. function to_mmCIF(name: string, structure: Structure, asBinary = false) {
  107. const enc = CifWriter.createEncoder({ binary: asBinary });
  108. enc.startDataBlock(name);
  109. encode_mmCIF_categories(enc, structure);
  110. return enc.getData();
  111. }
  112. export default to_mmCIF