Browse Source

mol writing with inline charges and chiral flag

JonStargaryen 4 years ago
parent
commit
48d0418f0e

+ 9 - 3
src/mol-io/writer/ligand-encoder.ts

@@ -7,6 +7,7 @@
 import { StringBuilder } from '../../mol-util';
 import Writer from './writer';
 import { Encoder, Category, Field } from './cif/encoder';
+import { ComponentAtom } from '../../mol-model-formats/structure/property/bonds/atom';
 import { ComponentBond } from '../../mol-model-formats/structure/property/bonds/comp';
 
 interface Atom {
@@ -24,7 +25,8 @@ function Atom(partial: any): Atom {
 export abstract class LigandEncoder implements Encoder<string> {
     protected builder: StringBuilder;
     protected meta: StringBuilder;
-    protected componentData: ComponentBond;
+    protected componentAtomData: ComponentAtom;
+    protected componentBondData: ComponentBond;
     protected error = false;
     protected encoded = false;
     readonly isBinary = false;
@@ -61,8 +63,12 @@ export abstract class LigandEncoder implements Encoder<string> {
         this._writeCategory(category, context);
     }
 
-    setComponentBondData(componentData: ComponentBond) {
-        this.componentData = componentData;
+    setComponentAtomData(componentAtomData: ComponentAtom) {
+        this.componentAtomData = componentAtomData;
+    }
+
+    setComponentBondData(componentBondData: ComponentBond) {
+        this.componentBondData = componentBondData;
     }
 
     writeTo(stream: Writer) {

+ 22 - 4
src/mol-io/writer/mol/encoder.ts

@@ -11,7 +11,6 @@ import { LigandEncoder } from '../ligand-encoder';
 
 // specification: http://c4.cabrillo.edu/404/ctfile.pdf
 // SDF wraps MOL and allows for multiple molecules per file as well as additional properties
-// TODO add support for stereo/chiral flags, add charges
 export class MolEncoder extends LigandEncoder {
     _writeCategory<Ctx>(category: Category<Ctx>, context?: Ctx) {
         // use separate builder because we still need to write Counts and Bonds line
@@ -25,19 +24,25 @@ export class MolEncoder extends LigandEncoder {
         // 3rd lines must be present and can contain comments
         StringBuilder.writeSafe(this.builder, `${name}\n  ${this.encoder}\n\n`);
 
-        const bondMap = this.componentData.entries.get(name)!;
+        const atomMap = this.componentAtomData.entries.get(name)!;
+        const bondMap = this.componentBondData.entries.get(name)!;
         let bondCount = 0;
+        let chiral = false;
 
         // traverse once to determine all actually present atoms
         const atoms = this.getAtoms(instance, source);
         for (let i1 = 0, il = atoms.length; i1 < il; i1++) {
             const atom = atoms[i1];
+            const { charge, stereo_config } = atomMap.map.get(atom.label_atom_id)!;
             StringBuilder.writePadLeft(ctab, atom.Cartn_x.toFixed(4), 10);
             StringBuilder.writePadLeft(ctab, atom.Cartn_y.toFixed(4), 10);
             StringBuilder.writePadLeft(ctab, atom.Cartn_z.toFixed(4), 10);
             StringBuilder.whitespace1(ctab);
             StringBuilder.writePadRight(ctab, atom.type_symbol, 2);
-            StringBuilder.writeSafe(ctab, '  0  0  0  0  0  0  0  0  0  0  0  0\n');
+            StringBuilder.writeSafe(ctab, '  0');
+            StringBuilder.writeIntegerPadLeft(ctab, this.mapCharge(charge), 3);
+            StringBuilder.writeSafe(ctab, '  0  0  0  0  0  0  0  0  0  0\n');
+            if (stereo_config !== 'N') chiral = true;
 
             bondMap.map.get(atom.label_atom_id)!.forEach((v, k) => {
                 const i2 = atoms.findIndex(e => e.label_atom_id === k);
@@ -56,7 +61,7 @@ export class MolEncoder extends LigandEncoder {
         // write counts line
         StringBuilder.writeIntegerPadLeft(this.builder, atoms.length, 3);
         StringBuilder.writeIntegerPadLeft(this.builder, bondCount, 3);
-        StringBuilder.writeSafe(this.builder, '  0  0  0  0  0  0  0  0  0\n');
+        StringBuilder.writeSafe(this.builder, `  0  0  ${chiral ? 1 : 0}  0  0  0  0  0  0\n`);
 
         StringBuilder.writeSafe(this.builder, StringBuilder.getString(ctab));
         StringBuilder.writeSafe(this.builder, StringBuilder.getString(bonds));
@@ -64,6 +69,19 @@ export class MolEncoder extends LigandEncoder {
         StringBuilder.writeSafe(this.builder, 'M  END\n');
     }
 
+    private mapCharge(raw: number): number {
+        // 0 = uncharged or value other than these, 1 = +3, 2 = +2, 3 = +1, 4 = doublet radical, 5 = -1, 6 = -2, 7 = -3
+        switch (raw) {
+            case 3: return 1;
+            case 2: return 2;
+            case 1: return 3;
+            case -1: return 5;
+            case -2: return 6;
+            case -3: return 7;
+            default: return 0;
+        }
+    }
+
     protected writeFullCategory<Ctx>(sb: StringBuilder, category: Category<Ctx>, context?: Ctx) {
         const { instance, source } = getCategoryInstanceData(category, context);
         const fields = instance.fields;

+ 1 - 2
src/mol-io/writer/mol2/encoder.ts

@@ -12,7 +12,6 @@ import { BondType } from '../../../mol-model/structure/model/types';
 
 // specification: http://chemyang.ccnu.edu.cn/ccb/server/AIMMS/mol2.pdf
 // 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
-// TODO support charges
 export class Mol2Encoder extends LigandEncoder {
     private out: StringBuilder;
 
@@ -25,7 +24,7 @@ export class Mol2Encoder extends LigandEncoder {
         const name = this.getName(instance, source);
         StringBuilder.writeSafe(this.builder, `# Name: ${name}\n# Created by ${this.encoder}\n\n`);
 
-        const bondMap = this.componentData.entries.get(name)!;
+        const bondMap = this.componentBondData.entries.get(name)!;
         let bondCount = 0;
 
         const atoms = this.getAtoms(instance, source);

+ 10 - 1
src/servers/model/server/query.ts

@@ -28,6 +28,7 @@ import { MolWriter } from '../../../mol-io/writer/mol';
 import { Mol2Writer } from '../../../mol-io/writer/mol2';
 import { MolEncoder } from '../../../mol-io/writer/mol/encoder';
 import { Mol2Encoder } from '../../../mol-io/writer/mol2/encoder';
+import { ComponentAtom } from '../../../mol-model-formats/structure/property/bonds/atom';
 
 export interface Stats {
     structure: StructureWrapper,
@@ -227,7 +228,15 @@ async function resolveJobEntry(entry: JobEntry, structure: StructureWrapper, enc
         encoder.writeCategory(_model_server_result, entry);
         encoder.writeCategory(_model_server_params, entry);
 
-        if (encoder instanceof MolEncoder || encoder instanceof Mol2Encoder) encoder.setComponentBondData(ComponentBond.Provider.get(structure.models[0])!);
+        if (entry.queryDefinition.niceName === 'Ligand') {
+            if (encoder instanceof MolEncoder || encoder instanceof Mol2Encoder) {
+                encoder.setComponentAtomData(ComponentAtom.Provider.get(structure.models[0])!);
+                encoder.setComponentBondData(ComponentBond.Provider.get(structure.models[0])!);
+            }
+        } else {
+            // TODO propagate data for cif/bcif as well
+        }
+
         if (!entry.copyAllCategories && entry.queryDefinition.filter) encoder.setFilter(entry.queryDefinition.filter);
         if (result.length > 0) encode_mmCIF_categories(encoder, result, { copyAllCategories: entry.copyAllCategories });
         if (!entry.copyAllCategories && entry.queryDefinition.filter) encoder.setFilter();