Browse Source

Merge pull request #415 from JonStargaryen/master

ModelServer Ligand Export: Fix Alternate Locations & Mismatching Atoms
David Sehnal 3 years ago
parent
commit
5e9d8298ef

+ 1 - 0
CHANGELOG.md

@@ -6,6 +6,7 @@ Note that since we don't clearly distinguish between a public and private interf
 
 ## [Unreleased]
 
+- ModelServer ligand queries: fixes for alternate locations, additional atoms & UNL ligand
 
 ## [v3.6.1] - 2022-04-03
 

+ 15 - 8
src/mol-io/writer/ligand-encoder.ts

@@ -9,12 +9,14 @@ import { Writer } from './writer';
 import { Encoder, Category, Field } from './cif/encoder';
 import { ComponentAtom } from '../../mol-model-formats/structure/property/atoms/chem_comp';
 import { ComponentBond } from '../../mol-model-formats/structure/property/bonds/chem_comp';
+import { getElementIdx, isHydrogen } from '../../mol-model/structure/structure/unit/bonds/common';
+import { ElementSymbol } from '../../mol-model/structure/model/types';
 
 interface Atom {
     Cartn_x: number,
     Cartn_y: number,
     Cartn_z: number,
-    type_symbol: string,
+    type_symbol: ElementSymbol,
     index: number
 }
 
@@ -109,11 +111,12 @@ export abstract class LigandEncoder implements Encoder<string> {
                 const key = it.move();
 
                 const lai = label_atom_id.value(key, data, index) as string;
-                const ts = type_symbol.value(key, data, index) as string;
-                if (this.skipHydrogen(ts)) {
-                    index++;
-                    continue;
-                }
+                // ignore all alternate locations after the first
+                if (atoms.has(lai)) continue;
+
+                const ts = type_symbol.value(key, data, index) as ElementSymbol;
+                if (this.skipHydrogen(ts)) continue;
+
                 const a: { [k: string]: (string | number) } = {};
 
                 for (let _f = 0, _fl = fields.length; _f < _fl; _f++) {
@@ -131,11 +134,15 @@ export abstract class LigandEncoder implements Encoder<string> {
         return atoms;
     }
 
-    protected skipHydrogen(type_symbol: string) {
+    protected skipHydrogen(type_symbol: ElementSymbol) {
         if (this.hydrogens) {
             return false;
         }
-        return type_symbol === 'H';
+        return this.isHydrogen(type_symbol);
+    }
+
+    protected isHydrogen(type_symbol: ElementSymbol) {
+        return isHydrogen(getElementIdx(type_symbol));
     }
 
     private getSortedFields<Ctx>(instance: Category.Instance<Ctx>, names: string[]) {

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

@@ -26,14 +26,27 @@ export class MolEncoder extends LigandEncoder {
 
         const atomMap = this.componentAtomData.entries.get(name)!;
         const bondMap = this.componentBondData.entries.get(name)!;
+        // happens for the unknown ligands (UNL)
+        if (!atomMap) throw Error(`The Chemical Component Dictionary doesn't hold any atom data for ${name}`);
+
         let bondCount = 0;
         let chiral = false;
 
         // traverse once to determine all actually present atoms
         const atoms = this.getAtoms(instance, source);
         atoms.forEach((atom1, label_atom_id1) => {
-            const { index: i1 } = atom1;
-            const { charge, stereo_config } = atomMap.map.get(label_atom_id1)!;
+            const { index: i1, type_symbol: type_symbol1 } = atom1;
+            const atomMapData1 = atomMap.map.get(label_atom_id1);
+
+            if (!atomMapData1) {
+                if (this.isHydrogen(type_symbol1)) {
+                    return;
+                } else {
+                    throw Error(`Unknown atom ${label_atom_id1} for component ${name}`);
+                }
+            }
+
+            const { charge, stereo_config } = atomMapData1;
             StringBuilder.writePadLeft(ctab, atom1.Cartn_x.toFixed(4), 10);
             StringBuilder.writePadLeft(ctab, atom1.Cartn_y.toFixed(4), 10);
             StringBuilder.writePadLeft(ctab, atom1.Cartn_z.toFixed(4), 10);
@@ -50,8 +63,8 @@ export class MolEncoder extends LigandEncoder {
                 const atom2 = atoms.get(label_atom_id2);
                 if (!atom2) return;
 
-                const { index: i2, type_symbol: type_symbol2 } = atom2;
-                if (i1 < i2 && !this.skipHydrogen(type_symbol2)) {
+                const { index: i2 } = atom2;
+                if (i1 < i2) {
                     const { order } = bond;
                     StringBuilder.writeIntegerPadLeft(bonds, i1 + 1, 3);
                     StringBuilder.writeIntegerPadLeft(bonds, i2 + 1, 3);

+ 17 - 4
src/mol-io/writer/mol2/encoder.ts

@@ -29,21 +29,34 @@ 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 atomMap = this.componentAtomData.entries.get(name)!;
         const bondMap = this.componentBondData.entries.get(name)!;
+        // happens for the unknown ligands (UNL)
+        if (!atomMap) throw Error(`The Chemical Component Dictionary doesn't hold any atom data for ${name}`);
         let bondCount = 0;
 
         const atoms = this.getAtoms(instance, source);
         StringBuilder.writeSafe(a, '@<TRIPOS>ATOM\n');
         StringBuilder.writeSafe(b, '@<TRIPOS>BOND\n');
         atoms.forEach((atom1, label_atom_id1) => {
-            const { index: i1 } = atom1;
+            const { index: i1, type_symbol: type_symbol1 } = atom1;
+            const atomMapData1 = atomMap.map.get(label_atom_id1);
+
+            if (!atomMapData1) {
+                if (this.isHydrogen(type_symbol1)) {
+                    return;
+                } else {
+                    throw Error(`Unknown atom ${label_atom_id1} for component ${name}`);
+                }
+            }
+
             if (bondMap?.map) {
                 bondMap.map.get(label_atom_id1)!.forEach((bond, label_atom_id2) => {
                     const atom2 = atoms.get(label_atom_id2);
                     if (!atom2) return;
 
-                    const { index: i2, type_symbol: type_symbol2 } = atom2;
-                    if (i1 < i2 && !this.skipHydrogen(type_symbol2)) {
+                    const { index: i2 } = atom2;
+                    if (i1 < i2) {
                         const { order, flags } = bond;
                         const ar = BondType.is(BondType.Flag.Aromatic, flags);
                         StringBuilder.writeSafe(b, `${++bondCount} ${i1 + 1} ${i2 + 1} ${ar ? 'ar' : order}`);
@@ -52,7 +65,7 @@ export class Mol2Encoder extends LigandEncoder {
                 });
             }
 
-            const sybyl = bondMap?.map ? this.mapToSybyl(label_atom_id1, atom1.type_symbol, bondMap) : atom1.type_symbol;
+            const sybyl = bondMap?.map ? this.mapToSybyl(label_atom_id1, type_symbol1, bondMap) : type_symbol1;
             StringBuilder.writeSafe(a, `${i1 + 1} ${label_atom_id1} ${atom1.Cartn_x.toFixed(3)} ${atom1.Cartn_y.toFixed(3)} ${atom1.Cartn_z.toFixed(3)} ${sybyl} 1 ${name} 0.000\n`);
         });
 

+ 6 - 0
src/servers/model/CHANGELOG.md

@@ -1,3 +1,9 @@
+# 0.9.9
+* /ligand queries: fix behavior for alternate locations
+* /ligand queries: handle additional atoms more gracefully
+* /ligand queries: better error message for UNL
+* /ligand queries: treat deuterium/tritium as hydrogen
+
 # 0.9.8
 * fix support for chem_comp_bond and struct_conn categories
 

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

@@ -237,10 +237,8 @@ async function resolveJobEntry(entry: JobEntry, structure: StructureWrapper, enc
         encoder.writeCategory(_model_server_params, entry);
 
         if (entry.queryDefinition.niceName === 'Ligand') {
-            if (encoder instanceof MolEncoder) {
-                encoder.setComponentAtomData(ComponentAtom.Provider.get(structure.models[0])!);
-            }
             if (encoder instanceof MolEncoder || encoder instanceof Mol2Encoder) {
+                encoder.setComponentAtomData(ComponentAtom.Provider.get(structure.models[0])!);
                 encoder.setComponentBondData(ComponentBond.Provider.get(structure.models[0])!);
             }
         }

+ 1 - 1
src/servers/model/version.ts

@@ -4,4 +4,4 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-export const VERSION = '0.9.8';
+export const VERSION = '0.9.9';