A <-> B in 1D, in the presence of membranes with selective permeability to passive transport.¶Eventually, the reaction, the passive membrane transport and the diffusion all come to an equilibrium.
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 life123 import check_version, BioSim1D, ChemData, UniformCompartment
check_version(LIFE123_VERSION)
OK
with two chemicals A and B
chem_data = ChemData(diffusion_rates=[2., 2.], plot_colors=["turquoise", "green"]) # Names "A", "B" automatically assigned
bio = BioSim1D(n_bins=9, chem_data=chem_data)
reactions = bio.get_reactions()
# Reaction A <-> B , 1st-order kinetics, mostly (but not hugely) in the forward direction
reactions.add_reaction(reactants="A", products="B", forward_rate=8., reverse_rate=2.)
reactions.describe_reactions()
Number of reactions: 1 (at temp. 25 C)
0: A <-> B (kF = 8 / kR = 2 / delta_G = -3,436.6 / K = 4) | 1st order in all reactants & products
Set of chemicals involved in the above reactions: {"B" (green), "A" (turquoise)}
bio.set_bin_conc(bin_address=4, chem_label="A", conc=10.) # Set the initial concentration of `A` in middle bin
bio.membranes().set_membranes(membranes=[ (4,5) ]) # By default impermeable
bio.describe_state()
SYSTEM STATE at Time t = 0: 9 bins and 2 chemical species Membranes present: <life123.bio_sim_1d.Membranes1D object at 0x000002B715646040>
| Species | Diff rate | Bin 0 | Bin 1 | Bin 2 | Bin 3 | Bin 4 | Bin 5 | Bin 6 | Bin 7 | Bin 8 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | A | 2.0 | 0.0 | 0.0 | 0.0 | 0.0 | 10.0 | 0.0 | 0.0 | 0.0 | 0.0 |
| 1 | B | 2.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
bio.system_heatmaps()
# Request to save the concentration history at the bin with the initial concentration injection,
# and at a couple of other bins
bio.enable_history(bins=[0, 2, 4], frequency=10, take_snapshot=True)
History enabled for bins [0, 2, 4] and chemicals None (None means 'all')
The membranes are impermeable for now; so, no transport across them occurs
delta_t = 0.002 # This will be our time "quantum" (fixed time step for reactions, passive transport and diffusion) for this experiment
bio.react_diffuse(time_step=delta_t, n_steps=350)
bio.describe_state()
SYSTEM STATE at Time t = 0.7: 9 bins and 2 chemical species Membranes present: <life123.bio_sim_1d.Membranes1D object at 0x000002B715646040>
| Species | Diff rate | Bin 0 | Bin 1 | Bin 2 | Bin 3 | Bin 4 | Bin 5 | Bin 6 | Bin 7 | Bin 8 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | A | 2.0 | 0.0 | 0.0 | 0.0 | 0.0 | 2.006795 | 0.0 | 0.0 | 0.0 | 0.0 |
| 1 | B | 2.0 | 0.0 | 0.0 | 0.0 | 0.0 | 7.993205 | 0.0 | 0.0 | 0.0 | 0.0 |
bio.system_heatmaps()
bio.plot_history_single_bin(bin_address=4)
# Check the reaction equilibrium
bio.reaction_in_equilibrium(bin_address=4, rxn_index=0, explain=True)
A <-> B
Current concentrations: [A] = 2.007 ; [B] = 7.993
1. Ratio of reactant/product concentrations, adjusted for reaction orders: 3.98307
Formula used: [B] / [A]
2. Ratio of forward/reverse reaction rates: 4
Discrepancy between the two values: 0.4233 %
Reaction IS in equilibrium (within 1% tolerance)
True
B from the central bin 4, by making the membrane permeable to it¶while still remaining impermeable to A. Note the system time when this happens:
bio.get_system_time()
0.7
bio.membranes().change_permeability("B", 1.) # Make the membrane permeable to `B` (and only to `B`!)
B across the membrane, and its diffusion outside¶bio.react_diffuse(time_step=delta_t, n_steps=30)
bio.describe_state()
SYSTEM STATE at Time t = 0.76: 9 bins and 2 chemical species Membranes present: <life123.bio_sim_1d.Membranes1D object at 0x000002B715646040>
| Species | Diff rate | Bin 0 | Bin 1 | Bin 2 | Bin 3 | Bin 4 | Bin 5 | Bin 6 | Bin 7 | Bin 8 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | A | 2.0 | 0.000002 | 0.000058 | 0.001457 | 0.020475 | 1.959723 | 0.020475 | 0.001457 | 0.000058 | 0.000002 |
| 1 | B | 2.0 | 0.000022 | 0.000815 | 0.022148 | 0.396922 | 7.156478 | 0.396922 | 0.022148 | 0.000815 | 0.000022 |
bio.system_heatmaps()
Notice what's happening:
B is diffusing away (because we made the membranes permeable to it)A <-> B in bin 4 is moving forward, because we're siphoning off the product BA is re-forming from B, from the reverse reaction
B, and diffusion¶bio.react_diffuse(time_step=delta_t, n_steps=50)
bio.describe_state()
SYSTEM STATE at Time t = 0.86: 9 bins and 2 chemical species Membranes present: <life123.bio_sim_1d.Membranes1D object at 0x000002B715646040>
| Species | Diff rate | Bin 0 | Bin 1 | Bin 2 | Bin 3 | Bin 4 | Bin 5 | Bin 6 | Bin 7 | Bin 8 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | A | 2.0 | 0.000145 | 0.001753 | 0.016262 | 0.091303 | 1.782424 | 0.091303 | 0.016262 | 0.001753 | 0.000145 |
| 1 | B | 2.0 | 0.000896 | 0.011279 | 0.114146 | 0.806955 | 6.132099 | 0.806955 | 0.114146 | 0.011279 | 0.000896 |
bio.system_heatmaps()
Notice how the concentration of A in the central bin 4 is continuing to drop
bio.plot_history_single_bin(bin_address=4, vertical_lines_to_add=[0.7],
title_prefix="Dashed vertical line at time when the membranes were made permeable to `B`")
bio.react_diffuse(time_step=delta_t, n_steps=150)
bio.describe_state()
SYSTEM STATE at Time t = 1.16: 9 bins and 2 chemical species Membranes present: <life123.bio_sim_1d.Membranes1D object at 0x000002B715646040>
| Species | Diff rate | Bin 0 | Bin 1 | Bin 2 | Bin 3 | Bin 4 | Bin 5 | Bin 6 | Bin 7 | Bin 8 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | A | 2.0 | 0.006541 | 0.026681 | 0.097462 | 0.246465 | 1.245765 | 0.246465 | 0.097462 | 0.026681 | 0.006541 |
| 1 | B | 2.0 | 0.027446 | 0.114477 | 0.439127 | 1.285782 | 4.266270 | 1.285782 | 0.439127 | 0.114477 | 0.027446 |
bio.system_heatmaps()
Notice how [A] in bin 4 is continuing to drop, as A keeps turns into B
bio.react_diffuse(time_step=delta_t, n_steps=500)
bio.describe_state()
SYSTEM STATE at Time t = 2.16: 9 bins and 2 chemical species Membranes present: <life123.bio_sim_1d.Membranes1D object at 0x000002B715646040>
| Species | Diff rate | Bin 0 | Bin 1 | Bin 2 | Bin 3 | Bin 4 | Bin 5 | Bin 6 | Bin 7 | Bin 8 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | A | 2.0 | 0.088838 | 0.133297 | 0.213601 | 0.298979 | 0.530569 | 0.298979 | 0.213601 | 0.133297 | 0.088838 |
| 1 | B | 2.0 | 0.355683 | 0.534950 | 0.865235 | 1.262993 | 1.962279 | 1.262993 | 0.865235 | 0.534950 | 0.355683 |
bio.system_heatmaps()
bio.react_diffuse(time_step=delta_t, n_steps=1500)
bio.describe_state()
SYSTEM STATE at Time t = 5.16: 9 bins and 2 chemical species Membranes present: <life123.bio_sim_1d.Membranes1D object at 0x000002B715646040>
| Species | Diff rate | Bin 0 | Bin 1 | Bin 2 | Bin 3 | Bin 4 | Bin 5 | Bin 6 | Bin 7 | Bin 8 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | A | 2.0 | 0.210019 | 0.215086 | 0.223046 | 0.230093 | 0.243514 | 0.230093 | 0.223046 | 0.215086 | 0.210019 |
| 1 | B | 2.0 | 0.840091 | 0.860435 | 0.892771 | 0.924150 | 0.965106 | 0.924150 | 0.892771 | 0.860435 | 0.840091 |
bio.system_heatmaps()
bio.react_diffuse(time_step=delta_t, n_steps=3000)
bio.describe_state()
SYSTEM STATE at Time t = 11.16: 9 bins and 2 chemical species Membranes present: <life123.bio_sim_1d.Membranes1D object at 0x000002B715646040>
| Species | Diff rate | Bin 0 | Bin 1 | Bin 2 | Bin 3 | Bin 4 | Bin 5 | Bin 6 | Bin 7 | Bin 8 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | A | 2.0 | 0.222142 | 0.222175 | 0.222228 | 0.222274 | 0.222362 | 0.222274 | 0.222228 | 0.222175 | 0.222142 |
| 1 | B | 2.0 | 0.888566 | 0.888701 | 0.888915 | 0.889122 | 0.889390 | 0.889122 | 0.888915 | 0.888701 | 0.888566 |
bio.system_heatmaps()
# Check the reaction equilibrium in one of the bins (doesn't matter which one, since all bin concentrations are now the same)
bio.reaction_in_equilibrium(bin_address=0, rxn_index=0, explain=True)
A <-> B
Current concentrations: [A] = 0.2221 ; [B] = 0.8886
1. Ratio of reactant/product concentrations, adjusted for reaction orders: 4
Formula used: [B] / [A]
2. Ratio of forward/reverse reaction rates: 4
Discrepancy between the two values: 1.211e-05 %
Reaction IS in equilibrium (within 1% tolerance)
True
A has diffused to equilibrium across all bins...¶A (trapped in bin 4 by a membrane impermeable to it) has transformed into B, which in turn has passively crossed the membrane, and then diffused across all bins, while at the same time forming A by the reverse reaction in all bins outside the membrane.¶A has "found a way, thru its disguise as B" to indirectly pass thru an impermeable membrane!¶# Let's look at the central bin 4
bio.plot_history_single_bin(bin_address=4, vertical_lines_to_add=[0.7],
title_prefix="Dashed vertical line at time when the membranes were made permeable to `B`;" \
"<br>notice the syphoning off of `B` and the resumed advance of the reaction that consumes `A`.")
# Let's look at a bin CLOSE to the central bin 4
bio.plot_history_single_bin(bin_address=2, vertical_lines_to_add=[0.7],
title_prefix="Dashed vertical line at time when the membranes were made permeable to `B`;" \
"<br>notice the QUICK arrival of `B` by diffusion, and the subsequent formation of `A` by reverse reaction.")
# Let's look at a bin FAR from the central bin 4
bio.plot_history_single_bin(bin_address=0, vertical_lines_to_add=[0.7],
title_prefix="Dashed vertical line at time when the membranes were made permeable to `B`;" \
"<br>notice the SLOW arrival of `B` by diffusion, and the subsequent formation of `A` by reverse reaction.")