A high-performance Rust library for pharmacokinetic/pharmacodynamic (PK/PD) simulation using analytical solutions, ordinary differential equations (ODEs), or stochastic differential equations (SDEs).
Add pharmsol to Cargo.toml:
cargo add pharmsolMost Rust-first workflows start with one of the equation macros: analytical!,
ode!, or sde!. Here is a one-compartment oral model using analytical!
with a derived analytical structure input:
use pharmsol::{Parameters, prelude::*};
let analytical = analytical! {
name: "one_cmt_oral",
params: [ka, ke0, v],
derived: [ke],
covariates: [wt],
states: [gut, central],
outputs: [cp],
routes: [
bolus(oral) -> gut,
],
structure: one_compartment_with_absorption,
derive: |_t| {
ke = ke0 * (wt / 70.0).powf(0.75);
},
out: |x, _t, y| {
y[cp] = x[central] / v;
},
};
let subject = Subject::builder("patient_001")
.bolus(0.0, 500.0, "oral")
.missing_observation(0.5, "cp")
.missing_observation(1.0, "cp")
.missing_observation(2.0, "cp")
.missing_observation(4.0, "cp")
.covariate("wt", 0.0, 75.0)
.build();
let parameters = Parameters::with_model(
&analytical,
[("ka", 1.2), ("ke0", 0.08), ("v", 194.0)],
)
.unwrap();
let predictions = analytical
.estimate_predictions(&subject, ¶meters)
.unwrap();Here is the same one-compartment IV setup written as an ODE:
use pharmsol::prelude::*;
let ode = ode! {
name: "one_cmt_iv",
params: [ke, v],
states: [central],
outputs: [cp],
routes: [
infusion(iv) -> central,
],
diffeq: |x, _p, _t, dx, _cov| {
dx[central] = -ke * x[central];
},
out: |x, _p, _t, _cov, y| {
y[cp] = x[central] / v;
},
};See examples/analytical_readme.rs, examples/ode_readme.rs, examples/sde_readme.rs, examples/dsl_jit_analytical_covariates.rs, examples/analytical_vs_ode.rs, and examples/compare_solvers.rs. For migration-oriented notes, see docs/analytical-authoring-migration.md and docs/ode-authoring-migration.md.
- One-compartment with IV infusion
- One-compartment with IV infusion and oral absorption
- Two-compartment with IV infusion
- Two-compartment with IV infusion and oral absorption
- Three-compartment with IV infusion
- Three-compartment with IV infusion and oral absorption
If the model needs to be loaded or compiled at runtime, pharmsol also provides a DSL with the same broad modeling coverage: ODE, analytical, and SDE authoring. The DSL can target an in-process JIT runtime, native ahead-of-time artifacts, or WASM bundles depending on how you want to ship and execute the model.
dsl-jit: compile DSL source into a runtime model inside the current process.dsl-aotanddsl-aot-load: emit a native artifact and load it later.dsl-wasm: compile and execute portable WASM model artifacts.
See examples/dsl_runtime_jit.rs for the in-repo JIT flow and
examples/dsl_jit_analytical_covariates.rs for a
small analytical covariate example written both as DSL JIT source and as an
analytical! model.
The companion pharmsol-examples crate includes end-to-end native AOT and WASM runtime
examples.
Analytical solutions provide 20-33× speedups compared to equivalent ODE formulations. See benchmarks for details.
pharmsol includes a complete NCA module for calculating standard pharmacokinetic parameters.
use pharmsol::prelude::*;
use pharmsol::nca::NCAOptions;
let subject = Subject::builder("patient_001")
.bolus(0.0, 100.0, "oral") // 100 mg oral dose
.observation(0.5, 5.0, "cp")
.observation(1.0, 10.0, "cp")
.observation(2.0, 8.0, "cp")
.observation(4.0, 4.0, "cp")
.observation(8.0, 2.0, "cp")
.build();
let result = subject.nca(&NCAOptions::default()).expect("NCA failed");
println!("Cmax: {:.2}", result.exposure.cmax);
println!("Tmax: {:.2} h", result.exposure.tmax);
println!("AUClast: {:.2}", result.exposure.auc_last);
if let Some(ref term) = result.terminal {
println!("Half-life: {:.2} h", term.half_life);
}Supported NCA Parameters:
- Exposure: Cmax, Tmax, Clast, Tlast, AUClast, AUCinf, tlag
- Terminal: λz, t½, MRT
- Clearance: CL/F, Vz/F, Vss
- IV-specific: C0 (back-extrapolation), Vd
- Steady-state: AUCtau, Cmin, Cavg, fluctuation, swing