Skip to content

Instantly share code, notes, and snippets.

@dpgoldenberg
Last active March 13, 2021 00:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dpgoldenberg/22e38bd9d0f31c3140ec4546bc5ebbcc to your computer and use it in GitHub Desktop.
Save dpgoldenberg/22e38bd9d0f31c3140ec4546bc5ebbcc to your computer and use it in GitHub Desktop.
Simulation of Biol 3515/Chem 3515 Experiment 3, part C
code_dict = {'0GXY02': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.0780933743000794e-05,
'bpti_conc': 3.385861355932284e-05,
'kcat': 12.479059780623182,
'km': 8.563436439963315e-05},
'DD0371': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.943933934989462e-05,
'bpti_conc': 3.517617542672528e-05,
'kcat': 9.174417463278722,
'km': 4.777372487256253e-05},
'X5FX87': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.95266826439055e-05,
'bpti_conc': 3.138461537261679e-05,
'kcat': 11.789268600290177,
'km': 5.58468122174051e-05},
'FG188G': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.285031188273903e-05,
'bpti_conc': 3.1851612073108055e-05,
'kcat': 13.047612493354922,
'km': 7.177787853319616e-05},
'34P10Q': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.722682984532362e-05,
'bpti_conc': 3.332900420043714e-05,
'kcat': 8.145189509187809,
'km': 6.57837365279514e-05},
'Y4N6KZ': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.8906484201970237e-05,
'bpti_conc': 3.2455623824540886e-05,
'kcat': 13.614464085817126,
'km': 5.4379228053721865e-05},
'W5XM03': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 1.9115719982294164e-05,
'bpti_conc': 3.0087175891276203e-05,
'kcat': 10.075765236194103,
'km': 3.320531258425417e-05},
'9V51PV': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.2972895706886008e-05,
'bpti_conc': 3.301735248311078e-05,
'kcat': 14.253426350877806,
'km': 8.949671739010028e-05},
'942913': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.0249547380195524e-05,
'bpti_conc': 3.2738298103130895e-05,
'kcat': 11.135081432311162,
'km': 7.07376899404837e-05},
'N898UN': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.8674877368836247e-05,
'bpti_conc': 3.062565836535658e-05,
'kcat': 10.097357504977413,
'km': 6.59893141525897e-05},
'H4CGP6': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.4074717025273083e-05,
'bpti_conc': 3.17443257141572e-05,
'kcat': 10.86527728039156,
'km': 4.0295083099196564e-05},
'13KE9N': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.0654600749048573e-05,
'bpti_conc': 3.103388919416864e-05,
'kcat': 14.476740111724716,
'km': 4.664199652900124e-05},
'31PQ53': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.264345369791618e-05,
'bpti_conc': 3.0372329090212688e-05,
'kcat': 6.560328970813622,
'km': 3.930321207228756e-05},
'KK644G': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 1.9346499463477278e-05,
'bpti_conc': 3.387315205215228e-05,
'kcat': 8.322469408595582,
'km': 4.5568979770163385e-05},
'2TNA45': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 1.9246819164209123e-05,
'bpti_conc': 3.578113635439317e-05,
'kcat': 5.953190657380047,
'km': 3.994947194229087e-05},
'N6AADQ': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 1.8630368266346355e-05,
'bpti_conc': 3.19547849326813e-05,
'kcat': 8.425285215458262,
'km': 8.304450919324063e-05},
'SV22HX': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.7310317193021016e-05,
'bpti_conc': 3.368622203870609e-05,
'kcat': 11.54988636685996,
'km': 5.4691251633663635e-05},
'VS8S0J': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.1921188687159537e-05,
'bpti_conc': 3.230870033910233e-05,
'kcat': 11.169575003686369,
'km': 6.197424343338799e-05},
'4U2RCD': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 1.876088862624062e-05,
'bpti_conc': 3.0681927928368936e-05,
'kcat': 11.746442846771568,
'km': 5.739471754772193e-05},
'ZCRTWU': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 1.9224581361271966e-05,
'bpti_conc': 3.162737039250661e-05,
'kcat': 5.076806311887225,
'km': 3.228842227365505e-05},
'7182Y1': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.47216027558941e-05,
'bpti_conc': 3.0170396309572576e-05,
'kcat': 8.241979108276192,
'km': 6.0074402918688364e-05},
'W8Y4P3': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.7782485501855382e-05,
'bpti_conc': 3.33401281619942e-05,
'kcat': 10.195874314801054,
'km': 3.488329626913995e-05},
'J9PWB4': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.475736161044849e-05,
'bpti_conc': 3.404517806270831e-05,
'kcat': 6.754889828781962,
'km': 8.773579280310646e-05},
'43479S': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.6621708935755493e-05,
'bpti_conc': 3.45845158476193e-05,
'kcat': 13.144983394351692,
'km': 5.9336652102587744e-05},
'MYX1AX': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 1.6766972738830726e-05,
'bpti_conc': 3.454616610975151e-05,
'kcat': 12.914367456489144,
'km': 4.940114234055595e-05},
'S0E0YV': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.7032357040088922e-05,
'bpti_conc': 3.340431664908145e-05,
'kcat': 10.957824559421038,
'km': 3.289894725028064e-05},
'DFKPU9': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 1.723282359923739e-05,
'bpti_conc': 3.364544537727849e-05,
'kcat': 7.466998500516203,
'km': 6.51627417368447e-05},
'9RFXYW': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.5000509553011084e-05,
'bpti_conc': 3.416324621738166e-05,
'kcat': 6.705684564747556,
'km': 5.8051980187978164e-05},
'88PTV8': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.4524217499362522e-05,
'bpti_conc': 3.526260165210863e-05,
'kcat': 5.011219927285401,
'km': 8.251030817940819e-05},
'H0X54M': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.4139816960349272e-05,
'bpti_conc': 3.104640020546267e-05,
'kcat': 6.589695629876994,
'km': 6.600131868852241e-05},
'KD1K2N': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 1.7172667254962533e-05,
'bpti_conc': 3.4168766856849033e-05,
'kcat': 10.800375363792174,
'km': 4.161401188579533e-05},
'23W0N6': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 1.922648717519026e-05,
'bpti_conc': 3.244865757821224e-05,
'kcat': 10.434092819204855,
'km': 8.777019172064786e-05},
'GQ5P11': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.7288023178577137e-05,
'bpti_conc': 3.372014139433057e-05,
'kcat': 8.027464722293182,
'km': 8.77400879268201e-05},
'597J70': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 1.912745303888913e-05,
'bpti_conc': 3.56382749381675e-05,
'kcat': 10.458568211196791,
'km': 4.246737812426825e-05},
'77YE98': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 1.6706669601780035e-05,
'bpti_conc': 3.245311391523635e-05,
'kcat': 7.330432197837356,
'km': 8.891603561232728e-05},
'994A4U': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.8895005256200494e-05,
'bpti_conc': 3.259486743957249e-05,
'kcat': 5.174640178640072,
'km': 5.320770050272746e-05},
'22NZ8S': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.1267935215025052e-05,
'bpti_conc': 3.247345993620789e-05,
'kcat': 9.340617525524213,
'km': 8.084998120581375e-05},
'33VZHV': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.588140524849908e-05,
'bpti_conc': 3.069265913614357e-05,
'kcat': 11.929996897914258,
'km': 4.824483178683697e-05},
'YD34E7': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.1705054520364497e-05,
'bpti_conc': 3.0709011784212644e-05,
'kcat': 10.27610221982453,
'km': 3.781178868193927e-05},
'B4EV54': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.0354460994888705e-05,
'bpti_conc': 3.116014032604333e-05,
'kcat': 7.021725135847429,
'km': 5.7035565773548506e-05},
'QXQEA0': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.3323423105185772e-05,
'bpti_conc': 3.1520993936694644e-05,
'kcat': 6.929654495543255,
'km': 3.181108783085965e-05},
'RCE9Z2': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.01296550430366e-05,
'bpti_conc': 3.348731237039411e-05,
'kcat': 13.410636428482128,
'km': 6.436577355205234e-05},
'W382SW': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 1.774481028180708e-05,
'bpti_conc': 3.5226383654173546e-05,
'kcat': 12.769638907644534,
'km': 7.359061292718445e-05},
'Y1UUUA': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.0828543359469805e-05,
'bpti_conc': 3.0181583232375123e-05,
'kcat': 6.72929920464161,
'km': 8.86483930276227e-05},
'240125': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.7918021956761915e-05,
'bpti_conc': 3.501677251343129e-05,
'kcat': 5.844744273832838,
'km': 4.588117735847368e-05},
'0P20E8': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 1.9706742739803737e-05,
'bpti_conc': 3.5919910588591474e-05,
'kcat': 11.3167024699186,
'km': 8.655867671649303e-05},
'CTZKSB': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.6678519754892104e-05,
'bpti_conc': 3.063378110243431e-05,
'kcat': 14.698116904941546,
'km': 7.633417818129455e-05},
'K60820': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 1.7891470882886813e-05,
'bpti_conc': 3.408969521270859e-05,
'kcat': 5.1724040679813585,
'km': 8.540103081102389e-05},
'WE21H5': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 1.8244941876266545e-05,
'bpti_conc': 3.552211078184191e-05,
'kcat': 9.019636410141747,
'km': 8.204223957035615e-05},
'N180MF': {'pip_sigma': 0.03,
'abs_sigma': 0.001,
'e_conc': 2.3419204687993863e-05,
'bpti_conc': 3.1431254231335e-05,
'kcat': 7.10770397148994,
'km': 5.528682797277228e-05}}
name: exp_3c_sim
channels:
- conda-forge
- defaults
- conda-forge/label/broken
dependencies:
- python=3.8.3
- numpy=1.19.2
- scipy = 1.5.2
- matplotlib=3.3.2
- ipython=7.19.0
- ipywidgets=7.5.1
- voila=0.2.6
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
## Module for import into Jupyter for simulation of Experiment 3, part C
## in Biology 3515/Chem 3515 at the University of Utah, Spring 2021
## https://goldenberg.biology.utah.edu/courses/biol3515/
## David P. Goldenberg, February 2021
## goldenberg@biology.utah.edu
import random as rand
import numpy as np
import matplotlib.pyplot as plt
import ipywidgets as widgets
from IPython.display import display, HTML
from time import sleep, time
import threading
from scipy.integrate import solve_ivp
import functools
from scipy.stats import linregress
from labcodes import codes
from code_dict_exp3 import code_dict
display(HTML("<style>div.output_scroll { height: 400ex; }</style>"))
##----------Global Parameters--------------------##
# delay time while filling the absorbance table
# Use 0 for testing and 0.2 for production to make the simulation run about
# 5-x the actual speed.
t_sleep = 0.2
# initial values of absorbance reading and background absorbance
abs_read = 0.0 # for uv abs of bpti soln
zero_set = 0.0 # for uv abs of bpti soln
# Total time and number of steps for abs reading to settle
t_tot = 2
steps = 25
graph_title = 'Experiment 3, Part C'
style = {'description_width': 'initial'}
def make_code_dict(codes):
'''Function to build code dictionary specific to this experiment
Don't actually call this function when loading the module, or else
the dictionary will change with every execution. Instead save value
of dictionary in this module.'''
code_dict ={}
for code in codes:
pip_sigma =0.03
abs_sigma = 0.001
e_conc = (1 + rand.random())*1.5e-5
bpti_conc = (1 + 0.2*rand.random())*3e-5
kcat = (1 + 2*rand.random())*5.0
km = (1 + 2*rand.random())*30e-6
code_dict[code]={}
code_dict[code]['pip_sigma']=pip_sigma
code_dict[code]['abs_sigma'] = abs_sigma
code_dict[code]['e_conc']=e_conc
code_dict[code]['bpti_conc']=bpti_conc
code_dict[code]['kcat']= kcat
code_dict[code]['km']=km
return code_dict
##----------------Classes----------------------##
class Cuvette():
'''Cuvette class, like Tube but with additional
attributes for sample name and reactions start time, t0'''
def __init__(self):
self.vol = 0.0
self.vol_n = 0.0
self.conc = {}
self.t0 =0.0
class VolTable(widgets.HTML):
def __init__(self,tube_dict,head):
super().__init__(
value='',
layout=widgets.Layout(width='250px'))
self.tube_dict = tube_dict
self.head = head
self.update()
def update(self):
'''function to update the table of tube or cuvette volumes'''
html_str = '''
<style>
table, th, td {
border: 1px solid black;
}
th, td {
padding: 5px;
}
table, {border-collapse: collapse;}
</style>
<table>
<tr>'''
html_str += f'<th style="width:100px"> {self.head} </th>'
html_str += '''<th style="width:100px"> Volume (&mu;L)</th></tr>'''
for tube in self.tube_dict:
vol = self.tube_dict[tube].vol_n
html_str += f'<tr><td>{tube}</td><td>{vol:.0f} </td></tr>\n'
html_str += '</table>'
self.value=html_str
class AbsTable(widgets.HTML):
def __init__(self,cuv_dict,t_int,t_tot):
super().__init__(
value='',
layout=widgets.Layout(width='600px'))
self.cuv_dict = cuv_dict
self.t_int = t_int
self.t_tot = t_tot
self.abs_data = []
self.n_meas = int(t_tot/t_int)+1
self.n_cuv = len(self.cuv_dict)
self.refresh()
self.update()
def refresh(self):
self.abs_data=[['Time (min)']]
for cuv in self.cuv_dict:
self.abs_data[0].append('Cuvette ' + str(cuv))
for i in range(self.n_meas):
t = i*self.t_int
self.abs_data.append([t]+[None]*self.n_cuv)
self.update()
def update(self):
html_str = '''
<style>
table, th, td {
border: 1px solid black;
}
th, td {
padding: 5px;
}
table, {border-collapse: collapse;}
</style>
<table>
<tr>
<th style="width:100px"> Time (min) </th>'''
for i in range(self.n_cuv):
html_str += f'<th style="width:100px"> {self.abs_data[0][i+1]} </th>'
html_str += '</tr>\n'
for i in range(self.n_meas):
html_str += f'<tr><td> {self.abs_data[i+1][0]}</td>'
for j in range(self.n_cuv):
if self.abs_data[i+1][j+1] != None:
html_str += f'<td>{self.abs_data[i+1][j+1]:0.3f}</td>'
else:
html_str += '<td></td>'
html_str += '</tr>\n'
html_str += '</table>'
self.value=html_str
class RegTable(widgets.HTML):
def __init__(self,cuv_list,reg_results):
super().__init__(
value='',
layout=widgets.Layout(width='500px'))
self.reg_results = reg_results
self.cuv_list = cuv_list
self.update()
def update(self):
'''function to update the table of results from linear regression'''
n_cuv = len(self.reg_results)
html_str = '''
<style>
table, th, td {
border: 1px solid black;
}
th, td {
padding: 5px;
}
table, {border-collapse: collapse;}
</style>
<table>
<tr>'''
html_str += '''<th style="width:100px"> Cuvette </th>'''
html_str += '''<th style="width:100px"> Rate (min<sup>-1</sup>) </th>'''
html_str += '''<th style="width:100px"> A<sub>0</sub> </th>'''
html_str += '''<th style="width:100px"> R<sup>2</sup> </th></tr>'''
for i in range(n_cuv):
cuv = self.cuv_list[i]
html_str += f'<tr><td> {cuv}</td>'
slope = self.reg_results[i].slope
html_str += f'<td>{slope:.4f}</td>'
a0 = self.reg_results[i].intercept
html_str += f'<td>{a0:.4f}</td>'
rsq = self.reg_results[i].rvalue**2
html_str += f'<td>{rsq:.3f}</td></tr>'
html_str += '</table>'
self.value=html_str
## --------------- Create Dictionaries ------------------##
# This has to come after the Tube and Cuvette classes are defined!
conc_dict = {'trypsin':0.0, 'hepes':0.0, 'cacl':0.0,'substr':0.0}
cuv_dict = {}
for n in range(1,4):
cuv_dict[n] = Cuvette()
cuv_dict[n].conc = conc_dict.copy()
solns_dict={} # used to store stock solution concentrations
# trypsin and BPTI concentrations are set when code is read or changed
solns_dict['trypsin'] =0.0
solns_dict['hepes'] = 0.2
solns_dict['cacl'] = 20.0*1e-3 # M
solns_dict['substr'] = 10.0*1e-3 # M
# create a cuvette dictionary with a single element, for the cuvette selected for the run
# Then create absorbance table
# The cuvette dictionary and abs table is updated with the cuvette selected when the cuvette is changed
cuv_dict_r ={}
cuv_dict_r[1] =cuv_dict[1]
abs_table = AbsTable(cuv_dict_r, 1.0,5.0)
#########---------Widgets and Their Functions --------------########
##----------------Pipettes -----------------------##
# Used for multiple parts of experiment
# The deliver button is specific to each part
def on_pipette_ch(change):
'''Function to changes volume parameters when pipette is changed.'''
pip_vol.max = 1000.0
if pipette.value == 1000.0:
pip_vol.min = 200.0
pip_vol.max = 1000.0
pip_vol.step = 10.0
elif pipette.value == 200.0:
pip_vol.min = 20.0
pip_vol.max = 200.0
pip_vol.step = 1.0
elif pipette.value == 20.0:
pip_vol.min = 1.0
pip_vol.max = 20.0
pip_vol.step = 0.1
pip_vol.value = pip_vol.max
pipette = widgets.Dropdown(
# Choose pipette size
options = [('P1000',1000.0),('P200',200.0),('P20',20.0)],
description = 'Pipette:',
style=style,
layout=widgets.Layout(width='150px',margin='0px 0px 0px 50px')
)
pipette.observe(on_pipette_ch,names=['value'])
pip_vol = widgets.BoundedFloatText(
# Pipette volume setting
value = 1000.0,
min = 200.0,
max = 1000.0,
step = 10.0,
description = 'Volume set (uL):',
style = style,
layout=widgets.Layout(width='200px',margin='0px 0px 0px 50px')
)
def pip_deliver(soln_widget, soln_dict, tube_widget,tube_dict):
tube_list = list(tube_dict)
conc_list = list(tube_dict[tube_list[0]].conc)
pip_sigma = code_dict[lab_code.value]['pip_sigma']
# get pipette err from normal distr
err_pipette = pipette.value*rand.normalvariate(0,pip_sigma)
# calculate delivered volume
vol = pip_vol.value+err_pipette
# save old volume and calculate new
vol_old = tube_dict[tube_widget.value].vol
vol_new = vol_old + vol
tube_list = list(tube_dict)
reag_list = list(tube_dict[tube_list[0]].conc)
incr = {}
for reag in reag_list:
incr[reag]=0.0
# calculate increment in solute amount (mg, moles,etc)
for reag in reag_list:
if reag == soln_widget.value:
incr[reag] = vol*soln_dict[reag]
if 'trypsin' == soln_widget.value:
# special provision for trypsin solution conaining 20 mM CaCl2
incr['cacl'] =vol*0.02
# update concentrations
for reag in reag_list:
conc_old = tube_dict[tube_widget.value].conc[reag]
conc_new = (conc_old*vol_old + incr[reag])/vol_new
tube_dict[tube_widget.value].conc[reag] = conc_new
#update volumes
tube_dict[tube_widget.value].vol = vol_new
tube_dict[tube_widget.value].vol_n += pip_vol.value
##----------------Lab Code Entry ------------------##
def on_code_ch(value):
'''Respond to change in the code entered in the code box'''
if lab_code.value != '' and lab_code.value not in codes:
code_warn.value = '<span style="color:red;">Invalid code</span>'
else:
solns_dict['trypsin']=code_dict[lab_code.value]['e_conc']
code_warn.value =''
deliver_butt.button_style = 'success'
deliver_butt.description='Pipette!'
run_butt.button_style = 'success'
run_butt.description='Run!'
code_label = widgets.HTML(
value = ''' <h3>Lab code: </h3>'''
)
lab_code = widgets.Text(
# text-entry widget to enter lab code
value = '',
placeholder='',
#description = 'Lab code',
layout=widgets.Layout(width='100px',margin='20px 0px 0px 15px')
)
lab_code.observe(on_code_ch,names=['value'])
code_warn = widgets.HTML(
# warning when invalid lab code is entered
value = '',
layout=widgets.Layout(width='200px',margin='15px 50px 15px 0px')
)
row_code = widgets.HBox([code_label,lab_code,code_warn])
##--------------- Cuvette Setup ------------------##
def on_cuv_change(change):
cuv = cuv_widget.value
cuv_dict_r = {}
cuv_dict_r[cuv] =cuv_dict[cuv]
abs_table.cuv_dict = cuv_dict_r
abs_table.refresh()
cuv_show(cuv)
def deliver(change):
if lab_code.value not in codes:
deliver_butt.description='Enter valid lab code'
deliver_butt.button_style = 'warning'
return
pip_deliver(solns_widget, solns_dict, cuv_widget, cuv_dict)
c_trypsin = cuv_dict[cuv_widget.value].conc['trypsin']
c_substr = cuv_dict[cuv_widget.value].conc['substr']
if c_trypsin >0 and c_substr >0:
cuv_dict[cuv_widget.value].t0 = time()
cuv_vol_table.update()
def empty_cuv(change):
cuv = cuv_dict[cuv_widget.value]
for conc in cuv.conc:
cuv.conc[conc]=0.0
cuv.vol = 0.0
cuv.vol_n = 0.0
cuv.t0 = 0.0
cuv_vol_table.update()
solns_widget= widgets.RadioButtons(
# solutions for cuvettes for part A
options =[('0.2 M HEPES pH 8','hepes'), ('20 mM CaCl2','cacl'), ('~0.5 mg/mL trypsin','trypsin'),
('10 mM NPGB','substr')],
description='Solutions',
layout=widgets.Layout(width='30%',margin='0px 0px 0px 0px')
)
cuv_widget = widgets.RadioButtons(
# Cuvettes for part A
options=[1,2,3],
description = 'Cuvettes:'
)
cuv_widget.observe(on_cuv_change)
empty_butt = widgets.Button(
# button to empty cuvettes for part A
description = 'Empty',
button_style = 'danger',
)
empty_butt.on_click(empty_cuv)
cuv_vol_table = VolTable(cuv_dict,'Cuvette')
deliver_butt = widgets.Button(
# Button to deliver volume for BSA dilution
description = 'Pipette!',
button_style = 'success',
)
deliver_butt.on_click(deliver)
cuvVb=widgets.VBox([cuv_widget,empty_butt],
layout=widgets.Layout(width='40%',margin='0px 0px 0px 00px'))
row_cuv= widgets.HBox([solns_widget,cuvVb,cuv_vol_table])
row_pip = widgets.HBox([deliver_butt,pipette,pip_vol],
layout=widgets.Layout(margin='15px 0px 0px 0px'))
box_cuv = widgets.VBox([row_cuv,row_pip])
##----------------Kinetic Run----------------------##
def burst_de(t,y,k_b,k_s1,k_s2,e_init):
s_conc = y[0]
e_conc = y[1]
p_conc = y[2]
# rate of burst phase - second-order
r_b = k_b*s_conc*e_conc
# rate of slow phase - includes a both firt- and second-order component
# second-order process is assumed to depend on total enzyme concentration
# and is unaffected by modificatio of the active site.
r_s= k_s1*s_conc + k_s2*s_conc*e_init
dp_conc = r_b + r_s
de_conc = -r_b
ds_conc = -dp_conc
return[ds_conc,de_conc,dp_conc]
def kin_sim(cuv_dict,t_tot):
# simulation is run for twice t_tot, to allow for starting delay
# and continued absorbance readings after the run
pts = int(1.5*t_tot*60)
# rate constants for burst and slow phases
k_b = 1e3
k_s1 = 1.7e-5
k_s2 = 2.4
cuv = list(cuv_dict)[0]
s0 = cuv_dict[cuv].conc['substr']
e0 =cuv_dict[cuv].conc['trypsin']
if cuv_dict[cuv].conc['hepes'] < 0.08:
# activity is reduced if tris concentration isn't adequate
e0 *= 0.5
if cuv_dict[cuv].conc['cacl'] < 0.0008:
# some of the enzyme is lost if CaCl2 concentration isn't adequate
e0 *= 0.5
# note the default integration method for solve_ivp, RK45, is unstable for this set of DEs
# The Ragu method is recommended for stiff equations, and works well here
kin_sol= solve_ivp(burst_de,[0.0,float(pts)],[s0,e0,0.0], method = 'Radau',
args=[k_b,k_s1,k_s2,e0],t_eval=np.linspace(0.0,float(pts),pts+1))
return kin_sol
def kin_run(abs_table,change):
if lab_code.value not in codes:
run_butt.description='Enter valid lab code'
run_butt.button_style = 'warning'
return
run_butt.disabled = True
abs_sigma = code_dict[lab_code.value]['abs_sigma']
abs_table.refresh()
if wl.value == '402 nm':
ext_coef=1.6e4
elif wl.value == '405 nm':
ext_coef=1.5e4
else:
ext_coef=0.0
t_int = abs_table.t_int
t_tot = abs_table.t_tot
n_cuv = abs_table.n_cuv
n_meas = abs_table.n_meas
cuv_dict = abs_table.cuv_dict
abs_data = abs_table.abs_data
kin_sol = kin_sim(cuv_dict,t_tot)
sleep(1)
delay_t = 0
cuv = list(cuv_dict)[0]
if cuv_dict[cuv].t0 > 0:
delay_t = int(time()-cuv_dict[cuv].t0)
t=0
tcuv=0
meas = 1
cuv_int = t_int*60
cuv_show(cuv)
abs_start = abs_read-zero_set
# add a little to the absorbance background from NPGB solution
abs_start += 0.002
pts_total = int((t_tot)*60)+1
absorb = kin_sol.y[2][delay_t]*ext_coef + abs_start
abs_show(absorb)
abs_data[meas][1] = absorb
abs_table.update()
meas += 1
for i in range(pts_total):
sleep(t_sleep)
absorb = kin_sol.y[2][t]*ext_coef + abs_start
abs_show(absorb)
if tcuv>=cuv_int:
read_t = t + delay_t
err = rand.normalvariate(0,abs_sigma)
absorb = abs_start + (kin_sol.y[2][read_t]*ext_coef)+err
abs_data[meas][1] = absorb
abs_table.update()
meas +=1
tcuv=0
tcuv+=1
t += 1
make_plot(abs_table.abs_data)
reset_butt.disabled = False
def make_plot(abs_data):
global reg_table
prop_cycle = plt.rcParams['axes.prop_cycle']
colors = prop_cycle.by_key()['color']
colors.pop(2) # remove green
#colors.pop(4) # remove brown
#colors.pop(5) # remove grey
cuv_dict = abs_table.cuv_dict
cuv_list = list(cuv_dict)
plot_data = [[]]
n_meas = abs_table.n_meas
n_cuv = abs_table.n_cuv
abs_data = abs_table.abs_data
for j in range(n_meas):
plot_data[0].append(abs_data[j+1][0])
for i in range(n_cuv):
plot_data.append([])
for j in range(n_meas):
plot_data[i+1].append(abs_data[j+1][i+1])
plot_data = np.array(plot_data)
reg_results = []
for i in range(n_cuv):
reg = linregress(plot_data[0],plot_data[i+1])
reg_results.append(reg)
reg_x = np.array([0.0,plot_data[0,-1]])
fig = plt.figure(figsize=(7.5,5))
ax= fig.add_subplot(1,1,1)
for i in range(n_cuv):
reg_yint = reg_results[i].intercept
reg_slope = reg_results[i].slope
reg_y = reg_x*reg_slope+reg_yint
ax.plot(plot_data[0],plot_data[i+1],'o',color=colors[i])
ax.plot(reg_x,reg_y,color=colors[i], label =f'Cuvette {cuv_list[i]}')
ax.set_title(graph_title,fontsize=18)
ax.set_xlabel('Time (min)',fontsize=14)
ylabel_str = 'Absorbance at ' + wl.value
ax.set_ylabel(ylabel_str,fontsize=14)
data_min = plot_data.min()
if data_min < 0.0:
ax.set_ylim(data_min,)
else:
ax.set_ylim(0,)
ax.set_xlim(0,)
ax.legend()
# Important note: close the plot so that it doesn't automatically
# display in Jupyter. Otherwise, it will never go away!
# display the fig in Output widget, which can be made to go away!
plt.close()
reg_table = RegTable(cuv_list,reg_results)
with fig_output:
display(fig)
# Important note 2: In order for the regression table (an HTML widget
# created within this function to appear in viola, it has to be put
# in an output widget declared globally.
with reg_table_output:
display(reg_table)
def on_wl_ch(value):
'''Respond to change in the wavelength widget'''
h2O_abs = {'260 nm':0.14,'280 nm':0.3, '402 nm':0.4, '405 nm':0.6,'595 nm':0.8}
absorb=h2O_abs[wl.value]
abs_update(absorb)
def zero(change):
'''Respond to the Zero button'''
global zero_set
zero_set = abs_read
abs_update(abs_read)
def damp_sin(t,nu,tau):
'''damped sine function to simulate settling
of absorbance reading. Input arguments:
t: time
nu: sine-function frequency
tau: exponential-decay time constant
'''
d_sin = np.exp(-t/tau)*np.sin(t*nu*2*np.pi)
return d_sin
def abs_update(absorb):
'''function to set global abs_read variable and
Updates absorbance display, with settling time
Input arguments:
absb: settled absorbance value
global variables used:
t_tot: total time for reading to settle
steps: number of times to update display
as it settles.
'''
global abs_read
displ_absb = absorb - zero_set
tau = 0.2*t_tot
nu = 5/t_tot
for i in range(steps+1):
t = t_tot*i/steps
abs_d = displ_absb*(1+damp_sin(t,nu,tau))
sleep(t_tot/steps)
abs_show(abs_d)
abs_read = absorb
def abs_show(absorb):
'''function to displays variable in the spec_dislp HTML widget'''
if absorb < -0.03:
spec_displ.value='<h2> Abs: <span style="color:red;"> Err </span></h2>'
else:
spec_displ.value='<h2> Abs: <span style="color:red;"> \
{:.3f}'.format(absorb) + '</span></h2>'
def cuv_show(cuv):
'''function to display the cuvette number in the cuv_display HTML widget'''
cuv_displ.value=f'<h2>Cuvette:<span style="color:red;">\
{cuv} </span></h2>'
def on_reset(change):
abs_table.refresh()
reg_table.close()
fig_output.clear_output()
run_butt.disabled = False
reset_butt.disabled = True
wl = widgets.Dropdown(
# Dropdown to select wavelength for spectrophotometer
options = [('260 nm'),('280 nm'),('402 nm'),('405 nm'),('595 nm')],
description = 'Wavelength:',
style = style,
layout=widgets.Layout(width='200px',margin='0px 0px 20px 0px')
)
wl.observe(on_wl_ch,names=['value'])
zero_butt = widgets.Button(
# button to zero absorbance display
description = 'Zero',
button_style = 'success',
layout=widgets.Layout(margin='0px 0px 0px 20px')
)
zero_butt.on_click(zero)
run_butt = widgets.Button(
# Button to start simulated kinetics run
description = 'Run!',
button_style = 'success',
layout=widgets.Layout(margin='0px 0px 0px 0px')
)
run_butt.on_click(functools.partial(kin_run,abs_table))
cuv_displ=widgets.HTML(
# cuvette number display
value='1',
layout=widgets.Layout(margin='-15px 0px 0px 20px')
)
spec_displ = widgets.HTML(
# spectrophotometer display
value='0.0',
layout=widgets.Layout(width='140px',margin='-15px 0px 0px 20px')
)
reset_butt = widgets.Button(
# button to reset kinetic simulation
description ='Reset',
button_style = 'danger',
disabled = True,
layout=widgets.Layout(margin='0px 0px 0px 20px')
)
reset_butt.on_click(on_reset)
abs_show(0)
cuv_show(cuv_widget.value)
row_spec1 = widgets.HBox([wl,zero_butt])
row_spec2 = widgets.HBox([run_butt, cuv_displ, spec_displ, reset_butt])
fig_output = widgets.Output(layout={'border': '1px solid black','width':'500px'})
reg_table_output=widgets.Output()
##-------------- Decorative and Informational Widgets ----------------------#
hrule = widgets.HTML(
# general purpose horizontal rule
value ='<hr border-width:6px, color:black>')
header_html = """
<h2> Biology 3515/Chemistry 3515
<br>
Biological Chemistry Laboratory
<br>
University of Utah </h2>
<h3> Spring 2021 </h3>
<h3> Experiment 3, Part C: Determination of Enzyme Concentration with a Burst Substrate</h3>
<p style="font-size:16px">
This web page simulates the procedures to be carried out in Part C of Experiment 3. For this part of the experiment, you will set up three cuvettes containing different concentrations of enzyme and follow the reaction with 4-nitrophenyl-4-guanidobenzoate, a burst substrate. This reagent forms a very stable acyl intermediate with the enzyme, so that each enzyme produces only a single molecule of nitro-phenol product. By measuring the amount of product produced, the concentration of enzyme can be determined. There is, however, a background rate of hydrolysis that must me measured and accounted for in the determination.
<br><br>
For this experiment, it is important that the spectrophotometer be zeroed with the cuvette containing everything except the substrate, and the kinetic run should be started as quickly as possible after mixing the enzyme and substrate. For these reasons, separate kinetic runs are conducted for each cuvette.
<br><br>
The kinetic run is carried out as in the previous parts of the experiment, and as would be done in the lab using the program MacSpec to control the spectrophotometer and record the data automatically. As with MacSpec, the simulation will output a table of absorbance values recorded at time intervals and make a plot of the data. The program also prints a table of data from least-squares fit of the data to a straight line.
<br><br>
The graph displayed can be readily copied for use in another program or saved to your computer by right-clicking on the image and choosing the appropriate image. The data in tables cannot be directly saved to a file, but they can be selected using the mouse and copied for use in another program. The rows and columns can be directly copied into the data table of SciDAVis or a spreadsheet.
<br><br>
The automatic curve fit includes the point at zero minutes, which may or may not be appropriate. The goal here is to extrapolate the background hydrolysis rate to time 0. If the burst phase is completed by the first time point, this point will lie close to the fit line, and the fit parameters can be used for the data analysis. If, however, the first point lies significantly below the line, the data should be anlyzed using a fit that excludes this point. This can be done by copying the data into SciDAVis and using the curve-fitting tools in that program.
<br><br>
As in the earlier experiment simulations, a set of radio buttons is used to select solutions to pipette from and the tubes or cuvettes to be pipetted into. Tables are used to show the nominal volumes in the tubes as reagents are added.
<br><br>
The simulation of the kinetic run is sped up by a factor of about 5-fold from what the real experiment takes.
<br><br>
Like the previous experiments, this simulations requires a lab code to function, and your group should use the same code assigned earlier. If you do not have an assigned code, but want to experiment with this simulation, use the code "0GXY02".
"""
header = widgets.HTML(
value=header_html
)
footer = widgets.HTML(
value='''
<hr border-width:6px, color:black>
David P. Goldenberg, February 2021<br>
<a href="mailto:goldenberg@biology.utah.edu" target="new">goldenberg@biology.utah.edu</a><br>
School of Biological Sciences, University of Utah<br>
Salt Lake City, Utah 8412-0840<br>
<a href="https://goldenberg.biology.utah.edu/courses/biol3515/index.shtml"
target="new">https://goldenberg.biology.utah.edu/courses/biol3550.</a>
''')
c1header = widgets.HTML(
# header for part B, part 1
value = '<h2> 1. Set up cuvettes</h2>'
)
c2header = widgets.HTML(
# header for part B, part 2
value = '<h2> 2. Kinetic run</h2>'
)
##--------------Test functions -----------------------#
def cuv_reset(cuv_dict):
'''Reset volumes, concentrations and t0 in all of the cuvettes in cuv_dict'''
for cuv in cuv_dict:
cuv_dict[cuv].vol = 0.0
cuv_dict[cuv].vol_n = 0.0
for c in cuv_dict[cuv].conc:
cuv_dict[cuv].conc[c] = 0.0
cuv_dict[cuv].t0=0.0
def test_cuv():
cuv_reset(cuv_dict)
# A function for quickly setting up cuvettes for testing
# sets up state before final addition of bpti-trypsin incubation mixture
e_stock = solns_dict['trypsin']
s_stock = 0.010
for cuv in cuv_dict:
cuv_dict[cuv].vol = 800.0
cuv_dict[cuv].vol_n = 800.0
cuv_dict[cuv].conc['cacl']=20e-3*400/800
cuv_dict[cuv].conc['hepes']=0.2*400.0/800
cuv_dict[cuv].conc['substr']= 0
cuv_dict[cuv].t0 = 0
cuv_dict[1].conc['trypsin'] =0.0
cuv_dict[2].conc['trypsin'] =e_stock/8.0
cuv_dict[3].conc['trypsin'] =3*e_stock/8.0
cuv_vol_table.update()
##-------------- Decorative and Informational Widgets ----------------------#
hrule = widgets.HTML(
# general purpose horizontal rule
value ='<hr border-width:6px, color:black>')
##--------------Display Everything -----------------------------#
#lab_code.value='0GXY02'
display(header)
display(hrule)
display(row_code)
display(hrule)
display(c1header)
display(box_cuv)
display(hrule)
display(c2header)
display(row_spec1)
display(row_spec2)
display(abs_table)
display(fig_output)
display(reg_table_output)
display(footer)
codes =['0GXY02',
'DD0371',
'X5FX87',
'FG188G',
'34P10Q',
'Y4N6KZ',
'W5XM03',
'9V51PV',
'942913',
'N898UN',
'H4CGP6',
'13KE9N',
'31PQ53',
'KK644G',
'2TNA45',
'N6AADQ',
'SV22HX',
'VS8S0J',
'4U2RCD',
'ZCRTWU',
'7182Y1',
'W8Y4P3',
'J9PWB4',
'43479S',
'MYX1AX',
'S0E0YV',
'DFKPU9',
'9RFXYW',
'88PTV8',
'H0X54M',
'KD1K2N',
'23W0N6',
'GQ5P11',
'597J70',
'77YE98',
'994A4U',
'22NZ8S',
'33VZHV',
'YD34E7',
'B4EV54',
'QXQEA0',
'RCE9Z2',
'W382SW',
'Y1UUUA',
'240125',
'0P20E8',
'CTZKSB',
'K60820',
'WE21H5',
'N180MF']
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment