Spatial growth

BranchingProcesses.tissue_growth! — Function
tissue_growth!(tree::BranchingProcessNode, ndim::Int)

Assign spatial grid positions to all nodes in the branching process tree using an adaptation of Algorithm 3 from Kerstjens et al. (2022).

The algorithm places the root node at the origin of an unbounded ndim-dimensional integer grid and processes all internal nodes (those with children) in order of ascending division time. For a node with n daughters, n - 1 independent random ray directions are drawn (one per extra daughter needed). For each ray:

  • Consecutive occupied slots along the ray are pushed one step outward (farthest first).
  • The freed adjacent slot is occupied by the next daughter.

The last daughter always replaces the parent at its current position. Using independent rays for each daughter (rather than all daughters along the same ray) better preserves the approximate radial symmetry of the growing tissue.

Time-consistency: The algorithm removes dead leaf nodes (cells with no offspring whose lifetime ended before the current division time) from the grid before processing each division. This ensures that at any intermediate time t, the alive cells occupy positions that are consistent with the spatial growth process up to time t.

Full position histories are recorded in the position_history field of each BranchingProcessNode. Use tissue_position to query the position of a node at any specific time t.

Arguments

  • tree: The root BranchingProcessNode of the branching process tree.
  • ndim: Spatial dimension; must be 1, 2, or 3.

Notes

  • Modifies the position and position_history fields of all BranchingProcessNodes in-place. After this call every node has position set to a Vector{Int} of length ndim (the final position), and position_history set to a sorted vector of (time, position) pairs.
  • The root is placed at the all-zeros origin.
  • When ndim = 1, step directions are drawn uniformly from {-1, +1}. For ndim = 2 or 3, directions are drawn uniformly from the continuous sphere surface and then discretised to the nearest integer lattice vector.
  • Division time ordering is respected: nodes that divide earlier are processed first, so their spatial positions are assigned before those of later-dividing nodes.

See also: tissue_position, ConstantRateBranchingProblem, BranchingProcessSolution

tissue_growth!(sol::BranchingProcessSolution)
tissue_growth!(sol::BranchingProcessSolution, ndim::Int)

Assign spatial grid positions to all BranchingProcessNodes in the solution tree using an adaptation of Algorithm 3 from Kerstjens et al. (2022).

When ndim is not provided it is read from sol.prob.ndim. If sol.prob.ndim is 0 (no spatial dimension configured in the problem), an ArgumentError is thrown and ndim must be passed explicitly.

Time-consistency: After this call, each node's position field holds its final position, while the position_history field stores the full time-indexed position sequence. Use tissue_position to query positions at any intermediate time t.

Arguments

  • sol: A BranchingProcessSolution whose tree nodes will receive spatial positions.
  • ndim: Spatial dimension (1, 2, or 3). Optional when sol.prob.ndim is already set to a valid value.

Returns

The input sol, mutated in-place (all node position and position_history fields populated).

Examples

using BranchingProcesses, StochasticDiffEq

f(u, p, t) = 0.0
g(u, p, t) = 0.5
prob = SDEProblem(f, g, 1.0, (0.0, 3.0))

# Spatial dimension baked into the problem — positions assigned automatically by solve
bp = ConstantRateBranchingProblem(prob, 1.0, 2; ndim=2)
sol = solve(bp, EM(); dt=0.01)

# Standalone: build a tree without spatial info, then assign positions afterwards
bp2 = ConstantRateBranchingProblem(prob, 1.0, 2)
sol2 = solve(bp2, EM(); dt=0.01)
tissue_growth!(sol2, 2)      # pass ndim explicitly

See also: tissue_growth!(tree, ndim), tissue_position, ConstantRateBranchingProblem, BranchingProcessNode

BranchingProcesses.tissue_position — Function
tissue_position(node::BranchingProcessNode, t::Real)

Return the spatial grid position of node at time t, using the position history recorded by tissue_growth!.

The position is defined as the last recorded position whose timestamp is ≤ t. Returns nothing if t is before the node's first recorded position (i.e. before its birth), or if tissue_growth! has not been run yet (falling back to node.position).

This function enables time-consistent queries: for any time t, the positions returned for nodes alive at t (i.e. node.sol.t[1] ≤ t ≤ node.sol.t[end]) reflect the spatial layout of the tissue at time t, not at the final time of the simulation.

Arguments

Returns

A Vector{Int} with the grid position at time t, or nothing if no position has been recorded at or before t.

Examples

using BranchingProcesses, StochasticDiffEq

f(u, p, t) = 0.0
g(u, p, t) = 0.5
prob = SDEProblem(f, g, 1.0, (0.0, 5.0))
bp = ConstantRateBranchingProblem(prob, 1.0, 2; ndim=2)
sol = solve(bp, EM(); dt=0.01)

# Query position of each alive node at t=2.0
for node in PreOrderDFS(sol.tree)
    if node.sol.t[1] <= 2.0 <= node.sol.t[end]
        println(tissue_position(node, 2.0))
    end
end

See also: tissue_growth!