Browse Source

Merge branch 'master' into alpha-orbitals

David Sehnal 4 years ago
parent
commit
d771bdc8ff

+ 54 - 1
src/apps/viewer/index.ts

@@ -28,6 +28,12 @@ import { ANVILMembraneOrientation } from '../../extensions/anvil/behavior';
 import { DnatcoConfalPyramids } from '../../extensions/dnatco';
 import { G3DFormat, G3dProvider } from '../../extensions/g3d/format';
 import { DataFormatProvider } from '../../mol-plugin-state/formats/provider';
+import { BuildInVolumeFormat } from '../../mol-plugin-state/formats/volume';
+import { Color } from '../../mol-util/color';
+import { StateObjectSelector } from '../../mol-state';
+import { PluginStateObject } from '../../mol-plugin-state/objects';
+import { StateTransforms } from '../../mol-plugin-state/transforms';
+import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers/volume-representation-params';
 
 require('mol-plugin-ui/skin/light.scss');
 
@@ -68,6 +74,7 @@ const DefaultViewerOptions = {
     viewportShowAnimation: PluginConfig.Viewport.ShowAnimation.defaultValue,
     pluginStateServer: PluginConfig.State.DefaultServer.defaultValue,
     volumeStreamingServer: PluginConfig.VolumeStreaming.DefaultServer.defaultValue,
+    volumeStreamingDisabled: !PluginConfig.VolumeStreaming.Enabled.defaultValue,
     pdbProvider: PluginConfig.Download.DefaultPdbProvider.defaultValue,
     emdbProvider: PluginConfig.Download.DefaultEmdbProvider.defaultValue,
 };
@@ -116,6 +123,7 @@ export class Viewer {
                 [PluginConfig.State.DefaultServer, o.pluginStateServer],
                 [PluginConfig.State.CurrentServer, o.pluginStateServer],
                 [PluginConfig.VolumeStreaming.DefaultServer, o.volumeStreamingServer],
+                [PluginConfig.VolumeStreaming.Enabled, !o.volumeStreamingDisabled],
                 [PluginConfig.Download.DefaultPdbProvider, o.pdbProvider],
                 [PluginConfig.Download.DefaultEmdbProvider, o.emdbProvider]
             ]
@@ -152,6 +160,15 @@ export class Viewer {
         }));
     }
 
+    async loadAllModelsOrAssemblyFromUrl(url: string, format: BuiltInTrajectoryFormat = 'mmcif', isBinary = false) {
+        const plugin = this.plugin;
+
+        const data = await plugin.builders.data.download({ url, isBinary }, { state: { isGhost: true } });
+        const trajectory = await plugin.builders.structure.parseTrajectory(data, format);
+
+        await this.plugin.builders.structure.hierarchy.applyPreset(trajectory, 'all-models', { useDefaultIfSingleModel: true });
+    }
+
     async loadStructureFromData(data: string | number[], format: BuiltInTrajectoryFormat, options?: { dataLabel?: string }) {
         const _data = await this.plugin.builders.data.rawData({ data, label: options?.dataLabel });
         const trajectory = await this.plugin.builders.structure.parseTrajectory(_data, format);
@@ -209,4 +226,40 @@ export class Viewer {
             }
         }));
     }
-}
+
+    async loadVolumeFromUrl(url: string, format: BuildInVolumeFormat, isBinary: boolean, isovalues: VolumeIsovalueInfo[], entryId?: string) {
+        const plugin = this.plugin;
+
+        if (!plugin.dataFormats.get(format)) {
+            throw new Error(`Unknown density format: ${format}`);
+        }
+
+        return plugin.dataTransaction(async () => {
+            const data = await plugin.builders.data.download({ url, isBinary, label: entryId }, { state: { isGhost: true } });
+
+            const parsed = await plugin.dataFormats.get(format)!.parse(plugin, data, { entryId });
+            const volume = (parsed.volume || parsed.volumes[0]) as StateObjectSelector<PluginStateObject.Volume.Data>;
+            if (!volume?.isOk) throw new Error('Failed to parse any volume.');
+
+            const repr = plugin.build().to(volume);
+            for (const iso of isovalues) {
+                repr.apply(StateTransforms.Representation.VolumeRepresentation3D, createVolumeRepresentationParams(this.plugin, volume.data!, {
+                    type: 'isosurface',
+                    typeParams: { alpha: iso.alpha ?? 1, isoValue: iso.type === 'absolute' ?  { kind: 'absolute', absoluteValue: iso.value } : { kind: 'relative', relativeValue: iso.value } },
+                    color: 'uniform',
+                    colorParams: { value: iso.color }
+                }));
+            }
+
+            await repr.commit();
+        });
+    }
+}
+
+export interface VolumeIsovalueInfo {
+    type: 'absolute' | 'relative',
+    value: number,
+    color: Color,
+    alpha?: number
+}
+

+ 57 - 7
src/cli/chem-comp-dict/create-table.ts

@@ -27,6 +27,10 @@ function ccbKey(compId: string, atomId1: string, atomId2: string) {
     return atomId1 < atomId2 ? `${compId}:${atomId1}-${atomId2}` : `${compId}:${atomId2}-${atomId1}`;
 }
 
+function ccaKey(compId: string, atomId: string) {
+    return `${compId}:${atomId}`;
+}
+
 function addChemCompBondToSet(set: Set<string>, ccb: CCB) {
     for (let i = 0, il = ccb._rowCount; i < il; ++i) {
         set.add(ccbKey(ccb.comp_id.value(i), ccb.atom_id_1.value(i), ccb.atom_id_2.value(i)));
@@ -36,7 +40,7 @@ function addChemCompBondToSet(set: Set<string>, ccb: CCB) {
 
 function addChemCompAtomToSet(set: Set<string>, cca: CCA) {
     for (let i = 0, il = cca._rowCount; i < il; ++i) {
-        set.add(cca.atom_id.value(i));
+        set.add(ccaKey(cca.comp_id.value(i), cca.atom_id.value(i)));
     }
     return set;
 }
@@ -82,6 +86,27 @@ function checkAddingBondsFromPVCD(pvcd: DatabaseCollection<CCD_Schema>) {
     }
 }
 
+function checkAddingAtomsFromPVCD(pvcd: DatabaseCollection<CCD_Schema>) {
+    const ccaSetByParent = DefaultMap<string, Set<string>>(() => new Set());
+
+    for (const k in pvcd) {
+        const { chem_comp, chem_comp_atom } = pvcd[k];
+        if (chem_comp_atom._rowCount) {
+            const parentIds = chem_comp.mon_nstd_parent_comp_id.value(0);
+            if (parentIds.length === 0) {
+                const set = ccaSetByParent.getDefault(chem_comp.id.value(0));
+                addChemCompAtomToSet(set, chem_comp_atom);
+            } else {
+                for (let i = 0, il = parentIds.length; i < il; ++i) {
+                    const parentId = parentIds[i];
+                    const set = ccaSetByParent.getDefault(parentId);
+                    addChemCompAtomToSet(set, chem_comp_atom);
+                }
+            }
+        }
+    }
+}
+
 async function createBonds(
     ccd: DatabaseCollection<CCD_Schema>,
     pvcd: DatabaseCollection<CCD_Schema>,
@@ -152,10 +177,12 @@ async function createBonds(
         { chem_comp_bond: bondTable }
     );
 
-    return { bonds: bondDatabase, atoms: atomsRequested ? createAtoms(ccd) : void 0 };
+    return { bonds: bondDatabase, atoms: atomsRequested ? createAtoms(ccd, pvcd) : void 0 };
 }
 
-function createAtoms(ccd: DatabaseCollection<CCD_Schema>) {
+function createAtoms(ccd: DatabaseCollection<CCD_Schema>, pvcd: DatabaseCollection<CCD_Schema>) {
+    const ccaSet = new Set<string>();
+
     const comp_id: string[] = [];
     const atom_id: string[] = [];
     const charge: number[] = [];
@@ -163,10 +190,33 @@ function createAtoms(ccd: DatabaseCollection<CCD_Schema>) {
 
     function addAtoms(compId: string, cca: CCA) {
         for (let i = 0, il = cca._rowCount; i < il; ++i) {
-            atom_id.push(cca.atom_id.value(i));
-            comp_id.push(compId);
-            charge.push(cca.charge.value(i));
-            pdbx_stereo_config.push(cca.pdbx_stereo_config.value(i));
+            const atomId = cca.atom_id.value(i);
+            const k = ccaKey(compId, atomId);
+            if (!ccaSet.has(k)) {
+                atom_id.push(atomId);
+                comp_id.push(compId);
+                charge.push(cca.charge.value(i));
+                pdbx_stereo_config.push(cca.pdbx_stereo_config.value(i));
+                ccaSet.add(k);
+            }
+        }
+    }
+
+    // check adding atoms from PVCD
+    checkAddingAtomsFromPVCD(pvcd);
+
+    // add atoms from PVCD
+    for (const k in pvcd) {
+        const { chem_comp, chem_comp_atom } = pvcd[k];
+        if (chem_comp_atom._rowCount) {
+            const parentIds = chem_comp.mon_nstd_parent_comp_id.value(0);
+            if (parentIds.length === 0) {
+                addAtoms(chem_comp.id.value(0), chem_comp_atom);
+            } else {
+                for (let i = 0, il = parentIds.length; i < il; ++i) {
+                    addAtoms(parentIds[i], chem_comp_atom);
+                }
+            }
         }
     }
 

+ 40 - 3
src/extensions/g3d/model.ts

@@ -142,7 +142,7 @@ async function getTraj(ctx: RuntimeContext, data: G3dDataBlock) {
         entity: entityBuilder.getEntityTable(),
         ihm_model_list: Table.ofPartialColumns(BasicSchema.ihm_model_list, {
             model_id: Column.ofIntArray([1]),
-            model_name: Column.ofStringArray(['3DG Model']),
+            model_name: Column.ofStringArray(['G3D Model']),
         }, 1),
         ihm_sphere_obj_site
     });
@@ -153,7 +153,8 @@ async function getTraj(ctx: RuntimeContext, data: G3dDataBlock) {
         haplotypes: Object.keys(data.data),
         haplotype: normalized.haplotype,
         resolution: data.resolution,
-        start: normalized.start
+        start: normalized.start,
+        chroms: normalized.chromosome,
     });
 
     return models;
@@ -174,6 +175,22 @@ export const G3dSymbols = {
             const seqId = ctx.element.unit.model.coarseHierarchy.spheres.seq_id_begin.value(ctx.element.element);
             return info.haplotype[seqId] || '';
         }
+    ),
+    chromosome: QuerySymbolRuntime.Dynamic(CustomPropSymbol('g3d', 'chromosome', Type.Str),
+        ctx => {
+            if (Unit.isAtomic(ctx.element.unit)) return '';
+            const { asym_id } = ctx.element.unit.model.coarseHierarchy.spheres;
+            return asym_id.value(ctx.element.element) || '';
+        }
+    ),
+    region: QuerySymbolRuntime.Dynamic(CustomPropSymbol('g3d', 'region', Type.Num),
+        ctx => {
+            if (Unit.isAtomic(ctx.element.unit)) return '';
+            const info = (G3dInfoDataProperty as any).get(ctx.element.unit.model);
+            if (!info) return 0;
+            const seqId = ctx.element.unit.model.coarseHierarchy.spheres.seq_id_begin.value(ctx.element.element);
+            return info.start[seqId] || 0;
+        }
     )
 };
 
@@ -185,11 +202,31 @@ export function g3dHaplotypeQuery(haplotype: string) {
     });
 }
 
+export function g3dChromosomeQuery(chr: string) {
+    return MS.struct.generator.atomGroups({
+        'chain-test': MS.core.logic.and([
+            MS.core.rel.eq([MS.ammp('objectPrimitive'), 'sphere']),
+            MS.core.rel.eq([G3dSymbols.chromosome.symbol(), chr])
+        ])
+    });
+}
+
+export function g3dRegionQuery(chr: string, start: number, end: number) {
+    return MS.struct.generator.atomGroups({
+        'chain-test': MS.core.logic.and([
+            MS.core.rel.eq([MS.ammp('objectPrimitive'), 'sphere']),
+            MS.core.rel.eq([G3dSymbols.chromosome.symbol(), chr])
+        ]),
+        'residue-test': MS.core.rel.inRange([G3dSymbols.region.symbol(), start, end])
+    });
+}
+
 export interface G3dInfoData {
     haplotypes: string[],
     haplotype: string[],
     start: Int32Array,
-    resolution: number
+    resolution: number,
+    chroms: string[]
 };
 
 export const G3dLabelProvider: LociLabelProvider = {

+ 5 - 1
src/mol-model-formats/structure/property/atoms/chem_comp.ts

@@ -56,7 +56,11 @@ export namespace ComponentAtom {
         const entries: Map<string, Entry> = new Map();
 
         function addEntry(id: string) {
-            let e = new Entry(id);
+            // weird behavior when 'PRO' is requested - will report a single bond between N and H because a later operation would override real content
+            if (entries.has(id)) {
+                return entries.get(id)!;
+            }
+            const e = new Entry(id);
             entries.set(id, e);
             return e;
         }

+ 10 - 1
src/mol-plugin-state/builder/structure/hierarchy-preset.ts

@@ -73,6 +73,11 @@ const defaultPreset = TrajectoryHierarchyPresetProvider({
     }
 });
 
+const AllModelsParams = (a: PluginStateObject.Molecule.Trajectory | undefined, plugin: PluginContext) => ({
+    useDefaultIfSingleModel: PD.Optional(PD.Boolean(false)),
+    ...CommonParams(a, plugin)
+});
+
 const allModels = TrajectoryHierarchyPresetProvider({
     id: 'preset-trajectory-all-models',
     display: {
@@ -82,11 +87,15 @@ const allModels = TrajectoryHierarchyPresetProvider({
     isApplicable: o => {
         return o.data.frameCount > 1;
     },
-    params: CommonParams,
+    params: AllModelsParams,
     async apply(trajectory, params, plugin) {
         const tr = StateObjectRef.resolveAndCheck(plugin.state.data, trajectory)?.obj?.data;
         if (!tr) return { };
 
+        if (tr.frameCount === 1 && params.useDefaultIfSingleModel) {
+            return defaultPreset.apply(trajectory, params as any, plugin);
+        }
+
         const builder = plugin.builders.structure;
 
         const models = [], structures = [];

+ 1 - 1
src/mol-plugin-ui/controls.tsx

@@ -292,7 +292,7 @@ export class DefaultStructureTools extends PluginUIComponent {
             <StructureMeasurementsControls />
             <StructureSuperpositionControls />
             <StructureComponentControls />
-            <VolumeStreamingControls />
+            {this.plugin.config.get(PluginConfig.VolumeStreaming.Enabled) && <VolumeStreamingControls />}
             <VolumeSourceControls />
 
             <CustomStructureControls />

+ 1 - 0
src/mol-plugin/config.ts

@@ -32,6 +32,7 @@ export const PluginConfig = {
         HistoryCapacity: item('history-capacity.server', 5)
     },
     VolumeStreaming: {
+        Enabled: item('volume-streaming.enabled', true),
         DefaultServer: item('volume-streaming.server', 'https://ds.litemol.org'),
         CanStream: item('volume-streaming.can-stream', (s: Structure, plugin: PluginContext) => {
             return s.models.length === 1 && Model.probablyHasDensityMap(s.models[0]);