import ast
import json
import re
import ipywidgets as widgets
import matplotlib.colors as mcolors
import numpy as np
from IPython.display import display, clear_output, HTML
from ipywidgets import Output
from pyccapt.calibration import clustering
from pyccapt.calibration.core import mc_plot, ion_selection
from pyccapt.calibration.core.mc_plot_peak_helpers import gaussian_mrp_report
from pyccapt.calibration.data_tools import data_loadcrop
from pyccapt.calibration.reconstructions import reconstruction, sdm, rdf, density_map
from pyccapt.calibration.reconstructions import iso_surface, proxigram
from pyccapt.calibration.tutorials.tutorials_helpers.helper_peak_spectral_analysis import build_peak_spectral_analysis_panel
# Define a layout for labels to make them a fixed width
label_layout = widgets.Layout(width='200px')
[docs]
def call_visualization(variables, colab=False):
if getattr(variables, 'range_data', None) is not None and 'name' not in variables.range_data.columns:
def _to_neutral_label(raw_value):
text = str(raw_value).strip().replace('$', '')
text = re.sub(r'_\{([^}]*)\}', r'\1', text)
text = re.sub(r'\^\{[^}]*\}', '', text)
text = text.replace('{', '').replace('}', '')
text = re.sub(r'\s*\d*[+-]+\s*$', '', text)
return text.strip()
if 'ion' in variables.range_data.columns:
variables.range_data['name'] = variables.range_data['ion'].apply(_to_neutral_label)
elif 'ion_name' in variables.range_data.columns:
variables.range_data['name'] = variables.range_data['ion_name'].apply(_to_neutral_label)
else:
variables.range_data['name'] = [f'range_{idx}' for idx in range(len(variables.range_data))]
print("Range table did not include 'name'; generated it from legacy ion labels.")
plot_mc_button = widgets.Button(description='Plot mc')
plot_3d_button = widgets.Button(description='Plot 3D')
plot_3d_button_iso = widgets.Button(description='Plot 3D iso surface')
plot_heatmap_button = widgets.Button(description='Plot heatmap')
plot_projection_button = widgets.Button(description='Plot projection')
clear_button = widgets.Button(description='Clear plots')
plot_fdm_button = widgets.Button(description="Plot FDM")
plot_experiment_button = widgets.Button(description="Plot experiment history")
density_map_button = widgets.Button(description="Plot density map")
plot_sdm_button = widgets.Button(description='Plot SDM')
plot_rdf_button = widgets.Button(description='Plot RDF')
show_color = widgets.Button(description='Show color')
change_color = widgets.Button(description='Change color')
if variables.range_data.empty or variables.range_data['ion'].iloc[0] == 'unranged':
element_percentage = str({'unranged': 0.01})
else:
# Iterate over unique elements in the "element" column
element_percentage = {}
for element_list in variables.range_data['element']:
for element in element_list:
# Check if the element is already a key in the dictionary
if element not in element_percentage:
element_percentage[element] = 0.01
element_percentage = str(element_percentage)
if variables.range_data.empty or variables.range_data['ion'].iloc[0] == 'unranged':
element_alpha = str({'unranged': 0.9})
else:
element_alpha = {}
for element_list in variables.range_data['element']:
for element in element_list:
if element not in element_alpha:
element_alpha[element] = 0.9
element_alpha = str(element_alpha)
#############
peak_find_plot = widgets.Dropdown(options=[('True', True), ('False', False)])
peaks_find = widgets.Dropdown(options=[('True', True), ('False', False)])
plot_ranged_colors = widgets.Dropdown(options=[('False', False), ('True', True)])
plot_ranged_peak = widgets.Dropdown(options=[('False', False), ('True', True)])
range_overlay_note = widgets.HTML(
value="<span style='color:#666;'>Ranged colors and ranged peak labels are mutually exclusive. "
"If you enable one, the other will be turned off.</span>"
)
target_mode = widgets.Dropdown(options=[('mc', 'mc'), ('mc_uc', 'mc_uc'), ('tof_c', 'tof_c'), ('tof', 'tof')])
print_info = widgets.Dropdown(options=[('False', False), ('True', True)])
log_widget = widgets.Dropdown(options=[('True', True), ('False', False)])
normalize = widgets.Dropdown(options=[('False', False), ('True', True)])
legend_widget = widgets.Dropdown(options=[('long', 'long'), ('short', 'short')])
# hist_steps = widgets.Dropdown(options=[('bar', 'bar'), ('stepfilled', 'stepfilled')])
bin_size_pm = widgets.FloatText(value=0.1)
lim_mc_pm = widgets.IntText(value=150)
prominence = widgets.IntText(value=50)
distance = widgets.IntText(value=50)
mrp_all = widgets.Dropdown(options=[('False', False), ('True', True)], value=False)
percent = widgets.IntText(value=50)
background_mc = widgets.Dropdown(options=[('None', None), ('aspls', 'aspls'), ('fabc', 'fabc'),
('manual@4', 'manual@4'),
('manual@100', 'manual@100'), ('manual', 'manual')])
figname_mc = widgets.Text(value='mc')
figure_mc_size_x_mc = widgets.FloatText(value=9.0)
figure_mc_size_y_mc = widgets.FloatText(value=5.0)
save_mc = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
grid_mc = widgets.Dropdown(options=[('False', False), ('True', True)])
mrp_left_mc = widgets.FloatText(value=0.0)
mrp_right_mc = widgets.FloatText(value=0.0)
load_mrp_window_mc_button = widgets.Button(description='Load selection')
gaussian_mrp_mc_button = widgets.Button(description='Gaussian MRP')
range_sequence_mc = widgets.Textarea(value='[0,0]')
range_detx_mc = widgets.Textarea(value='[0,0]')
range_dety_mc = widgets.Textarea(value='[0,0]')
range_mc_mc = widgets.Textarea(value='[0,0]')
range_x_mc = widgets.Textarea(value='[0,0]')
range_y_mc = widgets.Textarea(value='[0,0]')
range_z_mc = widgets.Textarea(value='[0,0]')
range_vol_mc = widgets.Textarea(value='[0,0]')
plot_mc_button.on_click(lambda b: plot_mc(b, variables, out))
def _sync_ranged_overlay(change, source):
if change.get('name') != 'value' or change.get('new') is not True:
return
if source == 'colors' and plot_ranged_peak.value:
plot_ranged_peak.value = False
elif source == 'peak' and plot_ranged_colors.value:
plot_ranged_colors.value = False
plot_ranged_colors.observe(lambda change: _sync_ranged_overlay(change, 'colors'), names='value')
plot_ranged_peak.observe(lambda change: _sync_ranged_overlay(change, 'peak'), names='value')
def plot_mc(b, variables, out):
plot_mc_button.disabled = True
figure_size = (figure_mc_size_x_mc.value, figure_mc_size_y_mc.value)
with out:
range_sequence = []
range_mc = []
range_detx = []
range_dety = []
range_x = []
range_y = []
range_z = []
range_vol = []
try:
# Use json.loads to convert the entered string to a list
range_sequence = json.loads(range_sequence_mc.value)
range_mc = json.loads(range_mc_mc.value)
range_detx = json.loads(range_detx_mc.value)
range_dety = json.loads(range_dety_mc.value)
range_x = json.loads(range_x_mc.value)
range_y = json.loads(range_y_mc.value)
range_z = json.loads(range_z_mc.value)
range_vol = json.loads(range_vol_mc.value)
if range_sequence == [0, 0]:
range_sequence = []
if range_mc == [0, 0]:
range_mc = []
if range_detx == [0, 0] or range_dety == [0, 0]:
range_detx = []
range_dety = []
if range_x == [0, 0] or range_y == [] or range_z == [0, 0]:
range_x = []
range_y = []
range_z = []
if range_vol == [0, 0]:
range_vol = []
except json.JSONDecodeError:
# Handle invalid input
print(f"Invalid range input")
plot_mc_button.disabled = False
return
effective_plot_ranged_colors = plot_ranged_colors.value
effective_plot_ranged_peak = plot_ranged_peak.value
if effective_plot_ranged_colors and effective_plot_ranged_peak:
print('Both ranged colors and ranged peak labels were selected. Using ranged colors overlay.')
effective_plot_ranged_peak = False
mc_plot.hist_plot(variables, bin_size_pm.value, log=log_widget.value, target=target_mode.value,
normalize=normalize.value,
prominence=prominence.value, distance=distance.value, percent=percent.value,
selector='rect',
figname=figname_mc.value, lim=lim_mc_pm.value, peaks_find=peaks_find.value,
peaks_find_plot=peak_find_plot.value, plot_ranged_colors=effective_plot_ranged_colors,
plot_ranged_peak=effective_plot_ranged_peak, mrp_all=mrp_all.value,
background=background_mc.value, grid=grid_mc.value,
range_sequence=range_sequence, range_mc=range_mc, range_detx=range_detx,
range_dety=range_dety, range_x=range_x, range_y=range_y, range_z=range_z,
range_vol=range_vol, print_info=print_info.value, figure_size=figure_size,
save_fig=save_mc.value, legend_mode=legend_widget.value)
plot_mc_button.disabled = False
def _resolve_visualization_hist_array():
if target_mode.value == 'mc_uc':
return variables.data['mc_uc (Da)']
if target_mode.value == 'tof_c':
return variables.data['t_c (ns)']
if target_mode.value == 'tof':
return variables.data['t (ns)']
return variables.data['mc (Da)']
def _resolve_visualization_gaussian_window():
left = float(mrp_left_mc.value)
right = float(mrp_right_mc.value)
if right > left:
return left, right
if getattr(variables, 'selected_x2', 0) > getattr(variables, 'selected_x1', 0):
return float(variables.selected_x1), float(variables.selected_x2)
return None
def _print_visualization_gaussian_report(result):
print('=' * 60)
print('PEAK PROFILE MRP REPORT')
print('=' * 60)
print(f'MRP model: {result["recommended_label"]}')
print(f'MRP bin size used: {result["bin_size"]} ({result["num_bins"]} bins)')
print(f'Peak position: {result["peak_position"]:.4f}')
print(f'Ions in range: {result["num_ions"]:,}')
print(f'Recommended FWHM MRP: {result["formatted_recommended_mrp"][0]}')
if result["window_warning"]:
print(result["window_warning"])
print()
print('Gaussian fit MRP:' if result['gaussian_ok'] else 'Gaussian fit FAILED')
if result['gaussian_ok']:
print(f' MRP(0.5) = {result["formatted_gaussian_mrp"][0]}')
print(f' MRP(0.1) = {result["formatted_gaussian_mrp"][1]}')
print(f' MRP(0.01) = {result["formatted_gaussian_mrp"][2]}')
print()
print('Voigt fit MRP:' if result['voigt_ok'] else 'Voigt fit FAILED')
if result['voigt_ok']:
print(f' MRP(0.5) = {result["formatted_voigt_mrp"][0]}')
print(f' MRP(0.1) = {result["formatted_voigt_mrp"][1]}')
print(f' MRP(0.01) = {result["formatted_voigt_mrp"][2]}')
print(f' Voigt FWHM = {result["voigt_fwhm"]:.6f}')
print()
print('Histogram-based MRP:')
print(f' MRP(0.5) = {result["formatted_histogram_mrp"][0]}')
print(f' MRP(0.1) = {result["formatted_histogram_mrp"][1]}')
print(f' MRP(0.01) = {result["formatted_histogram_mrp"][2]}')
print('=' * 60)
def load_mc_gaussian_window(_):
with out:
window = _resolve_visualization_gaussian_window()
if window is None:
print('No active mass/charge selection is available yet.')
else:
mrp_left_mc.value, mrp_right_mc.value = window
print(f'Loaded Gaussian MRP window: ({mrp_left_mc.value:.4f}, {mrp_right_mc.value:.4f})')
def run_mc_gaussian_mrp(_):
gaussian_mrp_mc_button.disabled = True
with out:
window = _resolve_visualization_gaussian_window()
if window is None:
print('Set MRP left/right or draw a selection first.')
else:
mrp_left_mc.value, mrp_right_mc.value = window
result = gaussian_mrp_report(
_resolve_visualization_hist_array(),
mrp_left_mc.value,
mrp_right_mc.value,
bin_size=0.001,
)
if result is None:
print('Gaussian MRP: insufficient data in selected range')
else:
_print_visualization_gaussian_report(result)
gaussian_mrp_mc_button.disabled = False
load_mrp_window_mc_button.on_click(load_mc_gaussian_window)
gaussian_mrp_mc_button.on_click(run_mc_gaussian_mrp)
#############
# Define widgets for each parameter
frac_fdm_widget = widgets.FloatText(value=1.0)
bins_fdm = widgets.Textarea(value='[256,256]')
axis_mode_fdm = widgets.Dropdown(options=['normal', 'scalebar'], value='normal')
figure_size_x_fdm = widgets.FloatText(value=5.0)
figure_size_y_fdm = widgets.FloatText(value=4.0)
save_fdm_widget = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
figname_fdm_widget = widgets.Text(value='fdm_ini')
range_sequence_fdm = widgets.Textarea(value='[0,0]')
range_detx_fdm = widgets.Textarea(value='[0,0]')
range_dety_fdm = widgets.Textarea(value='[0,0]')
range_mc_fdm = widgets.Textarea(value='[0,0]')
range_x_fdm = widgets.Textarea(value='[0,0]')
range_y_fdm = widgets.Textarea(value='[0,0]')
range_z_fdm = widgets.Textarea(value='[0,0]')
range_vol_fdm = widgets.Textarea(value='[0,0]')
plot_fdm_button.on_click(lambda b: plot_fdm(b, variables, out))
def plot_fdm(b, variables, out):
plot_fdm_button.disabled = True
with out: # Capture the output within the 'out' widget
# Call the function
data = variables.data.copy()
bins = json.loads(bins_fdm.value)
figure_size = (figure_size_x_fdm.value, figure_size_y_fdm.value)
try:
# Use json.loads to convert the entered string to a list
range_sequence = json.loads(range_sequence_fdm.value)
range_mc = json.loads(range_mc_fdm.value)
range_detx = json.loads(range_detx_fdm.value)
range_dety = json.loads(range_dety_fdm.value)
range_x = json.loads(range_x_fdm.value)
range_y = json.loads(range_y_fdm.value)
range_z = json.loads(range_z_fdm.value)
range_vol = json.loads(range_vol_fdm.value)
if range_sequence == [0, 0]:
range_sequence = []
if range_mc == [0, 0]:
range_mc = []
if range_detx == [0, 0] or range_dety == [0, 0]:
range_detx = []
range_dety = []
if range_x == [0, 0] or range_y == [] or range_z == [0, 0]:
range_x = []
range_y = []
range_z = []
if range_vol == [0, 0]:
range_vol = []
except json.JSONDecodeError:
# Handle invalid input
print(f"Invalid range input")
data_loadcrop.plot_crop_fdm(data['x_det (cm)'].to_numpy(), data['y_det (cm)'].to_numpy(), bins,
frac_fdm_widget.value, axis_mode_fdm.value, figure_size,
variables, range_sequence,
range_mc, range_detx, range_dety, range_x, range_y, range_z, range_vol,
False, False, 'circle',
save_fdm_widget.value, figname_fdm_widget.value)
# Enable the button when the code is finished
plot_fdm_button.disabled = False
#############
figname_3d = widgets.Text(value='3d_plot')
rotary_fig_save_p3 = widgets.Dropdown(options=[('False', False), ('True', True)])
element_percentage_p3 = widgets.Textarea(value=element_percentage)
element_alpha_p3 = widgets.Textarea(value=element_alpha)
opacity = widgets.FloatText(value=0.5, min=0, max=1, step=0.1)
save_3d = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
ions_individually_plots = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
make_gif_p3 = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
make_evap_3d = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
cluster_precipitate_3d = widgets.Dropdown(options=[('False', False), ('True', True)], value=False)
cluster_labels_3d = widgets.Text(value='', placeholder='Ni3Al, Al')
cluster_method_3d = widgets.Dropdown(
options=[
('MMax separation', 'maximum-separation'),
('HDBSCAN', 'hdbscan'),
('Comp-Seeded Support HDBSCAN', 'comp-seeded-support-hdbscan'),
('Composition GMM Voxel', 'composition-gmm-voxel'),
('CompSpace Agnostic + Seeded', 'compspace-agnostic-seeded'),
('Min-Max', 'min-max'),
],
value='maximum-separation',
)
cluster_count_3d = widgets.BoundedIntText(value=2, min=2, max=12)
cluster_dmax_3d = widgets.BoundedFloatText(value=1.0, min=0.0001, max=1_000_000.0, step=0.05)
cluster_auto_dmax_3d = widgets.Dropdown(options=[('True', True), ('False', False)], value=True)
cluster_kth_neighbor_3d = widgets.BoundedIntText(value=3, min=1, max=100)
cluster_percentile_3d = widgets.BoundedFloatText(value=50.0, min=1.0, max=99.9, step=1.0)
cluster_min_size_3d = widgets.BoundedIntText(value=25, min=2, max=1_000_000)
plot_3d_button.on_click(lambda b: plot_3d(b, variables, out))
range_sequence_3d = widgets.Textarea(value='[0,0]')
range_detx_3d = widgets.Textarea(value='[0,0]')
range_dety_3d = widgets.Textarea(value='[0,0]')
range_mc_3d = widgets.Textarea(value='[0,0]')
range_x_3d = widgets.Textarea(value='[0,0]')
range_y_3d = widgets.Textarea(value='[0,0]')
range_z_3d = widgets.Textarea(value='[0,0]')
range_vol_3d = widgets.Textarea(value='[0,0]')
def plot_3d(
b,
variables,
out,
cluster_display_mode='overlay',
cluster_result_override=None,
cluster_opacity_override=None,
):
plot_3d_button.disabled = True
try:
with out:
try:
# Use json.loads to convert the entered string to a list
range_sequence = json.loads(range_sequence_3d.value)
range_mc = json.loads(range_mc_3d.value)
range_detx = json.loads(range_detx_3d.value)
range_dety = json.loads(range_dety_3d.value)
range_x = json.loads(range_x_3d.value)
range_y = json.loads(range_y_3d.value)
range_z = json.loads(range_z_3d.value)
range_vol = json.loads(range_vol_3d.value)
if range_sequence == [0, 0]:
range_sequence = []
if range_mc == [0, 0]:
range_mc = []
if range_detx == [0, 0] or range_dety == [0, 0]:
range_detx = []
range_dety = []
if range_x == [0, 0] or range_y == [] or range_z == [0, 0]:
range_x = []
range_y = []
range_z = []
if range_vol == [0, 0]:
range_vol = []
except json.JSONDecodeError:
# Handle invalid input
print(f"Invalid range input")
element_percentage_dic = ast.literal_eval(element_percentage_p3.value)
# Iterate through the 'element' column
element_percentage_list = []
for row_elements in variables.range_data['element']:
max_value = 0.1 # Default value if no matching element is found
for element in row_elements:
if element in element_percentage_dic:
max_value = element_percentage_dic[element]
element_percentage_list.append(max_value)
element_alpha_list = _build_element_value_list(element_alpha_p3.value, 0.9)
if cluster_result_override is not None:
cluster_result = cluster_result_override
else:
cluster_result = _run_cluster_segmentation(
enabled=cluster_precipitate_3d.value,
selection_text=cluster_labels_3d.value,
method_value=cluster_method_3d.value,
cluster_count_value=cluster_count_3d.value,
d_max_value=cluster_dmax_3d.value,
auto_d_max_value=cluster_auto_dmax_3d.value,
kth_neighbor_value=cluster_kth_neighbor_3d.value,
percentile_value=cluster_percentile_3d.value,
n_min_value=cluster_min_size_3d.value,
context_label='3D clustering',
)
reconstruction.reconstruction_plot(variables, element_percentage_list, opacity.value,
rotary_fig_save_p3.value, figname_3d.value,
save_3d.value, make_gif_p3.value, make_evap_3d.value, range_sequence,
range_mc, range_detx, range_dety, range_x, range_y, range_z, range_vol,
ions_individually_plots.value, cluster_result=cluster_result,
cluster_display_mode=cluster_display_mode,
cluster_opacity_override=cluster_opacity_override,
element_alpha=element_alpha_list)
finally:
plot_3d_button.disabled = False
#############
max_tof_mc_widget = widgets.FloatText(value=variables.max_tof)
frac_mc_widget = widgets.FloatText(value=1.0)
bins_x_mc = widgets.IntText(value=1200)
bins_y_mc = widgets.IntText(value=800)
figure_size_x_mc = widgets.FloatText(value=7.0)
figure_size_y_mc = widgets.FloatText(value=3.0)
draw_rect_mc_widget = widgets.fixed(False)
data_crop_mc_widget = widgets.fixed(True)
pulse_plot_mc_widget = widgets.Dropdown(options=[('False', False), ('True', True)], value=False)
dc_plot_mc_widget = widgets.Dropdown(options=[('True', True), ('False', False)], value=True)
pulse_mode_mc_widget = widgets.Dropdown(options=[('voltage', 'voltage'), ('laser', 'laser')], value='voltage')
save_mc_widget = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
figname_mc_widget = widgets.Text(value='hist_ini')
plot_experiment_button.on_click(lambda b: plot_experimetns_hitstory(b, variables, out))
def plot_experimetns_hitstory(b, variables, out):
plot_experiment_button.disabled = True
with out:
data = variables.data.copy()
variables = variables
max_tof = max_tof_mc_widget.value
frac = frac_mc_widget.value
# Get the values from the editable widgets and create tuples
bins = (bins_x_mc.value, bins_y_mc.value)
figure_size = (figure_size_x_mc.value, figure_size_y_mc.value)
draw_rect = draw_rect_mc_widget.value
data_crop = data_crop_mc_widget.value
pulse_plot = pulse_plot_mc_widget.value
dc_plot = dc_plot_mc_widget.value
pulse_mode = pulse_mode_mc_widget.value
save = save_mc_widget.value
figname = figname_mc_widget.value
with out: # Capture the output within the 'out' widget
# Call the actual function with the obtained values
data_loadcrop.plot_crop_experiment_history(data, variables, max_tof, frac, bins, figure_size,
draw_rect, data_crop, pulse_plot, dc_plot,
pulse_mode, save, figname)
plot_experiment_button.disabled = False
#############
element_percentage_ph = widgets.Textarea(value=element_percentage)
figname_heatmap = widgets.Text(value='hitmap')
save_heatmap = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
figure_mc_size_x_heatmap = widgets.FloatText(value=5.0)
figure_mc_size_y_heatmap = widgets.FloatText(value=5.0)
range_sequence_heatmap = widgets.Textarea(value='[0,0]')
range_detx_heatmap = widgets.Textarea(value='[0,0]')
range_dety_heatmap = widgets.Textarea(value='[0,0]')
range_mc_heatmap = widgets.Textarea(value='[0,0]')
range_x_heatmap = widgets.Textarea(value='[0,0]')
range_y_heatmap = widgets.Textarea(value='[0,0]')
range_z_heatmap = widgets.Textarea(value='[0,0]')
range_vol_heatmap = widgets.Textarea(value='[0,0]')
plot_heatmap_button.on_click(lambda b: plot_heatmap(b, variables, out))
def plot_heatmap(b, variables, out):
plot_heatmap_button.disabled = True
figure_size = (figure_mc_size_x_heatmap.value, figure_mc_size_y_heatmap.value)
with out:
try:
# Use json.loads to convert the entered string to a list
range_sequence = json.loads(range_sequence_heatmap.value)
range_mc = json.loads(range_mc_heatmap.value)
range_detx = json.loads(range_detx_heatmap.value)
range_dety = json.loads(range_dety_heatmap.value)
range_x = json.loads(range_x_heatmap.value)
range_y = json.loads(range_y_heatmap.value)
range_z = json.loads(range_z_heatmap.value)
range_vol = json.loads(range_vol_heatmap.value)
if range_sequence == [0, 0]:
range_sequence = []
if range_mc == [0, 0]:
range_mc = []
if range_detx == [0, 0] or range_dety == [0, 0]:
range_detx = []
range_dety = []
if range_x == [0, 0] or range_y == [] or range_z == [0, 0]:
range_x = []
range_y = []
range_z = []
if range_vol == [0, 0]:
range_vol = []
except json.JSONDecodeError:
# Handle invalid input
print(f"Invalid range input")
element_percentage_dic = ast.literal_eval(element_percentage_p3.value)
# Iterate through the 'element' column
element_percentage_list = []
for row_elements in variables.range_data['element']:
max_value = 0.1 # Default value if no matching element is found
for element in row_elements:
if element in element_percentage_dic and element_percentage_dic[element] > max_value:
max_value = element_percentage_dic[element]
element_percentage_list.append(max_value)
reconstruction.heatmap(variables, element_percentage_list, range_sequence, range_mc, range_detx,
range_dety, range_x, range_y, range_z, range_vol,
figname_heatmap.value, figure_sie=figure_size, save=save_heatmap.value)
plot_heatmap_button.disabled = False
#############
points_per_frame_anim = widgets.IntText(value=50000)
selected_area_temporally_anim = widgets.Dropdown(options=[('False', False), ('True', True)])
selected_area_specially_anim = widgets.Dropdown(options=[('False', False), ('True', True)])
figname_anim = widgets.Text(value='detector_animation')
figure_size_x_anim = widgets.FloatText(value=5.0)
figure_size_y_anim = widgets.FloatText(value=5.0)
ranged_anim = widgets.Dropdown(options=[('False', False), ('True', True)])
plot_animated_heatmap_button = widgets.Button(description="Plot animated heatmap")
def plot_animated_heatmap(b, variables, out):
points_per_frame = points_per_frame_anim.value
selected_area_temporally = selected_area_temporally_anim.value
selected_area_specially = selected_area_specially_anim.value
figure_name = figname_anim.value
figure_size = (figure_size_x_anim.value, figure_size_y_anim.value)
ranged = ranged_anim.value
plot_animated_heatmap_button.disabled = True
with out:
reconstruction.detector_animation(variables, points_per_frame, ranged, selected_area_specially,
selected_area_temporally, figure_name,
figure_size, save=True)
display(HTML(variables.animation_detector_html))
plot_animated_heatmap_button.disabled = False
plot_animated_heatmap_button.on_click(lambda b: plot_animated_heatmap(b, variables, out))
#############
element_percentage_pp = widgets.Textarea(value=element_percentage)
x_or_y_pp = widgets.Dropdown(options=['x', 'y'], value='x')
figname_p = widgets.Text(value='projection')
save_projection = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
figure_mc_size_x_projection = widgets.FloatText(value=5.0)
figure_mc_size_y_projection = widgets.FloatText(value=5.0)
range_sequence_pp = widgets.Textarea(value='[0,0]')
range_detx_pp = widgets.Textarea(value='[0,0]')
range_dety_pp = widgets.Textarea(value='[0,0]')
range_mc_pp = widgets.Textarea(value='[0,0]')
range_x_pp = widgets.Textarea(value='[0,0]')
range_y_pp = widgets.Textarea(value='[0,0]')
range_z_pp = widgets.Textarea(value='[0,0]')
range_vol_pp = widgets.Textarea(value='[0,0]')
plot_projection_button.on_click(lambda b: plot_projection(b, variables, out))
def plot_projection(b, variables, out):
plot_projection_button.disabled = True
figure_size = (figure_mc_size_x_projection.value, figure_mc_size_y_projection.value)
with out:
try:
# Use json.loads to convert the entered string to a list
range_sequence = json.loads(range_sequence_pp.value)
range_mc = json.loads(range_mc_pp.value)
range_detx = json.loads(range_detx_pp.value)
range_dety = json.loads(range_dety_pp.value)
range_x = json.loads(range_x_pp.value)
range_y = json.loads(range_y_pp.value)
range_z = json.loads(range_z_pp.value)
range_vol = json.loads(range_vol_pp.value)
if range_sequence == [0, 0]:
range_sequence = []
if range_mc == [0, 0]:
range_mc = []
if range_detx == [0, 0] or range_dety == [0, 0]:
range_detx = []
range_dety = []
if range_x == [0, 0] or range_y == [] or range_z == [0, 0]:
range_x = []
range_y = []
range_z = []
if range_vol == [0, 0]:
range_vol = []
except json.JSONDecodeError:
# Handle invalid input
print(f"Invalid range input")
element_percentage_dic = ast.literal_eval(element_percentage_p3.value)
# Iterate through the 'element' column
element_percentage_list = []
for row_elements in variables.range_data['element']:
max_value = 0.1 # Default value if no matching element is found
for element in row_elements:
if element in element_percentage_dic and element_percentage_dic[element] > max_value:
max_value = element_percentage_dic[element]
element_percentage_list.append(max_value)
reconstruction.projection(variables, element_percentage_list, range_sequence, range_mc, range_detx,
range_dety, range_x, range_y, range_z, range_vol, x_or_y_pp.value,
figname_p.value, figure_size, save_projection.value)
plot_projection_button.disabled = False
#############
first_axis_d = widgets.Dropdown(options=['x', 'y', 'z'], value='x')
second_axis_d = widgets.Dropdown(options=['x', 'y', 'z'], value='y')
composition_d = widgets.Textarea(value="['Al']")
roi_d = widgets.Textarea(value='[0,0,0]')
bins2d_hist = widgets.Textarea(value='[0.5]')
percentage_2d_hist = widgets.FloatText(value=1.0)
save_2d_hist = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
z_weight_2d_hist = widgets.Dropdown(options=[('False', False), ('True', True)], value=False)
log_d = widgets.Dropdown(options=[('True', True), ('False', False)])
axis_mode_d = widgets.Dropdown(options=['normal', 'scalebar'], value='normal')
# plasma, viridis, inferno, cividis, coolwarm, twilight, cubehelix
cmap_d = widgets.Dropdown(options=['plasma', 'viridis', 'inferno', 'cividis', 'coolwarm', 'twilight', 'cubehelix'])
normalize_d = widgets.Dropdown(options=[('False', False), ('True', True)], value=False)
normalize_axes_d = widgets.Dropdown(options=[('False', False), ('True', True)], value=False)
figname_2d_hist = widgets.Text(value='density_map')
figure_size_x_2d_hist = widgets.FloatText(value=5.0)
figure_size_y_2d_hist = widgets.FloatText(value=4.0)
range_sequence_2d_hist = widgets.Textarea(value='[0,0]')
range_detx_2d_hist = widgets.Textarea(value='[0,0]')
range_dety_2d_hist = widgets.Textarea(value='[0,0]')
range_mc_2d_hist = widgets.Textarea(value='[0,0]')
range_x_2d_hist = widgets.Textarea(value='[0,0]')
range_y_2d_hist = widgets.Textarea(value='[0,0]')
range_z_2d_hist = widgets.Textarea(value='[0,0]')
range_vol_2d_hist = widgets.Textarea(value='[0,0]')
density_map_button.on_click(lambda b: density_map_plot(b, variables, out))
def density_map_plot(b, variables, out):
density_map_button.disabled = True
with out: # Capture the output within the 'out' widget
if first_axis_d.value == 'x' and second_axis_d.value == 'y':
x = variables.x
y = variables.y
axis_d = ['x', 'y']
elif first_axis_d.value == 'y' and second_axis_d.value == 'x':
x = variables.y
y = variables.x
axis_d = ['y', 'x']
elif first_axis_d.value == 'x' and second_axis_d.value == 'z':
x = variables.x
y = variables.z
axis_d = ['x', 'z']
elif first_axis_d.value == 'z' and second_axis_d.value == 'x':
x = variables.z
y = variables.x
axis_d = ['z', 'x']
elif first_axis_d.value == 'y' and second_axis_d.value == 'z':
x = variables.y
y = variables.z
axis_d = ['y', 'z']
elif first_axis_d.value == 'z' and second_axis_d.value == 'y':
x = variables.z
y = variables.y
axis_d = ['z', 'y']
else:
raise ValueError('Invalid axis')
bins = json.loads(bins2d_hist.value)
percentage = percentage_2d_hist.value
composition = ast.literal_eval(composition_d.value)
roi = ast.literal_eval(roi_d.value)
figure_name = figname_2d_hist.value + '_' + first_axis_d.value + '_' + second_axis_d.value
save = save_2d_hist.value
figure_size = (figure_size_x_2d_hist.value, figure_size_y_2d_hist.value)
try:
# Use json.loads to convert the entered string to a list
range_sequence = json.loads(range_sequence_2d_hist.value)
range_mc = json.loads(range_mc_2d_hist.value)
range_detx = json.loads(range_detx_2d_hist.value)
range_dety = json.loads(range_dety_2d_hist.value)
range_x = json.loads(range_x_2d_hist.value)
range_y = json.loads(range_y_2d_hist.value)
range_z = json.loads(range_z_2d_hist.value)
range_vol = json.loads(range_vol_2d_hist.value)
if range_sequence == [0, 0]:
range_sequence = []
if range_mc == [0, 0]:
range_mc = []
if range_detx == [0, 0] or range_dety == [0, 0]:
range_detx = []
range_dety = []
if range_x == [0, 0] or range_y == [] or range_z == [0, 0]:
range_x = []
range_y = []
range_z = []
if range_vol == [0, 0]:
range_vol = []
except json.JSONDecodeError:
# Handle invalid input
print(f"Invalid range input")
density_map.plot_density_map(x, y, z_weight_2d_hist.value, log_d.value, bins, percentage, axis_mode_d.value,
figure_size, variables, composition, roi, range_sequence, range_mc, range_detx,
range_dety, range_x, range_y, range_z, range_vol, False, False, 'circle', axis_d, save,
figure_name, cmap_d.value, normalize_d.value, normalize_axes_d.value)
density_map_button.disabled = False
#############
first_axis_sdm = widgets.Dropdown(options=['x', 'y', 'z'], value='z')
second_axis_sdm = widgets.Dropdown(options=['x', 'y', 'z'], value='y')
sdm_mode = widgets.Dropdown(options=['1D', '2D'], value='1D')
normalized_sdm = widgets.Dropdown(options=[('False', False), ('True', True)], value=False)
log_sdm = widgets.Dropdown(options=[('True', True), ('False', False)], value=True)
roi_sdm = widgets.Textarea(value='[0,0,0.5]')
unique_ions = variables.range_data['name'].unique()
composition_sdm_list_i = []
composition_sdm_list_j = []
for ion in unique_ions:
composition_sdm_list_i.append(widgets.Checkbox(value=False, description=ion))
composition_sdm_list_j.append(widgets.Checkbox(value=False, description=ion))
i_composition_sdm = composition_sdm_list_i
j_composition_sdm = composition_sdm_list_j
theta_x_sdm = widgets.FloatText(value=0.0)
phi_y_sdm = widgets.FloatText(value=0.0)
percentage_sdm = widgets.FloatText(value=1.0)
range_sequence_sdm = widgets.Textarea(value='[0,0]')
range_detx_sdm = widgets.Textarea(value='[0,0]')
range_dety_sdm = widgets.Textarea(value='[0,0]')
range_mc_sdm = widgets.Textarea(value='[0,0]')
range_x_sdm = widgets.Textarea(value='[0,0]')
range_y_sdm = widgets.Textarea(value='[0,0]')
range_z_sdm = widgets.Textarea(value='[0,0]')
range_vol_sdm = widgets.Textarea(value='[0,0]')
z_cut_sdm = widgets.Dropdown(options=[('False', False), ('True', True)], value=True)
plot_roi_sdm = widgets.Dropdown(options=[('False', False), ('True', True)], value=True)
bins_size_sdm = widgets.FloatText(value=0.02)
save_sdm = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
plot_mode_sdm = widgets.Dropdown(options=[('line', 'line'), ('bar', 'bar')])
figname_2d_sdm = widgets.Text(value='sdm')
figure_size_x_2d_sdm = widgets.FloatText(value=10.0)
figure_size_y_2d_sdm = widgets.FloatText(value=4.0)
plot_sdm_button.on_click(lambda b: plot_sdm(b, variables, out))
def plot_sdm(b, variables, out):
try:
# Use json.loads to convert the entered string to a list
range_sequence = json.loads(range_sequence_sdm.value)
range_mc = json.loads(range_mc_sdm.value)
range_detx = json.loads(range_detx_sdm.value)
range_dety = json.loads(range_dety_sdm.value)
range_x = json.loads(range_x_sdm.value)
range_y = json.loads(range_y_sdm.value)
range_z = json.loads(range_z_sdm.value)
range_vol = json.loads(range_vol_sdm.value)
if range_sequence == [0, 0]:
range_sequence = []
if range_mc == [0, 0]:
range_mc = []
if range_detx == [0, 0] or range_dety == [0, 0]:
range_detx = []
range_dety = []
if range_x == [0, 0] or range_y == [] or range_z == [0, 0]:
range_x = []
range_y = []
range_z = []
if range_vol == [0, 0]:
range_vol = []
except json.JSONDecodeError:
# Handle invalid input
print(f"Invalid range input")
particles = np.vstack((variables.x, variables.y, variables.z)).T
figure_size = (figure_size_x_2d_sdm.value, figure_size_y_2d_sdm.value)
if sdm_mode.value == '1D':
axis_s = [first_axis_sdm.value]
elif sdm_mode.value == '2D':
axis_s = [first_axis_sdm.value, second_axis_sdm.value]
plot_sdm_button.disabled = True
roi = ast.literal_eval(roi_sdm.value)
with out:
if 'x' in axis_s and 'y' in axis_s:
axis_sdm = ['x', 'y']
elif 'y' in axis_s and 'x' in axis_s:
axis_sdm = ['y', 'x']
elif 'x' in axis_s and 'z' in axis_s:
axis_sdm = ['x', 'z']
elif 'z' in axis_s and 'x' in axis_s:
axis_sdm = ['z', 'x']
elif 'y' in axis_s and 'z' in axis_s:
axis_sdm = ['y', 'z']
elif 'z' in axis_s and 'y' in axis_s:
axis_sdm = ['z', 'y']
elif 'x' in axis_s:
axis_sdm = ['x']
elif 'y' in axis_s:
axis_sdm = ['y']
elif 'z' in axis_s:
axis_sdm = ['z']
else:
raise ValueError('Invalid axis')
i_composition = [devom.value for devom in i_composition_sdm]
j_composition = [devom.value for devom in j_composition_sdm]
i_composition_filtered = [
checkbox.description for checkbox, include in zip(i_composition_sdm, i_composition) if include
]
j_composition_filtered = [
checkbox.description for checkbox, include in zip(j_composition_sdm, j_composition) if include
]
sdm.sdm(particles, bins_size_sdm.value, variables, roi, z_cut_sdm.value, normalized_sdm.value,
plot_mode_sdm.value, True, save_sdm.value, figure_size, figname_2d_sdm.value, sdm_mode.value,
axis_sdm, i_composition_filtered, j_composition_filtered, plot_roi_sdm.value, theta_x_sdm.value,
phi_y_sdm.value, log_sdm.value, percentage_sdm.value,
range_sequence, range_mc, range_detx, range_dety, range_x, range_y, range_z, range_vol)
plot_sdm_button.disabled = False
##############
normalized_rdf = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
reference_point_rdf = widgets.Textarea(value='[0,0,0]')
box_dimension_rdf = widgets.Textarea(value='[1,1,1]')
dr_rdf = widgets.FloatText(value=0.1)
rdf_cutoff = widgets.FloatText(value=0.9)
save_rdf = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
figname_2d_rdf = widgets.Text(value='rdf')
figure_size_x_2d_rdf = widgets.FloatText(value=5.0)
figure_size_y_2d_rdf = widgets.FloatText(value=4.0)
range_sequence_rdf = widgets.Textarea(value='[0,0]')
plot_rdf_button.on_click(lambda b: plot_rdf(b, variables, out))
def plot_rdf(b, variables, out):
try:
# Use json.loads to convert the entered string to a list
reference_point = json.loads(reference_point_rdf.value)
box_dimension = json.loads(box_dimension_rdf.value)
range_sequence = json.loads(range_sequence_rdf.value)
except json.JSONDecodeError:
# Handle invalid input
print(f"Invalid range input")
if range_sequence != [0, 0]:
mask_sequence = np.zeros_like(variables.dld_x_det, dtype=bool)
mask_sequence[range_sequence[0]:range_sequence[1]] = True
else:
mask_sequence = np.ones_like(variables.dld_x_det, dtype=bool)
particles = np.vstack((variables.x[mask_sequence], variables.y[mask_sequence], variables.z[mask_sequence])).T
figure_size_rdf = (figure_size_x_2d_rdf.value, figure_size_y_2d_rdf.value)
plot_rdf_button.disabled = True
with out:
rdf.rdf(particles, dr_rdf.value, variables, rcutoff=rdf_cutoff.value, normalize=normalized_rdf.value,
reference_point=reference_point, box_dimensions=box_dimension, plot=True, save=save_rdf.value,
figure_size=figure_size_rdf, figname=figname_2d_rdf.value)
plot_rdf_button.disabled = False
def _parse_common_ranges(sequence_widget, mc_widget, detx_widget, dety_widget,
x_widget, y_widget, z_widget, vol_widget):
range_sequence = json.loads(sequence_widget.value)
range_mc = json.loads(mc_widget.value)
range_detx = json.loads(detx_widget.value)
range_dety = json.loads(dety_widget.value)
range_x = json.loads(x_widget.value)
range_y = json.loads(y_widget.value)
range_z = json.loads(z_widget.value)
range_vol = json.loads(vol_widget.value)
if range_sequence == [0, 0]:
range_sequence = []
if range_mc == [0, 0]:
range_mc = []
if range_detx == [0, 0] or range_dety == [0, 0]:
range_detx = []
range_dety = []
if range_x == [0, 0] or range_y == [0, 0] or range_z == [0, 0]:
range_x = []
range_y = []
range_z = []
if range_vol == [0, 0]:
range_vol = []
return range_sequence, range_mc, range_detx, range_dety, range_x, range_y, range_z, range_vol
def _parse_isosurface_dict(value, *, allow_multiple=True, field_name='Isosurface dictionary'):
text = str(value).strip()
if not text:
if allow_multiple:
raise ValueError(
f'{field_name} must not be empty. Example: {{Al: [3,3,3], Ni: [4,4,4]}}'
)
raise ValueError(f'{field_name} must not be empty. Example: {{Al: [3,3,3]}}')
formatted_string = re.sub(r'([A-Za-z0-9_]+)\s*:', r'"\1":', text)
try:
parsed = ast.literal_eval(formatted_string)
except (ValueError, SyntaxError) as exc:
if allow_multiple:
raise ValueError(
f'{field_name} must look like {{Al: [3,3,3], Ni: [4,4,4]}}'
) from exc
raise ValueError(
f'{field_name} must look like {{Al: [3,3,3]}}'
) from exc
if not isinstance(parsed, dict) or not parsed:
raise ValueError(f'{field_name} must be a non-empty dictionary')
if not allow_multiple and len(parsed) != 1:
raise ValueError(
f'{field_name} must contain exactly one entry for proxigram, for example {{Al: [3,3,3]}}'
)
normalized = {}
for key, raw_value in parsed.items():
element_name = str(key).strip()
if not element_name:
raise ValueError(f'{field_name} contains an empty element name')
if not isinstance(raw_value, (list, tuple)) or len(raw_value) != 3:
raise ValueError(
f'{field_name} entry for {element_name} must be a 3-value list like [3,3,3]'
)
try:
normalized[element_name] = [float(item) for item in raw_value]
except (TypeError, ValueError) as exc:
raise ValueError(
f'{field_name} entry for {element_name} must contain only numeric values'
) from exc
return normalized
def _parse_element_list(value, field_name='Element list'):
text = str(value).strip()
if not text:
raise ValueError(f'{field_name} must not be empty. Example: Al, Ni, Cr')
parsed = None
if text.startswith('[') or text.startswith('('):
try:
parsed = ast.literal_eval(text)
except (ValueError, SyntaxError) as exc:
raise ValueError(
f'{field_name} must be comma-separated like Al, Ni, Cr or a list like ["Al", "Ni", "Cr"]'
) from exc
if parsed is None:
items = re.split(r'[,;\n]+', text)
elif isinstance(parsed, str):
items = [parsed]
elif isinstance(parsed, (list, tuple, set)):
items = list(parsed)
else:
raise ValueError(
f'{field_name} must be comma-separated like Al, Ni, Cr or a list like ["Al", "Ni", "Cr"]'
)
normalized = []
seen = set()
for item in items:
item = str(item).strip()
if not item or item in seen:
continue
normalized.append(item)
seen.add(item)
if not normalized:
raise ValueError(f'{field_name} must contain at least one valid element')
return normalized
def _build_element_percentage_list(value):
element_percentage_dic = ast.literal_eval(value)
element_percentage_list = []
for row_elements in variables.range_data['element']:
max_value = 0.1
for element in row_elements:
if element in element_percentage_dic:
max_value = element_percentage_dic[element]
element_percentage_list.append(max_value)
return element_percentage_list
def _build_element_value_list(value, default_value):
element_value_dic = ast.literal_eval(value)
element_value_list = []
for row_elements in variables.range_data['element']:
max_value = default_value
for element in row_elements:
if element in element_value_dic:
max_value = element_value_dic[element]
element_value_list.append(max_value)
return element_value_list
def _run_cluster_segmentation(*, enabled, selection_text, method_value, cluster_count_value,
d_max_value, auto_d_max_value, kth_neighbor_value, percentile_value,
n_min_value, context_label, method_kwargs=None):
if not enabled:
return None
cluster_selection = clustering.parse_label_selection(selection_text)
if not cluster_selection:
print(f'{context_label} is enabled, but no ion or element labels were provided. Skipping segmentation.')
return None
try:
method = clustering.normalize_clustering_method(method_value)
if method_kwargs is None:
method_kwargs = {}
if method in ('min-max', 'composition-gmm-voxel', 'compspace-agnostic-seeded'):
cluster_result = clustering.segment_ions(
variables,
cluster_selection,
method=method,
n_clusters=max(2, int(cluster_count_value)),
n_min=max(2, int(n_min_value)),
**method_kwargs,
)
print(f'{method} clustering counts:', cluster_result.counts)
return cluster_result
cluster_result = clustering.segment_ions(
variables,
cluster_selection,
method=method,
d_max=float(d_max_value),
n_min=max(2, int(n_min_value)),
auto_d_max=bool(auto_d_max_value),
kth_neighbor=max(1, int(kth_neighbor_value)),
percentile=float(percentile_value),
**method_kwargs,
)
d_max_used = None
if cluster_result.parameters is not None:
d_max_used = float(cluster_result.parameters.get('d_max', 0.0))
method_title = 'HDBSCAN' if method == 'hdbscan' else 'Maximum separation'
if cluster_result.n_clusters == 0:
if d_max_used is not None and d_max_used > 0:
print(
f'{method_title} found no clusters '
f'(d_max={d_max_used:.4f}, min cluster size={max(2, int(n_min_value))}).'
)
else:
print(f'{method_title} found no clusters.')
else:
print(f'{method_title} counts:', cluster_result.counts)
if d_max_used is not None and d_max_used > 0:
mode_name = 'auto-estimated' if auto_d_max_value else 'manual'
print(
f'Using d_max={d_max_used:.4f} ({mode_name}), '
f'min cluster size={max(2, int(n_min_value))}.'
)
return cluster_result
except ValueError as exc:
print(f'{context_label} error: {exc}')
return None
#############
figname_3d_iso = widgets.Text(value='3d_plot_iso')
rotary_fig_save_p3_iso = widgets.Dropdown(options=[('False', False), ('True', True)])
element_percentage_p3_iso = widgets.Textarea(value=element_percentage)
opacity_iso = widgets.FloatText(value=0.5, min=0, max=1, step=0.1)
save_3d_iso = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
ions_individually_plots_iso = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
make_gif_p3_iso = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
cluster_precipitate_iso = widgets.Dropdown(options=[('False', False), ('True', True)], value=False)
cluster_labels_iso = widgets.Text(value='', placeholder='Ni3Al, Al')
cluster_method_iso = widgets.Dropdown(
options=[
('MMax separation', 'maximum-separation'),
('HDBSCAN', 'hdbscan'),
('Comp-Seeded Support HDBSCAN', 'comp-seeded-support-hdbscan'),
('Composition GMM Voxel', 'composition-gmm-voxel'),
('CompSpace Agnostic + Seeded', 'compspace-agnostic-seeded'),
('Min-Max', 'min-max'),
],
value='maximum-separation',
)
cluster_count_iso = widgets.BoundedIntText(value=2, min=2, max=12)
cluster_dmax_iso = widgets.BoundedFloatText(value=1.0, min=0.0001, max=1_000_000.0, step=0.05)
cluster_auto_dmax_iso = widgets.Dropdown(options=[('True', True), ('False', False)], value=True)
cluster_kth_neighbor_iso = widgets.BoundedIntText(value=3, min=1, max=100)
cluster_percentile_iso = widgets.BoundedFloatText(value=50.0, min=1.0, max=99.9, step=1.0)
cluster_min_size_iso = widgets.BoundedIntText(value=25, min=2, max=1_000_000)
plot_3d_button_iso.on_click(lambda b: plot_3d_iso(b, variables, out))
isosurface_dic_p3_iso = widgets.Textarea(
value="{Al: [3,3,3]}",
placeholder="{Al: [3,3,3], Ni: [4,4,4]}",
)
detailed_isotope_charge_3d_iso = widgets.Dropdown(options=[('False', False), ('True', True)], value=False)
only_iso_3d_iso = widgets.Dropdown(options=[('False', False), ('True', True)], value=False)
pure_element_only_iso = widgets.Dropdown(options=[('True', True), ('False', False)], value=True)
smoothing_sigma_iso = widgets.FloatText(value=1.0)
manual_iso_value_iso = widgets.FloatText(value=0.0)
min_atoms_per_voxel_iso = widgets.IntText(value=10)
min_vertices_iso = widgets.IntText(value=20)
range_sequence_3d_iso = widgets.Textarea(value='[0,0]')
range_detx_3d_iso = widgets.Textarea(value='[0,0]')
range_dety_3d_iso = widgets.Textarea(value='[0,0]')
range_mc_3d_iso = widgets.Textarea(value='[0,0]')
range_x_3d_iso = widgets.Textarea(value='[0,0]')
range_y_3d_iso = widgets.Textarea(value='[0,0]')
range_z_3d_iso = widgets.Textarea(value='[0,0]')
range_vol_3d_iso = widgets.Textarea(value='[0,0]')
def _sync_cluster_method_controls(method_widget, count_widget, auto_dmax_widget, dmax_widget,
kth_neighbor_widget, percentile_widget, min_size_widget,
note_widget=None):
method = clustering.normalize_clustering_method(method_widget.value)
uses_fixed_count = method in ('min-max', 'composition-gmm-voxel', 'compspace-agnostic-seeded')
uses_density_params = method in ('maximum-separation', 'hdbscan')
auto_d_max = bool(auto_dmax_widget.value)
count_widget.disabled = not uses_fixed_count
auto_dmax_widget.disabled = not uses_density_params
min_size_widget.disabled = not uses_density_params
dmax_widget.disabled = (not uses_density_params) or auto_d_max
kth_neighbor_widget.disabled = (not uses_density_params) or (not auto_d_max)
percentile_widget.disabled = (not uses_density_params) or (not auto_d_max)
if note_widget is not None:
if uses_fixed_count:
note_widget.value = (
"<i>This method uses the selected number of clusters and ignores d max settings.</i>"
)
elif auto_d_max:
note_widget.value = (
"<i>This method finds as many connected clusters as the data supports. "
"Number of clusters is not used.</i>"
)
else:
note_widget.value = (
"<i>This method uses the manual d max cutoff and ignores Number of clusters.</i>"
)
def _update_all_cluster_control_states(*_args):
_sync_cluster_method_controls(
cluster_method_3d,
cluster_count_3d,
cluster_auto_dmax_3d,
cluster_dmax_3d,
cluster_kth_neighbor_3d,
cluster_percentile_3d,
cluster_min_size_3d,
)
_sync_cluster_method_controls(
cluster_method_iso,
cluster_count_iso,
cluster_auto_dmax_iso,
cluster_dmax_iso,
cluster_kth_neighbor_iso,
cluster_percentile_iso,
cluster_min_size_iso,
)
for widget in (
cluster_method_3d,
cluster_auto_dmax_3d,
cluster_method_iso,
cluster_auto_dmax_iso,
):
widget.observe(_update_all_cluster_control_states, names='value')
_update_all_cluster_control_states()
def plot_3d_iso(b, variables, out, cluster_display_mode='overlay', cluster_result_override=None):
plot_3d_button_iso.disabled = True
try:
with out:
try:
range_sequence, range_mc, range_detx, range_dety, range_x, range_y, range_z, range_vol = _parse_common_ranges(
range_sequence_3d_iso,
range_mc_3d_iso,
range_detx_3d_iso,
range_dety_3d_iso,
range_x_3d_iso,
range_y_3d_iso,
range_z_3d_iso,
range_vol_3d_iso,
)
isosurface_dic_p3_iso_value = _parse_isosurface_dict(
isosurface_dic_p3_iso.value,
allow_multiple=True,
field_name='Iso surface dictionary',
)
element_percentage_list_iso = _build_element_percentage_list(element_percentage_p3_iso.value)
except (json.JSONDecodeError, ValueError, SyntaxError) as exc:
print(f'Invalid iso plot input: {exc}')
return
if cluster_result_override is not None:
cluster_result = cluster_result_override
else:
cluster_result = _run_cluster_segmentation(
enabled=cluster_precipitate_iso.value,
selection_text=cluster_labels_iso.value,
method_value=cluster_method_iso.value,
cluster_count_value=cluster_count_iso.value,
d_max_value=cluster_dmax_iso.value,
auto_d_max_value=cluster_auto_dmax_iso.value,
kth_neighbor_value=cluster_kth_neighbor_iso.value,
percentile_value=cluster_percentile_iso.value,
n_min_value=cluster_min_size_iso.value,
context_label='Iso-surface clustering',
)
iso_surface.reconstruction_plot(variables, element_percentage_list_iso, opacity_iso.value,
rotary_fig_save_p3_iso.value, figname_3d_iso.value,
save_3d_iso.value, make_gif_p3_iso.value,
range_sequence,
range_mc, range_detx, range_dety, range_x, range_y, range_z, range_vol,
ions_individually_plots_iso.value,
max_num_ions=None, min_num_ions=None,
isosurface_dic=isosurface_dic_p3_iso_value,
detailed_isotope_charge=detailed_isotope_charge_3d_iso.value,
only_iso=only_iso_3d_iso.value,
cluster_result=cluster_result,
smoothing_sigma=smoothing_sigma_iso.value,
manual_iso_value=(manual_iso_value_iso.value
if manual_iso_value_iso.value > 0 else None),
min_atoms_per_voxel=min_atoms_per_voxel_iso.value,
min_isosurface_vertices=min_vertices_iso.value,
pure_element_only=pure_element_only_iso.value,
cluster_display_mode=cluster_display_mode)
finally:
plot_3d_button_iso.disabled = False
#############
plot_proxigram_button = widgets.Button(description='Plot proxigram')
figname_proxigram = widgets.Text(value='proxigram')
proxigram_isosurface_dic = widgets.Textarea(
value="{Al: [3,3,3]}",
placeholder="{Al: [3,3,3]}",
)
proxigram_elements = widgets.Text(
value='Al',
placeholder='Al, Ni, Ti or [\"Al\", \"Ni\", \"Ti\"]',
)
proxigram_bin_size = widgets.FloatText(value=0.1)
proxigram_symmetric_range = widgets.FloatText(value=0.0)
proxigram_flip_normals = widgets.Dropdown(options=[('False', False), ('True', True)], value=False)
proxigram_save = widgets.Dropdown(options=[('True', True), ('False', False)], value=False)
proxigram_pure_element_only = widgets.Dropdown(options=[('True', True), ('False', False)], value=True)
proxigram_sigma = widgets.FloatText(value=1.0)
proxigram_manual_iso_value = widgets.FloatText(value=0.0)
proxigram_min_atoms = widgets.IntText(value=10)
proxigram_min_vertices = widgets.IntText(value=20)
range_sequence_prox = widgets.Textarea(value='[0,0]')
range_detx_prox = widgets.Textarea(value='[0,0]')
range_dety_prox = widgets.Textarea(value='[0,0]')
range_mc_prox = widgets.Textarea(value='[0,0]')
range_x_prox = widgets.Textarea(value='[0,0]')
range_y_prox = widgets.Textarea(value='[0,0]')
range_z_prox = widgets.Textarea(value='[0,0]')
range_vol_prox = widgets.Textarea(value='[0,0]')
def plot_proxigram_view(b, variables, out):
plot_proxigram_button.disabled = True
with out:
try:
range_sequence, range_mc, range_detx, range_dety, range_x, range_y, range_z, range_vol = _parse_common_ranges(
range_sequence_prox,
range_mc_prox,
range_detx_prox,
range_dety_prox,
range_x_prox,
range_y_prox,
range_z_prox,
range_vol_prox,
)
interface_dic = _parse_isosurface_dict(
proxigram_isosurface_dic.value,
allow_multiple=False,
field_name='Proxigram interface isosurface',
)
proxigram_elements_list = _parse_element_list(
proxigram_elements.value,
field_name='Proxigram elements',
)
symmetric_range = proxigram_symmetric_range.value if proxigram_symmetric_range.value > 0 else None
proxigram.plot_proxigram(
variables,
interface_dic,
proxigram_elements_list,
figname=figname_proxigram.value,
save=proxigram_save.value,
bin_size=proxigram_bin_size.value,
symmetric_range=symmetric_range,
flip_normals=proxigram_flip_normals.value,
range_sequence=range_sequence,
range_mc=range_mc,
range_detx=range_detx,
range_dety=range_dety,
range_x=range_x,
range_y=range_y,
range_z=range_z,
range_vol=range_vol,
smoothing_sigma=proxigram_sigma.value,
min_atoms_per_voxel=proxigram_min_atoms.value,
min_isosurface_vertices=proxigram_min_vertices.value,
pure_only=proxigram_pure_element_only.value,
manual_iso_value=(proxigram_manual_iso_value.value
if proxigram_manual_iso_value.value > 0 else None),
)
except (json.JSONDecodeError, ValueError, SyntaxError) as exc:
print(f'Unable to plot proxigram: {exc}')
plot_proxigram_button.disabled = False
plot_proxigram_button.on_click(lambda b: plot_proxigram_view(b, variables, out))
#############
row_index = widgets.IntText(value=0, description='Index row:')
color_picker = widgets.ColorPicker(description='Select a color:')
change_color.on_click(lambda b: change_color_m(b, variables, out))
show_color.on_click(lambda b: show_color_ions(b, variables, out))
def show_color_ions(b, variables, output):
with output:
clear_output(True)
display(variables.range_data.style.applymap(ion_selection.display_color, subset=['color']))
#############
def change_color_m(b, variables, output):
with output:
selected_color = mcolors.to_hex(color_picker.value)
variables.range_data.at[row_index.value, 'color'] = selected_color
clear_output(True)
display(variables.range_data.style.applymap(ion_selection.display_color, subset=['color']))
clear_button.on_click(lambda b: clear(b, out))
def clear(b, out):
with out:
clear_output(True)
print('')
def _validate_cluster_plot_request(selection_text, context_label):
if getattr(variables, 'range_data', None) is None or variables.range_data.empty:
print(f'{context_label} requires range data. Load a range file first.')
return False
if not clustering.parse_label_selection(selection_text):
print(f'{context_label}: enter one or more cluster ions/elements first.')
return False
return True
def _apply_method_settings_to_3d_iso(selection_text, method_value, n_clusters, n_min, auto_d_max, d_max, kth, percentile):
cluster_labels_3d.value = selection_text
cluster_labels_iso.value = selection_text
cluster_method_3d.value = method_value
cluster_method_iso.value = method_value
cluster_count_3d.value = n_clusters
cluster_count_iso.value = n_clusters
cluster_min_size_3d.value = n_min
cluster_min_size_iso.value = n_min
cluster_auto_dmax_3d.value = auto_d_max
cluster_auto_dmax_iso.value = auto_d_max
cluster_dmax_3d.value = d_max
cluster_dmax_iso.value = d_max
cluster_kth_neighbor_3d.value = kth
cluster_kth_neighbor_iso.value = kth
cluster_percentile_3d.value = percentile
cluster_percentile_iso.value = percentile
_update_all_cluster_control_states()
def _build_clustering_method_panel(method_key, display_name, method_note, *, seed_placeholder=''):
selection_widget = widgets.Text(value='', placeholder='Ni3Al, Al')
n_clusters_widget = widgets.BoundedIntText(value=2, min=2, max=12)
n_min_widget = widgets.BoundedIntText(value=25, min=2, max=1_000_000)
auto_dmax_widget = widgets.Dropdown(options=[('True', True), ('False', False)], value=True)
dmax_widget = widgets.BoundedFloatText(value=1.0, min=0.0001, max=1_000_000.0, step=0.05)
kth_widget = widgets.BoundedIntText(value=3, min=1, max=100)
percentile_widget = widgets.BoundedFloatText(value=50.0, min=1.0, max=99.9, step=1.0)
voxel_size_widget = widgets.FloatText(value=1.0)
seed_widget = widgets.Text(value='', placeholder=seed_placeholder)
note_widget = widgets.HTML(value=f'<i>{method_note}</i>')
calculate_button = widgets.Button(description='Calculate segmentation')
plot_button = widgets.Button(description='Plot')
plot_iso_button = widgets.Button(description='Plot iso')
clear_local_button = widgets.Button(description='Clear plots')
cache = {'result': None}
def _set_disabled(disabled):
calculate_button.disabled = disabled
clear_local_button.disabled = disabled
plot_button.disabled = disabled or cache.get('result') is None
plot_iso_button.disabled = disabled or cache.get('result') is None
def _sync_local_control_state(*_):
_sync_cluster_method_controls(
widgets.fixed(method_key),
n_clusters_widget,
auto_dmax_widget,
dmax_widget,
kth_widget,
percentile_widget,
n_min_widget,
note_widget=note_widget,
)
# widgets.fixed doesn't trigger observers; call directly and also when auto mode changes
auto_dmax_widget.observe(_sync_local_control_state, names='value')
def _calculate(_):
_set_disabled(True)
try:
with out:
out.clear_output()
if not _validate_cluster_plot_request(selection_widget.value, f'{display_name} segmentation'):
cache['result'] = None
return
method_kwargs = {}
if method_key == 'composition-gmm-voxel':
method_kwargs['voxel_size'] = float(voxel_size_widget.value)
elif method_key in ('compspace-agnostic-seeded', 'comp-seeded-support-hdbscan'):
method_kwargs['seed_labels'] = seed_widget.value
cluster_result = _run_cluster_segmentation(
enabled=True,
selection_text=selection_widget.value,
method_value=method_key,
cluster_count_value=n_clusters_widget.value,
d_max_value=dmax_widget.value,
auto_d_max_value=auto_dmax_widget.value,
kth_neighbor_value=kth_widget.value,
percentile_value=percentile_widget.value,
n_min_value=n_min_widget.value,
context_label=display_name,
method_kwargs=method_kwargs,
)
cache['result'] = cluster_result
if cluster_result is None:
print(f'{display_name}: segmentation did not produce clusters.')
else:
print(f'{display_name}: segmented {cluster_result.n_clusters} clusters.')
if method_key == 'composition-gmm-voxel':
print(f'Composition voxel size: {voxel_size_widget.value:.3f}')
if method_key in ('compspace-agnostic-seeded', 'comp-seeded-support-hdbscan') and seed_widget.value.strip():
print(f'Seed labels: {seed_widget.value.strip()}')
finally:
_set_disabled(False)
def _plot_cluster(_):
_set_disabled(True)
try:
result = cache.get('result')
if result is None:
with out:
out.clear_output()
print(f'{display_name}: run Calculate segmentation first.')
return
_apply_method_settings_to_3d_iso(
selection_widget.value,
method_key,
int(n_clusters_widget.value),
int(n_min_widget.value),
bool(auto_dmax_widget.value),
float(dmax_widget.value),
int(kth_widget.value),
float(percentile_widget.value),
)
original_opacity = float(opacity.value)
cluster_precipitate_3d.value = True
opacity.value = 0.3
try:
plot_3d(
None,
variables,
out,
cluster_display_mode='overlay',
cluster_result_override=result,
cluster_opacity_override=1.0,
)
finally:
opacity.value = original_opacity
finally:
_set_disabled(False)
def _plot_iso(_):
_set_disabled(True)
try:
result = cache.get('result')
if result is None:
with out:
out.clear_output()
print(f'{display_name}: run Calculate segmentation first.')
return
_apply_method_settings_to_3d_iso(
selection_widget.value,
method_key,
int(n_clusters_widget.value),
int(n_min_widget.value),
bool(auto_dmax_widget.value),
float(dmax_widget.value),
int(kth_widget.value),
float(percentile_widget.value),
)
cluster_precipitate_iso.value = True
plot_3d_iso(None, variables, out, cluster_display_mode='clusters-only', cluster_result_override=result)
finally:
_set_disabled(False)
def _clear_local(_):
cache['result'] = None
with out:
clear_output(True)
_set_disabled(False)
calculate_button.on_click(_calculate)
plot_button.on_click(_plot_cluster)
plot_iso_button.on_click(_plot_iso)
clear_local_button.on_click(_clear_local)
_sync_local_control_state()
_set_disabled(False)
method_specific_rows = []
if method_key == 'composition-gmm-voxel':
method_specific_rows.append(
widgets.HBox([widgets.Label(value='Voxel size:', layout=label_layout), voxel_size_widget])
)
if method_key in ('compspace-agnostic-seeded', 'comp-seeded-support-hdbscan'):
method_specific_rows.append(
widgets.HBox([widgets.Label(value='Seed labels:', layout=label_layout), seed_widget])
)
return widgets.VBox([
widgets.HBox([widgets.Label(value='Cluster ions/elements:', layout=label_layout), selection_widget]),
widgets.HBox([widgets.Label(value='Number of clusters:', layout=label_layout), n_clusters_widget]),
widgets.HBox([widgets.Label(value='Min atoms per cluster:', layout=label_layout), n_min_widget]),
widgets.HBox([widgets.Label(value='Auto estimate d max:', layout=label_layout), auto_dmax_widget]),
widgets.HBox([widgets.Label(value='D max:', layout=label_layout), dmax_widget]),
widgets.HBox([widgets.Label(value='K-th NN:', layout=label_layout), kth_widget]),
widgets.HBox([widgets.Label(value='NN percentile:', layout=label_layout), percentile_widget]),
*method_specific_rows,
note_widget,
widgets.HBox([calculate_button, plot_button, plot_iso_button, clear_local_button]),
])
tab0 = widgets.HBox([
widgets.VBox([
widgets.HBox([widgets.Label(value="Target:", layout=label_layout), target_mode]),
widgets.HBox([widgets.Label(value="Peak find:", layout=label_layout), peaks_find]),
widgets.HBox([widgets.Label(value="Peak find plot:", layout=label_layout), peak_find_plot]),
widgets.HBox([widgets.Label(value="Plot ranged colors:", layout=label_layout), plot_ranged_colors]),
widgets.HBox([widgets.Label(value="Plot ranged peak:", layout=label_layout), plot_ranged_peak]),
range_overlay_note,
widgets.HBox([widgets.Label(value="Print info:", layout=label_layout), print_info]),
widgets.HBox([widgets.Label(value="Bins size:", layout=label_layout), bin_size_pm]),
widgets.HBox([widgets.Label(value="Limit mc:", layout=label_layout), lim_mc_pm]),
widgets.HBox([widgets.Label(value="Log:", layout=label_layout), log_widget]),
widgets.HBox([widgets.Label(value="Normalize:", layout=label_layout), normalize]),
widgets.HBox([widgets.Label(value="MRP all(1%, 10%, 50%):", layout=label_layout), mrp_all]),
widgets.HBox([widgets.Label(value="MRP Percent:", layout=label_layout), percent]),
widgets.HBox([widgets.Label(value="Legend mode:", layout=label_layout), legend_widget]),
widgets.HBox([widgets.Label(value="Peak prominence:", layout=label_layout), prominence]),
widgets.HBox([widgets.Label(value="Peak distance:", layout=label_layout), distance]),
widgets.HBox([widgets.Label(value="Background:", layout=label_layout), background_mc]),
widgets.HBox([widgets.Label(value="Grid:", layout=label_layout), grid_mc]),
widgets.HBox([widgets.Label(value="MRP range:", layout=label_layout),
widgets.HBox([mrp_left_mc, mrp_right_mc])]),
widgets.HBox([load_mrp_window_mc_button, gaussian_mrp_mc_button]),
widgets.HBox([plot_mc_button, clear_button])
]),
widgets.VBox([
widgets.HBox([widgets.Label(value="Sequence range:", layout=label_layout), range_sequence_mc]),
widgets.HBox([widgets.Label(value="Mc range:", layout=label_layout), range_mc_mc]),
widgets.HBox([widgets.Label(value="Detx range:", layout=label_layout), range_detx_mc]),
widgets.HBox([widgets.Label(value="Dety range:", layout=label_layout), range_dety_mc]),
widgets.HBox([widgets.Label(value="X range:", layout=label_layout), range_x_mc]),
widgets.HBox([widgets.Label(value="Y range:", layout=label_layout), range_y_mc]),
widgets.HBox([widgets.Label(value="Z range:", layout=label_layout), range_z_mc]),
widgets.HBox([widgets.Label(value="Voltage range:", layout=label_layout), range_vol_mc]),
widgets.HBox([widgets.Label(value="Fig name:", layout=label_layout), figname_mc]),
widgets.HBox([widgets.Label(value="Save fig:", layout=label_layout), save_mc]),
widgets.HBox([widgets.Label(value="Fig size:", layout=label_layout),
widgets.HBox([figure_mc_size_x_mc, figure_mc_size_y_mc]),]),
])
])
tab1 = widgets.VBox([
widgets.HBox([widgets.Label(value='Max TOF:', layout=label_layout), max_tof_mc_widget]),
widgets.HBox([widgets.Label(value='Fraction:', layout=label_layout), frac_mc_widget]),
widgets.HBox([widgets.Label(value='Bins:', layout=label_layout), widgets.HBox([bins_x_mc, bins_y_mc])]),
widgets.HBox([widgets.Label(value='Pulse Plot:', layout=label_layout), pulse_plot_mc_widget]),
widgets.HBox([widgets.Label(value='DC Plot:', layout=label_layout), dc_plot_mc_widget]),
widgets.HBox([widgets.Label(value='Pulse Mode:', layout=label_layout), pulse_mode_mc_widget]),
widgets.HBox([widgets.Label(value='Fig name:', layout=label_layout), figname_mc_widget]),
widgets.HBox([widgets.Label(value='Save:', layout=label_layout), save_mc_widget]),
widgets.HBox([widgets.Label(value='Fig size:', layout=label_layout),
widgets.HBox([figure_size_x_mc, figure_size_y_mc])]),
widgets.HBox([plot_experiment_button, clear_button]),
])
tab2 = (widgets.HBox([
widgets.VBox([
widgets.HBox([widgets.Label(value='Fraction:', layout=label_layout), frac_fdm_widget]),
widgets.HBox([widgets.Label(value='Bins:', layout=label_layout), bins_fdm]),
widgets.HBox([widgets.Label(value='Axis mode:', layout=label_layout), axis_mode_fdm]),
widgets.HBox([widgets.Label(value='Fig name:', layout=label_layout), figname_fdm_widget]),
widgets.HBox([widgets.Label(value='Save:', layout=label_layout), save_fdm_widget]),
widgets.HBox([widgets.Label(value='Fig size:', layout=label_layout),
widgets.HBox([figure_size_x_fdm, figure_size_y_fdm])]),
widgets.HBox([plot_fdm_button, clear_button]),
]),
widgets.VBox([
widgets.HBox([widgets.Label(value="Sequence range:", layout=label_layout), range_sequence_fdm]),
widgets.HBox([widgets.Label(value='Mc range:', layout=label_layout), range_mc_fdm]),
widgets.HBox([widgets.Label(value='Detx range:', layout=label_layout), range_detx_fdm]),
widgets.HBox([widgets.Label(value='Dety range:', layout=label_layout), range_dety_fdm]),
widgets.HBox([widgets.Label(value='X range:', layout=label_layout), range_x_fdm]),
widgets.HBox([widgets.Label(value='Y range:', layout=label_layout), range_y_fdm]),
widgets.HBox([widgets.Label(value='Z range:', layout=label_layout), range_z_fdm]),
widgets.HBox([widgets.Label(value="Voltage range:", layout=label_layout), range_vol_fdm]),
])
]))
tab3 = (widgets.HBox([
widgets.VBox([
widgets.HBox([widgets.Label(value='Element percentage:', layout=label_layout), element_percentage_p3]),
widgets.HBox([widgets.Label(value='Element alphas:', layout=label_layout), element_alpha_p3]),
widgets.HBox([widgets.Label(value='Opacity:', layout=label_layout), opacity]),
widgets.HBox(
[widgets.Label(value='Ions individually plots:', layout=label_layout), ions_individually_plots]),
widgets.HBox([widgets.Label(value='Fig name:', layout=label_layout), figname_3d]),
widgets.HBox([widgets.Label(value='Rotary save:', layout=label_layout), rotary_fig_save_p3]),
widgets.HBox([widgets.Label(value='Save GIF:', layout=label_layout), make_gif_p3]),
widgets.HBox([widgets.Label(value='Save evaporation GIF:', layout=label_layout), make_evap_3d]),
widgets.HBox([widgets.Label(value='Save fig:', layout=label_layout), save_3d]),
widgets.HBox([plot_3d_button, clear_button]),
]),
widgets.VBox([
widgets.HBox([widgets.Label(value="Sequence range:", layout=label_layout), range_sequence_3d]),
widgets.HBox([widgets.Label(value='Range mc:', layout=label_layout), range_mc_3d]),
widgets.HBox([widgets.Label(value='Range detx:', layout=label_layout), range_detx_3d]),
widgets.HBox([widgets.Label(value='Range dety:', layout=label_layout), range_dety_3d]),
widgets.HBox([widgets.Label(value='Range x:', layout=label_layout), range_x_3d]),
widgets.HBox([widgets.Label(value='Range y:', layout=label_layout), range_y_3d]),
widgets.HBox([widgets.Label(value='Range z:', layout=label_layout), range_z_3d]),
widgets.HBox([widgets.Label(value="Voltage range:", layout=label_layout), range_vol_3d]),
])
]))
tab4 = (widgets.HBox([
widgets.VBox([
widgets.HBox([widgets.Label(value='Element percentage:', layout=label_layout), element_percentage_ph]),
widgets.HBox([widgets.Label(value='Fig name:', layout=label_layout), figname_heatmap]),
widgets.HBox([widgets.Label(value='Save fig:', layout=label_layout), save_heatmap]),
widgets.HBox([widgets.Label(value='Fig size:', layout=label_layout),
widgets.HBox([figure_mc_size_x_heatmap, figure_mc_size_y_heatmap])]),
widgets.HBox([plot_heatmap_button, clear_button]),
]),
widgets.VBox([
widgets.HBox([widgets.Label(value="Sequence range:", layout=label_layout), range_sequence_heatmap]),
widgets.HBox([widgets.Label(value='Range mc:', layout=label_layout), range_mc_heatmap]),
widgets.HBox([widgets.Label(value='Range detx:', layout=label_layout), range_detx_heatmap]),
widgets.HBox([widgets.Label(value='Range dety:', layout=label_layout), range_dety_heatmap]),
widgets.HBox([widgets.Label(value='Range x:', layout=label_layout), range_x_heatmap]),
widgets.HBox([widgets.Label(value='Range y:', layout=label_layout), range_y_heatmap]),
widgets.HBox([widgets.Label(value='Range z:', layout=label_layout), range_z_heatmap]),
widgets.HBox([widgets.Label(value="Voltage range:", layout=label_layout), range_vol_heatmap]),
])
]))
tab5 = widgets.VBox([
widgets.HBox([widgets.Label(value='Points per frame:', layout=label_layout), points_per_frame_anim]),
widgets.HBox([widgets.Label(value='Ranged:', layout=label_layout), ranged_anim]),
widgets.HBox([widgets.Label(value='Fig name:', layout=label_layout), figname_anim]),
widgets.HBox([widgets.Label(value='Fig size:', layout=label_layout),
widgets.HBox([figure_size_x_anim, figure_size_y_anim])]),
widgets.HBox([plot_animated_heatmap_button, clear_button]),
])
tab6 = (widgets.HBox([
widgets.VBox([
widgets.HBox([widgets.Label(value='Element percentage:', layout=label_layout), element_percentage_pp]),
widgets.HBox([widgets.Label(value='X or Y:', layout=label_layout), x_or_y_pp]),
widgets.HBox([widgets.Label(value='Fig name:', layout=label_layout), figname_p]),
widgets.HBox([widgets.Label(value='Save fig:', layout=label_layout), save_projection]),
widgets.HBox([widgets.Label(value='Fig size:', layout=label_layout),
widgets.HBox([figure_mc_size_x_projection, figure_mc_size_y_projection])]),
widgets.HBox([plot_projection_button, clear_button])
]),
widgets.VBox([
widgets.HBox([widgets.Label(value="Sequence range:", layout=label_layout), range_sequence_pp]),
widgets.HBox([widgets.Label(value='Range mc:', layout=label_layout), range_mc_pp]),
widgets.HBox([widgets.Label(value='Range detx:', layout=label_layout), range_detx_pp]),
widgets.HBox([widgets.Label(value='Range dety:', layout=label_layout), range_dety_pp]),
widgets.HBox([widgets.Label(value='Range x:', layout=label_layout), range_x_pp]),
widgets.HBox([widgets.Label(value='Range y:', layout=label_layout), range_y_pp]),
widgets.HBox([widgets.Label(value='Range z:', layout=label_layout), range_z_pp]),
widgets.HBox([widgets.Label(value="Voltage range:", layout=label_layout), range_vol_pp]),
])
]))
tab7 = (widgets.HBox([
widgets.VBox([
widgets.HBox([widgets.Label(value='First axis:', layout=label_layout), first_axis_d]),
widgets.HBox([widgets.Label(value='Second axis:', layout=label_layout), second_axis_d]),
widgets.HBox([widgets.Label(value='Composition:', layout=label_layout), composition_d]),
widgets.HBox([widgets.Label(value='Z weight:', layout=label_layout), z_weight_2d_hist]),
widgets.HBox([widgets.Label(value='Bin size (nm):', layout=label_layout), bins2d_hist]),
widgets.HBox([widgets.Label(value='Log:', layout=label_layout), log_d]),
widgets.HBox([widgets.Label(value='Axis mode:', layout=label_layout), axis_mode_d]),
widgets.HBox([widgets.Label(value='Cmap:', layout=label_layout), cmap_d]),
widgets.HBox([widgets.Label(value='Normalize:', layout=label_layout), normalize_d]),
widgets.HBox([widgets.Label(value='Normalize axes:', layout=label_layout), normalize_axes_d]),
widgets.HBox([widgets.Label(value='Fig name:', layout=label_layout), figname_2d_hist]),
widgets.HBox([widgets.Label(value='Save:', layout=label_layout), save_2d_hist]),
widgets.HBox([widgets.Label(value='Fig size:', layout=label_layout),
widgets.HBox([figure_size_x_2d_hist, figure_size_y_2d_hist])]),
widgets.HBox([density_map_button, clear_button]),
]),
widgets.VBox([
widgets.HBox([widgets.Label(value='Percentage:', layout=label_layout), percentage_2d_hist]),
widgets.HBox([widgets.Label(value="Sequence range:", layout=label_layout), range_sequence_2d_hist]),
widgets.HBox([widgets.Label(value='Mc range:', layout=label_layout), range_mc_2d_hist]),
widgets.HBox([widgets.Label(value='Detx range:', layout=label_layout), range_detx_2d_hist]),
widgets.HBox([widgets.Label(value='Dety range:', layout=label_layout), range_dety_2d_hist]),
widgets.HBox([widgets.Label(value='X range:', layout=label_layout), range_x_2d_hist]),
widgets.HBox([widgets.Label(value='Y range:', layout=label_layout), range_y_2d_hist]),
widgets.HBox([widgets.Label(value='Z range:', layout=label_layout), range_z_2d_hist]),
widgets.HBox([widgets.Label(value="Voltage range:", layout=label_layout), range_vol_2d_hist]),
])
]))
tab8 = (widgets.HBox([
widgets.VBox([
widgets.HBox([widgets.Label(value='First axis:', layout=label_layout), first_axis_sdm]),
widgets.HBox([widgets.Label(value='Second axis:', layout=label_layout), second_axis_sdm]),
widgets.HBox([widgets.Label(value='Mode:', layout=label_layout), sdm_mode]),
widgets.HBox([widgets.Label(value='ROI:', layout=label_layout), roi_sdm]),
widgets.HBox([widgets.Label(value='Z cut 1 nm:', layout=label_layout), z_cut_sdm]),
widgets.HBox([widgets.Label(value='Theta x:', layout=label_layout), theta_x_sdm]),
widgets.HBox([widgets.Label(value='Phi y:', layout=label_layout), phi_y_sdm]),
widgets.HBox([widgets.Label(value='Bins size (nm):', layout=label_layout), bins_size_sdm]),
widgets.HBox([widgets.Label(value='Plot ROI:', layout=label_layout), plot_roi_sdm]),
widgets.HBox([widgets.Label(value='Normalized:', layout=label_layout), normalized_sdm]),
widgets.HBox([widgets.Label(value='Log:', layout=label_layout), log_sdm]),
widgets.HBox([widgets.Label(value='Plot mode:', layout=label_layout), plot_mode_sdm]),
widgets.HBox([widgets.Label(value='Fig name:', layout=label_layout), figname_2d_sdm]),
widgets.HBox([widgets.Label(value='Save:', layout=label_layout), save_sdm]),
widgets.HBox([widgets.Label(value='Fig size:', layout=label_layout),
widgets.HBox([figure_size_x_2d_sdm, figure_size_y_2d_sdm])]),
widgets.HBox([plot_sdm_button, clear_button]),
]),
widgets.VBox([
widgets.Label(value="Ions I for SDM:"),
*i_composition_sdm,
]),
widgets.VBox([
widgets.Label(value="Ions J for SDM:"),
*j_composition_sdm,
]),
widgets.VBox([
widgets.HBox([widgets.Label(value='Percentage:', layout=label_layout), percentage_sdm]),
widgets.HBox([widgets.Label(value='Range sequence:', layout=label_layout), range_sequence_sdm]),
widgets.HBox([widgets.Label(value='Range mc:', layout=label_layout), range_mc_sdm]),
widgets.HBox([widgets.Label(value='Range detx:', layout=label_layout), range_detx_sdm]),
widgets.HBox([widgets.Label(value='Range dety:', layout=label_layout), range_dety_sdm]),
widgets.HBox([widgets.Label(value='Range x:', layout=label_layout), range_x_sdm]),
widgets.HBox([widgets.Label(value='Range y:', layout=label_layout), range_y_sdm]),
widgets.HBox([widgets.Label(value='Range z:', layout=label_layout), range_z_sdm]),
widgets.HBox([widgets.Label(value='Range vol:', layout=label_layout), range_vol_sdm]),
])
]))
tab9 = (widgets.HBox([
widgets.VBox([
widgets.HBox([widgets.Label(value='Dr:', layout=label_layout), dr_rdf]),
widgets.HBox([widgets.Label(value='Rdf cutoff:', layout=label_layout), rdf_cutoff]),
widgets.HBox([widgets.Label(value='Normalized:', layout=label_layout), normalized_rdf]),
widgets.HBox([widgets.Label(value='Reference point:', layout=label_layout), reference_point_rdf]),
widgets.HBox([widgets.Label(value='Fig name:', layout=label_layout), figname_2d_rdf]),
widgets.HBox([widgets.Label(value='Save:', layout=label_layout), save_rdf]),
widgets.HBox([widgets.Label(value='Fig size:', layout=label_layout),
widgets.HBox([figure_size_x_2d_rdf, figure_size_y_2d_rdf])]),
widgets.HBox([plot_rdf_button, clear_button]),
]),
widgets.VBox([
widgets.HBox([widgets.Label(value='Range sequence:', layout=label_layout), range_sequence_rdf]),
])
]))
tab10 = (widgets.HBox([
widgets.VBox([
widgets.HTML(
value="<b>Iso surface dictionary</b>: one or more entries are allowed, for example "
"<code>{Al: [3,3,3], Ni: [4,4,4]}</code>."
),
widgets.HBox([widgets.Label(value='Element percentage:', layout=label_layout), element_percentage_p3_iso]),
widgets.HBox([widgets.Label(value='Opacity:', layout=label_layout), opacity_iso]),
widgets.HBox([widgets.Label(value='Ions individually plots:', layout=label_layout), ions_individually_plots_iso]),
widgets.HBox([widgets.Label(value='Iso dict (multi):', layout=label_layout), isosurface_dic_p3_iso]),
widgets.HBox([widgets.Label(value='Detailed isotope charge:', layout=label_layout),
detailed_isotope_charge_3d_iso]),
widgets.HBox([widgets.Label(value='Only iso:', layout=label_layout), only_iso_3d_iso]),
widgets.HBox([widgets.Label(value='Pure elements only:', layout=label_layout), pure_element_only_iso]),
widgets.HBox([widgets.Label(value='Smoothing sigma:', layout=label_layout), smoothing_sigma_iso]),
widgets.HBox([widgets.Label(value='Iso value override (0=auto):', layout=label_layout), manual_iso_value_iso]),
widgets.HBox([widgets.Label(value='Min atoms / voxel:', layout=label_layout), min_atoms_per_voxel_iso]),
widgets.HBox([widgets.Label(value='Min iso vertices:', layout=label_layout), min_vertices_iso]),
widgets.HBox([widgets.Label(value='Make GIF:', layout=label_layout), make_gif_p3_iso]),
widgets.HBox([widgets.Label(value='Fig name:', layout=label_layout), figname_3d_iso]),
widgets.HBox([widgets.Label(value='Save:', layout=label_layout), save_3d_iso]),
widgets.HBox([widgets.Label(value='Rotary save:', layout=label_layout), rotary_fig_save_p3_iso]),
widgets.HBox([plot_3d_button_iso, clear_button]),
]),
widgets.VBox([
widgets.HBox([widgets.Label(value="Sequence range:", layout=label_layout), range_sequence_3d_iso]),
widgets.HBox([widgets.Label(value='Range mc:', layout=label_layout), range_mc_3d_iso]),
widgets.HBox([widgets.Label(value='Range detx:', layout=label_layout), range_detx_3d_iso]),
widgets.HBox([widgets.Label(value='Range dety:', layout=label_layout), range_dety_3d_iso]),
widgets.HBox([widgets.Label(value='Range x:', layout=label_layout), range_x_3d_iso]),
widgets.HBox([widgets.Label(value='Range y:', layout=label_layout), range_y_3d_iso]),
widgets.HBox([widgets.Label(value='Range z:', layout=label_layout), range_z_3d_iso]),
widgets.HBox([widgets.Label(value="Voltage range:", layout=label_layout), range_vol_3d_iso]),
])
]))
tab11 = (widgets.HBox([
widgets.VBox([
widgets.HTML(value="<b>Interface isosurface</b>: use one element entry, for example <code>{Al: [3,3,3]}</code>."),
widgets.HBox([widgets.Label(value='Interface iso dict:', layout=label_layout), proxigram_isosurface_dic]),
widgets.HTML(
value="<b>Proxigram elements</b>: multiple elements are allowed. "
"Examples: <code>Al, Ni, Ti</code> or <code>[\"Al\", \"Ni\", \"Ti\"]</code>."
),
widgets.HBox([widgets.Label(value='Proxigram elements:', layout=label_layout), proxigram_elements]),
widgets.HBox([widgets.Label(value='Bin size (nm):', layout=label_layout), proxigram_bin_size]),
widgets.HBox([widgets.Label(value='Symmetric range (nm):', layout=label_layout), proxigram_symmetric_range]),
widgets.HBox([widgets.Label(value='Flip normals:', layout=label_layout), proxigram_flip_normals]),
widgets.HBox([widgets.Label(value='Pure elements only:', layout=label_layout), proxigram_pure_element_only]),
widgets.HBox([widgets.Label(value='Smoothing sigma:', layout=label_layout), proxigram_sigma]),
widgets.HBox([widgets.Label(value='Iso value override (0=auto):', layout=label_layout), proxigram_manual_iso_value]),
widgets.HBox([widgets.Label(value='Min atoms / voxel:', layout=label_layout), proxigram_min_atoms]),
widgets.HBox([widgets.Label(value='Min iso vertices:', layout=label_layout), proxigram_min_vertices]),
widgets.HBox([widgets.Label(value='Fig name:', layout=label_layout), figname_proxigram]),
widgets.HBox([widgets.Label(value='Save:', layout=label_layout), proxigram_save]),
widgets.HBox([plot_proxigram_button, clear_button]),
]),
widgets.VBox([
widgets.HBox([widgets.Label(value="Sequence range:", layout=label_layout), range_sequence_prox]),
widgets.HBox([widgets.Label(value='Range mc:', layout=label_layout), range_mc_prox]),
widgets.HBox([widgets.Label(value='Range detx:', layout=label_layout), range_detx_prox]),
widgets.HBox([widgets.Label(value='Range dety:', layout=label_layout), range_dety_prox]),
widgets.HBox([widgets.Label(value='Range x:', layout=label_layout), range_x_prox]),
widgets.HBox([widgets.Label(value='Range y:', layout=label_layout), range_y_prox]),
widgets.HBox([widgets.Label(value='Range z:', layout=label_layout), range_z_prox]),
widgets.HBox([widgets.Label(value="Voltage range:", layout=label_layout), range_vol_prox]),
])
]))
cluster_method_panels = [
_build_clustering_method_panel(
'maximum-separation',
'MMax separation',
'Uses connected-component clustering with d max and min atoms controls.',
),
_build_clustering_method_panel(
'hdbscan',
'HDBSCAN',
'Density-based mode. Uses the same d max / neighbor controls in this workflow.',
),
_build_clustering_method_panel(
'comp-seeded-support-hdbscan',
'Comp-Seeded Support HDBSCAN',
'Density-based mode with optional seed labels to guide clustering support.',
seed_placeholder='Al, Ni',
),
_build_clustering_method_panel(
'composition-gmm-voxel',
'Composition GMM Voxel',
'Uses fixed cluster count with composition-voxel controls.',
),
_build_clustering_method_panel(
'compspace-agnostic-seeded',
'CompSpace Agnostic + Seeded',
'Uses fixed cluster count and optional seed labels.',
seed_placeholder='Al, Ni',
),
]
cluster_subtabs = widgets.Tab(children=cluster_method_panels)
cluster_subtabs.set_title(0, 'MMax separation')
cluster_subtabs.set_title(1, 'HDBSCAN')
cluster_subtabs.set_title(2, 'Comp-Seeded Support HDBSCAN')
cluster_subtabs.set_title(3, 'Composition GMM Voxel')
cluster_subtabs.set_title(4, 'CompSpace Agnostic + Seeded')
tab12 = widgets.VBox([
widgets.HTML(value='<b>Clustering methods</b>: calculate segmentation first, then plot cluster or iso.'),
cluster_subtabs,
])
tab13 = build_peak_spectral_analysis_panel(variables, label_layout=label_layout)
tab14 = widgets.VBox([
widgets.HBox([widgets.Label(value='Index row:', layout=label_layout), row_index]),
widgets.HBox([widgets.Label(value='Color:', layout=label_layout), color_picker]),
widgets.VBox([show_color, change_color, clear_button]),
])
if not colab:
tab = widgets.Tab([tab0, tab1, tab2, tab3, tab4, tab5, tab6, tab7, tab8, tab9, tab10, tab11,
tab12, tab13, tab14])
tab.set_title(0, 'mc')
tab.set_title(1, 'Experiment history')
tab.set_title(2, 'FDM')
tab.set_title(3, '3D')
tab.set_title(4, 'Hitmap')
tab.set_title(5, 'Animated hitmap')
tab.set_title(6, 'Projection')
tab.set_title(7, 'Disparity map')
tab.set_title(8, 'SDM')
tab.set_title(9, 'RDF')
tab.set_title(10, 'Iso surface')
tab.set_title(11, 'Proxigram')
tab.set_title(12, 'Clustering')
tab.set_title(13, 'Peak analysis')
tab.set_title(14, 'Change Color')
out = Output()
display(widgets.VBox([tab, out]))
else:
# Define the content for each tab
content = [tab0, tab1, tab2, tab3, tab4, tab5, tab6, tab7, tab8, tab9, tab10, tab11,
tab12, tab13, tab14]
# Create buttons for each tab
buttons = [widgets.Button(description=title) for title in [
'mc', 'Experiment history', 'FDM', '3D', 'Hitmap', 'Animated hitmap',
'Projection', 'Disparity map', 'SDM', 'RDF', 'Iso surface', 'Proxigram', 'Clustering', 'Peak analysis', 'Change Color'
]]
# Output widget to display the content
out = widgets.Output()
def on_button_click(index):
def handler(change):
with out:
clear_output(wait=True)
display(content[index])
return handler
# Attach click handlers to each button
for i, button in enumerate(buttons):
button.on_click(on_button_click(i))
# Display buttons and the output widget
display(widgets.HBox(buttons))
display(out)