diff --git a/README.md b/README.md index d35442edd4eaefce2553a3486341583f9c5b44ad..b056d10f9655b711a5d4bf6ba840aa991b2cd85d 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ The core of Mol* currently consists of these modules: - `mol-math` Math related (loosely) algorithms and data structures. - `mol-io` Parsing library. Each format is parsed into an interface that corresponds to the data stored by it. Support for common coordinate, experimental/map, and annotation data formats. - `mol-model` Data structures and algorithms (such as querying) for representing molecular data (including coordinate, experimental/map, and annotation data). +- `mol-model-formats` Data format parsers for `mol-model`. - `mol-model-props` Common "custom properties". - `mol-script` A scriting language for creating representations/scenes and querying (includes the [MolQL query language](https://molql.github.io)). - `mol-geo` Creating (molecular) geometries. diff --git a/package.json b/package.json index b139054660070dab572be66c51017642c92bcb75..58063c0f529d6e7e66126062b60c766bf529dfca 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "mol-math($|/.*)": "<rootDir>/src/mol-math$1", "mol-model($|/.*)": "<rootDir>/src/mol-model$1", "mol-model-props($|/.*)": "<rootDir>/src/mol-model-props$1", + "mol-model-formats($|/.*)": "<rootDir>/src/mol-model-formats$1", "mol-plugin($|/.*)": "<rootDir>/src/mol-plugin$1", "mol-ql($|/.*)": "<rootDir>/src/mol-ql$1", "mol-repr($|/.*)": "<rootDir>/src/mol-repr$1", diff --git a/src/apps/basic-wrapper/index.ts b/src/apps/basic-wrapper/index.ts index 0a4cca600a8226afda02b5ec155e417adc970c75..fe4ab89136da9c20237902db70943d0299fc0650 100644 --- a/src/apps/basic-wrapper/index.ts +++ b/src/apps/basic-wrapper/index.ts @@ -55,7 +55,7 @@ class BasicWrapper { const state = this.stateTemplate.build(); state.to('url').update(StateTransforms.Data.Download, p => ({ ...p, url })); - state.to('asm').update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: assemblyId })); + state.to('asm').update(StateTransforms.Model.StructureAssemblyFromModel, p => ({ ...p, id: assemblyId || 'deposited' })); await PluginCommands.State.Update.dispatch(this.plugin, { state: this.plugin.state.dataState, tree: state }); diff --git a/src/apps/structure-info/model.ts b/src/apps/structure-info/model.ts index c01698f91a40bda96ebad2fd4955b18e9a1546a7..58c76c69f32b3202f852b75bf5d1a54126eb3de4 100644 --- a/src/apps/structure-info/model.ts +++ b/src/apps/structure-info/model.ts @@ -9,11 +9,12 @@ import * as argparse from 'argparse' require('util.promisify').shim(); import { CifFrame } from 'mol-io/reader/cif' -import { Model, Structure, StructureElement, Unit, Format, StructureProperties, UnitRing } from 'mol-model/structure' +import { Model, Structure, StructureElement, Unit, StructureProperties, UnitRing } from 'mol-model/structure' // import { Run, Progress } from 'mol-task' import { OrderedSet } from 'mol-data/int'; import { openCif, downloadCif } from './helpers'; import { Vec3 } from 'mol-math/linear-algebra'; +import { trajectoryFromMmCIF } from 'mol-model-formats/structure/mmcif'; async function downloadFromPdb(pdb: string) { @@ -198,7 +199,7 @@ export function printModelStats(models: ReadonlyArray<Model>) { } export async function getModelsAndStructure(frame: CifFrame) { - const models = await Model.create(Format.mmCIF(frame)).run(); + const models = await trajectoryFromMmCIF(frame).run(); const structure = Structure.ofModel(models[0]); return { models, structure }; } @@ -247,7 +248,7 @@ interface Args { download?: string, file?: string, - models?:boolean, + models?: boolean, seq?: boolean, ihm?: boolean, units?: boolean, diff --git a/src/apps/structure-info/volume.ts b/src/apps/structure-info/volume.ts index 56a7812d9af2ae3b78f588bd80ec21d60057ce9a..378f4631358b3626d6cab6f8497dcce53d61df68 100644 --- a/src/apps/structure-info/volume.ts +++ b/src/apps/structure-info/volume.ts @@ -8,7 +8,7 @@ import * as fs from 'fs' import * as argparse from 'argparse' import * as util from 'util' -import { VolumeData, parseDensityServerData, VolumeIsoValue } from 'mol-model/volume' +import { VolumeData, VolumeIsoValue } from 'mol-model/volume' import { downloadCif } from './helpers' import CIF from 'mol-io/reader/cif' import { DensityServer_Data_Database } from 'mol-io/reader/cif/schema/density-server'; @@ -17,6 +17,7 @@ import { StringBuilder } from 'mol-util'; import { Task } from 'mol-task'; import { createVolumeIsosurfaceMesh } from 'mol-repr/volume/isosurface'; import { createEmptyTheme } from 'mol-theme/theme'; +import { parseDensityServerData } from 'mol-model-formats/volume/density-server'; require('util.promisify').shim(); const writeFileAsync = util.promisify(fs.writeFile); diff --git a/src/mol-io/reader/_spec/cif.spec.ts b/src/mol-io/reader/_spec/cif.spec.ts index a2fb03ed952f69e1c9c93878a7b5904d174f3ddd..43e5cd4c5139e3da21b9d7de00d57cae59bae5c0 100644 --- a/src/mol-io/reader/_spec/cif.spec.ts +++ b/src/mol-io/reader/_spec/cif.spec.ts @@ -6,17 +6,16 @@ */ import * as Data from '../cif/data-model' -import TextField from '../cif/text/field' import * as Schema from '../cif/schema' import { Column } from 'mol-data/db' const columnData = `123abc d,e,f '4 5 6'`; // 123abc d,e,f '4 5 6' -const intField = TextField({ data: columnData, indices: [0, 1, 1, 2, 2, 3], count: 3 }, 3); -const strField = TextField({ data: columnData, indices: [3, 4, 4, 5, 5, 6], count: 3 }, 3); -const strListField = TextField({ data: columnData, indices: [7, 12], count: 1 }, 1); -const intListField = TextField({ data: columnData, indices: [14, 19], count: 1 }, 1); +const intField = Data.CifField.ofTokens({ data: columnData, indices: [0, 1, 1, 2, 2, 3], count: 3 }); +const strField = Data.CifField.ofTokens({ data: columnData, indices: [3, 4, 4, 5, 5, 6], count: 3 }); +const strListField = Data.CifField.ofTokens({ data: columnData, indices: [7, 12], count: 1 }); +const intListField = Data.CifField.ofTokens({ data: columnData, indices: [14, 19], count: 1 }); const testBlock = Data.CifBlock(['test'], { test: Data.CifCategory('test', 3, ['int', 'str', 'strList', 'intList'], { diff --git a/src/mol-io/reader/_spec/csv.spec.ts b/src/mol-io/reader/_spec/csv.spec.ts index 0802bc7491fedc90c702ef6bc24d7fa653746895..8977b5909aa73d7714760f377a3d912048a20db5 100644 --- a/src/mol-io/reader/_spec/csv.spec.ts +++ b/src/mol-io/reader/_spec/csv.spec.ts @@ -62,7 +62,7 @@ describe('csv reader', () => { }); it('tabs', async () => { - const parsed = await Csv(tabString, { delimiter: '\t' }).run();; + const parsed = await Csv(tabString, { delimiter: '\t' }).run(); if (parsed.isError) return; const csvFile = parsed.result; diff --git a/src/mol-io/reader/cif/data-model.ts b/src/mol-io/reader/cif/data-model.ts index fe044426017e052ae96a2e9d420f8c13d52c53c1..855cecb9bf74387ad68f08325da0bdfa00da2c8c 100644 --- a/src/mol-io/reader/cif/data-model.ts +++ b/src/mol-io/reader/cif/data-model.ts @@ -5,10 +5,12 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Column } from 'mol-data/db' +import { Column, ColumnHelpers } from 'mol-data/db' import { Tensor } from 'mol-math/linear-algebra' -import { getNumberType, NumberType } from '../common/text/number-parser'; +import { getNumberType, NumberType, parseInt as fastParseInt, parseFloat as fastParseFloat } from '../common/text/number-parser'; import { Encoding } from '../../common/binary-cif'; +import { Tokens } from '../common/text/tokenizer'; +import { areValuesEqualProvider } from '../common/text/column/token'; export interface CifFile { readonly name?: string, @@ -55,6 +57,19 @@ export namespace CifCategory { export function empty(name: string): CifCategory { return { rowCount: 0, name, fieldNames: [], getField(name: string) { return void 0; } }; }; + + export type SomeFields<S> = { [P in keyof S]?: CifField } + export type Fields<S> = { [P in keyof S]: CifField } + + export function ofFields(name: string, fields: { [name: string]: CifField | undefined }): CifCategory { + const fieldNames = Object.keys(fields); + return { + rowCount: fieldNames.length > 0 ? fields[fieldNames[0]]!.rowCount : 0, + name, + fieldNames, + getField(name) { return fields[name]; } + }; + } } /** @@ -81,6 +96,108 @@ export interface CifField { toFloatArray(params?: Column.ToArrayParams<number>): ReadonlyArray<number> } +export namespace CifField { + export function ofString(value: string) { + return ofStrings([value]); + } + + export function ofStrings(values: string[]): CifField { + const rowCount = values.length; + const str: CifField['str'] = row => { const ret = values[row]; if (!ret || ret === '.' || ret === '?') return ''; return ret; }; + const int: CifField['int'] = row => { const v = values[row]; return fastParseInt(v, 0, v.length) || 0; }; + const float: CifField['float'] = row => { const v = values[row]; return fastParseFloat(v, 0, v.length) || 0; }; + const valueKind: CifField['valueKind'] = row => { + const v = values[row], l = v.length; + if (l > 1) return Column.ValueKind.Present; + if (l === 0) return Column.ValueKind.NotPresent; + const c = v.charCodeAt(0); + if (c === 46 /* . */) return Column.ValueKind.NotPresent; + if (c === 63 /* ? */) return Column.ValueKind.Unknown; + return Column.ValueKind.Present; + }; + + return { + __array: void 0, + binaryEncoding: void 0, + isDefined: true, + rowCount, + str, + int, + float, + valueKind, + areValuesEqual: (rowA, rowB) => values[rowA] === values[rowB], + toStringArray: params => ColumnHelpers.createAndFillArray(rowCount, str, params), + toIntArray: params => ColumnHelpers.createAndFillArray(rowCount, int, params), + toFloatArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params) + } + } + + export function ofNumbers(values: number[]): CifField { + const rowCount = values.length; + const str: CifField['str'] = row => { return '' + values[row]; }; + const float: CifField['float'] = row => values[row]; + const valueKind: CifField['valueKind'] = row => Column.ValueKind.Present; + + return { + __array: void 0, + binaryEncoding: void 0, + isDefined: true, + rowCount, + str, + int: float, + float, + valueKind, + areValuesEqual: (rowA, rowB) => values[rowA] === values[rowB], + toStringArray: params => ColumnHelpers.createAndFillArray(rowCount, str, params), + toIntArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params), + toFloatArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params) + } + } + + export function ofTokens(tokens: Tokens): CifField { + const { data, indices, count: rowCount } = tokens; + + const str: CifField['str'] = row => { + const ret = data.substring(indices[2 * row], indices[2 * row + 1]); + if (ret === '.' || ret === '?') return ''; + return ret; + }; + + const int: CifField['int'] = row => { + return fastParseInt(data, indices[2 * row], indices[2 * row + 1]) || 0; + }; + + const float: CifField['float'] = row => { + return fastParseFloat(data, indices[2 * row], indices[2 * row + 1]) || 0; + }; + + const valueKind: CifField['valueKind'] = row => { + const s = indices[2 * row], l = indices[2 * row + 1] - s; + if (l > 1) return Column.ValueKind.Present; + if (l === 0) return Column.ValueKind.NotPresent; + const v = data.charCodeAt(s); + if (v === 46 /* . */) return Column.ValueKind.NotPresent; + if (v === 63 /* ? */) return Column.ValueKind.Unknown; + return Column.ValueKind.Present; + }; + + return { + __array: void 0, + binaryEncoding: void 0, + isDefined: true, + rowCount, + str, + int, + float, + valueKind, + areValuesEqual: areValuesEqualProvider(tokens), + toStringArray: params => ColumnHelpers.createAndFillArray(rowCount, str, params), + toIntArray: params => ColumnHelpers.createAndFillArray(rowCount, int, params), + toFloatArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params) + } + } +} + export function getTensor(category: CifCategory, field: string, space: Tensor.Space, row: number, zeroIndexed: boolean): Tensor.Data { const ret = space.create(); const offset = zeroIndexed ? 0 : 1; diff --git a/src/mol-io/reader/cif/text/field.ts b/src/mol-io/reader/cif/text/field.ts deleted file mode 100644 index 3248cd1eff26dd48478d9dba29184b23f71cf04d..0000000000000000000000000000000000000000 --- a/src/mol-io/reader/cif/text/field.ts +++ /dev/null @@ -1,100 +0,0 @@ -/** - * Copyright (c) 2017-2018 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 { Column, ColumnHelpers } from 'mol-data/db' -import * as TokenColumn from '../../common/text/column/token' -import { Tokens } from '../../common/text/tokenizer' -import * as Data from '../data-model' -import { parseInt as fastParseInt, parseFloat as fastParseFloat } from '../../common/text/number-parser' - -export default function CifTextField(tokens: Tokens, rowCount: number): Data.CifField { - const { data, indices } = tokens; - - const str: Data.CifField['str'] = row => { - const ret = data.substring(indices[2 * row], indices[2 * row + 1]); - if (ret === '.' || ret === '?') return ''; - return ret; - }; - - const int: Data.CifField['int'] = row => { - return fastParseInt(data, indices[2 * row], indices[2 * row + 1]) || 0; - }; - - const float: Data.CifField['float'] = row => { - return fastParseFloat(data, indices[2 * row], indices[2 * row + 1]) || 0; - }; - - const valueKind: Data.CifField['valueKind'] = row => { - const s = indices[2 * row], l = indices[2 * row + 1] - s; - if (l > 1) return Column.ValueKind.Present; - if (l === 0) return Column.ValueKind.NotPresent; - const v = data.charCodeAt(s); - if (v === 46 /* . */) return Column.ValueKind.NotPresent; - if (v === 63 /* ? */) return Column.ValueKind.Unknown; - return Column.ValueKind.Present; - }; - - return { - __array: void 0, - binaryEncoding: void 0, - isDefined: true, - rowCount, - str, - int, - float, - valueKind, - areValuesEqual: TokenColumn.areValuesEqualProvider(tokens), - toStringArray: params => ColumnHelpers.createAndFillArray(rowCount, str, params), - toIntArray: params => ColumnHelpers.createAndFillArray(rowCount, int, params), - toFloatArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params) - } -} - -export function CifTextValueField(values: string[]): Data.CifField { - const rowCount = values.length; - - const str: Data.CifField['str'] = row => { - const ret = values[row]; - if (!ret || ret === '.' || ret === '?') return ''; - return ret; - }; - - const int: Data.CifField['int'] = row => { - const v = values[row]; - return fastParseInt(v, 0, v.length) || 0; - }; - - const float: Data.CifField['float'] = row => { - const v = values[row]; - return fastParseFloat(v, 0, v.length) || 0; - }; - - const valueKind: Data.CifField['valueKind'] = row => { - const v = values[row], l = v.length; - if (l > 1) return Column.ValueKind.Present; - if (l === 0) return Column.ValueKind.NotPresent; - const c = v.charCodeAt(0); - if (c === 46 /* . */) return Column.ValueKind.NotPresent; - if (c === 63 /* ? */) return Column.ValueKind.Unknown; - return Column.ValueKind.Present; - }; - - return { - __array: void 0, - binaryEncoding: void 0, - isDefined: true, - rowCount, - str, - int, - float, - valueKind, - areValuesEqual: (rowA, rowB) => values[rowA] === values[rowB], - toStringArray: params => ColumnHelpers.createAndFillArray(rowCount, str, params), - toIntArray: params => ColumnHelpers.createAndFillArray(rowCount, int, params), - toFloatArray: params => ColumnHelpers.createAndFillArray(rowCount, float, params) - } -} \ No newline at end of file diff --git a/src/mol-io/reader/cif/text/parser.ts b/src/mol-io/reader/cif/text/parser.ts index ae617076ed75406d12b3925be29a381b5ea60c82..920546393c25b5aa585b34d8740d872222e63670 100644 --- a/src/mol-io/reader/cif/text/parser.ts +++ b/src/mol-io/reader/cif/text/parser.ts @@ -23,7 +23,6 @@ */ import * as Data from '../data-model' -import Field from './field' import { Tokens, TokenBuilder } from '../../common/text/tokenizer' import { ReaderResult as Result } from '../../result' import { Task, RuntimeContext, chunkedSubtask } from 'mol-task' @@ -445,7 +444,7 @@ function handleSingle(tokenizer: TokenizerState, ctx: FrameContext): CifCategory errorMessage: 'Expected value.' } } - fields[fieldName] = Field({ data: tokenizer.data, indices: [tokenizer.tokenStart, tokenizer.tokenEnd], count: 1 }, 1); + fields[fieldName] = Data.CifField.ofTokens({ data: tokenizer.data, indices: [tokenizer.tokenStart, tokenizer.tokenEnd], count: 1 }); fieldNames[fieldNames.length] = fieldName; moveNext(tokenizer); } @@ -529,7 +528,7 @@ async function handleLoop(tokenizer: TokenizerState, ctx: FrameContext): Promise const rowCount = (state.tokenCount / fieldCount) | 0; const fields = Object.create(null); for (let i = 0; i < fieldCount; i++) { - fields[fieldNames[i]] = Field(tokens[i], rowCount); + fields[fieldNames[i]] = Data.CifField.ofTokens(tokens[i]); } const catName = name.substr(1); diff --git a/src/mol-io/reader/common/text/tokenizer.ts b/src/mol-io/reader/common/text/tokenizer.ts index 8664601d45a214e2a15e934af85a8584d827444c..fce7c9037cc7c9eb546edae1a12fb4d838b06efa 100644 --- a/src/mol-io/reader/common/text/tokenizer.ts +++ b/src/mol-io/reader/common/text/tokenizer.ts @@ -228,6 +228,7 @@ namespace Tokenizer { state.tokenStart = s; state.tokenEnd = e + 1; state.position = end; + return state; } } @@ -265,6 +266,10 @@ export namespace TokenBuilder { tokens.count++; } + export function addToken(tokens: Tokens, tokenizer: Tokenizer) { + add(tokens, tokenizer.tokenStart, tokenizer.tokenEnd); + } + export function addUnchecked(tokens: Tokens, start: number, end: number) { (tokens as Builder).indices[(tokens as Builder).offset++] = start; (tokens as Builder).indices[(tokens as Builder).offset++] = end; diff --git a/src/mol-io/reader/csv/field.ts b/src/mol-io/reader/csv/field.ts index fdc4c5135d4037d72dbd06385accae9bd805bbfa..48d1f1072aa1a54cc9dd77e94318ecfce6ce14e5 100644 --- a/src/mol-io/reader/csv/field.ts +++ b/src/mol-io/reader/csv/field.ts @@ -4,6 +4,6 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import Field from '../cif/text/field' +import { CifField } from '../cif/data-model'; -export default Field \ No newline at end of file +export default CifField.ofTokens \ No newline at end of file diff --git a/src/mol-io/reader/csv/parser.ts b/src/mol-io/reader/csv/parser.ts index 6b7e14c5fe5e137206b98d5e6a17e0d4091aa7c4..4207202703338f1300bce9ebc0142c8ac6b39294 100644 --- a/src/mol-io/reader/csv/parser.ts +++ b/src/mol-io/reader/csv/parser.ts @@ -254,7 +254,7 @@ async function handleRecords(state: State): Promise<Data.CsvTable> { const columns: Data.CsvColumns = Object.create(null); for (let i = 0; i < state.columnCount; ++i) { - columns[state.columnNames[i]] = Field(state.tokens[i], state.recordCount); + columns[state.columnNames[i]] = Field(state.tokens[i]); } return Data.CsvTable(state.recordCount, state.columnNames, columns) diff --git a/src/mol-io/reader/pdb/to-cif.ts b/src/mol-io/reader/pdb/to-cif.ts deleted file mode 100644 index f206a7bb26744508ee2f5e304ff721917d2b2e49..0000000000000000000000000000000000000000 --- a/src/mol-io/reader/pdb/to-cif.ts +++ /dev/null @@ -1,280 +0,0 @@ -/** - * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import { CifField, CifCategory } from '../cif'; -import { mmCIF_Schema } from '../cif/schema/mmcif'; -import CifTextField, { CifTextValueField } from '../cif/text/field'; -import { TokenBuilder, Tokenizer } from '../common/text/tokenizer'; -import { PdbFile } from './schema'; -import { CifFile } from '../cif/data-model'; -import { substringStartsWith } from 'mol-util/string'; -import { Task } from 'mol-task'; - -function toCategory(name: string, fields: { [name: string]: CifField | undefined }, rowCount: number): CifCategory { - return { - name, - fieldNames: Object.keys(fields), - rowCount, - getField(f: string) { - return fields[f]; - } - } -} - -function _entity(): { [K in keyof mmCIF_Schema['entity']]?: CifField } { - return { - id: CifTextValueField(['1', '2', '3']), - type: CifTextValueField(['polymer', 'non-polymer', 'water']) - } -} - -function atom_site_template(data: string, count: number) { - const str = () => new Array(count) as string[]; - const ts = () => TokenBuilder.create(data, 2 * count); - return { - index: 0, - count, - group_PDB: ts(), - id: str(), - auth_atom_id: ts(), - label_alt_id: ts(), - auth_comp_id: ts(), - auth_asym_id: ts(), - auth_seq_id: ts(), - pdbx_PDB_ins_code: ts(), - Cartn_x: ts(), - Cartn_y: ts(), - Cartn_z: ts(), - occupancy: ts(), - B_iso_or_equiv: ts(), - type_symbol: ts(), - pdbx_PDB_model_num: str(), - label_entity_id: str() - }; -} - -function _atom_site(sites: AtomSiteTemplate): { [K in keyof mmCIF_Schema['atom_site']]?: CifField } { - const auth_asym_id = CifTextField(sites.auth_asym_id, sites.count); - const auth_atom_id = CifTextField(sites.auth_atom_id, sites.count); - const auth_comp_id = CifTextField(sites.auth_comp_id, sites.count); - const auth_seq_id = CifTextField(sites.auth_seq_id, sites.count); - - return { - auth_asym_id, - auth_atom_id, - auth_comp_id, - auth_seq_id, - B_iso_or_equiv: CifTextField(sites.B_iso_or_equiv, sites.count), - Cartn_x: CifTextField(sites.Cartn_x, sites.count), - Cartn_y: CifTextField(sites.Cartn_y, sites.count), - Cartn_z: CifTextField(sites.Cartn_z, sites.count), - group_PDB: CifTextField(sites.group_PDB, sites.count), - id: CifTextValueField(sites.id), - - label_alt_id: CifTextField(sites.label_alt_id, sites.count), - - label_asym_id: auth_asym_id, - label_atom_id: auth_atom_id, - label_comp_id: auth_comp_id, - label_seq_id: auth_seq_id, - label_entity_id: CifTextValueField(sites.label_entity_id), - - occupancy: CifTextField(sites.occupancy, sites.count), - type_symbol: CifTextField(sites.type_symbol, sites.count), - - pdbx_PDB_ins_code: CifTextField(sites.pdbx_PDB_ins_code, sites.count), - pdbx_PDB_model_num: CifTextValueField(sites.pdbx_PDB_model_num) - }; -} - -const WaterNames = new Set([ 'SOL', 'WAT', 'HOH', 'H2O', 'W', 'DOD', 'D3O', 'TIP3', 'TIP4', 'SPC' ]); - -function getEntityId(residueName: string, isHet: boolean) { - if (isHet) { - if (WaterNames.has(residueName)) return '3'; - return '2'; - } - return '1'; -} - -function addAtom(sites: AtomSiteTemplate, model: string, data: Tokenizer, s: number, e: number, isHet: boolean) { - const { data: str } = data; - let startPos = s; - let start = s; - const end = e; - const length = end - start; - - // TODO: filter invalid atoms - - // COLUMNS DATA TYPE CONTENTS - // -------------------------------------------------------------------------------- - // 1 - 6 Record name "ATOM " - Tokenizer.trim(data, start, start + 6); - TokenBuilder.add(sites.group_PDB, data.tokenStart, data.tokenEnd); - - // 7 - 11 Integer Atom serial number. - // TODO: support HEX - start = startPos + 6; - Tokenizer.trim(data, start, start + 5); - sites.id[sites.index] = data.data.substring(data.tokenStart, data.tokenEnd); - - // 13 - 16 Atom Atom name. - start = startPos + 12; - Tokenizer.trim(data, start, start + 4); - TokenBuilder.add(sites.auth_atom_id, data.tokenStart, data.tokenEnd); - - // 17 Character Alternate location indicator. - if (str.charCodeAt(startPos + 16) === 32) { // ' ' - TokenBuilder.add(sites.label_alt_id, 0, 0); - } else { - TokenBuilder.add(sites.label_alt_id, startPos + 16, startPos + 17); - } - - // 18 - 20 Residue name Residue name. - start = startPos + 17; - Tokenizer.trim(data, start, start + 3); - TokenBuilder.add(sites.auth_comp_id, data.tokenStart, data.tokenEnd); - const residueName = str.substring(data.tokenStart, data.tokenEnd); - - // 22 Character Chain identifier. - TokenBuilder.add(sites.auth_asym_id, startPos + 21, startPos + 22); - - // 23 - 26 Integer Residue sequence number. - // TODO: support HEX - start = startPos + 22; - Tokenizer.trim(data, start, start + 4); - TokenBuilder.add(sites.auth_seq_id, data.tokenStart, data.tokenEnd); - - // 27 AChar Code for insertion of residues. - if (str.charCodeAt(startPos + 26) === 32) { // ' ' - TokenBuilder.add(sites.label_alt_id, 0, 0); - } else { - TokenBuilder.add(sites.label_alt_id, startPos + 26, startPos + 27); - } - - // 31 - 38 Real(8.3) Orthogonal coordinates for X in Angstroms. - start = startPos + 30; - Tokenizer.trim(data, start, start + 8); - TokenBuilder.add(sites.Cartn_x, data.tokenStart, data.tokenEnd); - - // 39 - 46 Real(8.3) Orthogonal coordinates for Y in Angstroms. - start = startPos + 38; - Tokenizer.trim(data, start, start + 8); - TokenBuilder.add(sites.Cartn_y, data.tokenStart, data.tokenEnd); - - // 47 - 54 Real(8.3) Orthogonal coordinates for Z in Angstroms. - start = startPos + 46; - Tokenizer.trim(data, start, start + 8); - TokenBuilder.add(sites.Cartn_z, data.tokenStart, data.tokenEnd); - - // 55 - 60 Real(6.2) Occupancy. - start = startPos + 54; - Tokenizer.trim(data, start, start + 6); - TokenBuilder.add(sites.occupancy, data.tokenStart, data.tokenEnd); - - // 61 - 66 Real(6.2) Temperature factor (Default = 0.0). - if (length >= 66) { - start = startPos + 60; - Tokenizer.trim(data, start, start + 6); - TokenBuilder.add(sites.B_iso_or_equiv, data.tokenStart, data.tokenEnd); - } else { - TokenBuilder.add(sites.label_alt_id, 0, 0); - } - - // 73 - 76 LString(4) Segment identifier, left-justified. - // ignored - - // 77 - 78 LString(2) Element symbol, right-justified. - if (length >= 78) { - start = startPos + 76; - Tokenizer.trim(data, start, start + 2); - - if (data.tokenStart < data.tokenEnd) { - TokenBuilder.add(sites.type_symbol, data.tokenStart, data.tokenEnd); - } else { - // "guess" the symbol - TokenBuilder.add(sites.type_symbol, startPos + 12, startPos + 13); - } - } else { - TokenBuilder.add(sites.type_symbol, startPos + 12, startPos + 13); - } - - sites.label_entity_id[sites.index] = getEntityId(residueName, isHet); - sites.pdbx_PDB_model_num[sites.index] = model; - - sites.index++; -} - -type AtomSiteTemplate = typeof atom_site_template extends (...args: any) => infer T ? T : never - -async function pdbToMmCIF(pdb: PdbFile): Promise<CifFile> { - const { lines } = pdb; - const { data, indices } = lines; - const tokenizer = Tokenizer(data); - - // Count the atoms - let atomCount = 0; - for (let i = 0, _i = lines.count; i < _i; i++) { - const s = indices[2 * i], e = indices[2 * i + 1]; - switch (data[s]) { - case 'A': - if (substringStartsWith(data, s, e, 'ATOM ')) atomCount++; - break; - case 'H': - if (substringStartsWith(data, s, e, 'HETATM')) atomCount++; - break; - } - } - - const atom_site = atom_site_template(data, atomCount); - - let modelNum = 0, modelStr = ''; - - for (let i = 0, _i = lines.count; i < _i; i++) { - const s = indices[2 * i], e = indices[2 * i + 1]; - switch (data[s]) { - case 'A': - if (!substringStartsWith(data, s, e, 'ATOM ')) continue; - if (!modelNum) { modelNum++; modelStr = '' + modelNum; } - addAtom(atom_site, modelStr, tokenizer, s, e, false); - break; - case 'H': - if (!substringStartsWith(data, s, e, 'HETATM')) continue; - if (!modelNum) { modelNum++; modelStr = '' + modelNum; } - addAtom(atom_site, modelStr, tokenizer, s, e, true); - break; - case 'M': - if (substringStartsWith(data, s, e, 'MODEL ')) { - modelNum++; - modelStr = '' + modelNum; - } - break; - - } - } - - const categories = { - entity: toCategory('entity', _entity(), 3), - atom_site: toCategory('atom_site', _atom_site(atom_site), atomCount) - } - - return { - name: pdb.id, - blocks: [{ - saveFrames: [], - header: pdb.id || 'PDB', - categoryNames: Object.keys(categories), - categories - }] - }; -} - -export function convertPDBtoMmCif(pdb: PdbFile): Task<CifFile> { - return Task.create('Convert PDB to mmCIF', async ctx => { - await ctx.update('Converting to mmCIF...'); - return pdbToMmCIF(pdb); - }); -} \ No newline at end of file diff --git a/src/mol-math/linear-algebra/3d/mat4.ts b/src/mol-math/linear-algebra/3d/mat4.ts index b18fb3e5cc57036ca1ea9d69a9dba0f5162bf7cb..abf5cfb9660c20a43039510c799baa7e568f88f2 100644 --- a/src/mol-math/linear-algebra/3d/mat4.ts +++ b/src/mol-math/linear-algebra/3d/mat4.ts @@ -119,6 +119,10 @@ namespace Mat4 { a[4 * j + i] = value; } + export function getValue(a: Mat4, i: number, j: number) { + return a[4 * j + i]; + } + export function toArray(a: Mat4, out: NumberArray, offset: number) { out[offset + 0] = a[0]; out[offset + 1] = a[1]; diff --git a/src/mol-model-formats/structure/format.ts b/src/mol-model-formats/structure/format.ts new file mode 100644 index 0000000000000000000000000000000000000000..d8ecc3d822830da08eb6718c50166457d89d644a --- /dev/null +++ b/src/mol-model-formats/structure/format.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif'; +import CIF, { CifFrame } from 'mol-io/reader/cif'; + +type ModelFormat = + | ModelFormat.mmCIF + +namespace ModelFormat { + export interface mmCIF { kind: 'mmCIF', data: mmCIF_Database, frame: CifFrame } + export function mmCIF(frame: CifFrame, data?: mmCIF_Database): mmCIF { return { kind: 'mmCIF', data: data || CIF.schema.mmCIF(frame), frame }; } +} + +export { ModelFormat } \ No newline at end of file diff --git a/src/mol-model/structure/model/formats/gro.ts b/src/mol-model-formats/structure/gro.ts similarity index 100% rename from src/mol-model/structure/model/formats/gro.ts rename to src/mol-model-formats/structure/gro.ts diff --git a/src/mol-model-formats/structure/mmcif.ts b/src/mol-model-formats/structure/mmcif.ts new file mode 100644 index 0000000000000000000000000000000000000000..6eecc06b6b3e94ae8e5da0aa09141a4582fdb15c --- /dev/null +++ b/src/mol-model-formats/structure/mmcif.ts @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2017-2018 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 { 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'; + +export function trajectoryFromMmCIF(frame: CifFrame): Task<Model.Trajectory> { + return Task.create('Create mmCIF Model', ctx => _parse_mmCif(ModelFormat.mmCIF(frame), ctx)); +} \ No newline at end of file diff --git a/src/mol-model/structure/model/formats/mmcif/assembly.ts b/src/mol-model-formats/structure/mmcif/assembly.ts similarity index 94% rename from src/mol-model/structure/model/formats/mmcif/assembly.ts rename to src/mol-model-formats/structure/mmcif/assembly.ts index d0c9eddeac44e99333be0e4f0b77c14d189cfe56..ad0fa39b9b9cf687478b2d6b5fc6730b4ea5bc03 100644 --- a/src/mol-model/structure/model/formats/mmcif/assembly.ts +++ b/src/mol-model-formats/structure/mmcif/assembly.ts @@ -6,12 +6,11 @@ import { Mat4, Tensor } from 'mol-math/linear-algebra' import { SymmetryOperator } from 'mol-math/geometry/symmetry-operator' -import Format from '../../format' -import { Assembly, OperatorGroup, OperatorGroups } from '../../properties/symmetry' -import { Queries as Q } from '../../../query' - -import mmCIF_Format = Format.mmCIF -import { StructureProperties } from '../../../structure'; +import { Assembly, OperatorGroup, OperatorGroups } from 'mol-model/structure/model/properties/symmetry' +import { Queries as Q } from 'mol-model/structure' +import { StructureProperties } from 'mol-model/structure'; +import { ModelFormat } from '../format'; +import mmCIF_Format = ModelFormat.mmCIF export function createAssemblies(format: mmCIF_Format): ReadonlyArray<Assembly> { const { pdbx_struct_assembly } = format.data; diff --git a/src/mol-model/structure/model/formats/mmcif/atomic.ts b/src/mol-model-formats/structure/mmcif/atomic.ts similarity index 86% rename from src/mol-model/structure/model/formats/mmcif/atomic.ts rename to src/mol-model-formats/structure/mmcif/atomic.ts index 8ae819b489f88f991e96dac5701c74f9699e8004..7c4fbfbbfba1b8a266302d7bfbb2627186153ead 100644 --- a/src/mol-model/structure/model/formats/mmcif/atomic.ts +++ b/src/mol-model-formats/structure/mmcif/atomic.ts @@ -8,18 +8,18 @@ import { Column, Table } from 'mol-data/db'; import { Interval, Segmentation } from 'mol-data/int'; import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif'; import UUID from 'mol-util/uuid'; -import { ElementIndex } from '../../../../structure'; -import Format from '../../format'; -import { Model } from '../../model'; -import { AtomicConformation, AtomicData, AtomicHierarchy, AtomicSegments, AtomsSchema, ChainsSchema, ResiduesSchema } from '../../properties/atomic'; -import { getAtomicIndex } from '../../properties/utils/atomic-index'; -import { ElementSymbol } from '../../types'; -import { Entities } from '../../properties/common'; - -import mmCIF_Format = Format.mmCIF -import { getAtomicRanges } from '../../properties/utils/atomic-ranges'; -import { FormatData } from '../mmcif'; -import { getAtomicDerivedData } from '../../properties/utils/atomic-derived'; +import { ElementIndex } from 'mol-model/structure'; +import { Model } from 'mol-model/structure/model/model'; +import { AtomicConformation, AtomicData, AtomicHierarchy, AtomicSegments, AtomsSchema, ChainsSchema, ResiduesSchema } from 'mol-model/structure/model/properties/atomic'; +import { getAtomicIndex } from 'mol-model/structure/model/properties/utils/atomic-index'; +import { ElementSymbol } from 'mol-model/structure/model/types'; +import { Entities } from 'mol-model/structure/model/properties/common'; +import { getAtomicRanges } from 'mol-model/structure/model/properties/utils/atomic-ranges'; +import { getAtomicDerivedData } from 'mol-model/structure/model/properties/utils/atomic-derived'; +import { ModelFormat } from '../format'; +import mmCIF_Format = ModelFormat.mmCIF +import { FormatData } from './parser'; + type AtomSite = mmCIF_Database['atom_site'] diff --git a/src/mol-model/structure/model/formats/mmcif/bonds.ts b/src/mol-model-formats/structure/mmcif/bonds.ts similarity index 100% rename from src/mol-model/structure/model/formats/mmcif/bonds.ts rename to src/mol-model-formats/structure/mmcif/bonds.ts diff --git a/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts b/src/mol-model-formats/structure/mmcif/bonds/comp.ts similarity index 96% rename from src/mol-model/structure/model/formats/mmcif/bonds/comp.ts rename to src/mol-model-formats/structure/mmcif/bonds/comp.ts index 4304807338aded73a011f2d8bbf8100fd276cf18..eceb64760607d51d544a8a9a587a46471bd9334d 100644 --- a/src/mol-model/structure/model/formats/mmcif/bonds/comp.ts +++ b/src/mol-model-formats/structure/mmcif/bonds/comp.ts @@ -5,11 +5,11 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Model } from '../../../model' -import { LinkType } from '../../../types' -import { ModelPropertyDescriptor } from '../../../properties/custom'; +import { Model } from 'mol-model/structure/model/model' +import { LinkType } from 'mol-model/structure/model/types' +import { ModelPropertyDescriptor } from 'mol-model/structure/model/properties/custom'; import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif'; -import { Structure, Unit, StructureProperties, StructureElement } from '../../../../structure'; +import { Structure, Unit, StructureProperties, StructureElement } from 'mol-model/structure'; import { Segmentation } from 'mol-data/int'; import { CifWriter } from 'mol-io/writer/cif' diff --git a/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts b/src/mol-model-formats/structure/mmcif/bonds/struct_conn.ts similarity index 96% rename from src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts rename to src/mol-model-formats/structure/mmcif/bonds/struct_conn.ts index 486c071a1de7c2bc4f5f9b1e144add4b9187512c..21a5b9e57f6b076c6b7184738dee962ecd58164d 100644 --- a/src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts +++ b/src/mol-model-formats/structure/mmcif/bonds/struct_conn.ts @@ -5,16 +5,16 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Model } from '../../../model' -import { Structure } from '../../../../structure' -import { LinkType } from '../../../types' +import { Model } from 'mol-model/structure/model/model' +import { Structure } from 'mol-model/structure' +import { LinkType } from 'mol-model/structure/model/types' import { findEntityIdByAsymId, findAtomIndexByLabelName } from '../util' import { Column } from 'mol-data/db' -import { ModelPropertyDescriptor } from '../../../properties/custom'; +import { ModelPropertyDescriptor } from 'mol-model/structure/model/properties/custom'; import { mmCIF_Database, mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif'; import { SortedArray } from 'mol-data/int'; import { CifWriter } from 'mol-io/writer/cif' -import { ElementIndex, ResidueIndex } from '../../../indexing'; +import { ElementIndex, ResidueIndex } from 'mol-model/structure/model/indexing'; export interface StructConn { getResidueEntries(residueAIndex: ResidueIndex, residueBIndex: ResidueIndex): ReadonlyArray<StructConn.Entry>, diff --git a/src/mol-model/structure/model/formats/mmcif/ihm.ts b/src/mol-model-formats/structure/mmcif/ihm.ts similarity index 89% rename from src/mol-model/structure/model/formats/mmcif/ihm.ts rename to src/mol-model-formats/structure/mmcif/ihm.ts index 8cba685fb7c13ecaed3ca2c39cb91851b0d81534..731af9e3af83de9784c51bba90da32d0c15de777 100644 --- a/src/mol-model/structure/model/formats/mmcif/ihm.ts +++ b/src/mol-model-formats/structure/mmcif/ihm.ts @@ -5,16 +5,16 @@ */ import { mmCIF_Database as mmCIF, mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif' -import { CoarseHierarchy, CoarseConformation, CoarseElementData, CoarseSphereConformation, CoarseGaussianConformation } from '../../properties/coarse' -import { Entities } from '../../properties/common'; +import { CoarseHierarchy, CoarseConformation, CoarseElementData, CoarseSphereConformation, CoarseGaussianConformation } from 'mol-model/structure/model/properties/coarse' +import { Entities } from 'mol-model/structure/model/properties/common'; import { Column } from 'mol-data/db'; -import { getCoarseKeys } from '../../properties/utils/coarse-keys'; +import { getCoarseKeys } from 'mol-model/structure/model/properties/utils/coarse-keys'; import { UUID } from 'mol-util'; import { Segmentation, Interval } from 'mol-data/int'; import { Mat3, Tensor } from 'mol-math/linear-algebra'; -import { ElementIndex, ChainIndex } from '../../indexing'; -import { getCoarseRanges } from '../../properties/utils/coarse-ranges'; -import { FormatData } from '../mmcif'; +import { ElementIndex, ChainIndex } from 'mol-model/structure/model/indexing'; +import { getCoarseRanges } from 'mol-model/structure/model/properties/utils/coarse-ranges'; +import { FormatData } from './parser'; export interface IHMData { model_id: number, diff --git a/src/mol-model/structure/model/formats/mmcif/pair-restraint.ts b/src/mol-model-formats/structure/mmcif/pair-restraint.ts similarity index 100% rename from src/mol-model/structure/model/formats/mmcif/pair-restraint.ts rename to src/mol-model-formats/structure/mmcif/pair-restraint.ts diff --git a/src/mol-model/structure/model/formats/mmcif/pair-restraints/cross-links.ts b/src/mol-model-formats/structure/mmcif/pair-restraints/cross-links.ts similarity index 96% rename from src/mol-model/structure/model/formats/mmcif/pair-restraints/cross-links.ts rename to src/mol-model-formats/structure/mmcif/pair-restraints/cross-links.ts index 0aebfc74fe1146a789d35b54804064dc5c3acf66..2964745e9b5723e3621f97551592d9961e1277df 100644 --- a/src/mol-model/structure/model/formats/mmcif/pair-restraints/cross-links.ts +++ b/src/mol-model-formats/structure/mmcif/pair-restraints/cross-links.ts @@ -4,12 +4,12 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Model } from '../../../model' +import { Model } from 'mol-model/structure/model/model' import { Table } from 'mol-data/db' import { mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif'; import { findAtomIndexByLabelName } from '../util'; -import { Unit } from '../../../../structure'; -import { ElementIndex } from '../../../indexing'; +import { Unit } from 'mol-model/structure'; +import { ElementIndex } from 'mol-model/structure/model/indexing'; function findAtomIndex(model: Model, entityId: string, asymId: string, seqId: number, atomId: string) { if (!model.atomicHierarchy.atoms.auth_atom_id.isDefined) return -1 diff --git a/src/mol-model/structure/model/formats/mmcif/pair-restraints/predicted-contacts.ts b/src/mol-model-formats/structure/mmcif/pair-restraints/predicted-contacts.ts similarity index 100% rename from src/mol-model/structure/model/formats/mmcif/pair-restraints/predicted-contacts.ts rename to src/mol-model-formats/structure/mmcif/pair-restraints/predicted-contacts.ts diff --git a/src/mol-model/structure/model/formats/mmcif.ts b/src/mol-model-formats/structure/mmcif/parser.ts similarity index 90% rename from src/mol-model/structure/model/formats/mmcif.ts rename to src/mol-model-formats/structure/mmcif/parser.ts index aa0e73a544466f23c428e0b749884b445454788c..5d435458b246b19a6efc80e5d43705c89e22a1c6 100644 --- a/src/mol-model/structure/model/formats/mmcif.ts +++ b/src/mol-model-formats/structure/mmcif/parser.ts @@ -9,26 +9,31 @@ import { Column, Table } from 'mol-data/db'; import { mmCIF_Database, mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif'; import { Spacegroup, SpacegroupCell, SymmetryOperator } from 'mol-math/geometry'; import { Tensor, Vec3 } from 'mol-math/linear-algebra'; -import { Task, RuntimeContext } from 'mol-task'; +import { RuntimeContext } from 'mol-task'; import UUID from 'mol-util/uuid'; -import Format from '../format'; -import { Model } from '../model'; -import { Entities } from '../properties/common'; -import { CustomProperties } from '../properties/custom'; -import { ModelSymmetry } from '../properties/symmetry'; -import { createAssemblies } from './mmcif/assembly'; -import { getAtomicHierarchyAndConformation } from './mmcif/atomic'; -import { ComponentBond } from './mmcif/bonds'; -import { getIHMCoarse, EmptyIHMCoarse, IHMData } from './mmcif/ihm'; -import { getSecondaryStructureMmCif } from './mmcif/secondary-structure'; -import { getSequence } from './mmcif/sequence'; -import { sortAtomSite } from './mmcif/sort'; -import { StructConn } from './mmcif/bonds/struct_conn'; -import { ChemicalComponent, ChemicalComponentMap, CommonChemicalComponentMap } from '../properties/chemical-component'; -import { ComponentType, getMoleculeType, MoleculeType } from '../types'; - -import mmCIF_Format = Format.mmCIF +import { Model } from 'mol-model/structure/model/model'; +import { Entities } from 'mol-model/structure/model/properties/common'; +import { CustomProperties } from 'mol-model/structure/model/properties/custom'; +import { ModelSymmetry } from 'mol-model/structure/model/properties/symmetry'; +import { createAssemblies } from './assembly'; +import { getAtomicHierarchyAndConformation } from './atomic'; +import { ComponentBond } from './bonds'; +import { getIHMCoarse, EmptyIHMCoarse, IHMData } from './ihm'; +import { getSecondaryStructureMmCif } from './secondary-structure'; +import { getSequence } from './sequence'; +import { sortAtomSite } from './sort'; +import { StructConn } from './bonds/struct_conn'; +import { ChemicalComponent, ChemicalComponentMap, CommonChemicalComponentMap } from 'mol-model/structure/model/properties/chemical-component'; +import { ComponentType, getMoleculeType, MoleculeType } from 'mol-model/structure/model/types'; +import { ModelFormat } from '../format'; import { SaccharideComponentMap, SaccharideComponent, SaccharidesSnfgMap, SaccharideCompIdMap, UnknownSaccharideComponent } from 'mol-model/structure/structure/carbohydrates/constants'; +import mmCIF_Format = ModelFormat.mmCIF + +export async function _parse_mmCif(format: mmCIF_Format, ctx: RuntimeContext) { + const formatData = getFormatData(format) + const isIHM = format.data.ihm_model_list._rowCount > 0; + return isIHM ? await readIHM(ctx, format, formatData) : await readStandard(ctx, format, formatData); +} type AtomSite = mmCIF_Database['atom_site'] @@ -303,14 +308,4 @@ async function readIHM(ctx: RuntimeContext, format: mmCIF_Format, formatData: Fo } return models; -} - -function buildModels(format: mmCIF_Format): Task<ReadonlyArray<Model>> { - const formatData = getFormatData(format) - return Task.create('Create mmCIF Model', async ctx => { - const isIHM = format.data.ihm_model_list._rowCount > 0; - return isIHM ? await readIHM(ctx, format, formatData) : await readStandard(ctx, format, formatData); - }); -} - -export default buildModels; \ No newline at end of file +} \ No newline at end of file diff --git a/src/mol-model/structure/model/formats/mmcif/secondary-structure.ts b/src/mol-model-formats/structure/mmcif/secondary-structure.ts similarity index 95% rename from src/mol-model/structure/model/formats/mmcif/secondary-structure.ts rename to src/mol-model-formats/structure/mmcif/secondary-structure.ts index d871052c96890e86bdd7c0b241755a944d2e5b20..3c0def78910279781a359199a4a06a0e841dad20 100644 --- a/src/mol-model/structure/model/formats/mmcif/secondary-structure.ts +++ b/src/mol-model-formats/structure/mmcif/secondary-structure.ts @@ -6,11 +6,11 @@ */ import { mmCIF_Database as mmCIF, mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif' -import { SecondaryStructureType } from '../../types'; -import { AtomicHierarchy } from '../../properties/atomic'; -import { SecondaryStructure } from '../../properties/seconday-structure'; +import { SecondaryStructureType } from 'mol-model/structure/model/types'; +import { AtomicHierarchy } from 'mol-model/structure/model/properties/atomic'; +import { SecondaryStructure } from 'mol-model/structure/model/properties/seconday-structure'; import { Column } from 'mol-data/db'; -import { ChainIndex, ResidueIndex } from '../../indexing'; +import { ChainIndex, ResidueIndex } from 'mol-model/structure/model/indexing'; export function getSecondaryStructureMmCif(data: mmCIF_Database, hierarchy: AtomicHierarchy): SecondaryStructure { const map: SecondaryStructureMap = new Map(); diff --git a/src/mol-model/structure/model/formats/mmcif/sequence.ts b/src/mol-model-formats/structure/mmcif/sequence.ts similarity index 87% rename from src/mol-model/structure/model/formats/mmcif/sequence.ts rename to src/mol-model-formats/structure/mmcif/sequence.ts index 70caf352fd19e0ffa1177dc0615c90b825f72417..9915651b634980c5c522935caa45eabc3c702aee 100644 --- a/src/mol-model/structure/model/formats/mmcif/sequence.ts +++ b/src/mol-model-formats/structure/mmcif/sequence.ts @@ -5,11 +5,11 @@ */ import { mmCIF_Database as mmCIF } from 'mol-io/reader/cif/schema/mmcif' -import StructureSequence from '../../properties/sequence' +import StructureSequence from 'mol-model/structure/model/properties/sequence' import { Column } from 'mol-data/db'; -import { AtomicHierarchy } from '../../properties/atomic'; -import { Entities } from '../../properties/common'; -import { Sequence } from '../../../../sequence'; +import { AtomicHierarchy } from 'mol-model/structure/model/properties/atomic'; +import { Entities } from 'mol-model/structure/model/properties/common'; +import { Sequence } from 'mol-model/sequence'; // TODO how to handle microheterogeneity // see http://mmcif.wwpdb.org/dictionaries/mmcif_pdbx_v50.dic/Categories/entity_poly_seq.html diff --git a/src/mol-model/structure/model/formats/mmcif/sort.ts b/src/mol-model-formats/structure/mmcif/sort.ts similarity index 100% rename from src/mol-model/structure/model/formats/mmcif/sort.ts rename to src/mol-model-formats/structure/mmcif/sort.ts diff --git a/src/mol-model/structure/model/formats/mmcif/util.ts b/src/mol-model-formats/structure/mmcif/util.ts similarity index 89% rename from src/mol-model/structure/model/formats/mmcif/util.ts rename to src/mol-model-formats/structure/mmcif/util.ts index 044dee3a6bc4aba02a65e9d047ec0c3e7afe2188..1f398a11bee1cfd425447854ecc9e0bc36bfe71a 100644 --- a/src/mol-model/structure/model/formats/mmcif/util.ts +++ b/src/mol-model-formats/structure/mmcif/util.ts @@ -4,8 +4,8 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { Model } from '../../model' -import { ElementIndex } from '../../indexing'; +import { Model } from 'mol-model/structure/model' +import { ElementIndex } from 'mol-model/structure/model/indexing'; export function findEntityIdByAsymId(model: Model, asymId: string) { if (model.sourceData.kind !== 'mmCIF') return '' diff --git a/src/mol-model-formats/structure/pdb.ts b/src/mol-model-formats/structure/pdb.ts new file mode 100644 index 0000000000000000000000000000000000000000..a8695876900d671bf514d1c8ec01872fdcfbae31 --- /dev/null +++ b/src/mol-model-formats/structure/pdb.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +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'; + +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); + }) +} diff --git a/src/mol-model-formats/structure/pdb/assembly.ts b/src/mol-model-formats/structure/pdb/assembly.ts new file mode 100644 index 0000000000000000000000000000000000000000..7ca740d902425a06c1fe8286592109e43de73338 --- /dev/null +++ b/src/mol-model-formats/structure/pdb/assembly.ts @@ -0,0 +1,230 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { CifCategory, CifField } from 'mol-io/reader/cif'; +import { mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif'; +import { Mat4 } from 'mol-math/linear-algebra'; +import { Tokens } from 'mol-io/reader/common/text/tokenizer'; + +export function parseCryst1(id: string, record: string): CifCategory[] { + // COLUMNS DATA TYPE CONTENTS + // -------------------------------------------------------------------------------- + // 1 - 6 Record name "CRYST1" + // 7 - 15 Real(9.3) a (Angstroms) + // 16 - 24 Real(9.3) b (Angstroms) + // 25 - 33 Real(9.3) c (Angstroms) + // 34 - 40 Real(7.2) alpha (degrees) + // 41 - 47 Real(7.2) beta (degrees) + // 48 - 54 Real(7.2) gamma (degrees) + // 56 - 66 LString Space group + // 67 - 70 Integer Z value + + const get = (s: number, l: number) => (record.substr(s, l) || '').trim() + + const cell: CifCategory.Fields<mmCIF_Schema['cell']> = { + entry_id: CifField.ofString(id), + length_a: CifField.ofString(get(6, 9)), + length_b: CifField.ofString(get(15, 9)), + length_c: CifField.ofString(get(24, 9)), + angle_alpha: CifField.ofString(get(33, 7)), + angle_beta: CifField.ofString(get(40, 7)), + angle_gamma: CifField.ofString(get(47, 7)), + Z_PDB: CifField.ofString(get(66, 4)), + pdbx_unique_axis: CifField.ofString('?') + }; + const symmetry: CifCategory.Fields<mmCIF_Schema['symmetry']> = { + entry_id: CifField.ofString(id), + 'space_group_name_H-M': CifField.ofString(get(55, 11)), + Int_Tables_number: CifField.ofString('?'), + cell_setting: CifField.ofString('?'), + space_group_name_Hall: CifField.ofString('?') + } + return [CifCategory.ofFields('cell', cell), CifCategory.ofFields('symmetry', symmetry)]; +} + +interface PdbAssembly { + id: string, + details: string, + groups: { chains: string[], operators: { id: number, matrix: Mat4 }[] }[] +} + +function PdbAssembly(id: string, details: string): PdbAssembly { + return { id, details, groups: [] }; +} + +export function parseRemark350(lines: Tokens, lineStart: number, lineEnd: number): CifCategory[] { + const assemblies: PdbAssembly[] = []; + + // Read the assemblies + let current: PdbAssembly, group: PdbAssembly['groups'][0], matrix: Mat4, operId = 1; + const getLine = (n: number) => lines.data.substring(lines.indices[2 * n], lines.indices[2 * n + 1]); + for (let i = lineStart; i < lineEnd; i++) { + let line = getLine(i); + if (line.substr(11, 12) === 'BIOMOLECULE:') { + const id = line.substr(23).trim(); + line = getLine(++i); + const details = line.substr(11).trim(); + current = PdbAssembly(id, details); + assemblies.push(current); + } else if (line.substr(13, 5) === 'BIOMT') { + const biomt = line.split(/\s+/) + const row = parseInt(line[18]) - 1 + + if (row === 0) { + matrix = Mat4.identity(); + group!.operators.push({ id: operId++, matrix }); + } + + Mat4.setValue(matrix!, row, 0, parseFloat(biomt[4])); + Mat4.setValue(matrix!, row, 1, parseFloat(biomt[5])); + Mat4.setValue(matrix!, row, 2, parseFloat(biomt[6])); + Mat4.setValue(matrix!, row, 3, parseFloat(biomt[7])); + } else if ( + line.substr(11, 30) === 'APPLY THE FOLLOWING TO CHAINS:' || + line.substr(11, 30) === ' AND CHAINS:') { + + if (line.substr(11, 5) === 'APPLY') { + group = { chains: [], operators: [] }; + current!.groups.push(group); + } + + const chainList = line.substr(41, 30).split(','); + for (let j = 0, jl = chainList.length; j < jl; ++j) { + const c = chainList[j].trim(); + if (c) group!.chains.push(c); + } + } + } + + if (assemblies.length === 0) return []; + + // Generate CIF + + // pdbx_struct_assembly + const pdbx_struct_assembly: CifCategory.SomeFields<mmCIF_Schema['pdbx_struct_assembly']> = { + id: CifField.ofStrings(assemblies.map(a => a.id)), + details: CifField.ofStrings(assemblies.map(a => a.details)) + }; + + + // pdbx_struct_assembly_gen + const pdbx_struct_assembly_gen_rows: { [P in keyof CifCategory.Fields<mmCIF_Schema['pdbx_struct_assembly_gen']>]: string }[] = []; + for (const asm of assemblies) { + for (const group of asm.groups) { + pdbx_struct_assembly_gen_rows.push({ + assembly_id: asm.id, + oper_expression: group.operators.map(o => o.id).join(','), + asym_id_list: group.chains.join(',') + }); + } + } + const pdbx_struct_assembly_gen: CifCategory.Fields<mmCIF_Schema['pdbx_struct_assembly_gen']> = { + assembly_id: CifField.ofStrings(pdbx_struct_assembly_gen_rows.map(r => r.assembly_id)), + oper_expression: CifField.ofStrings(pdbx_struct_assembly_gen_rows.map(r => r.oper_expression)), + asym_id_list: CifField.ofStrings(pdbx_struct_assembly_gen_rows.map(r => r.asym_id_list)) + }; + + // pdbx_struct_oper_list + const pdbx_struct_oper_list_rows: { [P in keyof CifCategory.Fields<mmCIF_Schema['pdbx_struct_oper_list']>]?: string }[] = []; + for (const asm of assemblies) { + for (const group of asm.groups) { + for (const oper of group.operators) { + const row = { + id: '' + oper.id, + type: '?', + name: '?', + symmetry_operation: '?' + } as (typeof pdbx_struct_oper_list_rows)[0] as any; + for (let i = 0; i < 3; i++) { + for (let j = 0; j < 3; j++) { + row[`matrix[${i + 1}][${j + 1}]`] = '' + Mat4.getValue(oper.matrix, i, j); + } + row[`vector[${i + 1}]`] = '' + Mat4.getValue(oper.matrix, i, 3); + } + pdbx_struct_oper_list_rows.push(row); + } + } + } + + const pdbx_struct_oper_list: CifCategory.SomeFields<mmCIF_Schema['pdbx_struct_oper_list']> = { + id: CifField.ofStrings(pdbx_struct_oper_list_rows.map(r => r.id!)), + type: CifField.ofStrings(pdbx_struct_oper_list_rows.map(r => r.type!)), + name: CifField.ofStrings(pdbx_struct_oper_list_rows.map(r => r.name!)), + symmetry_operation: CifField.ofStrings(pdbx_struct_oper_list_rows.map(r => r.symmetry_operation!)) + }; + for (let i = 0; i < 3; i++) { + for (let j = 0; j < 3; j++) { + const k = `matrix[${i + 1}][${j + 1}]`; + (pdbx_struct_oper_list as any)[k] = CifField.ofStrings(pdbx_struct_oper_list_rows.map(r => (r as any)[k]!)); + } + const k = `vector[${i + 1}]`; + (pdbx_struct_oper_list as any)[k] = CifField.ofStrings(pdbx_struct_oper_list_rows.map(r => (r as any)[k]!)); + } + + return [ + CifCategory.ofFields('pdbx_struct_assembly', pdbx_struct_assembly), + CifCategory.ofFields('pdbx_struct_assembly_gen', pdbx_struct_assembly_gen), + CifCategory.ofFields('pdbx_struct_oper_list', pdbx_struct_oper_list) + ]; +} + +export function parseMtrix(lines: Tokens, lineStart: number, lineEnd: number): CifCategory[] { + const matrices: Mat4[] = []; + let matrix: Mat4; + + const getLine = (n: number) => lines.data.substring(lines.indices[2 * n], lines.indices[2 * n + 1]); + for (let i = lineStart; i < lineEnd; i++) { + let line = getLine(i); + + const ncs = line.split(/\s+/); + const row = parseInt(line[5]) - 1; + + if (row === 0) { + matrix = Mat4.identity(); + matrices.push(matrix); + } + + Mat4.setValue(matrix!, row, 0, parseFloat(ncs[2])); + Mat4.setValue(matrix!, row, 1, parseFloat(ncs[3])); + Mat4.setValue(matrix!, row, 2, parseFloat(ncs[4])); + Mat4.setValue(matrix!, row, 3, parseFloat(ncs[5])); + } + + if (matrices.length === 0) return []; + + const struct_ncs_oper_rows: { [P in keyof CifCategory.Fields<mmCIF_Schema['struct_ncs_oper']>]?: string }[] = []; + let id = 1; + for (const oper of matrices) { + const row = { + id: 'ncsop' + (id++), + code: '.', + details: '.' + } as (typeof struct_ncs_oper_rows)[0] as any; + for (let i = 0; i < 3; i++) { + for (let j = 0; j < 3; j++) { + row[`matrix[${i + 1}][${j + 1}]`] = '' + Mat4.getValue(oper, i, j); + } + row[`vector[${i + 1}]`] = '' + Mat4.getValue(oper, i, 3); + } + struct_ncs_oper_rows.push(row); + } + + const struct_ncs_oper: CifCategory.SomeFields<mmCIF_Schema['struct_ncs_oper']> = { + id: CifField.ofStrings(struct_ncs_oper_rows.map(r => r.id!)), + code: CifField.ofStrings(struct_ncs_oper_rows.map(r => r.code!)), + details: CifField.ofStrings(struct_ncs_oper_rows.map(r => r.details!)), + }; + for (let i = 0; i < 3; i++) { + for (let j = 0; j < 3; j++) { + const k = `matrix[${i + 1}][${j + 1}]`; + (struct_ncs_oper as any)[k] = CifField.ofStrings(struct_ncs_oper_rows.map(r => (r as any)[k]!)); + } + const k = `vector[${i + 1}]`; + (struct_ncs_oper as any)[k] = CifField.ofStrings(struct_ncs_oper_rows.map(r => (r as any)[k]!)); + } + + return [CifCategory.ofFields('struct_ncs_oper', struct_ncs_oper)]; +} \ No newline at end of file diff --git a/src/mol-model-formats/structure/pdb/to-cif.ts b/src/mol-model-formats/structure/pdb/to-cif.ts new file mode 100644 index 0000000000000000000000000000000000000000..b5963db10ee48ad8212dcee2aab90ecb3429126a --- /dev/null +++ b/src/mol-model-formats/structure/pdb/to-cif.ts @@ -0,0 +1,275 @@ +/** + * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { substringStartsWith } from 'mol-util/string'; +import { CifField, CifCategory, CifFrame } from 'mol-io/reader/cif'; +import { mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif'; +import { TokenBuilder, Tokenizer } from 'mol-io/reader/common/text/tokenizer'; +import { PdbFile } from 'mol-io/reader/pdb/schema'; +import { parseCryst1, parseRemark350, parseMtrix } from './assembly'; + +function _entity(): { [K in keyof mmCIF_Schema['entity']]?: CifField } { + return { + id: CifField.ofStrings(['1', '2', '3']), + type: CifField.ofStrings(['polymer', 'non-polymer', 'water']) + } +} + +type AtomSiteTemplate = typeof atom_site_template extends (...args: any) => infer T ? T : never +function atom_site_template(data: string, count: number) { + const str = () => [] as string[]; + const ts = () => TokenBuilder.create(data, 2 * count); + return { + index: 0, + count, + group_PDB: ts(), + id: str(), + auth_atom_id: ts(), + label_alt_id: ts(), + auth_comp_id: ts(), + auth_asym_id: ts(), + auth_seq_id: ts(), + pdbx_PDB_ins_code: ts(), + Cartn_x: ts(), + Cartn_y: ts(), + Cartn_z: ts(), + occupancy: ts(), + B_iso_or_equiv: ts(), + type_symbol: ts(), + pdbx_PDB_model_num: str(), + label_entity_id: str() + }; +} + +function _atom_site(sites: AtomSiteTemplate): { [K in keyof mmCIF_Schema['atom_site']]?: CifField } { + const auth_asym_id = CifField.ofTokens(sites.auth_asym_id); + const auth_atom_id = CifField.ofTokens(sites.auth_atom_id); + const auth_comp_id = CifField.ofTokens(sites.auth_comp_id); + const auth_seq_id = CifField.ofTokens(sites.auth_seq_id); + + return { + auth_asym_id, + auth_atom_id, + auth_comp_id, + auth_seq_id, + B_iso_or_equiv: CifField.ofTokens(sites.B_iso_or_equiv), + Cartn_x: CifField.ofTokens(sites.Cartn_x), + Cartn_y: CifField.ofTokens(sites.Cartn_y), + Cartn_z: CifField.ofTokens(sites.Cartn_z), + group_PDB: CifField.ofTokens(sites.group_PDB), + id: CifField.ofStrings(sites.id), + + label_alt_id: CifField.ofTokens(sites.label_alt_id), + + label_asym_id: auth_asym_id, + label_atom_id: auth_atom_id, + label_comp_id: auth_comp_id, + label_seq_id: auth_seq_id, + label_entity_id: CifField.ofStrings(sites.label_entity_id), + + occupancy: CifField.ofTokens(sites.occupancy), + type_symbol: CifField.ofTokens(sites.type_symbol), + + pdbx_PDB_ins_code: CifField.ofTokens(sites.pdbx_PDB_ins_code), + pdbx_PDB_model_num: CifField.ofStrings(sites.pdbx_PDB_model_num) + }; +} + +const WaterNames = new Set(['SOL', 'WAT', 'HOH', 'H2O', 'W', 'DOD', 'D3O', 'TIP3', 'TIP4', 'SPC']); + +function getEntityId(residueName: string, isHet: boolean) { + if (isHet) { + if (WaterNames.has(residueName)) return '3'; + return '2'; + } + return '1'; +} + +function addAtom(sites: AtomSiteTemplate, model: string, data: Tokenizer, s: number, e: number, isHet: boolean) { + const { data: str } = data; + const length = e - s; + + // TODO: filter invalid atoms + + // COLUMNS DATA TYPE CONTENTS + // -------------------------------------------------------------------------------- + // 1 - 6 Record name "ATOM " + TokenBuilder.addToken(sites.group_PDB, Tokenizer.trim(data, s, s + 6)); + + // 7 - 11 Integer Atom serial number. + // TODO: support HEX + Tokenizer.trim(data, s + 6, s + 11); + sites.id[sites.index] = data.data.substring(data.tokenStart, data.tokenEnd); + + // 13 - 16 Atom Atom name. + TokenBuilder.addToken(sites.auth_atom_id, Tokenizer.trim(data, s + 12, s + 16)); + + // 17 Character Alternate location indicator. + if (str.charCodeAt(s + 16) === 32) { // ' ' + TokenBuilder.add(sites.label_alt_id, 0, 0); + } else { + TokenBuilder.add(sites.label_alt_id, s + 16, s + 17); + } + + // 18 - 20 Residue name Residue name. + TokenBuilder.addToken(sites.auth_comp_id, Tokenizer.trim(data, s + 17, s + 20)); + const residueName = str.substring(data.tokenStart, data.tokenEnd); + + // 22 Character Chain identifier. + TokenBuilder.add(sites.auth_asym_id, s + 21, s + 22); + + // 23 - 26 Integer Residue sequence number. + // TODO: support HEX + TokenBuilder.addToken(sites.auth_seq_id, Tokenizer.trim(data, s + 22, s + 26)); + + // 27 AChar Code for insertion of residues. + if (str.charCodeAt(s + 26) === 32) { // ' ' + TokenBuilder.add(sites.label_alt_id, 0, 0); + } else { + TokenBuilder.add(sites.label_alt_id, s + 26, s + 27); + } + + // 31 - 38 Real(8.3) Orthogonal coordinates for X in Angstroms. + TokenBuilder.addToken(sites.Cartn_x, Tokenizer.trim(data, s + 30, s + 38)); + + // 39 - 46 Real(8.3) Orthogonal coordinates for Y in Angstroms. + TokenBuilder.addToken(sites.Cartn_y, Tokenizer.trim(data, s + 38, s + 46)); + + // 47 - 54 Real(8.3) Orthogonal coordinates for Z in Angstroms. + TokenBuilder.addToken(sites.Cartn_z, Tokenizer.trim(data, s + 46, s + 54)); + + // 55 - 60 Real(6.2) Occupancy. + TokenBuilder.addToken(sites.occupancy, Tokenizer.trim(data, s + 54, s + 60)); + + // 61 - 66 Real(6.2) Temperature factor (Default = 0.0). + if (length >= 66) { + TokenBuilder.addToken(sites.B_iso_or_equiv, Tokenizer.trim(data, s + 60, s + 66)); + } else { + TokenBuilder.add(sites.label_alt_id, 0, 0); + } + + // 73 - 76 LString(4) Segment identifier, left-justified. + // ignored + + // 77 - 78 LString(2) Element symbol, right-justified. + if (length >= 78) { + Tokenizer.trim(data, s + 76, s + 78); + + if (data.tokenStart < data.tokenEnd) { + TokenBuilder.addToken(sites.type_symbol, data); + } else { + // "guess" the symbol + TokenBuilder.add(sites.type_symbol, s + 12, s + 13); + } + } else { + TokenBuilder.add(sites.type_symbol, s + 12, s + 13); + } + + sites.label_entity_id[sites.index] = getEntityId(residueName, isHet); + sites.pdbx_PDB_model_num[sites.index] = model; + + sites.index++; +} + +export async function pdbToMmCif(pdb: PdbFile): Promise<CifFrame> { + const { lines } = pdb; + const { data, indices } = lines; + const tokenizer = Tokenizer(data); + + // Count the atoms + let atomCount = 0; + for (let i = 0, _i = lines.count; i < _i; i++) { + const s = indices[2 * i], e = indices[2 * i + 1]; + switch (data[s]) { + case 'A': + if (substringStartsWith(data, s, e, 'ATOM ')) atomCount++; + break; + case 'H': + if (substringStartsWith(data, s, e, 'HETATM')) atomCount++; + break; + } + } + + const atom_site = atom_site_template(data, atomCount); + + const helperCategories: CifCategory[] = []; + + let modelNum = 0, modelStr = ''; + + for (let i = 0, _i = lines.count; i < _i; i++) { + let s = indices[2 * i], e = indices[2 * i + 1]; + switch (data[s]) { + case 'A': + if (!substringStartsWith(data, s, e, 'ATOM ')) continue; + if (!modelNum) { modelNum++; modelStr = '' + modelNum; } + addAtom(atom_site, modelStr, tokenizer, s, e, false); + break; + case 'C': + if (substringStartsWith(data, s, e, 'CRYST1')) { + helperCategories.push(...parseCryst1(pdb.id || '?', data.substring(s, e))); + } + break; + case 'H': + if (substringStartsWith(data, s, e, 'HETATM')) { + if (!modelNum) { modelNum++; modelStr = '' + modelNum; } + addAtom(atom_site, modelStr, tokenizer, s, e, true); + } + // TODO: HELIX + break; + case 'M': + if (substringStartsWith(data, s, e, 'MODEL ')) { + modelNum++; + modelStr = '' + modelNum; + } + if (substringStartsWith(data, s, e, 'MTRIX')) { + let j = i + 1; + while (true) { + s = indices[2 * j]; e = indices[2 * j + 1]; + if (!substringStartsWith(data, s, e, 'MTRIX')) break; + j++; + } + helperCategories.push(...parseMtrix(lines, i, j)); + i = j - 1; + } + break; + case 'O': + // TODO + // ORIGX to generate _atom_sites + break; + case 'R': + if (substringStartsWith(data, s, e, 'REMARK 350')) { + let j = i + 1; + while (true) { + s = indices[2 * j]; e = indices[2 * j + 1]; + if (!substringStartsWith(data, s, e, 'REMARK 350')) break; + j++; + } + helperCategories.push(...parseRemark350(lines, i, j)); + i = j - 1; + } + break; + case 'S': + // TODO + // SHEET + break; + } + } + + const categories = { + entity: CifCategory.ofFields('entity', _entity()), + atom_site: CifCategory.ofFields('atom_site', _atom_site(atom_site)) + } as any; + + for (const c of helperCategories) { + categories[c.name] = c; + } + + return { + header: pdb.id || 'PDB', + categoryNames: Object.keys(categories), + categories + }; +} \ No newline at end of file diff --git a/src/mol-model/volume/formats/ccp4.ts b/src/mol-model-formats/volume/ccp4.ts similarity index 98% rename from src/mol-model/volume/formats/ccp4.ts rename to src/mol-model-formats/volume/ccp4.ts index 33b8087c80d2e4d9f7153d9800972a889161fd0f..5a6d9cfac971938ed881439ac32d9c1df00111e9 100644 --- a/src/mol-model/volume/formats/ccp4.ts +++ b/src/mol-model-formats/volume/ccp4.ts @@ -4,7 +4,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { VolumeData } from '../data' +import { VolumeData } from 'mol-model/volume/data' import { Task } from 'mol-task'; import { SpacegroupCell, Box3D } from 'mol-math/geometry'; import { Tensor, Vec3 } from 'mol-math/linear-algebra'; diff --git a/src/mol-model/volume/formats/density-server.ts b/src/mol-model-formats/volume/density-server.ts similarity index 97% rename from src/mol-model/volume/formats/density-server.ts rename to src/mol-model-formats/volume/density-server.ts index c01282e8e9d69ebd0cb24e35fef3730da9b0f337..450f8fcb001d1005a18742949ed5e097227a6d3f 100644 --- a/src/mol-model/volume/formats/density-server.ts +++ b/src/mol-model-formats/volume/density-server.ts @@ -5,7 +5,7 @@ */ import { DensityServer_Data_Database } from 'mol-io/reader/cif/schema/density-server' -import { VolumeData } from '../data' +import { VolumeData } from 'mol-model/volume/data' import { Task } from 'mol-task'; import { SpacegroupCell, Box3D } from 'mol-math/geometry'; import { Tensor, Vec3 } from 'mol-math/linear-algebra'; diff --git a/src/mol-model/volume/formats/dsn6.ts b/src/mol-model-formats/volume/dsn6.ts similarity index 97% rename from src/mol-model/volume/formats/dsn6.ts rename to src/mol-model-formats/volume/dsn6.ts index d945c19cfb03a6458fa57acd78138369c772c236..b256fe599afc421722d8943defc96fccc4fe4f4f 100644 --- a/src/mol-model/volume/formats/dsn6.ts +++ b/src/mol-model-formats/volume/dsn6.ts @@ -4,7 +4,7 @@ * @author Alexander Rose <alexander.rose@weirdbyte.de> */ -import { VolumeData } from '../data' +import { VolumeData } from 'mol-model/volume/data' import { Task } from 'mol-task'; import { SpacegroupCell, Box3D } from 'mol-math/geometry'; import { Tensor, Vec3 } from 'mol-math/linear-algebra'; diff --git a/src/mol-model/structure/model.ts b/src/mol-model/structure/model.ts index 05dec32e216ca63c83ad5f5e80767f027fef847b..df9e98d18fa289780ace42f1326250fd232ce92e 100644 --- a/src/mol-model/structure/model.ts +++ b/src/mol-model/structure/model.ts @@ -6,10 +6,9 @@ import { Model } from './model/model' import * as Types from './model/types' -import Format from './model/format' import { ModelSymmetry } from './model/properties/symmetry' import StructureSequence from './model/properties/sequence' export * from './model/properties/custom' export * from './model/indexing' -export { Model, Types, Format, ModelSymmetry, StructureSequence } \ No newline at end of file +export { Model, Types, ModelSymmetry, StructureSequence } \ No newline at end of file diff --git a/src/mol-model/structure/model/format.ts b/src/mol-model/structure/model/format.ts deleted file mode 100644 index 18f242c7caba2ca51dfb8e920141342a0afb5c33..0000000000000000000000000000000000000000 --- a/src/mol-model/structure/model/format.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -// import { File as GroFile } from 'mol-io/reader/gro/schema' -import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif' -import CIF, { CifFrame } from 'mol-io/reader/cif'; -import { PdbFile } from 'mol-io/reader/pdb/schema'; - -type Format = - // | Format.gro - | Format.mmCIF - -namespace Format { - // export interface gro { kind: 'gro', data: GroFile } - export interface mmCIF { kind: 'mmCIF', data: mmCIF_Database, frame: CifFrame } - export function mmCIF(frame: CifFrame, data?: mmCIF_Database): mmCIF { return { kind: 'mmCIF', data: data || CIF.schema.mmCIF(frame), frame }; } - - export interface PDB { kind: 'PDB', data: PdbFile } - export function PDB(data: PdbFile) { return { kind: 'PDB', data }; } -} - -export default Format \ No newline at end of file diff --git a/src/mol-model/structure/model/formats/pdb.ts b/src/mol-model/structure/model/formats/pdb.ts deleted file mode 100644 index 6ff81ec997b08de3118495b0167ca80b8a327a47..0000000000000000000000000000000000000000 --- a/src/mol-model/structure/model/formats/pdb.ts +++ /dev/null @@ -1,269 +0,0 @@ -/** - * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. - * - * @author David Sehnal <david.sehnal@gmail.com> - */ - -import Format from '../format'; -import { Model } from '../model'; -import { Task } from 'mol-task'; -import { PdbFile } from 'mol-io/reader/pdb/schema'; -import from_mmCIF from './mmcif'; -import { mmCIF_Schema } from 'mol-io/reader/cif/schema/mmcif'; -import { substringStartsWith } from 'mol-util/string'; -import { TokenBuilder, Tokenizer } from 'mol-io/reader/common/text/tokenizer'; -import { CifField, CifCategory } from 'mol-io/reader/cif'; -import CifTextField, { CifTextValueField } from 'mol-io/reader/cif/text/field'; - -function toCategory(name: string, fields: { [name: string]: CifField | undefined }, rowCount: number): CifCategory { - return { - name, - fieldNames: Object.keys(fields), - rowCount, - getField(f: string) { - return fields[f]; - } - } -} - -function _entity(): { [K in keyof mmCIF_Schema['entity']]?: CifField } { - return { - id: CifTextValueField(['1', '2', '3']), - type: CifTextValueField(['polymer', 'non-polymer', 'water']) - } -} - -function atom_site_template(data: string, count: number) { - const str = () => new Array(count) as string[]; - const ts = () => TokenBuilder.create(data, 2 * count); - return { - count, - group_PDB: ts(), - id: str(), - auth_atom_id: ts(), - label_alt_id: ts(), - auth_comp_id: ts(), - auth_asym_id: ts(), - auth_seq_id: ts(), - pdbx_PDB_ins_code: ts(), - Cartn_x: ts(), - Cartn_y: ts(), - Cartn_z: ts(), - occupancy: ts(), - B_iso_or_equiv: ts(), - type_symbol: ts(), - pdbx_PDB_model_num: str(), - label_entity_id: str() - }; -} - -function _atom_site(sites: AtomSiteTemplate): { [K in keyof mmCIF_Schema['atom_site']]?: CifField } { - const auth_asym_id = CifTextField(sites.auth_asym_id, sites.count); - const auth_atom_id = CifTextField(sites.auth_atom_id, sites.count); - const auth_comp_id = CifTextField(sites.auth_comp_id, sites.count); - const auth_seq_id = CifTextField(sites.auth_seq_id, sites.count); - - return { - auth_asym_id, - auth_atom_id, - auth_comp_id, - auth_seq_id, - B_iso_or_equiv: CifTextField(sites.B_iso_or_equiv, sites.count), - Cartn_x: CifTextField(sites.Cartn_x, sites.count), - Cartn_y: CifTextField(sites.Cartn_y, sites.count), - Cartn_z: CifTextField(sites.Cartn_z, sites.count), - group_PDB: CifTextField(sites.group_PDB, sites.count), - id: CifTextValueField(sites.id), - - label_alt_id: CifTextField(sites.label_alt_id, sites.count), - - label_asym_id: auth_asym_id, - label_atom_id: auth_atom_id, - label_comp_id: auth_comp_id, - label_seq_id: auth_seq_id, - label_entity_id: CifTextValueField(sites.label_entity_id), - - occupancy: CifTextField(sites.occupancy, sites.count), - type_symbol: CifTextField(sites.type_symbol, sites.count), - - pdbx_PDB_ins_code: CifTextField(sites.pdbx_PDB_ins_code, sites.count), - pdbx_PDB_model_num: CifTextValueField(sites.pdbx_PDB_model_num) - }; -} - -function addAtom(sites: AtomSiteTemplate, model: string, data: Tokenizer, s: number, e: number) { - const { data: str } = data; - let startPos = s; - let start = s; - const end = e; - const length = end - start; - - // TODO: filter invalid atoms - - // COLUMNS DATA TYPE CONTENTS - // -------------------------------------------------------------------------------- - // 1 - 6 Record name "ATOM " - Tokenizer.trim(data, start, start + 6); - TokenBuilder.add(sites.group_PDB, data.tokenStart, data.tokenEnd); - - // 7 - 11 Integer Atom serial number. - // TODO: support HEX - start = startPos + 6; - Tokenizer.trim(data, start, start + 5); - sites.id[sites.id.length] = data.data.substring(data.tokenStart, data.tokenEnd); - - // 13 - 16 Atom Atom name. - start = startPos + 12; - Tokenizer.trim(data, start, start + 4); - TokenBuilder.add(sites.auth_atom_id, data.tokenStart, data.tokenEnd); - - // 17 Character Alternate location indicator. - if (str.charCodeAt(startPos + 16) === 32) { // ' ' - TokenBuilder.add(sites.label_alt_id, 0, 0); - } else { - TokenBuilder.add(sites.label_alt_id, startPos + 16, startPos + 17); - } - - // 18 - 20 Residue name Residue name. - start = startPos + 17; - Tokenizer.trim(data, start, start + 3); - TokenBuilder.add(sites.auth_comp_id, data.tokenStart, data.tokenEnd); - - // 22 Character Chain identifier. - TokenBuilder.add(sites.auth_asym_id, startPos + 21, startPos + 22); - - // 23 - 26 Integer Residue sequence number. - // TODO: support HEX - start = startPos + 22; - Tokenizer.trim(data, start, start + 4); - TokenBuilder.add(sites.auth_seq_id, data.tokenStart, data.tokenEnd); - - // 27 AChar Code for insertion of residues. - if (str.charCodeAt(startPos + 26) === 32) { // ' ' - TokenBuilder.add(sites.label_alt_id, 0, 0); - } else { - TokenBuilder.add(sites.label_alt_id, startPos + 26, startPos + 27); - } - - // 31 - 38 Real(8.3) Orthogonal coordinates for X in Angstroms. - start = startPos + 30; - Tokenizer.trim(data, start, start + 8); - TokenBuilder.add(sites.Cartn_x, data.tokenStart, data.tokenEnd); - - // 39 - 46 Real(8.3) Orthogonal coordinates for Y in Angstroms. - start = startPos + 38; - Tokenizer.trim(data, start, start + 8); - TokenBuilder.add(sites.Cartn_y, data.tokenStart, data.tokenEnd); - - // 47 - 54 Real(8.3) Orthogonal coordinates for Z in Angstroms. - start = startPos + 46; - Tokenizer.trim(data, start, start + 8); - TokenBuilder.add(sites.Cartn_z, data.tokenStart, data.tokenEnd); - - // 55 - 60 Real(6.2) Occupancy. - start = startPos + 54; - Tokenizer.trim(data, start, start + 6); - TokenBuilder.add(sites.occupancy, data.tokenStart, data.tokenEnd); - - // 61 - 66 Real(6.2) Temperature factor (Default = 0.0). - if (length >= 66) { - start = startPos + 60; - Tokenizer.trim(data, start, start + 6); - TokenBuilder.add(sites.B_iso_or_equiv, data.tokenStart, data.tokenEnd); - } else { - TokenBuilder.add(sites.label_alt_id, 0, 0); - } - - // 73 - 76 LString(4) Segment identifier, left-justified. - // ignored - - // 77 - 78 LString(2) Element symbol, right-justified. - if (length >= 78) { - start = startPos + 76; - Tokenizer.trim(data, start, start + 2); - - if (data.tokenStart < data.tokenEnd) { - TokenBuilder.add(sites.type_symbol, data.tokenStart, data.tokenEnd); - } else { - // "guess" the symbol - TokenBuilder.add(sites.type_symbol, startPos + 12, startPos + 13); - } - } else { - TokenBuilder.add(sites.type_symbol, startPos + 12, startPos + 13); - } - - // TODO - sites.label_entity_id.push('1'); - sites.pdbx_PDB_model_num.push(model); - -} - -type AtomSiteTemplate = typeof atom_site_template extends (...args: any) => infer T ? T : never - -async function pdbToMmCIF(pdb: PdbFile): Promise<Format.mmCIF> { - const { lines } = pdb; - const { data, indices } = lines; - const tokenizer = Tokenizer(data); - - // Count the atoms - let atomCount = 0; - for (let i = 0, _i = lines.count; i < _i; i++) { - const s = indices[2 * i], e = indices[2 * i + 1]; - switch (data[s]) { - case 'A': - if (substringStartsWith(data, s, e, 'ATOM ')) atomCount++; - break; - case 'H': - if (!substringStartsWith(data, s, e, 'HETATM')) atomCount++; - break; - } - } - - const atom_site = atom_site_template(data, atomCount); - - let modelNum = 0, modelStr = ''; - - for (let i = 0, _i = lines.count; i < _i; i++) { - const s = indices[2 * i], e = indices[2 * i + 1]; - switch (data[s]) { - case 'A': - if (!substringStartsWith(data, s, e, 'ATOM ')) continue; - if (!modelNum) { modelNum++; modelStr = '' + modelNum; } - addAtom(atom_site, modelStr, tokenizer, s, e); - break; - case 'H': - if (!substringStartsWith(data, s, e, 'HETATM')) continue; - if (!modelNum) { modelNum++; modelStr = '' + modelNum; } - addAtom(atom_site, modelStr, tokenizer, s, e); - break; - case 'M': - if (substringStartsWith(data, s, e, 'MODEL ')) { - modelNum++; - modelStr = '' + modelNum; - } - break; - - } - } - - const categories = { - entity: toCategory('entity', _entity(), 3), - atom_site: toCategory('atom_site', _atom_site(atom_site), atomCount) - } - - return Format.mmCIF({ - header: pdb.id || 'PDB', - categoryNames: Object.keys(categories), - categories - }); -} - -function buildModels(format: Format.PDB): Task<ReadonlyArray<Model>> { - return Task.create('Create PDB Model', async ctx => { - await ctx.update('Converting to mmCIF...'); - const cif = await pdbToMmCIF(format.data); - return from_mmCIF(cif).runInContext(ctx); - }); -} - -export default buildModels; \ No newline at end of file diff --git a/src/mol-model/structure/model/model.ts b/src/mol-model/structure/model/model.ts index a9442fc948c6ffe8e1de0f19236ec750760e8a68..9c44135df7b8e64b081ffa4c449622d0a4443635 100644 --- a/src/mol-model/structure/model/model.ts +++ b/src/mol-model/structure/model/model.ts @@ -4,19 +4,17 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import UUID from 'mol-util/uuid' -import Format from './format' -import StructureSequence from './properties/sequence' -import { AtomicHierarchy, AtomicConformation } from './properties/atomic' -import { ModelSymmetry } from './properties/symmetry' -import { CoarseHierarchy, CoarseConformation } from './properties/coarse' +import UUID from 'mol-util/uuid'; +import StructureSequence from './properties/sequence'; +import { AtomicHierarchy, AtomicConformation } from './properties/atomic'; +import { ModelSymmetry } from './properties/symmetry'; +import { CoarseHierarchy, CoarseConformation } from './properties/coarse'; import { Entities } from './properties/common'; import { CustomProperties } from './properties/custom'; import { SecondaryStructure } from './properties/seconday-structure'; - -import from_mmCIF from './formats/mmcif' import { ChemicalComponentMap } from './properties/chemical-component'; import { SaccharideComponentMap } from '../structure/carbohydrates/constants'; +import { ModelFormat } from 'mol-model-formats/structure/format'; /** * Interface to the "source data" of the molecule. @@ -30,7 +28,7 @@ export interface Model extends Readonly<{ // for IHM, corresponds to ihm_model_list.model_id modelNum: number, - sourceData: Format, + sourceData: ModelFormat, symmetry: ModelSymmetry, entities: Entities, @@ -69,10 +67,6 @@ export interface Model extends Readonly<{ } { } export namespace Model { - export function create(format: Format) { - switch (format.kind) { - // case 'gro': return from_gro(format); - case 'mmCIF': return from_mmCIF(format); - } - } + // TODO: is this enough? + export type Trajectory = ReadonlyArray<Model> } \ No newline at end of file diff --git a/src/mol-model/structure/structure/unit/links/data.ts b/src/mol-model/structure/structure/unit/links/data.ts index 00559aec5a5cb63269a7ef784ccc5b7bb8d145d0..d8f40a7ec87e3328bb1adc1d763928b08c79bba2 100644 --- a/src/mol-model/structure/structure/unit/links/data.ts +++ b/src/mol-model/structure/structure/unit/links/data.ts @@ -66,10 +66,10 @@ class InterUnitBonds { pairBonds.linkedElementIndices.forEach(indexA => { pairBonds.getBonds(indexA).forEach(bondInfo => { const { unitA, unitB } = pairBonds - + const bondKey = InterUnitBonds.getBondKey(indexA, unitA, bondInfo.indexB, unitB) bondKeyIndex.set(bondKey, bonds.length) - + const elementKey = InterUnitBonds.getElementKey(indexA, unitA) const e = elementKeyIndex.get(elementKey) if (e === undefined) elementKeyIndex.set(elementKey, [bonds.length]) diff --git a/src/mol-model/structure/structure/unit/links/inter-compute.ts b/src/mol-model/structure/structure/unit/links/inter-compute.ts index f4f0143449d11a83c936185027e17e9b068ad0f5..ac094daa8c3f143658e3783378f50e1ab2be1487 100644 --- a/src/mol-model/structure/structure/unit/links/inter-compute.ts +++ b/src/mol-model/structure/structure/unit/links/inter-compute.ts @@ -4,7 +4,6 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { StructConn } from '../../../model/formats/mmcif/bonds'; import { LinkType } from '../../../model/types'; import Structure from '../../structure'; import Unit from '../../unit'; @@ -14,6 +13,7 @@ import { UniqueArray } from 'mol-data/generic'; import { SortedArray } from 'mol-data/int'; import { Vec3, Mat4 } from 'mol-math/linear-algebra'; import StructureElement from '../../element'; +import { StructConn } from 'mol-model-formats/structure/mmcif/bonds'; const MAX_RADIUS = 4; diff --git a/src/mol-model/structure/structure/unit/links/intra-compute.ts b/src/mol-model/structure/structure/unit/links/intra-compute.ts index 5ba8eb95b351a01f472f10132a16f190af4ece99..04b7497de8988bcda8b034a76fff8971c41e6834 100644 --- a/src/mol-model/structure/structure/unit/links/intra-compute.ts +++ b/src/mol-model/structure/structure/unit/links/intra-compute.ts @@ -6,11 +6,11 @@ import { LinkType } from '../../../model/types' import { IntraUnitLinks } from './data' -import { StructConn, ComponentBond } from '../../../model/formats/mmcif/bonds' import Unit from '../../unit' import { IntAdjacencyGraph } from 'mol-math/graph'; import { LinkComputationParameters, getElementIdx, MetalsSet, getElementThreshold, isHydrogen, getElementPairThreshold } from './common'; import { SortedArray } from 'mol-data/int'; +import { StructConn, ComponentBond } from 'mol-model-formats/structure/mmcif/bonds'; function getGraph(atomA: number[], atomB: number[], _order: number[], _flags: number[], atomCount: number): IntraUnitLinks { const builder = new IntAdjacencyGraph.EdgeBuilder(atomCount, atomA, atomB); diff --git a/src/mol-model/structure/structure/unit/pair-restraints/extract-cross-links.ts b/src/mol-model/structure/structure/unit/pair-restraints/extract-cross-links.ts index 28ae9d1be1bb2d38ac20cd7a50206415f6acfd88..a6e027e9cc5937eecdcd7e39b129c186cbbb6871 100644 --- a/src/mol-model/structure/structure/unit/pair-restraints/extract-cross-links.ts +++ b/src/mol-model/structure/structure/unit/pair-restraints/extract-cross-links.ts @@ -6,9 +6,9 @@ import Unit from '../../unit'; import Structure from '../../structure'; -import { IHMCrossLinkRestraint } from '../../../model/formats/mmcif/pair-restraint'; import { PairRestraints, CrossLinkRestraint } from './data'; import { StructureElement } from '../../../structure'; +import { IHMCrossLinkRestraint } from 'mol-model-formats/structure/mmcif/pair-restraint'; function _addRestraints(map: Map<number, number>, unit: Unit, restraints: IHMCrossLinkRestraint) { const { elements } = unit; diff --git a/src/mol-model/volume.ts b/src/mol-model/volume.ts index aa8cf1ce46056532d4723f8c3584de3842c4e484..c622e171bf076b1d77cca1d2f027464ac44734ee 100644 --- a/src/mol-model/volume.ts +++ b/src/mol-model/volume.ts @@ -4,5 +4,4 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -export * from './volume/data' -export * from './volume/formats/density-server' \ No newline at end of file +export * from './volume/data' \ No newline at end of file diff --git a/src/mol-plugin/state/actions/basic.ts b/src/mol-plugin/state/actions/basic.ts index 48c855fbcc57c4d8e3892d0715c743aee30c30b6..483e317e3e5edf2ee7ed63d51253082a772b520c 100644 --- a/src/mol-plugin/state/actions/basic.ts +++ b/src/mol-plugin/state/actions/basic.ts @@ -94,7 +94,7 @@ export const OpenStructure = StateAction.build({ function createModelTree(b: StateTreeBuilder.To<PluginStateObject.Data.Binary | PluginStateObject.Data.String>, format: 'pdb' | 'cif' = 'cif') { const parsed = format === 'cif' ? b.apply(StateTransforms.Data.ParseCif).apply(StateTransforms.Model.TrajectoryFromMmCif) - : b.apply(StateTransforms.Data.ConvertPDBtoMmCif).apply(StateTransforms.Model.TrajectoryFromMmCif); + : b.apply(StateTransforms.Model.TrajectoryFromPDB); return parsed.apply(StateTransforms.Model.ModelFromTrajectory, { modelIndex: 0 }); } diff --git a/src/mol-plugin/state/transforms/data.ts b/src/mol-plugin/state/transforms/data.ts index c8395300b2d6b25a0e000220390ef33b51b0949f..59ed1b3a0d9e8e3eb3fb6fb615a4a3bfbb18b017 100644 --- a/src/mol-plugin/state/transforms/data.ts +++ b/src/mol-plugin/state/transforms/data.ts @@ -15,8 +15,6 @@ import { Transformer } from 'mol-state'; import { readFromFile } from 'mol-util/data-source'; import * as CCP4 from 'mol-io/reader/ccp4/parser' import * as DSN6 from 'mol-io/reader/dsn6/parser' -import { parsePDB } from 'mol-io/reader/pdb/parser'; -import { convertPDBtoMmCif } from 'mol-io/reader/pdb/to-cif'; export { Download } type Download = typeof Download @@ -97,24 +95,6 @@ const ParseCif = PluginStateTransform.BuiltIn({ } }); -export { ConvertPDBtoMmCif } -type ConvertPDBtoMmCif = typeof ConvertPDBtoMmCif -const ConvertPDBtoMmCif = PluginStateTransform.BuiltIn({ - name: 'convert-pdb-to-mmcif', - display: { name: 'Convert PDB string to mmCIF' }, - from: [SO.Data.String], - to: SO.Format.Cif -})({ - apply({ a }) { - return Task.create('Parse CIF', async ctx => { - const parsed = await parsePDB(a.data).runInContext(ctx); - if (parsed.isError) throw new Error(parsed.message); - const cif = await convertPDBtoMmCif(parsed.result).runInContext(ctx); - return new SO.Format.Cif(cif); - }); - } -}); - export { ParseCcp4 } type ParseCcp4 = typeof ParseCcp4 const ParseCcp4 = PluginStateTransform.BuiltIn({ diff --git a/src/mol-plugin/state/transforms/model.ts b/src/mol-plugin/state/transforms/model.ts index 66e8fff9116f5d79f83cc675d1adf09749959d85..0df6a421c424eb64c2426ba8a3479f5f0afef48c 100644 --- a/src/mol-plugin/state/transforms/model.ts +++ b/src/mol-plugin/state/transforms/model.ts @@ -8,7 +8,7 @@ import { PluginStateTransform } from '../objects'; import { PluginStateObject as SO } from '../objects'; import { Task, RuntimeContext } from 'mol-task'; -import { Model, Format, Structure, ModelSymmetry, StructureSymmetry, QueryContext, StructureSelection as Sel, StructureQuery, Queries } from 'mol-model/structure'; +import { Model, Structure, ModelSymmetry, StructureSymmetry, QueryContext, StructureSelection as Sel, StructureQuery, Queries } from 'mol-model/structure'; import { ParamDefinition as PD } from 'mol-util/param-definition'; import Expression from 'mol-script/language/expression'; import { compile } from 'mol-script/runtime/query/compiler'; @@ -16,9 +16,12 @@ import { MolScriptBuilder } from 'mol-script/language/builder'; import { StateObject } from 'mol-state'; import { PluginContext } from 'mol-plugin/context'; import { stringToWords } from 'mol-util/string'; -import { volumeFromCcp4 } from 'mol-model/volume/formats/ccp4'; +import { volumeFromCcp4 } from 'mol-model-formats/volume/ccp4'; import { Vec3 } from 'mol-math/linear-algebra'; -import { volumeFromDsn6 } from 'mol-model/volume/formats/dsn6'; +import { volumeFromDsn6 } from 'mol-model-formats/volume/dsn6'; +import { trajectoryFromMmCIF } from 'mol-model-formats/structure/mmcif'; +import { parsePDB } from 'mol-io/reader/pdb/parser'; +import { trajectoryFromPDB } from 'mol-model-formats/structure/pdb'; export { TrajectoryFromMmCif } type TrajectoryFromMmCif = typeof TrajectoryFromMmCif @@ -45,7 +48,7 @@ const TrajectoryFromMmCif = PluginStateTransform.BuiltIn({ const header = params.blockHeader || a.data.blocks[0].header; const block = a.data.blocks.find(b => b.header === header); if (!block) throw new Error(`Data block '${[header]}' not found.`); - const models = await Model.create(Format.mmCIF(block)).runInContext(ctx); + const models = await trajectoryFromMmCIF(block).runInContext(ctx); if (models.length === 0) throw new Error('No models found.'); const props = { label: models[0].label, description: `${models.length} model${models.length === 1 ? '' : 's'}` }; return new SO.Molecule.Trajectory(models, props); @@ -53,6 +56,27 @@ const TrajectoryFromMmCif = PluginStateTransform.BuiltIn({ } }); + +export { TrajectoryFromPDB } +type TrajectoryFromPDB = typeof TrajectoryFromPDB +const TrajectoryFromPDB = PluginStateTransform.BuiltIn({ + name: 'trajectory-from-pdb', + display: { name: 'Parse PDB string and create trajectory' }, + from: [SO.Data.String], + to: SO.Molecule.Trajectory +})({ + apply({ a }) { + return Task.create('Parse PDB', async ctx => { + const parsed = await parsePDB(a.data).runInContext(ctx); + if (parsed.isError) throw new Error(parsed.message); + const models = await trajectoryFromPDB(parsed.result).runInContext(ctx); + const props = { label: models[0].label, description: `${models.length} model${models.length === 1 ? '' : 's'}` }; + return new SO.Molecule.Trajectory(models, props); + }); + } +}); + + export { ModelFromTrajectory } const plus1 = (v: number) => v + 1, minus1 = (v: number) => v - 1; type ModelFromTrajectory = typeof ModelFromTrajectory @@ -105,11 +129,11 @@ const StructureAssemblyFromModel = PluginStateTransform.BuiltIn({ to: SO.Molecule.Structure, params(a) { if (!a) { - return { id: PD.makeOptional(PD.Text('', { label: 'Assembly Id', description: 'Assembly Id. If none specified (undefined or empty string), the asymmetric unit is used.' })) }; + return { id: PD.makeOptional(PD.Text('', { label: 'Assembly Id', description: 'Assembly Id. Value \'deposited\' can be used to specify deposited asymmetric unit.' })) }; } const model = a.data; const ids = model.symmetry.assemblies.map(a => [a.id, `${a.id}: ${stringToWords(a.details)}`] as [string, string]); - if (!ids.length) ids.push(['deposited', 'Deposited']) + ids.push(['deposited', 'Deposited']); return { id: PD.makeOptional(PD.Select(ids[0][0], ids, { label: 'Asm Id', description: 'Assembly Id' })) }; } })({ @@ -122,7 +146,7 @@ const StructureAssemblyFromModel = PluginStateTransform.BuiltIn({ const base = Structure.ofModel(model); if (!asm) { - plugin.log.warn(`Model '${a.label}' has no assembly, returning deposited structure.`); + if (!!id && id !== 'deposited') plugin.log.warn(`Model '${a.label}' has no assembly, returning deposited structure.`); const label = { label: a.data.label, description: structureDesc(base) }; return new SO.Molecule.Structure(base, label); } diff --git a/src/mol-plugin/ui/controls/parameters.tsx b/src/mol-plugin/ui/controls/parameters.tsx index 99abe5c7de98766cd34204577a1a9c2367e2d923..caa63c87a96e8ed267212070ff1285fd947c3161 100644 --- a/src/mol-plugin/ui/controls/parameters.tsx +++ b/src/mol-plugin/ui/controls/parameters.tsx @@ -248,8 +248,9 @@ export class SelectControl extends SimpleParam<PD.Select<string | number>> { } } renderControl() { - return <select value={this.props.value || ''} onChange={this.onChange} disabled={this.props.isDisabled}> - {!this.props.param.options.some(e => e[0] === this.props.value) && <option key={this.props.value} value={this.props.value}>{`[Invalid] ${this.props.value}`}</option>} + const isInvalid = this.props.value !== void 0 && !this.props.param.options.some(e => e[0] === this.props.value); + return <select value={this.props.value || this.props.param.defaultValue} onChange={this.onChange} disabled={this.props.isDisabled}> + {isInvalid && <option key={this.props.value} value={this.props.value}>{`[Invalid] ${this.props.value}`}</option>} {this.props.param.options.map(([value, label]) => <option key={value} value={value}>{label}</option>)} </select>; } diff --git a/src/perf-tests/lookup3d.ts b/src/perf-tests/lookup3d.ts index 1a18900644164459d29349e5c24156d364281e74..a625a6cd16187429723d8b36b6fbb0e29bc52d7d 100644 --- a/src/perf-tests/lookup3d.ts +++ b/src/perf-tests/lookup3d.ts @@ -2,11 +2,12 @@ import * as util from 'util' import * as fs from 'fs' import CIF from 'mol-io/reader/cif' -import { Structure, Model, Format } from 'mol-model/structure' +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'; require('util.promisify').shim(); const readFileAsync = util.promisify(fs.readFile); @@ -31,11 +32,10 @@ export async function readCIF(path: string) { throw parsed; } - const mmcif = Format.mmCIF(parsed.result.blocks[0]); - const models = await Model.create(mmcif).run(); + const models = await trajectoryFromMmCIF(parsed.result.blocks[0]).run(); const structures = models.map(Structure.ofModel); - return { mmcif: mmcif.data, models, structures }; + return { mmcif: models[0].sourceData.data, models, structures }; } export async function test() { diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts index 7616ed526dd7b49747d35fdf445acd95830a80f3..48f9e54d5823f01af4f6d529a278513827004bab 100644 --- a/src/perf-tests/structure.ts +++ b/src/perf-tests/structure.ts @@ -11,11 +11,12 @@ import * as fs from 'fs' import fetch from 'node-fetch' import CIF from 'mol-io/reader/cif' -import { Structure, Model, Queries as Q, StructureElement, StructureSelection, StructureSymmetry, StructureQuery, Format, StructureProperties as SP } from 'mol-model/structure' +import { Structure, Model, Queries as Q, StructureElement, StructureSelection, StructureSymmetry, StructureQuery, StructureProperties as SP } from 'mol-model/structure' // import { Segmentation, OrderedSet } from 'mol-data/int' 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 { printUnits } from 'apps/structure-info/model'; // import { EquivalenceClasses } from 'mol-data/util'; @@ -69,16 +70,12 @@ export async function readCIF(path: string) { } const data = parsed.result.blocks[0]; - console.time('schema') - const mmcif = Format.mmCIF(data); - - console.timeEnd('schema') console.time('buildModels') - const models = await Model.create(mmcif).run(); + const models = await trajectoryFromMmCIF(data).run(); console.timeEnd('buildModels') const structures = models.map(Structure.ofModel); - return { mmcif, models, structures }; + return { mmcif: models[0].sourceData.data, models, structures }; } const DATA_DIR = './build/data'; diff --git a/src/servers/model/server/structure-wrapper.ts b/src/servers/model/server/structure-wrapper.ts index 7c8bb1b255ddf845a0c86ac861332e39bd681755..615e57e758653a9aa27fd43ef896be644b3ed91b 100644 --- a/src/servers/model/server/structure-wrapper.ts +++ b/src/servers/model/server/structure-wrapper.ts @@ -4,7 +4,7 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Structure, Model, Format } from 'mol-model/structure'; +import { Structure, Model } from 'mol-model/structure'; import { PerformanceMonitor } from 'mol-util/performance-monitor'; import { Cache } from './cache'; import Config from '../config'; @@ -15,6 +15,7 @@ import * as zlib from 'zlib' import { Job } from './jobs'; import { ConsoleLogger } from 'mol-util/console-logger'; import { ModelPropertiesProvider } from '../property-provider'; +import { trajectoryFromMmCIF } from 'mol-model-formats/structure/mmcif'; require('util.promisify').shim(); @@ -108,7 +109,7 @@ export async function readStructureWrapper(key: string, sourceId: string | '_loc const frame = (await parseCif(data)).blocks[0]; perf.end('parse'); perf.start('createModel'); - const models = await Model.create(Format.mmCIF(frame)).run(); + const models = await trajectoryFromMmCIF(frame).run(); perf.end('createModel'); const modelMap = new Map<number, Model>(); diff --git a/tsconfig.json b/tsconfig.json index 70aa52c9b25f08ccd1f961c4cf42d4dc0138d4aa..0a845d1985969692496c7afa252226647952ee33 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,6 +23,7 @@ "mol-math": ["./mol-math"], "mol-model": ["./mol-model"], "mol-model-props": ["./mol-model-props", "./mol-model-props/index.ts"], + "mol-model-formats": ["./mol-model-formats"], "mol-ql": ["./mol-ql"], "mol-repr": ["./mol-repr"], "mol-script": ["./mol-script"],