Skip to content

Commit 1f43fe8

Browse files
OpenBind processing update (#339)
* Improve logging * Synchweb attachments & html viewers * Ligand scoring
1 parent b61ba59 commit 1f43fe8

File tree

9 files changed

+586
-453
lines changed

9 files changed

+586
-453
lines changed

setup.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@
7979
"mrbump = dlstbx.wrapper.mrbump:MrBUMPWrapper",
8080
"pandda_xchem = dlstbx.wrapper.pandda_xchem:PanDDAWrapper",
8181
"pandda_post = dlstbx.wrapper.pandda_post:PanDDApostWrapper",
82-
"pandda_rhofit = dlstbx.wrapper.pandda_rhofit:PanDDARhofitWrapper",
8382
"pipedream_xchem = dlstbx.wrapper.pipedream_xchem:PipedreamWrapper",
8483
"phaser_ellg = dlstbx.wrapper.phaser_ellg:PhasereLLGWrapper",
8584
"rlv = dlstbx.wrapper.rlv:RLVWrapper",

src/dlstbx/util/mvs/helpers.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from __future__ import annotations
2+
3+
from pathlib import Path
4+
5+
import gemmi
6+
7+
8+
def find_residue_by_name(structure, name):
9+
for model in structure:
10+
for chain in model:
11+
for res in chain:
12+
if res.name == name:
13+
return chain, res
14+
raise ValueError(f"Residue {name} not found")
15+
16+
17+
def residue_centroid(residue):
18+
n = 0
19+
x = y = z = 0.0
20+
for at in residue:
21+
p = at.pos
22+
x += p.x
23+
y += p.y
24+
z += p.z
25+
n += 1
26+
if n == 0:
27+
raise ValueError("Residue has no atoms")
28+
return gemmi.Position(x / n, y / n, z / n)
29+
30+
31+
def save_cropped_map(pdb_file, map_file, resname, radius):
32+
st = gemmi.read_structure(pdb_file)
33+
cell = st.cell
34+
m = gemmi.read_ccp4_map(map_file, setup=True)
35+
grid = m.grid
36+
37+
chain, res = find_residue_by_name(st, resname)
38+
center = residue_centroid(res) # ligand center
39+
40+
mask = grid.clone()
41+
mask.fill(0.0)
42+
43+
mask.set_points_around(center, radius, 1.0, use_pbc=True) # spherical mask in Å
44+
45+
dl = gemmi.Position(radius, radius, radius) # box d/2
46+
box = gemmi.FractionalBox()
47+
box.extend(cell.fractionalize(center - dl))
48+
box.extend(cell.fractionalize(center + dl))
49+
50+
grid.array[:] *= mask.array
51+
m.set_extent(box)
52+
path = Path(map_file)
53+
map_out = str(path.parents[0] / f"{path.stem}_cropped.ccp4")
54+
m.write_ccp4_map(map_out)
55+
return map_out
Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
from __future__ import annotations
22

3+
import gemmi
34
import molviewspec as mvs
45

6+
from dlstbx.util.mvs.helpers import find_residue_by_name
7+
8+
9+
def gen_html_ligandfit(pdb_file, map_file, resname, outdir, acr, smiles, cc):
10+
# make an mvs story from snapshots
11+
st = gemmi.read_structure(pdb_file)
12+
chain, res = find_residue_by_name(st, resname)
13+
residue = mvs.ComponentExpression(label_seq_id=res.seqid.num)
514

6-
def gen_html_ligandfit(pdb_file, map_file, outdir, acr, smiles, cc):
7-
# make a story from snapshots
815
builder = mvs.create_builder()
916
structure = builder.download(url=pdb_file).parse(format="pdb").model_structure()
1017
structure.component(selector="polymer").representation(
@@ -30,21 +37,28 @@ def gen_html_ligandfit(pdb_file, map_file, outdir, acr, smiles, cc):
3037

3138
snapshot1 = builder.get_snapshot(
3239
title="Main View",
33-
description=f"## Ligand_Fit Results: \n ### {acr} with ligand & electron density map \n - SMILES: {smiles} \n - 2FO-FC at 1.5σ, blue \n - Fitting CC = {cc}",
34-
transition_duration_ms=2000,
35-
linger_duration_ms=5000,
40+
description=f"## Ligand_Fit Results: \n ### {acr} with ligand & electron density map \n - SMILES: {smiles} \n - 2FO-FC map at 1.5σ, blue \n - Fitting CC = {cc}",
41+
transition_duration_ms=700,
42+
linger_duration_ms=4000,
3643
)
3744

3845
# SNAPSHOT2
3946
builder = mvs.create_builder()
4047
structure = builder.download(url=pdb_file).parse(format="pdb").model_structure()
4148
structure.component(selector="polymer").representation(
4249
type="surface", size_factor=0.7
43-
).opacity(opacity=0.5).color(color="#D8BFD8")
44-
structure.component(selector="polymer").representation().opacity(opacity=0.6).color(
45-
color="grey"
50+
).opacity(opacity=0.2).color(color="#AABDF1")
51+
structure.component(selector="polymer").representation().opacity(
52+
opacity=0.25
53+
).color(custom={"molstar_color_theme_name": "chain_id"})
54+
structure.component(selector="ligand").representation(type="ball_and_stick").color(
55+
custom={"molstar_color_theme_name": "element-symbol"}
4656
)
47-
structure.component(selector="ligand").focus().representation(
57+
structure.component(selector="ligand").representation(type="surface").opacity(
58+
opacity=0.1
59+
).color(custom={"molstar_color_theme_name": "element-symbol"})
60+
61+
structure.component(selector=residue).focus().representation(
4862
type="ball_and_stick"
4963
).color(custom={"molstar_color_theme_name": "element-symbol"})
5064

@@ -56,25 +70,19 @@ def gen_html_ligandfit(pdb_file, map_file, outdir, acr, smiles, cc):
5670
show_faces=False,
5771
).color(color="blue").opacity(opacity=0.25)
5872

59-
# add a label
60-
# info = get_chain_and_residue_numbers(pdb_file, "LIG")
61-
# resid = info[0][1]
62-
residue = mvs.ComponentExpression(label_seq_id=202)
63-
(
64-
structure.component(
65-
selector=residue,
66-
custom={
67-
"molstar_show_non_covalent_interactions": True,
68-
"molstar_non_covalent_interactions_radius_ang": 5.0,
69-
},
70-
).label(text=f"CC = {cc}")
73+
structure.component(
74+
selector=residue,
75+
custom={
76+
"molstar_show_non_covalent_interactions": True,
77+
"molstar_non_covalent_interactions_radius_ang": 5,
78+
},
7179
)
7280

7381
snapshot2 = builder.get_snapshot(
7482
title="Focus View",
7583
description=f"## Ligand_Fit Results: \n ### {acr} with ligand & electron density map \n - SMILES: {smiles} \n - 2FO-FC at 1.5σ, blue \n - Fitting CC = {cc}",
76-
transition_duration_ms=2000,
77-
linger_duration_ms=5000,
84+
transition_duration_ms=700,
85+
linger_duration_ms=4000,
7886
)
7987

8088
states = mvs.States(
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
from __future__ import annotations
2+
3+
from pathlib import Path
4+
5+
import gemmi
6+
import molviewspec as mvs
7+
8+
from dlstbx.util.mvs.helpers import find_residue_by_name
9+
10+
11+
def gen_html_pandda(pdb_file, event_map, z_map, resname, outdir, dtag, smiles, score):
12+
# make an mvs story from snapshots
13+
st = gemmi.read_structure(pdb_file)
14+
chain, res = find_residue_by_name(st, resname)
15+
residue = mvs.ComponentExpression(label_seq_id=res.seqid.num)
16+
17+
builder = mvs.create_builder()
18+
structure = builder.download(url=pdb_file).parse(format="pdb").model_structure()
19+
structure.component(selector="polymer").representation(
20+
type="surface", size_factor=0.7
21+
).opacity(opacity=0.2).color(color="#AABDF1")
22+
structure.component(selector="polymer").representation().opacity(
23+
opacity=0.25
24+
).color(custom={"molstar_color_theme_name": "chain_id"})
25+
structure.component(selector="ligand").representation(
26+
type="surface", size_factor=0.7
27+
).opacity(opacity=0.1).color(custom={"molstar_color_theme_name": "element-symbol"})
28+
29+
structure.component(selector=residue).focus().representation(
30+
type="ball_and_stick"
31+
).color(custom={"molstar_color_theme_name": "element-symbol"})
32+
33+
ccp4 = builder.download(url=event_map).parse(format="map")
34+
ccp4.volume().representation(
35+
type="isosurface",
36+
relative_isovalue=3,
37+
show_wireframe=True,
38+
show_faces=False,
39+
).color(color="blue").opacity(opacity=0.25)
40+
41+
structure.component(
42+
selector=residue,
43+
custom={
44+
"molstar_show_non_covalent_interactions": True,
45+
"molstar_non_covalent_interactions_radius_ang": 5,
46+
},
47+
)
48+
49+
snapshot1 = builder.get_snapshot(
50+
title="Event_map",
51+
description=f"## PanDDA2 Results: \n ### {dtag} \n - Ligand score: {score} \n - SMILES: {smiles} \n - Event map at 3σ, blue",
52+
transition_duration_ms=700,
53+
linger_duration_ms=4000,
54+
)
55+
56+
# SNAPSHOT2
57+
builder = mvs.create_builder()
58+
structure = builder.download(url=pdb_file).parse(format="pdb").model_structure()
59+
structure.component(selector="polymer").representation(
60+
type="surface", size_factor=0.7
61+
).opacity(opacity=0.2).color(color="#AABDF1")
62+
structure.component(selector="polymer").representation().opacity(
63+
opacity=0.25
64+
).color(custom={"molstar_color_theme_name": "chain_id"})
65+
structure.component(selector="ligand").representation(
66+
type="surface", size_factor=0.7
67+
).opacity(opacity=0.1).color(custom={"molstar_color_theme_name": "element-symbol"})
68+
69+
structure.component(selector=residue).focus().representation(
70+
type="ball_and_stick"
71+
).color(custom={"molstar_color_theme_name": "element-symbol"})
72+
73+
ccp4 = builder.download(url=z_map).parse(format="map")
74+
ccp4.volume().representation(
75+
type="isosurface",
76+
relative_isovalue=3,
77+
show_wireframe=True,
78+
show_faces=False,
79+
).color(color="green").opacity(opacity=0.25)
80+
81+
structure.component(
82+
selector=residue,
83+
custom={
84+
"molstar_show_non_covalent_interactions": True,
85+
"molstar_non_covalent_interactions_radius_ang": 5,
86+
},
87+
)
88+
89+
snapshot2 = builder.get_snapshot(
90+
title="Z_map",
91+
description=f"## PanDDA2 Results: \n ### {dtag} \n - Ligand score: {score} \n - SMILES: {smiles} \n - Z_map at 3σ, green",
92+
transition_duration_ms=700,
93+
linger_duration_ms=4000,
94+
)
95+
96+
states = mvs.States(
97+
snapshots=[snapshot1, snapshot2], # [snapshot1, snapshot2]
98+
metadata=mvs.GlobalMetadata(description="PanDDA2 Results"),
99+
)
100+
101+
with open(pdb_file) as f:
102+
pdb_data = f.read()
103+
104+
# with open(event_map, mode="rb") as f:
105+
# map_data1 = f.read()
106+
107+
with open(z_map, mode="rb") as f:
108+
map_data2 = f.read()
109+
110+
html = mvs.molstar_html(
111+
states,
112+
data={pdb_file: pdb_data, z_map: map_data2}, # event_map: map_data1,
113+
ui="stories",
114+
)
115+
116+
out_file = Path(f"{outdir}/pandda2_mvs.html")
117+
with open(out_file, "w") as f:
118+
f.write(html)
119+
120+
return out_file
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from __future__ import annotations
2+
3+
from pathlib import Path
4+
5+
import gemmi
6+
import molviewspec as mvs
7+
8+
from dlstbx.util.mvs.helpers import find_residue_by_name
9+
10+
11+
def gen_html_pipedream(pdb_file, map_file, resname, outdir, dtag, smiles):
12+
# make an mvs story from snapshots
13+
st = gemmi.read_structure(pdb_file)
14+
chain, res = find_residue_by_name(st, resname)
15+
residue = mvs.ComponentExpression(label_seq_id=res.seqid.num)
16+
17+
builder = mvs.create_builder()
18+
structure = builder.download(url=pdb_file).parse(format="pdb").model_structure()
19+
structure.component(selector="polymer").representation(
20+
type="surface", size_factor=0.7
21+
).opacity(opacity=0.2).color(color="#AABDF1")
22+
structure.component(selector="polymer").representation().opacity(
23+
opacity=0.25
24+
).color(custom={"molstar_color_theme_name": "chain_id"})
25+
26+
structure.component(selector=residue).focus().representation(
27+
type="ball_and_stick"
28+
).color(custom={"molstar_color_theme_name": "element-symbol"})
29+
30+
ccp4 = builder.download(url=map_file).parse(format="map")
31+
ccp4.volume().representation(
32+
type="isosurface",
33+
relative_isovalue=1.5,
34+
show_wireframe=True,
35+
show_faces=False,
36+
).color(color="blue").opacity(opacity=0.25)
37+
38+
structure.component(
39+
selector=residue,
40+
custom={
41+
"molstar_show_non_covalent_interactions": True,
42+
"molstar_non_covalent_interactions_radius_ang": 5,
43+
},
44+
)
45+
46+
snapshot1 = builder.get_snapshot(
47+
title="Main View",
48+
description=f"## Pipedream Results: \n ### {dtag} \n - SMILES: {smiles} \n - 2FO-FC map map at 1.5σ, blue",
49+
transition_duration_ms=700,
50+
linger_duration_ms=4000,
51+
)
52+
53+
states = mvs.States(
54+
snapshots=[snapshot1],
55+
metadata=mvs.GlobalMetadata(description="Pipedream Results"),
56+
)
57+
58+
with open(pdb_file) as f:
59+
pdb_data = f.read()
60+
61+
with open(map_file, mode="rb") as f:
62+
map_data = f.read()
63+
64+
html = mvs.molstar_html(
65+
states,
66+
data={pdb_file: pdb_data, map_file: map_data},
67+
ui="stories",
68+
)
69+
70+
out_file = Path(f"{outdir}/pipedream_mvs.html")
71+
with open(out_file, "w") as f:
72+
f.write(html)
73+
74+
return out_file

src/dlstbx/wrapper/ligand_fit.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from shutil import ignore_patterns
1010

1111
import dlstbx.util.symlink
12-
from dlstbx.util.mvs.ligandfit import gen_html_ligandfit
12+
from dlstbx.util.mvs.viewer_ligandfit import gen_html_ligandfit
1313
from dlstbx.wrapper import Wrapper
1414

1515

@@ -149,7 +149,13 @@ def run(self):
149149
CC = self.pull_CC_from_log(pipeline_directory)
150150
try:
151151
gen_html_ligandfit(
152-
out_pdb, out_map, pipeline_directory, cc=CC, smiles=smiles, acr=acr
152+
out_pdb,
153+
out_map,
154+
resname="LIG",
155+
outdir=pipeline_directory,
156+
cc=CC,
157+
smiles=smiles,
158+
acr=acr,
153159
)
154160
except Exception as e:
155161
self.log.debug(f"Exception generating mvs html: {e}")

0 commit comments

Comments
 (0)