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

added support to build custom assembly from symmetry operations and asym ids

parent 3681f01f
No related branches found
No related tags found
No related merge requests found
/** /**
* Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* *
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Alexander Rose <alexander.rose@weirdbyte.de>
...@@ -11,10 +11,11 @@ import { Spacegroup, SpacegroupCell, SymmetryOperator } from '../../../mol-math/ ...@@ -11,10 +11,11 @@ import { Spacegroup, SpacegroupCell, SymmetryOperator } from '../../../mol-math/
import { Vec3, Mat4 } from '../../../mol-math/linear-algebra'; import { Vec3, Mat4 } from '../../../mol-math/linear-algebra';
import { RuntimeContext, Task } from '../../../mol-task'; import { RuntimeContext, Task } from '../../../mol-task';
import { Symmetry, Model } from '../model'; import { Symmetry, Model } from '../model';
import { QueryContext, StructureSelection } from '../query'; import { QueryContext, StructureSelection, Queries as Q } from '../query';
import Structure from './structure'; import Structure from './structure';
import Unit from './unit'; import Unit from './unit';
import { ModelSymmetry } from '../../../mol-model-formats/structure/property/symmetry'; import { ModelSymmetry } from '../../../mol-model-formats/structure/property/symmetry';
import StructureProperties from './properties';
namespace StructureSymmetry { namespace StructureSymmetry {
export function buildAssembly(structure: Structure, asmName: string) { export function buildAssembly(structure: Structure, asmName: string) {
...@@ -48,6 +49,40 @@ namespace StructureSymmetry { ...@@ -48,6 +49,40 @@ namespace StructureSymmetry {
}); });
} }
export type Generators = { operators: { index: number, shift: Vec3 }[], asymIds: string[] }[]
export function buildSymmetryAssembly(structure: Structure, generators: Generators, symmetry: Symmetry) {
return Task.create('Build Symmetry Assembly', async ctx => {
const models = structure.models;
if (models.length !== 1) throw new Error('Can only build symmetry assemblies from structures based on 1 model.');
const modelCenter = Vec3() // Model.getCenter(models[0])
const assembler = Structure.Builder({ label: structure.label });
const queryCtx = new QueryContext(structure);
for (const g of generators) {
const selector = getSelector(g.asymIds);
const selection = selector(queryCtx);
if (StructureSelection.structureCount(selection) === 0) {
continue;
}
const { units } = StructureSelection.unionStructure(selection);
for (const { index, shift: [i, j, k] } of g.operators) {
const operators = getOperatorsForIndex(symmetry, index, i, j, k, modelCenter)
for (const unit of units) {
for (const op of operators) {
assembler.addWithOperator(unit, op);
}
}
}
}
return assembler.getStructure();
});
}
export function builderSymmetryMates(structure: Structure, radius: number) { export function builderSymmetryMates(structure: Structure, radius: number) {
return Task.create('Find Symmetry Mates', ctx => findMatesRadius(ctx, structure, radius)); return Task.create('Find Symmetry Mates', ctx => findMatesRadius(ctx, structure, radius));
} }
...@@ -96,7 +131,35 @@ namespace StructureSymmetry { ...@@ -96,7 +131,35 @@ namespace StructureSymmetry {
} }
} }
function getOperators(symmetry: Symmetry, ijkMin: Vec3, ijkMax: Vec3, modelCenter: Vec3) { function getSelector(asymIds: string[]) {
return Q.generators.atoms({ chainTest: Q.pred.and(
Q.pred.eq(ctx => StructureProperties.unit.operator_name(ctx.element), SymmetryOperator.DefaultName),
Q.pred.inSet(ctx => StructureProperties.chain.label_asym_id(ctx.element), asymIds)
)});
}
function getOperatorsForIndex(symmetry: Symmetry, index: number, i: number, j: number, k: number, modelCenter: Vec3) {
const { spacegroup, ncsOperators } = symmetry;
const operators: SymmetryOperator[] = []
const { toFractional } = spacegroup.cell
const ref = Vec3.transformMat4(Vec3(), modelCenter, toFractional)
const symOp = Spacegroup.getSymmetryOperatorRef(spacegroup, index, i, j, k, ref)
if (ncsOperators && ncsOperators.length) {
for (let u = 0, ul = ncsOperators.length; u < ul; ++u) {
const ncsOp = ncsOperators![u]
const matrix = Mat4.mul(Mat4(), symOp.matrix, ncsOp.matrix)
const operator = SymmetryOperator.create(`${symOp.name} ${ncsOp.name}`, matrix, symOp.assembly, ncsOp.ncsId, symOp.hkl, symOp.spgrOp);
operators.push(operator)
}
} else {
operators.push(symOp)
}
return operators
}
function getOperatorsForRange(symmetry: Symmetry, ijkMin: Vec3, ijkMax: Vec3, modelCenter: Vec3) {
const { spacegroup, ncsOperators } = symmetry; const { spacegroup, ncsOperators } = symmetry;
const ncsCount = (ncsOperators && ncsOperators.length) || 0 const ncsCount = (ncsOperators && ncsOperators.length) || 0
const operators: SymmetryOperator[] = []; const operators: SymmetryOperator[] = [];
...@@ -117,18 +180,7 @@ function getOperators(symmetry: Symmetry, ijkMin: Vec3, ijkMax: Vec3, modelCente ...@@ -117,18 +180,7 @@ function getOperators(symmetry: Symmetry, ijkMin: Vec3, ijkMax: Vec3, modelCente
for (let k = ijkMin[2]; k <= ijkMax[2]; k++) { for (let k = ijkMin[2]; k <= ijkMax[2]; k++) {
// check if we have added identity as the 1st operator. // check if we have added identity as the 1st operator.
if (!ncsCount && op === 0 && i === 0 && j === 0 && k === 0) continue; if (!ncsCount && op === 0 && i === 0 && j === 0 && k === 0) continue;
operators.push(...getOperatorsForIndex(symmetry, op, i, j, k, ref))
const symOp = Spacegroup.getSymmetryOperatorRef(spacegroup, op, i, j, k, ref)
if (ncsCount) {
for (let u = 0; u < ncsCount; ++u) {
const ncsOp = ncsOperators![u]
const matrix = Mat4.mul(Mat4.zero(), symOp.matrix, ncsOp.matrix)
const operator = SymmetryOperator.create(`${symOp.name} ${ncsOp.name}`, matrix, symOp.assembly, ncsOp.ncsId, symOp.hkl, symOp.spgrOp);
operators[operators.length] = operator;
}
} else {
operators[operators.length] = symOp;
}
} }
} }
} }
...@@ -142,7 +194,7 @@ function getOperatorsCached333(symmetry: Symmetry, ref: Vec3) { ...@@ -142,7 +194,7 @@ function getOperatorsCached333(symmetry: Symmetry, ref: Vec3) {
} }
symmetry._operators_333 = { symmetry._operators_333 = {
ref: Vec3.clone(ref), ref: Vec3.clone(ref),
operators: getOperators(symmetry, Vec3.create(-3, -3, -3), Vec3.create(3, 3, 3), ref) operators: getOperatorsForRange(symmetry, Vec3.create(-3, -3, -3), Vec3.create(3, 3, 3), ref)
}; };
return symmetry._operators_333.operators; return symmetry._operators_333.operators;
} }
...@@ -181,7 +233,7 @@ async function findSymmetryRange(ctx: RuntimeContext, structure: Structure, ijkM ...@@ -181,7 +233,7 @@ async function findSymmetryRange(ctx: RuntimeContext, structure: Structure, ijkM
if (SpacegroupCell.isZero(spacegroup.cell)) return structure; if (SpacegroupCell.isZero(spacegroup.cell)) return structure;
const modelCenter = Model.getCenter(models[0]) const modelCenter = Model.getCenter(models[0])
const operators = getOperators(symmetry, ijkMin, ijkMax, modelCenter); const operators = getOperatorsForRange(symmetry, ijkMin, ijkMax, modelCenter);
return assembleOperators(structure, operators); return assembleOperators(structure, operators);
} }
......
/** /**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
* *
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/ */
import { Model, Structure, StructureSymmetry } from '../../../mol-model/structure'; import { Model, Structure, StructureSymmetry } from '../../../mol-model/structure';
import { stringToWords } from '../../../mol-util/string'; import { stringToWords } from '../../../mol-util/string';
import { SpacegroupCell } from '../../../mol-math/geometry'; import { SpacegroupCell, Spacegroup } from '../../../mol-math/geometry';
import { ParamDefinition as PD } from '../../../mol-util/param-definition'; import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { Vec3 } from '../../../mol-math/linear-algebra'; import { Vec3 } from '../../../mol-math/linear-algebra';
import { RuntimeContext } from '../../../mol-task'; import { RuntimeContext } from '../../../mol-task';
...@@ -14,14 +15,33 @@ import { PluginContext } from '../../context'; ...@@ -14,14 +15,33 @@ import { PluginContext } from '../../context';
import { Assembly, Symmetry } from '../../../mol-model/structure/model/properties/symmetry'; import { Assembly, Symmetry } from '../../../mol-model/structure/model/properties/symmetry';
import { PluginStateObject as SO } from '../objects'; import { PluginStateObject as SO } from '../objects';
import { ModelSymmetry } from '../../../mol-model-formats/structure/property/symmetry'; import { ModelSymmetry } from '../../../mol-model-formats/structure/property/symmetry';
import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif';
export namespace ModelStructureRepresentation { export namespace ModelStructureRepresentation {
export function getParams(model?: Model, defaultValue?: 'deposited' | 'assembly' | 'symmetry' | 'symmetry-mates') { export function getParams(model?: Model, defaultValue?: 'deposited' | 'assembly' | 'symmetry' | 'symmetry-mates' | 'symmetry-assembly') {
const symmetry = model && ModelSymmetry.Provider.get(model) const symmetry = model && ModelSymmetry.Provider.get(model)
const assemblyIds = symmetry ? symmetry.assemblies.map(a => [a.id, `${a.id}: ${stringToWords(a.details)}`] as [string, string]) : []; const assemblyIds = symmetry ? symmetry.assemblies.map(a => [a.id, `${a.id}: ${stringToWords(a.details)}`] as [string, string]) : [];
const showSymm = !symmetry ? true : !SpacegroupCell.isZero(symmetry.spacegroup.cell); const showSymm = !symmetry ? true : !SpacegroupCell.isZero(symmetry.spacegroup.cell);
const operatorOptions: [number, string][] = []
if (symmetry) {
const { operators } = symmetry.spacegroup
for (let i = 0, il = operators.length; i < il; i++) {
operatorOptions.push([i, `${i + 1}: ${Spacegroup.getOperatorXyz(operators[i])}`])
}
}
const asymIdsOptions: [string, string][] = []
if (model && MmcifFormat.is(model?.sourceData)) {
// TODO make generally available for models, also include auth_asym_id
const { struct_asym } = model.sourceData.data.db
for (let i = 0, il = struct_asym._rowCount; i < il; ++i) {
const id = struct_asym.id.value(i)
asymIdsOptions.push([id, id])
}
}
const modes = { const modes = {
deposited: PD.EmptyGroup(), deposited: PD.EmptyGroup(),
assembly: PD.Group({ assembly: PD.Group({
...@@ -35,6 +55,19 @@ export namespace ModelStructureRepresentation { ...@@ -35,6 +55,19 @@ export namespace ModelStructureRepresentation {
'symmetry': PD.Group({ 'symmetry': PD.Group({
ijkMin: PD.Vec3(Vec3.create(-1, -1, -1), { label: 'Min IJK', fieldLabels: { x: 'I', y: 'J', z: 'K' } }), ijkMin: PD.Vec3(Vec3.create(-1, -1, -1), { label: 'Min IJK', fieldLabels: { x: 'I', y: 'J', z: 'K' } }),
ijkMax: PD.Vec3(Vec3.create(1, 1, 1), { label: 'Max IJK', fieldLabels: { x: 'I', y: 'J', z: 'K' } }) ijkMax: PD.Vec3(Vec3.create(1, 1, 1), { label: 'Max IJK', fieldLabels: { x: 'I', y: 'J', z: 'K' } })
}, { isFlat: true }),
'symmetry-assembly': PD.Group({
generators: PD.ObjectList({
operators: PD.ObjectList({
index: PD.Select(0, operatorOptions),
shift: PD.Vec3(Vec3(), { label: 'IJK', fieldLabels: { x: 'I', y: 'J', z: 'K' } })
}, e => `${e.index + 1}_${e.shift.map(a => a + 5).join('')}`, {
defaultValue: [] as { index: number, shift: Vec3 }[]
}),
asymIds: PD.MultiSelect([] as string[], asymIdsOptions)
}, e => `${e.asymIds.length} asym ids, ${e.operators.length} operators`, {
defaultValue: [] as { operators: { index: number, shift: Vec3 }[], asymIds: string[] }[]
})
}, { isFlat: true }) }, { isFlat: true })
}; };
...@@ -49,6 +82,7 @@ export namespace ModelStructureRepresentation { ...@@ -49,6 +82,7 @@ export namespace ModelStructureRepresentation {
if (showSymm) { if (showSymm) {
options.push(['symmetry-mates', 'Symmetry Mates']); options.push(['symmetry-mates', 'Symmetry Mates']);
options.push(['symmetry', 'Symmetry (indices)']); options.push(['symmetry', 'Symmetry (indices)']);
options.push(['symmetry-assembly', 'Symmetry (assembly)']);
} }
return { return {
...@@ -105,8 +139,16 @@ export namespace ModelStructureRepresentation { ...@@ -105,8 +139,16 @@ export namespace ModelStructureRepresentation {
return new SO.Molecule.Structure(s, props); return new SO.Molecule.Structure(s, props);
} }
async function buildSymmetryAssembly(ctx: RuntimeContext, model: Model, generators: StructureSymmetry.Generators, symmetry: Symmetry) {
const base = Structure.ofModel(model);
const s = await StructureSymmetry.buildSymmetryAssembly(base, generators, symmetry).runInContext(ctx);
const props = { label: `Symmetry Assembly`, description: Structure.elementDescription(s) };
return new SO.Molecule.Structure(s, props);
}
export async function create(plugin: PluginContext, ctx: RuntimeContext, model: Model, params?: Params): Promise<SO.Molecule.Structure> { export async function create(plugin: PluginContext, ctx: RuntimeContext, model: Model, params?: Params): Promise<SO.Molecule.Structure> {
if (!params || params.name === 'deposited') { const symmetry = ModelSymmetry.Provider.get(model)
if (!symmetry || !params || params.name === 'deposited') {
const s = Structure.ofModel(model); const s = Structure.ofModel(model);
return new SO.Molecule.Structure(s, { label: 'Deposited', description: Structure.elementDescription(s) }); return new SO.Molecule.Structure(s, { label: 'Deposited', description: Structure.elementDescription(s) });
} }
...@@ -119,6 +161,9 @@ export namespace ModelStructureRepresentation { ...@@ -119,6 +161,9 @@ export namespace ModelStructureRepresentation {
if (params.name === 'symmetry-mates') { if (params.name === 'symmetry-mates') {
return buildSymmetryMates(ctx, model, params.params.radius) return buildSymmetryMates(ctx, model, params.params.radius)
} }
if (params.name === 'symmetry-assembly') {
return buildSymmetryAssembly(ctx, model, params.params.generators, symmetry)
}
throw new Error(`Unknown represetation type: ${(params as any).name}`); throw new Error(`Unknown represetation type: ${(params as any).name}`);
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment