using CairoMakie
CairoMakie.activate!()

Directed Migration (Chemotaxis)

Directed cell migration along chemical gradients — chemotaxis — is central to wound healing, immune surveillance, embryonic morphogenesis, and cancer metastasis. Neutrophils, for example, follow bacterial N-formylmethionine peptide gradients over millimetre distances to reach infection sites within minutes. In the Potts, chemotaxis is implemented as a bias on the Metropolis copy attempt: moves that advance a cell's centre of mass up the gradient receive an extra energy reduction proportional to χ (chi), the chemotactic sensitivity.

Packages

using PottsToolkit
using MakiePotts

Cell Type

We model a single neutrophil-like cell type migrating through an inert medium toward a chemoattractant source at the right edge of the domain.

Neutrophil = CellType(:Neutrophil)
Medium = CellType(:Medium, is_background = true)

Chemical Gradient Field

The chemoattractant concentration is a static Float32 matrix that lives on the same lattice as the cells. Here we construct a simple left-to-right linear gradient: concentration is 0 at x = 1 and 1 at x = W. In a more realistic scenario this field would be solved from a reaction-diffusion PDE, but a linear field suffices to demonstrate directed migration cleanly.

The field array has dimensions (height, width) = (rows, cols) to match Julia's column-major layout and the Potts grid convention.

const W = 200   # grid width  (x direction — gradient direction)
const H = 100   # grid height (y direction)

gradient_field = Float32[j / W for i in 1:H, j in 1:W]
100×200 Matrix{Float32}:
 0.005  0.01  0.015  0.02  0.025  0.03  …  0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03     0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03     0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03     0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03     0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03  …  0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03     0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03     0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03     0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03     0.98  0.985  0.99  0.995  1.0
 ⋮                                ⋮     ⋱  ⋮                         
 0.005  0.01  0.015  0.02  0.025  0.03     0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03     0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03     0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03     0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03  …  0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03     0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03     0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03     0.98  0.985  0.99  0.995  1.0
 0.005  0.01  0.015  0.02  0.025  0.03     0.98  0.985  0.99  0.995  1.0

Energy Model

ChemotaxisComponent takes a per-cell-type sensitivity χ and the pre-computed field. Positive χ drives cells up the gradient (toward higher concentration). We combine it with a VolumeComponent so cells maintain a target area, and a modest AdhesionComponent so the cell boundary stays compact while migrating.

sys = PottsSystem(
    cell_types = [Medium, Neutrophil],
    penalties = [
        VolumeComponent(Neutrophil => (λ = 8.0f0, target = 200)),
        AdhesionComponent(
            (Neutrophil, Medium) => 20.0f0,
            (Neutrophil, Neutrophil) => 2.0f0
        ),
        ChemotaxisComponent(Neutrophil => 10000.0f0, chemical_field = gradient_field)
    ]
)

Problem

We place a small cluster of neutrophils near the far left edge of a 200 × 100 grid using RectangleLayout. This mimics a transwell assay, where cells must migrate entirely across the grid to reach the chemical source on the right. saveat = 5 gives smooth playback at 24 fps.

prob = PottsProblem(
    sys,
    RectangleLayout(Neutrophil, (10, 45), (20, 55)),
    (H, W);
    tspan = (0, 1500),
    topology = VonNeumannTopology{2}()
)

alg = CheckerboardMetropolis(T = 1.5f0, sweeps_per_step = 10)

sol = solve(prob, alg; saveat = 5)

Visualisation

The "Mean X Position" metric tracks the average x-coordinate of all neutrophil pixels. Over time it should shift rightward as cells migrate up the gradient, providing a quantitative readout of directional bias.

record_potts(
    "03_directed_migration.mp4",
    sol;
    metrics = [
        "N Cells" => u -> u.N_cells[],
        "Mean X Pos" => u -> begin
            grid = Array(u.grid)
            xs = [col for (row, col) in Tuple.(findall(>(0), grid))]
            isempty(xs) ? 0.0 : sum(xs) / length(xs)
        end
    ],
    framerate = 24,
    resolution = (1300, 700)
)

This page was generated using Literate.jl.