-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add pages to show multiple applications (results and details) (#29)
* update readme * add new button * add stand-alone results page * add stand-alone details page * update export scripts * update aiida-core version Co-authored-by: Leopold Talirz <[email protected]>
- Loading branch information
1 parent
9bf7a34
commit e284170
Showing
23 changed files
with
1,754 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
from bokeh.plotting import figure | ||
import bokeh.models as bmd | ||
|
||
|
||
def get_startindex(steps): | ||
'''Take a list of steps and decide starting indices (and final number of steps).''' | ||
start_indices = [] | ||
for i, step in enumerate(steps): | ||
if i == 0: | ||
start_indices.append(0) | ||
else: | ||
if step <= steps[i - 1]: | ||
start_indices.append(i) | ||
start_indices.append(len(steps)) | ||
return start_indices | ||
|
||
|
||
def plot_energy_steps(dftopt_out): #pylint: disable=too-many-locals | ||
"""Plot the total energy graph.""" | ||
|
||
units = 'eV' | ||
ha2u = {'eV': 27.211399} | ||
|
||
out_dict = dftopt_out.get_dict() | ||
|
||
tooltips = [("Step (total)", "@index"), ("Step (stage)", "@step"), ("Energy", "@energy eV/atom"), | ||
("Energy (dispersion)", "@dispersion_energy_au Ha"), ("SCF converged", "@scf_converged"), | ||
("Cell A", "@cell_a_angs Angs"), ("Cell Vol", "@cell_vol_angs3 Angs^3"), | ||
("MAX Step", "@max_step_au Bohr"), ("Pressure", "@pressure_bar bar")] | ||
hover = bmd.HoverTool(tooltips=tooltips) | ||
TOOLS = ["pan", "wheel_zoom", "box_zoom", "reset", "save", hover] | ||
|
||
natoms = out_dict['natoms'] | ||
values = [x / natoms * ha2u[units] for x in out_dict['step_info']['energy_au']] | ||
values = [x - min(values) for x in values] | ||
|
||
data = bmd.ColumnDataSource(data=dict( | ||
index=range(len(values)), | ||
step=out_dict['step_info']['step'], | ||
energy=values, | ||
dispersion_energy_au=out_dict['step_info']['dispersion_energy_au'], | ||
scf_converged=out_dict['step_info']['scf_converged'], | ||
cell_a_angs=out_dict['step_info']['cell_a_angs'], | ||
cell_vol_angs3=out_dict['step_info']['cell_vol_angs3'], | ||
max_step_au=out_dict['step_info']['max_step_au'], | ||
pressure_bar=out_dict['step_info']['pressure_bar'], | ||
)) | ||
|
||
p = figure(tools=TOOLS, title='Energy profile of the DFT minimization', height=350, width=550) | ||
|
||
p.xgrid.grid_line_color = None | ||
p.xaxis.axis_label = 'Steps' | ||
p.yaxis.axis_label = 'Energy ({}/atom)'.format(units) | ||
|
||
# Colored background | ||
colors = ['red', 'orange', 'green', 'yellow', 'cyan', 'pink', 'palegreen'] | ||
start = 0 | ||
for i, steps in enumerate(out_dict['stage_info']['nsteps']): | ||
end = start + steps | ||
p.add_layout(bmd.BoxAnnotation(left=start, right=end, fill_alpha=0.2, fill_color=colors[i])) | ||
start = end | ||
|
||
# Trace line and markers | ||
p.line('index', 'energy', source=data, line_color='blue') | ||
p.circle('index', 'energy', source=data, line_color='blue', size=3) | ||
|
||
return p |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import numpy as np | ||
import pandas as pd | ||
from bokeh.plotting import figure | ||
import bokeh.models as bmd | ||
from pipeline_config import gasses, get_isotherm_nodes | ||
from details.utils import get_provenance_url, get_provenance_link | ||
|
||
|
||
def plot_isotherms(mat_id): #pylint: disable=too-many-locals | ||
"""Plot figure with all isotherms.""" | ||
|
||
nodes_dict = get_isotherm_nodes(mat_id) | ||
|
||
tooltips = [ | ||
("Molecule", "@legend_label"), | ||
("Uptake (mol/kg)", "@q_avg"), | ||
] | ||
hover = bmd.HoverTool(tooltips=tooltips) | ||
tap = bmd.TapTool() | ||
tap.callback = bmd.OpenURL(url=get_provenance_url(uuid="@uuid")) | ||
TOOLS = ["pan", "wheel_zoom", "box_zoom", "reset", "save", hover, tap] | ||
|
||
p1 = figure(tools=TOOLS, height=350, width=450) | ||
p1.xaxis.axis_label = 'Pressure (bar)' | ||
p1.yaxis.axis_label = 'Uptake (mol/kg)' | ||
|
||
for gas, gas_dict in gasses.items(): | ||
if gas not in nodes_dict: | ||
continue | ||
|
||
for node in nodes_dict[gas]: | ||
try: # avoid to fail if there are problems with one dict | ||
isot_out = node.get_dict() | ||
|
||
if isot_out['is_porous']: | ||
p = isot_out['isotherm']["pressure"] #(bar) | ||
q_avg = isot_out['isotherm']["loading_absolute_average"] #(mol/kg) | ||
q_dev = isot_out['isotherm']["loading_absolute_dev"] #(mol/kg) | ||
q_upper = np.array(q_avg) + np.array(q_dev) | ||
q_lower = np.array(q_avg) - np.array(q_dev) | ||
h_avg = isot_out['isotherm']["enthalpy_of_adsorption_average"] #(kJ/mol) | ||
h_dev = isot_out['isotherm']["enthalpy_of_adsorption_dev"] #(kJ/mol) | ||
# TRICK: use the enthalpy from widom (energy-RT) which is more accurate that the one at 0.001 bar | ||
# (and which also is NaN for weakly interacting systems) | ||
h_avg[0] = isot_out['adsorption_energy_widom_average'] - isot_out['temperature'] / 120.027 | ||
h_dev[0] = isot_out['adsorption_energy_widom_dev'] | ||
h_upper = np.array(h_avg) + np.array(h_dev) | ||
h_lower = np.array(h_avg) - np.array(h_dev) | ||
else: | ||
p = [0, 100] | ||
q_avg = q_upper = q_lower = h_avg = h_upper = h_lower = [0, 0] | ||
|
||
legend_label = "{} ({}K)".format(gas_dict['legend'], int(isot_out['temperature'])) | ||
|
||
data = dict(p=p, | ||
q_avg=q_avg, | ||
q_upper=q_upper, | ||
q_lower=q_lower, | ||
h_avg=h_avg, | ||
h_upper=h_upper, | ||
h_lower=h_lower, | ||
uuid=[str(node.uuid) for _ in q_avg], | ||
legend_label=[legend_label] * len(p)) | ||
source = bmd.ColumnDataSource(data=data) | ||
|
||
p1.line(x='p', | ||
y='q_avg', | ||
source=source, | ||
line_color=gas_dict['color'], | ||
line_width=2, | ||
legend_label=legend_label) | ||
p1.circle(x='p', y='q_avg', source=source, color=gas_dict['color'], size=5, legend_label=legend_label) | ||
p1.add_layout(bmd.Whisker(source=source, base="p", upper="q_upper", lower="q_lower")) | ||
except (KeyError, TypeError): | ||
continue | ||
|
||
p1.legend.location = "bottom_right" | ||
|
||
fig = p1 | ||
|
||
return fig | ||
|
||
|
||
def get_widom_df(mat_nodes_dict, select): | ||
"""Geting a df table for all the available Henry coefficients or heat of adsorption at zero pressure. | ||
Options: select = "kh" or "hoa" | ||
""" | ||
|
||
property_dict = {"kh": 'henry_coefficient_average', "hoa": 'adsorption_energy_widom_average'} | ||
|
||
temp_list = [ | ||
77, | ||
198, | ||
298, | ||
300, | ||
] | ||
|
||
pd.set_option('display.max_colwidth', None) | ||
df = pd.DataFrame(index=[x['legend'] for x in gasses.values()], columns=["{}K".format(x) for x in temp_list]) | ||
df = df.fillna("-") | ||
|
||
for tag, node in mat_nodes_dict.items(): | ||
try: # will fail if tag has no "_" separation, like "dftopt" | ||
wc, mol = tag.split("_") | ||
except ValueError: | ||
continue | ||
|
||
if mol not in gasses: | ||
continue | ||
|
||
legend = gasses[mol]['legend'] | ||
|
||
# the try...except skips problematic or nonporous results | ||
# NOTE: nonporous results can not be shown because the temperature of the calculation is unknown! | ||
|
||
try: # avoid to fail if there are problems with one dict | ||
if wc in ['kh', 'isot']: | ||
if select == "kh": | ||
val = '{:.2e}'.format(node[property_dict[select]]) | ||
elif select == "hoa": | ||
val = '{:.2f}'.format(node[property_dict[select]]) | ||
temperature = "{}K".format(int(node['temperature'])) | ||
df.loc[legend, temperature] = val + get_provenance_link(uuid=node.uuid) | ||
elif wc in ['isotmt']: | ||
for i, temp in enumerate(node['temperature']): | ||
if select == "kh": | ||
val = '{:.2e}'.format(node[property_dict[select]][i]) | ||
elif select == "hoa": | ||
val = '{:.2f}'.format(node[property_dict[select]][i]) | ||
temperature = "{}K".format(int(temp)) | ||
df.loc[legend, temperature] = val + get_provenance_link(uuid=node.uuid) | ||
except (KeyError, TypeError): | ||
continue | ||
|
||
return df |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
#!/usr/bin/env python | ||
|
||
import panel as pn | ||
import pandas as pd | ||
|
||
from aiida import load_profile | ||
from details.isotherms import plot_isotherms, get_widom_df | ||
from details.dft_info import plot_energy_steps | ||
from details.structure import structure_jsmol | ||
from details.utils import get_mat_id, get_details_title, get_geom_table, get_appl_table, get_title | ||
from pipeline_config import get_mat_nodes_dict | ||
|
||
load_profile() | ||
|
||
pn.extension(css_files=['details/static/style.css']) | ||
|
||
|
||
class DetailView(): | ||
|
||
def __init__(self): | ||
self.mat_id = get_mat_id() | ||
self.mat_nodes_dict = get_mat_nodes_dict(self.mat_id) | ||
print(">> Display details of MAT_ID:", self.mat_id, self.mat_nodes_dict['orig_cif']) | ||
|
||
def title_col(self): | ||
col = pn.Column(width=700) | ||
col.append(pn.pane.Markdown(get_details_title(self.mat_nodes_dict['orig_cif']))) | ||
return col | ||
|
||
def structure_col(self): | ||
nodes = self.mat_nodes_dict | ||
col = pn.Column(sizing_mode='stretch_width') | ||
col.append(get_title('Cell optimized structure', uuid=nodes['opt_cif_ddec'].uuid)) | ||
col.append(pn.pane.Bokeh(structure_jsmol(nodes['opt_cif_ddec']))) | ||
col.append(get_title('Energy profile during cell optimization', uuid=nodes['dftopt'].uuid)) | ||
col.append(pn.pane.Bokeh(plot_energy_steps(dftopt_out=nodes['dftopt']))) | ||
col.append(get_title('Geometric properties', uuid=nodes["opt_zeopp"].uuid)) | ||
col.append(pn.pane.Markdown(get_geom_table(nodes["opt_zeopp"]))) | ||
return col | ||
|
||
def properties_col(self): | ||
col = pn.Column(sizing_mode='stretch_width') | ||
col.append(pn.pane.Markdown('## All computed isotherms')) | ||
col.append(pn.pane.Bokeh(plot_isotherms(self.mat_id), sizing_mode='stretch_width')) | ||
col.append(pn.pane.Markdown("## All computed Henry's coefficients (mol/kg/Pa)")) | ||
with pd.option_context('display.max_colwidth', None): | ||
col.append(get_widom_df(self.mat_nodes_dict, select="kh").to_html(escape=False)) | ||
col.append(pn.pane.Markdown("## All computed Heat of adsorption @ infinite dilution (kJ/mol)")) | ||
with pd.option_context('display.max_colwidth', None): | ||
col.append(get_widom_df(self.mat_nodes_dict, select="hoa").to_html(escape=False)) | ||
return col | ||
|
||
def applications_col(self): | ||
col = pn.Column(sizing_mode='stretch_width') | ||
col.append(pn.pane.Markdown('## Numerical values for all the applications')) | ||
col.append(pn.pane.HTML(get_appl_table(self.mat_nodes_dict))) | ||
return col | ||
|
||
|
||
dv = DetailView() | ||
|
||
tabs = pn.Tabs(tabs_location='left', sizing_mode='stretch_width') | ||
tabs.extend([ | ||
("Applications", dv.applications_col()), | ||
("Properties", dv.properties_col()), | ||
("Structure", dv.structure_col()), | ||
]) | ||
|
||
page = dv.title_col() | ||
page.append(tabs) | ||
page.servable() |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
img.provenance-logo { | ||
width: 1em; | ||
vertical-align: middle; | ||
height: auto; | ||
margin-left: 0.3em; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
from jsmol_bokeh_extension import JSMol | ||
import bokeh.models as bmd | ||
|
||
|
||
def structure_jsmol(cif_node): | ||
|
||
script_source = bmd.ColumnDataSource() | ||
cif_str = cif_node.get_content() | ||
|
||
info = dict( | ||
height="100%", | ||
width="100%", | ||
use="HTML5", | ||
serverURL="https://chemapps.stolaf.edu/jmol/jsmol/php/jsmol.php", | ||
j2sPath="https://chemapps.stolaf.edu/jmol/jsmol/j2s", | ||
#serverURL="https://www.materialscloud.org/discover/scripts/external/jsmol/php/jsmol.php", | ||
#j2sPath="https://www.materialscloud.org/discover/scripts/external/jsmol/j2s", | ||
#serverURL="details/static/jsmol/php/jsmol.php", | ||
#j2sPath="details/static/jsmol/j2s", | ||
script=""" | ||
set antialiasDisplay ON; | ||
load data "cifstring" | ||
{} | ||
end "cifstring" | ||
""".format(cif_str)) | ||
|
||
applet = JSMol( | ||
width=600, | ||
height=600, | ||
script_source=script_source, | ||
info=info, | ||
#js_url="details/static/jsmol/JSmol.min.js", | ||
) | ||
|
||
return applet |
Oops, something went wrong.