Browse Source

Support/bugfixes for atom_site.pdbx_sifts_xref categories

dsehnal 3 years ago
parent
commit
8528e5a666

+ 1 - 0
CHANGELOG.md

@@ -15,6 +15,7 @@ Note that since we don't clearly distinguish between a public and private interf
 - Bugfix: Automatically treat empty string as "non-present" value in BinaryCIF writer.
 - Fix coarse model support in entity-id color theme
 - Fix marking of carbohydrate visuals (whole chain could get marked instead of single residue)
+- Support/bugfixes for ``atom_site.pdbx_sifts_xref`` categories
 
 ## [v3.0.0-dev.10] - 2022-01-17
 

+ 15 - 15
src/mol-model-props/sequence/best-database-mapping.ts

@@ -29,7 +29,7 @@ namespace BestDatabaseSequenceMapping {
         type: 'static',
         defaultParams: {},
         getParams: () => ({}),
-        isApplicable: (data: Model) => MmcifFormat.is(data.sourceData) && data.sourceData.data.frame.categories?.atom_site?.fieldNames.indexOf('db_name') >= 0,
+        isApplicable: (data: Model) => MmcifFormat.is(data.sourceData) && data.sourceData.data.frame.categories?.atom_site?.fieldNames.indexOf('pdbx_sifts_xref_db_name') >= 0,
         obtain: async (ctx, data) => {
             return { value: fromCif(data) };
         }
@@ -40,7 +40,7 @@ namespace BestDatabaseSequenceMapping {
         const data = Provider.get(model).value;
         if (!data) return '';
         const eI = loc.unit.elements[loc.element];
-        const rI = model.atomicHierarchy.residueAtomSegments.offsets[eI];
+        const rI = model.atomicHierarchy.residueAtomSegments.index[eI];
         return data.accession[rI];
     }
 
@@ -49,7 +49,7 @@ namespace BestDatabaseSequenceMapping {
         const data = Provider.get(model).value;
         if (!data) return;
         const eI = loc.unit.elements[loc.element];
-        const rI = model.atomicHierarchy.residueAtomSegments.offsets[eI];
+        const rI = model.atomicHierarchy.residueAtomSegments.index[eI];
         const dbName = data.dbName[rI];
         if (!dbName) return;
         return `${dbName} ${data.accession[rI]} ${data.num[rI]} ${data.residue[rI]}`;
@@ -59,10 +59,10 @@ namespace BestDatabaseSequenceMapping {
         if (!MmcifFormat.is(model.sourceData)) return;
 
         const { atom_site } = model.sourceData.data.frame.categories;
-        const db_name = atom_site.getField('db_name');
-        const db_acc = atom_site.getField('db_acc');
-        const db_num = atom_site.getField('db_num');
-        const db_res = atom_site.getField('db_res');
+        const db_name = atom_site.getField('pdbx_sifts_xref_db_name');
+        const db_acc = atom_site.getField('pdbx_sifts_xref_db_acc');
+        const db_num = atom_site.getField('pdbx_sifts_xref_db_num');
+        const db_res = atom_site.getField('pdbx_sifts_xref_db_res');
 
         if (!db_name || !db_acc || !db_num || !db_res) return;
 
@@ -77,17 +77,17 @@ namespace BestDatabaseSequenceMapping {
             const row = atomSourceIndex.value(residueOffsets[i]);
 
             if (db_name.valueKind(row) !== Column.ValueKind.Present) {
-                dbName[row] = '';
-                accession[row] = '';
-                num[row] = 0;
-                residue[row] = '';
+                dbName[i] = '';
+                accession[i] = '';
+                num[i] = 0;
+                residue[i] = '';
                 continue;
             }
 
-            dbName[row] = db_name.str(row);
-            accession[row] = db_acc.str(row);
-            num[row] = db_num.int(row);
-            residue[row] = db_res.str(row);
+            dbName[i] = db_name.str(row);
+            accession[i] = db_acc.str(row);
+            num[i] = db_num.int(row);
+            residue[i] = db_res.str(row);
         }
 
         return { dbName, accession, num, residue };

+ 12 - 5
src/mol-model/structure/structure/util/superposition-db-mapping.ts

@@ -5,6 +5,7 @@
  */
 
 import { Segmentation } from '../../../../mol-data/int';
+import { Mat4 } from '../../../../mol-math/linear-algebra';
 import { MinimizeRmsd } from '../../../../mol-math/linear-algebra/3d/minimize-rmsd';
 import { BestDatabaseSequenceMapping } from '../../../../mol-model-props/sequence/best-database-mapping';
 import { ElementIndex } from '../../model/indexing';
@@ -33,6 +34,7 @@ export function alignAndSuperposeWithBestDatabaseMapping(structures: Structure[]
     for (const p of pairs) {
         const [a, b] = getPositionTables(index, p.i, p.j, p.count);
         const transform = MinimizeRmsd.compute({ a, b });
+        console.log(Mat4.makeTable(transform.bTransform), transform.rmsd);
         ret.push({ transform, pivot: p.i, other: p.j });
     }
 
@@ -51,6 +53,8 @@ function getPositionTables(index: IndexEntry[], pivot: number, other: number, N:
 
         const l = Math.min(a[2] - a[1], b[2] - b[1]);
 
+        // TODO: allow to use just backbone atoms?
+        // TODO: check if residue types match?
         for (let i = 0; i < l; i++) {
             let eI = (a[1] + i) as ElementIndex;
             xs.x[o] = a[0].conformation.x(eI);
@@ -122,7 +126,6 @@ function buildIndex(structure: Structure, index: Map<string, IndexEntry>, sI: nu
         if (unit.kind !== Unit.Kind.Atomic) continue;
 
         const { elements, model } = unit;
-        const { offsets: residueOffset } = model.atomicHierarchy.residueAtomSegments;
 
         const map = BestDatabaseSequenceMapping.Provider.get(model).value;
         if (!map) return;
@@ -137,23 +140,27 @@ function buildIndex(structure: Structure, index: Map<string, IndexEntry>, sI: nu
             residuesIt.setSegment(chainSegment);
             while (residuesIt.hasNext) {
                 const residueSegment = residuesIt.move();
-                const eI = elements[residueSegment.start];
-                const rI = residueOffset[eI];
+                const rI = residueSegment.index;
 
                 if (!dbName[rI]) continue;
 
+                const start = elements[residueSegment.start];
+                const end = elements[residueSegment.end - 1] + 1 as ElementIndex;
+
                 const key = `${dbName[rI]}-${accession[rI]}-${num[rI]}`;
 
                 if (!index.has(key)) {
-                    index.set(key, { key, pivots: { [sI]: [unit, eI, elements[residueSegment.end]] } });
+                    index.set(key, { key, pivots: { [sI]: [unit, start, end] } });
                 } else {
                     const entry = index.get(key)!;
 
                     if (!entry.pivots[sI]) {
-                        entry.pivots[sI] = [unit, eI, elements[residueSegment.end]];
+                        entry.pivots[sI] = [unit, start, end];
                     }
                 }
             }
         }
     }
+
+    console.log(index);
 }

+ 2 - 2
src/mol-plugin-ui/structure/superposition.tsx

@@ -185,9 +185,9 @@ export class SuperpositionControls extends PurePluginUIComponent<{ }, Superposit
             rmsd += xform.transform.rmsd;
         }
 
-        rmsd /= transforms.length - 1;
+        rmsd /= Math.max(transforms.length - 1, 1);
 
-        this.plugin.log.info(`Superposed ${input.length} structures with avg. RMSD ${rmsd.toFixed(2)}.`);
+        this.plugin.log.info(`Superposed ${input.length} structures with avg. RMSD ${rmsd.toFixed(2)} Å.`);
         await new Promise(res => requestAnimationFrame(res));
         PluginCommands.Camera.Reset(this.plugin);
     };