Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/superpose #6

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions src/lib/elements/Superposition.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<script lang="ts">
/**
* Load a structure from an URL
* @param url URL to load the structure from
* @param type Type of the structure
* @param chainId Chain ID to select
*/
import { getContext, onMount, onDestroy } from 'svelte';
import type { PluginContext } from 'molstar/lib/mol-plugin/context.js';
import { PluginCommands } from 'molstar/lib/mol-plugin/commands.js';
import { MolScriptBuilder as MS } from 'molstar/lib/mol-script/language/builder.js';
import type { BuiltInTrajectoryFormat } from 'molstar/lib/mol-plugin-state/formats/trajectory.js';
import { dynamicSuperpositionTest } from './superposition-utils.ts';
export let urlsAndChains: Array<{ url: string; type: BuiltInTrajectoryFormat; chainId: string }>;

type Preset =
| 'auto'
| 'empty'
| 'illustrative'
| 'atomic-detail'
| 'polymer-cartoon'
| 'polymer-and-ligand'
| 'protein-and-nucleic'
| 'coarse-surface'
| undefined;

const plugin = getContext<{ getPlugin: () => PluginContext }>('molstar').getPlugin();

function chainSelection(auth_asym_id: string) {
return MS.struct.generator.atomGroups({
'chain-test': MS.core.rel.eq([
MS.struct.atomProperty.macromolecular.auth_asym_id(),
auth_asym_id
])
});
}
let structures: Awaited<ReturnType<typeof plugin.builders.structure.createStructure>>[] = [];

const init = async () => {
urlsAndChains.forEach(async (element) => {
let data;
data = await plugin.builders.data.download(
{ url: element.url },
{ state: { isGhost: false } }
);
const trajectory = await plugin.builders.structure.parseTrajectory(data, element.type);
const model = await plugin.builders.structure.createModel(trajectory);
const struct = await plugin.builders.structure.createStructure(model);

//select the chain on the structure
const chain = await plugin.builders.structure.tryCreateComponentFromExpression(
struct,
chainSelection(`${element.chainId}`),
`Chain ${element.chainId}`
);

structures.push(struct);

if (chain)
await plugin.builders.structure.representation.addRepresentation(chain, {
type: 'cartoon'
});
});
};
onMount(async () => {
await init();
});
onDestroy(() => {
structures.forEach((structure) => {
plugin.commands.dispatch(PluginCommands.State.RemoveObject, {
state: plugin.state.data,
ref: structure.ref
});
});
});
</script>
182 changes: 182 additions & 0 deletions src/lib/elements/superposition-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <[email protected]>
*/

import { Mat4 } from 'molstar/lib/mol-math/linear-algebra.js';
import { QueryContext, StructureSelection } from 'molstar/lib/mol-model/structure.js';
import { superpose } from 'molstar/lib/mol-model/structure/structure/util/superposition.js';
import type { PluginStateObject as PSO } from 'molstar/lib/mol-plugin-state/objects.js';
import type { PluginContext } from 'molstar/lib/mol-plugin/context.js';
import { MolScriptBuilder as MS } from 'molstar/lib/mol-script/language/builder.js';
import type { Expression } from 'molstar/lib/mol-script/language/expression.js';
import { compile } from 'molstar/lib/mol-script/runtime/query/compiler.js';
import type { StateObjectRef } from 'molstar/lib/mol-state';
import type { BuiltInTrajectoryFormat } from 'molstar/lib/mol-plugin-state/formats/trajectory.js';
import { StateTransforms } from 'molstar/lib/mol-plugin-state/transforms.js';
import { Asset } from 'molstar/lib/mol-util/assets.js';

export type SuperpositionTestInput = {
pdbId: string;
auth_asym_id: string;
matrix: Mat4;
}[];

export const StaticSuperpositionTestData: SuperpositionTestInput = [
{
pdbId: '1aj5',
auth_asym_id: 'A',
matrix: Mat4.identity()
},
{
pdbId: '1df0',
auth_asym_id: 'B',
matrix: Mat4.ofRows([
[0.406, 0.879, 0.248, -200.633],
[0.693, -0.473, 0.544, 73.403],
[0.596, -0.049, -0.802, -14.209],
[0, 0, 0, 1]
])
},
{
pdbId: '1dvi',
auth_asym_id: 'A',
matrix: Mat4.ofRows([
[-0.053, -0.077, 0.996, -45.633],
[-0.312, 0.949, 0.057, -12.255],
[-0.949, -0.307, -0.074, 53.562],
[0, 0, 0, 1]
])
}
];

export function buildStaticSuperposition(plugin: PluginContext, src: SuperpositionTestInput) {
return plugin.dataTransaction(async () => {
for (const s of src) {
const { structure } = await loadStructure(
plugin,
`https://www.ebi.ac.uk/pdbe/static/entry/${s.pdbId}_updated.cif`,
'mmcif'
);
await transform(plugin, structure, s.matrix);
const chain = await plugin.builders.structure.tryCreateComponentFromExpression(
structure,
chainSelection(s.auth_asym_id),
`Chain ${s.auth_asym_id}`
);
if (chain)
await plugin.builders.structure.representation.addRepresentation(chain, {
type: 'cartoon'
});
}
});
}

export function dynamicSuperpositionTest(plugin: PluginContext, src: string[], comp_id: string) {
return plugin.dataTransaction(async () => {
let structs = [];
for (const s of src) {
await loadStructure(
plugin,
`https://www.ebi.ac.uk/pdbe/static/entry/${s}_updated.cif`,
'mmcif'
);
}

const pivot = MS.struct.filter.first([
MS.struct.generator.atomGroups({
'residue-test': MS.core.rel.eq([
MS.struct.atomProperty.macromolecular.label_comp_id(),
comp_id
]),
'group-by': MS.struct.atomProperty.macromolecular.residueKey()
})
]);

const rest = MS.struct.modifier.exceptBy({
0: MS.struct.modifier.includeSurroundings({
0: pivot,
radius: 5
}),
by: pivot
});

const query = compile<StructureSelection>(pivot);
const xs = plugin.managers.structure.hierarchy.current.structures;
const selections = xs.map((s) =>
StructureSelection.toLociWithCurrentUnits(query(new QueryContext(s.cell.obj!.data)))
);

const transforms = superpose(selections);

await siteVisual(plugin, xs[0].cell, pivot, rest);
for (let i = 1; i < selections.length; i++) {
await transform(plugin, xs[i].cell, transforms[i - 1].bTransform);
await siteVisual(plugin, xs[i].cell, pivot, rest);
}
});
}

async function siteVisual(
plugin: PluginContext,
s: StateObjectRef<PSO.Molecule.Structure>,
pivot: Expression,
rest: Expression
) {
const center = await plugin.builders.structure.tryCreateComponentFromExpression(
s,
pivot,
'pivot'
);
if (center)
await plugin.builders.structure.representation.addRepresentation(center, {
type: 'ball-and-stick',
color: 'residue-name'
});

const surr = await plugin.builders.structure.tryCreateComponentFromExpression(s, rest, 'rest');
if (surr)
await plugin.builders.structure.representation.addRepresentation(surr, {
type: 'ball-and-stick',
color: 'uniform',
size: 'uniform',
sizeParams: { value: 0.33 }
});
}

async function loadStructure(
plugin: PluginContext,
url: string,
format: BuiltInTrajectoryFormat,
assemblyId?: string
) {
const data = await plugin.builders.data.download({ url: Asset.Url(url) });
const trajectory = await plugin.builders.structure.parseTrajectory(data, format);
const model = await plugin.builders.structure.createModel(trajectory);
const structure = await plugin.builders.structure.createStructure(
model,
assemblyId ? { name: 'assembly', params: { id: assemblyId } } : void 0
);

return { data, trajectory, model, structure };
}

function chainSelection(auth_asym_id: string) {
return MS.struct.generator.atomGroups({
'chain-test': MS.core.rel.eq([
MS.struct.atomProperty.macromolecular.auth_asym_id(),
auth_asym_id
])
});
}

function transform(plugin: PluginContext, s: StateObjectRef<PSO.Molecule.Structure>, matrix: Mat4) {
const b = plugin.state.data
.build()
.to(s)
.insert(StateTransforms.Model.TransformStructureConformation, {
transform: { name: 'matrix', params: { data: matrix, transpose: false } }
});
return plugin.runTask(plugin.state.data.updateTree(b));
}
17 changes: 16 additions & 1 deletion src/routes/components/simple-elements/+page.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
return m.default;
});

const loadComponentDemoSuperposition = async () =>
import('./DemoSuperposition.svelte').then((m) => {
return m.default;
});

const pdbId1 = '6a3v'
const url1 = {url: 'https://alphafold.ebi.ac.uk/files/AF-Q07011-F1-model_v4.cif', type: 'mmcif'}
const pdbList2 = ['7D4B', '5WJF', '5WIW', '6A3W']
Expand All @@ -32,7 +37,7 @@
const urlChainList = [
{url:'https://files.rcsb.org/view/6A3V.cif', type:'mmcif', chainId:'B'},
{url:'https://files.rcsb.org/view/6A3W.cif', type:'mmcif', chainId:'C'},
{url:'https://files.rcsb.org/view/6BWV.cif', type:'mmcif', chainId:'C'},
{url:'https://files.rcsb.org/view/6CU0.cif', type:'mmcif', chainId:'A'},
{url:'https://files.rcsb.org/view/6CPR.cif', type:'mmcif', chainId:'F'},
];

Expand Down Expand Up @@ -92,6 +97,8 @@
{/await}
{/if}

## URLChain

### URLChain one instance

{#if browser}
Expand All @@ -108,4 +115,12 @@
{#await loadComponentDemoWithURLChains() then MolstarComp}
<svelte:component this={MolstarComp} structuresURLs={urlChainList} class="border h-18 bg-slate-300"/>
{/await}
{/if}

## Superposition

{#if browser}
{#await loadComponentDemoSuperposition() then MolstarComp}
<svelte:component this={MolstarComp} structuresURLs={urlChainList} class="border h-18 bg-slate-300"/>
{/await}
{/if}
31 changes: 31 additions & 0 deletions src/routes/components/simple-elements/DemoSuperposition.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script lang="ts">
// replace "$lib/" with "molstar-svelte/" in your project:
import Superposition from '$lib/elements/Superposition.svelte';
import MolstarWrapper from '$lib/wrappers/SimpleWrapper.svelte';
import StructuresURLsList from './StructuresURLsList.svelte';
export let structuresURLs = [
{ url: 'https://files.rcsb.org/view/7U4A.pdb', type: 'pdb', chainId: 'A' },
{ url: 'https://files.rcsb.org/view/7YLB.pdb', type: 'pdb', chainId: 'A' },
{ url: 'https://files.rcsb.org/view/7YUB.cif', type: 'mmcif', chainId: 'A' },
{
url: 'https://alphafold.ebi.ac.uk/files/AF-P00533-F1-model_v4.cif',
type: 'mmcif',
chainId: 'A'
}
];
let selectedStructuresURLs = [...structuresURLs];
</script>

<p class="text-xs">
Selected: <span class="text-violet-500"
>{selectedStructuresURLs.map((e) => e.url).join(', ')}</span
>
</p>
<MolstarWrapper class="h-96">
<svelte:fragment slot="elements">
<Superposition urlsAndChains={structuresURLs} />
</svelte:fragment>
</MolstarWrapper>
{#if structuresURLs.length > 1}
<StructuresURLsList {structuresURLs} bind:selectedStructuresURLs />
{/if}
Loading