COMPARTMENTAL_PK
Overview
The COMPARTMENTAL_PK function simulates drug concentration over time using a one-compartment pharmacokinetic model with first-order elimination kinetics. This model represents the simplest pharmacokinetic system where the drug distributes instantaneously throughout a single compartment (the body) and is eliminated at a rate proportional to its concentration. The governing differential equation is:
\frac{dC}{dt} = -k_{el} \cdot C
where C is the drug concentration, t is time, and k_{el} is the elimination rate constant. The analytical solution to this equation is C(t) = C_0 \cdot e^{-k_{el} \cdot t}, where C_0 is the initial concentration. However, this function uses numerical integration to solve the system, which provides a foundation for extending to more complex multi-compartment models or non-linear elimination processes.
The implementation uses scipy.integrate.solve_ivp, a versatile ordinary differential equation (ODE) solver from the SciPy library. The function supports multiple integration methods including explicit Runge-Kutta methods (RK45, RK23, DOP853) suitable for non-stiff problems, and implicit methods (Radau, BDF, LSODA) for stiff systems. The default RK45 method uses a fifth-order accurate Runge-Kutta formula with adaptive step sizing and error control.
One-compartment models are widely used in early-stage drug development, clinical pharmacology, and therapeutic drug monitoring. The volume of distribution (V_d) parameter relates the administered dose to the resulting plasma concentration, while the elimination rate constant determines the drug’s half-life (t_{1/2} = \ln(2)/k_{el}). For more information on compartmental modeling in pharmacokinetics, see Compartmental models in pharmacokinetics on Wikipedia.
This example function is provided as-is without any representation of accuracy.
Excel Usage
=COMPARTMENTAL_PK(c_initial, k_el, v_d, t_start, t_end, timesteps, solve_ivp_method)
c_initial(float, required): Initial drug concentration (mass/volume).k_el(float, required): Elimination rate constant (1/time).v_d(float, required): Volume of distribution (volume).t_start(float, required): Start time for integration.t_end(float, required): End time for integration.timesteps(int, optional, default: 10): Number of output time points.solve_ivp_method(str, optional, default: “RK45”): Integration method.
Returns (list[list]): 2D list with header [t, C], or error message string.
Examples
Example 1: Demo case 1
Inputs:
| c_initial | k_el | v_d | t_start | t_end | timesteps |
|---|---|---|---|---|---|
| 10 | 0.2 | 5 | 0 | 10 | 10 |
Excel formula:
=COMPARTMENTAL_PK(10, 0.2, 5, 0, 10, 10)
Expected output:
| t | C |
|---|---|
| 0 | 10 |
| 1.1111 | 8.0074 |
| 2.2222 | 6.4106 |
| 3.3333 | 5.1308 |
| 4.4444 | 4.1091 |
| 5.5556 | 3.2933 |
| 6.6667 | 2.6376 |
| 7.7778 | 2.1116 |
| 8.8889 | 1.6911 |
| 10 | 1.3545 |
Example 2: Demo case 2
Inputs:
| c_initial | k_el | v_d | t_start | t_end | timesteps | solve_ivp_method |
|---|---|---|---|---|---|---|
| 5 | 0.1 | 2 | 0 | 5 | 10 | RK23 |
Excel formula:
=COMPARTMENTAL_PK(5, 0.1, 2, 0, 5, 10, "RK23")
Expected output:
| t | C |
|---|---|
| 0 | 5 |
| 0.5556 | 4.7298 |
| 1.1111 | 4.4739 |
| 1.6667 | 4.2316 |
| 2.2222 | 4.0023 |
| 2.7778 | 3.7854 |
| 3.3333 | 3.5804 |
| 3.8889 | 3.3868 |
| 4.4444 | 3.2038 |
| 5 | 3.0306 |
Example 3: Demo case 3
Inputs:
| c_initial | k_el | v_d | t_start | t_end | timesteps | solve_ivp_method |
|---|---|---|---|---|---|---|
| 20 | 0.05 | 10 | 0 | 20 | 10 | BDF |
Excel formula:
=COMPARTMENTAL_PK(20, 0.05, 10, 0, 20, 10, "BDF")
Expected output:
| t | C |
|---|---|
| 0 | 20 |
| 2.2222 | 17.8976 |
| 4.4444 | 16.0144 |
| 6.6667 | 14.3295 |
| 8.8889 | 12.8231 |
| 11.1111 | 11.4754 |
| 13.3333 | 10.2698 |
| 15.5556 | 9.1904 |
| 17.7778 | 8.2245 |
| 20 | 7.3598 |
Example 4: Demo case 4
Inputs:
| c_initial | k_el | v_d | t_start | t_end | timesteps |
|---|---|---|---|---|---|
| 1 | 0.5 | 1 | 0 | 1 | 10 |
Excel formula:
=COMPARTMENTAL_PK(1, 0.5, 1, 0, 1, 10)
Expected output:
| t | C |
|---|---|
| 0 | 1 |
| 0.1111 | 0.946 |
| 0.2222 | 0.8948 |
| 0.3333 | 0.8465 |
| 0.4444 | 0.8007 |
| 0.5556 | 0.7575 |
| 0.6667 | 0.7165 |
| 0.7778 | 0.6778 |
| 0.8889 | 0.6412 |
| 1 | 0.6065 |
Python Code
from scipy.integrate import solve_ivp as scipy_solve_ivp
import numpy as np
import math
def compartmental_pk(c_initial, k_el, v_d, t_start, t_end, timesteps=10, solve_ivp_method='RK45'):
"""
Numerically solves the basic one-compartment pharmacokinetics ODE using scipy.integrate.solve_ivp.
See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.solve_ivp.html
This example function is provided as-is without any representation of accuracy.
Args:
c_initial (float): Initial drug concentration (mass/volume).
k_el (float): Elimination rate constant (1/time).
v_d (float): Volume of distribution (volume).
t_start (float): Start time for integration.
t_end (float): End time for integration.
timesteps (int, optional): Number of output time points. Default is 10.
solve_ivp_method (str, optional): Integration method. Valid options: RK45, RK23, DOP853, Radau, BDF, LSODA. Default is 'RK45'.
Returns:
list[list]: 2D list with header [t, C], or error message string.
"""
valid_methods = ['RK45', 'RK23', 'DOP853', 'Radau', 'BDF', 'LSODA']
# Validate input types
try:
c0 = float(c_initial)
k = float(k_el)
vd = float(v_d)
t0 = float(t_start)
t1 = float(t_end)
ntp = int(timesteps)
except Exception:
return "Invalid input: All initial values, parameters, and timesteps must be numbers."
if t1 <= t0:
return "Invalid input: t_end must be greater than t_start."
if ntp <= 0:
return "Invalid input: timesteps must be a positive integer."
if k <= 0 or vd <= 0:
return "Invalid input: k_el and v_d must be positive."
if solve_ivp_method not in valid_methods:
return f"Invalid input: solve_ivp_method must be one of {valid_methods}."
# Create time array for evaluation
t_eval = np.linspace(t0, t1, ntp)
# ODE system: dC/dt = -k * C (first-order elimination)
def pk_ode(t, y):
c = y[0]
dc_dt = -k * c
return [dc_dt]
# Integrate using scipy solve_ivp
try:
sol = scipy_solve_ivp(
pk_ode,
[t0, t1],
[c0],
method=solve_ivp_method,
dense_output=False,
t_eval=t_eval
)
except Exception as e:
return f"Integration error: {e}"
if not sol.success:
return f"Integration failed: {sol.message}"
# Format output: header row then data rows
result = [["t", "C"]]
for i in range(len(sol.t)):
t_val = float(sol.t[i])
c_val = float(sol.y[0][i])
# Validate output values
if math.isnan(t_val) or math.isnan(c_val):
return "Invalid output: NaN detected in results."
if math.isinf(t_val) or math.isinf(c_val):
return "Invalid output: Infinite value detected in results."
result.append([t_val, c_val])
return result