What it does
Quick two-arm trials
Event risks, allocation, α, and simulation count → power, ARR/RR/RRR/NNT with confidence intervals, and per-replicate plots.
“What if the trial goes badly?”
Loss to follow-up (with risk multipliers), noncompliance, crossover, and imperfect ascertainment — compare the pragmatic trial to the ideal one, side by side.
Sample size & power
Formula sample sizes for two proportions, then simulated power curves around them — under ideal and pragmatic assumptions.
Interim stopping rules
Peto, Pocock, and O'Brien–Fleming legacy threshold tables plus custom plans; see where trials stop and what it costs in Type I error.
Risk subgroups
Full trial scenarios per subgroup, aggregated by summing 2×2 counts per replicate — never by averaging effects — with a forest display.
Cluster designs
Post-only cluster trials (beta-binomial, ICC, design effects, three labeled analyses) and pre/post designs with correlated baselines.
The Phoenix principle
Code is ephemeral; specification is source; tests are truth.
IcebergSim RCT is regenerated from an implementation-independent specification
(spec/) distilled from the historical
ICEBERGSIM — the Controlled Trial Simulator created by Eduardo Bergel with David Sackett and an
international consortium. Every behavior is pinned by language-agnostic canonical tests
(spec/tests.yaml: all 19 cases and 5 property tests pass), and every result carries a
reproducibility manifest: input hash, seed, RNG algorithm, spec version, and analysis method.
Delete the implementation, regenerate it from the spec, and the tests must pass again.
Quick start
git clone https://github.com/ebergel/icebergsim-rct && cd icebergsim-rct
uv sync # Python engine + API deps
cd web && npm install && npm run build && cd .. # build the UI once
uv run icebergsim-server # → http://127.0.0.1:8000 (local-only)
# or use the engine directly from Python
from icebergsim import validate_trial_definition, simulate_trial
from icebergsim.io import load_definition
trial = validate_trial_definition(load_definition("spec/examples/simple_two_arm.yaml"))
result = simulate_trial(trial, include_type_i_error=True)
print(result.summary.power) # 0.81…, reproducible from the seed
Design commitments
Pure statistical engine
Frozen data, injected randomness, no formulas in the UI. The web interface renders exactly what the engine reports — nothing more.
Honest by construction
Undefined quantities are null with a diagnostic, inconsistent scenarios are rejected with structured errors, and anti-conservative analyses are labeled as such.
Reproducible always
Same definition + same seed → identical arrays. Seeded PCG64 with independent named streams for null copies, subgroups, and scenario families.