Skip to content

Commit

Permalink
Added fit-to-data CX plots to PDF; At a glance TBD
Browse files Browse the repository at this point in the history
  • Loading branch information
aozalevsky committed Feb 22, 2024
1 parent abbf6b3 commit 539e407
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 10 deletions.
31 changes: 25 additions & 6 deletions ihm_validation/cx.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import iqplot
import json
from bokeh.embed import json_item
from bokeh.io import export_svgs

pd.options.mode.chained_assignment = None
NA = 'Not available'
Expand Down Expand Up @@ -66,6 +67,9 @@ def infinite_defaultdict(): return defaultdict(infinite_defaultdict)


class CxValidation(GetInputInformation):
ID = None
driver = None

def __init__(self, mmcif_file):
super().__init__(mmcif_file)
self.ID = str(self.get_id())
Expand Down Expand Up @@ -730,6 +734,8 @@ def scatter_plot(stats):
marker_kwargs=dict(alpha=0.5, size=7)
)

p.output_backend = "svg"

p.x_range = Range1d(xmin, xmax)
p.xaxis.axis_label = 'Satisfaction rate, %'

Expand Down Expand Up @@ -777,7 +783,7 @@ def scatter_plot(stats):
out_stats = pd.DataFrame(out_stats_)

p = scatter_plot(out_stats)
title = f'{self.ID}\\nSatisfaction rates in model group {gimg}'
title = f'Satisfaction rates in model group {gimg}'
p.title.text = title

p.title.text_font_size = "12pt"
Expand Down Expand Up @@ -823,11 +829,13 @@ def plot_distograms_per_model_group(self, imgDirname='.'):
data=data_, q='Crosslinks', density=False,
bins=bins,
style="step_filled",
frame_width=400, frame_height=100,
frame_width=500, frame_height=100,
# sizing_mode='scale_width',
)

title = f'{self.ID}\\n{lt}: {rt}, {d:.1f} Å'
p.output_backend = "svg"

title = f"Model group {gimg}; {lt}: {rt}, {d:.1f} Å"

p.title.text_font_size = "12pt"
p.xaxis.axis_label_text_font_size = "14pt"
Expand Down Expand Up @@ -861,9 +869,11 @@ def plot_distograms_per_model_group(self, imgDirname='.'):
return self.save_plots(tabs, title, imgDirname)

def save_plots(self, plot, title, imgDirname='.'):
stem = f'{self.ID}_{title}'

imgpath = Path(
imgDirname,
f'{self.ID}_{title}.html')
f'{stem}.html')
save(
plot, imgpath,
resources=CDN,
Expand All @@ -872,9 +882,18 @@ def save_plots(self, plot, title, imgDirname='.'):

imgpath_json = Path(
imgDirname,
f'{self.ID}_{title}.json')
f'{stem}.json')

with open(imgpath_json, 'w') as f:
json.dump(json_item(plot, title), f)

return (imgpath, imgpath_json)
imgpath_svg = Path(
imgDirname,
f'{stem}.svg')

svgs = export_svgs(plot, filename=imgpath_svg,
webdriver=self.driver)

svgs = [Path(x).name for x in svgs]

return (imgpath, imgpath_json, svgs)
7 changes: 5 additions & 2 deletions ihm_validation/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,15 +433,18 @@ def run_cx_validation_plots(self, Template_Dict: dict, imageDirName: str) -> Non
if bool(Template_Dict['cx']):
if Template_Dict['cx_stats'] is not None:

html_fn, json_fn = self.I_cx.plot_distograms_per_model_group(imageDirName)
self.I_cx.driver = self.driver
html_fn, json_fn, svgs_fn = self.I_cx.plot_distograms_per_model_group(imageDirName)
with open(json_fn, 'r') as f:
plot = json.dumps(json.load(f))
Template_Dict['cx_distograms_plot_json'] = plot
Template_Dict['cx_distograms_plots_svg'] = svgs_fn

html_fn, json_fn = self.I_cx.plot_satisfaction_per_ensemble(imageDirName)
html_fn, json_fn, svgs_fn = self.I_cx.plot_satisfaction_per_ensemble(imageDirName)
with open(json_fn, 'r') as f:
plot = json.dumps(json.load(f))
Template_Dict['cx_satisfaction_plot_json'] = plot
Template_Dict['cx_satisfaction_plots_svg'] = svgs_fn

def run_quality_glance(self, molprobity_dict: dict, exv_data: dict,
sas_data: dict, sas_fit: dict,
Expand Down
159 changes: 157 additions & 2 deletions templates/full_validation_pdf.html
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,162 @@ <h5 align=center>
<p style=margin-bottom:0.5cm;> </p>
{% endif %}

{% if cx %}
<h5 align=center id="cxms">
<u> Fit of model(s) to CX-MS data</u>
</h5>

<h5 align=center id="cxms_ertypes">
<u>Restraint types</u>
</h5>

{% if cx_ertypes is none %}
<p>Restraint types in this entry are not supported at the moment.</p>
{% else %}
<div class="row">
<div class='col-lg-12'>
<p>There are {{ cx_num_of_restraints }} crosslinking restraints
combined in {{ cx_num_of_restraint_groups }} restraint groups.
Restraint types are summarized in the table below.
Only atomic or per-residue restraints are supported at the moment.
</p>
<div class='table-responsive text-center' style="max-height: 75vh; overflow: auto;">
<table id='cx_ertypes_table' class='table table-sm table-striped table-bordered' style='border-color':#003366;>
<thead>
<tr>
<th align='center' class='text-center' style='vertical-align:middle'>
Type #
</th>
{% for k in cx_ertypes['columns'] %}
<th align='center' class='text-center' style='vertical-align:middle'>
{{ k }}
</th>
{% endfor %}
</tr>
</thead>

{% for i in range(cx_ertypes['data']|length) %}
<tr>
<td>{{ i }}</td>
{% for v in cx_ertypes['data'][i] %}
<td>{{ v }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
</div>
</div>
<div class=col-lg-12>
<div text-center container-fluid no-padding align=center>
<div>
<p><u>Distograms of individual restraints</u></p>
</div>
{% if cx_stats is none %}
<p>Distograms for this entry are unavailable the moment.</p>
{% else %}
<p style='text-align: left'><i>Restraints with identical thresholds are grouped into one plot. Only best distance per restraint per model group/ensemble is plotted. Intra- and intermolecular self-link restraints are also grouped into one plot.</i></p>
{% for i in range(cx_distograms_plots_svg|length) %}
<img id='cx_distograms_plot_{{ i }}' src='../images/{{ cx_distograms_plots_svg[i] }}'>
{% endfor %}
{% endif %}
</div>
</div>
</div>
{% endif %}
<!-- end of cx_ertypes -->

<h5 align=center id="cxms_satisfaction">
<u>Satisfaction of restraints</u>
</h5>

{% if cx_stats is none %}
<p>Satisfaction rates for this entry are unavailable at the moment.</p>
{% else %}
<div class=row>
<div class=col-lg-12>
<p>
<i>
Satisfaction of restraints is calculated on a restraint
group level. Satisfaction of a restraint group depends on
satisfaction of individual restraints in the group and the
conditionality (all/any). Restraint group is considered
satisfied, if condition was met in at least one model of
the model group/ensemble. Only deposited models are used
for validation right now.
</i>
</p>

<div class='table-responsive text-center'>
<table id='cx_satisfaction_table' class='table table-sm table-striped table-bordered' style='border-color':#003366;>
<thead>
<tr>
<th style='vertical-align:middle'>
State group
</th>
<th style='vertical-align:middle'>
State
</th>
<th style='vertical-align:middle'>
Model group
</th>
<th style='vertical-align:middle'>
# of Deposited models/Total
</th>
<th style='vertical-align:middle'>
Restraint group type
</th>
<th style='vertical-align:middle'>
Satisfied (%)
</th>
<th style='vertical-align:middle'>
Violated (%)
</th>
<th style='vertical-align:middle'>
Count
</th>
</tr>
</thead>
{% for sg, sgv in cx_stats.items() %}
{% set state_group_loop = loop %}
{% for st, stv in sgv.items() %}
{% for mg, mgv in stv.items() %}
{% for k, v in mgv["cx_stats"].items() %}
{% set rowspan = mgv["cx_stats"]|length %}
<tr>
{% if loop.index == 1 %}
<td rowspan={{ rowspan }}>{{ sg }}</td>
<td rowspan={{ rowspan }}>{{ st }}</td>
<td rowspan={{ rowspan }}>{{ mg }}</td>
<td rowspan={{ rowspan }}>{{ mgv["ens_stats"]["num_models_deposited"] ~ "/" ~ mgv["ens_stats"]["num_models"]}}</td>
{% endif %}
<td>{{ k | replace("/", "/<br>")}}</td>
<td>{{ v["Satisfied"] }}</td>
<td>{{ v["Violated"] }}</td>
<td style='horizontal-align:right'>{{ v["Count"] }}</td>
</tr>
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
</table>
</div>
</div>
<div class=col-lg-12>
<div text-center container-fluid no-padding align=center>
<p><u>Per-model satisfaction rates in ensembles</u></p>
<p style="text-align: left"><i>Every point represents one model in a model group/ensemble. Where possible, boxplots with quartile marks are also plotted.</i></p>
{% for i in range(cx_satisfaction_plots_svg|length) %}
<img id='cx_satisfaction_plot_{{ i }}' src='../images/{{ cx_satisfaction_plots_svg[i] }}'>
{% endfor %}
</div>
</div>
</div>
<!-- end of cx_stats -->
{% endif %}
<p style=margin-bottom:0.5cm;> </p>
<!-- end of cx -->
{% endif %}

{% for i in range(Unique_dataset|length) %}
<h5 align= center>
<u><a name=last>{{ Unique_dataset[i] }}</a></u>
Expand All @@ -754,8 +910,7 @@ <h5 align= center>
Validation for this section is under development.
</p>

<p style=margin-bottom:0.5cm;> </p>

<p style=margin-bottom:0.5cm;> </p>
{% endfor %}
</div>
</div>
Expand Down

0 comments on commit 539e407

Please sign in to comment.