enzyme_1_a but with much-larger amounts of Enzyme relative to the initial Substrate concentration.¶E + S <-> ES, and ES -> E + P , using real-life kinetic parameters.¶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 = "Nov. 4, 2024"
LIFE123_VERSION = "1.0.0.rc.0" # 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 ipynbname
import pandas as pd
from life123 import check_version, ChemData, UniformCompartment, ReactionEnz, PlotlyHelper
check_version(LIFE123_VERSION)
OK
chem_data = ChemData(names=["P", "ES"])
# Our Enzyme
chem_data.add_chemical(name="Adenosine deaminase", label="E")
# Our Substrate
chem_data.add_chemical(name="2,6-Diamino-9-β-D-deoxyribofuranosyl-9-H-purine", label="S");
chem_data.all_chemicals()
| name | label | |
|---|---|---|
| 0 | P | P |
| 1 | ES | ES |
| 2 | Adenosine deaminase | E |
| 3 | 2,6-Diamino-9-β-D-deoxyribofuranosyl-9-H-purine | S |
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
# Reaction E + S <-> ES , with 1st-order kinetics,
chem_data.add_reaction(reactants=["E", "S"], products=["ES"],
forward_rate=18., reverse_rate=100.)
# Reaction ES <-> E + P , with 1st-order kinetics, ignoring the reverse reaction
chem_data.add_reaction(reactants=["ES"], products=["E", "P"],
forward_rate=49., reverse_rate=0) # We are ignoring the reverse reaction
chem_data.describe_reactions()
Number of reactions: 2 (at temp. 25 C)
0: E + S <-> ES (kF = 18 / kR = 100 / delta_G = 4,250.9 / K = 0.18) | 1st order in all reactants & products
1: ES <-> E + P (kF = 49 / kR = 0) | 1st order in all reactants & products
Set of chemicals involved in the above reactions: {'P', 'E', 'ES', 'S'}
S0 = 20.
E0 = 10. # 10 times more than in experiment `enzyme_1` ; almost as much enzyme as substrate!
# 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")
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
Set of chemicals involved in reactions: {'P', 'E', 'ES', 'S'}
uc.enable_diagnostics() # To save diagnostic information about the simulation - in particular, the REACTION RATES
# 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`
Some steps were backtracked and re-done, to prevent negative concentrations or overly large concentration changes
615 total step(s) taken
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}
uc.plot_history(colors=['green', 'red', 'violet', 'darkturquoise'], 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?¶rates = uc.get_diagnostics().get_system_history_with_rxn_rates(rxn_index=1) # We specify the reaction that involves `P`
rates
Reaction: ES <-> E + P
| TIME | P | ES | E | S | caption | rate | |
|---|---|---|---|---|---|---|---|
| 0 | 0.000000 | 0.000000 | 0.000000 | 10.000000 | 20.000000 | 0.000000 | |
| 1 | 0.000500 | 0.000000 | 1.800000 | 8.200000 | 18.200000 | 88.200000 | |
| 2 | 0.000750 | 0.022050 | 2.404530 | 7.595470 | 17.573420 | 117.821970 | |
| 3 | 0.000763 | 0.023523 | 2.430084 | 7.569916 | 17.546393 | 119.074126 | |
| 4 | 0.000769 | 0.024267 | 2.442764 | 7.557236 | 17.532969 | 119.695434 | |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 610 | 0.195573 | 19.863565 | 0.081649 | 9.918351 | 0.054786 | 4.000798 | |
| 611 | 0.196593 | 19.867645 | 0.079217 | 9.920783 | 0.053138 | 3.881655 | |
| 612 | 0.197612 | 19.871602 | 0.076858 | 9.923142 | 0.051540 | 3.766032 | |
| 613 | 0.198632 | 19.875442 | 0.074568 | 9.925432 | 0.049990 | 3.653827 | |
| 614 | 0.199652 | 19.879167 | 0.072346 | 9.927654 | 0.048487 | 3.544941 |
615 rows × 7 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="TIME", fields="rate",
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 = ReactionEnz(enzyme="E", substrate="S", product="P",
k1_F=chem_data.get_forward_rate(0), k1_R=chem_data.get_reverse_rate(0),
k2_F=chem_data.get_forward_rate(1))
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
| TIME | P | ES | E | S | caption | rate | Michaelis_rate | |
|---|---|---|---|---|---|---|---|---|
| 0 | 0.000000 | 0.000000 | 0.000000 | 10.000000 | 20.000000 | 0.000000 | 346.561886 | |
| 1 | 0.000500 | 0.000000 | 1.800000 | 8.200000 | 18.200000 | 88.200000 | 336.810743 | |
| 2 | 0.000750 | 0.022050 | 2.404530 | 7.595470 | 17.573420 | 117.821970 | 333.097749 | |
| 3 | 0.000763 | 0.023523 | 2.430084 | 7.569916 | 17.546393 | 119.074126 | 332.933539 | |
| 4 | 0.000769 | 0.024267 | 2.442764 | 7.557236 | 17.532969 | 119.695434 | 332.851850 | |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 610 | 0.195573 | 19.863565 | 0.081649 | 9.918351 | 0.054786 | 4.000798 | 3.221698 | |
| 611 | 0.196593 | 19.867645 | 0.079217 | 9.920783 | 0.053138 | 3.881655 | 3.125424 | |
| 612 | 0.197612 | 19.871602 | 0.076858 | 9.923142 | 0.051540 | 3.766032 | 3.032014 | |
| 613 | 0.198632 | 19.875442 | 0.074568 | 9.925432 | 0.049990 | 3.653827 | 2.941384 | |
| 614 | 0.199652 | 19.879167 | 0.072346 | 9.927654 | 0.048487 | 3.544941 | 2.853451 |
615 rows × 8 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=["rate", "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
| TIME | P | ES | E | S | caption | rate | Michaelis_rate | Morrison_rate | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.000000 | 0.000000 | 0.000000 | 10.000000 | 20.000000 | 0.000000 | 346.561886 | 305.920075 | |
| 1 | 0.000500 | 0.000000 | 1.800000 | 8.200000 | 18.200000 | 88.200000 | 336.810743 | 305.920075 | |
| 2 | 0.000750 | 0.022050 | 2.404530 | 7.595470 | 17.573420 | 117.821970 | 333.097749 | 305.762583 | |
| 3 | 0.000763 | 0.023523 | 2.430084 | 7.569916 | 17.546393 | 119.074126 | 332.933539 | 305.752055 | |
| 4 | 0.000769 | 0.024267 | 2.442764 | 7.557236 | 17.532969 | 119.695434 | 332.851850 | 305.746735 | |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 610 | 0.195573 | 19.863565 | 0.081649 | 9.918351 | 0.054786 | 4.000798 | 3.221698 | 3.645236 | |
| 611 | 0.196593 | 19.867645 | 0.079217 | 9.920783 | 0.053138 | 3.881655 | 3.125424 | 3.536609 | |
| 612 | 0.197612 | 19.871602 | 0.076858 | 9.923142 | 0.051540 | 3.766032 | 3.032014 | 3.431196 | |
| 613 | 0.198632 | 19.875442 | 0.074568 | 9.925432 | 0.049990 | 3.653827 | 2.941384 | 3.328903 | |
| 614 | 0.199652 | 19.879167 | 0.072346 | 9.927654 | 0.048487 | 3.544941 | 2.853451 | 3.229639 |
615 rows × 9 columns
PlotlyHelper.plot_pandas(df=rates, x_var="S", fields=["rate", "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_3, we'll see how the Morrison model is the absolute winner when the amount of enzyme is even larger