From e8aab0dff8fdf8686c98e8fd68ac6e8492d48445 Mon Sep 17 00:00:00 2001 From: Riccardo Foffi Date: Tue, 21 May 2024 11:09:02 +0200 Subject: [PATCH] Api fixes (#86) * add exports and docstrings * update API page * more lightweight plots in examples for docs * update docs * bump patch version --- Project.toml | 2 +- docs/src/api.md | 26 ++++++++-- docs/src/examples/Chemotaxis/1_linear_ramp.md | 4 +- .../examples/Chemotaxis/2_celani_gauss2D.md | 10 ++-- examples/Chemotaxis/1_linear_ramp.jl | 4 +- examples/Chemotaxis/2_celani_gauss2D.jl | 10 ++-- src/fields.jl | 47 ++++++++++++++++-- src/microbe_step.jl | 2 +- src/motility.jl | 49 ++++++++++++++++++- 9 files changed, 130 insertions(+), 24 deletions(-) diff --git a/Project.toml b/Project.toml index fe36947..2dc8c03 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "MicrobeAgents" uuid = "b17a4bac-8667-4a2d-84f7-1883ae0b8dbb" authors = ["Riccardo Foffi and contributors"] -version = "0.4.0" +version = "0.4.1" [deps] Agents = "46ada45e-f475-11e8-01d0-f70cc89e6671" diff --git a/docs/src/api.md b/docs/src/api.md index 1dd3d5c..17e853d 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -1,5 +1,6 @@ # API +## [Microbes](@id Microbes) ```@docs position direction @@ -9,14 +10,31 @@ motilepattern rotational_diffusivity radius state -``` - -## [Utils](@id Utils) -```@docs +motilestate +states +transition_weights +duration +polar +azimuthal distance distancevector random_speed random_velocity +``` + +## [Chemoattractants](@id Chemoattractants) +```@docs +AbstractChemoattractant +GenericChemoattractant +chemoattractant +concentration +gradient +time_derivative +chemoattractant_diffusivity +``` + +## [Utils](@id Utils) +```@docs Analysis.adf_to_matrix Analysis.adf_to_vectors Analysis.unfold diff --git a/docs/src/examples/Chemotaxis/1_linear_ramp.md b/docs/src/examples/Chemotaxis/1_linear_ramp.md index 9f53da8..a5791d1 100644 --- a/docs/src/examples/Chemotaxis/1_linear_ramp.md +++ b/docs/src/examples/Chemotaxis/1_linear_ramp.md @@ -117,8 +117,8 @@ ts = unique(adf.time) .* Δt lw = eachindex(ts) ./ length(ts) .* 3 xmesh = range(0,Lx,length=100) ymesh = range(0,Ly,length=100) -xn = @view x[:,1:15] -yn = @view y[:,1:15] +xn = @view x[1:4:end,1:15] +yn = @view y[1:4:end,1:15] c = concentration_field.(Iterators.product(xmesh,ymesh),Lx,C₀,C₁) heatmap(xmesh, ymesh, c', cbar=false, c=:bone, ratio=1, axis=false, grid=false, xlims=(0,Lx), ylims=(0,Ly) diff --git a/docs/src/examples/Chemotaxis/2_celani_gauss2D.md b/docs/src/examples/Chemotaxis/2_celani_gauss2D.md index 4bc080d..1a30e8e 100644 --- a/docs/src/examples/Chemotaxis/2_celani_gauss2D.md +++ b/docs/src/examples/Chemotaxis/2_celani_gauss2D.md @@ -43,17 +43,17 @@ for _ in 1:300 ) end -nsteps = 1500 +nsteps = 600 adata = [position, bias] adf, = run!(model, nsteps; adata) traj = Analysis.adf_to_matrix(adf, :position) -xmesh = range(0, first(spacesize(model)); length=100) -ymesh = range(0, last(spacesize(model)); length=100) +xmesh = range(0, first(spacesize(model)); length=80) +ymesh = range(0, last(spacesize(model)); length=80) c = [concentration_field(p, p₀, C, σ) for p in Iterators.product(xmesh, ymesh)] heatmap(xmesh, ymesh, c', cbar=false, ratio=1, c=:bone, axis=false) -x = getindex.(traj,1)[end-300:2:end, :] -y = getindex.(traj,2)[end-300:2:end, :] +x = getindex.(traj,1)[end-400:5:end, :] +y = getindex.(traj,2)[end-400:5:end, :] a = axes(x,1) ./ size(x,1) plot!(x, y, lab=false, lims=(0,1000), lw=a.^2, alpha=a, diff --git a/examples/Chemotaxis/1_linear_ramp.jl b/examples/Chemotaxis/1_linear_ramp.jl index b629d45..3bdd2d0 100644 --- a/examples/Chemotaxis/1_linear_ramp.jl +++ b/examples/Chemotaxis/1_linear_ramp.jl @@ -112,8 +112,8 @@ ts = unique(adf.time) .* Δt lw = eachindex(ts) ./ length(ts) .* 3 xmesh = range(0,Lx,length=100) ymesh = range(0,Ly,length=100) -xn = @view x[:,1:15] -yn = @view y[:,1:15] +xn = @view x[1:4:end,1:15] +yn = @view y[1:4:end,1:15] c = concentration_field.(Iterators.product(xmesh,ymesh),Lx,C₀,C₁) heatmap(xmesh, ymesh, c', cbar=false, c=:bone, ratio=1, axis=false, grid=false, xlims=(0,Lx), ylims=(0,Ly) diff --git a/examples/Chemotaxis/2_celani_gauss2D.jl b/examples/Chemotaxis/2_celani_gauss2D.jl index dd272f2..0efeb11 100644 --- a/examples/Chemotaxis/2_celani_gauss2D.jl +++ b/examples/Chemotaxis/2_celani_gauss2D.jl @@ -40,17 +40,17 @@ for _ in 1:300 ) end -nsteps = 1500 +nsteps = 600 adata = [position, bias] adf, = run!(model, nsteps; adata) traj = Analysis.adf_to_matrix(adf, :position) -xmesh = range(0, first(spacesize(model)); length=100) -ymesh = range(0, last(spacesize(model)); length=100) +xmesh = range(0, first(spacesize(model)); length=80) +ymesh = range(0, last(spacesize(model)); length=80) c = [concentration_field(p, p₀, C, σ) for p in Iterators.product(xmesh, ymesh)] heatmap(xmesh, ymesh, c', cbar=false, ratio=1, c=:bone, axis=false) -x = getindex.(traj,1)[end-300:2:end, :] -y = getindex.(traj,2)[end-300:2:end, :] +x = getindex.(traj,1)[end-400:5:end, :] +y = getindex.(traj,2)[end-400:5:end, :] a = axes(x,1) ./ size(x,1) plot!(x, y, lab=false, lims=(0,1000), lw=a.^2, alpha=a, diff --git a/src/fields.jl b/src/fields.jl index 9819f9c..38d0189 100644 --- a/src/fields.jl +++ b/src/fields.jl @@ -1,7 +1,18 @@ -export AbstractChemoattractant, chemoattractant, - concentration, gradient, time_derivative, chemoattractant_diffusivity, - GenericChemoattractant +export AbstractChemoattractant, GenericChemoattractant, chemoattractant +export concentration, gradient, time_derivative, chemoattractant_diffusivity +""" + AbstractChemoattractant{D,T} +Abstract type for chemoattractants. +Requires dimensionality (`D`) and number type (`T`, typically `Float64`). + +The interface is defined by four core functions that operate on `AgentBasedModel`s: +- `chemoattractant`: returns the chemoattractant object +- `concentration`: returns the function for the concentration field +- `gradient`: returns the function for the concentration gradient +- `time_derivative`: returns the function for the concentration ramp +- `chemoattractant_diffusivity`: returns the thermal diffusivity of the chemoattractant +""" abstract type AbstractChemoattractant{D,T} end @inline function concentration(pos::SVector{D,T}, model::ABM) where {D,T} @@ -14,16 +25,46 @@ end time_derivative(chemoattractant(model))(pos, model)::T end +""" + chemoattractant(model) +Returns the chemoattractant object from `model`. +""" chemoattractant(model::ABM) = model.chemoattractant +""" + concentration(model) +Returns the function `f` that defines the concentration field. +The returned function has signature `f(pos, model)` and returns a scalar. +""" concentration(model::ABM) = concentration(chemoattractant(model)) +""" + gradient(model) +Returns the function `f` that defines the gradient of the concentration field. +The returned function has signature `f(pos, model)` and returns a `SVector` +with the same dimensionality as the microbe position `pos`. +""" gradient(model::ABM) = concentration(chemoattractant(model)) +""" + time_derivative(model) +Returns the function `f` that defines the time derivative of the concentration field. +The returned function has signature `f(pos, model)` and returns a scalar. +""" time_derivative(model::ABM) = time_derivative(chemoattractant(model)) +""" + chemoattractant_diffusivity(model) +Returns the thermal diffusivity of the chemoattractant compound. +""" chemoattractant_diffusivity(model::ABM) = chemoattractant_diffusivity(chemoattractant(model)) concentration(c::AbstractChemoattractant) = c.concentration_field gradient(c::AbstractChemoattractant) = c.concentration_gradient time_derivative(c::AbstractChemoattractant) = c.concentration_ramp chemoattractant_diffusivity(c::AbstractChemoattractant) = c.diffusivity +""" + GenericChemoattractant{D,T} <: AbstractChemoattractant{D,T} +Type for a generic chemoattractant field. +Concentration field, gradient and time derivative default to zero values. +Diffusivity defaults to 608 μm^2/s. +""" @kwdef struct GenericChemoattractant{D,T} <: AbstractChemoattractant{D,T} concentration_field::Function = (pos, model) -> zero(T) concentration_gradient::Function = (pos, model) -> zero(SVector{D,T}) diff --git a/src/microbe_step.jl b/src/microbe_step.jl index 8f01ae8..6cb2e9c 100644 --- a/src/microbe_step.jl +++ b/src/microbe_step.jl @@ -1,4 +1,4 @@ -export microbe_step!, microbe_pathfinder_step! +export microbe_step!, microbe_pathfinder_step!, switching_probability """ microbe_step!(microbe, model) diff --git a/src/motility.jl b/src/motility.jl index 242c01a..aa592cc 100644 --- a/src/motility.jl +++ b/src/motility.jl @@ -1,6 +1,8 @@ export MotileState, RunState, TurnState, Run, Tumble, Reverse, Flick, Stop export Motility, RunTumble, RunReverse, RunReverseFlick, RunStop -export update_motilestate!, motilestate, state, states, transition_weights, duration +export update_motilestate! +export motilepattern, motilestate, state, states, transition_weights +export duration, speed, polar, azimuthal export Arccos # from Agents @compact_structs MotileState begin @@ -141,6 +143,11 @@ function RunStop(run_duration, run_speed, stop_duration) end # API +""" + update_motilestate!(microbe, model) +Update the motile state of `microbe` by randomly sampling the next state +according to the prescribed transition weights. +""" function update_motilestate!(microbe::AbstractMicrobe, model::AgentBasedModel) update_motilestate!(motilepattern(microbe), model) end @@ -152,21 +159,61 @@ function update_motilestate!(motility::Motility, model::AgentBasedModel) end update_motilestate!(motility::Motility, j::Int) = (motility.current_state = j) +""" + update_speed!(microbe, model) +Update the speed of `microbe` by randomly sampling from the +speed distribution of the current motile state. +""" function update_speed!(microbe::AbstractMicrobe, model::AgentBasedModel) microbe.speed = rand(abmrng(model), speed(motilestate(microbe))) end +""" + motilestate(microbe) +Return the current motile state of `microbe`. +""" function motilestate(microbe::AbstractMicrobe) m = motilepattern(microbe) states(m)[state(m)] end +""" + state(motility::Motility) +Return the index of active motile state. +""" state(m::Motility) = m.current_state +""" + state(motility::Motility) +Return the list of all motile states. +""" states(m::Motility) = m.motile_states transition_weights(m::Motility) = m.transition_probabilities +""" + transition_weights(motility::Motility, i::Integer) +Return the transition weights from the state with index `i` +towards the other motile states. +""" transition_weights(m::Motility, i::Integer) = transition_weights(m)[i] +""" + duration(motility::Motility) +Return the average unbiased duration of the current motile state. +""" duration(m::Motility) = duration(states(m)[state(m)]) +""" + speed(motility::Motility) +Return the speed distribution of the current motile state. +""" speed(m::Motility) = speed(states(m)[state(m)]) +""" + polar(motility::Motility) +Return the distribution of polar reorientation angles of the +current motile state. +""" polar(m::Motility) = polar(states(m)[state(m)]) +""" + azimuthal(motility::Motility) +Return the distribution of azimuthal reorientation angles of the +current motile state. +""" azimuthal(m::Motility) = azimuthal(states(m)[state(m)]) duration(s::MotileState) = s.duration speed(s::MotileState) = s.speed