Analysis

Analyze output from a PCMM project. It is anticipated that this will eventually be split off into its own module or even package. Possibly with loader.jl.

PhysiCellModelManager.connectedComponentsFunction
connectedComponents(snapshot::PhysiCellSnapshot, graph=:neighbors; include_cell_type_names=:all_in_one, exclude_cell_type_names::String[], include_dead::Bool=false)
connectedComponents(simulation::Simulation, index, graph; kwargs...)
connectedComponents(pcmm_output::PCMMOutput{Simulation}, args...; kwargs...)

Find the connected components of a graph in a PhysiCell snapshot.

The computation can be done on subsets of cells based on their cell types.

Arguments

  • snapshot::PhysiCellSnapshot: The snapshot to analyze
  • graph: The graph data to use (default is :neighbors); must be one of :neighbors, :attachments, or :spring_attachments; can also be a string
  • simulation::Simulation: The simulation object to analyze
  • index: The index of the snapshot to analyze (can be an integer or a symbol)
  • pcmm_output::PCMMOutput{Simulation}: The output of a PCMM simulation run to analyze

Keyword Arguments

  • include_cell_type_names: The cell types to include in the analysis (default is :all_in_one). Full list of options:
    • :all - compute connected components for all cell types individually
    • :all_in_one - compute connected components for all cell types together
    • "cell_type_1" - compute connected components only for the cells of type cell_type_1
    • ["cell_type_1", "cell_type_2"] - compute connected components for the cells of type cell_type_1 and cell_type_2 separately
    • [["cell_type_1", "cell_type_2"]] - compute connected components for the cells of type cell_type_1 and cell_type_2 together
    • [["cell_type_1", "cell_type_2"], "cell_type_3"] - compute connected components for the cells of type cell_type_1 and cell_type_2 together, and for the cells of type cell_type_3 separately
  • exclude_cell_type_names: The cell types to exclude from the analysis (default is String[]); can be a single string or a vector of strings
  • include_dead: Whether to include dead cells in the analysis (default is false)

Returns

A dictionary in which each key is one of the following:

  • cell type name (String)
  • list of cell type names (Vector{String})
  • list of cell type names followed by the symbol :include_dead (Vector{Any})

For each key, the value is a list of connected components in the graph. Each component is represented as a vector of vertex labels. As of this writing, the vertex labels are the simple AgentID class that wraps the cell ID.

source
PhysiCellModelManager.motilityStatisticsMethod
motilityStatistics(simulation_id::Integer[; direction=:any])
motilityStatistics(simulation::Simulation[; direction=:any])
motilityStatistics(pcmm_output::PCMMOutput{Simulation}[; direction=:any])

Return the mean speed, distance traveled, and time alive for each cell in the simulation, broken down by cell type in the case of cell type transitions.

The time is counted from when the cell first appears in simulation output until it dies or the simulation ends, whichever comes first. If the cell transitions to a new cell type during the simulation, the time is counted for each cell type separately. Each cell type taken on by a given cell will be a key in the dictionary returned at that entry.

Arguments

  • simulation_id::Integer: The ID of the PhysiCell simulation. A Simulation object can also be passed in.
  • simulation::Simulation: The simulation object.
  • pcmm_output::PCMMOutput{Simulation}: The output of a PCMM simulation run.

Keyword Arguments

  • direction::Symbol: The direction to compute the mean speed. Can be :x, :y, :z, or :any (default). If :x, for example, the mean speed is calculated using only the x component of the cell's movement.

Returns

  • AgentDict{Dict{String, NamedTuple}}: An AgentDict, i.e., one entry per cell in the simulation. Each dictionary has keys for each cell type taken on by the cell. The values are NamedTuples with fields :time, :distance, and :speed.

Example

ms = motilityStatistics(1) # an AgentDict{Dict{String, NamedTuple}}, one per cell in the simulation
ms[1]["epithelial"] # NamedTuple with fields :time, :distance, :speed for the cell with ID 1 in the simulation corresponding to its time as an `epithelial` cell
ms[1]["mesenchymal"].time # time spent as a `mesenchymal` cell for the cell with ID 1 in the simulation
ms[1]["mesenchymal"].distance # distance traveled as a `mesenchymal` cell for the cell with ID 1 in the simulation
ms[1]["mesenchymal"].speed # mean speed as a `mesenchymal` cell for the cell with ID 1 in the simulation
source
PhysiCellModelManager.PCMMPCFResultType
PCMMPCFResult

A struct to hold the results of the pair correlation function (PCF) calculation.

The start and end radii for each annulus are stored in the radii field. Thus, there is one more radius than there are annuli, i.e. length(radii) == size(g, 1) + 1. Each column of g corresponds to a time point in the time field, hence size(g, 2) == length(time).

Fields

  • time::Vector{Float64}: The time points at which the PCF was calculated.
  • pcf_result::PairCorrelationFunction.PCFResult: The result of the PCF calculation.

Example

using PairCorrelationFunction
time = 12.0
radii = [0.0, 1.0, 2.0]
g = [0.5, 1.2]
PhysiCellModelManager.PCMMPCFResult(time, PairCorrelationFunction.PCFResult(radii, g))
# output
PCMMPCFResult:
  Time: 12.0
  Radii: 0.0 - 2.0 with 2 annuli, Δr = 1.0
  g: 0.5 - 1.2 (min - max)
using PairCorrelationFunction
time = [12.0; 24.0; 36.0]
radii = [0.0, 1.0, 2.0]
g = [0.5 0.6 0.4; 1.2 1.15 1.4]
PhysiCellModelManager.PCMMPCFResult(time, PairCorrelationFunction.PCFResult(radii, g))
# output
PCMMPCFResult:
  Time: 12.0 - 36.0 (n = 3)
  Radii: 0.0 - 2.0 with 2 annuli, Δr = 1.0
  g: 0.4 - 1.4 (min - max)
source
PairCorrelationFunction.pcfFunction
pcf(S::AbstractPhysiCellSequence, center_cell_types, target_cell_types=center_cell_types; include_dead::Union{Bool,Tuple{Bool,Bool}}=false, dr::Float64=20.0)
pcf(simulation::Simulation[, index], args...; kwargs...)
pcf(simulation_id::Integer[, index], args...; kwargs...)
pcf(pcmm_output::PCMMOutput{Simulation}[, index], args...; kwargs...)

Calculate the pair correlation function (PCF) between two sets of cell types in a PhysiCell simulation snapshot or sequence.

The center_cell_types and target_cell_types can be strings or vectors of strings. This will compute one PCF rather than one for each pair of (center, target) cell types, i.e., all centers are compared to all targets. If omitted, the targetcelltypes will be the same as the centercelltypes, i.e., not a cross-PCF. The include_dead argument can be a boolean or a tuple of booleans to indicate whether to include the dead centers and/or targets, respectively. The dr argument specifies the bin size (thickness of each annulus) for the PCF calculation.

In the signatures with an index, if it is provided then a single snapshot is analyzed, otherwise the entire sequence is analyzed. The index must be an integer or either the symbol :initial or :final.

Arguments

  • S::AbstractPhysiCellSequence: A PhysiCellSnapshot or PhysiCellSequence object.
  • center_cell_types: The cell type name(s) to use as the center of the PCF.
  • target_cell_types: The cell type name(s) to use as the target of the PCF.
  • simulation::Simulation: The simulation object.
  • simulation_id::Integer: The ID of the PhysiCell simulation.
  • pcmm_output::PCMMOutput{Simulation}: The output of a PCMM simulation run.
  • index::Union{Integer, Symbol}: The index of the snapshot to analyze (can be an integer or the symbol :initial or :final).

Keyword Arguments

  • include_dead::Union{Bool,Tuple{Bool,Bool}}: Whether to include dead cells in the PCF calculation. If a tuple, the first element indicates whether to include dead centers and the second element indicates whether to include dead targets.
  • dr::Float64: The bin size for the PCF calculation.

Alternate methods

  • pcf(simulation::Simulation, index::Union{Integer, Symbol}, center_cell_types, target_cell_types=center_cell_types; kwargs...): Calculate the PCF for a specific snapshot in a simulation.
  • pcf(simulation_id::Integer, index::Union{Integer, Symbol}, center_cell_types, target_cell_types=center_cell_types; kwargs...): Calculate the PCF for a specific snapshot in a simulation by ID.
  • pcf(simulation_id::Integer, center_cell_types, target_cell_types=center_cell_types; kwargs...): Calculate the PCF for all snapshots in a simulation by ID.
  • pcf(simulation::Simulation, center_cell_types, target_cell_types=center_cell_types; kwargs...): Calculate the PCF for all snapshots in a simulation.

Returns

A PCMMPCFResult object containing the time, radii, and g values of the PCF. Regardless of the type of S, the time and radii will always be vectors. If S is a snapshot, the g values will be a vector of the PCF. If S is a sequence, the g values will be a (length(radii)-1 x length(time)) matrix of the PCF.

source
PhysiCellModelManager.MonadPopulationTimeSeriesType
MonadPopulationTimeSeries <: AbstractPopulationTimeSeries

Holds the data for a monad's population time series.

Note: unlike SimulationPopulationTimeSeries, this type does not save the data to a file.

Examples

mpts = MonadPopulationTimeSeries(1)
mpts = MonadPopulationTimeSeries(Monad(1))

Fields

  • monad_id::Int: The ID of the monad.
  • monad_length::Int: The number of simulations in the monad.
  • time::Vector{Real}: The time points of the population time series.
  • cell_count::Dict{String, NamedTuple}: A dictionary where keys are cell type names and values are NamedTuples with fields :counts, :mean, and :std.
source
PhysiCellModelManager.SimulationPopulationTimeSeriesType
SimulationPopulationTimeSeries <: AbstractPopulationTimeSeries

Holds the data for a simulation's population time series.

If constructed using a Simulation or an Integer (representing a simulation ID), it will save the time series inside the simulations/simulation_id/summary/ folder. It will also look for previously computed time series there to avoid recomputing them.

Examples

spts = SimulationPopulationTimeSeries(1) # first checks if the population time series is already computed and if not, computes it
spts = SimulationPopulationTimeSeries(Simulation(1)) # first checks if the population time series is already computed and if not, computes it
spts = SimulationPopulationTimeSeries(1; include_dead=true) # similar, but counts dead cells as well; the file name has "_include_dead" appended

Fields

  • time::Vector{Real}: The time points of the population time series.
  • cell_count::Dict{String, Vector{Integer}}: A dictionary where keys are cell type names and values are vectors of cell counts over time.
source
PhysiCellModelManager.finalPopulationCountMethod
finalPopulationCount(simulation::Simulation[; include_dead::Bool=false])

Return the final population count of a simulation as a dictionary with cell type names as keys and their counts as values.

Also works with the simulation ID:

fpc = finalPopulationCount(1)

Example

fpc = finalPopulationCount(simulation)
final_default_count = fpc["default"]
source
PhysiCellModelManager.finalPopulationCountMethod
finalPopulationCount(monad::Monad; include_dead::Bool=false)

Return the mean final-snapshot cell counts across all replicates in a monad.

Returns a Dict{String,Float64} mapping cell type name → mean count. Throws an error if the monad has no simulations.

Examples

fpc = finalPopulationCount(Monad(1))
fpc["default"]  # mean count of "default" cell type at final snapshot
source
PhysiCellModelManager.plotbycelltypeFunction
plotbycelltype(T::AbstractTrial; include_dead::Bool=false, include_cell_type_names=:all, exclude_cell_type_names=String[], time_unit=:min)

Plot the population time series of a trial by cell type.

Each cell type gets its own subplot. Each monad gets its own series within each subplot.

Arguments

  • T::AbstractTrial: The trial to plot.

Keyword Arguments

  • include_dead::Bool: Whether to include dead cells in the count. Default is false.
  • include_cell_type_names::Vector{String}: A vector of cell type names to include in the plot. Default is :all, which includes all cell types in separate plots.
  • exclude_cell_type_names::Vector{String}: A vector of cell type names to exclude from the plot. Default is an empty vector, i.e., no cell types are excluded.
  • time_unit::Symbol: The time unit to use for the x-axis. Default is :min, which uses minutes.
source
PhysiCellModelManager.populationCountFunction
populationCount(snapshot, cell_type_to_name_dict::Dict{Int,String}=Dict{Int,String}(), labels::Vector{String}=String[]; include_dead::Bool=false)

Return the population count of a snapshot as a dictionary with cell type names as keys and their counts as values.

If the snapshot is missing, it will return missing. This helps in cases where the files have been deleted, for example by pruning.

Arguments

  • snapshot::PhysiCellSnapshot: The snapshot to count the cells in.
  • cell_type_to_name_dict::Dict{Int,String}: A dictionary mapping cell type IDs to names (default is an empty dictionary). If not provided, it is read from the snapshot files.
  • labels::Vector{String}: The labels to identify the cell data. If not provided, it is read from the snapshot files.

Keyword Arguments

  • include_dead::Bool: Whether to include dead cells in the count. Default is false.
source
PhysiCellModelManager.populationTimeSeriesMethod
populationTimeSeries(M::AbstractMonad[; include_dead::Bool=false])
populationTimeSeries(pcmm_output::PCMMOutput{<:AbstractMonad}[; include_dead::Bool=false])

Return the population time series of a simulation or a monad.

See SimulationPopulationTimeSeries and MonadPopulationTimeSeries for more details.

source
PhysiCellModelManager.AverageSubstrateTimeSeriesType
AverageSubstrateTimeSeries

A struct to hold the average substrate concentrations over time for a PhysiCell simulation.

Constructed using AverageSubstrateTimeSeries(x) where x is an Integer (simulation ID) or a Simulation. A pre-loaded sequence can be used via AverageSubstrateTimeSeries(sequence::PhysiCellSequence).

Fields

  • time::Vector{Real}: The time points at which the snapshots were taken.
  • substrate_concentrations::Dict{String, Vector{Real}}: A dictionary mapping substrate names to vectors of their average concentrations over time.

Example

asts = PhysiCellModelManager.AverageSubstrateTimeSeries(1) # Load average substrate time series for Simulation 1
asts.time # Get the time points
asts["time"] # alternative way to get the time points
asts["oxygen"] # Get the oxygen concentration over time
source
PhysiCellModelManager.ExtracellularSubstrateTimeSeriesType
ExtracellularSubstrateTimeSeries

A struct to hold the mean extracellular substrate concentrations per cell type over time for a PhysiCell simulation.

Fields

  • time::Vector{Real}: The time points at which the snapshots were taken.
  • data::Dict{String, Dict{String, Vector{Real}}}: A dictionary mapping cell type names to dictionaries mapping substrate names to vectors of their average concentrations over time.

Example

ests = PhysiCellModelManager.ExtracellularSubstrateTimeSeries(1) # Load extracellular substrate time series for Simulation 1
ests.time # Get the time points
ests["cancer"]["oxygen"] # Get the oxygen concentration over time for the cancer cell type

ests = PhysiCellModelManager.ExtracellularSubstrateTimeSeries(simulation; include_dead=true) # Load extracellular substrate time series for a Simulation object, including dead cells
ests["time"] # Alternate way to get the time points
ests["cd8"]["IFNg"] # Get the interferon gamma concentration over time for the CD8 cell type

ests = PhysiCellModelManager.ExtracellularSubstrateTimeSeries(sequence) # Load from a pre-loaded PhysiCellSequence
source

Ready-made post_processor builders

Functions that return a post_processor (see run) ready to pass straight to run(T; post_processor=...).

PhysiCellModelManager.populationCountQoIFunction
populationCountQoI(; index::Union{Integer,Symbol}=:final, cell_types=nothing, include_dead::Bool=false)

Return a QoI that records per-cell-type population counts.

Reads the snapshot at index:final (default), :initial, or an integer snapshot index — via PhysiCellSnapshot and populationCount. Each cell type becomes one entry keyed by its name, stored by run in the post-processing sink under the column population_count.<cell_type> (e.g. population_count.default) and readable back with postProcessingTable or simulationsTable(...; post_processing=true).

If the requested snapshot doesn't exist (e.g. it was pruned), compute returns nothing and nothing is recorded for that simulation rather than throwing.

One QoI covers every cell type: they are read from the simulation's own output and so are not known until it has run, and ModelManager expands a Dict return into one column per key.

This QoI defines no reduce, so it is for the sink only. The sink never reduces — it fires once per simulation — but every other consumer does, and the default mean cannot combine a vector of Dicts. Use endpointPopulationCountQoI for calibration; it measures the same thing and carries a reducer.

Arguments

  • index: Which snapshot to count — :final, :initial, or an integer snapshot index.
  • cell_types: Optional Vector{String} to restrict which cell types are recorded. If nothing, all cell types present in the simulation are included.
  • include_dead: Whether to include dead cells in the count (default false).

Examples

run(sampling; post_processor = populationCountQoI())                       # final counts
run(sampling; post_processor = populationCountQoI(; index=0))              # counts at snapshot 0
run(sampling; post_processor = populationCountQoI(; include_dead=true))    # include dead cells
run(sampling; post_processor = populationCountQoI(; cell_types=["tumor"])) # only "tumor"
source