2A + 5B <-> 4C + 3D, with 1st-order kinetics for each species, taken to equilibrium¶Diffusion not applicable (just 1 bin)
LAST_REVISED = "June 6, 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
from experiments.get_notebook_info import get_notebook_basename
from life123 import BioSim1D, ChemData, check_version
from life123 import HtmlLog as log
from life123 import GraphicLog
check_version(LIFE123_VERSION)
OK
# Initialize the HTML logging
log_file = get_notebook_basename() + ".log.htm" # Use the notebook base filename for the log file
# Set up the use of some specified graphic (Vue) components
GraphicLog.config(filename=log_file,
components=["vue_cytoscape_2"],
extra_js="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.21.2/cytoscape.umd.js")
-> Output will be LOGGED into the file 'reaction_6.log.htm'
# Initialize the system; NOTE: Diffusion not applicable (just 1 bin)
chem_data = ChemData(names=["A", "B", "C", "D"], plot_colors=['navy', 'cyan', 'red', 'orange'])
bio = BioSim1D(n_bins=1, chem_data=chem_data)
bio.set_all_uniform_concentrations( [4., 7., 5., 2.] )
bio.describe_state()
SYSTEM STATE at Time t = 0: 1 bins and 4 chemical species
| Species | Diff rate | Bin 0 | |
|---|---|---|---|
| 0 | A | None | 4.0 |
| 1 | B | None | 7.0 |
| 2 | C | None | 5.0 |
| 3 | D | None | 2.0 |
# Specify the reaction
reactions = bio.get_reactions()
# Reaction 2A + 5B <-> 4C + 3D , with 1st-order kinetics for each species
reactions.add_reaction(reactants=[(2,"A",1) , (5,"B",1)], products=[(4,"C",1) , (3,"D",1)],
forward_rate=5., reverse_rate=2.)
reactions.describe_reactions()
Number of reactions: 1 (at temp. 25 C)
0: 2 A + 5 B <-> 4 C + 3 D (kF = 5 / kR = 2 / delta_G = -2,271.4 / K = 2.5) | 1st order in all reactants & products
Set of chemicals involved in the above reactions: {"D" (orange), "C" (red), "A" (navy), "B" (cyan)}
# Send a header and a plot to the HTML log file
log.write("Reaction 2 A + 5 B <-> 4 C + 3 D",
style=log.h2)
reactions.plot_reaction_network("vue_cytoscape_2")
Reaction 2 A + 5 B 4 C + 3 D [GRAPHIC ELEMENT SENT TO LOG FILE `reaction_6.log.htm`]
# Let's enable history - by default for all chemicals and all bins
bio.enable_history(take_snapshot=True, caption="Initial state")
History enabled for bins None and chemicals None (None means 'all')
bio.get_bin_history(bin_address=0)
| SYSTEM TIME | A | B | C | D | caption | |
|---|---|---|---|---|---|---|
| 0 | 0.0 | 4.0 | 7.0 | 5.0 | 2.0 | Initial state |
bio.get_reaction_handler().enable_diagnostics() # To save diagnostic information for the simulation run, below
# First step
bio.react(time_step=0.001, n_steps=1)
bio.describe_state()
System Time is now: 0.001 SYSTEM STATE at Time t = 0.001: 1 bins and 4 chemical species
| Species | Diff rate | Bin 0 | |
|---|---|---|---|
| 0 | A | None | 3.76 |
| 1 | B | None | 6.40 |
| 2 | C | None | 5.48 |
| 3 | D | None | 2.36 |
Early in the reaction : [A] = 3.76 , [B] = 6.4 , [C] = 5.48 , [D] = 2.36
bio.get_bin_history(bin_address=0)
| SYSTEM TIME | A | B | C | D | caption | |
|---|---|---|---|---|---|---|
| 0 | 0.000 | 4.00 | 7.0 | 5.00 | 2.00 | Initial state |
| 1 | 0.001 | 3.76 | 6.4 | 5.48 | 2.36 |
# Numerous more steps
bio.react(time_step=0.001, n_steps=40)
bio.describe_state()
System Time is now: 0.041 SYSTEM STATE at Time t = 0.041: 1 bins and 4 chemical species
| Species | Diff rate | Bin 0 | |
|---|---|---|---|
| 0 | A | None | 2.802846 |
| 1 | B | None | 4.007114 |
| 2 | C | None | 7.394309 |
| 3 | D | None | 3.795732 |
Consistent with the 5/2 ratio of forward/reverse rates (and the 1st order reactions),
the systems settles in the following equilibrium:
[A] = 2.80284552 , [B] = 4.00711381 , [C] = 7.39430896 , [D] = 3.79573172
# Verify that the reaction has reached equilibrium
bio.get_reaction_handler().is_in_equilibrium(rxn_index=0, conc=bio.bin_snapshot(bin_address = 0))
2 A + 5 B <-> 4 C + 3 D
Current concentrations: [A] = 2.803 ; [B] = 4.007 ; [C] = 7.394 ; [D] = 3.796
1. Ratio of reactant/product concentrations, adjusted for reaction orders: 2.49898
Formula used: ([C][D]) / ([A][B])
2. Ratio of forward/reverse reaction rates: 2.5
Discrepancy between the two values: 0.04092 %
Reaction IS in equilibrium (within 1% tolerance)
True
df = bio.get_bin_history(bin_address=0)
df
| SYSTEM TIME | A | B | C | D | caption | |
|---|---|---|---|---|---|---|
| 0 | 0.000 | 4.000000 | 7.000000 | 5.000000 | 2.000000 | Initial state |
| 1 | 0.001 | 3.760000 | 6.400000 | 5.480000 | 2.360000 | |
| 2 | 0.002 | 3.571091 | 5.927728 | 5.857818 | 2.643363 | |
| 3 | 0.003 | 3.421344 | 5.553360 | 6.157312 | 2.867984 | |
| 4 | 0.004 | 3.301981 | 5.254952 | 6.396039 | 3.047029 | |
| 5 | 0.005 | 3.206419 | 5.016047 | 6.587162 | 3.190372 | |
| 6 | 0.006 | 3.129645 | 4.824113 | 6.740709 | 3.305532 | |
| 7 | 0.007 | 3.067794 | 4.669486 | 6.864411 | 3.398309 | |
| 8 | 0.008 | 3.017854 | 4.544634 | 6.964293 | 3.473220 | |
| 9 | 0.009 | 2.977457 | 4.443643 | 7.045085 | 3.533814 | |
| 10 | 0.010 | 2.944734 | 4.361834 | 7.110532 | 3.582899 | |
| 11 | 0.011 | 2.918195 | 4.295487 | 7.163611 | 3.622708 | |
| 12 | 0.012 | 2.896651 | 4.241627 | 7.206699 | 3.655024 | |
| 13 | 0.013 | 2.879148 | 4.197870 | 7.241704 | 3.681278 | |
| 14 | 0.014 | 2.864920 | 4.162300 | 7.270160 | 3.702620 | |
| 15 | 0.015 | 2.853348 | 4.133370 | 7.293304 | 3.719978 | |
| 16 | 0.016 | 2.843932 | 4.109831 | 7.312135 | 3.734101 | |
| 17 | 0.017 | 2.836269 | 4.090672 | 7.327463 | 3.745597 | |
| 18 | 0.018 | 2.830029 | 4.075073 | 7.339942 | 3.754956 | |
| 19 | 0.019 | 2.824948 | 4.062370 | 7.350104 | 3.762578 | |
| 20 | 0.020 | 2.820809 | 4.052024 | 7.358381 | 3.768786 | |
| 21 | 0.021 | 2.817438 | 4.043596 | 7.365123 | 3.773843 | |
| 22 | 0.022 | 2.814692 | 4.036729 | 7.370617 | 3.777962 | |
| 23 | 0.023 | 2.812454 | 4.031135 | 7.375092 | 3.781319 | |
| 24 | 0.024 | 2.810630 | 4.026576 | 7.378739 | 3.784054 | |
| 25 | 0.025 | 2.809144 | 4.022861 | 7.381711 | 3.786283 | |
| 26 | 0.026 | 2.807933 | 4.019834 | 7.384133 | 3.788100 | |
| 27 | 0.027 | 2.806947 | 4.017366 | 7.386107 | 3.789580 | |
| 28 | 0.028 | 2.806142 | 4.015355 | 7.387716 | 3.790787 | |
| 29 | 0.029 | 2.805487 | 4.013717 | 7.389027 | 3.791770 | |
| 30 | 0.030 | 2.804952 | 4.012381 | 7.390095 | 3.792572 | |
| 31 | 0.031 | 2.804517 | 4.011292 | 7.390966 | 3.793225 | |
| 32 | 0.032 | 2.804162 | 4.010405 | 7.391676 | 3.793757 | |
| 33 | 0.033 | 2.803872 | 4.009681 | 7.392255 | 3.794191 | |
| 34 | 0.034 | 2.803637 | 4.009092 | 7.392727 | 3.794545 | |
| 35 | 0.035 | 2.803444 | 4.008611 | 7.393111 | 3.794833 | |
| 36 | 0.036 | 2.803288 | 4.008219 | 7.393424 | 3.795068 | |
| 37 | 0.037 | 2.803160 | 4.007900 | 7.393680 | 3.795260 | |
| 38 | 0.038 | 2.803056 | 4.007640 | 7.393888 | 3.795416 | |
| 39 | 0.039 | 2.802971 | 4.007428 | 7.394058 | 3.795543 | |
| 40 | 0.040 | 2.802902 | 4.007255 | 7.394196 | 3.795647 | |
| 41 | 0.041 | 2.802846 | 4.007114 | 7.394309 | 3.795732 |
A and B get depleted, while C and D get produced.
2A + 5B <-> 4C + 3D
# We'll check the first two arrays of concentrations, from the run's history
arr0 = bio.get_reaction_handler().get_historical_concentrations(row=0, df=df)
arr1 = bio.get_reaction_handler().get_historical_concentrations(row=1, df=df)
arr0, arr1
(array([4., 7., 5., 2.], dtype=float32), array([3.76, 6.4 , 5.48, 2.36], dtype=float32))
# Check that the changes in the first reaction step conform to the stoichiometry
bio.get_reaction_handler().get_diagnostics().stoichiometry_checker(rxn_index=0,
conc_arr_before = arr0,
conc_arr_after = arr1)
True
arr1 - arr0
array([-0.24000001, -0.5999999 , 0.48000002, 0.3599999 ], dtype=float32)
Indeed, the change in [A] is -2 x 0.12, and the change in [B] is -5 X 0.12,
while the change in [C] is 4 x 0.12, and the change in [D] is 3 X 0.12
(arr1 - arr0) / 0.12 # 0.12 is the "moles of reactions" change
array([-2.0000002, -4.9999995, 4.0000005, 2.9999993], dtype=float32)
bio.plot_history_single_bin(bin_address=0,
title_prefix="Reaction `2A + 5B <-> 4C + 3D`")