import set_path # Importing this module will add the project's home directory to sys.path
Added 'D:\Docs\- MY CODE\BioSimulations\life123-Win7' to sys.path
from src.modules.chemicals.chem_data import ChemData
from src.modules.reactions.reaction_dynamics import ReactionDynamics
from src.modules.movies.movies import MovieTabular
import pandas as pd
import plotly.express as px
# Initialize the system
chem_data = ChemData(names=["S", "P", "E"])
# Reaction S <-> P , with 1st-order kinetics, favorable thermodynamics in the forward direction,
# and a forward rate that is much slower than it would be with the enzyme - as seen in the next reaction, below
chem_data.add_reaction(reactants="S", products="P",
forward_rate=1., delta_G=-3989.73)
# Reaction S + E <-> P + E , with 1st-order kinetics, and a forward rate that is faster than it was without the enzyme
# Thermodynamically, there's no change from the reaction without the enzyme
chem_data.add_reaction(reactants=["S", "E"], products=["P", "E"],
forward_rate=10., delta_G=-3989.73)
chem_data.describe_reactions() # Notice how the enzyme `E` is noted in the printout below
Number of reactions: 2 (at temp. 25 C)
0: S <-> P (kF = 1 / kR = 0.2 / delta_G = -3,989.7 / K = 5) | 1st order in all reactants & products
1: S + E <-> P + E (kF = 10 / kR = 2 / delta_G = -3,989.7 / K = 5) | Enzyme: E | 1st order in all reactants & products
Set of chemicals involved in the above reactions (not counting enzymes): {'S', 'P'}
Set of enzymes involved in the above reactions: {'E'}
dynamics = ReactionDynamics(chem_data=chem_data)
dynamics.set_conc(conc={"S": 20., "P": 0., "E": 0.},
snapshot=True) # Initially, no enzyme `E`
dynamics.describe_state()
SYSTEM STATE at Time t = 0:
3 species:
Species 0 (S). Conc: 20.0
Species 1 (P). Conc: 0.0
Species 2 (E). Conc: 0.0
Set of chemicals involved in reactions (not counting enzymes): {'S', 'P'}
Set of enzymes involved in reactions: {'E'}
dynamics.set_diagnostics() # To save diagnostic information about the call to single_compartment_react()
# All of these settings are currently close to the default values... but subject to change; set for repeatability
dynamics.set_thresholds(norm="norm_A", low=0.5, high=0.8, abort=1.44)
dynamics.set_thresholds(norm="norm_B", low=0.08, high=0.5, abort=1.5)
dynamics.set_step_factors(upshift=1.2, downshift=0.5, abort=0.4)
dynamics.set_error_step_factor(0.25)
# Perform the reactions
dynamics.single_compartment_react(reaction_duration=4.0,
initial_step=0.1, variable_steps=True, explain_variable_steps=False)
* INFO: the tentative time step (0.1) leads to a least one norm value > its ABORT threshold:
-> will backtrack, and re-do step with a SMALLER delta time, multiplied by 0.4 (set to 0.04) [Step started at t=0, and will rewind there]
35 total step(s) taken
#dynamics.explain_time_advance()
dynamics.plot_history(colors=['darkorange', 'green', 'violet'], show_intervals=True, title_prefix="With ZERO enzyme")
# To save up snapshots of crossover times at various Enzyme concentrations
crossover_points = MovieTabular(parameter_name="Enzyme concentration")
new_crossover = dynamics.curve_intersection("S", "P", t_start=0, t_end=1.0)
crossover_points.store(par=dynamics.get_chem_conc("E"),
data_snapshot = {"crossover time": new_crossover[0]})
new_crossover
Min abs distance found at data row: 20
(0.7435259497128707, 9.999999999999996)
# Verify that the reaction has reached equilibrium
dynamics.is_in_equilibrium(tolerance=2)
0: S <-> P
Final concentrations: [S] = 3.372 ; [P] = 16.63
1. Ratio of reactant/product concentrations, adjusted for reaction orders: 4.93068
Formula used: [P] / [S]
2. Ratio of forward/reverse reaction rates: 5.00000519021548
Discrepancy between the two values: 1.387 %
Reaction IS in equilibrium (within 2% tolerance)
1: S + E <-> P + E
Final concentrations: [S] = 3.372 ; [P] = 16.63 ; [E] = 0
Reaction IS in equilibrium because it can't proceed in either direction due to zero concentrations in both some reactants and products!
D:\Docs\- MY CODE\BioSimulations\life123-Win7\src\modules\reactions\reaction.py:468: RuntimeWarning: invalid value encountered in double_scalars
True
dynamics = ReactionDynamics(chem_data=chem_data) # A brand-new simulation
dynamics.set_conc(conc={"S": 20., "P": 0., "E": 0.2},
snapshot=True) # A tiny bit of enzyme `E`
dynamics.describe_state()
SYSTEM STATE at Time t = 0:
3 species:
Species 0 (S). Conc: 20.0
Species 1 (P). Conc: 0.0
Species 2 (E). Conc: 0.2
Set of chemicals involved in reactions (not counting enzymes): {'S', 'P'}
Set of enzymes involved in reactions: {'E'}
dynamics.single_compartment_react(reaction_duration=1.5,
initial_step=0.05, variable_steps=True, explain_variable_steps=False)
* INFO: the tentative time step (0.05) leads to a least one norm value > its ABORT threshold:
-> will backtrack, and re-do step with a SMALLER delta time, multiplied by 0.4 (set to 0.02) [Step started at t=0, and will rewind there]
Some steps were backtracked and re-done, to prevent negative concentrations or excessively large concentration changes
33 total step(s) taken
#dynamics.explain_time_advance()
dynamics.plot_history(colors=['darkorange', 'green', 'violet'], show_intervals=True, title_prefix="With a tiny amount of enzyme")
new_crossover = dynamics.curve_intersection("S", "P", t_start=0, t_end=0.5)
crossover_points.store(par=dynamics.get_chem_conc("E"),
data_snapshot = {"crossover time": new_crossover[0]})
new_crossover
Min abs distance found at data row: 17
(0.24710707675961718, 10.0)
# Verify that the reaction has reached equilibrium
dynamics.is_in_equilibrium()
0: S <-> P
Final concentrations: [S] = 3.34 ; [P] = 16.66
1. Ratio of reactant/product concentrations, adjusted for reaction orders: 4.98745
Formula used: [P] / [S]
2. Ratio of forward/reverse reaction rates: 5.00000519021548
Discrepancy between the two values: 0.2511 %
Reaction IS in equilibrium (within 1% tolerance)
1: S + E <-> P + E
Final concentrations: [S] = 3.34 ; [P] = 16.66 ; [E] = 0.2
1. Ratio of reactant/product concentrations, adjusted for reaction orders: 4.98745
Formula used: ([P][E]) / ([S][E])
2. Ratio of forward/reverse reaction rates: 5.00000519021548
Discrepancy between the two values: 0.2511 %
Reaction IS in equilibrium (within 1% tolerance)
True
dynamics = ReactionDynamics(chem_data=chem_data) # A brand-new simulation
dynamics.set_conc(conc={"S": 20., "P": 0., "E": 1.},
snapshot=True) # A more substantial amount of enzyme `E`
dynamics.describe_state()
SYSTEM STATE at Time t = 0:
3 species:
Species 0 (S). Conc: 20.0
Species 1 (P). Conc: 0.0
Species 2 (E). Conc: 1.0
Set of chemicals involved in reactions (not counting enzymes): {'S', 'P'}
Set of enzymes involved in reactions: {'E'}
dynamics.single_compartment_react(reaction_duration=0.5,
initial_step=0.02, variable_steps=True, explain_variable_steps=False)
* INFO: the tentative time step (0.02) leads to a least one norm value > its ABORT threshold:
-> will backtrack, and re-do step with a SMALLER delta time, multiplied by 0.4 (set to 0.008) [Step started at t=0, and will rewind there]
* INFO: the tentative time step (0.008) leads to a least one norm value > its ABORT threshold:
-> will backtrack, and re-do step with a SMALLER delta time, multiplied by 0.4 (set to 0.0032) [Step started at t=0, and will rewind there]
Some steps were backtracked and re-done, to prevent negative concentrations or excessively large concentration changes
39 total step(s) taken
#dynamics.explain_time_advance()
dynamics.plot_history(colors=['darkorange', 'green', 'violet'], show_intervals=True, title_prefix="With a more substantial amount of enzyme")
new_crossover = dynamics.curve_intersection("S", "P", t_start=0, t_end=0.5)
crossover_points.store(par=dynamics.get_chem_conc("E"),
data_snapshot = {"crossover time": new_crossover[0]})
new_crossover
Min abs distance found at data row: 21
(0.06769827987210066, 10.0)
# Verify that the reaction has reached equilibrium
dynamics.is_in_equilibrium(explain=False)
True
dynamics = ReactionDynamics(chem_data=chem_data) # A brand-new simulation
dynamics.set_conc(conc={"S": 20., "P": 0., "E": 5.},
snapshot=True) # A good amount of enzyme `E`
dynamics.describe_state()
SYSTEM STATE at Time t = 0:
3 species:
Species 0 (S). Conc: 20.0
Species 1 (P). Conc: 0.0
Species 2 (E). Conc: 5.0
Set of chemicals involved in reactions (not counting enzymes): {'S', 'P'}
Set of enzymes involved in reactions: {'E'}
dynamics.single_compartment_react(reaction_duration=0.2,
initial_step=0.01, variable_steps=True, explain_variable_steps=False)
* INFO: the tentative time step (0.01) leads to a least one norm value > its ABORT threshold:
-> will backtrack, and re-do step with a SMALLER delta time, multiplied by 0.4 (set to 0.004) [Step started at t=0, and will rewind there]
* INFO: the tentative time step (0.004) leads to a least one norm value > its ABORT threshold:
-> will backtrack, and re-do step with a SMALLER delta time, multiplied by 0.4 (set to 0.0016) [Step started at t=0, and will rewind there]
Some steps were backtracked and re-done, to prevent negative concentrations or excessively large concentration changes
36 total step(s) taken
#dynamics.explain_time_advance()
dynamics.plot_history(colors=['darkorange', 'green', 'violet'], show_intervals=True, title_prefix="With a good amount of enzyme")
new_crossover = dynamics.curve_intersection("S", "P", t_start=0, t_end=0.5)
crossover_points.store(par=dynamics.get_chem_conc("E"),
data_snapshot = {"crossover time": new_crossover[0]})
new_crossover
Min abs distance found at data row: 15
(0.014494267066104415, 10.000000000000002)
# Verify that the reaction has reached equilibrium
dynamics.is_in_equilibrium(explain=False)
True
dynamics = ReactionDynamics(chem_data=chem_data) # A brand-new simulation
dynamics.set_conc(conc={"S": 20., "P": 0., "E": 20.},
snapshot=True) # A lot of enzyme `E`
dynamics.describe_state()
SYSTEM STATE at Time t = 0:
3 species:
Species 0 (S). Conc: 20.0
Species 1 (P). Conc: 0.0
Species 2 (E). Conc: 20.0
Set of chemicals involved in reactions (not counting enzymes): {'S', 'P'}
Set of enzymes involved in reactions: {'E'}
dynamics.single_compartment_react(reaction_duration=0.05,
initial_step=0.005, variable_steps=True, explain_variable_steps=False)
* INFO: the tentative time step (0.005) leads to a least one norm value > its ABORT threshold:
-> will backtrack, and re-do step with a SMALLER delta time, multiplied by 0.4 (set to 0.002) [Step started at t=0, and will rewind there]
* INFO: the tentative time step (0.002) leads to a least one norm value > its ABORT threshold:
-> will backtrack, and re-do step with a SMALLER delta time, multiplied by 0.4 (set to 0.0008) [Step started at t=0, and will rewind there]
* INFO: the tentative time step (0.0008) leads to a least one norm value > its ABORT threshold:
-> will backtrack, and re-do step with a SMALLER delta time, multiplied by 0.4 (set to 0.00032) [Step started at t=0, and will rewind there]
Some steps were backtracked and re-done, to prevent negative concentrations or excessively large concentration changes
39 total step(s) taken
#dynamics.explain_time_advance()
dynamics.plot_history(colors=['darkorange', 'green', 'violet'], show_intervals=True, title_prefix="With a lot of enzyme")
new_crossover = dynamics.curve_intersection("S", "P", t_start=0, t_end=0.5)
crossover_points.store(par=dynamics.get_chem_conc("E"),
data_snapshot = {"crossover time": new_crossover[0]})
new_crossover
Min abs distance found at data row: 17
(0.003687634618431487, 9.999999999999996)
# Verify that the reaction has reached equilibrium
dynamics.is_in_equilibrium(explain=False)
True
dynamics = ReactionDynamics(chem_data=chem_data) # A brand-new simulation
dynamics.set_conc(conc={"S": 20., "P": 0., "E": 30.},
snapshot=True) # A very large amount of enzyme `E`
dynamics.describe_state()
SYSTEM STATE at Time t = 0:
3 species:
Species 0 (S). Conc: 20.0
Species 1 (P). Conc: 0.0
Species 2 (E). Conc: 30.0
Set of chemicals involved in reactions (not counting enzymes): {'S', 'P'}
Set of enzymes involved in reactions: {'E'}
dynamics.single_compartment_react(reaction_duration=0.02,
initial_step=0.001, variable_steps=True, explain_variable_steps=False)
* INFO: the tentative time step (0.001) leads to a least one norm value > its ABORT threshold:
-> will backtrack, and re-do step with a SMALLER delta time, multiplied by 0.4 (set to 0.0004) [Step started at t=0, and will rewind there]
* INFO: the tentative time step (0.0004) leads to a least one norm value > its ABORT threshold:
-> will backtrack, and re-do step with a SMALLER delta time, multiplied by 0.4 (set to 0.00016) [Step started at t=0, and will rewind there]
Some steps were backtracked and re-done, to prevent negative concentrations or excessively large concentration changes
36 total step(s) taken
#dynamics.explain_time_advance()
dynamics.plot_history(colors=['darkorange', 'green', 'violet'], show_intervals=True, title_prefix="With a very large amount of enzyme")
new_crossover = dynamics.curve_intersection("S", "P", t_start=0, t_end=0.5)
crossover_points.store(par=dynamics.get_chem_conc("E"),
data_snapshot = {"crossover time": new_crossover[0]})
new_crossover
Min abs distance found at data row: 18
(0.002465868124424963, 10.0)
# Verify that the reaction has reached equilibrium
dynamics.is_in_equilibrium(explain=False)
True
dynamics = ReactionDynamics(chem_data=chem_data) # A brand-new simulation
dynamics.set_conc(conc={"S": 20., "P": 0., "E": 100.},
snapshot=True) # A lavish amount of enzyme `E`
dynamics.describe_state()
SYSTEM STATE at Time t = 0:
3 species:
Species 0 (S). Conc: 20.0
Species 1 (P). Conc: 0.0
Species 2 (E). Conc: 100.0
Set of chemicals involved in reactions (not counting enzymes): {'S', 'P'}
Set of enzymes involved in reactions: {'E'}
dynamics.single_compartment_react(reaction_duration=0.02,
initial_step=0.0005, variable_steps=True, explain_variable_steps=False)
* INFO: the tentative time step (0.0005) leads to a least one norm value > its ABORT threshold:
-> will backtrack, and re-do step with a SMALLER delta time, multiplied by 0.4 (set to 0.0002) [Step started at t=0, and will rewind there]
* INFO: the tentative time step (0.0002) leads to a least one norm value > its ABORT threshold:
-> will backtrack, and re-do step with a SMALLER delta time, multiplied by 0.4 (set to 8e-05) [Step started at t=0, and will rewind there]
40 total step(s) taken
#dynamics.explain_time_advance()
dynamics.plot_history(colors=['darkorange', 'green', 'violet'], show_intervals=True, title_prefix="With a huge amount of enzyme")
new_crossover = dynamics.curve_intersection("S", "P", t_start=0, t_end=0.5)
crossover_points.store(par=dynamics.get_chem_conc("E"),
data_snapshot = {"crossover time": new_crossover[0]})
new_crossover
Min abs distance found at data row: 15
(0.0007383445224719487, 10.0)
# Verify that the reaction has reached equilibrium
dynamics.is_in_equilibrium(explain=False)
True
dynamics = ReactionDynamics(chem_data=chem_data) # A brand-new simulation
dynamics.set_conc(conc={"S": 20., "P": 0., "E": 2000.},
snapshot=True) # A lavish amount of enzyme `E`
dynamics.describe_state()
SYSTEM STATE at Time t = 0:
3 species:
Species 0 (S). Conc: 20.0
Species 1 (P). Conc: 0.0
Species 2 (E). Conc: 2000.0
Set of chemicals involved in reactions (not counting enzymes): {'S', 'P'}
Set of enzymes involved in reactions: {'E'}
dynamics.single_compartment_react(reaction_duration=0.0015,
initial_step=0.000005, variable_steps=True, explain_variable_steps=False)
* INFO: the tentative time step (5e-06) leads to a least one norm value > its ABORT threshold:
-> will backtrack, and re-do step with a SMALLER delta time, multiplied by 0.4 (set to 2e-06) [Step started at t=0, and will rewind there]
48 total step(s) taken
#dynamics.explain_time_advance()
dynamics.plot_history(chemicals=['S', 'P'],
colors=['darkorange', 'green', 'violet'], show_intervals=True, title_prefix="With a LAVISH amount of enzyme (NOT shown)")
Note: NOT showing the enzyme (concentration 2,000) in the graph, to avoid squishing down the other curves!
new_crossover = dynamics.curve_intersection("S", "P", t_start=0, t_end=0.5)
crossover_points.store(par=dynamics.get_chem_conc("E"),
data_snapshot = {"crossover time": new_crossover[0]})
new_crossover
Min abs distance found at data row: 20
(3.7174487975074336e-05, 10.0)
# Verify that the reaction has reached equilibrium
dynamics.is_in_equilibrium()
0: S <-> P
Final concentrations: [S] = 3.313 ; [P] = 16.69
1. Ratio of reactant/product concentrations, adjusted for reaction orders: 5.03748
Formula used: [P] / [S]
2. Ratio of forward/reverse reaction rates: 5.00000519021548
Discrepancy between the two values: 0.7496 %
Reaction IS in equilibrium (within 1% tolerance)
1: S + E <-> P + E
Final concentrations: [S] = 3.313 ; [P] = 16.69 ; [E] = 2,000
1. Ratio of reactant/product concentrations, adjusted for reaction orders: 5.03748
Formula used: ([P][E]) / ([S][E])
2. Ratio of forward/reverse reaction rates: 5.00000519021548
Discrepancy between the two values: 0.7496 %
Reaction IS in equilibrium (within 1% tolerance)
True
df = crossover_points.get_dataframe()
df
| Enzyme concentration | crossover time | caption | |
|---|---|---|---|
| 0 | 0.0 | 0.743526 | |
| 1 | 0.2 | 0.247107 | |
| 2 | 1.0 | 0.067698 | |
| 3 | 5.0 | 0.014494 | |
| 4 | 20.0 | 0.003688 | |
| 5 | 30.0 | 0.002466 | |
| 6 | 100.0 | 0.000738 | |
| 7 | 2000.0 | 0.000037 |
# Let's plot just the first 6 data points, to avoid squishing the curve
px.line(data_frame=df.loc[0:5],
x="Enzyme concentration", y=["crossover time"],
title="Time of crossover of [S] and [P] , as a function of Enzyme concentration (first 6 points)",
labels={"value":"Crossover time"},
line_shape="spline")
# Same plot, but with log scales on both axes (all data points used this time,
# but the value E = 0 is automatically dropped by the graphic function because of the log scale)
px.line(data_frame=df,
x="Enzyme concentration", y=["crossover time"],
log_x=True, log_y=True,
title="Time of crossover of [S] and [P] , as a function of Enzyme conc (log scales on both axes)",
labels={"value":"Crossover time"},
line_shape="spline")