Browse Source

wip sybyl

JonStargaryen 4 years ago
parent
commit
78c9dda257
2 changed files with 36 additions and 27 deletions
  1. 1 0
      src/mol-io/writer/ligand-encoder.ts
  2. 35 27
      src/mol-io/writer/mol2/encoder.ts

+ 1 - 0
src/mol-io/writer/ligand-encoder.ts

@@ -136,6 +136,7 @@ export abstract class LigandEncoder implements Encoder<string> {
     }
 
     protected getLabel(s: string) {
+        // actually, getTypeSymbol would be way more useful
         return s.replace(/[^A-Z]+/g, '');
     }
 

+ 35 - 27
src/mol-io/writer/mol2/encoder.ts

@@ -13,7 +13,7 @@ import { ComponentBond } from '../../../mol-model-formats/structure/property/bon
 
 // type MOL_TYPE = 'SMALL' | 'BIOPOLYMER' | 'PROTEIN' | 'NUCLEIC_ACID' | 'SACCHARIDE';
 // type CHARGE_TYPE = 'NO_CHARGES' | 'DEL_RE' | 'GASTEIGER' | 'GAST_HUCK' | 'HUCKEL' | 'PULLMAN' | 'GAUSS80_CHARGES' | 'AMPAC_CHARGES' | 'MULLIKEN_CHARGES' | 'DICT_ CHARGES' | 'MMFF94_CHARGES' | 'USER_CHARGES';
-const NON_METAL_ATOMS = new Set('H D B C N O F Si P S Cl As Se Br Te I At He Ne Ar Kr Xe Rn'.split(' '));
+const NON_METAL_ATOMS = 'H D B C N O F Si P S Cl As Se Br Te I At He Ne Ar Kr Xe Rn'.split(' ');
 type BondData = { label_atom_id: string, order: number, aromatic: boolean };
 
 // specification: http://chemyang.ccnu.edu.cn/ccb/server/AIMMS/mol2.pdf
@@ -39,6 +39,9 @@ export class Mol2Encoder extends LigandEncoder {
             const atom = atoms[i1];
             const lai = atom.label_atom_id;
 
+            console.log(lai);
+            console.log(bondMap.map);
+            console.log(bondMap.map.get(lai));
             bondMap.map.get(lai)!.forEach((v, lai2) => {
                 const i2 = atoms.findIndex(e => e.label_atom_id === lai2);
                 const label2 = this.getLabel(lai2);
@@ -72,37 +75,37 @@ export class Mol2Encoder extends LigandEncoder {
         const bond = this.toArray(partialBondMap);
 
         const num_bond = bond.length;
-        const nonmet = bond.filter(e => NON_METAL_ATOMS.has(e.label_atom_id));
+        const nonmet = bond.filter(b => this.isNonMetalBond(b));
         const num_nonmet = nonmet.length;
         const arom = bond.filter(e => e.aromatic);
         const num_arom = arom.length;
+        console.log(`${label_atom_id} ${num_bond} ${num_nonmet} ${num_arom}`);
 
         // TODO if altLoc: 'Du' // 1.1
         // TODO if end of polymeric bond: 'Du' // 1.2
         if (type_symbol === 'D') return 'H'; // 1.3
-        if (type_symbol === 'P') return 'P.3'; // 1.4
+        if (type_symbol === 'P') return 'P.3'; // 1.4, 4mpo/ligand?encoding=mol2&auth_seq_id=203 (PO4)
         if (type_symbol === 'Co' || type_symbol === 'Ru') return type_symbol + '.oh'; // 1.5
         if (type_symbol === 'C') { // 1.6
-            console.log(num_bond + ' ' + num_arom);
-            if (num_bond >= 4 && bond.every(b => b.order === 1)) return 'C.3'; // 1.6.1
-            if (num_bond === 3 && this.isCat(label_atom_id, bond, bondMap)) return 'C.cat'; // 1.6.2
-            if (num_bond >= 2 && num_arom === 2) return 'C.ar'; // 1.6.3
-            if ((num_bond === 1 || num_bond === 2) && bond.filter(b => b.order === 3).length === 1) return 'C.1'; // 1.6.4
+            if (num_bond >= 4 && bond.every(b => b.order === 1)) return 'C.3'; // 1.6.1, 3rga/ligand?encoding=mol2&auth_seq_id=307 (MOH)
+            if (num_bond === 3 && this.isCat(label_atom_id, bond, bondMap)) return 'C.cat'; // 1.6.2, 1acj/ligand?encoding=mol2&auth_seq_id=19 (ARG, incomplete), 1acj/ligand?encoding=mol2&auth_seq_id=44 (ARG), 5vj/ligand?encoding=mol2&auth_seq_id=101 (GAI)
+            if (num_bond >= 2 && num_arom >= 2) return 'C.ar'; // 1.6.3, 1acj/ligand?encoding=mol2&auth_seq_id=30 (PHE), 1acj/ligand?encoding=mol2&auth_seq_id=63 (TYR), 1acj/ligand?encoding=mol2&auth_seq_id=84 (TRP), 1acj/ligand?encoding=mol2&auth_seq_id=999 (THA)
+            if ((num_bond === 1 || num_bond === 2) && bond.filter(b => b.order === 3).length === 1) return 'C.1'; // 1.6.4, 3i04/ligand?encoding=mol2&auth_asym_id=C&auth_seq_id=900 (CYN)
             return 'C.2'; // 1.6.5
         }
         if (type_symbol === 'O') { // 1.7
             if (num_nonmet === 1) { // 1.7.1
-                if (this.isOC(nonmet[0], bondMap)) return 'C.o2'; // 1.7.1.1
-                if (this.isOP(nonmet[0], bondMap)) return 'C.o2'; // 1.7.1.2
+                if (this.isOC(nonmet[0], bondMap)) return 'O.co2'; // 1.7.1.1, 4h2v/ligand?encoding=mol2&auth_seq_id=403 (ACT)
+                if (this.isOP(nonmet[0], bondMap)) return 'O.co2'; // 1.7.1.2, 4mpo/ligand?encoding=mol2&auth_seq_id=203 (PO4)
             }
-            if (num_nonmet >= 2 && bond.every(b => b.order === 1)) return 'O.3'; // 1.7.2
+            if (num_nonmet >= 2 && bond.every(b => b.order === 1)) return 'O.3'; // 1.7.2, 1acj/ligand?encoding=mol2&auth_seq_id=601 (HOH), 3rga/ligand?encoding=mol2&auth_seq_id=307 (MOH)
             return 'O.2'; // 1.7.3
         }
         if (type_symbol === 'N') { // 1.8
             if (num_nonmet === 4 && bond.every(b => b.order === 1)) return 'N.4'; // 1.8.1
-            if (num_bond >= 2 && num_arom === 2) return 'N.ar'; // 1.8.2
-            if (num_nonmet === 1 && nonmet.some(b => b.order === 3)) return 'N.1'; // 1.8.3
-            if (num_nonmet === 2 && (nonmet[0].order + nonmet[1].order === 4)) return 'N.1'; // 1.8.4
+            if (num_bond >= 2 && num_arom === 2) return 'N.ar'; // 1.8.2, 1acj/ligand?encoding=mol2&auth_seq_id=84 (TRP), 1acj/ligand?encoding=mol2&auth_seq_id=999 (THA)
+            if (num_nonmet === 1 && nonmet.some(b => b.order === 3)) return 'N.1'; // 1.8.3, 3i04/ligand?encoding=mol2&auth_asym_id=C&auth_seq_id=900 (CYN)
+            if (num_nonmet === 2 && (nonmet[0].order + nonmet[1].order === 4)) return 'N.1'; // 1.8.4, 3sbr/ligand?encoding=mol2&auth_seq_id=640&auth_asym_id=D (N2O)
             if (num_nonmet === 3 && this.hasCOCS(nonmet, bondMap)) return 'N.am'; // 1.8.5
             if (num_nonmet === 3) { // 1.8.6
                 if (nonmet.filter(b => b.order > 1).length === 1) return 'N.pl3'; // 1.8.6.1
@@ -125,6 +128,11 @@ export class Mol2Encoder extends LigandEncoder {
         return type_symbol; // 1.11
     }
 
+    private isNonMetalBond(b: BondData): boolean {
+        // would be nice to have type_symbol here...
+        return NON_METAL_ATOMS.some(a => this.getLabel(b.label_atom_id).startsWith(a));
+    }
+
     // 1.8.6.2.1: If one single bond is to an atom that forms a bond of type double, triple, aromatic or
     // delocalised .AND. one other single bond is to H then atom_type is N.pl3
     // 1.8.6.2.2: If one single bond is to an atom that forms a bond of type double, triple, aromatic or
@@ -136,8 +144,8 @@ export class Mol2Encoder extends LigandEncoder {
             const consumed = nonmet[i];
             // determine index that fulfills 1st criterion
             if (this.toArray(bondMap.map.get(consumed.label_atom_id)!).some(b => b.order > 1 || b.aromatic)) {
-                if (nonmet.filter(b => b !== consumed).filter(b => this.getLabel(b.label_atom_id) === 'H').length === 1) return true; // 1.8.6.2.1
-                if (nonmet.filter(b => b !== consumed).every(b => this.getLabel(b.label_atom_id) !== 'H')) return true; // 1.8.6.2.2
+                if (nonmet.filter(b => b !== consumed).filter(b => this.getLabel(b.label_atom_id).startsWith('H')).length === 1) return true; // 1.8.6.2.1
+                if (nonmet.filter(b => b !== consumed).every(b => !this.getLabel(b.label_atom_id).startsWith('H'))) return true; // 1.8.6.2.2
             }
         }
         return false;
@@ -146,27 +154,27 @@ export class Mol2Encoder extends LigandEncoder {
     // If bond is to carbon .AND. carbon forms a total of 3 bonds, 2 of which are to an oxygen
     // forming only 1 non-metal bond then atom_type is O.co2
     private isOC(nonmet: BondData, bondMap: ComponentBond.Entry): boolean {
-        if (this.getLabel(nonmet.label_atom_id) !== 'C') return false;
+        if (!this.getLabel(nonmet.label_atom_id).startsWith('C')) return false;
         const carbonBonds = this.toArray(bondMap.map.get(nonmet.label_atom_id)!);
         if (carbonBonds.length !== 3) return false;
-        return carbonBonds.filter(b => this.getLabel(b.label_atom_id) === 'O' &&
-            this.toArray(bondMap.map.get(b.label_atom_id)!).filter(ob => NON_METAL_ATOMS.has(this.getLabel(ob.label_atom_id))).length === 1).length === 2;
+        return carbonBonds.filter(b => this.getLabel(b.label_atom_id).startsWith('O') &&
+            this.toArray(bondMap.map.get(b.label_atom_id)!).filter(ob => this.isNonMetalBond(ob)).length === 1).length === 2;
     }
 
     // If bond is to phosphorus .AND. phosphorus forms at least 2 bonds to an oxygen forming
     // only 1 non-metal bond then atom_type is O.co2
     private isOP(nonmet: BondData, bondMap: ComponentBond.Entry): boolean {
-        if (this.getLabel(nonmet.label_atom_id) !== 'P') return false;
+        if (!this.getLabel(nonmet.label_atom_id).startsWith('P')) return false;
         const phosphorusBonds = this.toArray(bondMap.map.get(nonmet.label_atom_id)!);
         if (phosphorusBonds.length < 2) return false;
-        return phosphorusBonds.filter(b => this.getLabel(b.label_atom_id) === 'O' &&
-            this.toArray(bondMap.map.get(b.label_atom_id)!).filter(ob => NON_METAL_ATOMS.has(this.getLabel(ob.label_atom_id))).length === 1).length >= 2;
+        return phosphorusBonds.filter(b => this.getLabel(b.label_atom_id).startsWith('O') &&
+            this.toArray(bondMap.map.get(b.label_atom_id)!).filter(ob => this.isNonMetalBond(ob)).length === 1).length >= 2;
     }
 
     // If num_bond .eq. 3 .AND. all bonds are acyclic .AND. all bonds are to nitrogen .AND. each
     // nitrogen forms bonds to 2 other atoms both of which are not oxygen then atom_type is C.cat.
     private isCat(root: string, bond: BondData[], bondMap: ComponentBond.Entry): boolean {
-        if (bond.some(b => this.getLabel(b.label_atom_id) !== 'N')) return false;
+        if (bond.some(b => !this.getLabel(b.label_atom_id).startsWith('N'))) return false;
         const nitrogenBonds = bond.map(b => b.label_atom_id).map(label_atom_id => this.toArray(bondMap.map.get(label_atom_id)!));
 
         // ensure no cycles
@@ -180,18 +188,18 @@ export class Mol2Encoder extends LigandEncoder {
 
     private countOfOxygenWithSingleNonmet(nonmet: BondData[], bondMap: ComponentBond.Entry): number {
         return nonmet.map(b => b.label_atom_id)
-            .filter(label_atom_id => this.getLabel(label_atom_id) === 'O')
+            .filter(label_atom_id => this.getLabel(label_atom_id).startsWith('O'))
             .map(label_atom_id => this.toArray(bondMap.map.get(label_atom_id)!)
-                .filter(b => NON_METAL_ATOMS.has(this.getLabel(b.label_atom_id))).length === 1)
+                .filter(b => this.isNonMetalBond(b)).length === 1)
             .length;
     }
 
     private hasCOCS(nonmet: BondData[], bondMap: ComponentBond.Entry): boolean {
         return nonmet.map(b => b.label_atom_id)
-            .filter(label_atom_id => this.getLabel(label_atom_id) === 'C')
+            .filter(label_atom_id => this.getLabel(label_atom_id).startsWith('C'))
             .filter(label_atom_id => this.toArray(bondMap.map.get(label_atom_id)!)
                 .filter(b => b.order === 2)
-                .filter(b => this.getLabel(b.label_atom_id) === 'O' || this.getLabel(b.label_atom_id) === 'S'))
+                .filter(b => this.getLabel(b.label_atom_id).startsWith('O') || this.getLabel(b.label_atom_id).startsWith('S')))
             .length === 1;
     }