E + S <-> ES -> E + P - compared to the Michaelis-Menten model approximation and to the alternative Morrison model.¶enzyme_1_a but with much-larger amounts of Enzyme relative to the initial Substrate concentration.¶Please refer to enzyme_1_a 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 experiment enzyme_1
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 = 10. # 10 times more than in experiment `enzyme_1` ; almost as much enzyme as substrate!
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: 10.0 Species 3 (S). Conc: 20.0 Chemicals involved in reactions: ['E', 'ES', 'S', 'P']
# Perform the reactions
uc.single_compartment_react(duration=0.2, initial_step=0.05) # A much-shorter duration than the 1.5 of experiment `enzyme_1`
615 total variable step(s) taken in 1.290 sec
Number of step re-do's because of negative concentrations: 2
Number of step re-do's because of elective soft aborts: 1
Norm usage: {'norm_A': 513, 'norm_B': 516, 'norm_C': 512, 'norm_D': 512}
System Time is now: 0.20067
uc.plot_history(show_intervals=True,
title_prefix="Almost as much E as S(0)")
plot_pandas() NOTICE: Excessive number of vertical lines (616) - only showing 1 every 5 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 | 10.000000 | 20.000000 | Set concentration | 0.000000 | |
| 1 | 0.000500 | 0.000000 | 1.800000 | 8.200000 | 18.200000 | 1 | 1st reaction step | 88.200000 |
| 2 | 0.000750 | 0.022050 | 2.404530 | 7.595470 | 17.573420 | 2 | 117.821970 | |
| 3 | 0.000763 | 0.023523 | 2.430084 | 7.569916 | 17.546393 | 3 | 119.074126 | |
| 4 | 0.000769 | 0.024267 | 2.442764 | 7.557236 | 17.532969 | 4 | 119.695434 | |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 610 | 0.195573 | 19.863565 | 0.081649 | 9.918351 | 0.054786 | 610 | 4.000798 | |
| 611 | 0.196593 | 19.867645 | 0.079217 | 9.920783 | 0.053138 | 611 | 3.881655 | |
| 612 | 0.197612 | 19.871602 | 0.076858 | 9.923142 | 0.051540 | 612 | 3.766032 | |
| 613 | 0.198632 | 19.875442 | 0.074568 | 9.925432 | 0.049990 | 613 | 3.653827 | |
| 614 | 0.199652 | 19.879167 | 0.072346 | 9.927654 | 0.048487 | 614 | 3.544941 |
615 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
490.0
initial_rxn_rate = rxn.compute_rate(S_conc=S0) # (vmax * S0) / (kM + S0)
initial_rxn_rate
346.56188605108053
vmax and the initial reaction rate are 10x what they were in experiment enzyme_1, 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 | 10.000000 | 20.000000 | Set concentration | 0.000000 | 346.561886 | |
| 1 | 0.000500 | 0.000000 | 1.800000 | 8.200000 | 18.200000 | 1 | 1st reaction step | 88.200000 | 336.810743 |
| 2 | 0.000750 | 0.022050 | 2.404530 | 7.595470 | 17.573420 | 2 | 117.821970 | 333.097749 | |
| 3 | 0.000763 | 0.023523 | 2.430084 | 7.569916 | 17.546393 | 3 | 119.074126 | 332.933539 | |
| 4 | 0.000769 | 0.024267 | 2.442764 | 7.557236 | 17.532969 | 4 | 119.695434 | 332.851850 | |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 610 | 0.195573 | 19.863565 | 0.081649 | 9.918351 | 0.054786 | 610 | 4.000798 | 3.221698 | |
| 611 | 0.196593 | 19.867645 | 0.079217 | 9.920783 | 0.053138 | 611 | 3.881655 | 3.125424 | |
| 612 | 0.197612 | 19.871602 | 0.076858 | 9.923142 | 0.051540 | 612 | 3.766032 | 3.032014 | |
| 613 | 0.198632 | 19.875442 | 0.074568 | 9.925432 | 0.049990 | 613 | 3.653827 | 2.941384 | |
| 614 | 0.199652 | 19.879167 | 0.072346 | 9.927654 | 0.048487 | 614 | 3.544941 | 2.853451 |
615 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=12.1,
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)
We no longer have the striking overlap we had in experiment enzyme_1
E than in experiment enzyme_1, 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 | 10.000000 | 20.000000 | Set concentration | 0.000000 | 346.561886 | 305.920075 | |
| 1 | 0.000500 | 0.000000 | 1.800000 | 8.200000 | 18.200000 | 1 | 1st reaction step | 88.200000 | 336.810743 | 305.920075 |
| 2 | 0.000750 | 0.022050 | 2.404530 | 7.595470 | 17.573420 | 2 | 117.821970 | 333.097749 | 305.762583 | |
| 3 | 0.000763 | 0.023523 | 2.430084 | 7.569916 | 17.546393 | 3 | 119.074126 | 332.933539 | 305.752055 | |
| 4 | 0.000769 | 0.024267 | 2.442764 | 7.557236 | 17.532969 | 4 | 119.695434 | 332.851850 | 305.746735 | |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 610 | 0.195573 | 19.863565 | 0.081649 | 9.918351 | 0.054786 | 610 | 4.000798 | 3.221698 | 3.645236 | |
| 611 | 0.196593 | 19.867645 | 0.079217 | 9.920783 | 0.053138 | 611 | 3.881655 | 3.125424 | 3.536609 | |
| 612 | 0.197612 | 19.871602 | 0.076858 | 9.923142 | 0.051540 | 612 | 3.766032 | 3.032014 | 3.431196 | |
| 613 | 0.198632 | 19.875442 | 0.074568 | 9.925432 | 0.049990 | 613 | 3.653827 | 2.941384 | 3.328903 | |
| 614 | 0.199652 | 19.879167 | 0.072346 | 9.927654 | 0.048487 | 614 | 3.544941 | 2.853451 | 3.229639 |
615 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=12.1,
colors=["blue", "yellow", "orange"])
In the next experiment, enzyme_1_c, we'll see how the Morrison model is the absolute winner when the amount of enzyme is even larger