Source code for AFL.automation.prepare.SweepBuilderWidget

import numpy as np
import pandas as pd
from math import sqrt

import plotly.graph_objects as go
import plotly.express as px

import ipywidgets
from ipywidgets import Layout,Label,Button,Checkbox,VBox,HBox,Text
import pickle

import AFL.automation.prepare 
from AFL.automation.shared.units import units

[docs] class SweepBuilderWidget:
[docs] def __init__(self,deck): self.data_model = SweepBuilderWidget_Model(deck) self.data_view = SweepBuilderWidget_View()
[docs] def plot_binary_cb(self,click): if self.data_model.sample_series is None: return component_A = self.data_view.binary_component_A_select.value component_B = self.data_view.binary_component_B_select.value x = [] y = [] x_validated = [] y_validated = [] for sample,validated in self.data_model.sample_series: mass_A = sample.target_check[component_A].mass.to('mg') mass_B = sample.target_check[component_B].mass.to('mg') if (abs(mass_A.magnitude)>1e4) or (abs(mass_B.magnitude)>1e4): #need to weed out erroneous calculations... continue x.append(mass_A.magnitude) y.append(mass_B.magnitude) if validated: x_validated.append(mass_A.magnitude) y_validated.append(mass_B.magnitude) self.data_view.binary.data[0].update(x=x,y=y) self.data_view.binary.layout.update({ 'xaxis.title':component_A + f' ({mass_A.units})', 'yaxis.title':component_B + f' ({mass_B.units})' }) if x_validated: self.data_view.binary.data[1].update(x=x_validated,y=y_validated)
[docs] def plot_ternary_cb(self,click): if not self.data_model.sweep: return component_A = self.data_view.ternary_component_A_select.value component_B = self.data_view.ternary_component_B_select.value component_C = self.data_view.ternary_component_C_select.value a = [] b = [] c = [] a_validated = [] b_validated = [] c_validated = [] for sample,validated in self.data_model.sample_series: mass_A = sample.target_check[component_A].mass.to('mg') mass_B = sample.target_check[component_B].mass.to('mg') mass_C = sample.target_check[component_C].mass.to('mg') if (abs(mass_A.magnitude)>1e4) or (abs(mass_B.magnitude)>1e4) or (abs(mass_C.magnitude)>1e4): #need to weed out erroneous calculations... continue a.append(mass_A.magnitude) b.append(mass_B.magnitude) c.append(mass_C.magnitude) if validated: a_validated.append(mass_A.magnitude) b_validated.append(mass_B.magnitude) c_validated.append(mass_C.magnitude) self.data_view.ternary.data[0].update(a=a,b=b,c=c) self.data_view.ternary.layout.update({ 'ternary.aaxis.title':component_A, 'ternary.baxis.title':component_B, 'ternary.caxis.title':component_C }) if a_validated: self.data_view.ternary.data[1].update(a=a_validated,b=b_validated,c=c_validated)
[docs] def calc_sweep_cb(self,click): sweep_data = self.get_sweep_data() self.data_view.sweep_progress_label.value = 'Calculating sweep compositions...' sweep = self.data_model.calc_sweep(sweep_data,self.data_view.sweep_progress) ntotal = len(self.data_model.sample_series.samples) self.data_view.sweep_progress_label.value = f'Done! Made {ntotal} samples.'
[docs] def validate_sweep_cb(self,click): self.data_view.sweep_progress_label.value = 'Validating sweep compositions...' sweep = self.data_model.validate_sweep( self.data_view.validate_tol.value, self.data_view.sweep_progress ) ntotal = len(self.data_model.sample_series.samples) nvalidated = sum(self.data_model.sample_series.validated) self.data_view.sweep_progress_label.value = f'Done! Validated {nvalidated}/{ntotal} samples.'
[docs] def get_sweep_data(self): sweep = {} for component_name,widgets in self.data_view.sweep_spec.items(): sweep[component_name] = {} if component_name == 'total': sweep[component_name]['amount'] = widgets['amount'].value sweep[component_name]['units'] = widgets['units'].value else: for name,item in widgets.items(): sweep[component_name][name] = item.value return sweep
[docs] def get_deck(self): return self.data_model.deck
[docs] def update_component_row_cb(self,event): component_name = event['owner'].component_name if event['new']==True: self.data_view.sweep_spec[component_name]['amount'].layout.visibility='hidden' self.data_view.sweep_spec[component_name]['steps'].layout.visibility='visible' self.data_view.sweep_spec[component_name]['lower'].layout.visibility='visible' self.data_view.sweep_spec[component_name]['upper'].layout.visibility='visible' elif event['new']==False: self.data_view.sweep_spec[component_name]['amount'].layout.visibility='visible' self.data_view.sweep_spec[component_name]['steps'].layout.visibility='hidden' self.data_view.sweep_spec[component_name]['lower'].layout.visibility='hidden' self.data_view.sweep_spec[component_name]['upper'].layout.visibility='hidden'
[docs] def start(self): widget = self.data_view.start(self.data_model.component_names) self.data_view.sweep_button.on_click(self.calc_sweep_cb) self.data_view.validate_button.on_click(self.validate_sweep_cb) for component_name,items in self.data_view.sweep_spec.items(): if 'vary' in items: #this is gross but my normal lambda wrapping doesn't work because Python is Python items['vary'].component_name = component_name items['vary'].observe(self.update_component_row_cb,names=['value']) self.data_view.binary_plot_button.on_click(self.plot_binary_cb) self.data_view.ternary_plot_button.on_click(self.plot_ternary_cb) return widget
[docs] class SweepBuilderWidget_Model:
[docs] def __init__(self,deck): self.sweep = [] self.sample_series = None self.deck = deck self.component_names,_,_ = deck.get_components()
#self.component_names = set() # for stock in deck.stocks: # for component_name in stock.components.keys(): # self.component_names.add(component_name)
[docs] def validate_sweep(self,tolerance,progress=None): self.deck.validate_sample_series(tolerance,progress=progress) return self.sample_series
[docs] def calc_sweep(self,sweep_dict,progress): components = [] vary = [] lo = [] hi = [] num = [] unit_list = [] properties = None for component_name,items in sweep_dict.items(): if (component_name == 'total') and (items['amount']>0): properties = {'volume':items['amount']*units(items['units'])} else: if ('vary' in items) and (items['vary']==True): components.append(component_name) unit = items['units'] if '%' in unit: unit = units('') else: unit = units(unit) vary.append(component_name) lo.append(items['lower']*unit) hi.append(items['upper']*unit) num.append(items['steps']) elif ('vary' in items) and (items['vary']==False): components.append(component_name) dasfasdf self.sweep = AFL.automation.prepare.compositionSweepFactory( name='SweepBuilder', components = components, vary_components = vary, lo=lo, hi=hi, num=num, progress=progress, properties=properties, ) self.deck.reset_targets() for target in self.sweep: self.deck.add_target(target,name='auto') self.sample_series = self.deck.make_sample_series(reset_sample_series=True) return self.sweep
[docs] class SweepBuilderWidget_View:
[docs] def __init__(self): self.sweep_spec = None
[docs] def make_stock_grid(self,component_names): self.stock_grid_nrows = len(component_names) self.stock_grid_ncols = 7 text_width='100px' layout = ipywidgets.Layout( #grid_template_columns='10px '+(text_width+' ')*(self.stock_grid_ncols-1), #grid_template_rows='20px'*self.stock_grid_nrows, grid_gap='0px', max_width='700px', ) stock_grid = ipywidgets.GridspecLayout( n_rows=self.stock_grid_nrows+2, n_columns=self.stock_grid_ncols, layout=layout, ) stock_grid[0,0] = ipywidgets.Label(value='Vary',layout=Layout(width='35px')) stock_grid[0,1] = ipywidgets.Label(value='Component',layout=Layout(width=text_width)) stock_grid[0,2] = ipywidgets.Label(value='Amount',layout=Layout(width=text_width)) stock_grid[0,3] = ipywidgets.Label(value='Steps',layout=Layout(width=text_width)) stock_grid[0,4] = ipywidgets.Label(value='Lower',layout=Layout(width=text_width)) stock_grid[0,5] = ipywidgets.Label(value='Upper',layout=Layout(width=text_width)) stock_grid[0,6] = ipywidgets.Label(value='Units',layout=Layout(width=text_width)) i = 1 self.sweep_spec = {} for component_name in component_names: stock_grid[i,0] = ipywidgets.Checkbox(layout=Layout(width='35px'),indent=False) stock_grid[i,1] = ipywidgets.Text(value=component_name,disabled=True,layout=Layout(width=text_width)) stock_grid[i,2] = ipywidgets.FloatText(value=0.0,layout=Layout(width=text_width)) stock_grid[i,3] = ipywidgets.IntText(value=5,layout=Layout(width=text_width,visibility='hidden')) stock_grid[i,4] = ipywidgets.FloatText(value=50.0,layout=Layout(width=text_width,visibility='hidden')) stock_grid[i,5] = ipywidgets.FloatText(value=200.0,layout=Layout(width=text_width,visibility='hidden')) stock_grid[i,6] = ipywidgets.Text(value='mg/ml',layout=Layout(width=text_width)) self.sweep_spec[component_name] = {} self.sweep_spec[component_name]['vary'] = stock_grid[i,0] self.sweep_spec[component_name]['amount'] = stock_grid[i,2] self.sweep_spec[component_name]['steps'] = stock_grid[i,3] self.sweep_spec[component_name]['lower'] = stock_grid[i,4] self.sweep_spec[component_name]['upper'] = stock_grid[i,5] self.sweep_spec[component_name]['units'] = stock_grid[i,6] i+=1 stock_grid[i,1] = ipywidgets.Text(value='Total',disabled=True,layout=Layout(width=text_width)) stock_grid[i,2] = ipywidgets.FloatText(value=300.0,layout=Layout(width=text_width)) stock_grid[i,6] = ipywidgets.Text(value='ul',layout=Layout(width=text_width)) self.sweep_spec['total'] = {} self.sweep_spec['total']['amount'] = stock_grid[i,2] self.sweep_spec['total']['units'] = stock_grid[i,6] i+=1 self.sweep_button = ipywidgets.Button(description="Calculate Sweep") #self.validate_sweep = ipywidgets.Checkbox(description="Validate Sweep",indent=False) self.validate_button = ipywidgets.Button(description="Validate Sweep") self.validate_tol = ipywidgets.FloatText(value=0.15,description="Tolerance") self.sweep_progress = ipywidgets.IntProgress(min=0,max=100,value=100) self.sweep_progress_label = ipywidgets.Label('') progress_hbox = HBox([self.sweep_progress,self.sweep_progress_label]) button_hbox = HBox([self.sweep_button,self.validate_button,self.validate_tol]) vbox = VBox([stock_grid,button_hbox,progress_hbox]) return vbox
[docs] def make_ternary_plot(self,component_names): self.ternary = go.FigureWidget([ go.Scatterternary( a = [], b = [], c = [], mode = 'markers', marker={'color':'red'}, opacity=1.0, showlegend=False, ) , go.Scatterternary( a = [], b = [], c = [], mode = 'markers', marker={'color':'blue'}, opacity=1.0, showlegend=False, ) , ], layout=dict(width=600,margin=dict(t=20,b=20,l=10,r=10)), ) starting_components = [] for i in range(3): try: starting_components.append(component_names[i]) except IndexError: starting_components.append('') label_A = Label('Component A') self.ternary_component_A_select = ipywidgets.Dropdown( options=component_names, value=starting_components[0], layout=Layout(width='100px'), ) label_B = Label('Component B') self.ternary_component_B_select = ipywidgets.Dropdown( options=component_names, value=starting_components[1], layout=Layout(width='100px'), ) label_C = Label('Component C') self.ternary_component_C_select = ipywidgets.Dropdown( options=component_names, value=starting_components[-1], layout=Layout(width='100px'), ) self.ternary_plot_button = Button(description='Update Plot') hbox = HBox([ VBox([label_A,self.ternary_component_A_select]), VBox([label_B,self.ternary_component_B_select]), VBox([label_C,self.ternary_component_C_select]), ]) return VBox([hbox, self.ternary_plot_button ,self.ternary])
[docs] def make_binary_plot(self,component_names): self.binary = go.FigureWidget([ go.Scatter( x = [], y = [], mode = 'markers', marker={'color':'red'}, opacity=1.0, showlegend=False, ), go.Scatter( x = [], y = [], mode = 'markers', marker={'color':'blue'}, opacity=1.0, showlegend=False, ) ], layout=dict(width=600,margin=dict(t=5,b=5,l=5,r=5)), ) label_A = Label('Component A') starting_components = [] for i in range(2): try: starting_components.append(component_names[i]) except IndexError: starting_components.append('') self.binary_component_A_select = ipywidgets.Dropdown( options=component_names, value=starting_components[0], layout=Layout(width='100px'), ) label_B = Label('Component B') self.binary_component_B_select = ipywidgets.Dropdown( options=component_names, value=starting_components[1], layout=Layout(width='100px'), ) self.binary_plot_button = Button(description='Update Plot') hbox = HBox([ VBox([label_A,self.binary_component_A_select]), VBox([label_B,self.binary_component_B_select]), ]) return VBox([hbox,self.binary_plot_button,self.binary])
[docs] def start(self,component_names): component_names =list(component_names) stock_grid = self.make_stock_grid(component_names) ternary = self.make_ternary_plot(component_names) binary = self.make_binary_plot(component_names) self.tabs = ipywidgets.Tab() self.tabs.children = [stock_grid,binary,ternary] self.tabs.set_title(0,'Calculate') self.tabs.set_title(1,'Plot Binary') self.tabs.set_title(2,'Plot Ternary') return self.tabs