Coming from WinNonlin / NONMEM
A quick-reference guide for scientists transitioning from Phoenix WinNonlin and NONMEM to OpenPKFlow.
WinNonlin NCA -> OpenPKFlow NCA
| WinNonlin |
OpenPKFlow |
Notes |
NCA -> Model 200 (plasma) |
NCAStudy.from_csv() + study.analyze() |
CSV input, returns NCASummaryResults |
AUClast (linear trapezoidal) |
auc_linear(times, concs) |
Explicit method required |
AUClast (log-linear trapezoidal) |
auc_log(times, concs) |
Returns AUCResult with warnings |
AUC linear-up/log-down |
auc_linear_up_log_down(times, concs) |
FDA/EMA default |
AUCINF_obs |
NCAResult.auc_inf_obs |
AUClast + Clast/lambda_z |
%AUCextrap |
NCAResult.auc_percent_extrapolated |
Warns if >20% (FDA criterion) |
Cmax |
cmax(concs) |
NCAResult.cmax |
Tmax |
tmax(times, concs) |
NCAResult.tmax |
lambda_z (best-fit) |
lambda_z(times, concs, method="auto") |
BAR² auto-selects tail window |
t1/2 |
NCAResult.half_life |
ln(2) / lambda_z |
CL_F (oral) |
NCAResult.cl_f |
Apparent clearance |
Vz_F (oral) |
NCAResult.vz_f |
Apparent volume |
CL (IV) |
NCAResult.cl |
Absolute clearance |
| BLQ handling (set to 0) |
blq_method="zero" |
One of: none/drop/zero/half_lloq/lloq |
| Steady-state NCA |
NCAStudy(..., steady_state=True, tau=12) |
AUCtau, Cmax_ss, Cmin_ss, fluctuation% |
| Urinary excretion |
NCAStudy(..., urine_volume_col="vol", urine_conc_col="conc_u") |
Ae, CLr, %excreted |
| CDISC PP output |
summary.to_cdisc_pp() |
PPTESTCD/PPORRES/PPORRESU format |
| Dose-normalised params |
NCAResult.dn_auclast, .dn_cmax |
Per-mg normalisation |
Example
from openpkflow.nca import NCAStudy
study = NCAStudy.from_csv("subjects.csv", auc_method="linear_up_log_down")
results = study.analyze()
print(results.summary())
results.report("nca_report.html")
WinNonlin Bioequivalence -> OpenPKFlow BE
| WinNonlin |
OpenPKFlow |
Notes |
BE -> 2x2 crossover (TOST) |
be_tost(reference, test) |
Paired per-subject log-differences |
| GMR + 90% CI |
BETOSTResult.gmr, .gmr_lower_90ci, .gmr_upper_90ci |
Default limits 80-125% |
| Intra-subject CV% |
BETOSTResult.cv_intra_pct |
Back-transformed from log-diff SD |
| BE verdict (PASS/FAIL) |
BETOSTResult.bioequivalent |
Boolean flag |
| NTI limits (90-111%) |
be_tost(..., be_lower=0.90, be_upper=1.1111) |
|
| Sample size (power 80%) |
be_sample_size(gmr=0.95, cv=0.20) |
Sequential search |
| Power for given N |
be_tost_power(gmr=0.95, cv=0.20, n=24) |
Non-central t exact method |
| BE report |
result.report("be_report.html") |
HTML with CI bar chart |
| Export to BioEqPy |
study.to_bioeqpy_csv() |
For RSABE/replicate designs |
WinNonlin IVIVC -> OpenPKFlow IVIVC
| WinNonlin |
OpenPKFlow |
Notes |
| Wagner-Nelson deconvolution |
wagner_nelson(times, concs) |
Returns F_a fraction absorbed |
| Loo-Riegelman deconvolution |
loo_riegelman(times, concs, dose, k10, k12, k21) |
2-compartment method |
| Convolution prediction |
convolution_predict(diss_times, diss_frac, ir_fn) |
Numerical convolution |
| Levy plot |
levy_plot_data(f_in_vitro, f_in_vivo) |
Linear regression R² |
| Predictability (%PE) |
ivivc_predictability(...) |
FDA <=15% individual, <=10% mean |
Example
from openpkflow.ivivc import IVIVCStudy
study = IVIVCStudy(dissolution_csv="dissolution.csv", plasma_csv="plasma.csv",
dose=100, method="wagner_nelson")
result = study.run()
result.report("ivivc_report.html")
Phoenix NLME / NONMEM -> OpenPKFlow Pop PK
| Phoenix NLME / NONMEM |
OpenPKFlow |
Notes |
| FOCE-I (METHOD=1) |
run_foce_i(model, data) |
scipy L-BFGS-B, zero extra deps |
| SAEM (METHOD=3) |
run_saem(model, data) |
PyMC optional, numpy fallback |
$PK block |
PopPKModel(route="oral", n_cmt=1, omega_type="diagonal") |
Frozen dataclass |
$THETA |
model.to_theta()/model.from_theta() |
Pack/unpack for optimizer |
$OMEGA |
PopPKModel(omega_type="full") |
Diagonal or full block matrix |
$SIGMA |
Built into proportional error model |
Fixed at v2.3.0 |
$DATA |
load_pop_csv(path) |
NONMEM-style CSV (ID, TIME, DV, EVID, MDV) |
$TABLE output |
result.to_dataframe() |
Parameter estimates + SEs |
| Objective function (-2LL) |
PopPKResult.minus_2ll |
Also AIC, BIC |
| EBE shrinkage |
PopPKResult.ebe_shrinkage |
Per-parameter shrinkage |
| SEs (delta method) |
PopPKResult.se_* fields |
Via inverse Hessian |
| 2-compartment models |
PopPKModel(n_cmt=2) |
Oral or IV bolus |
| IV bolus route |
PopPKModel(route="iv_bolus") |
Supports 1-cmt and 2-cmt |
| Covariates |
Not available (removed in v2.3.0) |
Use Pharmpy/nlmixr2 for covariate selection |
| GOF diagnostic plots |
result.plot() |
6-panel OBS/IPRED/CWRES/EBE |
| VPC |
simulate_vpc(model, data) |
Simulation-based percentile bands |
| Population dataset |
create_nonmem_dataset(...) |
Dose + observation records merged |
Example
from openpkflow.pop import PopPKModel, run_foce_i
model = PopPKModel(route="oral", n_cmt=1, omega_type="diagonal")
result = run_foce_i(model, "theoph.csv")
print(result.summary())
result.report("pop_report.html")
NONMEM VPC -> OpenPKFlow Pop Diagnostics
NONMEM (PsN vpc) |
OpenPKFlow |
Notes |
| Simulation-based VPC |
simulate_vpc(model, data, n_sim=500) |
Users sim/ analytical engine |
| 5th/50th/95th percentile bands |
VPCResult.observed_bands, .simulated_bands |
Time-binned |
| VPC plot |
vpc_result.plot() or vpc_result.report() |
scatter + band overlay |
WinNonlin Dissolution -> OpenPKFlow Dissolution
| WinNonlin |
OpenPKFlow |
Notes |
| f1 (difference factor) |
f1(reference, test) |
0 = identical |
| f2 (similarity factor) |
f2(reference, test) |
100 = identical, >=50 passes |
| Bootstrap f2 |
study.bootstrap_compare(ref, test) |
CI for samples <12 vessels |
| Max deviation |
max_deviation(reference, test) |
FDA alternative when f2 cannot be used |
| MSD (Mahalanobis) |
msd(reference, test_matrix) |
Chi-squared test |
| Model fitting (Weibull) |
study.fit_models(formulation, models=["weibull"]) |
AICc ranking |
| Model fitting (KP) |
models=["korsmeyer_peppas"] |
60% rule check |
| Model fitting (Higuchi) |
models=["higuchi"] |
|
| Multi-media (pH panel) |
MultiMediaStudy({...}) |
ICH M13A/B |
| Alcohol dose-dumping |
Supported in multi-media |
Separate medium with ethanol |
| Report generation |
result.report("out.html") |
HTML/PDF/DOCX/MD |
Example
from openpkflow.dissolution import DissolutionStudy
study = DissolutionStudy.from_csv("dissolution.csv")
result = study.compare("Reference", "Test")
print(f"f2 = {result.f2_value:.1f}") # >= 50: similar
result.report("dissolution_report.html")
PK Simulation -> OpenPKFlow Sim
| Phoenix WinNonlin PKS |
OpenPKFlow |
Notes |
| 1-cmt IV bolus |
c_1cmt_iv_bolus(times, dose, CL, Vz) |
Gibaldi & Perrier 2nd ed. |
| 1-cmt IV infusion |
c_1cmt_iv_infusion(times, dose, CL, Vz, duration) |
Constant-rate infusion |
| 1-cmt oral |
c_1cmt_oral(times, dose, CL_F, Vz_F, ka) |
Bateman equation |
| 2-cmt IV bolus |
c_2cmt_iv_bolus(times, dose, CL, V1, Q, V2) |
Biexponential |
| 2-cmt oral |
c_2cmt_oral(times, dose, CL_F, Vz_F, ka, Q, V2) |
3-exponential |
| Repeated dosing |
DoseRegimen.from_repeated(...) + simulate() |
Superposition |
| Model object |
OneCompartmentModel(...) or TwoCompartmentModel(...) |
CL/V parameterization |
Key Differences
Parameterization: OpenPKFlow uses CL/V (or CL_F/Vz_F), not rate constants (k10, k12, k21). Clearance and volume are more interpretable and align with NCA output naming.
No GUI: OpenPKFlow is a Python library + CLI. All outputs are scriptable, versionable, and CI-compatible.
No covariates in Pop PK: Covariate estimation code was removed in v2.3.0. Use Pharmpy or nlmixr2 for covariate selection, then OpenPKFlow for base model estimation.
Reports are static HTML/PDF/DOCX/MD: No interactive dashboards. All report templates are Jinja2 and can be customized.
RSABE / replicate BE: Not in OpenPKFlow. Use the companion BioEqPy package with to_bioeqpy_csv() export.
Environment R -> OpenPKFlow
| R (PKNCA/nlmixr2) |
OpenPKFlow |
Notes |
PKNCA::pk.nca() |
NCAStudy.from_csv().analyze() |
Cross-validated against PKNCA 0.12.1 |
nlmixr2::nlmixr() FOCE-I |
run_foce_i() |
Cross-validated against nlme (Pinheiro & Bates 2000, Table 8.1) |
nlmixr2::nlmixr() SAEM |
run_saem() |
PyMC Metropolis + Robbins-Monro |
PowerTOST::power.TOST() |
be_tost_power() |
Exact non-central t method |
PowerTOST::sampleN.TOST() |
be_sample_size() |
Cross-validated against PowerTOST 1.5-7 |
bootf2::calcf2() |
f2(method="all_points") |
Point estimate validated |
lm() / optim() model fits |
fit_dissolution_models() |
All 5 models cross-validated against base R |