Skip to content
Snippets Groups Projects
Commit 5fcb495d authored by JonStargaryen's avatar JonStargaryen
Browse files

centroid and extent calculation

parent 5e6eb7ed
No related branches found
No related tags found
No related merge requests found
/**
* 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)
}
};
\ No newline at end of file
/**
* 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) };
}
});
\ No newline at end of file
/**
* 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;
}
}
\ No newline at end of file
/**
* 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>
}
\ No newline at end of file
......@@ -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]);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment