CellSim
A physics-first, uncertainty-honest in-silico cell and drug-response simulator in Python. Open source, Apple-silicon and Linux, no GPU required.
CellSim is converging on one product: an interactive dish. Pick a cell line and a drug, set a dose schedule, watch the colony respond, and get a dose-response curve whose error bar has been checked against public cell-line data. The molecular layer (docking, ADMET, quantum descriptors) feeds that engine as an input with provenance and a measured accuracy flag; it is not the product on its own.
Status (October 2026). Phases 0–2 of the plan are done and Phase 3 is in review. The smoke suite is green, the repository has one identity, and
cellsim/cell/holds a validated drug-response engine that runs well-mixed or in space.Phase 1 closed with a negative result that set the direction. The engine reproduces each drug's potency scale, but predicting which cell line is more sensitive turned out to be unreachable: eleven canonical markers, fitted optimally across 156–683 lines, explain only 9–20 % of the difference between lines. So per-line ranking is out of scope, and Phase 2 aimed at what mechanism is actually good for — within-line dynamics: dose schedule, wash-out, resistance under selection, combination timing.
Phase 2 built the dish.
cellsim dishgrows those cells in space — a monolayer with contact inhibition, or a spheroid with oxygen and drug diffusing in, hypoxic quiescence and a necrotic core — andweb/viewer/renders the per-cell stream in a browser. Every Phase-2 claim was re-measured over seeds and doses before the dish was built on it; one was retracted and one qualified.Phase 3 found the exception to the Phase-1 result. Across ten drugs and 91 held-out predictions the conclusion holds — except for the one drug whose target is part of the modelled mechanism. An MDM2 inhibitor works by stabilising p53, and the engine calls all eleven lines correctly (a constant gets five), including HeLa, whose TP53 is wild-type by sequence but whose p53 is degraded by HPV E6 rather than by MDM2. So: no ranking lines for cytotoxic drugs, but yes for a drug whose mechanism is modelled. Every number is in
docs/VALIDATION.md, misses included.The 2026 C++/Metal prototype lives in
OLD/as a biology reference; it still builds and passes its 8 headless benchmarks, but its UI is retired and a new interface is planned on top of the Python engine.
What works today
| Layer | What you can run | How it is validated |
|---|---|---|
Docking triage (cellsim dock) |
SMILES list + receptor PDB → ranked CSV with ΔG ± CI, pose-trust (UFF strain), PoseBusters, ADMET, one triage column | 15-cocrystal blind re-dock, structures and SMILES fetched from RCSB: top-3 pose recovery 87 %, top-1 73 %, PoseBusters validity 100 % |
Target-class reliability (cellsim/uq) |
Measured docking error per receptor family, so every ΔG carries an accuracy flag, not just seed scatter | trypsin-like 0.9, kinase ATP-site 2.2, ultra-tight binders 5.0 kcal/mol MAE (n = 4–6 each) |
ADMET descriptors (cellsim admet, profile) |
Lipinski, TPSA, QED, ESOL logS, BBB / hERG / Ames rule flags, one-page profile PNG | Published formulae, cited at the point of use in cellsim/chem/admet.py |
CYP3A4 site of metabolism (cellsim som) |
xTB C–H bond-dissociation ranking with a heme-accessibility re-rank | 2/3 on the bundled literature set; blind to N-dealkylation. Advisory only. |
Your own plate (cellsim plate) |
Read a plate-reader export (grid or tidy), normalise to the plate's own controls, fit a dose-response curve, and get GR metrics comparable across lines | Coverage measured against known IC50s: a nominal 95 % interval contains the truth ~90 % of the time on ten concentrations — and only 68 % on five, which is flagged. Flat plates are refused; ranges that clip the response are marked extrapolated |
Calibrated uncertainty (scripts/calibrate_cell_uncertainty.py) |
Every predicted IC50 comes with an interval whose coverage has been scored | Leave-one-drug-out: ±2.6× covers 64 % (68 % nominal), ±4.8× covers 85 % (90 %), ±6.5× covers 95 % (95 %). Wide on purpose — GDSC's own replicate screens disagree by a median 5.3× |
Cell drug-response engine (cellsim cell-sim, cellsim cell-stream) |
Pick a cell line, a drug and a dose schedule; get the colony over time, viability, and a per-cell state stream (JSON Lines) for a UI. Built for within-line questions: schedule, wash-out, resistance, combinations | Each drug's potency scale is right (A549 cisplatin 10.9 µM vs 9.8 measured; 20/25 held-out predictions inside GDSC's replicate span). Ranking one cell line against another is explicitly out of scope — measured as unreachable from canonical markers (why). |
Spatial dish (cellsim dish) and web viewer (web/viewer/) |
Grow cells as a monolayer or a spheroid under a dose schedule; oxygen and drug fields, contact inhibition, hypoxic quiescence, necrosis, clones. Open the JSON Lines stream in the viewer: 3-D cells coloured by phase, p53, caspase-3, oxygen or lineage, time scrubbing, charts, CSV export | Field solvers match closed forms (Grimes's 233 µm oxygen limit; the analytic slab profile). Re-running the Cell Tracking Challenge HeLa movie: colony size within 3–4 % at 46 h. Spheroid growth calibrated on DLD-1 (one constant). Open misses: the spheroid's necrotic core is too large, and real HeLa splits into fast cyclers and non-dividers, which the engine does not (details). |
Cell-level PK/PD modules (cellsim/cell) |
Occupancy, permeation, pH trapping, efflux, binding sink, tissue penetration, fate, resistance, clearance, cell cycle, lattice agents | Each module is checked against its analytic limit (22 tests). |
Alchemical FEP (cellsim/fep) |
Hydration and binding ΔG scaffolds on openmmtools + MBAR | Experimental, not a product path. FreeSolv-12 MAE 1.42 kcal/mol on 10/12 with a size-dependent bias (no barostat). Binding ΔG has never produced a number on a real binder. |
The full set of numbers, with reproducers and caveats, is in
docs/VALIDATION.md.
Quickstart: simulate cells in one minute
The cell simulator needs only NumPy and SciPy, so it installs with plain
pip — no conda, no compilers, no chemistry stack. (Checked in CI on a bare
virtual environment: tests/test_pip_install.py.)
pip install cellsim # or: pip install -e . from a checkout
# A dose-response curve and an IC50 for one line and drug.
cellsim cell-sim --line A549 --drug cisplatin --hours 72
# Grow a spheroid for five days and watch oxygen run out in its core.
cellsim dish --line DLD-1 --geometry spheroid --hours 120 \
--cells 3000 --grid 48 --every 8 --out spheroid.jsonl
# Open spheroid.jsonl in web/viewer/index.html (no server needed for a
# local file: use the "Open .jsonl" button).
In a notebook, cellsim.api returns tidy tables (a pandas.DataFrame
when pandas is installed, a list of dicts otherwise):
from cellsim.api import curve, exposure, washout, combination, spheroid
curve("A549", "paclitaxel") # viability vs concentration, with IC50
exposure("A549", "paclitaxel") # concentration needed for half kill at
# each exposure time — the schedule question
washout("A549", "cisplatin", 20.0, 12.0) # a 12 h pulse, then recovery
combination("A549", "cisplatin", "paclitaxel", 10.0, 0.03) # vs independent action
spheroid("DLD-1", days=6) # size, hypoxia, necrotic core over time
docs/cell_tutorial.md walks through a real
question end to end — should I pulse this drug or leave it on? — with
every command run and its actual output.
ic50_spread() is worth running before quoting any IC50 from this
engine: the number moves 10–20 % between seeds, because a population's
IC50 is set by which resistant lineages were drawn rather than by how
many cells were counted (why). Two numbers that
differ by less than that do not differ.
exposure() is the one worth knowing about: it returns inf where no
concentration reaches half kill, which is the honest answer for a taxane
given for three hours, and the reason AUC is the wrong exposure metric
for that class.
The molecular layers (docking, MD, FEP, quantum) need the conda stack below.
Quickstart: first docking screen in five minutes
# 1. One-time environment (conda or mamba).
mamba env create -f environment.yml
conda activate cellsim
pip install -e . # registers the `cellsim` command
cellsim doctor # expect "42/42 checks passed"
# 2. Screen the bundled 5-compound batch against streptavidin (PDB 1STP).
cellsim dock \
--smi benchmarks/dock/1stp_batch_5.smi \
--receptor benchmarks/dock/1stp.pdb \
--out-csv /tmp/run/report.csv \
--mc 4 --profile-top-k 3 \
--crystal-pdb benchmarks/dock/1stp.pdb --crystal-resname BTN
You get a ranked CSV (triage, dG_kcalmol, Kd_human, strain_band,
pocket_ok, ADMET columns) plus one-page profile PNGs for the top hits.
Omit --center / --box for any other receptor and fpocket finds the
site. cellsim help lists every subcommand;
docs/docking_tutorial.md walks through a
full screen and, in §8, says which target classes to trust.
Where this is going
| Phase | Deliverable | Target |
|---|---|---|
| 0 — done | July fixes merged, CI green, one identity, honest validation page | Oct 2026 |
| 1 — closed, with a finding | Single-cell drug-response engine with one state object and one integrator: cell cycle and p53 axis ported from OLD/, death that actually executes, cellsim/cell PK on top. Calibrated against GDSC for cisplatin, doxorubicin and paclitaxel (ten drugs by Phase 3). Headless state stream for any UI. Closed early: the per-line prediction it aimed at was measured to be unreachable |
Oct 2026 |
| 2 — code done | The dish: spatial colony, diffusion, drug penetration, contact inhibition, validated on the CTC HeLa movie and calibrated on DLD-1 spheroids; web viewer reading the engine stream. Outside users still to find | Apr 2027 |
| 3 — in review | Ten drugs × ten lines, combinations and evolved resistance, calibrated uncertainty with coverage, JOSS paper, conda-forge | Oct 2027 |
| 4 — started | Your own cells: cellsim plate reads a plate-reader export and fits it with a measured error bar; next, fit this line's own simulator constants and give predictions as bands rather than lines (plan) |
2028 |
Details, the keep/kill list and success metrics: docs/PLAN.md.
Layout
cellsim/
dock/ Vina + Meeko + PoseBusters + fpocket, batch screen, triage, strain, off-target
chem/ SMILES → OpenFF Sage + AM1-BCC; ADMET descriptors; profile dashboards
uq/ Monte-Carlo, Sobol, split-conformal, target-class reliability table
quantum/ xTB GFN2, CYP3A4 site-of-metabolism (advisory), PySCF hook
cell/ cell-level PK/PD modules (Phase 1 engine grows here)
bridge/ binding ΔG → rate-law priors with provenance and accuracy
cache/ content-addressed SQLite cache for every expensive physics call
fep/ alchemical ΔG scaffolds (experimental; see VALIDATION.md)
md/ OpenMM ligand / protein MD helpers
benchmarks/ every input needed to reproduce a number in docs/VALIDATION.md
tests/ one folder per src/ module; each file is a standalone gate
scripts/ the `cellsim` CLI dispatcher, fetchers, benchmark runners, doctor
docs/ plan, validation, docking tutorial, archive of the 2026 campaign docs
OLD/ frozen 2026 C++/Metal prototype, its 6 headless validators and scripts (see OLD/README.md)
Validation and CI
Every push and pull request runs smoke.yml:
54 gates across chem, MD, docking, FEP scaffolds, quantum, UQ, bridge,
cell and cache, about 15 minutes on a 2-core runner. A regression in any
gate blocks merge. Numbers that take hours (FEP sampling, scale tests)
run only on workflow_dispatch and are reported in
docs/VALIDATION.md with the commit that produced
them.
History, briefly
v1.0 (March 2026) was a real-time C++/Metal 3D cell simulator with drug
PK/PD. In April 2026 the project restarted around a non-AI molecular
stack after a critique of circular validation; that stack is the
src/ tree above. The July 2026 fixes corrected the docking numbers and
the FEP binding path. In October 2026 the project was refocused on the
single product described at the top. The complete record, including the
original roadmap, the professor debrief, and every benchmark post-mortem,
is preserved in docs/archive/campaign1/.
License
MIT, see LICENSE. Third-party attributions are in
THIRD_PARTY_NOTICES.md.
Metadata
Release files for cellsim 1.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cellsim-1.4.0.tar.gz | 319.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cellsim-1.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 685.2 kB
Release files / cellsim-1.4.0.tar.gz
| Download URL | cellsim-1.4.0.tar.gz |
|---|---|
| Size | 319.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
79cfac0898761f79b774d84572fb2838a8b344d29c741aada2c77685cac5355b
|
|
BLAKE2b-256 checksum How to use checksums |
f493914fc428da9b8aa43632e86ae88650a738d20e7b894da6784e068490cc1c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|
Release files / cellsim-1.4.0-py3-none-any.whl
| Download URL | cellsim-1.4.0-py3-none-any.whl |
|---|---|
| Size | 365.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b8239fd8e438337abc44ebe042334f7a10dacf3b66f6fffd0eb6f84e413ef9b7
|
|
BLAKE2b-256 checksum How to use checksums |
4ba30ed03af0a676e5417fb301fa913e017d64b73be3292dba9fe41c78a16966
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|