E + S <-> ES -> E + P - compared to the Michaelis-Menten model approximation and to the alternative Morrison model.¶enzyme_1_a and enzyme_1_b but with a lavish amount of Enzyme relative to the initial Substrate concentration.¶Experiment enzyme_1_b had a ten-fold increase in enzyme, relative to enzyme_1_a. Here, we do another x10 increase!
Please refer to enzyme_1_a and enzyme_1_b for more details.
the enzyme Adenosine deaminase with the substrate 2,6-Diamino-9-β-D-deoxyribofuranosyl-9-H-purine (same as in experiment enzyme_1)
satisfies the customary Michaelis-Menten assumptions that k1_reverse >> k2_forward
However, the initial concentration values choosen below, do NOT satisfy the expected Michaelis-Menten assumptions that [E] << [S]
Source of kinetic parameters: page 16 of "Analysis of Enzyme Reaction Kinetics, Vol. 1", by F. Xavier Malcata, Wiley, 2023
LAST_REVISED = "Sep. 2, 2025"
LIFE123_VERSION = "1.0.0rc6" # Library version this experiment is based on
#import set_path # Using MyBinder? Uncomment this before running the next cell!
#import sys
#sys.path.append("C:/some_path/my_env_or_install") # CHANGE to the folder containing your venv or libraries installation!
# NOTE: If any of the imports below can't find a module, uncomment the lines above, or try: import set_path
import pandas as pd
from life123 import check_version, ChemData, UniformCompartment, ReactionEnzyme, PlotlyHelper
check_version(LIFE123_VERSION) # To check compatibility
OK
chem_data = ChemData(names=["P", "ES"], plot_colors=["green", "red"])
# Our Enzyme
chem_data.add_chemical(name="Adenosine deaminase", label="E", plot_color="violet")
# Our Substrate
chem_data.add_chemical(name="2,6-Diamino-9-β-D-deoxyribofuranosyl-9-H-purine", label="S", plot_color="darkturquoise")
chem_data.all_chemicals()
| name | label | plot_color | |
|---|---|---|---|
| 0 | P | P | green |
| 1 | ES | ES | red |
| 2 | Adenosine deaminase | E | violet |
| 3 | 2,6-Diamino-9-β-D-deoxyribofuranosyl-9-H-purine | S | darkturquoise |
This part is identical to experiments enzyme_1 and enzyme_2
Source: page 16 of "Analysis of Enzyme Reaction Kinetics, Vol. 1", by F. Xavier Malcata, Wiley, 2023
# Here we use the "slower" preset for the variable steps, a conservative option prioritizing accuracy over speed
uc = UniformCompartment(chem_data=chem_data, preset="slower")
# Enzymatic reaction `E + S <-> ES -> E + P`
uc.add_reaction(reactants="S", products="P", enzyme="E",
k1_F=18., k1_R=100., k2_F=49.)
uc.describe_reactions()
Number of reactions: 1
0: E + S <-> ES -> E + P (Enzymatic reaction) (k1_F = 18.0 / k1_R = 100.0 / k2_F = 49.0 / Temp = 25 C)
Chemicals involved in the above reactions: {"P" (green), "ES" (red), "E" (violet), "S" (darkturquoise)}
S0 = 20.
E0 = 100. # A LAVISH AMOUNT! 10 times more than in experiment `enzyme_2` - and x100 as much as experiment`enzyme_1`
uc.set_conc(conc={"S": S0, "E": E0}) # Small ampount of enzyme `E`, relative to substrate `S`
uc.describe_state()
SYSTEM STATE at Time t = 0: 4 species: Species 0 (P). Conc: 0.0 Species 1 (ES). Conc: 0.0 Species 2 (E). Conc: 100.0 Species 3 (S). Conc: 20.0 Chemicals involved in reactions: ['S', 'E', 'ES', 'P']
# Perform the reactions
uc.single_compartment_react(duration=0.05, initial_step=0.01) # A much-shorter duration than in previous experiments in the series
998 total variable step(s) taken in 2.010 sec
Number of step re-do's because of negative concentrations: 2
Number of step re-do's because of elective soft aborts: 3
Norm usage: {'norm_A': 997, 'norm_B': 1000, 'norm_C': 996, 'norm_D': 996}
System Time is now: 0.050362
uc.plot_history(show_intervals=True,
title_prefix="Almost as much E as S(0)")
plot_pandas() NOTICE: Excessive number of vertical lines (999) - only showing 1 every 7 lines
The general shapes of the various curves is reminiscent of their counterparts in experiment enzyme_1, but notice:
P is being produced on a much-faster timescale than beforeES is no longer puny; in fact, it's a good fraction of the initial [S]ES, at the very beginning, no longer looks instantaneous relatively to the main reaction: by the time ES reaches its maximum value, a non-trivial amount of product P has been producedIn short, the "transient" is not so "transient"!
P?¶# We specify the reaction that involves `P`
rates = uc.add_rate_to_conc_history(rate_name="rxn0_rate_2")
rates
| SYSTEM TIME | P | ES | E | S | step | caption | rxn0_rate_2 | |
|---|---|---|---|---|---|---|---|---|
| 0 | 0.000000 | 0.000000 | 0.000000 | 100.000000 | 20.000000 | Set concentration | 0.000000 | |
| 1 | 0.000010 | 0.000000 | 0.360000 | 99.640000 | 19.640000 | 1 | 1st reaction step | 17.640000 |
| 2 | 0.000011 | 0.000018 | 0.395523 | 99.604477 | 19.604459 | 2 | 19.380617 | |
| 3 | 0.000011 | 0.000019 | 0.397295 | 99.602705 | 19.602686 | 3 | 19.467446 | |
| 4 | 0.000011 | 0.000019 | 0.398181 | 99.601819 | 19.601800 | 4 | 19.510856 | |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 993 | 0.047918 | 17.775498 | 2.101933 | 97.898067 | 0.122569 | 993 | 102.994725 | |
| 994 | 0.048397 | 17.824842 | 2.055364 | 97.944636 | 0.119793 | 994 | 100.712859 | |
| 995 | 0.048881 | 17.873576 | 2.009370 | 97.990630 | 0.117055 | 995 | 98.459109 | |
| 996 | 0.049370 | 17.921695 | 1.963952 | 98.036048 | 0.114353 | 996 | 96.233637 | |
| 997 | 0.049863 | 17.969197 | 1.919114 | 98.080886 | 0.111689 | 997 | 94.036596 |
998 rows × 8 columns
# Let's take a look at how the reaction rate varies with time
PlotlyHelper.plot_pandas(df=rates,
title="Reaction rate, dP/dt, over time",
x_var="SYSTEM TIME", fields="rxn0_rate_2",
x_label="time", y_label="dP/dt")
The initial transient phase is no longer miniscule in duration
for background reference, see: https://vallance.chem.ox.ac.uk/pdfs/KineticsLectureNotes.pdf (p. 20)
rxn = uc.get_single_reaction(0)
rxn.kM # For the data in this experiment, it comes out to (49. + 100.) / 18.
8.277777777777779
rxn.kcat
49.0
vmax = rxn.compute_vmax(E_tot=E0) # kcat * E0
vmax
4900.0
initial_rxn_rate = rxn.compute_rate(S_conc=S0) # (vmax * S0) / (kM + S0)
initial_rxn_rate
3465.6188605108055
vmax and the initial reaction rate are 10x what they were in experiment enzyme_1_a, because we started with an E0 10 times as large¶
# Let's add a column with the rate estimated by the Michaelis-Menten model
rates["Michaelis_rate"] = rxn.compute_rate(S_conc=rates["S"])
rates
| SYSTEM TIME | P | ES | E | S | step | caption | rxn0_rate_2 | Michaelis_rate | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.000000 | 0.000000 | 0.000000 | 100.000000 | 20.000000 | Set concentration | 0.000000 | 3465.618861 | |
| 1 | 0.000010 | 0.000000 | 0.360000 | 99.640000 | 19.640000 | 1 | 1st reaction step | 17.640000 | 3447.122503 |
| 2 | 0.000011 | 0.000018 | 0.395523 | 99.604477 | 19.604459 | 2 | 19.380617 | 3445.270565 | |
| 3 | 0.000011 | 0.000019 | 0.397295 | 99.602705 | 19.602686 | 3 | 19.467446 | 3445.178054 | |
| 4 | 0.000011 | 0.000019 | 0.398181 | 99.601819 | 19.601800 | 4 | 19.510856 | 3445.131800 | |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 993 | 0.047918 | 17.775498 | 2.101933 | 97.898067 | 0.122569 | 993 | 102.994725 | 71.495562 | |
| 994 | 0.048397 | 17.824842 | 2.055364 | 97.944636 | 0.119793 | 994 | 100.712859 | 69.899661 | |
| 995 | 0.048881 | 17.873576 | 2.009370 | 97.990630 | 0.117055 | 995 | 98.459109 | 68.323957 | |
| 996 | 0.049370 | 17.921695 | 1.963952 | 98.036048 | 0.114353 | 996 | 96.233637 | 66.768545 | |
| 997 | 0.049863 | 17.969197 | 1.919114 | 98.080886 | 0.111689 | 997 | 94.036596 | 65.233510 |
998 rows × 9 columns
# Let's see how our computed rate compares with the approximations from the Michaelis-Menten model
PlotlyHelper.plot_pandas(df=rates, x_var="S", fields=["rxn0_rate_2", "Michaelis_rate"],
title="Reaction rate, dP/dt, as a function of Substrate concentration",
y_label="dP/dt", legend_header="Rates",
vertical_lines_to_add=1.65,
colors=["blue", "yellow"])
Let's recall that our reactions started out with [S]=20
The curve overlap is still passable at later times (left part of graph, when [S] drops below about 12), but is rather bad at earlier times (when [S] > 12)
enzyme_1_a¶E than in experiment enzyme_1_a, the Michaelis-Menten model suffers from poor accuracy for an non-trivial early time, because the transient early phase (when ES builds up from zero) is no longer very brief¶[E] << [S]¶
rates["Morrison_rate"] = rxn.compute_rate_morrison(E_tot=E0,
S_tot=rates["S"] + rates["ES"])
rates
| SYSTEM TIME | P | ES | E | S | step | caption | rxn0_rate_2 | Michaelis_rate | Morrison_rate | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.000000 | 0.000000 | 0.000000 | 100.000000 | 20.000000 | Set concentration | 0.000000 | 3465.618861 | 889.979162 | |
| 1 | 0.000010 | 0.000000 | 0.360000 | 99.640000 | 19.640000 | 1 | 1st reaction step | 17.640000 | 3447.122503 | 889.979162 |
| 2 | 0.000011 | 0.000018 | 0.395523 | 99.604477 | 19.604459 | 2 | 19.380617 | 3445.270565 | 889.978385 | |
| 3 | 0.000011 | 0.000019 | 0.397295 | 99.602705 | 19.602686 | 3 | 19.467446 | 3445.178054 | 889.978342 | |
| 4 | 0.000011 | 0.000019 | 0.398181 | 99.601819 | 19.601800 | 4 | 19.510856 | 3445.131800 | 889.978321 | |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 993 | 0.047918 | 17.775498 | 2.101933 | 97.898067 | 0.122569 | 993 | 102.994725 | 71.495562 | 100.506658 | |
| 994 | 0.048397 | 17.824842 | 2.055364 | 97.944636 | 0.119793 | 994 | 100.712859 | 69.899661 | 98.280755 | |
| 995 | 0.048881 | 17.873576 | 2.009370 | 97.990630 | 0.117055 | 995 | 98.459109 | 68.323957 | 96.082240 | |
| 996 | 0.049370 | 17.921695 | 1.963952 | 98.036048 | 0.114353 | 996 | 96.233637 | 66.768545 | 93.911274 | |
| 997 | 0.049863 | 17.969197 | 1.919114 | 98.080886 | 0.111689 | 997 | 94.036596 | 65.233510 | 91.768007 |
998 rows × 10 columns
PlotlyHelper.plot_pandas(df=rates, x_var="S", fields=["rxn0_rate_2", "Michaelis_rate", "Morrison_rate"],
title="Reaction rate, dP/dt, as a function of Substrate concentration",
y_label="dP/dt", legend_header="Rates",
vertical_lines_to_add=1.65,
colors=["blue", "yellow", "orange"])