ANN: DynamicGrids.jl

It’s been a long time coming, but finally it’s time to announce DynamicGrids.jl - a high performance grid-based modelling framework. It’s been developed at cesar for use in our ecological dispersal modelling, but it should be useful for a whole lot more than that.

DynamicGrids.jl is set up to easily write cellular-automata style rules, but can also simulate a wide range of other behaviors like jumps around the grid and interactions between multiple grids. These can also be composed together into large multi rule/multi grid models that still run really fast. Check out the docs and JuliaCon talk below for more details.

After a bunch of iterations I can also tentatively say it has a nice syntax! - you can specify the math and behaviors you want without much boilerplate. Anyway, check it out:

A live REPL simulation:

(note: the simulation is slowed down for viewing, not sped up! without visuals this model can run at ~1000fps on a regular desktop)

And check out my recent JuliaCon talk for a more thorough description:

PS: I’ve been working pretty hard on this and a bunch of other packages, but I’ve either published and not announced or not even published most of what I’m working on. But I’m in pretty serious COVID lockdown now, so what better time?

I hereby pledge to do a package ANN every week for the next 3 months!

21 Likes

Being too damn lazy to write it myself, I guess there is an easy implementation of the Game of Life here?

Yep: Life().

This is the code to run it in the repl:

using DynamicGrids, Crayons
init = rand(Bool, 150, 200)
output = REPLOutput(init; tspan=1:200, fps=30, color=Crayon(foreground=:red, background=:black, bold=true))
sim!(output, Life())

You can pass in args to Life() if you want other game of life behaviors besides the common 3, (2,3) rule.

But this is a complete script to implement GOL from scratch and run it:

const life_sum_states = 
   (false, false, false, true, false, false, false, false, false), 
   (false, false, true, true, false, false, false, false, false)
life = Neighbors(Moore(1)) do hood, state
    life_sum_states[state + 1][sum(hood) + 1]
end
output = REPLOutput(rand(Bool, 150, 200); tspan=1:200)
sim!(output, life)

So its ok to be lazy

3 Likes

Cool! Works great, thankyou.

1 Like

Hello, I was wondering how you created the grids to have the spacial distribution of particular countries (such as Australia or the US)? Is there an easy way to do this for any given geographical region?

I use GeoData.jl to load rasterized data as an array. It will just pass though the simulation, and your outputs should also be plottable spatial GeoArray.

Normally I’m also running simulations with some kind of auxiliary data that is spatial (like environmental rasters or precalculated growth rates), and should match the size and spatial extent of the the initialization grids. The rules are passed the current cell index, so you can use that to index into the aux array.

In Dispersal.jl (rules you see above in simulations) we use DynamicGrids capability to do this in a generic way. In this growth rule carrycap and rate can be from aux data:

The get method gets the data for the current index from aux data if rule.rate is e.g. Aux{:auxkey}(), or from another grid if it’s Grid{:gridkey}(). It just uses the value directly otherwise.

You pass in the aux array to the simulation Output constructor, using the aux keyword, along with the init conditions and the time span. If the aux data is an AbstractDimensionalArray (like a GeoArray) it will also synchronize the time dimension with the simulation, if it has one. There has been a lot of improvements in this syntax recently, so may not be the best documented.

GeoData.jl also lets you set initial conditions by Lat/Lon -

init[Lon(Near(144)), Lat(Near(-37)] = 1000.0

Or something similar.

1 Like

Thanks! I’ll try this out.