Architecture
PhysiCore is designed with modularity and extensibility at its core. This page explains the logical structure and how different components interact to create a flexible multicellular simulation framework.
Design Philosophy
PhysiCore follows several key architectural principles:
- Time-scale separation - Physics processes are organized by their characteristic time scales
- Pluggable implementations - Each module supports multiple backend implementations
- Clear interfaces - Public APIs are stable and well-defined via CMake
FILE_SET HEADERS - Self-registering components - Implementations register themselves at startup for runtime selection
- Performance-first - Ready for HPC, vectorization, and GPU acceleration
Terminology
Understanding PhysiCore’s architecture requires familiarity with several key terms that describe different levels of abstraction: module, module implementation, and kernel.
---
config:
flowchart:
htmlLabels: false
---
flowchart LR
A[Module] --> B@{ shape: processes, label: "Multiple implementations" }
B --> C@{ shape: processes, label: "Multiple per-implementation kernels" }
Module
A module represents a distinct physics domain or time scale in multicellular simulations. Modules define the contract and interface that implementations must follow, but contain no concrete algorithms themselves.
Examples:
reactions-diffusion- Handles substrate transport and reaction kineticsmechanics- Manages cell-cell and cell-substrate mechanical interactionsphenotype- Integrates modules and defines biological behaviors
Modules are organized as separate CMake libraries under their own directories (e.g., reactions-diffusion/, mechanics/).
Module Implementations
Module implementations (also called models or engines) provide concrete algorithms and data structures that fulfill a module’s interface contract. Multiple implementations can exist for the same module, offering different algorithmic approaches or trade-offs.
Examples:
biofvm- A finite volume implementation of the reactions-diffusion modulemicromechanics- A spring-based implementation of the mechanics modulephysicore- The reference implementation of the phenotype module
Each implementation lives in its own subdirectory within the module (e.g., reactions-diffusion/biofvm/, mechanics/micromechanics/).
Kernels and Solvers
Kernels (also called solvers or backends) are hardware-specific implementations of the computational core within a module implementation. They provide the same algorithm but optimized for different execution environments.
Examples:
openmp_solver- CPU parallelization using OpenMP threadsthrust_solver- GPU/CPU parallelization using NVIDIA Thrustcuda_solver- Direct GPU acceleration using CUDA
Kernels self-register with registries (like solver_registry) at static initialization, enabling runtime selection without compile-time dependencies. They are located in kernels/ subdirectories within implementations (e.g., reactions-diffusion/biofvm/kernels/openmp_solver/).
The distinction between kernel and solver is subtle:
- Solver emphasizes the algorithmic approach (finite volume solver, finite element solver)
- Kernel emphasizes the execution backend (OpenMP kernel, CUDA kernel)
PhysiCore uses “kernel” to highlight that these components provide alternative execution strategies for the same underlying algorithm.
Architecture Overview
PhysiCore is organized into modules based on the time scale and physics domain they represent. Each module defines a contract (interface) that multiple implementations can provide.
---
config:
flowchart:
htmlLabels: false
---
flowchart TB
subgraph S0[Physicore Framework]
direction TB
subgraph S1[Core Modules]
direction TB
C["`Common Module<br/>*Core Abstractions*`"] --> D["`Reactions-Diffusion Module<br/>*Substrate Transport*`"] & M["`Mechanics Module<br/>*Cell Interactions*`"]
P["`Phenotype Module<br/>*Integration Layer*`"]
end
C --> X["`Module X<br/>*Added functionality*`"]
D --> P
M --> P
X --> P
end
subgraph S2["`**Applications**`"]
direction LR
F[Simulation Executables] ~~~ G[Analysis Tools]
end
S0 -.-> S2
Core Modules
PhysiCore consists of four primary modules:
| Module | Purpose | Location |
|---|---|---|
| common | Core abstractions, types, and interfaces | common/ |
| reactions-diffusion | Substrate diffusion and reaction kinetics | reactions-diffusion/ |
| mechanics | Cell-cell and cell-substrate mechanical interactions | mechanics/ |
| phenotype | Phenotype models and simulation wiring | phenotype/ |
Currently, these modules cover the essential physics for multicellular simulations. Additional modules can be added to extend functionality.
So far, PhysiCore implements common functionality and the reactions-diffusion module via the BioFVM implementation.
👉 Learn more about the Common Module
Directory Structure of an Implementation
As you know, each module can have multiple implementations. Each implementation follows a consistent directory structure to separate public APIs, internal implementations, kernels, examples, and tests.
Using BioFVM as an example:
reactions-diffusion/biofvm/
├── include/ # Public API headers
│ └── biofvm/
│ ├── solver.h
│ └── microenvironment.h
├── src/ # Implementation files and private headers
│ ├── solver.cpp
│ ├── config_reader.h
│ ├── config_reader.cpp
│ └── microenvironment.cpp
├── kernels/ # Backend-specific implementations
│ ├── openmp_solver/
│ └── thrust_solver/
├── examples/ # Demonstration applications
│ └── diffuse.cpp
└── tests/ # Unit tests
└── solver_test.cpp
Next Steps
Explore each module in detail:
- Common Module - Core abstractions, timestep executors, and agent containers