Parcourir la source

centroid and extent calculation

JonStargaryen il y a 4 ans
Parent
commit
5fcb495d24

+ 75 - 0
src/mol-model-props/computed/themes/topology.ts

@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { ParamDefinition as PD } from '../../../mol-util/param-definition';
+import { Color, ColorScale } from '../../../mol-util/color';
+import { ThemeDataContext } from '../../../mol-theme/theme';
+import { ColorTheme, LocationColor } from '../../../mol-theme/color';
+import { CustomProperty } from '../../common/custom-property';
+import { Location } from '../../../mol-model/location';
+
+const DefaultColor = Color(0xFAFAFA);
+const Description = 'Assigns a color based on the membrane topology of a residue.';
+
+export const TopologyColorThemeParams = {
+    list: PD.ColorList('rainbow', { presetKind: 'scale' }) // change to binary
+};
+export type TopologyColorThemeParams = typeof TopologyColorThemeParams
+export function getTopologyColorThemeParams(ctx: ThemeDataContext) {
+    return TopologyColorThemeParams; // TODO return copy
+}
+export function TopologyColorTheme(ctx: ThemeDataContext, props: PD.Values<TopologyColorThemeParams>): ColorTheme<TopologyColorThemeParams> {
+    let color: LocationColor;
+
+    const scale = ColorScale.create({
+        listOrName: props.list.colors,
+        minLabel: 'membrane',
+        maxLabel: 'non-membrane',
+        domain: [0.0, 1.0]
+    }); // prolly not needed
+
+    // const accessibleSurfaceArea = ctx.structure && AccessibleSurfaceAreaProvider.get(ctx.structure);
+    // const contextHash = accessibleSurfaceArea?.version;
+
+    if (/*accessibleSurfaceArea?.value &&*/ ctx.structure) {
+        // const asa = accessibleSurfaceArea.value;
+
+        color = (location: Location): Color => {
+            // if (StructureElement.Location.is(location) && Unit.isAtomic(location.unit)) {
+                // const value = AccessibleSurfaceArea.getNormalizedValue(location, asa);
+                // return value === -1 ? DefaultColor : scale.color(value);
+            // }
+            return DefaultColor;
+        };
+    } else {
+        color = () => DefaultColor;
+    }
+
+    return {
+        factory: TopologyColorTheme,
+        granularity: 'group',
+        color,
+        props,
+        /*contextHash*/'',
+        description: Description,
+        legend: scale ? scale.legend : undefined
+    };
+}
+
+export const TopologyColorThemeProvider: ColorTheme.Provider<TopologyColorThemeParams, 'topology'> = {
+    name: 'topology',
+    label: 'Membrane Topology',
+    category: ColorTheme.Category.Residue,
+    factory: TopologyColorTheme,
+    getParams: getTopologyColorThemeParams,
+    defaultValues: PD.getDefaultValues(TopologyColorThemeParams),
+    isApplicable: (ctx: ThemeDataContext) => !!ctx.structure,
+    ensureCustomProperties: {
+        attach: (ctx: CustomProperty.Context, data: ThemeDataContext) => data.structure ? TopologyProvider.attach(ctx, data.structure, void 0, true) : Promise.resolve(),
+        detach: (data) => data.structure && data.structure.customPropertyDescriptors.reference(TopologyProvider.descriptor, false)
+    }
+};

+ 58 - 0
src/mol-model-props/computed/topology.ts

@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { ParamDefinition as PD } from '../../mol-util/param-definition';
+import { Structure, Unit } from '../../mol-model/structure';
+import { CustomStructureProperty } from '../common/custom-structure-property';
+import { CustomProperty } from '../common/custom-property';
+import { QuerySymbolRuntime } from '../../mol-script/runtime/query/compiler';
+import { CustomPropSymbol } from '../../mol-script/language/symbol';
+import Type from '../../mol-script/language/type';
+import { CustomPropertyDescriptor } from '../../mol-model/custom-property';
+import { ANVILParams, Topology } from './topology/ANVIL';
+
+export const TopologyParams = {
+    ...ANVILParams
+};
+export type TopologyParams = typeof TopologyParams
+export type TopologyProps = PD.Values<TopologyParams>
+
+export const TopologySymbols = {
+    // TODO is this delegation needed?
+    isMembrane: QuerySymbolRuntime.Dynamic(CustomPropSymbol('computed', 'topology.is-membrane', Type.Bool),
+        ctx => {
+            if (!Unit.isAtomic(ctx.element.unit)) return false;
+            return !TopologyProvider.get(ctx.element.structure).value;
+        }
+    ),
+    isNotMembrane: QuerySymbolRuntime.Dynamic(CustomPropSymbol('computed', 'topology.is-not-membrane', Type.Bool),
+        ctx => {
+            if (!Unit.isAtomic(ctx.element.unit)) return false;
+            return TopologyProvider.get(ctx.element.structure).value;
+        }
+    ),
+};
+
+export type TopologyValue = Map<number, Topology>
+
+export const TopologyProvider: CustomStructureProperty.Provider<TopologyParams, Topology> = CustomStructureProperty.createProvider({
+    label: 'Predicted Membrane Topology',
+    descriptor: CustomPropertyDescriptor({
+        name: 'molstar_topology',
+        // TODO `cifExport`
+    }),
+    type: 'root',
+    defaultParams: TopologyParams,
+    getParams: (data: Structure) => TopologyParams,
+    isApplicable: (data: Structure) => true, 
+    // TODO needs ASA to be computed (or 'resolved' before trying computing topology) - how to achieve?
+    // TODO potentially, this should behave like secondary structure info where data can be either parsed or computed
+    obtain: async (ctx: CustomProperty.Context, data: Structure, props: Partial<TopologyProps>) => {
+        const p = { ...PD.getDefaultValues(TopologyParams), ...props };
+        return { value: await Topology.compute(data, p).runInContext(ctx.runtime) };
+    }
+});

+ 145 - 0
src/mol-model-props/computed/topology/ANVIL.ts

@@ -0,0 +1,145 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { Task, RuntimeContext } from '../../../mol-task';
+import { ParamDefinition as PD } from '../../../mol-util/param-definition';
+import { Structure, StructureElement, StructureProperties } from '../../../mol-model/structure';
+import { getElementMoleculeType } from '../../../mol-model/structure/util';
+import { MoleculeType } from '../../../mol-model/structure/model/types';
+import { CentroidHelper } from '../../../mol-math/geometry/centroid-helper';
+import { Vec3 } from '../../../mol-math/linear-algebra';
+import { AccessibleSurfaceArea } from '../accessible-surface-area/shrake-rupley';
+import { AccessibleSurfaceAreaProvider } from '../accessible-surface-area';
+
+export const ANVILParams = {
+    numberOfSpherePoints: PD.Numeric(350),
+    stepSize: PD.Numeric(1),
+    minThickness: PD.Numeric(20, { min: 10, max: 30, step: 1}, { description: 'Minimum membrane thickness used during refinement' }),
+    maxThickness: PD.Numeric(40, { min: 30, max: 50, step: 1}, { description: 'Maximum membrane thickness used during refinement' }),
+    afilter: PD.Numeric(40),
+    membranePointDensity: PD.Numeric(2, { min: 0.1, max: 10, step: 0.1 }, { description: 'Distance betwween points representing membrane layer'})
+};
+export type ANVILParams = typeof ANVILParams
+export type ANVILProps = PD.Values<ANVILParams>
+
+export { Topology };
+
+interface Topology {
+    readonly serialResidueIndex: ArrayLike<number>
+    readonly exposure: ArrayLike<Topology>
+}
+
+namespace Topology {
+    /**
+     * Implements:
+     * Membrane positioning for high- and low-resolution protein structures through a binary classification approach
+     * Guillaume Postic, Yassine Ghouzam, Vincent Guiraud, and Jean-Christophe Gelly
+     * Protein Engineering, Design & Selection, 2015, 1–5
+     * doi: 10.1093/protein/gzv063
+     */
+    export function compute(structure: Structure, props: Partial<ANVILProps> = {}) {
+        const p = { ...PD.getDefaultValues(ANVILParams), ...props };
+        return Task.create('Compute Membrane Topology', async runtime => {
+            return await calculate(runtime, structure, p);
+        });
+    }
+
+    const l = StructureElement.Location.create(void 0);
+    const centroidHelper = new CentroidHelper();
+    const vec = Vec3();
+    export async function calculate(runtime: RuntimeContext, structure: Structure, params: ANVILProps): Promise<Topology> {
+        const { label_atom_id, x, y, z } = StructureProperties.atom;
+        const elementCount = structure.elementCount;
+        centroidHelper.reset();
+        l.structure = structure;
+
+        const offsets = new Int32Array(elementCount);
+        const exposed: Array<Boolean> = new Array(elementCount);
+
+        // ensure ASA
+        const accessibleSurfaceArea = structure && AccessibleSurfaceAreaProvider.get(structure);
+        const asa = accessibleSurfaceArea.value!;
+        
+        let m = 0;
+        for (let i = 0, il = structure.units.length; i < il; ++i) {
+            const unit = structure.units[i];
+            const { elements } = unit;
+            l.unit = unit;
+
+            for (let j = 0, jl = elements.length; j < jl; ++j) {
+                const eI = elements[j];
+                l.element = eI;
+
+                // consider only amino acids
+                if (getElementMoleculeType(unit, eI) !== MoleculeType.Protein) {
+                    continue;
+                }
+
+                // only CA is considered for downstream operations
+                if (label_atom_id(l) !== 'CA') {
+                    continue;
+                }
+
+                // while iterating use first pass to compute centroid
+                Vec3.set(vec, x(l), y(l), z(l));
+                // console.log(vec);
+                centroidHelper.includeStep(vec);
+
+                // keep track of offsets and exposed state to reuse
+                offsets[m] = l.element;
+                exposed[m] = AccessibleSurfaceArea.getValue(l, asa) > params.afilter;
+
+                m++;
+            }
+        }
+
+        centroidHelper.finishedIncludeStep();
+        console.log(centroidHelper.center);
+
+        for (let k = 0; k < m; k++) {
+            setLocation(l, structure, offsets[k]);
+            Vec3.set(vec, x(l), y(l), z(l));
+            // console.log(vec);
+            centroidHelper.radiusStep(vec);
+        }
+
+        console.log(1.2 * Math.sqrt(centroidHelper.radiusSq));
+        
+        return {
+            exposure: new Array(),
+            serialResidueIndex: new Array()
+        };
+    }
+
+    function setLocation(l: StructureElement.Location, structure: Structure, serialIndex: number) {
+        l.structure = structure;
+        l.unit = structure.units[structure.serialMapping.unitIndices[serialIndex]];
+        l.element = structure.serialMapping.elementIndices[serialIndex];
+        return l;
+    }
+
+    export const enum Flag {
+        NA = 0x0,
+        Membrane = 0x1,
+        NotMembrane = 0x2
+    }
+
+    export function getValue(location: StructureElement.Location, topology: Topology) {
+        const { getSerialIndex } = location.structure.root.serialMapping;
+        const { exposure, serialResidueIndex } = topology;
+        const rSI = serialResidueIndex[getSerialIndex(location.unit, location.element)];
+        if (rSI === -1) return -1;
+        return exposure[rSI];
+    }
+
+    export function getFlag(location: StructureElement.Location, topology: Topology) {
+        const value = getValue(location, topology);
+        return value === -1 ? Flag.NA :
+            value ? Flag.NotMembrane :
+                Flag.Membrane;
+    }
+}

+ 23 - 0
src/mol-model-props/computed/topology/anvil/common.ts

@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Sebastian Bittrich <sebastian.bittrich@rcsb.org>
+ */
+
+import { Structure } from '../../../../mol-model/structure';
+import { Topology } from '../ANVIL';
+import { Vec3 } from '../../../../mol-math/linear-algebra';
+
+export interface ANVILContext {
+    structure: Structure,
+    numberOfSpherePoints: number,
+    stepSize: number,
+    minThickness: number,
+    maxThickness: number,
+    afilter: number,
+    membranePointDensity: number,
+    centerOfMass: Vec3,
+    maxExtent: number,
+    serialResidueIndex: Int32Array,
+    exposure: ArrayLike<Topology>
+}

+ 0 - 0
src/mol-plugin/behavior/dynamic/custom-props/computed/topology.ts


+ 2 - 1
src/tests/browser/render-structure.ts

@@ -27,6 +27,7 @@ import { SecondaryStructureProvider } from '../../mol-model-props/computed/secon
 import { SyncRuntimeContext } from '../../mol-task/execution/synchronous';
 import { AssetManager } from '../../mol-util/assets';
 import { AccessibleSurfaceAreaProvider } from '../../mol-model-props/computed/accessible-surface-area';
+import { TopologyProvider } from '../../mol-model-props/computed/topology';
 
 const parent = document.getElementById('app')!;
 parent.style.width = '100%';
@@ -120,7 +121,7 @@ function getGaussianSurfaceRepr() {
 async function init() {
     const ctx = { runtime: SyncRuntimeContext, assetManager: new AssetManager() };
 
-    const cif = await downloadFromPdb('3pqr');
+    const cif = await downloadFromPdb('1brr');
     const models = await getModels(cif);
     const structure = await getStructure(models[0]);