Bladeren bron

Merge branch 'master' of https://github.com/molstar/molstar-proto

# Conflicts:
#	src/mol-model-formats/structure/mmcif/parser.ts
Alexander Rose 6 jaren geleden
bovenliggende
commit
027dbbfe04
55 gewijzigde bestanden met toevoegingen van 842 en 838 verwijderingen
  1. 1 0
      README.md
  2. 1 0
      package.json
  3. 1 1
      src/apps/basic-wrapper/index.ts
  4. 4 3
      src/apps/structure-info/model.ts
  5. 2 1
      src/apps/structure-info/volume.ts
  6. 4 5
      src/mol-io/reader/_spec/cif.spec.ts
  7. 1 1
      src/mol-io/reader/_spec/csv.spec.ts
  8. 119 2
      src/mol-io/reader/cif/data-model.ts
  9. 0 100
      src/mol-io/reader/cif/text/field.ts
  10. 2 3
      src/mol-io/reader/cif/text/parser.ts
  11. 5 0
      src/mol-io/reader/common/text/tokenizer.ts
  12. 2 2
      src/mol-io/reader/csv/field.ts
  13. 1 1
      src/mol-io/reader/csv/parser.ts
  14. 0 280
      src/mol-io/reader/pdb/to-cif.ts
  15. 4 0
      src/mol-math/linear-algebra/3d/mat4.ts
  16. 18 0
      src/mol-model-formats/structure/format.ts
  17. 0 0
      src/mol-model-formats/structure/gro.ts
  18. 16 0
      src/mol-model-formats/structure/mmcif.ts
  19. 5 6
      src/mol-model-formats/structure/mmcif/assembly.ts
  20. 12 12
      src/mol-model-formats/structure/mmcif/atomic.ts
  21. 0 0
      src/mol-model-formats/structure/mmcif/bonds.ts
  22. 4 4
      src/mol-model-formats/structure/mmcif/bonds/comp.ts
  23. 5 5
      src/mol-model-formats/structure/mmcif/bonds/struct_conn.ts
  24. 6 6
      src/mol-model-formats/structure/mmcif/ihm.ts
  25. 0 0
      src/mol-model-formats/structure/mmcif/pair-restraint.ts
  26. 3 3
      src/mol-model-formats/structure/mmcif/pair-restraints/cross-links.ts
  27. 0 0
      src/mol-model-formats/structure/mmcif/pair-restraints/predicted-contacts.ts
  28. 24 29
      src/mol-model-formats/structure/mmcif/parser.ts
  29. 4 4
      src/mol-model-formats/structure/mmcif/secondary-structure.ts
  30. 4 4
      src/mol-model-formats/structure/mmcif/sequence.ts
  31. 0 0
      src/mol-model-formats/structure/mmcif/sort.ts
  32. 2 2
      src/mol-model-formats/structure/mmcif/util.ts
  33. 21 0
      src/mol-model-formats/structure/pdb.ts
  34. 230 0
      src/mol-model-formats/structure/pdb/assembly.ts
  35. 275 0
      src/mol-model-formats/structure/pdb/to-cif.ts
  36. 1 1
      src/mol-model-formats/volume/ccp4.ts
  37. 1 1
      src/mol-model-formats/volume/density-server.ts
  38. 1 1
      src/mol-model-formats/volume/dsn6.ts
  39. 1 2
      src/mol-model/structure/model.ts
  40. 0 25
      src/mol-model/structure/model/format.ts
  41. 0 269
      src/mol-model/structure/model/formats/pdb.ts
  42. 9 15
      src/mol-model/structure/model/model.ts
  43. 2 2
      src/mol-model/structure/structure/unit/links/data.ts
  44. 1 1
      src/mol-model/structure/structure/unit/links/inter-compute.ts
  45. 1 1
      src/mol-model/structure/structure/unit/links/intra-compute.ts
  46. 1 1
      src/mol-model/structure/structure/unit/pair-restraints/extract-cross-links.ts
  47. 1 2
      src/mol-model/volume.ts
  48. 1 1
      src/mol-plugin/state/actions/basic.ts
  49. 0 20
      src/mol-plugin/state/transforms/data.ts
  50. 31 7
      src/mol-plugin/state/transforms/model.ts
  51. 3 2
      src/mol-plugin/ui/controls/parameters.tsx
  52. 4 4
      src/perf-tests/lookup3d.ts
  53. 4 7
      src/perf-tests/structure.ts
  54. 3 2
      src/servers/model/server/structure-wrapper.ts
  55. 1 0
      tsconfig.json

+ 1 - 0
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.

+ 1 - 0
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",

+ 1 - 1
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 });
 

+ 4 - 3
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,

+ 2 - 1
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);

+ 4 - 5
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'], {

+ 1 - 1
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;
 

+ 119 - 2
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;

+ 0 - 100
src/mol-io/reader/cif/text/field.ts

@@ -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)
-    }
-}

+ 2 - 3
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);

+ 5 - 0
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;

+ 2 - 2
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
+export default CifField.ofTokens

+ 1 - 1
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)

+ 0 - 280
src/mol-io/reader/pdb/to-cif.ts

@@ -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);
-    });
-}

+ 4 - 0
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];

+ 18 - 0
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 }

+ 0 - 0
src/mol-model/structure/model/formats/gro.ts → src/mol-model-formats/structure/gro.ts


+ 16 - 0
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));
+}

+ 5 - 6
src/mol-model/structure/model/formats/mmcif/assembly.ts → 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;

+ 12 - 12
src/mol-model/structure/model/formats/mmcif/atomic.ts → 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']
 

+ 0 - 0
src/mol-model/structure/model/formats/mmcif/bonds.ts → src/mol-model-formats/structure/mmcif/bonds.ts


+ 4 - 4
src/mol-model/structure/model/formats/mmcif/bonds/comp.ts → 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'
 

+ 5 - 5
src/mol-model/structure/model/formats/mmcif/bonds/struct_conn.ts → 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>,

+ 6 - 6
src/mol-model/structure/model/formats/mmcif/ihm.ts → 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,

+ 0 - 0
src/mol-model/structure/model/formats/mmcif/pair-restraint.ts → src/mol-model-formats/structure/mmcif/pair-restraint.ts


+ 3 - 3
src/mol-model/structure/model/formats/mmcif/pair-restraints/cross-links.ts → 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

+ 0 - 0
src/mol-model/structure/model/formats/mmcif/pair-restraints/predicted-contacts.ts → src/mol-model-formats/structure/mmcif/pair-restraints/predicted-contacts.ts


+ 24 - 29
src/mol-model/structure/model/formats/mmcif.ts → 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;
+}

+ 4 - 4
src/mol-model/structure/model/formats/mmcif/secondary-structure.ts → 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();

+ 4 - 4
src/mol-model/structure/model/formats/mmcif/sequence.ts → 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

+ 0 - 0
src/mol-model/structure/model/formats/mmcif/sort.ts → src/mol-model-formats/structure/mmcif/sort.ts


+ 2 - 2
src/mol-model/structure/model/formats/mmcif/util.ts → 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 ''

+ 21 - 0
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);
+    })
+}

+ 230 - 0
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)];
+}

+ 275 - 0
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
+    };
+}

+ 1 - 1
src/mol-model/volume/formats/ccp4.ts → 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';

+ 1 - 1
src/mol-model/volume/formats/density-server.ts → 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';

+ 1 - 1
src/mol-model/volume/formats/dsn6.ts → 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';

+ 1 - 2
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 }
+export { Model, Types, ModelSymmetry, StructureSequence }

+ 0 - 25
src/mol-model/structure/model/format.ts

@@ -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

+ 0 - 269
src/mol-model/structure/model/formats/pdb.ts

@@ -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;

+ 9 - 15
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>
 }

+ 2 - 2
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])

+ 1 - 1
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;
 

+ 1 - 1
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);

+ 1 - 1
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;

+ 1 - 2
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'
+export * from './volume/data'

+ 1 - 1
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 });
 }

+ 0 - 20
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({

+ 31 - 7
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);
             }

+ 3 - 2
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>;
     }

+ 4 - 4
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() {

+ 4 - 7
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';

+ 3 - 2
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>();

+ 1 - 0
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"],