Integrating a long(ish) running simulation into Pluto

Hi,

quick question: I have a simulation model that takes in the order of 30-60 seconds to run. (This one if you’re interested).

If I wanted to embed it in a Pluto notebook, what’s the best practice? Can I detach it somehow from Pluto’s normal reactive behaviour? Is there something I can use as a progress indicator?

Graham

I very often write big simulations in Pluto.

The basic tip is that generally your long simulation should be called from a function and not a script (or a begin block or consecutive cells). Now you can have this long function defined in Pluto, but I usually do it differently.

What I commonly do is start Pluto in a local directory with local package manager using
Pkg.activate. I define a module in this directory and I put my non-Pluto bulky code (e.g. your simulation) in there. Because of Revise, I can also update it easily.

Then when I start Pluto the first cells do using Revise and Pkg.activate(".").
I import my local module using MyModule and I access the functions from there.

Can I detach it somehow from Pluto’s normal reactive behaviour?

No. If you change any variables that are used as arguments to call the simulation function in your notebook, then the simulation will rerun. But that’s desirable. If you structure your notebook nicely I don’t see why would that be a problem.

Regarding the progress bar, you can definitely use some inside you simulation function.
See ProgressMeter in Pluto
Pluto will then visualize it.

btw your link doesn’t work.

5 Likes

Using the temporary execution barrier feature in Pluto may help with the reactivity piece: Temporary Execution barrier by lungben · Pull Request #985 · fonsp/Pluto.jl · GitHub

2 Likes

is this the “Disable cell” button ?

3 Likes

See also Base.@async and Threads.@spawn. Those create tasks that are detached from the interative thread. You can then call fetch on the task, which will wait until a result is available, and give you that.

2 Likes

Thanks. That’s interesting and helpful.

FWIW, here is a macro I use if I deliberately want to save some output, in case I need to restart the notebook but don’t want to the simulation to recompute again. Note that this deliberately circumvents Pluto.jl consistency by using external state. So if you change something you need to consider that the values are not recomputed!
The snippet requires JLD2.jl for saving/loading and creates a folder ~/home/results/notebooks/<Notebookname>/ and saves the content of variables there. To save something you do:

@load_or_generate myvar = some_long_ass_computation(p1,p2,p3,p4)

Then each time the cell is run, the macro checks whether there is a save file myvar.jld2 in the corresponding folder and if yes just loads the contents. If you change the computation, you need to delete the file for the computation to refresh!

Code
begin
	NOTEBOOKNAME = basename(replace(@__FILE__, r"\.jl#==#.*" => ""))
	function load_or_generate(f, name)
		path = joinpath(homedir(), "results/notebooks", NOTEBOOKNAME)
		name = endswith(name, ".jld2") ? name : name*".jld2"
		isdir(path) || mkpath(path)
		fullpath = joinpath(path, name)
		isfile(fullpath) && return jldopen(fullpath, "r")["data"]
		data = f()
		jldsave(fullpath; data)
		return data
	end
	macro load_or_generate(expr)
		expr.head != :(=) && error("@load_or_generate is only applicable to simple assignments of the form `a = b`")
		var, rhs = expr.args
		#eqsign == := 
		return quote
			$(esc(var)) = $(esc(:load_or_generate))($(string(var))) do
				return $(esc(rhs))
			end
		end
	end
	md"""Setup save to $(joinpath(homedir(), "results/notebooks", NOTEBOOKNAME))"""
end
2 Likes

While we are at it, and you really need to change something that pointlessly reruns the simulation, you can use memoization tools, to avoid that, e.g., Memoization.jl. But be really careful with the consistency of the results. That’s similar to @abraemer solution, but doesn’t work between new sessions.

2 Likes