using CairoMakie
CairoMakie.activate!()

Cell Sorting

One of the most striking emergent behaviours in multicellular systems is cell sorting: a mixture of two cell types spontaneously segregates into pure-type clusters. This phenomenon was first observed by Holtfreter in amphibian embryo explants and later formalised by Malcolm Steinberg as the Differential Adhesion Hypothesis (DAH). The DAH states that cells behave like immiscible liquids — the tissue configuration that minimises total interfacial energy is the one cells evolve toward. In Potts terms, this means the relative magnitudes of the contact energy J determine whether two cell types mix, partially sort, or completely engulf one another.

Packages

using PottsToolkit
using MakiePotts
using Statistics: mean

Cell Types

We define two epithelial-like cell types, TypeA and TypeB, plus the background Medium. Steinberg's original experiments used pigmented retinal and neural retinal cells; here the two types are abstract but the physics is identical.

TypeA = CellType(:TypeA)
TypeB = CellType(:TypeB)
Medium = CellType(:Medium, is_background = true)

Adhesion Matrix — the Key DAH Parameter

The sorting outcome is governed entirely by the adhesion matrix:

PairJ valueMeaning
A–A2.0strong homotypic cohesion
B–B2.0strong homotypic cohesion
A–B14.0weak heterotypic adhesion
A–Medium16.0cells prefer each other over medium
B–Medium16.0cells prefer each other over medium

Because J(A,B) >> J(A,A) = J(B,B), the system minimises energy by maximising homotypic contacts — the hallmark of complete cell sorting. If J(A,B) were intermediate, you would observe partial engulfment instead.

sys = PottsSystem(
    cell_types = [Medium, TypeA, TypeB],
    penalties = [
        VolumeComponent(
            TypeA => (λ = 5.0f0, target = 150),
            TypeB => (λ = 5.0f0, target = 150)
        ),
        AdhesionComponent(
            (TypeA, TypeA) => 2.0f0,
            (TypeB, TypeB) => 2.0f0,
            (TypeA, TypeB) => 14.0f0,
            (TypeA, Medium) => 16.0f0,
            (TypeB, Medium) => 16.0f0
        )
    ]
)

Problem Setup

A 150 × 150 grid gives enough room for 30 + 30 cells (each ~150 sites) to sort without being critically space-constrained. We run for 1000 MCS so sorting has time to reach a near-equilibrium cluster configuration.

prob = PottsProblem(
    sys,
    Dict(TypeA => 30, TypeB => 30),
    (150, 150);
    tspan = (0, 1000),
    topology = VonNeumannTopology{2}()
)

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

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

Recording with a Cell-Count Metric

The metrics panel shows the number of TypeA and TypeB cells separately so we can confirm that no spurious cell loss or gain occurs during sorting. Cell identity is stored in u.cell_data.cell_types; type indices follow the order given to PottsSystem (TypeA = 1, TypeB = 2).

record_potts(
    "02_cell_sorting.mp4",
    sol;
    metrics = [
        "N TypeA" => u -> begin
            types = Array(u.cell_data.cell_types)
            count(==(1), types[1:u.N_cells[]])
        end,
        "N TypeB" => u -> begin
            types = Array(u.cell_data.cell_types)
            count(==(2), types[1:u.N_cells[]])
        end,
        "Mean Volume" => u -> mean(Array(u.cell_data.volumes)[1:u.N_cells[]])
    ],
    framerate = 24,
    resolution = (1400, 800)
)

This page was generated using Literate.jl.