Explorar el Código

refactored ModelFormat

- convert to mmCIF only when needed
- added gro, psf, 3dg formats
Alexander Rose hace 5 años
padre
commit
1293848d9b
Se han modificado 30 ficheros con 334 adiciones y 319 borrados
  1. 11 9
      src/apps/viewer/extensions/cellpack/model.ts
  2. 42 38
      src/mol-model-formats/structure/3dg.ts
  3. 6 5
      src/mol-model-formats/structure/basic/parser.ts
  4. 15 0
      src/mol-model-formats/structure/basic/schema.ts
  5. 8 10
      src/mol-model-formats/structure/common/component.ts
  6. 16 14
      src/mol-model-formats/structure/common/entity.ts
  7. 1 19
      src/mol-model-formats/structure/format.ts
  8. 41 42
      src/mol-model-formats/structure/gro.ts
  9. 85 4
      src/mol-model-formats/structure/mmcif.ts
  10. 0 78
      src/mol-model-formats/structure/mmcif/parser.ts
  11. 6 5
      src/mol-model-formats/structure/pdb.ts
  12. 2 2
      src/mol-model-formats/structure/pdb/to-cif.ts
  13. 37 42
      src/mol-model-formats/structure/psf.ts
  14. 3 2
      src/mol-model-props/common/wrapper.ts
  15. 5 4
      src/mol-model-props/computed/secondary-structure.ts
  16. 3 2
      src/mol-model-props/pdbe/preferred-assembly.ts
  17. 3 2
      src/mol-model-props/pdbe/struct-ref-domain.ts
  18. 6 5
      src/mol-model-props/pdbe/structure-quality-report.ts
  19. 3 2
      src/mol-model-props/rcsb/assembly-symmetry.ts
  20. 3 2
      src/mol-model-props/rcsb/validation-report.ts
  21. 5 4
      src/mol-model/structure/export/categories/utils.ts
  22. 2 2
      src/mol-model/structure/model/model.ts
  23. 1 1
      src/mol-model/structure/structure/properties.ts
  24. 7 4
      src/mol-model/structure/topology/topology.ts
  25. 11 10
      src/mol-plugin/behavior/dynamic/volume-streaming/util.ts
  26. 3 2
      src/mol-theme/color/entity-source.ts
  27. 1 1
      src/mol-theme/color/uncertainty.ts
  28. 1 1
      src/mol-theme/size/uncertainty.ts
  29. 4 4
      src/perf-tests/lookup3d.ts
  30. 3 3
      src/perf-tests/structure.ts

+ 11 - 9
src/apps/viewer/extensions/cellpack/model.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -11,7 +11,7 @@ import { ParamDefinition as PD } from '../../../../mol-util/param-definition';
 import { Ingredient, CellPacking, Cell } from './data';
 import { getFromPdb, getFromCellPackDB } from './util';
 import { Model, Structure, StructureSymmetry, StructureSelection, QueryContext, Unit } from '../../../../mol-model/structure';
-import { trajectoryFromMmCIF } from '../../../../mol-model-formats/structure/mmcif';
+import { trajectoryFromMmCIF, MmcifFormat } from '../../../../mol-model-formats/structure/mmcif';
 import { trajectoryFromPDB } from '../../../../mol-model-formats/structure/pdb';
 import { Mat4, Vec3, Quat } from '../../../../mol-math/linear-algebra';
 import { SymmetryOperator } from '../../../../mol-math/geometry';
@@ -28,11 +28,10 @@ import { compile } from '../../../../mol-script/runtime/query/compiler';
 import { UniformColorThemeProvider } from '../../../../mol-theme/color/uniform';
 import { ThemeRegistryContext } from '../../../../mol-theme/theme';
 import { ColorTheme } from '../../../../mol-theme/color';
-import { _parse_mmCif } from '../../../../mol-model-formats/structure/mmcif/parser';
-import { ModelFormat } from '../../../../mol-model-formats/structure/format';
 import { CifCategory, CifField } from '../../../../mol-io/reader/cif';
 import { mmCIF_Schema } from '../../../../mol-io/reader/cif/schema/mmcif';
 import { Column } from '../../../../mol-data/db';
+import { createModels } from '../../../../mol-model-formats/structure/basic/parser';
 
 function getCellPackModelUrl(fileName: string, baseUrl: string) {
     return `${baseUrl}/results/${fileName}`
@@ -124,7 +123,10 @@ function getAssembly(transforms: Mat4[], structure: Structure) {
 }
 
 function getCifCurve(name: string, transforms: Mat4[], model: Model) {
-    const d = model.sourceData.data.atom_site
+    if (!MmcifFormat.is(model.sourceData)) throw new Error('mmcif source data needed')
+
+    const { db } = model.sourceData.data
+    const d = db.atom_site
     const n = d._rowCount
     const rowCount = n * transforms.length
 
@@ -201,8 +203,8 @@ function getCifCurve(name: string, transforms: Mat4[], model: Model) {
     }
 
     const categories = {
-        entity: CifCategory.ofTable('entity', model.sourceData.data.entity),
-        chem_comp: CifCategory.ofTable('chem_comp', model.sourceData.data.chem_comp),
+        entity: CifCategory.ofTable('entity', db.entity),
+        chem_comp: CifCategory.ofTable('chem_comp', db.chem_comp),
         atom_site: CifCategory.ofFields('atom_site', _atom_site)
     }
 
@@ -217,8 +219,8 @@ async function getCurve(name: string, transforms: Mat4[], model: Model) {
     const cif = getCifCurve(name, transforms, model)
 
     const curveModelTask = Task.create('Curve Model', async ctx => {
-        const format = ModelFormat.mmCIF(cif)
-        const models = await _parse_mmCif(format, ctx)
+        const format = MmcifFormat.fromFrame(cif)
+        const models = await createModels(format.data.db, format, ctx)
         return models[0]
     })
 

+ 42 - 38
src/mol-model-formats/structure/3dg.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -7,16 +7,15 @@
 import { Model } from '../../mol-model/structure/model';
 import { Task } from '../../mol-task';
 import { ModelFormat } from './format';
-import { _parse_mmCif } from './mmcif/parser';
-import { CifCategory, CifField } from '../../mol-io/reader/cif';
-import { Column } from '../../mol-data/db';
-import { mmCIF_Schema } from '../../mol-io/reader/cif/schema/mmcif';
+import { Column, Table } from '../../mol-data/db';
 import { EntityBuilder } from './common/entity';
 import { File3DG } from '../../mol-io/reader/3dg/parser';
 import { fillSerial } from '../../mol-util/array';
 import { MoleculeType } from '../../mol-model/structure/model/types';
+import { BasicSchema, createBasic } from './basic/schema';
+import { createModels } from './basic/parser';
 
-function getCategories(table: File3DG['table']) {
+function getBasic(table: File3DG['table']) {
     const entityIds = new Array<string>(table._rowCount)
     const entityBuilder = new EntityBuilder()
 
@@ -33,47 +32,52 @@ function getCategories(table: File3DG['table']) {
         seqIdEnds[i] = seqIdStarts[i] + stride - 1
     }
 
-    const ihm_sphere_obj_site: CifCategory.SomeFields<mmCIF_Schema['ihm_sphere_obj_site']> = {
-        id: CifField.ofNumbers(fillSerial(new Uint32Array(table._rowCount))),
-        entity_id: CifField.ofStrings(entityIds),
-        seq_id_begin: CifField.ofNumbers(seqIdStarts),
-        seq_id_end: CifField.ofNumbers(seqIdEnds),
-        asym_id: CifField.ofColumn(table.chromosome),
+    const ihm_sphere_obj_site = Table.ofPartialColumns(BasicSchema.ihm_sphere_obj_site, {
+        id: Column.ofIntArray(fillSerial(new Uint32Array(table._rowCount))),
+        entity_id: Column.ofStringArray(entityIds),
+        seq_id_begin: Column.ofIntArray(seqIdStarts),
+        seq_id_end: Column.ofIntArray(seqIdEnds),
+        asym_id: table.chromosome,
 
-        Cartn_x: CifField.ofNumbers(Column.mapToArray(table.x, x => x * 10, Float32Array)),
-        Cartn_y: CifField.ofNumbers(Column.mapToArray(table.y, y => y * 10, Float32Array)),
-        Cartn_z: CifField.ofNumbers(Column.mapToArray(table.z, z => z * 10, Float32Array)),
+        Cartn_x: Column.ofFloatArray(Column.mapToArray(table.x, x => x * 10, Float32Array)),
+        Cartn_y: Column.ofFloatArray(Column.mapToArray(table.y, y => y * 10, Float32Array)),
+        Cartn_z: Column.ofFloatArray(Column.mapToArray(table.z, z => z * 10, Float32Array)),
 
-        object_radius: CifField.ofColumn(Column.ofConst(objectRadius, table._rowCount, Column.Schema.float)),
-        rmsf: CifField.ofColumn(Column.ofConst(0, table._rowCount, Column.Schema.float)),
-        model_id: CifField.ofColumn(Column.ofConst(1, table._rowCount, Column.Schema.int)),
-    }
+        object_radius: Column.ofConst(objectRadius, table._rowCount, Column.Schema.float),
+        rmsf: Column.ofConst(0, table._rowCount, Column.Schema.float),
+        model_id: Column.ofConst(1, table._rowCount, Column.Schema.int),
+    }, table._rowCount)
 
-    return {
-        entity: entityBuilder.getEntityCategory(),
-        ihm_model_list: CifCategory.ofFields('ihm_model_list', {
-            model_id: CifField.ofNumbers([1]),
-            model_name: CifField.ofStrings(['3DG Model']),
-        }),
-        ihm_sphere_obj_site: CifCategory.ofFields('ihm_sphere_obj_site', ihm_sphere_obj_site)
-    }
+    return createBasic({
+        entity: entityBuilder.getEntityTable(),
+        ihm_model_list: Table.ofPartialColumns(BasicSchema.ihm_model_list, {
+            model_id: Column.ofIntArray([1]),
+            model_name: Column.ofStringArray(['3DG Model']),
+        }, 1),
+        ihm_sphere_obj_site
+    })
 }
 
-async function mmCifFrom3dg(file3dg: File3DG) {
-    const categories = getCategories(file3dg.table)
+//
+
+export { Format3dg }
 
-    return {
-        header: '3DG',
-        categoryNames: Object.keys(categories),
-        categories
-    };
+type Format3dg = ModelFormat<File3DG>
+
+namespace Format3dg {
+    export function is(x: ModelFormat): x is Format3dg {
+        return x.kind === '3dg'
+    }
+
+    export function from3dg(file3dg: File3DG): Format3dg {
+        return { kind: '3dg', name: '3DG', data: file3dg };
+    }
 }
 
 export function trajectoryFrom3DG(file3dg: File3DG): Task<Model.Trajectory> {
     return Task.create('Parse 3DG', async ctx => {
-        await ctx.update('Converting to mmCIF');
-        const cif = await mmCifFrom3dg(file3dg);
-        const format = ModelFormat.mmCIF(cif);
-        return _parse_mmCif(format, ctx);
+        const format = Format3dg.from3dg(file3dg);
+        const basic = getBasic(file3dg.table)
+        return createModels(basic, format, ctx);
     })
 }

+ 6 - 5
src/mol-model-formats/structure/basic/parser.ts

@@ -22,10 +22,11 @@ import { getProperties } from './properties';
 import { getEntities } from './entities';
 import { getModelGroupName } from './util';
 
-export async function _parse_basic(data: BasicData, format: ModelFormat, ctx: RuntimeContext) {
+export async function createModels(data: BasicData, format: ModelFormat, ctx: RuntimeContext) {
     const properties = getProperties(data)
-    const isIHM = data.ihm_model_list._rowCount > 0;
-    return isIHM ? await readIntegrative(ctx, data, properties, format) : await readStandard(ctx, data, properties, format);
+    return data.ihm_model_list._rowCount > 0
+        ? await readIntegrative(ctx, data, properties, format)
+        : await readStandard(ctx, data, properties, format);
 }
 
 /** Standard atomic model */
@@ -87,8 +88,8 @@ function createIntegrativeModel(data: BasicData, ihm: CoarseData, properties: Mo
 
     const label: string[] = []
     if (entry) label.push(entry)
-    if (format.data.struct.title.valueKind(0) === Column.ValueKind.Present) label.push(format.data.struct.title.value(0))
-    if (ihm.model_group_name) label.push(ihm.model_name)
+    if (data.struct.title.valueKind(0) === Column.ValueKind.Present) label.push(data.struct.title.value(0))
+    if (ihm.model_name) label.push(ihm.model_name)
     if (ihm.model_group_name) label.push(ihm.model_group_name)
 
     return {

+ 15 - 0
src/mol-model-formats/structure/basic/schema.ts

@@ -7,6 +7,9 @@
 import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
 import { Table } from '../../../mol-data/db';
 
+// TODO split into conformation and hierarchy parts
+// TODO extract `pdbx_struct_mod_residue` as property?
+
 export type Entry = Table<mmCIF_Schema['entry']>
 export type Struct = Table<mmCIF_Schema['struct']>
 export type StructAsym = Table<mmCIF_Schema['struct_asym']>
@@ -63,4 +66,16 @@ export interface BasicData {
     ihm_sphere_obj_site: IhmSphereObjSite
     ihm_gaussian_obj_site: IhmGaussianObjSite
     pdbx_unobs_or_zero_occ_residues: UnobsOrZeroOccResidues
+}
+
+export function createBasic(data: Partial<BasicData>): BasicData {
+    const basic = Object.create(null)
+    for (const name of Object.keys(BasicSchema)) {
+        if (name in data) {
+            basic[name] = data[name as keyof typeof BasicSchema]
+        } else {
+            basic[name] = Table.ofUndefinedColumns(BasicSchema[name as keyof typeof BasicSchema], 0)
+        }
+    }
+    return basic
 }

+ 8 - 10
src/mol-model-formats/structure/common/component.ts

@@ -1,15 +1,14 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { _parse_mmCif } from '../mmcif/parser';
-import { CifCategory, CifField } from '../../../mol-io/reader/cif';
 import { Table, Column } from '../../../mol-data/db';
 import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
 import { WaterNames } from '../../../mol-model/structure/model/types';
 import { SetUtils } from '../../../mol-util/set';
+import { BasicSchema } from '../basic/schema';
 
 type Component = Table.Row<Pick<mmCIF_Schema['chem_comp'], 'id' | 'name' | 'type'>>
 
@@ -137,13 +136,12 @@ export class ComponentBuilder {
         return this.get(compId)!
     }
 
-    getChemCompCategory() {
-        const chemComp: CifCategory.SomeFields<mmCIF_Schema['chem_comp']> = {
-            id: CifField.ofStrings(this.ids),
-            name: CifField.ofStrings(this.names),
-            type: CifField.ofStrings(this.types),
-        }
-        return CifCategory.ofFields('chem_comp', chemComp)
+    getChemCompTable() {
+        return Table.ofPartialColumns(BasicSchema.chem_comp, {
+            id: Column.ofStringArray(this.ids),
+            name: Column.ofStringArray(this.names),
+            type: Column.ofStringAliasArray(this.types),
+        }, this.ids.length)
     }
 
     setNames(names: [string, string][]) {

+ 16 - 14
src/mol-model-formats/structure/common/entity.ts

@@ -1,20 +1,23 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { CifCategory, CifField } from '../../../mol-io/reader/cif';
 import { MoleculeType, isPolymer } from '../../../mol-model/structure/model/types';
-import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
+import { Column, Table } from '../../../mol-data/db';
+import { BasicSchema } from '../basic/schema';
 
 export type EntityCompound = { chains: string[], description: string }
 
+// TODO add support for `branched`
+type EntityType = 'water' | 'polymer' | 'non-polymer'
+
 export class EntityBuilder {
     private count = 0
     private ids: string[] = []
-    private types: string[] = []
-    private descriptions: string[] = []
+    private types: EntityType[] = []
+    private descriptions: string[][] = []
 
     private compoundsMap = new Map<string, string>()
     private namesMap = new Map<string, string>()
@@ -22,11 +25,11 @@ export class EntityBuilder {
     private chainMap = new Map<string, string>()
     private waterId?: string
 
-    private set(type: string, description: string) {
+    private set(type: EntityType, description: string) {
         this.count += 1
         this.ids.push(`${this.count}`)
         this.types.push(type)
-        this.descriptions.push(description)
+        this.descriptions.push([description])
     }
 
     getEntityId(compId: string, moleculeType: MoleculeType, chainId: string): string {
@@ -55,13 +58,12 @@ export class EntityBuilder {
         }
     }
 
-    getEntityCategory() {
-        const entity: CifCategory.SomeFields<mmCIF_Schema['entity']> = {
-            id: CifField.ofStrings(this.ids),
-            type: CifField.ofStrings(this.types),
-            pdbx_description: CifField.ofStrings(this.descriptions),
-        }
-        return CifCategory.ofFields('entity', entity)
+    getEntityTable() {
+        return Table.ofPartialColumns(BasicSchema.entity, {
+            id: Column.ofStringArray(this.ids),
+            type: Column.ofStringAliasArray(this.types),
+            pdbx_description: Column.ofStringListArray(this.descriptions),
+        }, this.count)
     }
 
     setCompounds(compounds: EntityCompound[]) {

+ 1 - 19
src/mol-model-formats/structure/format.ts

@@ -5,22 +5,4 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { mmCIF_Database } from '../../mol-io/reader/cif/schema/mmcif';
-import { CIF, CifFrame } from '../../mol-io/reader/cif';
-
-interface Format { readonly kind: string, name: string }
-
-type ModelFormat =
-    | ModelFormat.mmCIF
-
-namespace ModelFormat {
-    export interface mmCIF extends Format {
-        readonly kind: 'mmCIF', data: mmCIF_Database, frame: CifFrame
-    }
-    export function mmCIF(frame: CifFrame, data?: mmCIF_Database): mmCIF {
-        if (!data) data = CIF.schema.mmCIF(frame)
-        return { kind: 'mmCIF', name: data._name, data, frame };
-    }
-}
-
-export { ModelFormat }
+export interface ModelFormat<T = unknown> { readonly kind: string, name: string, data: T }

+ 41 - 42
src/mol-model-formats/structure/gro.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
@@ -7,22 +7,21 @@
 import { Model } from '../../mol-model/structure/model';
 import { Task } from '../../mol-task';
 import { ModelFormat } from './format';
-import { _parse_mmCif } from './mmcif/parser';
 import { GroFile, GroAtoms } from '../../mol-io/reader/gro/schema';
-import { CifCategory, CifField } from '../../mol-io/reader/cif';
-import { Column } from '../../mol-data/db';
-import { mmCIF_Schema } from '../../mol-io/reader/cif/schema/mmcif';
+import { Column, Table } from '../../mol-data/db';
 import { guessElementSymbolString } from './util';
 import { MoleculeType, getMoleculeType } from '../../mol-model/structure/model/types';
 import { ComponentBuilder } from './common/component';
 import { getChainId } from './common/util';
 import { EntityBuilder } from './common/entity';
+import { BasicData, BasicSchema, createBasic } from './basic/schema';
+import { createModels } from './basic/parser';
 
 // TODO multi model files
 
-function getCategories(atoms: GroAtoms) {
-    const auth_atom_id = CifField.ofColumn(atoms.atomName)
-    const auth_comp_id = CifField.ofColumn(atoms.residueName)
+function getBasic(atoms: GroAtoms): BasicData {
+    const auth_atom_id = atoms.atomName
+    const auth_comp_id = atoms.residueName
 
     const entityIds = new Array<string>(atoms.count)
     const asymIds = new Array<string>(atoms.count)
@@ -69,57 +68,57 @@ function getCategories(atoms: GroAtoms) {
         ids[i] = i
     }
 
-    const auth_asym_id = CifField.ofColumn(Column.ofStringArray(asymIds))
+    const auth_asym_id = Column.ofStringArray(asymIds)
 
-    const atom_site: CifCategory.SomeFields<mmCIF_Schema['atom_site']> = {
+    const atom_site = Table.ofPartialColumns(BasicSchema.atom_site, {
         auth_asym_id,
         auth_atom_id,
         auth_comp_id,
-        auth_seq_id: CifField.ofColumn(atoms.residueNumber),
-        B_iso_or_equiv: CifField.ofColumn(Column.Undefined(atoms.count, Column.Schema.float)),
-        Cartn_x: CifField.ofNumbers(Column.mapToArray(atoms.x, x => x * 10, Float32Array)),
-        Cartn_y: CifField.ofNumbers(Column.mapToArray(atoms.y, y => y * 10, Float32Array)),
-        Cartn_z: CifField.ofNumbers(Column.mapToArray(atoms.z, z => z * 10, Float32Array)),
-        group_PDB: CifField.ofColumn(Column.Undefined(atoms.count, Column.Schema.str)),
-        id: CifField.ofColumn(Column.ofIntArray(ids)),
-
-        label_alt_id: CifField.ofColumn(Column.Undefined(atoms.count, Column.Schema.str)),
+        auth_seq_id: atoms.residueNumber,
+        Cartn_x: Column.ofFloatArray(Column.mapToArray(atoms.x, x => x * 10, Float32Array)),
+        Cartn_y: Column.ofFloatArray(Column.mapToArray(atoms.y, y => y * 10, Float32Array)),
+        Cartn_z: Column.ofFloatArray(Column.mapToArray(atoms.z, z => z * 10, Float32Array)),
+        id: Column.ofIntArray(ids),
 
         label_asym_id: auth_asym_id,
         label_atom_id: auth_atom_id,
         label_comp_id: auth_comp_id,
-        label_seq_id: CifField.ofColumn(Column.ofIntArray(seqIds)),
-        label_entity_id: CifField.ofColumn(Column.ofStringArray(entityIds)),
+        label_seq_id: Column.ofIntArray(seqIds),
+        label_entity_id: Column.ofStringArray(entityIds),
 
-        occupancy: CifField.ofColumn(Column.ofConst(1, atoms.count, Column.Schema.float)),
-        type_symbol: CifField.ofStrings(Column.mapToArray(atoms.atomName, s => guessElementSymbolString(s))),
+        occupancy: Column.ofConst(1, atoms.count, Column.Schema.float),
+        type_symbol: Column.ofStringArray(Column.mapToArray(atoms.atomName, s => guessElementSymbolString(s))),
 
-        pdbx_PDB_ins_code: CifField.ofColumn(Column.Undefined(atoms.count, Column.Schema.str)),
-        pdbx_PDB_model_num: CifField.ofColumn(Column.ofConst('1', atoms.count, Column.Schema.str)),
-    }
+        pdbx_PDB_model_num: Column.ofConst(1, atoms.count, Column.Schema.int),
+    }, atoms.count)
 
-    return {
-        entity: entityBuilder.getEntityCategory(),
-        chem_comp: componentBuilder.getChemCompCategory(),
-        atom_site: CifCategory.ofFields('atom_site', atom_site)
-    }
+    return createBasic({
+        entity: entityBuilder.getEntityTable(),
+        chem_comp: componentBuilder.getChemCompTable(),
+        atom_site
+    })
 }
 
-function groToMmCif(gro: GroFile) {
-    const categories = getCategories(gro.structures[0].atoms)
+//
+
+export { GroFormat }
 
-    return {
-        header: gro.structures[0].header.title,
-        categoryNames: Object.keys(categories),
-        categories
-    };
+type GroFormat = ModelFormat<GroFile>
+
+namespace GroFormat {
+    export function is(x: ModelFormat): x is GroFormat {
+        return x.kind === 'gro'
+    }
+
+    export function fromGro(gro: GroFile): GroFormat {
+        return { kind: 'gro', name: gro.structures[0].header.title, data: gro };
+    }
 }
 
 export function trajectoryFromGRO(gro: GroFile): Task<Model.Trajectory> {
     return Task.create('Parse GRO', async ctx => {
-        await ctx.update('Converting to mmCIF');
-        const cif = groToMmCif(gro);
-        const format = ModelFormat.mmCIF(cif);
-        return _parse_mmCif(format, ctx);
+        const format = GroFormat.fromGro(gro);
+        const basic = getBasic(gro.structures[0].atoms)
+        return createModels(basic, format, ctx);
     })
 }

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

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-2020 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>
@@ -8,9 +8,90 @@
 import { Model } from '../../mol-model/structure/model/model';
 import { Task } from '../../mol-task';
 import { ModelFormat } from './format';
-import { _parse_mmCif } from './mmcif/parser';
-import { CifFrame } from '../../mol-io/reader/cif';
+import { CifFrame, CIF } from '../../mol-io/reader/cif';
+import { mmCIF_Database, mmCIF_Schema } from '../../mol-io/reader/cif/schema/mmcif';
+import { createModels } from './basic/parser';
+import { ModelSymmetry } from './property/symmetry';
+import { ModelSecondaryStructure } from './property/secondary-structure';
+import { Table } from '../../mol-data/db';
+import { AtomSiteAnisotrop } from './property/anisotropic';
+import { ComponentBond } from './property/bonds/comp';
+import { StructConn } from './property/bonds/struct_conn';
+import { ModelCrossLinkRestraint } from './property/pair-restraints/cross-links';
+
+function modelSymmetryFromMmcif(model: Model) {
+    if (!MmcifFormat.is(model.sourceData)) return;
+    return ModelSymmetry.fromData(model.sourceData.data.db)
+}
+ModelSymmetry.Provider.formatRegistry.add('mmCIF', modelSymmetryFromMmcif)
+
+function secondaryStructureFromMmcif(model: Model) {
+    if (!MmcifFormat.is(model.sourceData)) return;
+    const { struct_conf, struct_sheet_range } = model.sourceData.data.db
+    return ModelSecondaryStructure.fromStruct(struct_conf, struct_sheet_range, model.atomicHierarchy)
+}
+ModelSecondaryStructure.Provider.formatRegistry.add('mmCIF', secondaryStructureFromMmcif)
+
+function atomSiteAnisotropFromMmcif(model: Model) {
+    if (!MmcifFormat.is(model.sourceData)) return;
+    const { atom_site_anisotrop } = model.sourceData.data.db
+    const data = Table.ofColumns(mmCIF_Schema['atom_site_anisotrop'], atom_site_anisotrop);
+    const elementToAnsiotrop = AtomSiteAnisotrop.getElementToAnsiotrop(model, data)
+    return { data, elementToAnsiotrop }
+}
+AtomSiteAnisotrop.Provider.formatRegistry.add('mmCIF', atomSiteAnisotropFromMmcif)
+
+function componentBondFromMmcif(model: Model) {
+    if (!MmcifFormat.is(model.sourceData)) return;
+    const { chem_comp_bond } = model.sourceData.data.db;
+    if (chem_comp_bond._rowCount === 0) return;
+    return {
+        data: chem_comp_bond,
+        entries: ComponentBond.getEntriesFromChemCompBond(chem_comp_bond)
+    }
+}
+ComponentBond.Provider.formatRegistry.add('mmCIF', componentBondFromMmcif)
+
+function structConnFromMmcif(model: Model) {
+    if (!MmcifFormat.is(model.sourceData)) return;
+    const { struct_conn } = model.sourceData.data.db;
+    if (struct_conn._rowCount === 0) return;
+    const entries = StructConn.getEntriesFromStructConn(struct_conn, model)
+    return {
+        data: struct_conn,
+        byAtomIndex: StructConn.getAtomIndexFromEntries(entries),
+        entries,
+    }
+}
+StructConn.Provider.formatRegistry.add('mmCIF', structConnFromMmcif)
+
+function crossLinkRestraintFromMmcif(model: Model) {
+    if (!MmcifFormat.is(model.sourceData)) return;
+    const { ihm_cross_link_restraint } = model.sourceData.data.db;
+    if (ihm_cross_link_restraint._rowCount === 0) return;
+    return ModelCrossLinkRestraint.fromTable(ihm_cross_link_restraint, model)
+}
+ModelCrossLinkRestraint.Provider.formatRegistry.add('mmCIF', crossLinkRestraintFromMmcif)
+
+//
+
+export { MmcifFormat }
+
+type MmcifFormat = ModelFormat<MmcifFormat.Data>
+
+namespace MmcifFormat {
+    export type Data = { db: mmCIF_Database, frame: CifFrame }
+    export function is(x: ModelFormat): x is MmcifFormat {
+        return x.kind === 'mmCIF'
+    }
+
+    export function fromFrame(frame: CifFrame, db?: mmCIF_Database): MmcifFormat {
+        if (!db) db = CIF.schema.mmCIF(frame)
+        return { kind: 'mmCIF', name: db._name, data: { db, frame } };
+    }
+}
 
 export function trajectoryFromMmCIF(frame: CifFrame): Task<Model.Trajectory> {
-    return Task.create('Create mmCIF Model', ctx => _parse_mmCif(ModelFormat.mmCIF(frame), ctx));
+    const format = MmcifFormat.fromFrame(frame)
+    return Task.create('Create mmCIF Model', ctx => createModels(format.data.db, format, ctx));
 }

+ 0 - 78
src/mol-model-formats/structure/mmcif/parser.ts

@@ -1,78 +0,0 @@
-/**
- * Copyright (c) 2017-2020 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 { Table } from '../../../mol-data/db';
-import { mmCIF_Schema } from '../../../mol-io/reader/cif/schema/mmcif';
-import { RuntimeContext } from '../../../mol-task';
-import { Model } from '../../../mol-model/structure/model/model';
-import { ModelFormat } from '../format';
-import mmCIF_Format = ModelFormat.mmCIF
-import { AtomSiteAnisotrop } from '../property/anisotropic';
-import { _parse_basic } from '../basic/parser';
-import { ModelSymmetry } from '../property/symmetry';
-import { ModelSecondaryStructure } from '../property/secondary-structure';
-import { ComponentBond } from '../property/bonds/comp';
-import { StructConn } from '../property/bonds/struct_conn';
-import { ModelCrossLinkRestraint } from '../property/pair-restraints/cross-links';
-
-export async function _parse_mmCif(format: mmCIF_Format, ctx: RuntimeContext) {
-    return _parse_basic(format.data, format, ctx)
-}
-
-function modelSymmetryFromMmcif(model: Model) {
-    if (model.sourceData.kind !== 'mmCIF') return;
-    return ModelSymmetry.fromData(model.sourceData.data)
-}
-ModelSymmetry.Provider.formatRegistry.add('mmCIF', modelSymmetryFromMmcif)
-
-function secondaryStructureFromMmcif(model: Model) {
-    if (model.sourceData.kind !== 'mmCIF') return;
-    const { struct_conf, struct_sheet_range } = model.sourceData.data
-    return ModelSecondaryStructure.fromStruct(struct_conf, struct_sheet_range, model.atomicHierarchy)
-}
-ModelSecondaryStructure.Provider.formatRegistry.add('mmCIF', secondaryStructureFromMmcif)
-
-function atomSiteAnisotropFromMmcif(model: Model) {
-    if (model.sourceData.kind !== 'mmCIF') return;
-    const { atom_site_anisotrop } = model.sourceData.data
-    const data = Table.ofColumns(mmCIF_Schema['atom_site_anisotrop'], atom_site_anisotrop);
-    const elementToAnsiotrop = AtomSiteAnisotrop.getElementToAnsiotrop(model, data)
-    return { data, elementToAnsiotrop }
-}
-AtomSiteAnisotrop.Provider.formatRegistry.add('mmCIF', atomSiteAnisotropFromMmcif)
-
-function componentBondFromMmcif(model: Model) {
-    if (model.sourceData.kind !== 'mmCIF') return;
-    const { chem_comp_bond } = model.sourceData.data;
-    if (chem_comp_bond._rowCount === 0) return;
-    return {
-        data: chem_comp_bond,
-        entries: ComponentBond.getEntriesFromChemCompBond(chem_comp_bond)
-    }
-}
-ComponentBond.Provider.formatRegistry.add('mmCIF', componentBondFromMmcif)
-
-function structConnFromMmcif(model: Model) {
-    if (model.sourceData.kind !== 'mmCIF') return;
-    const { struct_conn } = model.sourceData.data;
-    if (struct_conn._rowCount === 0) return;
-    const entries = StructConn.getEntriesFromStructConn(struct_conn, model)
-    return {
-        data: struct_conn,
-        byAtomIndex: StructConn.getAtomIndexFromEntries(entries),
-        entries,
-    }
-}
-StructConn.Provider.formatRegistry.add('mmCIF', structConnFromMmcif)
-
-function crossLinkRestraintFromMmcif(model: Model) {
-    if (model.sourceData.kind !== 'mmCIF') return;
-    const { ihm_cross_link_restraint } = model.sourceData.data;
-    if (ihm_cross_link_restraint._rowCount === 0) return;
-    return ModelCrossLinkRestraint.fromTable(ihm_cross_link_restraint, model)
-}
-ModelCrossLinkRestraint.Provider.formatRegistry.add('mmCIF', crossLinkRestraintFromMmcif)

+ 6 - 5
src/mol-model-formats/structure/pdb.ts

@@ -1,21 +1,22 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 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 { PdbFile } from '../../mol-io/reader/pdb/schema';
 import { pdbToMmCif } from './pdb/to-cif';
 import { Model } from '../../mol-model/structure/model';
 import { Task } from '../../mol-task';
-import { ModelFormat } from './format';
-import { _parse_mmCif } from './mmcif/parser';
+import { MmcifFormat } from './mmcif';
+import { createModels } from './basic/parser';
 
 export function trajectoryFromPDB(pdb: PdbFile): Task<Model.Trajectory> {
     return Task.create('Parse PDB', async ctx => {
         await ctx.update('Converting to mmCIF');
         const cif = await pdbToMmCif(pdb);
-        const format = ModelFormat.mmCIF(cif);
-        return _parse_mmCif(format, ctx);
+        const format = MmcifFormat.fromFrame(cif)
+        return createModels(format.data.db, format, ctx)
     })
 }

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

@@ -162,8 +162,8 @@ export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> {
     }
 
     const categories = {
-        entity: entityBuilder.getEntityCategory(),
-        chem_comp: componentBuilder.getChemCompCategory(),
+        entity: entityBuilder.getEntityTable(),
+        chem_comp: componentBuilder.getChemCompTable(),
         atom_site: CifCategory.ofFields('atom_site', getAtomSite(atomSite)),
         atom_site_anisotrop: CifCategory.ofFields('atom_site_anisotrop', getAnisotropic(anisotropic))
     } as any;

+ 37 - 42
src/mol-model-formats/structure/psf.ts

@@ -1,26 +1,24 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { PsfFile } from '../../mol-io/reader/psf/parser';
-import { mmCIF_Schema } from '../../mol-io/reader/cif/schema/mmcif';
-import { Column } from '../../mol-data/db';
+import { Column, Table } from '../../mol-data/db';
 import { EntityBuilder } from './common/entity';
 import { ComponentBuilder } from './common/component';
-import { CifCategory, CifField } from '../../mol-io/reader/cif';
 import { guessElementSymbolString } from './util';
 import { MoleculeType, getMoleculeType } from '../../mol-model/structure/model/types';
 import { getChainId } from './common/util';
 import { Task } from '../../mol-task';
 import { ModelFormat } from './format';
 import { Topology } from '../../mol-model/structure/topology/topology';
+import { createBasic, BasicSchema } from './basic/schema';
 
-// TODO: shares most of the code with ./gro.ts#getCategories
-function getCategories(atoms: PsfFile['atoms']) {
-    const auth_atom_id = CifField.ofColumn(atoms.atomName)
-    const auth_comp_id = CifField.ofColumn(atoms.residueName)
+function getBasic(atoms: PsfFile['atoms']) {
+    const auth_atom_id = atoms.atomName
+    const auth_comp_id = atoms.residueName
 
     const entityIds = new Array<string>(atoms.count)
     const asymIds = new Array<string>(atoms.count)
@@ -62,57 +60,54 @@ function getCategories(atoms: PsfFile['atoms']) {
         ids[i] = i
     }
 
-    const auth_asym_id = CifField.ofColumn(Column.ofStringArray(asymIds))
+    const auth_asym_id = Column.ofStringArray(asymIds)
 
-    const atom_site: CifCategory.SomeFields<mmCIF_Schema['atom_site']> = {
+    const atom_site = Table.ofPartialColumns(BasicSchema.atom_site, {
         auth_asym_id,
         auth_atom_id,
         auth_comp_id,
-        auth_seq_id: CifField.ofColumn(atoms.residueId),
-        B_iso_or_equiv: CifField.ofColumn(Column.Undefined(atoms.count, Column.Schema.float)),
-        Cartn_x: CifField.ofColumn(Column.Undefined(atoms.count, Column.Schema.float)),
-        Cartn_y: CifField.ofColumn(Column.Undefined(atoms.count, Column.Schema.float)),
-        Cartn_z: CifField.ofColumn(Column.Undefined(atoms.count, Column.Schema.float)),
-        group_PDB: CifField.ofColumn(Column.Undefined(atoms.count, Column.Schema.str)),
-        id: CifField.ofColumn(Column.ofIntArray(ids)),
-
-        label_alt_id: CifField.ofColumn(Column.Undefined(atoms.count, Column.Schema.str)),
+        auth_seq_id: atoms.residueId,
+        id: Column.ofIntArray(ids),
 
         label_asym_id: auth_asym_id,
         label_atom_id: auth_atom_id,
         label_comp_id: auth_comp_id,
-        label_seq_id: CifField.ofColumn(Column.ofIntArray(seqIds)),
-        label_entity_id: CifField.ofColumn(Column.ofStringArray(entityIds)),
+        label_seq_id: Column.ofIntArray(seqIds),
+        label_entity_id: Column.ofStringArray(entityIds),
 
-        occupancy: CifField.ofColumn(Column.ofConst(1, atoms.count, Column.Schema.float)),
-        type_symbol: CifField.ofStrings(Column.mapToArray(atoms.atomName, s => guessElementSymbolString(s))),
+        occupancy: Column.ofConst(1, atoms.count, Column.Schema.float),
+        type_symbol: Column.ofStringArray(Column.mapToArray(atoms.atomName, s => guessElementSymbolString(s))),
 
-        pdbx_PDB_ins_code: CifField.ofColumn(Column.Undefined(atoms.count, Column.Schema.str)),
-        pdbx_PDB_model_num: CifField.ofColumn(Column.ofConst('1', atoms.count, Column.Schema.str)),
-    }
+        pdbx_PDB_model_num: Column.ofConst(1, atoms.count, Column.Schema.int),
+    }, atoms.count)
 
-    return {
-        entity: entityBuilder.getEntityCategory(),
-        chem_comp: componentBuilder.getChemCompCategory(),
-        atom_site: CifCategory.ofFields('atom_site', atom_site)
-    }
+    return createBasic({
+        entity: entityBuilder.getEntityTable(),
+        chem_comp: componentBuilder.getChemCompTable(),
+        atom_site
+    })
 }
 
-function psfToMmCif(psf: PsfFile) {
-    const categories = getCategories(psf.atoms)
+//
+
+export { PsfFormat }
 
-    return {
-        header: psf.id,
-        categoryNames: Object.keys(categories),
-        categories
-    };
+type PsfFormat = ModelFormat<PsfFile>
+
+namespace PsfFormat {
+    export function is(x: ModelFormat): x is PsfFormat {
+        return x.kind === 'psf'
+    }
+
+    export function fromPsf(psf: PsfFile): PsfFormat {
+        return { kind: 'psf', name: psf.id, data: psf };
+    }
 }
 
 export function topologyFromPsf(psf: PsfFile): Task<Topology> {
     return Task.create('Parse PSF', async ctx => {
-        const label = psf.id
-        const cif = psfToMmCif(psf);
-        const format = ModelFormat.mmCIF(cif);
+        const format = PsfFormat.fromPsf(psf);
+        const basic = getBasic(psf.atoms)
 
         const { atomIdA, atomIdB } = psf.bonds
 
@@ -130,6 +125,6 @@ export function topologyFromPsf(psf: PsfFile): Task<Topology> {
             order: Column.ofConst(1, psf.bonds.count, Column.Schema.int)
         }
 
-        return Topology.create(label, format, bonds)
+        return Topology.create(psf.id, basic, bonds, format)
     })
 }

+ 3 - 2
src/mol-model-props/common/wrapper.ts

@@ -7,6 +7,7 @@
 import { CifWriter } from '../../mol-io/writer/cif';
 import { Model } from '../../mol-model/structure';
 import { dateToUtcString } from '../../mol-util/date';
+import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
 
 interface PropertyWrapper<Data> {
     info: PropertyWrapper.Info,
@@ -40,11 +41,11 @@ namespace PropertyWrapper {
     ];
 
     export function tryGetInfoFromCif(categoryName: string, model: Model): Info | undefined {
-        if (model.sourceData.kind !== 'mmCIF' || !model.sourceData.frame.categoryNames.includes(categoryName)) {
+        if (!MmcifFormat.is(model.sourceData) || !model.sourceData.data.frame.categoryNames.includes(categoryName)) {
             return;
         }
 
-        const timestampField = model.sourceData.frame.categories[categoryName].getField('updated_datetime_utc');
+        const timestampField = model.sourceData.data.frame.categories[categoryName].getField('updated_datetime_utc');
         if (!timestampField || timestampField.rowCount === 0) return;
 
         return { timestamp_utc: timestampField.str(0) || dateToUtcString(new Date()) };

+ 5 - 4
src/mol-model-props/computed/secondary-structure.ts

@@ -12,6 +12,7 @@ import { Unit } from '../../mol-model/structure/structure';
 import { CustomStructureProperty } from '../common/custom-structure-property';
 import { CustomProperty } from '../common/custom-property';
 import { ModelSecondaryStructure } from '../../mol-model-formats/structure/property/secondary-structure';
+import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
 
 function getSecondaryStructureParams(data?: Structure) {
     let defaultType = 'mmcif' as 'mmcif' | 'dssp'
@@ -19,10 +20,10 @@ function getSecondaryStructureParams(data?: Structure) {
         defaultType = 'dssp'
         for (let i = 0, il = data.models.length; i < il; ++i) {
             const m = data.models[i]
-            if (m.sourceData.kind === 'mmCIF') {
-                if (data.model.sourceData.data.struct_conf.id.isDefined ||
-                    data.model.sourceData.data.struct_sheet_range.id.isDefined ||
-                    data.model.sourceData.data.database_2.database_id.isDefined
+            if (MmcifFormat.is(m.sourceData)) {
+                if (m.sourceData.data.db.struct_conf.id.isDefined ||
+                    m.sourceData.data.db.struct_sheet_range.id.isDefined ||
+                    m.sourceData.data.db.database_2.database_id.isDefined
                 ) {
                     // if there is any secondary structure definition given or if there is
                     // an archival model, don't calculate dssp by default

+ 3 - 2
src/mol-model-props/pdbe/preferred-assembly.ts

@@ -9,6 +9,7 @@ import { toTable } from '../../mol-io/reader/cif/schema';
 import { CifWriter } from '../../mol-io/writer/cif';
 import { Model, CustomPropertyDescriptor } from '../../mol-model/structure';
 import { ModelSymmetry } from '../../mol-model-formats/structure/property/symmetry';
+import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
 
 export namespace PDBePreferredAssembly {
     export type Property = string
@@ -47,8 +48,8 @@ export namespace PDBePreferredAssembly {
     });
 
     function fromCifData(model: Model): string | undefined {
-        if (model.sourceData.kind !== 'mmCIF') return void 0;
-        const cat = model.sourceData.frame.categories.pdbe_preferred_assembly;
+        if (!MmcifFormat.is(model.sourceData)) return void 0;
+        const cat = model.sourceData.data.frame.categories.pdbe_preferred_assembly;
         if (!cat) return void 0;
         return toTable(Schema.pdbe_preferred_assembly, cat).assembly_id.value(0) || getFirstFromModel(model);
     }

+ 3 - 2
src/mol-model-props/pdbe/struct-ref-domain.ts

@@ -9,6 +9,7 @@ import { toTable } from '../../mol-io/reader/cif/schema';
 import { CifWriter } from '../../mol-io/writer/cif';
 import { Model, CustomPropertyDescriptor } from '../../mol-model/structure';
 import { PropertyWrapper } from '../common/wrapper';
+import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
 
 export namespace PDBeStructRefDomain {
     export type Property = PropertyWrapper<Table<Schema['pdbe_struct_ref_domain']> | undefined>
@@ -57,8 +58,8 @@ export namespace PDBeStructRefDomain {
     });
 
     function fromCifData(model: Model): Property['data'] {
-        if (model.sourceData.kind !== 'mmCIF') return void 0;
-        const cat = model.sourceData.frame.categories.pdbe_struct_ref_domain;
+        if (!MmcifFormat.is(model.sourceData)) return void 0;
+        const cat = model.sourceData.data.frame.categories.pdbe_struct_ref_domain;
         if (!cat) return void 0;
         return toTable(Schema.pdbe_struct_ref_domain, cat);
     }

+ 6 - 5
src/mol-model-props/pdbe/structure-quality-report.ts

@@ -20,6 +20,7 @@ import { CustomModelProperty } from '../common/custom-model-property';
 import { ParamDefinition as PD } from '../../mol-util/param-definition'
 import { CustomProperty } from '../common/custom-property';
 import { arraySetAdd } from '../../mol-util/array';
+import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
 
 export { StructureQualityReport }
 
@@ -37,8 +38,8 @@ namespace StructureQualityReport {
     export function isApplicable(model?: Model): boolean {
         return (
             !!model &&
-            model.sourceData.kind === 'mmCIF' &&
-            (model.sourceData.data.database_2.database_id.isDefined ||
+            MmcifFormat.is(model.sourceData) &&
+            (model.sourceData.data.db.database_2.database_id.isDefined ||
                 model.entryId.length === 4)
         )
     }
@@ -103,10 +104,10 @@ namespace StructureQualityReport {
     }
 
     function getCifData(model: Model) {
-        if (model.sourceData.kind !== 'mmCIF') throw new Error('Data format must be mmCIF.');
+        if (!MmcifFormat.is(model.sourceData)) throw new Error('Data format must be mmCIF.');
         return {
-            residues: toTable(Schema.pdbe_structure_quality_report_issues, model.sourceData.frame.categories.pdbe_structure_quality_report_issues),
-            groups: toTable(Schema.pdbe_structure_quality_report_issue_types, model.sourceData.frame.categories.pdbe_structure_quality_report_issue_types),
+            residues: toTable(Schema.pdbe_structure_quality_report_issues, model.sourceData.data.frame.categories.pdbe_structure_quality_report_issues),
+            groups: toTable(Schema.pdbe_structure_quality_report_issue_types, model.sourceData.data.frame.categories.pdbe_structure_quality_report_issue_types),
         }
     }
 }

+ 3 - 2
src/mol-model-props/rcsb/assembly-symmetry.ts

@@ -14,6 +14,7 @@ import { GraphQLClient } from '../../mol-util/graphql-client';
 import { CustomProperty } from '../common/custom-property';
 import { NonNullableArray } from '../../mol-util/type-helpers';
 import { CustomStructureProperty } from '../common/custom-structure-property';
+import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
 
 const BiologicalAssemblyNames = new Set([
     'author_and_software_defined_assembly',
@@ -29,11 +30,11 @@ export namespace AssemblySymmetry {
 
     export function isApplicable(structure?: Structure): boolean {
         // check if structure is from pdb entry
-        if (!structure || structure.models.length !== 1 || structure.models[0].sourceData.kind !== 'mmCIF' || (!structure.models[0].sourceData.data.database_2.database_id.isDefined &&
+        if (!structure || structure.models.length !== 1 || !MmcifFormat.is(structure.models[0].sourceData) || (!structure.models[0].sourceData.data.db.database_2.database_id.isDefined &&
         structure.models[0].entryId.length !== 4)) return false
 
         // check if assembly is 'biological'
-        const mmcif = structure.models[0].sourceData.data
+        const mmcif = structure.models[0].sourceData.data.db
         if (!mmcif.pdbx_struct_assembly.details.isDefined) return false
         const id = structure.units[0].conformation.operator.assembly.id
         const indices = Column.indicesOf(mmcif.pdbx_struct_assembly.id, e => e === id)

+ 3 - 2
src/mol-model-props/rcsb/validation-report.ts

@@ -18,6 +18,7 @@ import { IntMap, SortedArray } from '../../mol-data/int';
 import { arrayMax } from '../../mol-util/array';
 import { equalEps } from '../../mol-math/linear-algebra/3d/common';
 import { Vec3 } from '../../mol-math/linear-algebra';
+import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
 
 export { ValidationReport }
 
@@ -90,8 +91,8 @@ namespace ValidationReport {
     export function isApplicable(model?: Model): boolean {
         return (
             !!model &&
-            model.sourceData.kind === 'mmCIF' &&
-            (model.sourceData.data.database_2.database_id.isDefined ||
+            MmcifFormat.is(model.sourceData) &&
+            (model.sourceData.data.db.database_2.database_id.isDefined ||
                 model.entryId.length === 4)
         )
     }

+ 5 - 4
src/mol-model/structure/export/categories/utils.ts

@@ -13,10 +13,11 @@ import { UniqueArray } from '../../../../mol-data/generic';
 import { sortArray } from '../../../../mol-data/util';
 import { CifWriter } from '../../../../mol-io/writer/cif';
 import { CifExportContext } from '../mmcif';
+import { MmcifFormat } from '../../../../mol-model-formats/structure/mmcif';
 
 export function getModelMmCifCategory<K extends keyof mmCIF_Schema>(model: Model, name: K): mmCIF_Database[K] | undefined {
-    if (model.sourceData.kind !== 'mmCIF') return;
-    return model.sourceData.data[name];
+    if (!MmcifFormat.is(model.sourceData)) return;
+    return model.sourceData.data.db[name];
 }
 
 export function getUniqueResidueNamesFromStructures(structures: Structure[]) {
@@ -50,9 +51,9 @@ export function copy_mmCif_category(name: keyof mmCIF_Schema, condition?: (struc
             if (condition && !condition(structures[0])) return CifWriter.Category.Empty;
 
             const model = structures[0].model;
-            if (model.sourceData.kind !== 'mmCIF') return CifWriter.Category.Empty;
+            if (!MmcifFormat.is(model.sourceData)) return CifWriter.Category.Empty;
 
-            const table = model.sourceData.data[name];
+            const table = model.sourceData.data.db[name];
             if (!table || !table._rowCount) return CifWriter.Category.Empty;
             return CifWriter.Category.ofTable(table);
         }

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

@@ -18,9 +18,9 @@ import { Vec3 } from '../../../mol-math/linear-algebra';
 import { Mutable } from '../../../mol-util/type-helpers';
 import { Coordinates } from '../coordinates';
 import { Topology } from '../topology';
-import { _parse_mmCif } from '../../../mol-model-formats/structure/mmcif/parser';
 import { Task } from '../../../mol-task';
 import { IndexPairBonds } from '../../../mol-model-formats/structure/property/bonds/index-pair';
+import { createModels } from '../../../mol-model-formats/structure/basic/parser';
 
 /**
  * Interface to the "source data" of the molecule.
@@ -107,7 +107,7 @@ export namespace Model {
 
     export function trajectoryFromTopologyAndCoordinates(topology: Topology, coordinates: Coordinates): Task<Trajectory> {
         return Task.create('Create Trajectory', async ctx => {
-            const model = (await _parse_mmCif(topology.format, ctx))[0];
+            const model = (await createModels(topology.basic, topology.sourceData, ctx))[0];
             if (!model) throw new Error('found no model')
             const trajectory = trajectoryFromModelAndCoordinates(model, coordinates)
             const bondData = { pairs: topology.bonds, count: model.atomicHierarchy.atoms._rowCount }

+ 1 - 1
src/mol-model/structure/structure/properties.ts

@@ -62,7 +62,7 @@ function _compId(l: StructureElement.Location) {
 function compId(l: StructureElement.Location) {
     if (!Unit.isAtomic(l.unit)) notAtomic()
     if (!hasMicroheterogeneity(l)) return _compId(l)
-    return l.unit.model.sourceData.data.atom_site.label_comp_id.value(l.element)
+    return l.unit.model.atomicHierarchy.residues.label_comp_id.value(l.unit.residueIndex[l.element])
 }
 
 function seqId(l: StructureElement.Location) {

+ 7 - 4
src/mol-model/structure/topology/topology.ts

@@ -1,11 +1,12 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
 import { UUID } from '../../../mol-util';
 import { Column } from '../../../mol-data/db';
+import { BasicData } from '../../../mol-model-formats/structure/basic/schema';
 import { ModelFormat } from '../../../mol-model-formats/structure/format';
 
 export { Topology }
@@ -14,7 +15,8 @@ interface Topology {
     readonly id: UUID
     readonly label: string
 
-    readonly format: ModelFormat
+    readonly basic: BasicData
+    readonly sourceData: ModelFormat
 
     readonly bonds: {
         readonly indexA: Column<number>,
@@ -47,11 +49,12 @@ interface Topology {
 }
 
 namespace Topology {
-    export function create(label: string, format: ModelFormat, bonds: Topology['bonds']): Topology {
+    export function create(label: string, basic: BasicData, bonds: Topology['bonds'], format: ModelFormat): Topology {
         return {
             id: UUID.create22(),
             label,
-            format,
+            basic,
+            sourceData: format,
             bonds
         }
     }

+ 11 - 10
src/mol-plugin/behavior/dynamic/volume-streaming/util.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2020 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>
@@ -9,30 +9,31 @@ import { Structure, Model } from '../../../../mol-model/structure';
 import { VolumeServerInfo } from './model';
 import { PluginContext } from '../../../../mol-plugin/context';
 import { RuntimeContext } from '../../../../mol-task';
+import { MmcifFormat } from '../../../../mol-model-formats/structure/mmcif';
 
 export function getStreamingMethod(s?: Structure, defaultKind: VolumeServerInfo.Kind = 'x-ray'): VolumeServerInfo.Kind {
     if (!s) return defaultKind;
 
     const model = s.models[0];
-    if (model.sourceData.kind !== 'mmCIF') return defaultKind;
+    if (!MmcifFormat.is(model.sourceData)) return defaultKind;
 
-    const { data } = model.sourceData;
+    const { db } = model.sourceData.data;
 
     // prefer EMDB entries over structure-factors (SF) e.g. for 'ELECTRON CRYSTALLOGRAPHY' entries
     // like 6axz or 6kj3 for which EMDB entries are available but map calculation from SF is hard
-    for (let i = 0, il = data.pdbx_database_related._rowCount; i < il; ++i) {
-        if (data.pdbx_database_related.db_name.value(i).toUpperCase() === 'EMDB') {
+    for (let i = 0, il = db.pdbx_database_related._rowCount; i < il; ++i) {
+        if (db.pdbx_database_related.db_name.value(i).toUpperCase() === 'EMDB') {
             return 'em'
         }
     }
 
-    if (data.pdbx_database_status.status_code_sf.isDefined && data.pdbx_database_status.status_code_sf.value(0) === 'REL') {
+    if (db.pdbx_database_status.status_code_sf.isDefined && db.pdbx_database_status.status_code_sf.value(0) === 'REL') {
         return 'x-ray'
     }
 
     // fallbacks
-    for (let i = 0; i < data.exptl.method.rowCount; i++) {
-        const v = data.exptl.method.value(i).toUpperCase();
+    for (let i = 0; i < db.exptl.method.rowCount; i++) {
+        const v = db.exptl.method.value(i).toUpperCase();
         if (v.indexOf('MICROSCOPY') >= 0) return 'em';
     }
     return defaultKind;
@@ -41,9 +42,9 @@ export function getStreamingMethod(s?: Structure, defaultKind: VolumeServerInfo.
 /** Returns EMD ID when available, otherwise falls back to PDB ID */
 export function getEmIds(model: Model): string[] {
     const ids: string[] = []
-    if (model.sourceData.kind !== 'mmCIF') return [ model.entryId ]
+    if (!MmcifFormat.is(model.sourceData)) return [ model.entryId ]
 
-    const { db_id, db_name, content_type } = model.sourceData.data.pdbx_database_related
+    const { db_id, db_name, content_type } = model.sourceData.data.db.pdbx_database_related
     if (!db_name.isDefined) return [ model.entryId ]
 
     for (let i = 0, il = db_name.rowCount; i < il; ++i) {

+ 3 - 2
src/mol-theme/color/entity-source.ts

@@ -16,6 +16,7 @@ import { getPaletteParams, getPalette } from '../../mol-util/color/palette';
 import { TableLegend, ScaleLegend } from '../../mol-util/legend';
 import { isInteger } from '../../mol-util/number';
 import { ColorLists } from '../../mol-util/color/lists';
+import { MmcifFormat } from '../../mol-model-formats/structure/mmcif';
 
 const DefaultList = 'dark-2'
 const DefaultColor = Color(0xFAFAFA)
@@ -100,8 +101,8 @@ function getMaps(models: ReadonlyArray<Model>) {
 
     for (let i = 0, il = models.length; i <il; ++i) {
         const m = models[i]
-        if (m.sourceData.kind !== 'mmCIF') continue
-        const { entity_src_gen, entity_src_nat, pdbx_entity_src_syn } = m.sourceData.data
+        if (!MmcifFormat.is(m.sourceData)) continue
+        const { entity_src_gen, entity_src_nat, pdbx_entity_src_syn } = m.sourceData.data.db
         addSrc(seqToSrcByModelEntity, srcKeySerialMap, i, m, entity_src_gen, entity_src_gen.pdbx_gene_src_scientific_name, entity_src_gen.plasmid_name, entity_src_gen.pdbx_gene_src_gene)
         addSrc(seqToSrcByModelEntity, srcKeySerialMap, i, m, entity_src_nat, entity_src_nat.pdbx_organism_scientific, entity_src_nat.pdbx_plasmid_name)
         addSrc(seqToSrcByModelEntity, srcKeySerialMap, i, m, pdbx_entity_src_syn, pdbx_entity_src_syn.organism_scientific)

+ 1 - 1
src/mol-theme/color/uncertainty.ts

@@ -67,5 +67,5 @@ export const UncertaintyColorThemeProvider: ColorTheme.Provider<UncertaintyColor
     factory: UncertaintyColorTheme,
     getParams: getUncertaintyColorThemeParams,
     defaultValues: PD.getDefaultValues(UncertaintyColorThemeParams),
-    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ctx.structure.models.some(m => m.atomicConformation.B_iso_or_equiv.isDefined || m.sourceData.data.ihm_sphere_obj_site.rmsf.isDefined)
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ctx.structure.models.some(m => m.atomicConformation.B_iso_or_equiv.isDefined || m.coarseHierarchy.isDefined)
 }

+ 1 - 1
src/mol-theme/size/uncertainty.ts

@@ -57,5 +57,5 @@ export const UncertaintySizeThemeProvider: SizeTheme.Provider<UncertaintySizeThe
     factory: UncertaintySizeTheme,
     getParams: getUncertaintySizeThemeParams,
     defaultValues: PD.getDefaultValues(UncertaintySizeThemeParams),
-    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ctx.structure.models.some(m => m.atomicConformation.B_iso_or_equiv.isDefined || m.sourceData.data.ihm_sphere_obj_site.rmsf.isDefined)
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure && ctx.structure.models.some(m => m.atomicConformation.B_iso_or_equiv.isDefined || m.coarseHierarchy.isDefined)
 }

+ 4 - 4
src/perf-tests/lookup3d.ts

@@ -7,7 +7,7 @@ import { Structure } from '../mol-model/structure'
 import { GridLookup3D } from '../mol-math/geometry';
 // import { sortArray } from 'mol-data/util';
 import { OrderedSet } from '../mol-data/int';
-import { trajectoryFromMmCIF } from '../mol-model-formats/structure/mmcif';
+import { trajectoryFromMmCIF, MmcifFormat } from '../mol-model-formats/structure/mmcif';
 
 require('util.promisify').shim();
 const readFileAsync = util.promisify(fs.readFile);
@@ -35,14 +35,14 @@ export async function readCIF(path: string) {
     const models = await trajectoryFromMmCIF(parsed.result.blocks[0]).run();
     const structures = models.map(Structure.ofModel);
 
-    return { mmcif: models[0].sourceData.data, models, structures };
+    return { mmcif: models[0].sourceData.data as MmcifFormat.Data, models, structures };
 }
 
 export async function test() {
     const { mmcif, structures } = await readCIF('e:/test/quick/1tqn_updated.cif');
 
-    const lookup = GridLookup3D({ x: mmcif.atom_site.Cartn_x.toArray(), y: mmcif.atom_site.Cartn_y.toArray(), z: mmcif.atom_site.Cartn_z.toArray(),
-        indices: OrderedSet.ofBounds(0, mmcif.atom_site._rowCount),
+    const lookup = GridLookup3D({ x: mmcif.db.atom_site.Cartn_x.toArray(), y: mmcif.db.atom_site.Cartn_y.toArray(), z: mmcif.db.atom_site.Cartn_z.toArray(),
+        indices: OrderedSet.ofBounds(0, mmcif.db.atom_site._rowCount),
         // radius: [1, 1, 1, 1]
         // indices: [1]
     });

+ 3 - 3
src/perf-tests/structure.ts

@@ -16,7 +16,7 @@ import { Structure, Model, Queries as Q, StructureElement, StructureSelection, S
 
 import to_mmCIF from '../mol-model/structure/export/mmcif'
 import { Vec3 } from '../mol-math/linear-algebra';
-import { trajectoryFromMmCIF } from '../mol-model-formats/structure/mmcif';
+import { trajectoryFromMmCIF, MmcifFormat } from '../mol-model-formats/structure/mmcif';
 // import { printUnits } from '../apps/structure-info/model';
 // import { EquivalenceClasses } from '../mol-data/util';
 
@@ -106,8 +106,8 @@ export async function getBcif(pdbId: string) {
 
 export namespace PropertyAccess {
     function baseline(model: Model) {
-        if (model.sourceData.kind !== 'mmCIF') throw new Error('Model must be mmCIF');
-        const atom_site = model.sourceData.data.atom_site;
+        if (!MmcifFormat.is(model.sourceData)) throw new Error('Model must be mmCIF');
+        const atom_site = model.sourceData.data.db.atom_site;
         const id = atom_site.id.value;
         let s = 0;
         for (let i = 0, _i = atom_site._rowCount; i < _i; i++) {