NONMEM Model Import
Import NONMEM control stream files (.ctl, .mod) into NeoPKPD Python.
Overview
from neopkpd.import_ import import_nonmem
model = import_nonmem("run001.ctl")
print(f"Model: {model.model_kind}")
print(f"Parameters: {model.params}")
Quick Start
Basic Import
from neopkpd.import_ import import_nonmem
# Import NONMEM control file
model = import_nonmem("run001.ctl")
# Access model information
print(f"Model type: {model.model_kind}")
print(f"Source: {model.source_format}")
# Access parameters
for name, value in model.params.items():
print(f" {name} = {value}")
With Dose Events
# Specify doses if not in control file
doses = [
{"time": 0.0, "amount": 100.0},
{"time": 12.0, "amount": 100.0}
]
model = import_nonmem("run001.ctl", doses=doses)
ImportedModel Class
@dataclass
class ImportedModel:
"""Result of importing a model from external format."""
# Source information
source_format: str # "nonmem" or "monolix"
source_file: str # Path to source file
# Model type
model_kind: str # NeoPKPD model name
# Fixed effects
params: dict[str, float] # Parameter name → value
theta_init: list[float] # Initial THETA values
theta_names: list[str] # Parameter names
# Random effects
omega_init: list[list[float]] # Omega matrix
omega_names: list[str] # Random effect names
# Residual error
sigma_type: str # "proportional", "additive", etc.
sigma_init: float # Error parameter value
# Quality
warnings: list[str] # Import warnings
# Metadata
metadata: dict[str, Any] # Additional information
Accessing Model Details
model = import_nonmem("run001.ctl")
# Fixed effects
print("Fixed Effects:")
for name, value in model.params.items():
print(f" {name} = {value}")
# Random effects
print("\nRandom Effects:")
for i, name in enumerate(model.omega_names):
omega = model.omega_init[i][i]
cv = (math.exp(omega) - 1) ** 0.5 * 100 # CV for lognormal
print(f" {name}: ω² = {omega:.4f} (CV ≈ {cv:.1f}%)")
# Residual error
print(f"\nError Model: {model.sigma_type}")
print(f"Sigma: {model.sigma_init}")
# Warnings
if model.warnings:
print("\nWarnings:")
for w in model.warnings:
print(f" ⚠️ {w}")
Supported ADVAN/TRANS
One-Compartment Models
| ADVAN |
TRANS |
Parameters |
Model |
| ADVAN1 |
TRANS1 |
K, V |
OneCompIVBolus |
| ADVAN1 |
TRANS2 |
CL, V |
OneCompIVBolus |
| ADVAN2 |
TRANS1 |
KA, K, V |
OneCompOralFirstOrder |
| ADVAN2 |
TRANS2 |
KA, CL, V |
OneCompOralFirstOrder |
Two-Compartment Models
| ADVAN |
TRANS |
Parameters |
Model |
| ADVAN3 |
TRANS1 |
K, K12, K21, V |
TwoCompIVBolus |
| ADVAN3 |
TRANS4 |
CL, V1, Q, V2 |
TwoCompIVBolus |
| ADVAN4 |
TRANS1 |
KA, K, K23, K32, V |
TwoCompOral |
| ADVAN4 |
TRANS4 |
KA, CL, V1, Q, V2 |
TwoCompOral |
Three-Compartment Models
| ADVAN |
TRANS |
Parameters |
Model |
| ADVAN11 |
TRANS4 |
CL, V1, Q2, V2, Q3, V3 |
ThreeCompIVBolus |
Special Models
| ADVAN |
Parameters |
Model |
| ADVAN10 |
VM, KM, V |
MichaelisMentenElimination |
Parsed Control File
Accessing Raw Parsed Data
model = import_nonmem("run001.ctl")
# Access parsed control file structure
parsed = model.metadata.get("parsed_control_file")
if parsed:
# Problem statement
print(f"Problem: {parsed.problem}")
# Subroutines
print(f"ADVAN: {parsed.subroutines.advan}")
print(f"TRANS: {parsed.subroutines.trans}")
# THETA specifications
for i, theta in enumerate(parsed.thetas):
print(f"THETA({i+1}): init={theta.init}, bounds=({theta.lower}, {theta.upper})")
THETASpec
@dataclass
class THETASpec:
init: float # Initial estimate
lower: float # Lower bound
upper: float # Upper bound
fixed: bool # Fixed parameter
name: str # Optional name
OMEGABlock
@dataclass
class OMEGABlock:
values: list[float] # Variance/covariance values
structure: OmegaStructure # DIAGONAL or BLOCK
dimension: int # Block dimension
fixed: bool # Fixed block
$PK Block Parsing
Supported Patterns
# Typical value definitions
# TVCL = THETA(1)
# Parameter assignments with ETA
# CL = TVCL * EXP(ETA(1)) → exponential
# CL = TVCL + ETA(1) → additive
# CL = TVCL * (1 + ETA(1)) → proportional
# Covariate effects
# TVCL = THETA(1) * (WT/70)**THETA(4) → power
# TVCL = THETA(1) * (1 + THETA(5)*(AGE-40)) → linear
Accessing PK Block
model = import_nonmem("run001.ctl")
parsed = model.metadata.get("parsed_control_file")
if parsed and parsed.pk_block:
pk = parsed.pk_block
# TV definitions
for tv, theta_idx in pk.tv_definitions.items():
print(f"{tv} = THETA({theta_idx})")
# Parameter assignments
for assign in pk.assignments:
print(f"{assign.parameter}: TV={assign.tv_symbol}, ETA={assign.eta_index}")
# Unsupported lines
if pk.unsupported_lines:
print("Unsupported $PK lines:")
for line in pk.unsupported_lines:
print(f" {line}")
$ERROR Block Parsing
Supported Error Models
| Type |
Pattern |
Formula |
proportional |
W = IPRED * THETA(n) |
Y = F · (1 + ε) |
additive |
W = THETA(n) |
Y = F + ε |
combined |
W = SQRT(...) |
Y = F · (1 + ε₁) + ε₂ |
exponential |
Y = F * EXP(ERR(1)) |
Y = F · e^ε |
Accessing Error Model
model = import_nonmem("run001.ctl")
print(f"Error type: {model.sigma_type}")
print(f"Sigma value: {model.sigma_init}")
# Detailed error block
parsed = model.metadata.get("parsed_control_file")
if parsed and parsed.error_block:
error = parsed.error_block
print(f"THETA indices in error: {error.theta_indices}")
print(f"SIGMA fixed to 1: {error.sigma_fixed_to_1}")
Covariate Effects
model = import_nonmem("run001.ctl")
# Covariate effects extracted from $PK
effects = model.metadata.get("covariate_effects", [])
for effect in effects:
print(f"{effect['covariate']} on {effect['parameter']}:")
print(f" Type: {effect['type']}")
print(f" THETA: {effect['theta_index']}")
print(f" Reference: {effect['reference_value']}")
Supported Effect Types
| Type |
NONMEM Pattern |
Example |
power |
(COV/REF)**THETA(n) |
(WT/70)**0.75 |
linear |
(1 + THETA(n)*(COV-REF)) |
(1 + 0.01*(AGE-40)) |
exponential |
EXP(THETA(n)*(COV-REF)) |
EXP(0.005*(CRCL-100)) |
Using Imported Models
Simulation
from neopkpd.import_ import import_nonmem
from neopkpd import simulate
# Import model
model = import_nonmem("run001.ctl")
# Create model spec for simulation
# (Automatic conversion)
times = list(range(0, 49)) # 0-48 hours
doses = [{"time": 0.0, "amount": 500.0, "route": "oral"}]
result = simulate(
model_kind=model.model_kind,
params=model.params,
times=times,
doses=doses
)
print(f"Cmax: {max(result.concentrations):.2f}")
Population Simulation
from neopkpd.import_ import import_nonmem
from neopkpd import simulate_population
model = import_nonmem("run001.ctl")
# Simulate population with imported IIV
pop_result = simulate_population(
model_kind=model.model_kind,
params=model.params,
omega=model.omega_init,
sigma=model.sigma_init,
sigma_type=model.sigma_type,
n_subjects=100,
times=list(range(0, 49)),
doses=[{"time": 0.0, "amount": 500.0}]
)
Validation
Check Import Quality
model = import_nonmem("run001.ctl")
# Check for warnings
if model.warnings:
print("Import warnings:")
for w in model.warnings:
print(f" ⚠️ {w}")
else:
print("✓ No warnings")
# Verify model type
if model.model_kind == "Unknown":
print("❌ Model type not recognized")
else:
print(f"✓ Model type: {model.model_kind}")
# Check parameters
print(f"✓ {len(model.params)} parameters imported")
print(f"✓ {len(model.omega_names)} random effects imported")
Unsupported Features
Not Supported
| Feature |
Workaround |
| Custom $DES |
Manual model definition |
| Complex IF statements |
Simplify before import |
| ALAG (lag time) |
Manual specification |
| F1, F2 (bioavailability) |
Assumes F=1 |
| R1, D1 (infusion params) |
Specify in dose events |
| MTIME |
Not supported |
| $MIX (mixture models) |
Not supported |
| $MODEL (custom compartments) |
Use predefined models |
Detection
model = import_nonmem("run001.ctl")
# Warnings include unsupported features
for w in model.warnings:
if "unsupported" in w.lower():
print(f"Unsupported: {w}")
Complete Example
from neopkpd.import_ import import_nonmem
from neopkpd import simulate
import math
# Import NONMEM control file
print("Importing NONMEM control file...")
model = import_nonmem("run001.ctl")
# Display results
print("=" * 50)
print("NONMEM Import Results")
print("=" * 50)
print(f"\nSource: {model.source_file}")
print(f"Model type: {model.model_kind}")
print("\n--- Fixed Effects ---")
for name, value in model.params.items():
print(f" {name} = {value}")
print("\n--- Random Effects ---")
for i, name in enumerate(model.omega_names):
omega_sq = model.omega_init[i][i]
cv = (math.exp(omega_sq) - 1) ** 0.5 * 100
print(f" {name}: ω² = {omega_sq:.4f} (CV ≈ {cv:.1f}%)")
print(f"\n--- Residual Error ---")
print(f" Type: {model.sigma_type}")
print(f" Value: {model.sigma_init}")
print("\n--- Warnings ---")
if model.warnings:
for w in model.warnings:
print(f" ⚠️ {w}")
else:
print(" None")
# Validate with simulation
print("\n--- Validation Simulation ---")
times = list(range(0, 49, 1))
doses = [{"time": 0.0, "amount": 500.0, "route": "oral"}]
result = simulate(
model_kind=model.model_kind,
params=model.params,
times=times,
doses=doses
)
cmax = max(result.concentrations)
tmax = times[result.concentrations.index(cmax)]
print(f"Cmax: {cmax:.2f}")
print(f"Tmax: {tmax} h")
print("✓ Simulation successful")
See Also