Ver Fonte

Merge pull request #389 from molstar/cif-check-present

fix handling of mmcif with empty label_asym_id
Alexander Rose há 3 anos atrás
pai
commit
3466a8a024

+ 2 - 0
CHANGELOG.md

@@ -6,6 +6,8 @@ Note that since we don't clearly distinguish between a public and private interf
 
 ## [Unreleased]
 
+- Fix handling of mmcif with empty ``label_*`` fields
+
 ## [v3.3.1] - 2022-02-27
 
 - Fix issue with unit boundary reuse (do at visual level instead)

+ 3 - 1
src/extensions/cellpack/model.ts

@@ -32,6 +32,7 @@ import { Color } from '../../mol-util/color';
 import { objectForEach } from '../../mol-util/object';
 import { readFromFile } from '../../mol-util/data-source';
 import { ColorNames } from '../../mol-util/color/names';
+import { createBasic } from '../../mol-model-formats/structure/basic/schema';
 
 function getCellPackModelUrl(fileName: string, baseUrl: string) {
     return `${baseUrl}/results/${fileName}`;
@@ -310,7 +311,8 @@ async function getCurve(plugin: PluginContext, name: string, ingredient: Ingredi
     const cif = getCifCurve(name, transforms, model);
     const curveModelTask = Task.create('Curve Model', async ctx => {
         const format = MmcifFormat.fromFrame(cif);
-        const models = await createModels(format.data.db, format, ctx);
+        const basic = createBasic(format.data.db, true);
+        const models = await createModels(basic, format, ctx);
         return models.representative;
     });
 

+ 1 - 16
src/mol-model-formats/structure/basic/atomic.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -45,15 +45,6 @@ function findHierarchyOffsets(atom_site: AtomSite) {
     return { residues, chains };
 }
 
-function substUndefinedColumn<T extends Table<any>>(table: T, a: keyof T, b: keyof T) {
-    if (!(table as any)[a].isDefined) {
-        (table as any)[a] = (table as any)[b];
-    }
-    if (!(table as any)[b].isDefined) {
-        (table as any)[b] = (table as any)[a];
-    }
-}
-
 function createHierarchyData(atom_site: AtomSite, sourceIndex: Column<number>, offsets: { residues: ArrayLike<number>, chains: ArrayLike<number> }): AtomicData {
     const atoms = Table.ofColumns(AtomsSchema, {
         type_symbol: Column.ofArray({ array: Column.mapToArray(atom_site.type_symbol, ElementSymbol), schema: Column.Schema.Aliased<ElementSymbol>(Column.Schema.str) }),
@@ -87,12 +78,6 @@ function createHierarchyData(atom_site: AtomSite, sourceIndex: Column<number>, o
     Table.columnToArray(residues, 'label_seq_id', Int32Array);
     Table.columnToArray(residues, 'auth_seq_id', Int32Array);
 
-    // Fix possibly missing auth_/label_ columns
-    substUndefinedColumn(atoms, 'label_atom_id', 'auth_atom_id');
-    substUndefinedColumn(atoms, 'label_comp_id', 'auth_comp_id');
-    substUndefinedColumn(residues, 'label_seq_id', 'auth_seq_id');
-    substUndefinedColumn(chains, 'label_asym_id', 'auth_asym_id');
-
     return { atoms, residues, chains, atomSourceIndex: sourceIndex };
 }
 

+ 1 - 3
src/mol-model-formats/structure/basic/parser.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -186,8 +186,6 @@ function splitTable<T extends Table<any>>(table: T, col: Column<number>) {
     return ret;
 }
 
-
-
 async function readIntegrative(ctx: RuntimeContext, data: BasicData, properties: CommonProperties, format: ModelFormat) {
     const entities = getEntityData(data);
     // when `atom_site.ihm_model_id` is undefined fall back to `atom_site.pdbx_PDB_model_num`

+ 6 - 2
src/mol-model-formats/structure/basic/schema.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2020-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -7,6 +7,7 @@
 import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
 import { Table } from '../../../mol-data/db';
 import { mmCIF_chemComp_schema } from '../../../mol-io/reader/cif/schema/mmcif-extras';
+import { getNormalizedAtomSite } from './util';
 
 // TODO split into conformation and hierarchy parts
 
@@ -68,7 +69,7 @@ export interface BasicData {
     pdbx_molecule: Molecule
 }
 
-export function createBasic(data: Partial<BasicData>): BasicData {
+export function createBasic(data: Partial<BasicData>, normalize = false): BasicData {
     const basic = Object.create(null);
     for (const name of Object.keys(BasicSchema)) {
         if (name in data) {
@@ -77,5 +78,8 @@ export function createBasic(data: Partial<BasicData>): BasicData {
             basic[name] = Table.ofUndefinedColumns(BasicSchema[name as keyof typeof BasicSchema], 0);
         }
     }
+    if (normalize) {
+        basic.atom_site = getNormalizedAtomSite(basic.atom_site);
+    }
     return basic;
 }

+ 27 - 3
src/mol-model-formats/structure/basic/util.ts

@@ -1,12 +1,12 @@
 /**
- * Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { BasicData } from './schema';
-import { Table } from '../../../mol-data/db';
+import { AtomSite, BasicData } from './schema';
+import { Column, Table } from '../../../mol-data/db';
 
 export function getModelGroupName(model_id: number, data: BasicData) {
     const { ihm_model_group, ihm_model_group_link } = data;
@@ -17,4 +17,28 @@ export function getModelGroupName(model_id: number, data: BasicData) {
         if (group) return group.name;
     }
     return '';
+}
+
+//
+
+function hasPresentValues(column: Column<any>) {
+    for (let i = 0, il = column.rowCount; i < il; i++) {
+        if (column.valueKind(i) === Column.ValueKind.Present) return true;
+    }
+    return false;
+}
+
+function substUndefinedColumn<T extends Table<any>>(table: T, a: keyof T, b: keyof T) {
+    if (!table[a].isDefined || !hasPresentValues(table[a])) table[a] = table[b];
+    if (!table[b].isDefined || !hasPresentValues(table[b])) table[b] = table[a];
+}
+
+/** Fix possibly missing auth_/label_ columns */
+export function getNormalizedAtomSite(atom_site: AtomSite) {
+    const normalized = Table.ofColumns(atom_site._schema, atom_site);
+    substUndefinedColumn(normalized, 'label_atom_id', 'auth_atom_id');
+    substUndefinedColumn(normalized, 'label_comp_id', 'auth_comp_id');
+    substUndefinedColumn(normalized, 'label_seq_id', 'auth_seq_id');
+    substUndefinedColumn(normalized, 'label_asym_id', 'auth_asym_id');
+    return normalized;
 }

+ 2 - 2
src/mol-model-formats/structure/cif-core.ts

@@ -146,13 +146,13 @@ async function getModels(db: CifCore_Database, format: CifCoreFormat, ctx: Runti
     componentBuilder.setNames([['MOL', name || 'Unknown Molecule']]);
     componentBuilder.add('MOL', 0);
 
-    const basics = createBasic({
+    const basic = createBasic({
         entity: entityBuilder.getEntityTable(),
         chem_comp: componentBuilder.getChemCompTable(),
         atom_site
     });
 
-    const models = await createModels(basics, format, ctx);
+    const models = await createModels(basic, format, ctx);
 
     if (models.frameCount > 0) {
         const first = models.representative;

+ 2 - 2
src/mol-model-formats/structure/cube.ts

@@ -53,13 +53,13 @@ async function getModels(cube: CubeFile, ctx: RuntimeContext) {
     componentBuilder.setNames([['MOL', 'Unknown Molecule']]);
     componentBuilder.add('MOL', 0);
 
-    const basics = createBasic({
+    const basic = createBasic({
         entity: entityBuilder.getEntityTable(),
         chem_comp: componentBuilder.getChemCompTable(),
         atom_site
     });
 
-    return await createModels(basics, MolFormat.create(cube), ctx);
+    return await createModels(basic, MolFormat.create(cube), ctx);
 }
 
 //

+ 4 - 2
src/mol-model-formats/structure/mmcif.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -19,6 +19,7 @@ import { ComponentBond } from './property/bonds/chem_comp';
 import { StructConn } from './property/bonds/struct_conn';
 import { Trajectory } from '../../mol-model/structure';
 import { GlobalModelTransformInfo } from '../../mol-model/structure/model/properties/global-transform';
+import { createBasic } from './basic/schema';
 
 function modelSymmetryFromMmcif(model: Model) {
     if (!MmcifFormat.is(model.sourceData)) return;
@@ -100,5 +101,6 @@ namespace MmcifFormat {
 
 export function trajectoryFromMmCIF(frame: CifFrame): Task<Trajectory> {
     const format = MmcifFormat.fromFrame(frame);
-    return Task.create('Create mmCIF Model', ctx => createModels(format.data.db, format, ctx));
+    const basic = createBasic(format.data.db, true);
+    return Task.create('Create mmCIF Model', ctx => createModels(basic, format, ctx));
 }

+ 2 - 2
src/mol-model-formats/structure/mol.ts

@@ -68,13 +68,13 @@ export async function getMolModels(mol: MolFile, format: ModelFormat<any> | unde
     componentBuilder.setNames([['MOL', 'Unknown Molecule']]);
     componentBuilder.add('MOL', 0);
 
-    const basics = createBasic({
+    const basic = createBasic({
         entity: entityBuilder.getEntityTable(),
         chem_comp: componentBuilder.getChemCompTable(),
         atom_site
     });
 
-    const models = await createModels(basics, format ?? MolFormat.create(mol), ctx);
+    const models = await createModels(basic, format ?? MolFormat.create(mol), ctx);
 
     if (models.frameCount > 0) {
         const indexA = Column.ofIntArray(Column.mapToArray(bonds.atomIdxA, x => x - 1, Int32Array));

+ 2 - 2
src/mol-model-formats/structure/mol2.ts

@@ -75,13 +75,13 @@ async function getModels(mol2: Mol2File, ctx: RuntimeContext) {
             componentBuilder.add(atoms.subst_name.value(i), i);
         }
 
-        const basics = createBasic({
+        const basic = createBasic({
             entity: entityBuilder.getEntityTable(),
             chem_comp: componentBuilder.getChemCompTable(),
             atom_site
         });
 
-        const _models = await createModels(basics, Mol2Format.create(mol2), ctx);
+        const _models = await createModels(basic, Mol2Format.create(mol2), ctx);
 
         if (_models.frameCount > 0) {
             const indexA = Column.ofIntArray(Column.mapToArray(bonds.origin_atom_id, x => x - 1, Int32Array));

+ 4 - 2
src/mol-model-formats/structure/pdb.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -14,6 +14,7 @@ import { Column } from '../../mol-data/db';
 import { AtomPartialCharge } from './property/partial-charge';
 import { Trajectory } from '../../mol-model/structure';
 import { ModelFormat } from '../format';
+import { createBasic } from './basic/schema';
 
 export { PdbFormat };
 
@@ -34,7 +35,8 @@ export function trajectoryFromPDB(pdb: PdbFile): Task<Trajectory> {
         await ctx.update('Converting to mmCIF');
         const cif = await pdbToMmCif(pdb);
         const format = MmcifFormat.fromFrame(cif, undefined, PdbFormat.create(pdb));
-        const models = await createModels(format.data.db, format, ctx);
+        const basic = createBasic(format.data.db, true);
+        const models = await createModels(basic, format, ctx);
         const partial_charge = cif.categories['atom_site']?.getField('partial_charge');
         if (partial_charge) {
             // TODO works only for single, unsorted model, to work generally

+ 2 - 2
src/mol-model-formats/structure/xyz.ts

@@ -78,13 +78,13 @@ function getModels(mol: XyzFile, ctx: RuntimeContext) {
     componentBuilder.setNames([['MOL', 'Unknown Molecule']]);
     componentBuilder.add('MOL', 0);
 
-    const basics = createBasic({
+    const basic = createBasic({
         entity: entityBuilder.getEntityTable(),
         chem_comp: componentBuilder.getChemCompTable(),
         atom_site
     });
 
-    return createModels(basics, XyzFormat.create(mol), ctx);
+    return createModels(basic, XyzFormat.create(mol), ctx);
 }
 
 //