def update_legends(ly): q_x = config.Quantity(inp_x.value) q_y = config.Quantity(inp_y.value) p = ly.children[0].children[1] #title = "{} vs {}".format(q_x.label, q_y.label) xhover = (q_x.label, "@x {}".format(q_x.unit_str)) yhover = (q_y.label, "@y {}".format(q_y.unit_str)) q_clr = config.Quantity(inp_clr.value) hover.tooltips = [ ("name", "@name"), xhover, yhover, (q_clr.label, "@color {}".format(q_clr.unit_str)), ] p.xaxis.axis_label = q_x.axis_label p.yaxis.axis_label = q_y.axis_label p.title.text = q_clr.axis_label url = "detail?name=@name&table=top_mofs" tap.callback = bmd.OpenURL(url=url)
def update_legends(ly): """Update figure legends.""" q_x = config.quantities[inp_x.value] q_y = config.quantities[inp_y.value] p = ly.children[0].children[1] #title = "{} vs {}".format(q_x["label"], q_y["label"]) xlabel = '{} [{}]'.format(q_x['label'], q_x['unit']) ylabel = '{} [{}]'.format(q_y['label'], q_y['unit']) xhover = (q_x['label'], '@x {}'.format(q_x['unit'])) yhover = (q_y['label'], '@y {}'.format(q_y['unit'])) q_clr = config.quantities[inp_clr.value] if 'unit' not in list(q_clr.keys()): clr_label = q_clr['label'] clr_val = '@color' else: clr_val = '@color {}'.format(q_clr['unit']) clr_label = '{} [{}]'.format(q_clr['label'], q_clr['unit']) hover.tooltips = [ ('name', '@name'), xhover, yhover, (q_clr['label'], clr_val), ] if inp_clr.value == 'bond_type': clr_label = 'Bond type' hover.tooltips = [ ('name', '@name'), xhover, yhover, ('Bond type', '@color'), ] else: q_clr = config.quantities[inp_clr.value] clr_label = '{} [{}]'.format(q_clr['label'], q_clr['unit']) hover.tooltips = [ ('name', '@name'), xhover, yhover, (q_clr['label'], '@color {}'.format(q_clr['unit'])), ] p.xaxis.axis_label = xlabel p.yaxis.axis_label = ylabel p.title.text = clr_label url = 'detail?name=@name' tap.callback = bmd.OpenURL(url=url)
def update_legends(ly): q_x = quantities[inp_x.value] q_y = quantities[inp_y.value] p = ly.children[0].children[1] #title = "{} vs {}".format(q_x["label"], q_y["label"]) xlabel = "{} [{}]".format(q_x["label"], q_x["unit"]) ylabel = "{} [{}]".format(q_y["label"], q_y["unit"]) xhover = (q_x["label"], "@x {}".format(q_x["unit"])) yhover = (q_y["label"], "@y {}".format(q_y["unit"])) q_clr = quantities[inp_clr.value] if 'unit' not in q_clr.keys(): clr_label = q_clr["label"] clr_val = "@color" else: clr_val = "@color {}".format(q_clr['unit']) clr_label = "{} [{}]".format(q_clr["label"], q_clr["unit"]) hover.tooltips = [ ("name", "@name"), xhover, yhover, (q_clr["label"], clr_val), ] if inp_clr.value == 'bond_type': clr_label = "Bond type" hover.tooltips = [ ("name", "@name"), xhover, yhover, ("Bond type", "@color"), ] else: q_clr = quantities[inp_clr.value] clr_label = "{} [{}]".format(q_clr["label"], q_clr["unit"]) hover.tooltips = [ ("name", "@name"), xhover, yhover, (q_clr["label"], "@color {}".format(q_clr["unit"])), ] p.xaxis.axis_label = xlabel p.yaxis.axis_label = ylabel p.title.text = clr_label url = "detail?name=@name" tap.callback = bmd.OpenURL(url=url)
def get_plot(inp_x, inp_y, inp_clr): """Returns a Bokeh plot of the input values, and a message with the number of COFs found.""" q_list = [quantities[label] for label in [inp_x, inp_y, inp_clr]] db_nodes_dict = get_db_nodes_dict( ) # TODO: try to move outside, to load it once! results = get_figure_values( db_nodes_dict, q_list) #returns [inp_x_value, inp_y_value, inp_clr_value, cif.label] # prepare data for plotting nresults = len(results) msg = "{} MOFs found.<br> <b>Click on any point for details!</b>".format( nresults) label, x, y, clrs = zip(*results) x = list(map(float, x)) y = list(map(float, y)) clrs = list(map(float, clrs)) mat_id = label data = {'x': x, 'y': y, 'color': clrs, 'mat_id': mat_id} # create bokeh plot source = bmd.ColumnDataSource(data=data) hover = bmd.HoverTool(tooltips=[]) tap = bmd.TapTool() p_new = bpl.figure( plot_height=600, plot_width=600, toolbar_location='above', tools=[ 'pan', 'wheel_zoom', 'box_zoom', 'save', 'reset', hover, tap, ], active_drag='box_zoom', output_backend='webgl', title='', # trick: title is used as the colorbar label title_location='right', x_axis_type=q_list[0]['scale'], y_axis_type=q_list[1]['scale'], ) p_new.title.align = 'center' p_new.title.text_font_size = '10pt' p_new.title.text_font_style = 'italic' update_legends(p_new, q_list, hover) tap.callback = bmd.OpenURL(url="detail_pyrenemofs?mat_id=@mat_id") cmap = bmd.LinearColorMapper(palette=Plasma256, low=min(clrs), high=max(clrs)) fill_color = {'field': 'color', 'transform': cmap} p_new.circle('x', 'y', size=10, source=source, fill_color=fill_color) cbar = bmd.ColorBar(color_mapper=cmap, location=(0, 0)) p_new.add_layout(cbar, 'right') return p_new, msg
def get_plot(appl): #pylint: disable=too-many-locals """Make the bokeh plot for a given application. 1. read important metrics from applications.yml 2. read metadata for thes metricy in quantities.yml 3. query the AiiDA database for values 4. rank materials according to their performance 5. generate the plot and return a message stating the number of found materials """ # Query for results q_list = tuple([quantities[label] for label in [appl['x'], appl['y']]]) results_wnone = get_data_aiida( q_list) #[[id_material, name_material, class_material, xval, yval]] # Clean the query from None values projections results = [] for l in results_wnone: if None not in l: results.append(l) # Prepare data for plotting nresults = len(results) if not results: results = [['none', 'none', 'none', 0, 0]] msg = "No materials found" else: msg = "{} materials shown".format(nresults) mat_id, name, class_mat, x, y = zip(*results) x = list(map(float, x)) y = list(map(float, y)) rank = rank_materials(x, y, appl['wx'], appl['wy']) # Setup plot hover = bmd.HoverTool() hover.tooltips = [ ("name", "@name"), ("MAT_ID", "@mat_id"), (q_list[0]["label"], "@x {}".format(q_list[0]["unit"])), (q_list[1]["label"], "@y {}".format(q_list[1]["unit"])), ('ranking', '@color'), ] tap = bmd.TapTool() p = bpl.figure( plot_height=600, plot_width=600, toolbar_location="right", # choose: above, below, right, left tools=[ 'pan', 'wheel_zoom', 'box_zoom', 'save', 'reset', hover, tap, ], active_drag='box_zoom', output_backend='webgl', x_axis_type=q_list[0]['scale'], y_axis_type=q_list[1]['scale'], x_axis_label="{} [{}]".format(q_list[0]["label"], q_list[0]["unit"]), y_axis_label="{} [{}]".format(q_list[1]["label"], q_list[1]["unit"]), ) tap.callback = bmd.OpenURL(url="details?mat_id=@mat_id") cmap = bmd.LinearColorMapper(palette=myRdYlGn) fill_color = {'field': 'color', 'transform': cmap} source = bmd.ColumnDataSource( data={ 'mat_id': mat_id, 'name': name, 'class_mat': class_mat, 'x': x, 'y': y, 'color': rank, }) p.circle(x='x', y='y', size=10, source=source, fill_color=fill_color, muted_alpha=0.2) #cbar = bmd.ColorBar(color_mapper=cmap, location=(0, 0)) #p.add_layout(cbar, 'right') #show colorbar return p, msg
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 compare(self, summary_catalogue, plot_name=None, summary_type='median', level=68., params_to_plot=None, overwrite=False, rows=None, interactive=False): # Match IDs in the two catalogs indx1, indx2 = match_ID(self.hdulist[1].data['ID'], summary_catalogue.hdulist[1].data['ID'], ignore_string=self.ignore_string) # Check whether the IDs of the two catalogues match #if 'ID' in self.hdulist[1].data: # for i, ID in enumerate(self.hdulist[1].data['ID']): # if not ID == summary_catalogue.hdulist['POSTERIOR PDF'].data['ID'][i]: # raise Exception("The object IDs of the `mock` and `summary` catalogues do not match!") # The summary statistics can be only 'mean' or 'median' if summary_type not in ('mean', 'median'): raise ValueError( "`summary_type` con only be set to `mean` or `median`") if plot_name is None: plot_name = "BEAGLE_mock_retrieved_params.pdf" # Check if the plot already exists if plot_exists(plot_name) and not overwrite: logging.warning('The plot `' + plot_name + '` already exists. \n Exiting the function.') return # By default you plot all parameters if params_to_plot is None: params_to_plot = list() for key, value in six.iteritems(self.adjust_params): params_to_plot.append(key) # Do you consider only some rows in the catalogue? #if rows is None: # rows = np.arange(len(self.hdulist[1].data.field(0))) _n = int(np.ceil(np.sqrt(len(params_to_plot)))) ###################################################################### # If interactive is `True`, you draw an interactive plot instead of # plotting into a pdf file ###################################################################### if interactive: # Size (in pixels) of each Bokeh figure size = 400 # Name of the output html file created by Bokeh name = prepare_plot_saving(os.path.splitext(plot_name)[0] + '.html', overwrite=overwrite) # Tell Bokeh to save the plot into an output html file bk_plt.output_file(name) # create a column data source for the plots to share, and fill the # dictionary with all the data that will then be used in the Bokeh # plot data = dict() data['ID'] = self.hdulist[1].data['ID'][indx1] for param in params_to_plot: # Store the "true" parameter key = param + '_true' data[key] = self.data[param][indx1] # and the retrieved one key = param + '_retrieved' _col = param + '_' + summary_type data[key] = summary_catalogue.hdulist['POSTERIOR PDF'].data[ _col][indx2] # Get the errors _col = param + '_' + '{:.2f}'.format(level) tmp = summary_catalogue.hdulist['POSTERIOR PDF'].data[_col][ indx2] # Store the errors in a way that is easily usable by the Bokeh # `multi_line` glyph err_xs = list() err_ys = list() key = param + '_true' for i, _x in enumerate(data[key]): err_xs.append((_x, _x)) err_ys.append((tmp[i, 0], tmp[i, 1])) key = param + '_err_xs' data[key] = err_xs key = param + '_err_ys' data[key] = err_ys #data[key] = 0.5*(tmp[:,1]-tmp[:,0]) strID = [ str(ID) + '_BEAGLE_input_for_Pierre.fits_snr_PS_CLEAR_PRISM_BEAGLE_marginal_SED_spec.pdf' for ID in data['ID'] ] data['strID'] = strID # Build the `ColumnDataSource` (a sort of dictionary) that will be # used by Bokeh to extract the data source = bk_mdl.ColumnDataSource(data=data) # Define the "tools" that will be included in the interactive Bokeh plot tools = 'wheel_zoom,pan,reset,resize,hover,tap' figs = list() for param in params_to_plot: # create a new plot and add a renderer fig = bk_plt.figure(tools=tools, width=size, height=size, title=None) # Plot the x and y data points as circles x = param + '_true' y = param + '_retrieved' error_low = param + '_err_xs' error_up = param + '_err_ys' fig.circle(x, y, source=source) # Plot the errorbars fig.multi_line(xs=error_low, ys=error_up, source=source, alpha=0.6) # Plot the 1-to-1 relation _max = np.amax(np.ravel([data[x], data[y]])) _min = np.amin(np.ravel([data[x], data[y]])) fig.line([_min, _max], [_min, _max], color='red') # Label the x- and y-axis label = self.adjust_params[param]['label_plain'] xlabel = label + " (true)" fig.xaxis.axis_label = xlabel ylabel = label fig.yaxis.axis_label = ylabel # Append the newly created figure to the `fgs` list of Bokeh figures figs.append(fig) # Arrange the different figures in a matrix-list grid_figs = list() for i in range(0, _n): _tmp = list() for j in range(_n): if i * _n + j >= len(figs): break else: _tmp.append(figs[i * _n + j]) grid_figs.append(_tmp) # Use the matrix-list to create a grid of Bokeh figures p = bk_plt.gridplot(grid_figs) # Here we customize the behaviour of some tools hover = p.select(dict(type=bk_mdl.HoverTool)) hover.tooltips = [ ("(x,y)", "($x, $y)"), ('ID', '@ID'), ] path = os.path.join(os.getcwd(), '@strID') url = "file://" + path print("url: ", url) taptool = p.select(type=bk_mdl.TapTool) taptool.callback = bk_mdl.OpenURL(url=url) bk_plt.show(p) return ###################################################################### # Below is a standard plot written to a pdf file ###################################################################### fig, axs = plt.subplots(_n, _n) fig.subplots_adjust(left=0.08, bottom=0.08) fontsize = 8 axs = np.ravel(axs) for i, param in enumerate(params_to_plot): # Extract the row corresponding to `param` from the mock catalogue # (this array contains the "true") values of the parameters if "extName" in self.adjust_params[param]: extName = self.adjust_params[param]["extName"] else: extName = "POSTERIOR PDF" if "colName" in self.adjust_params[param]: colName = self.adjust_params[param]["colName"] else: colName = param true_param = self.hdulist[extName].data[colName][indx1] _col = colName + '_' + summary_type retrieved_param = summary_catalogue.hdulist[extName].data[_col][ indx2] _col = colName + '_' + '{:.2f}'.format(level) error = summary_catalogue.hdulist[extName].data[_col][indx2] ax = axs[i] # Set the x- and y-axis labels label = self.adjust_params[param]['label'] xlabel = label + " (true)" ylabel = label ax.set_xlabel(xlabel) ax.set_ylabel(ylabel) # Check if we need to compute the log10 of the param or not x = true_param y = retrieved_param yerr_u = error[:, 1] - y yerr_d = y - error[:, 0] is_log = False if 'log' in self.adjust_params[param]: if self.adjust_params[param]['log']: is_log = True #x = np.log10(x) #y = np.log10(y) #yerr_d = np.log10(yerr_d) #yerr_u = np.log10(yerr_u) ax.errorbar(x, y, yerr=[yerr_d, yerr_u], ls="", marker='o', ms=3, mew=0, elinewidth=0.6, capsize=3, alpha=0.5) for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] + ax.get_xticklabels() + ax.get_yticklabels()): item.set_fontsize(fontsize) # Make the axes in log-scale if is_log: ax.set_xscale('log') ax.set_yscale('log') else: set_plot_ticks(ax, n_x=4, n_y=4) # Make axes of same aspect ratio plt.axis('equal') # Set same axis range for x- and y-axis, and use the (optional) # user-provided value xx = ax.get_xlim() yy = ax.get_ylim() ll = [min([xx[0], yy[0]]), max(xx[1], yy[1])] if 'axis_range' in self.adjust_params[param]: rr = self.adjust_params[param]['axis_range'] ll = [max(ll[0], rr[0]), min(ll[1], rr[1])] ax.set_autoscale_on(False) ax.set_xlim(ll) ax.set_ylim(ll) # Plot the 1-to-1 relations ax.plot(ax.get_xlim(), ax.get_ylim(), ls="-", color="black", c=".3") # Make the unused axes invisibles for ax in axs[len(params_to_plot):]: ax.axis('off') name = prepare_plot_saving(plot_name, overwrite=overwrite) plt.tight_layout() fig.savefig(name, dpi=None, facecolor='w', edgecolor='w', orientation='portrait', papertype='a4', format="pdf", transparent=False, bbox_inches="tight", pad_inches=0.1) plt.close(fig)
def get_plot(inp_x, inp_y, inp_clr): """Returns a Bokeh plot of the input values, and a message with the number of COFs found.""" q_list = [config.quantities[label] for label in [inp_x, inp_y, inp_clr]] results_wnone = get_data_aiida(q_list) #returns *** # dump None lists that make bokeh crash results = [] for l in results_wnone: if None not in l: results.append(l) # prepare data for plotting nresults = len(results) if not results: results = [['x', 'x', 'x', 0, 0, 0]] msg = "No matching COFs found." else: msg = "{} COFs found.<br> <b>Click on any point for details!</b>".format(nresults) mat_id, mat_name, mat_class, x, y, clrs = zip(*results) # returned *** x = list(map(float, x)) y = list(map(float, y)) clrs = list(map(float, clrs)) data = {'x': x, 'y': y, 'color': clrs, 'mat_id': mat_id, 'mat_name': mat_name, 'mat_class': mat_class} # create bokeh plot source = bmd.ColumnDataSource(data=data) hover = bmd.HoverTool(tooltips=[]) tap = bmd.TapTool() p_new = bpl.figure( plot_height=600, plot_width=600, toolbar_location='above', tools=[ 'pan', 'wheel_zoom', 'box_zoom', 'save', 'reset', hover, tap, ], active_drag='box_zoom', output_backend='webgl', title='', # trick: title is used as the colorbar label title_location='right', x_axis_type=q_list[0]['scale'], y_axis_type=q_list[1]['scale'], ) p_new.title.align = 'center' p_new.title.text_font_size = '10pt' p_new.title.text_font_style = 'italic' update_legends(p_new, q_list, hover) tap.callback = bmd.OpenURL(url="detail?mat_id=@mat_id") # Plot vertical line for comparison with amine-based technology (PE=1MJ/kg) if inp_y == 'CO2 parasitic energy (coal)': hline = bmd.Span(location=1, dimension='width', line_dash='dashed', line_color='grey', line_width=3) p_new.add_layout(hline) hline_descr = bmd.Label(x=30, y=1, x_units='screen', text_color='grey', text='amine-based sep.') p_new.add_layout(hline_descr) cmap = bmd.LinearColorMapper(palette=Plasma256, low=min(clrs), high=max(clrs)) fill_color = {'field': 'color', 'transform': cmap} p_new.circle('x', 'y', size=10, source=source, fill_color=fill_color) cbar = bmd.ColorBar(color_mapper=cmap, location=(0, 0)) p_new.add_layout(cbar, 'right') return p_new, msg
def get_plot(inp_x, inp_y, inp_clr): """Creates holoviews plot""" data, msg = prepare_data(inp_x, inp_y, inp_clr) source = bmd.ColumnDataSource(data=data) # hovering hover = get_hover(inp_x, inp_y, inp_clr) # tap tap = bmd.TapTool() tap.callback = bmd.OpenURL(url=explore_url + "/details/@uuid") # plot points = hv.Points( source.data, kdims=['x', 'y'], vdims=['color', 'name', 'uuid'], ) filtered = points.apply(filter_points, streams=[hv.streams.RangeXY(source=points)]) p_shaded = datashade( filtered, width=plot_px, height=plot_px, cmap=Plasma256, aggregator=ds.mean('color') # we want color of mean value under pixel ) p_hover = filtered.apply(hover_points) update_fn = functools.partial(update_legends, inp_x=inp_x, inp_y=inp_y, inp_clr=inp_clr) hv_plot = (dynspread(p_shaded) * p_hover).opts( hv.opts.Points( tools=[ tap, 'pan', 'box_zoom', 'save', 'reset', hover, ], active_tools=['wheel_zoom'], #active_scroll='box_zoom', #active_drag='box_zoom', alpha=0.2, hover_alpha=0.5, size=10, width=plot_px, height=plot_px, color='color', cmap=Plasma256, colorbar=True, show_grid=True, ), hv.opts(toolbar='above', finalize_hooks=[update_fn]), ) # output_backend='webgl', p_new = hv_renderer.get_plot(hv_plot).state return p_new, msg