using MEDYAN
using MEDYANVis
using CairoMakie
using Random
using SmallZarrGroups
Random.seed!(1234);
2. Rock Paper Scissors Stochastic Reaction Diffusion
2. Rock Paper Scissors Stochastic Reaction Diffusion
Inspired by:
https://www.youtube.com/watch?v=TORwMc2AaRE&t=0s
Setup
Open a julia REPL or jupyter notebook using the medyan-tutorial
environment you created in tutorial 1.
Load MEDYAN
with MEDYANVis
for visualization
Declaring agent names
The first step is declaring the names of agents in the system.
This system will contain three diffusing species.
= MEDYAN.AgentNames(;
agent_names =[
diffusingspeciesnames:R, # Rock
:P, # Paper
:S, # Scissors
], )
MEDYAN.AgentNames([:R, :P, :S], Symbol[], Symbol[], Symbol[], Tuple{Symbol, Vector{Symbol}}[], Symbol[], Symbol[], Symbol[])
Defining System
After the agent names are declared, the system can be defined.
This is done by constructing a MEDYAN.SysDef
object from the agent_names
and then mutating it to add parameters, reactions, and callbacks.
= MEDYAN.SysDef(agent_names) s
MEDYAN.SysDef
Diffusing species:
R: 0.0 nm²/s
P: 0.0 nm²/s
S: 0.0 nm²/s
Add a diffusion coefficient of 1E6 nm²/s to all species. All units are based on nm, s, pN
= 1E6
D add_diffusion_coeff!(s, :R, D) # nm²/s
add_diffusion_coeff!(s, :P, D) # nm²/s
add_diffusion_coeff!(s, :S, D) # nm²/s
MEDYAN.SysDef
Diffusing species:
R: 1.0e6 nm²/s
P: 1.0e6 nm²/s
S: 1.0e6 nm²/s
Add reactions, paper “beats” rock, scissors “beats” paper, rock “beats” scissors. In this simulation “beats” means convert to self type.
In this case because the reaction is between two diffusing species, it has units of nm³/s. You can think of this as the volume where if exactly one of each reactant existed the rate would be 1 per second.
= 50.0*(500.0^3) # nm³/s
rate addreaction!(s,
"diffusing.P + diffusing.R --> 2diffusing.P",
rate,1,
)addreaction!(s,
"diffusing.S + diffusing.P --> 2diffusing.S",
rate,1,
)addreaction!(s,
"diffusing.R + diffusing.S --> 2diffusing.R",
rate,1,
)
MEDYAN.SysDef
Diffusing species:
R: 1.0e6 nm²/s
P: 1.0e6 nm²/s
S: 1.0e6 nm²/s
Compartment reactions without callbacks:
"diffusing.P + diffusing.R --> 2diffusing.P" 6.25e9 nm³/s
"diffusing.S + diffusing.P --> 2diffusing.S" 6.25e9 nm³/s
"diffusing.R + diffusing.S --> 2diffusing.R" 6.25e9 nm³/s
Creating a grid
All simulations run in a grid.
This is a box that contains the simulation.
The grid is composed of chemistry voxels, small cubes that can have different counts of the various diffusing species.
Create a 50 by 50 by 1 grid of 500 nm side length voxels.
= 50
L = CubicGrid((L,L,1),500.0) grid
CubicGrid([50, 50, 1], 500.0)
Creating a Context
The Context
is the object the handles the state of a running simulation.
Create a Context
using s
and grid
= MEDYAN.Context(s, grid) c
MEDYAN.Context at time 0.0s in CubicGrid([50, 50, 1], 500.0)
Add agents to the context
The context starts empty.
Distribute 2000 of each species randomly to the chem voxels by mutating the context.
adddiffusingcount_rand!(c, s.diffusing.R, 2000)
adddiffusingcount_rand!(c, s.diffusing.P, 2000)
adddiffusingcount_rand!(c, s.diffusing.S, 2000)
Run chemistry
Run chemistry for 1.0s and visualize the results.
= Visualizer()
vis setvisible!(vis["/Grid"], false)
setvisible!(vis["/Axes"], false)
setvisible!(vis["/Background"], false)
If you open the visualizer in a browser with open(vis)
and run the following you should get an animated version of below.
for i in 1:100
run_chemistry!(c, 0.01)
drawdiffusing!(vis["diffusing"], c.grid, c.chemistryengine, s; size = 0.8)
MEDYANVis.sleep(0.02)
end
Snapshots and plotting
Next we will see how to plot quantities from the simulation.
If you want to analyze a trajectory in detail, or load a system state, one method is to use snapshots. For details see snapshot group reference
MEDYAN.load_snapshot!
can be used to reload snapshots, this is very useful for restarting simulations, or changing the simulation state in a different programming language, but not needed in this tutorial.
SmallZarrGroups.jl can be used to save/load snapshot groups to/from disk, in zip files, or in directories.
Run chemistry for another 1.0s and store a snapshot every 0.01s in a vector.
using SmallZarrGroups
= ZGroup[]
snapshots for i in 1:100
run_chemistry!(c, 0.01)
push!(snapshots, MEDYAN.snapshot(c))
end
Now that the simulation is done the results can be analyzed. For longer simulations, these snapshots should be saved to disk with for example:
save_dir("snapshot$i.zarr.zip",MEDYAN.snapshot(c)) SmallZarrGroups.
to avoid needed to rerun the simulation and to avoid using too much RAM.
= Int[]
total_rocks = Int[]
total_papers = Int[]
total_scissors = Float64[]
times for group in snapshots
= sum(collect(group["diffusingcounts"]);dims=2)
totals push!(times, attrs(group)["time (s)"])
push!(total_rocks, totals[s.diffusing.R])
push!(total_papers, totals[s.diffusing.P])
push!(total_scissors, totals[s.diffusing.S])
end
= lines(times, total_rocks; label="rock")
figure, axis, lineplot lines!(times, total_papers; label="paper")
lines!(times, total_scissors; label="scissors")
lines!(
times,.+total_papers.+total_rocks;
total_scissors="combined")
labelaxislegend()
current_figure()
┌ Warning: Found `resolution` in the theme when creating a `Scene`. The `resolution` keyword for `Scene`s and `Figure`s has been deprecated. Use `Figure(; size = ...` or `Scene(; size = ...)` instead, which better reflects that this is a unitless size and not a pixel resolution. The key could also come from `set_theme!` calls or related theming functions.
└ @ Makie ~/.julia/packages/Makie/pFPBw/src/scenes.jl:238
If you are using VSCode or Jupyter the figure will be displayed.
If you are using the REPL, save the figure as a PDF to view it.
save("tutorial2-plot.pdf", current_figure())