Browse Source

split structure formats into topology & coordinates

Alexander Rose 3 years ago
parent
commit
6fd9dcc72e

+ 6 - 5
src/apps/viewer/app.ts

@@ -24,7 +24,8 @@ import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
 import { PresetTrajectoryHierarchy } from '../../mol-plugin-state/builder/structure/hierarchy-preset';
 import { PresetStructureRepresentations, StructureRepresentationPresetProvider } from '../../mol-plugin-state/builder/structure/representation-preset';
 import { DataFormatProvider } from '../../mol-plugin-state/formats/provider';
-import { BuildInStructureFormat } from '../../mol-plugin-state/formats/structure';
+import { BuiltInTopologyFormat } from '../../mol-plugin-state/formats/topology';
+import { BuiltInCoordinatesFormat } from '../../mol-plugin-state/formats/coordinates';
 import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
 import { BuildInVolumeFormat } from '../../mol-plugin-state/formats/volume';
 import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
@@ -455,11 +456,11 @@ export interface VolumeIsovalueInfo {
 export interface LoadTrajectoryParams {
     model: { kind: 'model-url', url: string, format?: BuiltInTrajectoryFormat /* mmcif */, isBinary?: boolean }
     | { kind: 'model-data', data: string | number[] | ArrayBuffer | Uint8Array, format?: BuiltInTrajectoryFormat /* mmcif */ }
-    | { kind: 'topology-url', url: string, format: BuildInStructureFormat, isBinary?: boolean }
-    | { kind: 'topology-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
+    | { kind: 'topology-url', url: string, format: BuiltInTopologyFormat, isBinary?: boolean }
+    | { kind: 'topology-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuiltInTopologyFormat },
     modelLabel?: string,
-    coordinates: { kind: 'coordinates-url', url: string, format: BuildInStructureFormat, isBinary?: boolean }
-    | { kind: 'coordinates-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuildInStructureFormat },
+    coordinates: { kind: 'coordinates-url', url: string, format: BuiltInCoordinatesFormat, isBinary?: boolean }
+    | { kind: 'coordinates-data', data: string | number[] | ArrayBuffer | Uint8Array, format: BuiltInCoordinatesFormat },
     coordinatesLabel?: string,
     preset?: keyof PresetTrajectoryHierarchy
 }

+ 17 - 4
src/extensions/zenodo/ui.tsx

@@ -7,6 +7,8 @@
 import { DownloadFile } from '../../mol-plugin-state/actions/file';
 import { DownloadStructure, LoadTrajectory } from '../../mol-plugin-state/actions/structure';
 import { DownloadDensity } from '../../mol-plugin-state/actions/volume';
+import { CoordinatesFormatCategory } from '../../mol-plugin-state/formats/coordinates';
+import { TopologyFormatCategory } from '../../mol-plugin-state/formats/topology';
 import { TrajectoryFormatCategory } from '../../mol-plugin-state/formats/trajectory';
 import { VolumeFormatCategory } from '../../mol-plugin-state/formats/volume';
 import { CollapsableControls, CollapsableState } from '../../mol-plugin-ui/base';
@@ -64,7 +66,10 @@ function createImportParams(files: ZenodoFile[], plugin: PluginContext) {
     const compressedOpts: [string, string][] = [];
 
     const structureExts = new Map<string, { format: string, isBinary: boolean }>();
+    const coordinatesExts = new Map<string, { format: string, isBinary: boolean }>();
+    const topologyExts = new Map<string, { format: string, isBinary: boolean }>();
     const volumeExts = new Map<string, { format: string, isBinary: boolean }>();
+
     for (const { provider: { category, binaryExtensions, stringExtensions }, name } of plugin.dataFormats.list) {
         if (category === TrajectoryFormatCategory) {
             if (binaryExtensions) for (const e of binaryExtensions) structureExts.set(e, { format: name, isBinary: true });
@@ -72,6 +77,12 @@ function createImportParams(files: ZenodoFile[], plugin: PluginContext) {
         } else if (category === VolumeFormatCategory) {
             if (binaryExtensions) for (const e of binaryExtensions) volumeExts.set(e, { format: name, isBinary: true });
             if (stringExtensions) for (const e of stringExtensions) volumeExts.set(e, { format: name, isBinary: false });
+        } else if (category === CoordinatesFormatCategory) {
+            if (binaryExtensions) for (const e of binaryExtensions) coordinatesExts.set(e, { format: name, isBinary: true });
+            if (stringExtensions) for (const e of stringExtensions) coordinatesExts.set(e, { format: name, isBinary: false });
+        } else if (category === TopologyFormatCategory) {
+            if (binaryExtensions) for (const e of binaryExtensions) topologyExts.set(e, { format: name, isBinary: true });
+            if (stringExtensions) for (const e of stringExtensions) topologyExts.set(e, { format: name, isBinary: false });
         }
     }
 
@@ -84,10 +95,12 @@ function createImportParams(files: ZenodoFile[], plugin: PluginContext) {
         } else if (volumeExts.has(file.type)) {
             const { format, isBinary } = volumeExts.get(file.type)!;
             volumeOpts.push([`${file.links.self}|${format}|${isBinary}`, label]);
-        } else if (file.type === 'psf') {
-            topologyOpts.push([`${file.links.self}|${file.type}|false`, label]);
-        } else if (file.type === 'xtc' || file.type === 'dcd') {
-            coordinatesOpts.push([`${file.links.self}|${file.type}|true`, label]);
+        } else if (topologyExts.has(file.type)) {
+            const { format, isBinary } = topologyExts.get(file.type)!;
+            topologyOpts.push([`${file.links.self}|${format}|${isBinary}`, label]);
+        } else if (coordinatesExts.has(file.type)) {
+            const { format, isBinary } = coordinatesExts.get(file.type)!;
+            coordinatesOpts.push([`${file.links.self}|${format}|${isBinary}`, label]);
         } else if (file.type === 'zip') {
             compressedOpts.push([`${file.links.self}|${file.type}|true`, label]);
         }

+ 1 - 1
src/mol-plugin-state/actions/file.ts

@@ -22,7 +22,7 @@ async function processFile(file: Asset.File, plugin: PluginContext, format: stri
         : plugin.dataFormats.get(format);
 
     if (!provider) {
-        plugin.log.warn(`OpenFiles: could not find data provider for '${info.name}.${info.ext}'`);
+        plugin.log.warn(`OpenFiles: could not find data provider for '${info.ext}'`);
         await plugin.state.data.build().delete(data).commit();
         return;
     }

+ 17 - 11
src/mol-plugin-state/actions/structure.ts

@@ -20,6 +20,8 @@ import { Asset } from '../../mol-util/assets';
 import { PluginConfig } from '../../mol-plugin/config';
 import { getFileInfo } from '../../mol-util/file-info';
 import { assertUnreachable } from '../../mol-util/type-helpers';
+import { TopologyFormatCategory } from '../formats/topology';
+import { CoordinatesFormatCategory } from '../formats/coordinates';
 
 const DownloadModelRepresentationOptions = (plugin: PluginContext) => {
     const representationDefault = plugin.config.get(PluginConfig.Structure.DefaultRepresentationPreset) || PresetStructureRepresentations.auto.id;
@@ -318,14 +320,18 @@ export const LoadTrajectory = StateAction.build({
     from: PluginStateObject.Root,
     params(a, ctx: PluginContext) {
         const { options } = ctx.dataFormats;
-        const modelOptions = options.filter(o => o[2] === TrajectoryFormatCategory || o[0] === 'psf');
-        const coordinatesOptions = options.filter(o => o[0] === 'dcd' || o[0] === 'xtc');
-
-        const modelExtensions: string[] = [];
-        for (const { name, provider } of ctx.dataFormats.list) {
-            if (provider.category === TrajectoryFormatCategory || name === 'psf') {
-                if (provider.binaryExtensions) modelExtensions.push(...provider.binaryExtensions);
-                if (provider.stringExtensions) modelExtensions.push(...provider.stringExtensions);
+        const modelOptions = options.filter(o => o[2] === TrajectoryFormatCategory || o[2] === TopologyFormatCategory);
+        const coordinatesOptions = options.filter(o => o[2] === CoordinatesFormatCategory);
+
+        const modelExts: string[] = [];
+        const coordinatesExts: string[] = [];
+        for (const { provider } of ctx.dataFormats.list) {
+            if (provider.category === TrajectoryFormatCategory || provider.category === TopologyFormatCategory) {
+                if (provider.binaryExtensions) modelExts.push(...provider.binaryExtensions);
+                if (provider.stringExtensions) modelExts.push(...provider.stringExtensions);
+            } else if (provider.category === CoordinatesFormatCategory) {
+                if (provider.binaryExtensions) coordinatesExts.push(...provider.binaryExtensions);
+                if (provider.stringExtensions) coordinatesExts.push(...provider.stringExtensions);
             }
         }
 
@@ -343,8 +349,8 @@ export const LoadTrajectory = StateAction.build({
                     }, { isExpanded: true })
                 }, { isFlat: true }),
                 file: PD.Group({
-                    model: PD.File({ accept: modelExtensions.map(e => `.${e}`).join(','), label: 'Model' }),
-                    coordinates: PD.File({ accept: '.dcd,.xtc', label: 'Coordinates' }),
+                    model: PD.File({ accept: modelExts.map(e => `.${e}`).join(','), label: 'Model' }),
+                    coordinates: PD.File({ accept: coordinatesExts.map(e => `.${e}`).join(','), label: 'Coordinates' }),
                 }, { isFlat: true }),
             }, { options: [['url', 'URL'], ['file', 'File']] })
         };
@@ -384,7 +390,7 @@ export const LoadTrajectory = StateAction.build({
             const provider = ctx.dataFormats.auto(info, data.cell?.obj!);
 
             if (!provider) {
-                ctx.log.warn(`LoadTrajectory: could not find data provider for '${info.name}.${info.ext}'`);
+                ctx.log.warn(`LoadTrajectory: could not find data provider for '${info.ext}'`);
                 await ctx.state.data.build().delete(data).commit();
                 return;
             }

+ 5 - 33
src/mol-plugin-state/formats/structure.ts → src/mol-plugin-state/formats/coordinates.ts

@@ -8,38 +8,13 @@
 import { StateTransforms } from '../transforms';
 import { DataFormatProvider } from './provider';
 
-export const StructureFormatCategory = 'Structure';
-
-//
-
-export { PsfProvider };
-const PsfProvider = DataFormatProvider({
-    label: 'PSF',
-    description: 'PSF',
-    category: StructureFormatCategory,
-    stringExtensions: ['psf'],
-    parse: async (plugin, data) => {
-        const format = plugin.state.data.build()
-            .to(data)
-            .apply(StateTransforms.Data.ParsePsf, {}, { state: { isGhost: true } });
-        const topology = format.apply(StateTransforms.Model.TopologyFromPsf);
-
-        await format.commit();
-
-        return { format: format.selector, topology: topology.selector };
-    }
-});
-type PsfProvider = typeof PsfProvider;
-
-export type TopologyProvider = PsfProvider;
-
-//
+export const CoordinatesFormatCategory = 'Coordinates';
 
 export { DcdProvider };
 const DcdProvider = DataFormatProvider({
     label: 'DCD',
     description: 'DCD',
-    category: StructureFormatCategory,
+    category: CoordinatesFormatCategory,
     binaryExtensions: ['dcd'],
     parse: (plugin, data) => {
         const coordinates = plugin.state.data.build()
@@ -55,7 +30,7 @@ export { XtcProvider };
 const XtcProvider = DataFormatProvider({
     label: 'XTC',
     description: 'XTC',
-    category: StructureFormatCategory,
+    category: CoordinatesFormatCategory,
     binaryExtensions: ['xtc'],
     parse: (plugin, data) => {
         const coordinates = plugin.state.data.build()
@@ -69,12 +44,9 @@ type XtcProvider = typeof XtcProvider;
 
 export type CoordinatesProvider = DcdProvider | XtcProvider;
 
-//
-
-export const BuiltInStructureFormats = [
-    ['psf', PsfProvider] as const,
+export const BuiltInCoordinatesFormats = [
     ['dcd', DcdProvider] as const,
     ['xtc', XtcProvider] as const,
 ] as const;
 
-export type BuildInStructureFormat = (typeof BuiltInStructureFormats)[number][0]
+export type BuiltInCoordinatesFormat = (typeof BuiltInCoordinatesFormats)[number][0]

+ 4 - 2
src/mol-plugin-state/formats/registry.ts

@@ -11,7 +11,8 @@ import { DataFormatProvider } from './provider';
 import { BuiltInTrajectoryFormats } from './trajectory';
 import { BuiltInVolumeFormats } from './volume';
 import { BuiltInShapeFormats } from './shape';
-import { BuiltInStructureFormats } from './structure';
+import { BuiltInTopologyFormats } from './topology';
+import { BuiltInCoordinatesFormats } from './coordinates';
 
 export class DataFormatRegistry {
     private _list: { name: string, provider: DataFormatProvider }[] = [];
@@ -53,7 +54,8 @@ export class DataFormatRegistry {
 
     constructor() {
         for (const [id, p] of BuiltInVolumeFormats) this.add(id, p);
-        for (const [id, p] of BuiltInStructureFormats) this.add(id, p);
+        for (const [id, p] of BuiltInTopologyFormats) this.add(id, p);
+        for (const [id, p] of BuiltInCoordinatesFormats) this.add(id, p);
         for (const [id, p] of BuiltInShapeFormats) this.add(id, p);
         for (const [id, p] of BuiltInTrajectoryFormats) this.add(id, p);
     };

+ 38 - 0
src/mol-plugin-state/formats/topology.ts

@@ -0,0 +1,38 @@
+/**
+ * Copyright (c) 2018-2022 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 { StateTransforms } from '../transforms';
+import { DataFormatProvider } from './provider';
+
+export const TopologyFormatCategory = 'Topology';
+
+export { PsfProvider };
+const PsfProvider = DataFormatProvider({
+    label: 'PSF',
+    description: 'PSF',
+    category: TopologyFormatCategory,
+    stringExtensions: ['psf'],
+    parse: async (plugin, data) => {
+        const format = plugin.state.data.build()
+            .to(data)
+            .apply(StateTransforms.Data.ParsePsf, {}, { state: { isGhost: true } });
+        const topology = format.apply(StateTransforms.Model.TopologyFromPsf);
+
+        await format.commit();
+
+        return { format: format.selector, topology: topology.selector };
+    }
+});
+type PsfProvider = typeof PsfProvider;
+
+export type TopologyProvider = PsfProvider;
+
+export const BuiltInTopologyFormats = [
+    ['psf', PsfProvider] as const,
+] as const;
+
+export type BuiltInTopologyFormat = (typeof BuiltInTopologyFormats)[number][0]

+ 60 - 3
src/mol-plugin-state/transforms/model.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2022 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>
@@ -42,10 +42,18 @@ import { trajectoryFromXyz } from '../../mol-model-formats/structure/xyz';
 import { parseSdf } from '../../mol-io/reader/sdf/parser';
 import { trajectoryFromSdf } from '../../mol-model-formats/structure/sdf';
 import { assertUnreachable } from '../../mol-util/type-helpers';
+import { parseTrr } from '../../mol-io/reader/trr/parser';
+import { coordinatesFromTrr } from '../../mol-model-formats/structure/trr';
+import { parseNctraj } from '../../mol-io/reader/nctraj/parser';
+import { coordinatesFromNctraj } from '../../mol-model-formats/structure/nctraj';
+import { topologyFromPrmtop } from '../../mol-model-formats/structure/prmtop';
 
 export { CoordinatesFromDcd };
 export { CoordinatesFromXtc };
+export { CoordinatesFromTrr };
+export { CoordinatesFromNctraj };
 export { TopologyFromPsf };
+export { TopologyFromPrmtop };
 export { TrajectoryFromModelAndCoordinates };
 export { TrajectoryFromBlob };
 export { TrajectoryFromMmCif };
@@ -88,7 +96,7 @@ const CoordinatesFromDcd = PluginStateTransform.BuiltIn({
     }
 });
 
-type CoordinatesFromXtc = typeof CoordinatesFromDcd
+type CoordinatesFromXtc = typeof CoordinatesFromXtc
 const CoordinatesFromXtc = PluginStateTransform.BuiltIn({
     name: 'coordinates-from-xtc',
     display: { name: 'Parse XTC', description: 'Parse XTC binary data.' },
@@ -105,10 +113,44 @@ const CoordinatesFromXtc = PluginStateTransform.BuiltIn({
     }
 });
 
+type CoordinatesFromTrr = typeof CoordinatesFromTrr
+const CoordinatesFromTrr = PluginStateTransform.BuiltIn({
+    name: 'coordinates-from-trr',
+    display: { name: 'Parse TRR', description: 'Parse TRR binary data.' },
+    from: [SO.Data.Binary],
+    to: SO.Molecule.Coordinates
+})({
+    apply({ a }) {
+        return Task.create('Parse TRR', async ctx => {
+            const parsed = await parseTrr(a.data).runInContext(ctx);
+            if (parsed.isError) throw new Error(parsed.message);
+            const coordinates = await coordinatesFromTrr(parsed.result).runInContext(ctx);
+            return new SO.Molecule.Coordinates(coordinates, { label: a.label, description: 'Coordinates' });
+        });
+    }
+});
+
+type CoordinatesFromNctraj = typeof CoordinatesFromNctraj
+const CoordinatesFromNctraj = PluginStateTransform.BuiltIn({
+    name: 'coordinates-from-nctraj',
+    display: { name: 'Parse NCTRAJ', description: 'Parse NCTRAJ binary data.' },
+    from: [SO.Data.Binary],
+    to: SO.Molecule.Coordinates
+})({
+    apply({ a }) {
+        return Task.create('Parse NCTRAJ', async ctx => {
+            const parsed = await parseNctraj(a.data).runInContext(ctx);
+            if (parsed.isError) throw new Error(parsed.message);
+            const coordinates = await coordinatesFromNctraj(parsed.result).runInContext(ctx);
+            return new SO.Molecule.Coordinates(coordinates, { label: a.label, description: 'Coordinates' });
+        });
+    }
+});
+
 type TopologyFromPsf = typeof TopologyFromPsf
 const TopologyFromPsf = PluginStateTransform.BuiltIn({
     name: 'topology-from-psf',
-    display: { name: 'PSF Topology', description: 'Parse PSF string data.' },
+    display: { name: 'PSF Topology', description: 'Create topology from PSF.' },
     from: [SO.Format.Psf],
     to: SO.Molecule.Topology
 })({
@@ -120,6 +162,21 @@ const TopologyFromPsf = PluginStateTransform.BuiltIn({
     }
 });
 
+type TopologyFromPrmtop = typeof TopologyFromPrmtop
+const TopologyFromPrmtop = PluginStateTransform.BuiltIn({
+    name: 'topology-from-prmtop',
+    display: { name: 'PRMTOP Topology', description: 'Create topology from PRMTOP.' },
+    from: [SO.Format.Prmtop],
+    to: SO.Molecule.Topology
+})({
+    apply({ a }) {
+        return Task.create('Create Topology', async ctx => {
+            const topology = await topologyFromPrmtop(a.data).runInContext(ctx);
+            return new SO.Molecule.Topology(topology, { label: topology.label || a.label, description: 'Topology' });
+        });
+    }
+});
+
 async function getTrajectory(ctx: RuntimeContext, obj: StateObject, coordinates: Coordinates) {
     if (obj.type === SO.Molecule.Topology.type) {
         const topology = obj.data as Topology;