From 57f1aa51f32cd4be831bc8cff09f3e26c59b9657 Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Thu, 26 Apr 2018 19:12:35 +0200 Subject: [PATCH] Model.entities is now a standalone property, updated CoraseGrained, --- src/apps/domain-annotation-server/mapping.ts | 2 +- src/mol-data/db/column.ts | 13 +++++++ src/mol-data/db/table.ts | 4 +-- src/mol-model/structure/export/mmcif.ts | 8 ++--- src/mol-model/structure/model/formats/gro.ts | 19 +++++++++-- .../structure/model/formats/mmcif.ts | 11 ++++-- .../structure/model/formats/mmcif/ihm.ts | 6 ++++ .../structure/model/formats/mmcif/sequence.ts | 5 +-- src/mol-model/structure/model/model.ts | 3 ++ .../model/properties/coarse-grained.ts | 34 +++++++++++++++++-- .../structure/model/properties/common.ts | 14 ++++++++ .../structure/model/properties/hierarchy.ts | 9 +++-- .../structure/model/utils/hierarchy-keys.ts | 32 ++++++++++------- src/mol-model/structure/query/properties.ts | 20 +++++------ 14 files changed, 135 insertions(+), 45 deletions(-) create mode 100644 src/mol-model/structure/model/formats/mmcif/ihm.ts create mode 100644 src/mol-model/structure/model/properties/common.ts diff --git a/src/apps/domain-annotation-server/mapping.ts b/src/apps/domain-annotation-server/mapping.ts index da87d9b46..0ab03c359 100644 --- a/src/apps/domain-annotation-server/mapping.ts +++ b/src/apps/domain-annotation-server/mapping.ts @@ -112,6 +112,6 @@ function getDomain(name: string, schema: any, allData: any) { return domains.length > 0 ? { name, domains: Table.ofRows({ ...S.Base, ...schema }, domains), - mappings: Table.ofRows(S.mapping, mappings) + mappings: Table.ofRows<S.mapping>(S.mapping, mappings) } : void 0; } \ No newline at end of file diff --git a/src/mol-data/db/column.ts b/src/mol-data/db/column.ts index 93461e8a8..b50672afb 100644 --- a/src/mol-data/db/column.ts +++ b/src/mol-data/db/column.ts @@ -132,6 +132,10 @@ namespace Column { return createFirstIndexMapOfColumn(column); } + export function createIndexer<T>(column: Column<T>) { + return createIndexerOfColumn(column); + } + export function mapToArray<T, S>(column: Column<T>, f: (v: T) => S, ctor?: ArrayCtor<S>): ArrayLike<S> { return mapToArrayImpl<T, S>(column, f, ctor || Array); } @@ -174,6 +178,15 @@ function createFirstIndexMapOfColumn<T>(c: Column<T>): Map<T, number> { return map; } +function createIndexerOfColumn<T>(c: Column<T>): (value: T) => number { + const map = new Map<T, number>(); + for (let i = 0, _i = c.rowCount; i < _i; i++) { + const v = c.value(i); + if (!map.has(v)) map.set(c.value(i), i); + } + return v => map.has(v) ? map.get(v)! : -1; +} + function constColumn<T extends Column.Schema>(v: T['T'], rowCount: number, schema: T, valueKind: Column.ValueKind): Column<T['T']> { const value: Column<T['T']>['value'] = row => v; return { diff --git a/src/mol-data/db/table.ts b/src/mol-data/db/table.ts index cac162233..df96dacbe 100644 --- a/src/mol-data/db/table.ts +++ b/src/mol-data/db/table.ts @@ -58,7 +58,7 @@ namespace Table { return ret; } - export function ofRows<S extends Schema, R extends Table<S> = Table<S>>(schema: Schema, rows: ArrayLike<Row<S>>): R { + export function ofRows<S extends Schema, R extends Table<S> = Table<S>>(schema: Schema, rows: ArrayLike<Partial<Row<S>>>): R { const ret = Object.create(null); const rowCount = rows.length; const columns = Object.keys(schema); @@ -83,7 +83,7 @@ namespace Table { ret._columns = columns; ret._schema = schema; for (const k of columns) { - (ret as any)[k] = Column.ofArray({ array: arrays[k], schema: schema[k] }) + (ret as any)[k] = typeof arrays[k] !== 'undefined' ? Column.ofArray({ array: arrays[k], schema: schema[k] }) : Column.Undefined(ret._rowCount, schema[k]); } return ret as R; } diff --git a/src/mol-model/structure/export/mmcif.ts b/src/mol-model/structure/export/mmcif.ts index 9635dd78b..3958a39fa 100644 --- a/src/mol-model/structure/export/mmcif.ts +++ b/src/mol-model/structure/export/mmcif.ts @@ -102,10 +102,10 @@ const atom_site: Encoder.CategoryDefinition<Element.Location> = { function entityProvider({ model }: Context): Encoder.CategoryInstance { return { - data: model.hierarchy.entities, - definition: Encoder.CategoryDefinition.ofTable('entity', model.hierarchy.entities), - keys: () => Iterator.Range(0, model.hierarchy.entities._rowCount - 1), - rowCount: model.hierarchy.entities._rowCount + data: model.entities.data, + definition: Encoder.CategoryDefinition.ofTable('entity', model.entities.data), + keys: () => Iterator.Range(0, model.entities.data._rowCount - 1), + rowCount: model.entities.data._rowCount } } diff --git a/src/mol-model/structure/model/formats/gro.ts b/src/mol-model/structure/model/formats/gro.ts index 84b30517e..cad8c441a 100644 --- a/src/mol-model/structure/model/formats/gro.ts +++ b/src/mol-model/structure/model/formats/gro.ts @@ -16,9 +16,11 @@ import CoarseGrained from '../properties/coarse-grained' import findHierarchyKeys from '../utils/hierarchy-keys' import { guessElement } from '../utils/guess-element' import { ElementSymbol} from '../types' +import { mmCIF_Schema as mmCIF } from 'mol-io/reader/cif/schema/mmcif' import gro_Format = Format.gro import Sequence from '../properties/sequence'; +import { Entities } from '../properties/common'; type HierarchyOffsets = { residues: ArrayLike<number>, chains: ArrayLike<number> } @@ -71,9 +73,8 @@ function createHierarchyData(atomsData: Atoms, offsets: HierarchyOffsets): Hiera // }); const chains = Table.ofUndefinedColumns(Hierarchy.ChainsSchema, 0); - const entities = Table.ofUndefinedColumns(Hierarchy.EntitySchema, 0); - return { atoms, residues, chains, entities }; + return { atoms, residues, chains }; } function getConformation(atoms: Atoms): Conformation { @@ -112,13 +113,25 @@ function createModel(format: gro_Format, modelNum: number, previous?: Model): Mo residueSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, bounds), chainSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, bounds), } - const hierarchyKeys = findHierarchyKeys(hierarchyData, hierarchySegments); + + // TODO: create a better mock entity + const entityTable = Table.ofRows<mmCIF['entity']>(mmCIF.entity, [{ + id: '0', + src_method: 'syn', + type: 'polymer', + pdbx_number_of_molecules: 1 + }]); + + const entities: Entities = { data: entityTable, getEntityIndex: Column.createIndexer(entityTable.id) }; + + const hierarchyKeys = findHierarchyKeys(hierarchyData, entities, hierarchySegments); const hierarchy = { ...hierarchyData, ...hierarchyKeys, ...hierarchySegments }; return { id: UUID.create(), sourceData: format, modelNum, hierarchy, + entities, sequence: Sequence.fromHierarchy(hierarchy), conformation: getConformation(structure.atoms), coarseGrained: CoarseGrained.Empty, diff --git a/src/mol-model/structure/model/formats/mmcif.ts b/src/mol-model/structure/model/formats/mmcif.ts index b06ed9ef2..d88ca8ac7 100644 --- a/src/mol-model/structure/model/formats/mmcif.ts +++ b/src/mol-model/structure/model/formats/mmcif.ts @@ -19,6 +19,7 @@ import createAssemblies from './mmcif/assembly' import mmCIF_Format = Format.mmCIF import { getSequence } from './mmcif/sequence'; +import { Entities } from '../properties/common'; function findModelBounds({ data }: mmCIF_Format, startIndex: number) { const num = data.atom_site.pdbx_PDB_model_num; @@ -63,7 +64,7 @@ function createHierarchyData({ data }: mmCIF_Format, bounds: Interval, offsets: Table.columnToArray(residues, 'label_seq_id', Int32Array); Table.columnToArray(residues, 'auth_seq_id', Int32Array); const chains = Table.view(atom_site, Hierarchy.ChainsSchema, offsets.chains); - return { atoms, residues, chains, entities: data.entity }; + return { atoms, residues, chains }; } function getConformation({ data }: mmCIF_Format, bounds: Interval): Conformation { @@ -106,7 +107,10 @@ function createModel(format: mmCIF_Format, bounds: Interval, previous?: Model): residueSegments: Segmentation.ofOffsets(hierarchyOffsets.residues, bounds), chainSegments: Segmentation.ofOffsets(hierarchyOffsets.chains, bounds), } - const hierarchyKeys = findHierarchyKeys(hierarchyData, hierarchySegments); + + const entities: Entities = { data: format.data.entity, getEntityIndex: Column.createIndexer(format.data.entity.id) }; + + const hierarchyKeys = findHierarchyKeys(hierarchyData, entities, hierarchySegments); const hierarchy = { ...hierarchyData, ...hierarchyKeys, ...hierarchySegments }; @@ -114,8 +118,9 @@ function createModel(format: mmCIF_Format, bounds: Interval, previous?: Model): id: UUID.create(), sourceData: format, modelNum: format.data.atom_site.pdbx_PDB_model_num.value(Interval.start(bounds)), + entities, hierarchy, - sequence: getSequence(format.data, hierarchy), + sequence: getSequence(format.data, entities, hierarchy), conformation: getConformation(format, bounds), coarseGrained: CoarseGrained.Empty, symmetry: getSymmetry(format), diff --git a/src/mol-model/structure/model/formats/mmcif/ihm.ts b/src/mol-model/structure/model/formats/mmcif/ihm.ts new file mode 100644 index 000000000..879b4fb84 --- /dev/null +++ b/src/mol-model/structure/model/formats/mmcif/ihm.ts @@ -0,0 +1,6 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + diff --git a/src/mol-model/structure/model/formats/mmcif/sequence.ts b/src/mol-model/structure/model/formats/mmcif/sequence.ts index 85bb572ba..ccba6849b 100644 --- a/src/mol-model/structure/model/formats/mmcif/sequence.ts +++ b/src/mol-model/structure/model/formats/mmcif/sequence.ts @@ -8,8 +8,9 @@ import { mmCIF_Database as mmCIF } from 'mol-io/reader/cif/schema/mmcif' import Sequence from '../../properties/sequence' import { Column } from 'mol-data/db'; import { Hierarchy } from '../../properties/hierarchy'; +import { Entities } from '../../properties/common'; -export function getSequence(cif: mmCIF, hierarchy: Hierarchy): Sequence { +export function getSequence(cif: mmCIF, entities: Entities, hierarchy: Hierarchy): Sequence { if (!cif.entity_poly_seq._rowCount) return Sequence.fromHierarchy(hierarchy); const { entity_id, num, mon_id } = cif.entity_poly_seq; @@ -24,7 +25,7 @@ export function getSequence(cif: mmCIF, hierarchy: Hierarchy): Sequence { i++; const id = entity_id.value(start); - byEntityKey[hierarchy.findEntityKey(id)] = { entityId: id, compId: Column.window(mon_id, start, i), num: Column.window(num, start, i) } + byEntityKey[entities.getEntityIndex(id)] = { entityId: id, compId: Column.window(mon_id, start, i), num: Column.window(num, start, i) } } return { byEntityKey }; diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts index daf777734..9652cf5b1 100644 --- a/src/mol-model/structure/model/model.ts +++ b/src/mol-model/structure/model/model.ts @@ -11,6 +11,7 @@ import Hierarchy from './properties/hierarchy' import Conformation from './properties/conformation' import Symmetry from './properties/symmetry' import CoarseGrained from './properties/coarse-grained' +import { Entities } from './properties/common'; import from_gro from './formats/gro' import from_mmCIF from './formats/mmcif' @@ -27,7 +28,9 @@ interface Model extends Readonly<{ sourceData: Format, + entities: Entities, sequence: Sequence, + hierarchy: Hierarchy, conformation: Conformation, symmetry: Symmetry, diff --git a/src/mol-model/structure/model/properties/coarse-grained.ts b/src/mol-model/structure/model/properties/coarse-grained.ts index 725493ff1..730724114 100644 --- a/src/mol-model/structure/model/properties/coarse-grained.ts +++ b/src/mol-model/structure/model/properties/coarse-grained.ts @@ -4,12 +4,42 @@ * @author David Sehnal <david.sehnal@gmail.com> */ +import { mmCIF_Database as mmCIF } from 'mol-io/reader/cif/schema/mmcif' +import { Tensor } from 'mol-math/linear-algebra'; +import { Column } from 'mol-data/db'; + interface CoarseGrained { - // TODO + modelList: mmCIF['ihm_model_list'], + spheres: { [P in keyof CoarseGrained.Sphere]: Column<CoarseGrained.Sphere[P]> }, + gaussians: { [P in keyof CoarseGrained.Gaussian]: Column<CoarseGrained.Gaussian[P]> } } namespace CoarseGrained { - export const Empty: CoarseGrained = { }; + export const Empty: CoarseGrained = { } as any; + + interface Site { + // index to the Model.hierarchy.entities table + entityKey: number, + // index to the CoarseGrained.modelList table + modelKey: number, + + asym_id: string, + seq_id_begin: number, + seq_id_end: number, + x: number, + y: number, + z: number + } + + export interface Sphere extends Site { + radius: number, + rmsf: number + } + + export interface Gaussian extends Site { + weight: number, + covarianceMatrix: Tensor.Data + } } export default CoarseGrained; \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/common.ts b/src/mol-model/structure/model/properties/common.ts new file mode 100644 index 000000000..aa5d93aae --- /dev/null +++ b/src/mol-model/structure/model/properties/common.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { mmCIF_Database as mmCIF } from 'mol-io/reader/cif/schema/mmcif' + +interface Entities { + data: mmCIF['entity'], + getEntityIndex(id: string): number +} + +export { Entities } \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/hierarchy.ts b/src/mol-model/structure/model/properties/hierarchy.ts index 773e091d8..8987e070d 100644 --- a/src/mol-model/structure/model/properties/hierarchy.ts +++ b/src/mol-model/structure/model/properties/hierarchy.ts @@ -40,15 +40,15 @@ export const ChainsSchema = { export type ChainsSchema = typeof ChainsSchema export interface Chains extends Table<ChainsSchema> { } -export const EntitySchema = mmCIF['entity'] -export type EntitySchema = typeof EntitySchema -export interface Entities extends Table<EntitySchema> { } +// export const EntitySchema = mmCIF['entity'] +// export type EntitySchema = typeof EntitySchema +// export interface Entities extends Table<EntitySchema> { } export interface Data { atoms: Atoms, residues: Residues, chains: Chains, - entities: Entities + //entities: Entities } export interface Segments { @@ -70,7 +70,6 @@ export interface Keys { // also index to the Entities table. entityKey: Column<number>, - findEntityKey(id: string): number, findChainKey(entityId: string, label_asym_id: string): number, findResidueKey(entityId: string, label_asym_id: string, label_comp_id: string, auth_seq_id: number, pdbx_PDB_ins_code: string): number } diff --git a/src/mol-model/structure/model/utils/hierarchy-keys.ts b/src/mol-model/structure/model/utils/hierarchy-keys.ts index c09484245..d70a26a94 100644 --- a/src/mol-model/structure/model/utils/hierarchy-keys.ts +++ b/src/mol-model/structure/model/utils/hierarchy-keys.ts @@ -7,6 +7,7 @@ import { Column } from 'mol-data/db' import { Data, Segments, Keys } from '../properties/hierarchy' import { Interval, Segmentation } from 'mol-data/int' +import { Entities } from '../properties/common'; function getResidueId(comp_id: string, seq_id: number, ins_code: string) { return `${comp_id} ${seq_id} ${ins_code}`; @@ -26,24 +27,26 @@ function getElementSubstructureKeyMap(map: Map<number, Map<string, number>>, key return ret; } -function createLookUp(entity: Map<string, number>, chain: Map<number, Map<string, number>>, residue: Map<number, Map<string, number>>) { - const findEntityKey: Keys['findEntityKey'] = (id) => entity.has(id) ? entity.get(id)! : -1; +function createLookUp(entities: Entities, chain: Map<number, Map<string, number>>, residue: Map<number, Map<string, number>>) { + const getEntKey = entities.getEntityIndex; const findChainKey: Keys['findChainKey'] = (e, c) => { - if (!entity.has(e)) return -1; - const cm = chain.get(entity.get(e)!)!; + let eKey = getEntKey(e); + if (eKey < 0) return -1; + const cm = chain.get(eKey)!; if (!cm.has(c)) return -1; return cm.get(c)!; } const findResidueKey: Keys['findResidueKey'] = (e, c, name, seq, ins) => { - if (!entity.has(e)) return -1; - const cm = chain.get(entity.get(e)!)!; + let eKey = getEntKey(e); + if (eKey < 0) return -1; + const cm = chain.get(eKey)!; if (!cm.has(c)) return -1; const rm = residue.get(cm.get(c)!)! const id = getResidueId(name, seq, ins); if (!rm.has(id)) return -1; return rm.get(id)!; } - return { findEntityKey, findChainKey, findResidueKey }; + return { findChainKey, findResidueKey }; } function checkMonotonous(xs: ArrayLike<number>) { @@ -55,10 +58,13 @@ function checkMonotonous(xs: ArrayLike<number>) { return true; } -function create(data: Data, segments: Segments): Keys { - const { chains, residues, entities } = data; +function missingEntity(k: string) { + throw new Error(`Missing entity entry for entity id '${k}'.`); +} + +function create(data: Data, entities: Entities, segments: Segments): Keys { + const { chains, residues } = data; - const entityMap = Column.createFirstIndexMap(entities.id); const chainMaps = new Map<number, Map<string, number>>(), chainCounter = { index: 0 }; const residueMaps = new Map<number, Map<string, number>>(), residueCounter = { index: 0 }; @@ -78,7 +84,8 @@ function create(data: Data, segments: Segments): Keys { const chainSegment = chainsIt.move(); const cI = chainSegment.index; - const eKey = entityMap.get(label_entity_id.value(cI)) || 0; + let eKey = entities.getEntityIndex(label_entity_id.value(cI)); + if (eKey < 0) missingEntity(label_entity_id.value(cI)); const chainMap = getElementSubstructureKeyMap(chainMaps, eKey); const cKey = getElementKey(chainMap, label_asym_id.value(cI), chainCounter); @@ -99,14 +106,13 @@ function create(data: Data, segments: Segments): Keys { } } - const { findEntityKey, findChainKey, findResidueKey } = createLookUp(entityMap, chainMaps, residueMaps); + const { findChainKey, findResidueKey } = createLookUp(entities, chainMaps, residueMaps); return { isMonotonous: isMonotonous && checkMonotonous(entityKey) && checkMonotonous(chainKey) && checkMonotonous(residueKey), residueKey: Column.ofIntArray(residueKey), chainKey: Column.ofIntArray(chainKey), entityKey: Column.ofIntArray(entityKey), - findEntityKey, findChainKey, findResidueKey }; diff --git a/src/mol-model/structure/query/properties.ts b/src/mol-model/structure/query/properties.ts index 2ed1ac4e4..811c64a89 100644 --- a/src/mol-model/structure/query/properties.ts +++ b/src/mol-model/structure/query/properties.ts @@ -59,16 +59,16 @@ function eK(l: Element.Location) { return !Unit.isAtomic(l.unit) ? notAtomic() : const entity = { key: eK, - id: Element.property(l => l.unit.hierarchy.entities.id.value(eK(l))), - type: Element.property(l => l.unit.hierarchy.entities.type.value(eK(l))), - src_method: Element.property(l => l.unit.hierarchy.entities.src_method.value(eK(l))), - pdbx_description: Element.property(l => l.unit.hierarchy.entities.pdbx_description.value(eK(l))), - formula_weight: Element.property(l => l.unit.hierarchy.entities.formula_weight.value(eK(l))), - pdbx_number_of_molecules: Element.property(l => l.unit.hierarchy.entities.pdbx_number_of_molecules.value(eK(l))), - details: Element.property(l => l.unit.hierarchy.entities.details.value(eK(l))), - pdbx_mutation: Element.property(l => l.unit.hierarchy.entities.pdbx_mutation.value(eK(l))), - pdbx_fragment: Element.property(l => l.unit.hierarchy.entities.pdbx_fragment.value(eK(l))), - pdbx_ec: Element.property(l => l.unit.hierarchy.entities.pdbx_ec.value(eK(l))) + id: Element.property(l => l.unit.model.entities.data.id.value(eK(l))), + type: Element.property(l => l.unit.model.entities.data.type.value(eK(l))), + src_method: Element.property(l => l.unit.model.entities.data.src_method.value(eK(l))), + pdbx_description: Element.property(l => l.unit.model.entities.data.pdbx_description.value(eK(l))), + formula_weight: Element.property(l => l.unit.model.entities.data.formula_weight.value(eK(l))), + pdbx_number_of_molecules: Element.property(l => l.unit.model.entities.data.pdbx_number_of_molecules.value(eK(l))), + details: Element.property(l => l.unit.model.entities.data.details.value(eK(l))), + pdbx_mutation: Element.property(l => l.unit.model.entities.data.pdbx_mutation.value(eK(l))), + pdbx_fragment: Element.property(l => l.unit.model.entities.data.pdbx_fragment.value(eK(l))), + pdbx_ec: Element.property(l => l.unit.model.entities.data.pdbx_ec.value(eK(l))) } const unit = { -- GitLab