encoder.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /**
  2. * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
  5. */
  6. import { Category } from "../../../../mol-io/writer/cif/encoder";
  7. import { LigandEncoder } from "../ligand-encoder";
  8. import { StringBuilder } from "../../../../mol-util";
  9. import { getCategoryInstanceData } from "../../../../mol-io/writer/cif/encoder/util";
  10. import { BondType } from "../../../../mol-model/structure/model/types";
  11. // specification: http://chemyang.ccnu.edu.cn/ccb/server/AIMMS/mol2.pdf
  12. // TODO amide (and real sp/sp2/sp3) support for bonds and SYBYL atom types: see https://www.sdsc.edu/CCMS/Packages/cambridge/pluto/atom_types.html
  13. // TODO support charges
  14. export class Mol2Encoder extends LigandEncoder {
  15. private meta: StringBuilder;
  16. private out: StringBuilder;
  17. private encoded = false;
  18. private error = false;
  19. writeCategory<Ctx>(category: Category<Ctx>, context?: Ctx): void {
  20. if (this.encoded) {
  21. throw new Error('The writer contents have already been encoded, no more writing.');
  22. }
  23. if (this.metaInformation && (category.name === 'model_server_result' || category.name === 'model_server_params' || category.name === 'model_server_stats')) {
  24. this.writeFullCategory(this.meta, category, context);
  25. return;
  26. }
  27. // if error: force writing of meta information
  28. if (category.name === 'model_server_error') {
  29. this.writeFullCategory(this.meta, category, context);
  30. this.error = true;
  31. return;
  32. }
  33. // only care about atom_site category when writing SDF
  34. if (category.name !== 'atom_site') {
  35. return;
  36. }
  37. const a = StringBuilder.create();
  38. const b = StringBuilder.create();
  39. const { instance, source } = getCategoryInstanceData(category, context);
  40. // write header
  41. const name = this.getName(instance, source);
  42. StringBuilder.writeSafe(this.builder, `# Name: ${name}\n# Created by ${this.encoder}\n\n`);
  43. const bondMap = this.componentData.entries.get(name)!;
  44. let bondCount = 0;
  45. const atoms = this.getAtoms(instance, source);
  46. StringBuilder.writeSafe(a, '@<TRIPOS>ATOM\n');
  47. StringBuilder.writeSafe(b, '@<TRIPOS>BOND\n');
  48. for (let i1 = 0, il = atoms.length; i1 < il; i1++) {
  49. const atom = atoms[i1];
  50. let aromatic = false;
  51. bondMap.map.get(atom.id)!.forEach((v, k) => {
  52. const i2 = atoms.findIndex(e => e.id === k);
  53. const label2 = this.getLabel(k);
  54. if (i1 < i2 && atoms.findIndex(e => e.id === k) > -1 && !this.skipHydrogen(label2)) {
  55. const { order, flags } = v;
  56. const ar = flags === BondType.Flag.Aromatic;
  57. if (ar) aromatic = true;
  58. StringBuilder.writeSafe(b, `${++bondCount} ${i1 + 1} ${i2 + 1} ${ar ? 'ar' : order}`);
  59. StringBuilder.newline(b);
  60. }
  61. });
  62. const sub = aromatic ? '.ar' : '';
  63. StringBuilder.writeSafe(a, `${i1 + 1} ${atom.type_symbol} ${atom.x.toFixed(3)} ${atom.y.toFixed(3)} ${atom.z.toFixed(3)} ${atom.type_symbol}${sub} 1 ${name} 0.000\n`);
  64. }
  65. StringBuilder.writeSafe(this.out, `@<TRIPOS>MOLECULE\n${name}\n${atoms.length} ${bondCount} 0 0 0\nSMALL\nNO_CHARGES\n\n`);
  66. StringBuilder.writeSafe(this.out, StringBuilder.getString(a));
  67. StringBuilder.writeSafe(this.out, StringBuilder.getString(b));
  68. StringBuilder.writeSafe(this.out, `@<TRIPOS>SUBSTRUCTURE\n${name} ${name} 1\n`);
  69. }
  70. private writeFullCategory<Ctx>(sb: StringBuilder, category: Category<Ctx>, context?: Ctx) {
  71. const { instance, source } = getCategoryInstanceData(category, context);
  72. const fields = instance.fields;
  73. const src = source[0];
  74. const data = src.data;
  75. const it = src.keys();
  76. const key = it.move();
  77. for (let _f = 0; _f < fields.length; _f++) {
  78. const f = fields[_f]!;
  79. StringBuilder.writeSafe(sb, `# ${category.name}.${f.name}: `);
  80. const val = f.value(key, data, 0);
  81. StringBuilder.writeSafe(sb, val as string);
  82. StringBuilder.newline(sb);
  83. }
  84. StringBuilder.newline(sb);
  85. }
  86. encode(): void {
  87. // write meta-information, do so after ctab
  88. if (this.error || this.metaInformation) {
  89. StringBuilder.writeSafe(this.builder, StringBuilder.getString(this.meta));
  90. }
  91. StringBuilder.writeSafe(this.builder, StringBuilder.getString(this.out));
  92. this.encoded = true;
  93. }
  94. constructor(readonly encoder: string, readonly metaInformation: boolean, readonly hydrogens: boolean) {
  95. super(encoder, hydrogens);
  96. this.meta = StringBuilder.create();
  97. this.out = StringBuilder.create();
  98. }
  99. }