# pharmsol
[](https://github.com/LAPKB/pharmsol/actions/workflows/build.yml)
[](https://github.com/LAPKB/pharmsol/actions/workflows/docs.yml)
[](https://crates.io/crates/pharmsol)
A high-performance Rust library for pharmacokinetic/pharmacodynamic (PK/PD) simulation using analytical solutions, ordinary differential equations (ODEs), or stochastic differential equations (SDEs).
## Installation
Add `pharmsol` to `Cargo.toml`:
```bash
cargo add pharmsol
```
## Quick Start
Most 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:
```rust
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();
```
## Modeling Surfaces
Here is the same one-compartment IV setup written as an ODE:
```rust
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/analytical_readme.rs),
[examples/ode_readme.rs](examples/ode_readme.rs),
[examples/sde_readme.rs](examples/sde_readme.rs),
[examples/dsl_jit_analytical_covariates.rs](examples/dsl_jit_analytical_covariates.rs),
[examples/analytical_vs_ode.rs](examples/analytical_vs_ode.rs), and
[examples/compare_solvers.rs](examples/compare_solvers.rs). For migration-oriented notes,
see [docs/analytical-authoring-migration.md](docs/analytical-authoring-migration.md) and
[docs/ode-authoring-migration.md](docs/ode-authoring-migration.md).
### Built-In Analytical Kernels
- [x] One-compartment with IV infusion
- [x] One-compartment with IV infusion and oral absorption
- [x] Two-compartment with IV infusion
- [x] Two-compartment with IV infusion and oral absorption
- [x] Three-compartment with IV infusion
- [x] Three-compartment with IV infusion and oral absorption
## DSL and Runtime Targets
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-aot` and `dsl-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](examples/dsl_runtime_jit.rs) for the in-repo JIT flow and
[examples/dsl_jit_analytical_covariates.rs](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.
## Performance
Analytical solutions provide 20-33× speedups compared to equivalent ODE formulations. See [benchmarks](benches/) for details.
## Non-Compartmental Analysis (NCA)
pharmsol includes a complete NCA module for calculating standard pharmacokinetic parameters.
```rust
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
## Links
- [API Documentation](https://lapkb.github.io/pharmsol/pharmsol/)
- [Examples](examples/)