diff --git a/src/apps/viewer/extensions/cellpack/model.ts b/src/apps/viewer/extensions/cellpack/model.ts index 352bd8a7ea68b7739d38bedbfb660f3438ae5a9d..bae20822d53b91a0b15532b84b2d34809b01da64 100644 --- a/src/apps/viewer/extensions/cellpack/model.ts +++ b/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] }) diff --git a/src/mol-model-formats/structure/3dg.ts b/src/mol-model-formats/structure/3dg.ts index f72aff712cfc7d96f4bd3d7a33bd2d30d7942f17..74d5eba815f6560164c043e521319fb44401f6f8 100644 --- a/src/mol-model-formats/structure/3dg.ts +++ b/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); }) } diff --git a/src/mol-model-formats/structure/basic/parser.ts b/src/mol-model-formats/structure/basic/parser.ts index 6dc6cc2e049eaba45bd9513a5e637b60f9f67c29..cf41cd1866c7c82aef5f6b339cdf6d5f28a8e402 100644 --- a/src/mol-model-formats/structure/basic/parser.ts +++ b/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 { diff --git a/src/mol-model-formats/structure/basic/schema.ts b/src/mol-model-formats/structure/basic/schema.ts index 5b4d2d41728bb932661a00717a2772b12faa1c98..dea7e328e8a9db387542f49ee8c057e7fb9f73aa 100644 --- a/src/mol-model-formats/structure/basic/schema.ts +++ b/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 } \ No newline at end of file diff --git a/src/mol-model-formats/structure/common/component.ts b/src/mol-model-formats/structure/common/component.ts index 30f201b86c2f344560749e26fdd71ecdbb34f46a..483ac6e95369b33506deee7132a663104a8a7b91 100644 --- a/src/mol-model-formats/structure/common/component.ts +++ b/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][]) { diff --git a/src/mol-model-formats/structure/common/entity.ts b/src/mol-model-formats/structure/common/entity.ts index d44fefb2f7f9e7d5117333b80731f81d7181c604..3de2bfefd7c42951082f68b0f654d059d2dba716 100644 --- a/src/mol-model-formats/structure/common/entity.ts +++ b/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[]) { diff --git a/src/mol-model-formats/structure/format.ts b/src/mol-model-formats/structure/format.ts index efe82c0df900f2e7aec41bfec39e95cc55e8926c..a3cb77eb5327320f34972b02f43fc71995851534 100644 --- a/src/mol-model-formats/structure/format.ts +++ b/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 } \ No newline at end of file +export interface ModelFormat<T = unknown> { readonly kind: string, name: string, data: T } \ No newline at end of file diff --git a/src/mol-model-formats/structure/gro.ts b/src/mol-model-formats/structure/gro.ts index 8f141ea1c98305df18e3b54a5dd6ecf64a5b027a..c38beca49164cb7d98d322036b10db9d83a2a316 100644 --- a/src/mol-model-formats/structure/gro.ts +++ b/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); }) } diff --git a/src/mol-model-formats/structure/mmcif.ts b/src/mol-model-formats/structure/mmcif.ts index ea8c6de0653c21bb9f0542f25e4fa69123be5462..d52f7898ce97b0431e80ea59a6e726efa58e3660 100644 --- a/src/mol-model-formats/structure/mmcif.ts +++ b/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)); } \ No newline at end of file diff --git a/src/mol-model-formats/structure/mmcif/parser.ts b/src/mol-model-formats/structure/mmcif/parser.ts deleted file mode 100644 index cbe1f50e134cdade04ed1ef90f5e0addc556587d..0000000000000000000000000000000000000000 --- a/src/mol-model-formats/structure/mmcif/parser.ts +++ /dev/null @@ -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) \ No newline at end of file diff --git a/src/mol-model-formats/structure/pdb.ts b/src/mol-model-formats/structure/pdb.ts index 525ec39cccfa76c405fbb2d44b231b213ab0a946..c77392ec26435bf213bec83521a7f74f95836c17 100644 --- a/src/mol-model-formats/structure/pdb.ts +++ b/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) }) } diff --git a/src/mol-model-formats/structure/pdb/to-cif.ts b/src/mol-model-formats/structure/pdb/to-cif.ts index cf967d4359bbb2a6437f4973a3eb3a20069646b8..05e0f8592689d7a48a0d44b3cac74e06f83284d5 100644 --- a/src/mol-model-formats/structure/pdb/to-cif.ts +++ b/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; diff --git a/src/mol-model-formats/structure/psf.ts b/src/mol-model-formats/structure/psf.ts index ab4eae3e12a7c9c2a7d441f554fbe9d9b80dc59d..88e5a53d64a8e3f6dfdf798b15f8e61f580dccbf 100644 --- a/src/mol-model-formats/structure/psf.ts +++ b/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) }) } \ No newline at end of file diff --git a/src/mol-model-props/common/wrapper.ts b/src/mol-model-props/common/wrapper.ts index 21c57e124e18ad5ab3193eb9a67774cc8a0abd51..7f3523515dbfc88602fe798e05c7a6c79328b8f0 100644 --- a/src/mol-model-props/common/wrapper.ts +++ b/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()) }; diff --git a/src/mol-model-props/computed/secondary-structure.ts b/src/mol-model-props/computed/secondary-structure.ts index 40015d7d0e999271e802b7a0bb1883809fb7f8dc..d80c58b383b9a8725daed90eb9632203986e2f17 100644 --- a/src/mol-model-props/computed/secondary-structure.ts +++ b/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 diff --git a/src/mol-model-props/pdbe/preferred-assembly.ts b/src/mol-model-props/pdbe/preferred-assembly.ts index f156953280d95914287c3aa286eca830241452a5..ebc7fb081c24c9e50396450f84618f0a06a09b99 100644 --- a/src/mol-model-props/pdbe/preferred-assembly.ts +++ b/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); } diff --git a/src/mol-model-props/pdbe/struct-ref-domain.ts b/src/mol-model-props/pdbe/struct-ref-domain.ts index 54a31131e8e6ecd75134ea49a17227dbda57b06b..db37fee7e8412b3d06e7f5e587aea9822483ebe9 100644 --- a/src/mol-model-props/pdbe/struct-ref-domain.ts +++ b/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); } diff --git a/src/mol-model-props/pdbe/structure-quality-report.ts b/src/mol-model-props/pdbe/structure-quality-report.ts index 84bf3344aeae8a873ee1665a64bfa8c6cc4a5011..e153de014ba3749c37f0700edb55b9f0a01698b3 100644 --- a/src/mol-model-props/pdbe/structure-quality-report.ts +++ b/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), } } } diff --git a/src/mol-model-props/rcsb/assembly-symmetry.ts b/src/mol-model-props/rcsb/assembly-symmetry.ts index 562ad1bb4bcbb2f657c742d31af02b9d8da39077..6dce3e871e3ebd414256ecc7fb2af5825fe524f6 100644 --- a/src/mol-model-props/rcsb/assembly-symmetry.ts +++ b/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) diff --git a/src/mol-model-props/rcsb/validation-report.ts b/src/mol-model-props/rcsb/validation-report.ts index c2a2b054f8dab317d739a391fef2823b56658b0f..ada844656b5fd832507d19efae31e330cbe3d98c 100644 --- a/src/mol-model-props/rcsb/validation-report.ts +++ b/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) ) } diff --git a/src/mol-model/structure/export/categories/utils.ts b/src/mol-model/structure/export/categories/utils.ts index 091e92172dcbfeefa10bb7e069c717103474a05a..4f500efbf56d9dfe93322614ca74b4b1d1958e47 100644 --- a/src/mol-model/structure/export/categories/utils.ts +++ b/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); } diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts index 0fe77b75e9bd9f7a52517b5d9af6de3c82edbc36..04dd2a0dd482c28713a5bb4c68686bf244752153 100644 --- a/src/mol-model/structure/model/model.ts +++ b/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 } diff --git a/src/mol-model/structure/structure/properties.ts b/src/mol-model/structure/structure/properties.ts index ec2cef78ee5d63986e7a0a1943be96dca50a2205..48ab835a516d3c50613ad4e5004389db0f6d53a8 100644 --- a/src/mol-model/structure/structure/properties.ts +++ b/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) { diff --git a/src/mol-model/structure/topology/topology.ts b/src/mol-model/structure/topology/topology.ts index d5e6dbac27cde544ce95dc6784bbb880a2eeab67..6ef843f72f937e518baaa489571ca361759bc377 100644 --- a/src/mol-model/structure/topology/topology.ts +++ b/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 } } diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/util.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/util.ts index ea703fe3691773819183ba7f215374de547d1af7..0f1d5ecca749050a9ef7e3f7e0aa388d53e21dd5 100644 --- a/src/mol-plugin/behavior/dynamic/volume-streaming/util.ts +++ b/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) { diff --git a/src/mol-theme/color/entity-source.ts b/src/mol-theme/color/entity-source.ts index c38d9f14a2776eaf61953b61bc99816856bed500..3e94e70ed7f33ba312240bfd19de769bfc12a812 100644 --- a/src/mol-theme/color/entity-source.ts +++ b/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) diff --git a/src/mol-theme/color/uncertainty.ts b/src/mol-theme/color/uncertainty.ts index b715369f10fb8db503a5c89c3745a07b8382e44d..d7511cb0533f6554bf50d9d92797ef1b0df223f5 100644 --- a/src/mol-theme/color/uncertainty.ts +++ b/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) } \ No newline at end of file diff --git a/src/mol-theme/size/uncertainty.ts b/src/mol-theme/size/uncertainty.ts index 78e4dc41c8d915d8904b8c3c4d6bb47696dfe122..5f9da065b4b8331ac620db16e6e4b806446ac176 100644 --- a/src/mol-theme/size/uncertainty.ts +++ b/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) } \ No newline at end of file diff --git a/src/perf-tests/lookup3d.ts b/src/perf-tests/lookup3d.ts index 8df122b3e8f708a35d22e50f4ddf8ce927e34a9d..c4f7c19b319e039d08a2220ef925fc05b1cab758 100644 --- a/src/perf-tests/lookup3d.ts +++ b/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] }); diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts index 8f7d1ac4e6cd3db684566a39004a2e485c5ed0c5..d67f811cea1d544b23efbc7495d1bb6fce2b98e0 100644 --- a/src/perf-tests/structure.ts +++ b/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++) {