Browse Source

improved validation-report and assembly-symmetry presets

Alexander Rose 5 years ago
parent
commit
b7fa577d9b

+ 18 - 14
src/mol-model-props/rcsb/assembly-symmetry.ts

@@ -8,7 +8,7 @@ import { AssemblySymmetryQuery, AssemblySymmetryQueryVariables } from './graphql
 import query from './graphql/symmetry.gql';
 
 import { ParamDefinition as PD } from '../../mol-util/param-definition'
-import { CustomPropertyDescriptor, Structure } from '../../mol-model/structure';
+import { CustomPropertyDescriptor, Structure, Model } from '../../mol-model/structure';
 import { Database as _Database, Column } from '../../mol-data/db'
 import { GraphQLClient } from '../../mol-util/graphql-client';
 import { CustomProperty } from '../common/custom-property';
@@ -26,6 +26,18 @@ const BiologicalAssemblyNames = new Set([
     'software_defined_assembly'
 ])
 
+export function isBiologicalAssembly(structure: Structure): boolean {
+    if (!MmcifFormat.is(structure.models[0].sourceData)) return false
+    const mmcif = structure.models[0].sourceData.data.db
+    if (!mmcif.pdbx_struct_assembly.details.isDefined) return false
+    const id = structure.units[0].conformation.operator.assembly?.id || ''
+    if (id === '' || id === 'deposited') return true
+    const indices = Column.indicesOf(mmcif.pdbx_struct_assembly.id, e => e === id)
+    if (indices.length !== 1) return false
+    const details = mmcif.pdbx_struct_assembly.details.value(indices[0])
+    return BiologicalAssemblyNames.has(details)
+}
+
 export namespace AssemblySymmetry {
     export enum Tag {
         Cluster = 'rcsb-assembly-symmetry-cluster',
@@ -35,19 +47,11 @@ export namespace AssemblySymmetry {
     export const DefaultServerUrl = 'https://data-beta.rcsb.org/graphql'
 
     export function isApplicable(structure?: Structure): boolean {
-        // check if structure is from pdb entry
-        if (!structure || structure.models.length !== 1 || !MmcifFormat.is(structure.models[0].sourceData) || (!structure.models[0].sourceData.data.db.database_2.database_id.isDefined &&
-        structure.models[0].entryId.length !== 4)) return false
-
-        // check if assembly is 'biological'
-        const mmcif = structure.models[0].sourceData.data.db
-        if (!mmcif.pdbx_struct_assembly.details.isDefined) return false
-        const id = structure.units[0].conformation.operator.assembly?.id || ''
-        if (id === '' || id === 'deposited') return true
-        const indices = Column.indicesOf(mmcif.pdbx_struct_assembly.id, e => e === id)
-        if (indices.length !== 1) return false
-        const details = mmcif.pdbx_struct_assembly.details.value(indices[0])
-        return BiologicalAssemblyNames.has(details)
+        return (
+            !!structure && structure.models.length === 1 &&
+            Model.isFromPdbArchive(structure.models[0]) &&
+            isBiologicalAssembly(structure)
+        )
     }
 
     export async function fetch(ctx: CustomProperty.Context, structure: Structure, props: AssemblySymmetryDataProps): Promise<AssemblySymmetryDataValue> {

+ 3 - 0
src/mol-plugin/behavior/dynamic/custom-props/rcsb/assembly-symmetry.ts

@@ -155,6 +155,9 @@ export const AssemblySymmetryPreset = StructureRepresentationPresetProvider({
         name: 'Assembly Symmetry', group: 'Annotation',
         description: 'Shows Assembly Symmetry axes and cage; colors structure according to assembly symmetry cluster membership. Data calculated with BioJava, obtained via RCSB PDB.'
     },
+    isApplicable(a) {
+        return AssemblySymmetry.isApplicable(a.data)
+    },
     params: () => AssemblySymmetryPresetParams,
     async apply(ref, params, plugin) {
         const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);

+ 68 - 10
src/mol-plugin/behavior/dynamic/custom-props/rcsb/validation-report.ts

@@ -20,6 +20,7 @@ import { MolScriptBuilder as MS } from '../../../../../mol-script/language/build
 import { Task } from '../../../../../mol-task';
 import { StructureRepresentationPresetProvider, PresetStructureRepresentations } from '../../../../../mol-plugin-state/builder/structure/representation-preset';
 import { StateObjectRef } from '../../../../../mol-state';
+import { Model } from '../../../../../mol-model/structure';
 
 export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean, showTooltip: boolean }>({
     name: 'rcsb-validation-report-prop',
@@ -53,7 +54,10 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
 
             this.ctx.representation.structure.registry.add(ClashesRepresentationProvider)
             this.ctx.query.structure.registry.add(hasClash)
-            this.ctx.builders.structure.representation.registerPreset(ValidationReportPreset)
+
+            this.ctx.builders.structure.representation.registerPreset(ValidationReportGeometryQualityPreset)
+            this.ctx.builders.structure.representation.registerPreset(ValidationReportDensityFitPreset)
+            this.ctx.builders.structure.representation.registerPreset(ValidationReportRandomCoilIndexPreset)
         }
 
         update(p: { autoAttach: boolean, showTooltip: boolean }) {
@@ -78,7 +82,10 @@ export const RCSBValidationReport = PluginBehavior.create<{ autoAttach: boolean,
 
             this.ctx.representation.structure.registry.remove(ClashesRepresentationProvider)
             this.ctx.query.structure.registry.remove(hasClash)
-            this.ctx.builders.structure.representation.unregisterPreset(ValidationReportPreset)
+
+            this.ctx.builders.structure.representation.unregisterPreset(ValidationReportGeometryQualityPreset)
+            this.ctx.builders.structure.representation.unregisterPreset(ValidationReportDensityFitPreset)
+            this.ctx.builders.structure.representation.unregisterPreset(ValidationReportRandomCoilIndexPreset)
         }
     },
     params: () => ({
@@ -99,7 +106,7 @@ function geometryQualityLabel(loci: Loci): string | undefined {
 
             const validationReport = ValidationReportProvider.get(unit.model).value
             if (!validationReport) return
-            if (unit.model.customProperties.hasReference(ValidationReportProvider.descriptor)) return
+            if (!unit.model.customProperties.hasReference(ValidationReportProvider.descriptor)) return
 
             const { bondOutliers, angleOutliers } = validationReport
             const eI = unit.elements[OrderedSet.start(indices)]
@@ -127,6 +134,7 @@ function geometryQualityLabel(loci: Loci): string | undefined {
         for (const { indices, unit } of loci.elements) {
             const validationReport = ValidationReportProvider.get(unit.model).value
             if (!validationReport) continue
+            if (!unit.model.customProperties.hasReference(ValidationReportProvider.descriptor)) continue
             hasValidationReport = true
 
             const { geometryIssues } = validationReport
@@ -180,7 +188,7 @@ function densityFitLabel(loci: Loci): string | undefined {
         for (const { indices, unit } of loci.elements) {
             const validationReport = ValidationReportProvider.get(unit.model).value
             if (!validationReport) continue
-            if (unit.model.customProperties.hasReference(ValidationReportProvider.descriptor)) continue
+            if (!unit.model.customProperties.hasReference(ValidationReportProvider.descriptor)) continue
 
             const { rsrz, rscc } = validationReport
             const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index
@@ -237,7 +245,7 @@ function randomCoilIndexLabel(loci: Loci): string | undefined {
         for (const { indices, unit } of loci.elements) {
             const validationReport = ValidationReportProvider.get(unit.model).value
             if (!validationReport) continue
-            if (unit.model.customProperties.hasReference(ValidationReportProvider.descriptor)) continue
+            if (!unit.model.customProperties.hasReference(ValidationReportProvider.descriptor)) continue
 
             const { rci } = validationReport
             const residueIndex = unit.model.atomicHierarchy.residueAtomSegments.index
@@ -288,24 +296,26 @@ const hasClash = StructureSelectionQuery('Residues with Clashes', MS.struct.modi
 
 //
 
-export const ValidationReportPreset = StructureRepresentationPresetProvider({
-    id: 'preset-structure-representation-rcsb-validation-report',
+export const ValidationReportGeometryQualityPreset = StructureRepresentationPresetProvider({
+    id: 'preset-structure-representation-rcsb-validation-report-geometry-uality',
     display: {
-        name: 'Validation Report', group: 'Annotation',
+        name: 'Validation Report (Geometry Quality)', group: 'Annotation',
         description: 'Color structure based on geometry quality; show geometry clashes. Data from wwPDB Validation Report, obtained via RCSB PDB.'
     },
+    isApplicable(a) {
+        return a.data.models.length === 1 && ValidationReport.isApplicable(a.data.models[0])
+    },
     params: () => StructureRepresentationPresetProvider.CommonParams,
     async apply(ref, params, plugin) {
         const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
         const model = structureCell?.obj?.data.model
         if (!structureCell || !model) return {};
 
-        const colorTheme = GeometryQualityColorThemeProvider.name as any
-
         await plugin.runTask(Task.create('Validation Report', async runtime => {
             await ValidationReportProvider.attach({ fetch: plugin.fetch, runtime }, model)
         }))
 
+        const colorTheme = GeometryQualityColorThemeProvider.name as any
         const { components, representations } = await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme }, plugin)
 
         const clashes = await plugin.builders.structure.tryCreateComponentFromExpression(structureCell, hasClash.expression, 'clashes', { label: 'Clashes' })
@@ -320,4 +330,52 @@ export const ValidationReportPreset = StructureRepresentationPresetProvider({
         await plugin.updateDataState(update, { revertOnError: false });
         return { components: { ...components, clashes }, representations: { ...representations, clashesBallAndStick, clashesSnfg3d } };
     }
+});
+
+export const ValidationReportDensityFitPreset = StructureRepresentationPresetProvider({
+    id: 'preset-structure-representation-rcsb-validation-report-density-fit',
+    display: {
+        name: 'Validation Report (Density Fit)', group: 'Annotation',
+        description: 'Color structure based on density fit. Data from wwPDB Validation Report, obtained via RCSB PDB.'
+    },
+    isApplicable(a) {
+        return a.data.models.length === 1 && ValidationReport.isApplicable(a.data.models[0]) && Model.hasXrayMap(a.data.models[0])
+    },
+    params: () => StructureRepresentationPresetProvider.CommonParams,
+    async apply(ref, params, plugin) {
+        const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
+        const model = structureCell?.obj?.data.model
+        if (!structureCell || !model) return {};
+
+        await plugin.runTask(Task.create('Validation Report', async runtime => {
+            await ValidationReportProvider.attach({ fetch: plugin.fetch, runtime }, model)
+        }))
+
+        const colorTheme = DensityFitColorThemeProvider.name as any
+        return await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme }, plugin)
+    }
+});
+
+export const ValidationReportRandomCoilIndexPreset = StructureRepresentationPresetProvider({
+    id: 'preset-structure-representation-rcsb-validation-report-random-coil-index',
+    display: {
+        name: 'Validation Report (Random Coil Index)', group: 'Annotation',
+        description: 'Color structure based on Random Coil Index. Data from wwPDB Validation Report, obtained via RCSB PDB.'
+    },
+    isApplicable(a) {
+        return a.data.models.length === 1 && ValidationReport.isApplicable(a.data.models[0]) && Model.isFromNmr(a.data.models[0])
+    },
+    params: () => StructureRepresentationPresetProvider.CommonParams,
+    async apply(ref, params, plugin) {
+        const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, ref);
+        const model = structureCell?.obj?.data.model
+        if (!structureCell || !model) return {};
+
+        await plugin.runTask(Task.create('Validation Report', async runtime => {
+            await ValidationReportProvider.attach({ fetch: plugin.fetch, runtime }, model)
+        }))
+
+        const colorTheme = RandomCoilIndexColorThemeProvider.name as any
+        return await PresetStructureRepresentations.auto.apply(ref, { ...params, globalThemeName: colorTheme }, plugin)
+    }
 });