Skip to content
Snippets Groups Projects
Commit b7fa577d authored by Alexander Rose's avatar Alexander Rose
Browse files

improved validation-report and assembly-symmetry presets

parent 949425d1
Branches
No related tags found
No related merge requests found
......@@ -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> {
......
......@@ -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);
......
......@@ -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' })
......@@ -321,3 +331,51 @@ export const ValidationReportPreset = StructureRepresentationPresetProvider({
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)
}
});
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment