Example gallery
▶ Open the gallery in a new tab
This is the Pinax script that builds the gallery linked above. It assembles three independent examples as three @pages of one document; because the document has several pages, Pinax renders it as a thumbnail index plus one HTML page per @page. The script is shown verbatim — running it (with the libraries below installed) reproduces the gallery exactly.
Sources: DynamicalModels.jl, LSystems.jl, and a self-contained Ising model.
using Pinax, DynamicalModels, LSystems, Plots, Random, DataVault, StatisticsExample 1 — chaotic attractors (DynamicalModels.jl)
t = collect(0.0:0.02:80.0)
lorenz = ode_solver(RK4, Lorenz(), t, [1.0, 1.0, 1.0])
rossler = ode_solver(RK4, Rossler(), t, [1.0, 1.0, 1.0])
function orbit3d(tr; kw...)
return plot(tr[:, 1], tr[:, 2], tr[:, 3]; legend=false, lw=0.4, size=(420, 360), kw...)
end
function proj(tr, i, j; kw...)
return plot(tr[:, i], tr[:, j]; legend=false, lw=0.4, size=(420, 360), kw...)
endExample 2 — L-system fractals (LSystems.jl)
function lsys(name, iter; kw...)
tile = DEFINED_LSYSTEMS[name]
pos = LSystems.string2positions(tile, grow_string(tile, iter))
return plot(
[p[1] for p in pos],
[p[2] for p in pos];
legend=false,
aspect_ratio=:equal,
axis=false,
ticks=false,
grid=false,
lw=0.6,
size=(380, 380),
kw...,
)
endExample 3 — 2-D Ising Monte Carlo, stored in a DataVault
The temperature sweep is computed once into a DataVault (data + .done markers + log.toml discovery); the figures are read back from the vault, and render(; vault) makes the cache data-aware. Re-running recomputes only the keys still marked :pending.
function ising_sweep!(s, β)
L = size(s, 1)
@inbounds for _ in 1:(L * L)
i, j = rand(1:L), rand(1:L)
nb =
s[mod1(i - 1, L), j] +
s[mod1(i + 1, L), j] +
s[i, mod1(j - 1, L)] +
s[i, mod1(j + 1, L)]
dE = 2 * s[i, j] * nb
(dE <= 0 || rand() < exp(-β * dE)) && (s[i, j] = -s[i, j])
end
return s
end
function run_ising(T; L=32, sweeps=500)
s = fill(Int8(1), L, L) # ordered start → a clean M(T)
β, Ms, frames = 1 / T, Float64[], Matrix{Int8}[]
for k in 1:sweeps
ising_sweep!(s, β)
k > sweeps ÷ 2 && push!(Ms, abs(sum(Int, s)) / (L * L)) # measure over the second half
k % 8 == 0 && push!(frames, copy(s)) # snapshot for the gif
end
return (; M=mean(Ms), frames=frames)
end
Random.seed!(20240620)
Tc = 2 / log(1 + sqrt(2))a tiny config drives the sweep: T names the on-disk directory
isingcfg = joinpath(tempdir(), "ising.toml")
write(
isingcfg,
"""
[study]
project_name = "ising2d"
total_samples = 1
outdir = "ising_data"
[datavault]
path_keys = ["system.T"]
[[paramsets]]
[paramsets.system]
T = [1.6, 2.0, 2.27, 2.5, 3.2]
""",
)
vault = DataVault.Vault(isingcfg; run="mc")
for key in DataVault.keys(vault; status=:pending)
T = Float64(key.params["system.T"])
DataVault.mark_running!(vault, key)
r = run_ising(T)
DataVault.save!(
vault, key, Dict{String,Any}("T" => T, "M" => r.M, "frames" => r.frames)
)
DataVault.mark_done!(vault, key; tag_value=r.M)
endread the sweep back from the vault to build the figures
done = sort(DataVault.keys(vault; status=:done); by=k -> Float64(k.params["system.T"]))
Ts = [DataVault.load(vault, k)["T"] for k in done]
Ms = [DataVault.load(vault, k)["M"] for k in done]
key_tc = done[argmin(abs.(Ts .- Tc))]
isinggif = joinpath("gallery_media", "ising_spins.gif")
if !isfile(isinggif) # restored from the `media` branch in CI; rendered on the fly otherwise
mkpath(dirname(isinggif))
isinganim = @animate for f in DataVault.load(vault, key_tc)["frames"]
heatmap(
f;
c=:grays,
clims=(-1, 1),
axis=false,
ticks=false,
legend=false,
aspect_ratio=1,
size=(360, 360),
)
end
gif(isinganim, isinggif; fps=12)
endThe manuscript: one @page per example
Pinax.reset!(; title="Pinax example gallery")
@page :attractors "Chaotic attractors" begin
@section :lorenz "Lorenz" begin
@desc md"""
The **Lorenz** system $\dot x=\sigma(y-x),\ \dot y=x(\rho-z)-y,\ \dot z=xy-\beta z$
with $\sigma=10,\ \rho=28,\ \beta=8/3$.
"""
@figure orbit3d(lorenz; xlabel="x", ylabel="y", zlabel="z", title="Lorenz 3-D")
@caption md"3-D attractor"
@figure proj(lorenz, 1, 3; xlabel="x", ylabel="z", title="x–z")
@caption md"$x$–$z$ projection"
@figure proj(lorenz, 1, 2; xlabel="x", ylabel="y", title="x–y")
@caption md"$x$–$y$ projection"
end
@section :rossler "Rössler" begin
@desc md"The **Rössler** system $\dot x=-y-z,\ \dot y=x+ay,\ \dot z=b+z(x-c)$ ($a=b=0.2,\ c=5.7$)."
@figure orbit3d(rossler; xlabel="x", ylabel="y", zlabel="z", title="Rössler 3-D")
@caption md"3-D attractor"
@figure proj(rossler, 1, 2; xlabel="x", ylabel="y", title="x–y")
@caption md"$x$–$y$ projection"
end
end
@page :fractals "L-system fractals" begin
@section :koch "Koch & Sierpiński" begin
@desc md"Boundary fractals: Koch $D=\log4/\log3$, Sierpiński $D=\log3/\log2$."
@figure lsys("kochcurve", 4; title="Koch curve", lc=:steelblue)
@caption "Koch curve"
@figure lsys("kocksnowflake", 4; title="Koch snowflake", lc=:steelblue)
@caption "Koch snowflake"
@figure lsys("sierpinskigasket", 6; title="Sierpiński", lc=:seagreen)
@caption "Sierpiński gasket"
end
@section :curves "Dragons, space-filling & plants" begin
@desc md"A dragon, a space-filling curve, and a bracketed branching plant."
@figure lsys("heighwaydragon", 11; title="Heighway dragon", lc=:firebrick)
@caption "Heighway dragon"
@figure lsys("hilbeltpath", 5; title="Hilbert curve", lc=:darkorange)
@caption "Hilbert curve"
@figure lsys("ternarybranching", 6; title="Ternary branching", lc=:seagreen)
@caption "Ternary branching"
end
end
@page :ising "Ising model (Monte Carlo, via DataVault)" begin
@section :magnetization "Magnetization vs temperature" begin
@desc md"Order parameter read back from the vault: $\langle\lvert m\rvert\rangle$ collapses through the Onsager $T_c\approx2.269$ (dashed)."
@figure begin
plot(
Ts,
Ms;
marker=:circle,
lw=1.5,
legend=false,
xlabel="T",
ylabel="⟨|m|⟩",
size=(560, 380),
)
vline!([Tc]; ls=:dash, lc=:red)
end
@caption md"each point is one DataVault key (one temperature)"
end
@section :dynamics "Spin dynamics near Tc" layout = :wide begin
@desc md"The raw spin snapshots stored in the vault, played back as an animation."
@figure isinggif params = key_tc
@caption md"spin lattice near $T_c$ — data-aware (re-rendered only if this key's vault data changes)"
end
endRender: a multi-page document → a thumbnail index + one page per @page. Passing the vault makes the cache data-aware (re-materialize a figure when its vault data changes) and records provenance.
Pinax.render(; out="gallery", vault=vault, study="mc")