Skip to content

Commit a6ed1ac

Browse files
authored
Merge pull request #69 from BrainAnnex/dev
Dev beta 38
2 parents ac221c9 + ecc508a commit a6ed1ac

35 files changed

+13819
-2038
lines changed

experiments/reactions_single_compartment/cascade_1.ipynb

Lines changed: 565 additions & 541 deletions
Large diffs are not rendered by default.

experiments/reactions_single_compartment/cascade_1.py

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,9 @@
2121
# Adaptive variable time resolution is used, with extensive diagnostics,
2222
# and finally compared to a new run using fixed time intervals, with the same initial data.
2323
#
24-
# In part2, some diagnotic insight is explored.
25-
# In part3, two identical runs ("adaptive variable steps" and "fixed small steps") are compared.
24+
# In part2, some diagnostic insight is explored.
2625
#
27-
# LAST REVISED: June 23, 2024 (using v. 1.0 beta34.1)
26+
# In part3, two identical runs ("adaptive variable steps" vs. "fixed small steps") are compared.
2827

2928
# %% [markdown]
3029
# ## Bathtub analogy:
@@ -41,20 +40,28 @@
4140
# ![2 Coupled Reactions](../../docs/2_coupled_reactions.png)
4241

4342
# %%
44-
import set_path # Importing this module will add the project's home directory to sys.path
43+
LAST_REVISED = "July 26, 2024"
44+
LIFE123_VERSION = "1.0.0.beta.38" # Version this experiment is based on
45+
46+
# %%
47+
#import set_path # Using MyBinder? Uncomment this before running the next cell!
48+
# Importing this local file will add the project's home directory to sys.path
4549

4650
# %% tags=[]
47-
from experiments.get_notebook_info import get_notebook_basename
51+
#import sys
52+
#sys.path.append("C:/some_path/my_env_or_install") # CHANGE to the folder containing your venv or libraries installation!
53+
# NOTE: If any of the imports below can't find a module, uncomment the lines above, or try: import set_path
54+
55+
import ipynbname
4856

49-
from life123 import ChemData
50-
from life123 import UniformCompartment
51-
from life123.visualization.plotly_helper import PlotlyHelper
57+
from life123 import check_version, ChemData, UniformCompartment, PlotlyHelper, GraphicLog
5258

53-
from life123 import GraphicLog
59+
# %%
60+
check_version(LIFE123_VERSION)
5461

5562
# %% tags=[]
5663
# Initialize the HTML logging (for the graphics)
57-
log_file = get_notebook_basename() + ".log.htm" # Use the notebook base filename for the log file
64+
log_file = ipynbname.name() + ".log.htm" # Use the notebook base filename for the log file
5865

5966
# Set up the use of some specified graphic (Vue) components
6067
GraphicLog.config(filename=log_file,
@@ -66,15 +73,16 @@
6673
# Specify the chemicals and the reactions
6774

6875
# %% tags=[]
69-
# Instantiate the simulator and specify the chemicals
70-
chem = ChemData(names=["A", "B", "C"])
76+
# Specify the chemicals and the reactions; this data structure will get re-used in
77+
# the various simulations below
78+
chem = ChemData()
7179

7280
# Reaction A <-> B (fast)
73-
chem.add_reaction(reactants=["A"], products=["B"],
81+
chem.add_reaction(reactants="A", products="B",
7482
forward_rate=64., reverse_rate=8.)
7583

7684
# Reaction B <-> C (slow)
77-
chem.add_reaction(reactants=["B"], products=["C"],
85+
chem.add_reaction(reactants="B", products="C",
7886
forward_rate=12., reverse_rate=2.)
7987

8088
print("Number of reactions: ", chem.number_of_reactions())
@@ -86,6 +94,8 @@
8694
# Send a plot of the network of reactions to the HTML log file
8795
chem.plot_reaction_network("vue_cytoscape_2")
8896

97+
# %%
98+
8999
# %% [markdown]
90100
# ## Run the simulation
91101

@@ -102,7 +112,7 @@
102112
# ## Run the reaction
103113

104114
# %%
105-
dynamics.enable_diagnostics() # To save diagnostic information about the call to single_compartment_react()
115+
dynamics.enable_diagnostics() # To save diagnostic information about the call to single_compartment_react()
106116

107117
dynamics.single_compartment_react(initial_step=0.02, duration=0.4,
108118
snapshots={"initial_caption": "1st reaction step",
@@ -115,7 +125,7 @@
115125

116126
# %%
117127
dynamics.plot_history(title="Coupled reactions A <-> B and B <-> C",
118-
colors=['blue', 'orange', 'green'], show_intervals=True)
128+
colors=['darkturquoise', 'orange', 'green'], show_intervals=True)
119129

120130
# %%
121131
dynamics.curve_intersect("A", "B", t_start=0, t_end=0.05)
@@ -130,7 +140,7 @@
130140
dynamics.get_history()
131141

132142
# %%
133-
dynamics.explain_time_advance()
143+
dynamics.diagnostics.explain_time_advance()
134144

135145
# %% [markdown]
136146
# ### Check the final equilibrium
@@ -174,14 +184,13 @@
174184
# %%
175185
# Concentration increments due to reaction 0 (A <-> B)
176186
# Note that [C] is not affected
177-
dynamics.get_diagnostic_rxn_data(rxn_index=0)
187+
dynamics.diagnostics.get_diagnostic_rxn_data(rxn_index=0)
178188

179189
# %%
180-
# Concentration increments due to reaction 1 (B <-> C)
181-
# Note that [A] is not affected.
182-
# Also notice that the 0-th row from the A <-> B reaction isn't seen here, because that step was aborted
183-
# early on, before even getting to THIS reaction
184-
dynamics.get_diagnostic_rxn_data(rxn_index=1)
190+
# Concentration increments due to reaction 1 (B <-> C)
191+
# Also notice that the 0-th row from the A <-> B reaction isn't seen here (start time 0 and step 0.02),
192+
# because that step was aborted early on, BEFORE even getting to THIS reaction
193+
dynamics.diagnostics.get_diagnostic_rxn_data(rxn_index=1)
185194

186195
# %%
187196

@@ -209,10 +218,10 @@
209218

210219
# %%
211220
dynamics2.plot_history(title="Coupled reactions A <-> B and B <-> C , re-run with CONSTANT STEPS",
212-
colors=['blue', 'orange', 'green'], show_intervals=True)
221+
colors=['darkturquoise', 'orange', 'green'], show_intervals=True)
213222

214223
# %% [markdown]
215-
# _(Notice that the vertical steps are now equally spaced - and that there are so many of them that we're only showing some)_
224+
# _(Notice that the vertical steps are now equally spaced - and that there are so many of them that we're only showing 1 every 6)_
216225

217226
# %%
218227
dynamics2.curve_intersect(t_start=0, t_end=0.05, chem1="A", chem2="B")

experiments/reactions_single_compartment/cascade_2_a.ipynb

Lines changed: 126 additions & 81 deletions
Large diffs are not rendered by default.

experiments/reactions_single_compartment/cascade_2_a.py

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# ---
1414

1515
# %% [markdown]
16-
# ## A complex (multistep) reaction `A <-> C` derived from 2 coupled elementary reactions:
16+
# ## A complex (composite/multistep) reaction `A <-> C` derived from 2 coupled elementary reactions:
1717
# ## `A <-> B` and `B <-> C`
1818
# We are given the time evolution of the complex reaction,
1919
# and want to determine whether it can be modeled as an elementary reaction.
@@ -22,17 +22,24 @@
2222
# In PART 2, the time functions generated in Part 1 are taken as a _starting point,_ to explore how to model the composite reaction `A <-> C`
2323
#
2424
# **Background**: please see experiments `cascade_1` and `mystery_reaction_1`
25-
#
26-
# LAST REVISED: June 23, 2024 (using v. 1.0 beta36)
2725

2826
# %%
29-
import set_path # Importing this module will add the project's home directory to sys.path
27+
LAST_REVISED = "July 26, 2024"
28+
LIFE123_VERSION = "1.0.0.beta.38" # Version this experiment is based on
29+
30+
# %%
31+
#import set_path # Using MyBinder? Uncomment this before running the next cell!
32+
# Importing this module will add the project's home directory to sys.path
3033

3134
# %% tags=[]
32-
from experiments.get_notebook_info import get_notebook_basename
35+
#import sys
36+
#sys.path.append("C:/some_path/my_env_or_install") # CHANGE to the folder containing your venv or libraries installation!
37+
# NOTE: If any of the imports below can't find a module, uncomment the lines above, or try: import set_path
3338

34-
from life123 import UniformCompartment
35-
from life123.visualization.plotly_helper import PlotlyHelper
39+
from life123 import check_version, UniformCompartment, PlotlyHelper
40+
41+
# %%
42+
check_version(LIFE123_VERSION)
3643

3744
# %%
3845

@@ -44,7 +51,7 @@
4451

4552
# %% tags=[]
4653
# Instantiate the simulator and specify the chemicals
47-
dynamics = UniformCompartment(names=["A", "B", "C"], preset="mid")
54+
dynamics = UniformCompartment(preset="mid")
4855

4956
# Reaction A <-> B (slower, and with a smaller K)
5057
dynamics.add_reaction(reactants="A", products="B",
@@ -73,7 +80,7 @@
7380
dynamics.plot_history(colors=['darkturquoise', 'orange', 'green'], show_intervals=True)
7481

7582
# %%
76-
dynamics.is_in_equilibrium(tolerance=15)
83+
dynamics.is_in_equilibrium(tolerance=12)
7784

7885
# %%
7986

@@ -87,7 +94,8 @@
8794
# Let's start by taking stock of the actual data (saved during the simulation of part 1):
8895

8996
# %%
90-
df = dynamics.get_history(columns=["SYSTEM TIME", "A", "C", "caption"]) # We're NOT given the intermediary B
97+
# For this analysis, we're NOT given the intermediary B
98+
df = dynamics.get_history(columns=["SYSTEM TIME", "A", "C", "caption"])
9199
df
92100

93101
# %% [markdown]
@@ -111,11 +119,11 @@
111119
# Let's see what happens if we try to do such a linear fit!
112120

113121
# %%
114-
dynamics.estimate_rate_constants(t=t_arr, reactant_conc=A_conc, product_conc=C_conc, reactant_name="A", product_name="C")
122+
dynamics.estimate_rate_constants_simple(t=t_arr, A_conc=A_conc, B_conc=C_conc, reactant_name="A", product_name="C")
115123

116124
# %% [markdown]
117125
# ### The least-square fit is awful : the complex reaction `A <-> C` doesn't seem to be amenable to being modeled as a simple reaction with some suitable rate constants
118-
# Probably not too surprising given our "secret" knowledge from Part 1 that the complex reaction originates from 2 elementary reactions where one doesn't dominate the other one in terms of reaction kinetics
126+
# Probably not too surprising given our "secret" knowledge from Part 1 that the complex reaction originates from 2 elementary reactions where the intermediate product builds up at one point
119127

120128
# %% [markdown]
121129
# ### A glance at the above diagram reveals much-better linear fits, if split into 2 portions, one where A(t) ranges from 0 to about 24, and one from about 24 to 50
@@ -135,8 +143,8 @@
135143

136144
# %% [markdown]
137145
# ### Let's split the `A_conc` and `C_conc` arrays we extracted earlier (with the entire time evolution of, respectively, [A] and [C]) into two parts:
138-
# 1) points numbered 0-47
139-
# 2) points 48-end
146+
# 1) points numbered 0 thru 47
147+
# 2) points 48 - end
140148

141149
# %%
142150
A_conc_early = A_conc[:48]
@@ -161,12 +169,12 @@
161169
# ### I. Let's start with the EARLY region, when t < 0.1
162170

163171
# %%
164-
dynamics.estimate_rate_constants(t=t_arr_early, reactant_conc=A_conc_early, product_conc=C_conc_early,
165-
reactant_name="A", product_name="C")
172+
dynamics.estimate_rate_constants_simple(t=t_arr_early, A_conc=A_conc_early, B_conc=C_conc_early,
173+
reactant_name="A", product_name="C")
166174

167175
# %% [markdown]
168176
# Trying to fit an elementary reaction to that region leads to a **negative** reverse rate constant!
169-
# It's not surprise that an elementary reaction is a good fit, if one observes what happens to the time evolution of the concentrations. Repeating the earlier plot, but only showing `A` and `C` (i.e. hiding the intermediary `B`):
177+
# It's no surprise that an elementary reaction is a good fit, if one observes what happens to the time evolution of the concentrations. Repeating the earlier plot, but only showing `A` and `C` (i.e. hiding the intermediary `B`):
170178

171179
# %%
172180
dynamics.plot_history(colors=['darkturquoise', 'green'], xrange=[0, 0.4], vertical_lines=[0.1],
@@ -177,17 +185,19 @@
177185
# when the reactant `A` is plentiful, the rate of change (gradient) of the product `C` is low - and vice versa.
178186
# Does that look like an elementary reaction in its kinetics? Nope!
179187

188+
# %%
189+
180190
# %% [markdown]
181191
# ### II. And now let's consider the LATE region, when t > 0.1
182192

183193
# %%
184-
dynamics.estimate_rate_constants(t=t_arr_late, reactant_conc=A_conc_late, product_conc=C_conc_late,
185-
reactant_name="A", product_name="C")
194+
dynamics.estimate_rate_constants_simple(t=t_arr_late, A_conc=A_conc_late, B_conc=C_conc_late,
195+
reactant_name="A", product_name="C")
186196

187197
# %% [markdown]
188198
# This time we have an adequate linear fit AND meaningful rate constants : kF of about 8 and kR of about 0. Do those numbers sound familiar? A definite resemblance to the kF=8, kR=2 of the SLOWER elementary reaction `A <-> B`!
189199
#
190-
# #### The slower `A <-> B` reaction dominates the kinetics from about t=0.1
200+
# #### The slower `A <-> B` reaction dominates the kinetics, from about t=0.1 on
191201
#
192202
# Let's see the graph again:
193203

@@ -204,11 +214,16 @@
204214
# The slow link (`A <-> B`) largely determines the kinetics of the supply line.
205215

206216
# %% [markdown]
207-
# ### While it's a well-known Chemistry notion that the slower reaction is the rate-determining step in a chain, we saw in this experiment that the complex reaction could be roughly modeled with the rate constants of the slower reaction only after some time.
217+
# ### While it's a well-known Chemistry notion that the slower reaction is the rate-determining step in a chain, we saw in this experiment that **the complex reaction could be roughly modeled with the rate constants of the slower reaction ONLY AFTER SOME TIME**.
208218

209219
# %% [markdown]
210220
# If we were interested in early transients (for example, if diffusion quickly intervened), we couldn't use that model.
211221

222+
# %% [markdown]
223+
# #### Is that surprising? At early times, compare the inflection of the final product, C, of the composite reaction vs. the inflection of the product of a simple reaction (such as B in experiment `react1`, both appearing in green.)
224+
225+
# %%
226+
212227
# %% [markdown]
213228
# #### In the continuation experiment, `cascade_2_b`, we explore the scenario where the 2 elementary reactions are much more different from each other
214229

experiments/reactions_single_compartment/cascade_2_b.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@
111111
# Let's see what happens if we try to do such a linear fit!
112112

113113
# %%
114-
dynamics.estimate_rate_constants(t=t_arr, reactant_conc=A_conc, product_conc=C_conc, reactant_name="A", product_name="C")
114+
dynamics.estimate_rate_constants_simple(t=t_arr, A_conc=A_conc, B_conc=C_conc, reactant_name="A", product_name="C")
115115

116116
# %% [markdown]
117117
# ### The least-square fit is awful : the complex reaction `A <-> C` doesn't seem to be amenable to being modeled as a simple reaction with some suitable rate constants
@@ -153,8 +153,8 @@
153153
# ### I. Let's start with the EARLY region, when t < 0.028
154154

155155
# %%
156-
dynamics.estimate_rate_constants(t=t_arr_early, reactant_conc=A_conc_early, product_conc=C_conc_early,
157-
reactant_name="A", product_name="C")
156+
dynamics.estimate_rate_constants_simple(t=t_arr_early, A_conc=A_conc_early, B_conc=C_conc_early,
157+
reactant_name="A", product_name="C")
158158

159159
# %% [markdown]
160160
# Just as we saw in experiment `cascade_2_a`, trying to fit an elementary reaction to that region leads to a **negative** reverse rate constant!
@@ -164,8 +164,8 @@
164164
# ### II. And now let's consider the LATE region, when t > 0.028
165165

166166
# %%
167-
dynamics.estimate_rate_constants(t=t_arr_late, reactant_conc=A_conc_late, product_conc=C_conc_late,
168-
reactant_name="A", product_name="C")
167+
dynamics.estimate_rate_constants_simple(t=t_arr_late, A_conc=A_conc_late, B_conc=C_conc_late,
168+
reactant_name="A", product_name="C")
169169

170170
# %% [markdown]
171171
# This time we have an adequate linear fit AND meaningful rate constants : kF of about 8 and kR of about 0. Do those numbers sound familiar? A definite resemblance to the kF=8, kR=2 of the SLOWER elementary reaction `A <-> B`!

experiments/reactions_single_compartment/cascade_2_c.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@
111111
# Let's see what happens if we try to do such a linear fit!
112112

113113
# %%
114-
dynamics.estimate_rate_constants(t=t_arr, reactant_conc=A_conc, product_conc=C_conc, reactant_name="A", product_name="C")
114+
dynamics.estimate_rate_constants_simple(t=t_arr, A_conc=A_conc, B_conc=C_conc, reactant_name="A", product_name="C")
115115

116116
# %% [markdown]
117117
# ### A terrible fit! But it looks like we'll do much better if split it into 3 portions, one where A(t) ranges from 0 to about 0.04, one from about 0.04 to 5, and one from about 5 to 50
@@ -159,8 +159,8 @@
159159
# ### I. Let's start with the EARLY region, when t < 0.028
160160

161161
# %%
162-
dynamics.estimate_rate_constants(t=t_arr_early, reactant_conc=A_conc_early, product_conc=C_conc_early,
163-
reactant_name="A", product_name="C")
162+
dynamics.estimate_rate_constants_simple(t=t_arr_early, A_conc=A_conc_early, B_conc=C_conc_early,
163+
reactant_name="A", product_name="C")
164164

165165
# %% [markdown]
166166
# Just as we saw in experiment `cascade_2_b`, trying to fit an elementary reaction to that region leads to a **negative** reverse rate constant!
@@ -170,8 +170,8 @@
170170
# ### II. Let's now consider the MID region (not seen in earlier experiments in this series), when 0.028 < t < 0.1
171171

172172
# %%
173-
dynamics.estimate_rate_constants(t=t_arr_mid, reactant_conc=A_conc_mid, product_conc=C_conc_mid,
174-
reactant_name="A", product_name="C")
173+
dynamics.estimate_rate_constants_simple(t=t_arr_mid, A_conc=A_conc_mid, B_conc=C_conc_mid,
174+
reactant_name="A", product_name="C")
175175

176176
# %% [markdown]
177177
# For this region, too, trying to fit an elementary reaction to that region leads to a **negative** reverse rate!
@@ -181,8 +181,8 @@
181181
# ### III. And now let's consider the LATE region, when t > 0.1
182182

183183
# %%
184-
dynamics.estimate_rate_constants(t=t_arr_late, reactant_conc=A_conc_late, product_conc=C_conc_late,
185-
reactant_name="A", product_name="C")
184+
dynamics.estimate_rate_constants_simple(t=t_arr_late, A_conc=A_conc_late, B_conc=C_conc_late,
185+
reactant_name="A", product_name="C")
186186

187187
# %% [markdown]
188188
# This time we have an adequate linear fit AND positive rate constants, but a **huge value of kF** relative to that of any of the elementary reactions!

0 commit comments

Comments
 (0)