diff --git a/README.md b/README.md index 043668a218ce34cd7fafd11f0da749b2172919f2..bea25cde6c5ecf65b2286159027280754b09413c 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ Install CIFTools `npm install ciftools -g` ### Other scripts **Create chem comp bond table** - export NODE_PATH="lib"; node --max-old-space-size=8192 build/src/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b + export NODE_PATH="lib"; node --max-old-space-size=4096 lib/apps/chem-comp-bond/create-table.js build/data/ccb.bcif -b **Test model server** diff --git a/src/apps/chem-comp-bond/create-table.ts b/src/apps/chem-comp-bond/create-table.ts index cc29af24a60f6490751d22ec1b4101d68cb3665e..ba541008609cb3c9c61c6dd09ff192f8e3535ad9 100644 --- a/src/apps/chem-comp-bond/create-table.ts +++ b/src/apps/chem-comp-bond/create-table.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -15,12 +15,13 @@ const readFile = util.promisify(fs.readFile) const writeFile = util.promisify(fs.writeFile) import { Progress } from '../../mol-task' -import { Database, Table, DatabaseCollection, Column } from '../../mol-data/db' +import { Database, Table, DatabaseCollection } from '../../mol-data/db' import { CIF } from '../../mol-io/reader/cif' import { CifWriter } from '../../mol-io/writer/cif' import { CCD_Schema } from '../../mol-io/reader/cif/schema/ccd' import { SetUtils } from '../../mol-util/set' import { DefaultMap } from '../../mol-util/map' +import { mmCIF_chemCompBond_schema } from '../../mol-io/reader/cif/schema/mmcif-extras'; export async function ensureAvailable(path: string, url: string) { if (FORCE_DOWNLOAD || !fs.existsSync(path)) { @@ -74,16 +75,6 @@ export function getEncodedCif(name: string, database: Database<Database.Schema>, type CCB = Table<CCD_Schema['chem_comp_bond']> type CCA = Table<CCD_Schema['chem_comp_atom']> -const ChemCompBond_Schema = { - comp_id: CCD_Schema['chem_comp_bond'].comp_id, - atom_id_1: CCD_Schema['chem_comp_bond'].atom_id_1, - atom_id_2: CCD_Schema['chem_comp_bond'].atom_id_2, - value_order: CCD_Schema['chem_comp_bond'].value_order, - pdbx_aromatic_flag: CCD_Schema['chem_comp_bond'].pdbx_aromatic_flag, - pdbx_stereo_config: CCD_Schema['chem_comp_bond'].pdbx_stereo_config, - molstar_protonation_variant: Column.Schema.Str() -} - function ccbKey(compId: string, atomId1: string, atomId2: string) { return atomId1 < atomId2 ? `${compId}:${atomId1}-${atomId2}` : `${compId}:${atomId2}-${atomId1}` } @@ -202,14 +193,14 @@ async function createBonds() { } } - const bondTable = Table.ofArrays(ChemCompBond_Schema, { + const bondTable = Table.ofArrays(mmCIF_chemCompBond_schema, { comp_id, atom_id_1, atom_id_2, value_order, pdbx_aromatic_flag, pdbx_stereo_config, molstar_protonation_variant }) const bondDatabase = Database.ofTables( TABLE_NAME, - { chem_comp_bond: ChemCompBond_Schema }, + { chem_comp_bond: mmCIF_chemCompBond_schema }, { chem_comp_bond: bondTable } ) @@ -220,12 +211,15 @@ async function run(out: string, binary = false) { const bonds = await createBonds() const cif = getEncodedCif(TABLE_NAME, bonds, binary) + if (!fs.existsSync(path.dirname(out))) { + fs.mkdirSync(path.dirname(out)); + } writeFile(out, cif) } const TABLE_NAME = 'CHEM_COMP_BONDS' -const DATA_DIR = path.join(__dirname, '..', '..', '..', 'data') +const DATA_DIR = path.join(__dirname, '..', '..', '..', 'build/data') const CCD_PATH = path.join(DATA_DIR, 'components.cif') const PVCD_PATH = path.join(DATA_DIR, 'aa-variants-v1.cif') const CCD_URL = 'http://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif' diff --git a/src/mol-io/reader/cif/schema/mmcif-extras.ts b/src/mol-io/reader/cif/schema/mmcif-extras.ts index 10b5e32f74df25b17eaf76414e89f7cd9d98b74b..cb35f4879008bbffd1cc4fbd6c4f577aae29a53a 100644 --- a/src/mol-io/reader/cif/schema/mmcif-extras.ts +++ b/src/mol-io/reader/cif/schema/mmcif-extras.ts @@ -1,10 +1,12 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 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 { mmCIF_Schema } from './mmcif'; +import { mmCIF_Schema } from './mmcif'; +import { Column } from '../../../../mol-data/db'; export const mmCIF_residueId_schema = { label_comp_id: mmCIF_Schema.atom_site.label_comp_id, @@ -15,4 +17,10 @@ export const mmCIF_residueId_schema = { auth_comp_id: mmCIF_Schema.atom_site.auth_atom_id, auth_seq_id: mmCIF_Schema.atom_site.auth_seq_id, auth_asym_id: mmCIF_Schema.atom_site.auth_asym_id +} + +export const mmCIF_chemCompBond_schema = { + ...mmCIF_Schema.chem_comp_bond, + /** Indicates if the bond entry was taken from the protonation variant dictionary */ + molstar_protonation_variant: Column.Schema.Str() } \ No newline at end of file diff --git a/src/mol-model-props/wwpdb/chem-comp-bond.ts b/src/mol-model-props/wwpdb/chem-comp-bond.ts new file mode 100644 index 0000000000000000000000000000000000000000..2395d4832d341d32eb8aa32d44dfbbaab79aafc5 --- /dev/null +++ b/src/mol-model-props/wwpdb/chem-comp-bond.ts @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author Alexander Rose <alexander.rose@weirdbyte.de> + */ + +import { Column, Table } from '../../mol-data/db'; +import { toTable } from '../../mol-io/reader/cif/schema'; +import { Model, CustomPropertyDescriptor } from '../../mol-model/structure'; +import { mmCIF_chemCompBond_schema } from '../../mol-io/reader/cif/schema/mmcif-extras'; +import { CifWriter } from '../../mol-io/writer/cif'; + +export namespace ChemCompBond { + export type Property = Table<Schema['chem_comp_bond']> + + export function getFromModel(model: Model): Property { + if (model.sourceData.kind !== 'mmCIF') return Table.ofUndefinedColumns(Schema.chem_comp_bond, 0); + const { chem_comp_bond } = model.sourceData.data + return Table.ofColumns(Schema.chem_comp_bond, { + ...chem_comp_bond, + molstar_protonation_variant: Column.Undefined(chem_comp_bond._rowCount, Column.Schema.Str()) + }); + } + + export function get(model: Model): Property { + return model._staticPropertyData.__ChemCompBond__ || getFromModel(model); + } + function set(model: Model, prop: Property) { + (model._staticPropertyData.__ChemCompBond__ as Property) = prop; + } + + export const Schema = { chem_comp_bond: mmCIF_chemCompBond_schema }; + export type Schema = typeof Schema + + export const Descriptor = CustomPropertyDescriptor({ + isStatic: true, + name: 'chem_comp_bond', + cifExport: { + prefix: '', + context(ctx): Property { return get(ctx.firstModel); }, + categories: [{ + name: 'chem_comp_bond', + instance(ctx: Property) { + return CifWriter.Category.ofTable(ctx); + } + }] + } + }); + + function fromCifData(model: Model): Table<Schema['chem_comp_bond']> | undefined { + if (model.sourceData.kind !== 'mmCIF') return void 0; + const cat = model.sourceData.frame.categories.chem_comp_bond; + if (!cat) return void 0; + return toTable(Schema.chem_comp_bond, cat); + } + + export async function attachFromCifOrTable(model: Model, params: { + // optional Table source + wwPDB_apiSourceTable?: (model: Model) => Promise<Table<Schema['chem_comp_bond']>> + }) { + if (model.customProperties.has(Descriptor)) return true; + + let chemCompBond: Table<Schema['chem_comp_bond']> | undefined = fromCifData(model); + if (chemCompBond === void 0 && params.wwPDB_apiSourceTable) { + const data = await params.wwPDB_apiSourceTable(model); + if (!data) return false; + chemCompBond = chemCompBondFromTable(model, data); + } else { + return false; + } + + if (!chemCompBond) return false; + + model.customProperties.add(Descriptor); + set(model, chemCompBond); + return true; + } +} + +function chemCompBondFromTable(model: Model, table: Table<ChemCompBond.Schema['chem_comp_bond']>): Table<ChemCompBond.Schema['chem_comp_bond']> { + return Table.pick(table, ChemCompBond.Schema.chem_comp_bond, (i: number) => { + return model.properties.chemicalComponentMap.has(table.comp_id.value(i)) + }) +} \ No newline at end of file