Phase separation¶
Multivalent binding allows molecules to assemble into large connected complexes. Here, A can polymerize through its l and r sites and bind B through its b site. Each B has three A-binding sites and a dimerization site d. We will simulate the system and track the largest connected complex, the maximer.
[ ]:
import base64
import zlib
from pathlib import Path
import pandas as pd
from pykappa import System
Convert concentrations to molecule counts and kinetic rate constants to stochastic rates, and set up the model.
[ ]:
scale = 0.03 * 0.25
volume = 1e-12 * scale
avogadro = 6.022e23
alpha = 0.67e5 * scale
k_on = 1e8
initial_count = int(100 * (1e-9 * avogadro * volume))
g_on = k_on / (avogadro * volume)
g_on_local = alpha * g_on
g_off_medium = 1e-7 * k_on
g_off_weak = 1e-6 * k_on
model = f"""
%init: {initial_count} A(l[.], r[.], b[.])
%init: {initial_count} B(d[.], a1[.], a2[.], a3[.])
A(l[.]), A(r[.]) <-> A(l[1]), A(r[1]) @ {g_on} {{{g_on_local}}}, {g_off_weak}
A(b[.]), B(a1[.]) <-> A(b[1]), B(a1[1]) @ {g_on} {{{g_on_local}}}, {g_off_medium}
A(b[.]), B(a2[.]) <-> A(b[1]), B(a2[1]) @ {g_on} {{{g_on_local}}}, {g_off_medium}
A(b[.]), B(a3[.]) <-> A(b[1]), B(a3[1]) @ {g_on} {{{g_on_local}}}, {g_off_medium}
B(d[.]), B(d[.]) <-> B(d[1]), B(d[1]) @ {g_on} {{{g_on_local}}}, {g_off_medium}
"""
system = System.from_ka(model, seed=78746)
Follow the largest assembly over time, saving system snapshots and its history for later analysis.
[ ]:
output_directory = Path("phase_separation_output")
output_directory.mkdir(parents=True, exist_ok=True)
observations = []
snapshot_index = 0
end_time = system.time + 33.0
observation_time = snapshot_time = system.time
while system.time < end_time:
next_update_time = system.next_update_time
if next_update_time is None:
break
if next_update_time >= observation_time:
maximer = max(system.mixture.components, key=len)
encoded = base64.b64encode(
zlib.compress(maximer.kappa_str_with_agent_ids.encode(), level=6)
).decode()
observations.append((round(observation_time, 3), len(maximer), encoded))
observation_time += 0.005
if next_update_time >= snapshot_time:
system.advance_time_to(snapshot_time)
system.save(output_directory / f"snap_{snapshot_index:06}.pkl")
snapshot_index += 1
snapshot_time += 0.01
system.update()
data = pd.DataFrame(observations, columns=["time", "maximer size", "maximer_strings"])
data.to_pickle(output_directory / "maximer_series.pkl")