NLME integration¶
Basic Model Structure and Statements¶
A PML model defines the structure of a pharmacokinetic/pharmacodynamic model. The basic structure consists of a
main block enclosed in curly braces, typically named test(), containing statements that define the model’s components.
The fundamental structure of a PML model is:
modelName() {
// Statements defining the model
}
modelName(): The name of the model (e.g.,test,oneCompartmentModel). The parentheses()are required. While any valid name can be used,test()is a common convention for simple models.{ ... }: Curly braces define the model block. All model statements must be within this block.Statements within the block define the model’s components (e.g., parameters, equations, error models). Statement order is generally not important, except within
sequenceblocks and for assignment statements.
Statements¶
Statements are the building blocks of a PML model. They define parameters, equations, and other aspects of the model. Statements can span multiple lines, and semicolons are optional separators.
Types of Statements: PML includes various statement types, including:
Assignment statements (e.g.,
C = A1 / V)Declaration statements (e.g.,
stparm,fixef,ranef,covariate,error)Control flow statements (within
sequenceblocks:if,while,sleep)Model component statements (e.g.,
deriv,observe,multi,LL,dosepoint,cfMicro)
Order: In general, the order of statements does not matter in PML, except:
Within
sequenceblocks, where statements are executed sequentially.For assignment statements, where the order of assignments to the same variable matters.
When defining micro constants before
cfMicro
Semicolons: Semicolons (
;) are optional separators between statements. They can improve readability but are not required.Line Boundaries: Statements can span multiple lines.
Keywords: statement, assignment, declaration, semicolon, order
See also: PML Model Structure, Blocks, Variables, Assignment Statements
Blocks¶
Blocks in PML are sets of statements enclosed in curly
braces {}. They are used to group related statements and, in the
case of sequence blocks, to define a specific execution order.
Main Model Block: The entire model is enclosed in a block (e.g.,
test() { ... }).sequence Block: The
sequenceblock is a special type of block that defines a sequence of statements to be executed in order, at specific points in time. This is used for handling discontinuous events and time-dependent actions.Other Blocks: There are no other named blocks besides the main model block and
sequenceblocks.dobefore, doafter blocks: used to define some actions related to dose delivery and observations.
Example (sequence block):
sequence {
A = 10 // Initialize compartment A
sleep(5) // Wait for 5 time units
A = A + 5 // Add to compartment A
}
Keywords: block, curly braces, sequence, scope
See also: PML Model Structure, Statements, sequence
Variables¶
Variables in PML represent quantities within the model, such as parameters, compartment amounts, and concentrations. Variables can be declared explicitly or defined through assignment.
Data Types: All variables in PML are double-precision floating-point numbers (you can declare them using
realordouble, which are equivalent).Declared Variables: Introduced by declaration statements like
deriv,real,stparm,sequence, andcovariate. These can be modified at specific points in time (e.g., withinsequenceblocks). Examples:deriv(A = -k * A)(DeclaresAas an integrator variable)real(x)(Declaresxas a real variable, modifiable insequenceblocks)stparm(V = tvV * exp(nV))(DeclaresVas a structural parameter)
Functional Variables: Introduced by assignment at the top level of the model (i.e., not within a
sequenceblock). These are considered to be continuously calculated and cannot be modified withinsequenceblocks. Example:C = A / V(DefinesCas the concentration, calculated fromAandV)
Variable Names: Variable names must:
Be case-sensitive
Not contain special characters like “.”
Not begin with an underscore “_”
Predefined variables PML contains predefined variables like
t
Keywords: variable, declaration, assignment, scope, real, double, declared, functional
See also: Statements, Assignment Statements, stparm, fixef, ranef, deriv, real
Predefined Variables¶
t. Represents time. This variable is automatically available in time-based models (models containing at least onederivstatement).
Warning
The use of t on the right-hand-side of deriv statement is not recommended.
Note
Contrary to some other modeling languages, pi (the mathematical constant ≈ 3.14159) is not a predefined
variable in PML. You must define it explicitly (e.g., pi = 3.141592653589793).
Keywords: predefined variables, t, time, built-in variables
Assignment Statements¶
Assignment statements are used to define the value of a
variable based on an expression. They use the = operator. Variables
defined by top-level assignment are considered continuously calculated.
Syntax:
variable = expressionexpressionCan be any valid mathematical expression involving numbers, variables, operators, and functions.Top-Level Assignment: Assignments made at the top level of the model (outside of
sequenceblocks) define functional variables. These are continuously updated as the values of the variables they depend on change.
Note
Functional variables are analogous to variables defined by simple assignment within NONMEM’s $PK or $PRED blocks (before the Y= line).
Order matters: The order of the statements matter.
Multiple assignments: It is allowable to have multiple assignment statements assigned to the same variable, in which case the order between them matters.
Important Note: Variables defined and assigned via top-level assignment cannot be modified within
sequenceblocks.
Example:
test() {
A = 10 # Assigns the value 10 to A
V = 5 # Assigns the value 5 to V
C = A / V # Calculates C (concentration) continuously
C = C + 2 # C will be equal to A/V + 2
}
Keywords: assignment, =, equation, expression, continuous
See also: Variables, Statements
Parameter Declarations¶
stparm¶
The stparm statement defines structural parameters in the model, which are the core parameters describing the
pharmacokinetic/pharmacodynamic processes. stparm statements can include fixed effects, random effects, and covariate effects.
Purpose: Defines structural parameters and their relationships to fixed effects, random effects, and covariates.
Syntax:
stparm(parameter = expression)parameter: The name of the structural parameter (e.g.,V,Cl,Ka,EC50).expression: An expression defining how the parameter is calculated. This expression can include:Fixed effects (typically named with a
tvprefix, e.g.,tvV).Random effects (typically named with an
nprefix, e.g.,nV).Covariates.
Mathematical operators and functions.
Time-dependent logic using the ternary operator (
? :).
Common Distributions:
Log-Normal:
parameter = tvParameter * exp(nParameter)(Parameter is always positive)Normal:
parameter = tvParameter + nParameterLogit-Normal:
parameter = ilogit(tvParameter + nParameter)(Parameter is between 0 and 1)
Multiple stparm Statements: A model can have multiple
stparmstatements.
Note
stparm defines structural parameters. These are typically the parameters you are interested in estimating.
Variables defined by simple assignment in NONMEM’s $PK (before the Y= line) should not be defined using stparm in PML
unless they are also associated with a fixef (and thus represent an estimable parameter – a THETA in NONMEM). If a NONMEM variable is
assigned a value in $PK and is not a THETA, represent it in PML with a top-level assignment statement, not with stparm.
Execution: Structural parameter statements are executed before anything else, except sequence statements to initialize them.
Conditional Logic: Use the ternary operator (
? :) for conditional logic withinstparmexpressions, notif/else.Important Note: When using time-dependent logic within
stparm, closed-form solutions are not applicable, and the model will rely on a numerical ODE solver.
Example:
stparm(V = tvV * exp(nV)) // Volume (log-normal)
stparm(Ka = tvKa) // Absorption rate constant (fixed effect)
stparm(Cl = tvCl * exp(dCldSex1*(Sex==1)) * exp(nCl + nClx0*(Period==1) + nClx1*(Period==2))) // Clearance (log-normal) Example with occasion covariate Period with 2 levels amd categorical covariate Sex with 2 levels
Keywords: structural parameter, stparm, parameter, population, fixed effect, random effect, covariate, IIV
See also: fixef, ranef, Covariates, Fixed Effects, Random Effects, Log-Normal Distribution, if and Ternary Operator
fixef¶
The fixef statement defines fixed-effect parameters
(often called “thetas” in NONMEM), representing the typical or
population values of structural parameters. It also allows specifying
initial estimates, bounds, and enabling/disabling fixed effects for
covariate search.
Purpose: Defines fixed-effect parameters and their properties.
Syntax:
fixef(parameter[(freeze)][(enable=c(int))] [= c([lower bound],[ initial estimate],[ upper bound])])parameter: The name of the fixed-effect parameter (e.g.,tvV,tvCl,dCldSex).freeze: (Optional) If present, the parameter is not estimated; it’s fixed at its initial estimate.enable=c(int): (Optional) Controls whether the parameter is considered during covariate search procedures (0 = disabled, 1 = enabled). Has no effect on a regular model fit.= c([lower bound],[ initial estimate],[ upper bound])]: (Optional) Specifies the lower bound, initial estimate, and upper bound. Any of these can be omitted (using commas as placeholders). If only one value is given without bounds, it represents the initial value
Note
May use freeze for parameters that are defined and assigned values in the NONMEM $PK block but are not THETAs (i.e., not estimated),
but probably it is better to use the direct assignment outside fixef/stparm statements
Default Initial Values: If no initial estimate is provided:
Covariate effects: Default initial value is 0.
Other fixed effects: Default initial value is 1.
Multiple fixef: It is possible to have more than one
fixefstatement.
Example:
fixef(tvV = c(0, 10, 100)) // Initial estimate 10, bounds [0, 100]
fixef(tvCl = c(, 5, )) // Initial estimate 5, no bounds
fixef(dCldSex1(enable=c(0)) = c(, 0, )) // Enabled for covariate search, initial estimate 0
Keywords: fixed effect, fixef, population parameter, typical value, initial estimate, bounds, enable
See also: stparm, ranef, Covariates, Fixed Effects, Bounds
ranef¶
The ranef statement defines random-effect parameters
(often called “etas” in NONMEM), representing inter-individual
variability (IIV) in structural parameters. It also defines the
variance-covariance structure of the random effects.
Purpose: Defines random effects and their variance-covariance matrix.
Syntax:
ranef(specification)specification: Defines the random effects and their relationships. Common options include:diag(parameter1, parameter2, ...): Diagonal covariance matrix (random effects are independent).block(parameter1, parameter2, ...): Full covariance matrix (random effects can be correlated).same(parameter1, parameter2, ...): Specifies that parameters share the same variance (and covariance, if within ablock).= c(...): initial estimates for variance/covariance
The initial estimates are provided for variance-covariance matrix
Multiple ranef Statements: A model can have multiple
ranefstatements.NONMEM Equivalent:
diag(nV, nCl)is similar to NONMEM’s:
$OMEGA DIAG 0.04 ;nV 0.09 ;nCl
block(nV, nCl)is similar to NONMEM’s:
$OMEGA BLOCK(2) 0.04 ;nV 0.02 0.09 ;nCl
Examples:
ranef(diag(nV, nCl) = c(0.04, 0.09)) // Diagonal covariance: V and Cl vary independently
ranef(block(nV, nCl) = c(0.04, 0.02, 0.09)) // Full covariance: V and Cl are correlated
ranef(diag(nV) = c(0.04), diag(nCl) = c(0.09)) // Equivalent to the first example
ranef(block(nCl1, nCl2) = c(1, 0.5, 2), same(nCl3, nCl4)) //block + same
Keywords: random effect, ranef, inter-individual variability, IIV, variance, covariance, omega, diag, block, same
See also: stparm, fixef, Random Effects, Variance, Covariance, Inter-Individual Variability
Fixed Effects¶
Fixed effects represent the typical or population values of parameters in the model. They are estimated from the data and are assumed to be constant across all individuals.
* Represented by fixef * Usually defined with tv prefix
Keywords: fixed effect, population parameter, typical value, theta, fixef
See also: fixef, stparm, Random Effects
Random Effects¶
Random effects represent the variability in parameters between individuals. They are assumed to be drawn from a distribution (usually normal or log-normal) with a mean of zero and an estimated variance-covariance matrix.
* Represented by ranef * Usually defined with n prefix
Keywords: random effect, inter-individual variability, IIV, eta, ranef, variance, covariance
See also: ranef, stparm, Fixed Effects, Variance, Covariance, Inter-Individual Variability
Log-Normal Distribution¶
The log-normal distribution is commonly used for parameters that must be positive (e.g., clearance, volume). It’s implemented in stparm using the exp() function.
Syntax:
parameter = tvParameter * exp(nParameter)tvParameter: The typical value (fixed effect).nParameter: The random effect (normally distributed with mean 0).exp(): The exponential function. This ensures thatparameteris always positive, regardless of the value ofnParameter.
Why Log-Normal? Many pharmacokinetic parameters (e.g., clearance, volume) can only have positive values. The log-normal distribution guarantees positivity.
Example:
stparm(
Cl = tvCl * exp(nCl) // Clearance is log-normally distributed
)
Keywords: log-normal, distribution, positive parameter, stparm, exp
See also: stparm, Fixed Effects, Random Effects, Normal Distribution
Normal Distribution¶
The normal distribution can be used for parameters that can take on both positive and negative values.
Syntax:
parameter = tvParameter + nParameter
Example:
stparm(
Effect = tvEffect + nEffect // Effect can be positive or negative
)
Keywords: normal, distribution, stparm, additive
See also: stparm, Log-Normal Distribution
Bounds¶
Bounds (lower and upper limits) can be specified for fixed effects within the fixef statement. This constrains the parameter
values during estimation, preventing them from taking on unrealistic values.
Syntax:
fixef(parameter = c([lower bound],[ initial estimate],[ upper bound]))Any value could be skipped
Example:
fixef(
tvCl = c(0, 5, 20) // Cl must be between 0 and 20, with an initial estimate of 5
)
Keywords: bounds, fixef, lower bound, upper bound, parameter constraints
See also: fixef
Covariates¶
Covariates are variables that can influence the model’s parameters or outcomes. They represent characteristics of the individuals, their environment, or external influences. PML supports continuous, categorical, and occasional covariates, any of which can be time-varying.
Purpose: To incorporate the effects of individual characteristics, external factors, or time-dependent changes on the model.
Types of Covariates:
Continuous: Take on a continuous range of numerical values (e.g., weight, age, creatinine clearance).
Categorical: Take on a limited number of discrete values representing categories (e.g., sex, disease status, treatment group).
Occasional: Represent different occasions or periods within an individual’s data (e.g., different treatment cycles). These are inherently time-varying.
Declaration: Covariates must be declared using either the
covariate,fcovariate, orinterpolatestatement.Time-Varying Behavior: Any type of covariate (continuous, categorical, or occasional) can be time-varying. The
covariate,fcovariate, andinterpolatestatements control how the covariate values are handled over time.Usage: Covariates can be used in:
stparmstatements to model their effects on structural parameters.Expressions within the model (e.g., in
derivstatements or likelihood calculations).
Data Mapping: Covariates are linked to columns in the input dataset through column mappings. This is typically done in a separate file or within the user interface of the modeling software (e.g., Phoenix NLME). The correct syntax for mapping is, for example,
covr(wt<-"wt").
Keywords: covariate, independent variable, predictor, time-varying, categorical, continuous, occasional, fcovariate, covariate, input, data mapping
See also: covariate, fcovariate, interpolate, stparm, Continuous Covariates, Categorical Covariates, Occasional Covariates, Time-Varying Covariates, Data Mapping
covariate¶
The covariate statement declares a covariate and
specifies that its values should be extrapolated backward in time. The
most recent value of the covariate is used until a new value is
encountered.
Syntax:
covariate(covariateName)orcovariate(covariateName())covariateName: The name of the covariate.(): Empty parentheses are required for categorical covariates and recommended for occasional covariates (though technically optional for occasion).
Backward Extrapolation: The value of the covariate at any given time is the most recent value observed before that time. If no value is given at time=0, the first available value is used.
Example:
covariate(Weight) // Declares 'Weight' as a continuous covariate
covariate(Sex()) // Declares 'Sex' as a categorical covariate
NONMEM Equivalent: There is no direct equivalent in NONMEM. NONMEM’s default behavior for covariates is backward extrapolation, similar to PML’s covariate.
Keywords: covariate, backward extrapolation, time-varying covariate
See also: Covariates, fcovariate, interpolate, Time-Varying Covariates, Categorical Covariates, Occasional Covariates
fcovariate¶
The fcovariate statement declares a covariate and
specifies that its values should be extrapolated forward in time. The
current value is used until a new value is encountered. fcovariate
is generally preferred for occasion covariates.
Syntax:
fcovariate(covariateName)orfcovariate(covariateName())covariateName: The name of the covariate.(): Empty parentheses are required for categorical covariates and recommended (but technically optional) for occasional covariates.
Forward Extrapolation: The value of the covariate at any given time is the value observed at that time, and this value is carried forward until a new value is encountered. The first available value is also carried backward if no value is given at time=0.
Occasion Covariates:
fcovariateis generally preferred for occasion covariates.
Example:
fcovariate(DoseRate) // Declares 'DoseRate' as a time-varying covariate
fcovariate(Occasion()) // Declares 'Occasion' as an occasion covariate (recommended)
fcovariate(Occasion) // also valid, but less explicit
NONMEM Equivalent: There’s no direct equivalent in NONMEM. You would typically handle forward extrapolation of occasion covariates implicitly through the structure of your dataset and control stream.
Keywords: fcovariate, forward extrapolation, time-varying covariate, occasion covariate
See also: Covariates, covariate, interpolate, Time-Varying Covariates, Occasional Covariates, Categorical Covariates
interpolate¶
The interpolate statement declares a continuous
covariate whose values are linearly interpolated between the time points
at which it is set.
Syntax:
interpolate(covariateName)covariateName: The name of the covariate.
Linear Interpolation: The value of the covariate varies linearly between the time points at which it is set in time-based models.
Extrapolation: If no covariate value is given, the latest value is carried forward. If no value is given at time=0, the first available value is used.
Continuous Covariates Only:
interpolatecan only be used with continuous covariates.
Example:
interpolate(InfusionRate)
NONMEM Equivalent: There is no direct equivalent in NONMEM. Linear interpolation of covariates is not a built-in feature. You would typically pre-process your data to create interpolated values if needed.
Keywords: interpolate, covariate, linear interpolation, time-varying covariate, continuous covariate
See also: Covariates, covariate, fcovariate, Time-Varying Covariates, Continuous Covariates
Continuous Covariates¶
Continuous covariates take on a continuous range of numerical values. They can be time-varying or constant within an individual.
Examples: Weight, age, creatinine clearance, body surface area.
Declaration: Declared using
covariate,fcovariate, orinterpolate.Usage: Used directly in mathematical expressions within
stparmstatements or other model equations.Mapping:
covr(Wt<-"Wt")
Example:
fcovariate(Weight) // Weight as a time-varying covariate
stparm(
Cl = tvCl * (Weight / 70)^0.75 * exp(nCl) // Allometric scaling of clearance
)
Keywords: continuous covariate, covariate, numerical, time-varying
See also: Covariates, covariate, fcovariate, interpolate, stparm, Data Mapping
Categorical Covariates¶
Categorical covariates take on a limited number of discrete values representing categories. PML requires you to define the mapping between the data values and category names in the column definition file.
Examples: Sex (Male/Female), Disease Status (Normal/Mild/Severe), Treatment Group (Placebo/Active).
Declaration: Declared using
covariate(CovariateName())orfcovariate(CovariateName()). The empty parentheses()are required.Mapping (Column Definition File): You must map the covariate and its categories in the column definition file (or equivalent interface in your software).
With Labels: If your data file already contains meaningful category labels (e.g., “Male”, “Female”), map them directly. The general NONMEM-style syntax is:
covr(Sex <- "Sex"("Male" = 0, "Female" = 1)) // ExampleThis maps the “Sex” column in the data to a covariate namedSexin the model, with “Male” coded as 0 and “Female” as 1. The first category is used as a reference category.Without Labels: If your data file uses numeric codes (e.g., 0, 1) without explicit labels, you can define the labels during mapping using empty parentheses:
covr(Sex <- "Sex"()) // ExampleIn that case the first unique value will be used as a reference.
Usage in stparm: You typically use logical expressions
(CovariateName == value)withinstparmstatements to model the effects of different categories. This creates implicit “dummy variables.” The first category encountered in the data is treated as the reference category, and fixed effects for other categories represent differences from the reference. Use the ternary operator (? :) for more complex conditional logic.
Example (PML code):
test() {
fcovariate(Sex()) // Declare Sex as a categorical covariate
stparm(
Cl = tvCl * exp(dClSex * (Sex == 1) + nCl) // Effect of Sex on Cl
)
fixef(
tvCl = c(, 10, ),
dClSex = c(, 0, ) // Fixed effect for Sex=1 (relative to Sex=0)
)
ranef(diag(nCl) = c(0.25))
// ... rest of the model ...
}
NONMEM Equivalent: The PML code above is similar to the following in NONMEM (using abbreviated code):
$INPUT ... SEX ...
$SUBROUTINES ...
$PK
TVCL = THETA(1)
DCLSEX = THETA(2)
CL = TVCL * EXP(DCLSEX*(SEX.EQ.1) + ETA(1))
$THETA
(0, 10) ; TVCL
(0) ; DCLSEX
$OMEGA
0.25 ; ETA(1) variance (IIV on CL)
Keywords: categorical covariate, covariate, discrete, categories, dummy variable, indicator variable, time-varying, data mapping
See also: Covariates, covariate, fcovariate, stparm, Fixed Effects, Data Mapping, Dummy Variables, if and Ternary Operator
Occasion Covariates and Inter-Occasion Variability (IOV)¶
Occasion covariates represent distinct periods in a subject’s data (e.g., treatment cycles). Their primary use is to model Inter-Occasion Variability (IOV), which is the random, unpredictable variability within a single subject from one occasion to the next. This is modeled using random effects, not fixed effects.
Concept: While Inter-Individual Variability (IIV) describes why Subject A’s average parameter value is different from Subject B’s, IOV describes why Subject A’s parameter value during Occasion 1 is different from their own value during Occasion 2.
Declaration: Always declare the occasion variable, preferably with
fcovariate(Occasion()). The parentheses()are recommended for clarity.
Correct Implementation: Modeling IOV with Random Effects
This is the standard and pharmacokinetically correct approach. It assumes that a subject’s parameter value for a specific occasion is a random deviation from their overall mean parameter value.
PML Syntax: You create a separate random effect (
eta) for each occasion level and add it to the structural parameter expression.stparm(Param = tvParam * exp( nParam + nParamOcc1*(Occasion==1) + nParamOcc2*(Occasion==2) + ... ))
The term
nParamrepresents the subject’s IIV (their deviation from the population typical valuetvParam).The terms
nParamOcc1,nParamOcc2, etc., represent the IOV (the deviation for that specific occasion from the subject’s mean).
Example (IOV on Clearance):
test() {
fcovariate(Occasion()) // Assume Occasion has levels 1, 2, 3
// Cl includes a random effect for IIV (nCl) and separate random effects for IOV on each occasion
stparm(Cl = tvCl * exp( nCl + nCl_Occ1*(Occasion==1) + nCl_Occ2*(Occasion==2) + nCl_Occ3*(Occasion==3) ))
fixef(tvCl = c(, 1, ))
ranef(diag(nCl) = c(1)) // IIV variance on Cl
// Define the IOV variances. 'same()' is often used to assume variability is equal across occasions.
ranef(diag(nCl_Occ1) = c(1), same(nCl_Occ2), same(nCl_Occ3))
...
}
Incorrect Implementation: Modeling Occasions with Fixed Effects
It is also possible to model an occasion as a fixed effect, but this answers a different scientific question and is generally not what is meant by an “occasion effect.” A fixed effect tests whether the population average parameter is systematically different on one occasion versus another (e.g., “Is clearance for all subjects 20% lower on Occasion 2?”). This is a cohort effect, not within-subject variability.
Example of an (often incorrect) fixed-effect model:
// This model tests if the POPULATION mean Cl is different on Occasion 2 vs Occasion 1
stparm(Cl = tvCl * exp( dCldOcc2*(Occasion==2) ) * exp(nCl))
fixef(dCldOcc2 = c(, 0, )) // Fixed effect for Occasion 2
pyDarwin Automation Note: When searching for IOV on a parameter, the
token should provide two options: one with only the base IIV random
effect, and one that adds the IOV random effects. The token must swap
out both the stparm expression and the corresponding ranef
block.
Correct Token Structure for IOV on Clearance (_nCl):
"_nCl": [
[
"* exp(nCl)",
"ranef(diag(nCl) = c(1))"
],
[
"* exp(nCl + (Occasion==1)*nClOccasionx1 + (Occasion==2)*nClOccasionx2)",
"ranef(diag(nCl) = c(1))\\n\\tranef(diag(nClOccasionx1) = c(1), same(nClOccasionx2))"
]
]
This correctly links the structural model change to the necessary change in the random effects structure.
Keywords: occasion covariate, IOV, inter-occasion variability, fcovariate, random effects, fixed effects, within-subject variability
See also: fcovariate, Random Effects, ranef, Fixed Effects, stparm, Inter-Individual Variability
Time-Varying Covariates¶
Time-varying covariates are covariates whose values can change within an individual over time. They are essential for modeling dynamic processes and external influences that vary during the observation period. Any type of covariate (continuous, categorical, or occasional) can be time-varying.
Declaration: Declared using
covariate,fcovariate, orinterpolate.fcovariateis generally preferred for most time-varying covariates, especially for categorical and occasion covariates.Data: The input data must include multiple records per individual, with different time points and corresponding covariate values.
Extrapolation:
covariate: Backward extrapolation.fcovariate: Forward extrapolation (and backward extrapolation for the first value).interpolate: Linear interpolation between defined points (continuous covariates only), forward extrapolation after the last defined point.
Example:
fcovariate(DoseRate) // DoseRate can change over time
stparm(
Cl = tvCl * exp(dClDoseRate * DoseRate + nCl) // Clearance depends on DoseRate
)
// ... rest of the model ...
Keywords: time-varying covariate, covariate, fcovariate, covariate, changing covariate, dynamic covariate
See also: Covariates, covariate, fcovariate, interpolate, Continuous Covariates, Categorical Covariates, Occasional Covariates
Structural Model Definition¶
deriv¶
The deriv statement defines an ordinary differential
equation (ODE) in the model. This is used to describe how the amount of
drug (or other quantities) in a compartment changes over time. Models
with deriv statements are inherently time-based.
Purpose: To define the rate of change of a state variable (typically a compartment amount).
Syntax:
deriv(variable = expression)variable: The name of the state variable being differentiated (e.g.,A1,Aa,CumHaz). This variable is often called an “integrator variable.”expression: An expression defining the rate of change of the variable. This can involve parameters, covariates, other variables, and mathematical functions.
Time-Based Models: If a model contains any
derivstatement, it is considered a time-based model, and the built-in variablet(representing time) is automatically available.State variable modification: Variables on the left side of deriv statements can be modified when the model is stopped
Multiple deriv statements: A model will typically have multiple
derivstatements, one for each compartment or state variable being modeled.Right-hand-side Discontinuity: The use of
tvariable on the right-hand-side is discouraged.
Example:
deriv(Aa = -Ka * Aa) // Rate of change of amount in absorption compartment Aa
deriv(A1 = Ka*Aa -Cl * C) // Rate of change of amount in compartment A1
NONMEM Equivalent: The PML code above is similar to the following NONMEM code:
$DES
DADT(1) = -KA*A(1)
DADT(2) = KA*A(1) -CL*A(2)/V
Keywords: deriv, differential equation, ODE, integrator, state variable, time-based model, dynamic model
See also: Compartment Models, dosepoint, urinecpt
Compartment Models¶
Compartment models are a common type of pharmacokinetic model where the body is represented as a series of interconnected compartments. Drug movement between compartments is described by differential equations.
Compartments: Represent theoretical spaces where the drug distributes (e.g., central compartment, peripheral compartment, absorption compartment).
Differential Equations: Describe the rate of change of the amount of drug in each compartment.
Parameters: Define the rates of transfer between compartments and elimination from the body (e.g., clearance, volume, rate constants).
Implementation in PML: Compartment models can be implemented using:
derivstatements (for custom models or when flexibility is needed).cfMicroorcfMacrostatements (for standard 1, 2, or 3-compartment models).
Keywords: compartment model, compartment, differential equation, PK model, ADVAN
cfMicro¶
Provides an efficient closed-form (analytical) solution
for standard compartment models using micro-constants. This statement is
not compatible with time-varying parameters and must be replaced by
deriv statements in such cases.
Purpose: To define a standard one-, two-, or three-compartment model without explicitly writing out the differential equations, which can improve performance.
Parameterization: Uses micro-constants (e.g.,
Ke,K12,K21).Syntax:
cfMicro(A, Ke, [K12, K21], [K13, K31], [first = (Aa = Ka)])A: The amount in the central compartment.Ke: The elimination rate constant.K12,K21: (Optional) Transfer rate constants for a two-compartment model.K13,K31: (Optional) Transfer rate constants for a three-compartment model.first = (Aa = Ka): (Optional) Specifies first-order absorption from a depot compartmentAawith absorption rate constantKa.
CRITICAL LIMITATION: Time-Varying Parameters
The cfMicro statement uses a piecewise closed-form (analytical)
solution. The fundamental assumption is that model parameters (like
Cl, V, K12, etc.) are constant between time-based events
(such as doses or changes in covariate values). When an event occurs,
the system re-evaluates the parameters and applies the closed-form
solution for the next interval.
This means cfMicro can correctly handle structural parameters
that depend on time-varying covariates declared with covariate or
fcovariate, as these values change at discrete time points.
For example, a model with stparm(Cl = tvCl * (CRCL/100) * exp(nCl))
and covariate(CRCL) will work correctly with the following data, as
Cl is constant between t=0, t=2, and t=4:
ID |
TIME |
AMT |
Cobs |
CRCL |
|---|---|---|---|---|
1 |
0 |
100 |
0 |
70 |
1 |
2 |
. |
80 |
70 |
1 |
4 |
. |
70 |
60 |
However, this piecewise assumption is violated if a structural parameter changes continuously over time. This happens in two main scenarios:
A structural parameter depends on a covariate declared with
interpolate().A structural parameter’s definition explicitly includes the time variable
t.
CONSEQUENCE: Using
cfMicrowith continuously time-varying parameters will produce incorrect results without generating a syntax error.REQUIRED ACTION: If any model parameter changes continuously with time (due to
interpolate()or the use oft), you MUST usederivstatements to define the model structure. This forces the use of a numerical ODE solver that can correctly handle the dynamic parameters.Stricter Rules for cfMacro and cfMacro1: These are pure closed-form solutions and are more restrictive. They require structural parameters to be constant for the entire observation interval and do not support time-varying covariates of any kind.
Example of an INVALID model (due to interpolate):
test() {
interpolate(scr) // Makes scr a CONTINUOUSLY time-varying covariate
stparm(Cl = tvCl + dCldscr * scr) // Cl is now continuously time-varying
cfMicro(A1, Cl / V) // INVALID: cfMicro cannot handle a continuously time-varying Cl
...
}
Example of the CORRECT equivalent model:
test() {
interpolate(scr) // Continuously time-varying covariate
stparm(Cl = tvCl + dCldscr * scr) // Cl is continuously time-varying
deriv(A1 = - (Cl/V) * A1) // CORRECT: Must use deriv statement
...
}
Note
When designing a model search in pyDarwin, if any candidate model in your search space includes a time-varying covariate
effect on a structural parameter, all structural model options must be written using deriv statements. Do not mix cfMicro and
deriv based structural models in the same search if time-varying covariates are involved, as it would be an invalid comparison.
Keywords: cfMicro, closed-form solution, compartment model, micro-constants, deriv, time-varying covariate, ODE solver
See also: deriv, Compartment Models, covariate, interpolate, fcovariate, Time-Varying Covariates
cfMacro and cfMacro1¶
The cfMacro and cfMacro1 statements provide closed-form solutions for standard compartment models, parameterized
using macro-constants (coefficients and exponents of exponential terms). cfMacro offers more options, including a stripping dose.
Purpose: To define a standard compartment model without explicitly writing out the differential equations, using a macro-constant parameterization.
cfMacro Syntax:
cfMacro(A, C, DoseVar, A, Alpha, [B, Beta], [C, Gamma], [strip=StripVar], [first=(Aa=Ka)])A: The amount in the central compartment (cannot be referred to elsewhere in the model).C: The concentration variable in the model.DoseVar: A variable to record the initial bolus dose (needed foridosevar).A,Alpha: Macro-constants for the first exponential term.B,Beta: (Optional) Macro-constants for the second exponential term (two-compartment model).C,Gamma: (Optional) Macro-constants for the third exponential term (three-compartment model).strip=StripVar: (Optional) Specifies a covariate (StripVar) to provide a “stripping dose” for simulations.first = (Aa = Ka): (Optional) Specifies the first order absorption
cfMacro1 Syntax:
cfMacro1(A, Alpha, [B, Beta], [C, Gamma], [first=(Aa=Ka)])Simplified version of
cfMacro.A: Amount in the central compartment.Alpha,B,Beta,C,Gamma: Macro-constants. The response to bolus dose is predefinedfirst = (Aa = Ka): (Optional) Specifies the first order absorption
Stripping Dose: The
stripoption incfMacroallows you to specify a covariate that provides a “stripping dose” value. This is used in simulations to represent the initial dose used when the model was originally fit.
Example (cfMacro with two compartments and stripping dose):
cfMacro(A1, C1, A1Dose, A, Alpha, B, Beta, strip=A1Strip)
dosepoint(A1, idosevar = A1Dose)
covariate(A1Strip)
Example (cfMacro1 with one compartment):
cfMacro1(A, Alpha)
Keywords: cfMacro, cfMacro1, closed-form solution, compartment model, macro-constants, A, Alpha, B, Beta, C, Gamma, stripping dose
See also: Compartment Models, deriv, cfMicro, Pharmacokinetics
Micro-Constants vs. Macro-Constants¶
Compartment models can be parameterized using either
micro-constants (rate constants) or macro-constants (coefficients and
exponents of exponential terms). cfMicro uses micro-constants, while
cfMacro and cfMacro1 use macro-constants.
Micro-Constants:
Rate constants that describe the transfer of drug between compartments and elimination from the body (e.g.,
Ke,K12,K21).More directly related to the underlying physiological processes.
Used with
cfMicroandderivstatements.
Macro-Constants:
Coefficients and exponents of exponential terms in the closed-form solution (e.g.,
A,Alpha,B,Beta,C,Gamma).Less intuitive from a physiological perspective.
Used with
cfMacroandcfMacro1.
Conversion: It’s possible to convert between micro-constants and macro-constants, but the equations can be complex, especially for models with more than one compartment.
Keywords: micro-constants, macro-constants, Ke, K12, K21, A, Alpha, B, Beta, C, Gamma, parameterization, cfMicro, cfMacro
See also: cfMicro, cfMacro, cfMacro1, Compartment Models, Pharmacokinetics
dosepoint¶
The dosepoint statement specifies a compartment that can receive doses (either bolus or infusion). It also allows for options
like lag time, infusion duration/rate, bioavailability, and actions performed before/after dosing.
Purpose: To define where doses are administered in the model.
Syntax:
dosepoint(compartmentName [, tlag = expr][, duration = expr][, rate = expr][, bioavail = expr][, dobefore = sequenceStmt][, doafter = sequenceStmt][, split][, idosevar = variableName][, infdosevar = variableName][, infratevar = variableName])compartmentName: The name of the compartment receiving the dose.tlag = expr: (Optional) Time delay before dose delivery.duration = expr: (Optional) Duration of a zero-order infusion.rate = expr: (Optional) Rate of a zero-order infusion. Cannot be used withduration.bioavail = expr: (Optional) Bioavailability fraction (0 to 1).dobefore = sequenceStmt: (Optional)sequenceblock executed before dose delivery.doafter = sequenceStmt: (Optional)sequenceblock executed after dose delivery (or infusion completion).split: (Optional, rarely used) For UI compatibility.idosevar = variableName: (Optional) Captures the value of the first bolus dose.infdosevar = variableName: Captures the value of the first infusion dose.infratevar = variableName: (Optional) Captures the infusion rate of the first infusion dose.
Bolus vs. Infusion:
If neither
durationnorrateis specified, the dose is treated as a bolus (instantaneous).If
durationorrateis specified, the dose is treated as a zero-order infusion.
Multiple dosepoints: It is allowed to have several
dosepointstatements.dosepoint1 and dosepoint2:
dosepoint1is a direct synonym fordosepoint.dosepoint2has a special function: it allows defining a second dosing stream on the same compartment that already has adosepointordosepoint1defined. This is used for models where a single dose is split into multiple administration profiles (e.g., part bolus, part infusion).splitargument: When usingdosepointanddosepoint2on the same compartment to split a single dose amount from your data, you must add thesplitargument to the firstdosepointstatement. This tells the system that thedose()anddose2()mappings in the column definition file will both point to the same data column (e.g.,AMT).
Note
Without
split: The system would expectdose()anddose2()to map to two different amount columns (e.g.,AMT1,AMT2).With
split:dosepoint(Aa, bioavail=0.5, split)anddosepoint2(Aa, bioavail=0.5, ...)can both be sourced from a singleAMTcolumn.
Advanced Usage: Modeling with Multiple Dosepoints
A powerful feature of PML is the ability to use multiple dosepoint statements to model complex absorption. If two or more dosepoint
statements exist, a single dose amount (AMT) from the input data can be programmatically split between them using the bioavail
option. The sum of the bioavailability fractions across all active pathways for a given dose should typically equal 1. This is the
foundation for modeling the parallel and sequential absorption schemes used for advanced drug formulations.
See Modeling Complex Absorption Schemes for detailed examples.
Example (Bolus dose with lag time):
dosepoint(Aa, tlag = 0.5) // Bolus dose to Aa with a 0.5 time unit lag
Example (Zero-order infusion with bioavailability):
dosepoint(A1, duration = 2, bioavail = 0.8) // Infusion to A1 over 2 time units, 80% bioavailability
Example (Capturing the first bolus dose to use it in the secondary statement):
dosepoint(A1, idosevar = FirstDose)
Note
A dosepoint statement is REQUIRED for any compartment that receives doses. Without a dosepoint statement, even if your input dataset contains AMT values for that compartment, no dosing will occur in the model.
NONMEM Equivalent: * Bolus dose: In NONMEM, you’d typically use the AMT column in your dataset along with an appropriate EVID value
(usually 1) to indicate a bolus dose. * Infusion: In NONMEM, you’d use AMT, RATE (or DUR), and an EVID value (usually 4 for
infusions). * tlag: Similar to NONMEM’s ALAG * bioavail: Similar to NONMEM’s F * idosevar, infdosevar,
infratevar: There aren’t direct NONMEM equivalents. You might use workarounds with additional parameters or data items.
Keywords: dosepoint, dose, dosing, bolus, infusion, tlag, duration, rate, bioavail, dobefore, doafter, idosevar, infdosevar, infratevar, split, dosepoint1, dosepoint2
See also: sequence, Compartment Models, Bioavailability
urinecpt¶
The urinecpt statement defines an elimination
compartment, similar to deriv, but with specific behavior during
steady-state dosing: it’s ignored.
Purpose: To model the elimination of drug or a metabolite into an accumulating compartment (typically urine).
Syntax:
urinecpt(variable = expression [, fe = fractionExp])variable: The name of the elimination compartment amount.expression: Defines the rate of elimination into the compartment.fe = fractionExp: (Optional) Specifies fraction excreted
Steady-State Behavior: The key difference between
urinecptandderivis that during steady-state simulations, theurinecptstatement is ignored. This is because, at steady state, the amount in the elimination compartment is not relevant to the dynamics of the system.Resetting: The amount in the compartment could be set to 0 after being observed using observe statement and
doafteroption.
Example:
urinecpt(A0 = Cl * C) // Elimination compartment, rate proportional to concentration
NONMEM Equivalent: There isn’t a direct equivalent in NONMEM. You’d often model urine excretion using a regular compartment ($DES or
built-in ADVAN subroutine) and then, if needed for steady-state calculations, you would manually ensure that the urine compartment’s
contribution is handled appropriately (often by not including it in the objective function calculation at steady state).
Keywords: urinecpt, elimination compartment, excretion, steady-state
See also: deriv, Compartment Models, Elimination, Steady State
Observation and Error Models¶
observe¶
The observe statement links model predictions to
observed data, defining the residual error structure. Crucially, each
observe statement must include exactly one error variable. It also
handles below-quantification-limit (BQL) data.
Purpose: To connect model-predicted values to observed data and define how the model accounts for the difference (residual error) between them.
Syntax:
observe(observedVariable = expression [, bql[ = value]][, actionCode])observedVariable: The name of the observed variable (e.g.,CObs,EObs,Resp). This variable will be mapped to a column in your input data.expression: Defines the predicted value. This expression must include exactly one error variable (defined using anerrorstatement). The form of this expression, together with theerrorstatement, determines the error model (additive, proportional, etc.).bql: (Optional) Handles below-quantification-limit (BQL) data using the M3 method. See the separate entry on “BQL Handling” for details.bql: Uses dynamic LLOQ, requiring a mappedCObsBQLcolumn (or equivalent).bql = <value>: Uses a static LLOQ value. Important: This must be a numeric literal, not an expression.
actionCode: (Optional) Allows executing code (sequenceblock) before or after the observation (usingdobeforeordoafter).
Single Error Variable Restriction: This is a key difference from NONMEM. PML allows only one error variable per
observestatement. NONMEM allows combining multipleEPSterms (e.g.,Y = F*EXP(EPS(1)) + EPS(2)). In PML, you achieve combined error models using specific functional forms within theexpression, not by adding multiple error variables.Common Error Models (and how to express them in PML):
Additive:
observe(CObs = C + CEps)Observed value (
CObs) equals the predicted value (C) plus an error term (CEps). The error is constant regardless of the prediction’s magnitude.
Proportional:
observe(CObs = C * (1 + CEps))orobserve(CObs = C * exp(CEps))The error is proportional to the predicted value.
C * (1 + CEps)is a common approximation.C * exp(CEps)is a log-additive form, ensuring positivity.
Combined Additive and Proportional: PML provides two main ways to express this:
additiveMultiplicative(Built-in Form):observe(CObs = C + CEps * sqrt(1 + C^2 * (EMultStdev / sigma())^2)) error(CEps = ...) // Define CEps as usualThis form is mathematically equivalent to a combined additive and proportional error model and is often preferred for its numerical stability.EMultStdevrepresents the proportional error standard deviation, andsigma()represents the total variance when C=0.Using a Mixing Parameter (MixRatio):
stparm(CMixRatio = tvCMixRatio) // Define a structural parameter fixef(tvCMixRatio = c(, 0.1, )) // And its fixed effect error(CEps = ...) // Define the error variable observe(CObs = C + CEps * (1 + C * CMixRatio))This approach is more flexible and allows for easier interpretation of the mixing parameter.
Power:
observe(Obs = Pred + (Pred^power)*Eps)The error term’s magnitude scales with the predicted value raised to the power of power.
Multiple observe Statements: Use separate
observestatements for each observed variable (e.g., one for plasma concentration, one for a PD response). Eachobservestatement defines its own observed variable, predicted value, and error structure.
Example (Proportional Error):
error(CEps = 0.1) // Define the error variable and its SD
observe(CObs = C * (1 + CEps)) // Proportional error
Example (Combined Error - additiveMultiplicative):
error(CEps = 0.05) // Additive error SD
observe(CObs = C + CEps * sqrt(1 + C^2*(0.2/sigma())^2)) // Combined, EMultStdev=0.2
Example (Combined Error - MixRatio):
stparm(CMixRatio = tvCMixRatio)
fixef(tvCMixRatio = c(, 0.1, ))
error(CEps = 0.05)
observe(CObs = C + CEps * (1 + C * CMixRatio)) // Combined error
Example (Multiple Observations - PK and PD):
error(CEps = 0.1)
observe(CObs = C * (1 + CEps)) // PK observation
error(EEps = 2)
observe(EObs = E + EEps) // PD observation (additive error)
NONMEM Translation Note: When translating from NONMEM, remember that PML cannot directly combine multiple EPS terms in a single
Y = ... line. You must use PML’s built-in combined error forms (additiveMultiplicative or the MixRatio approach) or define a
custom likelihood using the LL statement. NONMEM’s Y = F*EXP(EPS(1)) + EPS(2) is approximated in PML by the
additiveMultiplicative form, as NONMEM uses a linear approximation for EXP(EPS) when the variances are small.
Keywords: observe, observation, error model, residual error, additive, proportional, combined, additiveMultiplicative, bql, censored data, M3 method
See also: error, Error Models, BQL Handling, LL, Data Mapping, additiveMultiplicative
error¶
The error statement defines a residual error variable
and its standard deviation. This variable is required for use within
an observe statement.
Purpose: To declare a residual error variable (often named with an “Eps” suffix) and specify its initial standard deviation.
Syntax:
error(errorVariable[(freeze)] = standardDeviation)errorVariable: The name of the error variable (e.g.,CEps,EEps). This name must be used in the correspondingobservestatement.freeze: (Optional) If present, the standard deviation is fixed at the given value and not estimated.standardDeviation: A numeric literal (e.g.,0.1,2.5) representing the initial estimate for the standard deviation.
Note
This must be a simple number. It cannot be an expression, a function call (like sqrt()), or a variable.
Default Value: If the standard deviation is not provided, the default value is 1. But not providing it is a bad practice.
Multiple error Statements: You need one
errorstatement for each error variable used in your model (usually one perobservestatement).Recommended placement: before
observestatement.
Example:
error(CEps = 0.1) // Error variable CEps, SD = 0.1
error(EEps(freeze) = 5) // Error variable EEps, fixed SD = 5
// INCORRECT - standardDeviation cannot be an expression:
// error(CEps = sqrt(0.04))
// CORRECT - Use the numeric value directly
error(CEps = 0.2) // Equivalent to sqrt(0.04) - standard deviation, not variance
Important Notes (CRITICAL):
Standard Deviation, NOT Variance: The standardDeviation in the error statement must be the standard deviation, NOT the variance. If you are translating a model from NONMEM, remember that the $SIGMA block often defines variances. You must take the square root of the NONMEM variance to obtain the correct standard deviation for PML.
Example: If NONMEM has $SIGMA 0.09, the corresponding PML would be error(CEps = 0.3) (because the square root of 0.09 is 0.3).
Numeric Literal Requirement: The standardDeviation must be a numeric literal. It cannot be an expression, a variable, or a function call.
NONMEM Equivalent: The error statement combined with the error
model specification in observe is conceptually similar to defining
error terms in NONMEM’s $ERROR block (or within
$PRED/$PREDPP). The freeze keyword corresponds to fixing the
associated SIGMA parameter in NONMEM. Important Difference:
NONMEM’s $SIGMA represents variance, while PML’s error
statement always expects the standard deviation. You must take the
square root of the variance from NONMEM’s $SIGMA when translating to
PML’s error.
Keywords: error, residual error, error variable, epsilon, standard deviation, freeze
See also: observe, Error Models, Residual Error
Error Models¶
Error models describe how the model accounts for the difference between predicted and observed values. Common models include additive, proportional, combined, and power.
Purpose: To quantify the discrepancy between model predictions and observations, reflecting measurement error, biological variability, and model misspecification.
Common Error Models (Review - with emphasis on PML implementation):
Additive:
Observed = Predicted + ErrorPML:
observe(Obs = Pred + Eps)Error is constant, regardless of prediction.
Proportional:
Observed = Predicted * (1 + Error)orObserved = Predicted * exp(Error)PML:
observe(Obs = Pred * (1 + Eps))(approximation) orobserve(Obs = Pred * exp(Eps))(log-additive)Error is proportional to the prediction.
Log-additive:
observe(CObs = C*exp(Eps))Combined Additive and Proportional:
PML (Preferred -
additiveMultiplicative):observe(CObs = C + CEps * sqrt(1 + C^2 * (EMultStdev / sigma())^2)) error(CEps = ...) // Define CEps as usualWhere ‘EMultStdev’ is proportional error standard deviation, and ‘sigma()’ represents the variance when C=0.PML (Using
MixRatio):stparm(CMixRatio = tvCMixRatio) fixef(tvCMixRatio = c(, 0.1, )) error(CEps = ...) observe(CObs = C + CEps * (1 + C * CMixRatio))
Power:
Observed = Predicted + (Predicted^power) * ErrorPML:
observe(Obs = Pred + (Pred^power)*Eps)
Choosing an Error Model: Select a model that reflects how variability changes with the magnitude of the predicted value.
Keywords: error model, residual error, additive, proportional, combined, additiveMultiplicative, log-additive, power
See also: observe, error, Residual Error, additiveMultiplicative
BQL Handling¶
Below Quantification Limit (BQL) data occurs when the true
value is below the assay’s detection limit. PML handles BQL data using
the M3 method within the observe statement.
Problem: BQL data are censored; we only know the value is below a limit (the LOQ).
M3 Method: Standard approach (Beal, 2001). Calculates the probability of the true value being below the LOQ.
PML Implementation: The
observestatement’sbqloption implements the M3 method.Two Ways to Use bql:
observe(Obs = ..., bql)(Dynamic LOQ):Creates a derived column
ObsBQL(e.g.,CObsBQL).Optionally map a column to this flag.
ObsBQL = 0(or missing): Observation is above the LOQ.ObsBQL = non-zero: Observation is at or below the LOQ. TheObsvalue becomes the LOQ.If
ObsBQLis not mapped, then the values inObsare used.
observe(Obs = ..., bql = <value>)(Static LOQ):Defines a fixed LOQ value (numeric literal).
Obsvalues less than<value>are treated as censored.Mapping
ObsBQLis optional; mapped values override the static value.
Mapping:
censor(CObsBQL)loq(LOQ)
Example (Dynamic LOQ):
error(CEps = 0.1)
observe(CObs = C * (1 + CEps), bql) // Dynamic BQL
// ... and in the column mappings:
// censor(CObsBQL) // Optional, for explicit BQL flags
// loq(LOQ)
Example (Static LOQ):
error(CEps = 0.1)
observe(CObs = C * (1 + CEps), bql = 0.05) // Static LOQ of 0.05
NONMEM Equivalent: In NONMEM, you’d use the M3 method.
$ERROR
CP=A(2)/V
PROP=CP*RUVCV
ADD=RUVSD
SD=SQRT(PROP*PROP+ADD*ADD)
IF (DV.GE.LLOQ) THEN
F_FLAG=0 ; ELS
Y=CP + SD*EPS1
ELSE
F_FLAG=1 ; LIKELIHOOD
Y=PHI((LLOQ-CP)/SD))
ENDIF
Keywords: BQL, below quantification limit, censored data, M3 method, observe, bql, CObsBQL, LOQ
See also: observe, Error Models, Data Mapping, Censored Data
Finding Extrema (peak function and alternative methods)¶
PML provides the peak function for automatically
finding the maximum (peak) or minimum (trough) values of a model
variable (typically concentration) and the corresponding times. An
alternative, more manual method using conditional logic is also
possible. Both approaches write results to output tables.
PML offers two primary methods for identifying and capturing the maximum
(Cmax, Tmax) and minimum (Cmin, Tmin) values of a model variable
(usually concentration, C) over time:
The peak Function (Automatic, with Interpolation):
Purpose: Automatically finds and reports the peak (maximum) or trough (minimum) of a specified variable, along with the time at which it occurs. Uses Lagrange interpolation for more precise estimation.
Syntax:
variableName = peak(internalVariableName = expression, [max/min] [, logicalExpression])variableName: A user-defined variable name to store the time of the extremum (e.g.,Tmax,Tmin). This variable will be written to the output table.internalVariableName: A user-defined variable to store the value. It is used internally by thepeak()function and should not be declared anywhere else.expression: The expression to track (usually the concentration,C).max/min: (Optional) Specifies whether to find the maximum (max, default) or minimum (min).logicalExpression: (Optional) A logical expression (e.g.,t < 6) that restricts the search. If the expression is true, the function searches for a peak/trough as specified. If false, it searches for the opposite (trough ifmax, peak ifmin).
Behavior:
Initialization: Before the first peak/trough is found (or after a
peakreset), the output variables (variableNameand theinternalVariableName) will be blank (missing) in the output table.Detection: The
peakfunction continuously monitors theexpressionand uses a 4-point window to detect potential extrema. It uses Lagrange interpolation on these points to estimate the precise time and value of the peak/trough.Updating: Once a peak/trough is found, the
variableName(time) and corresponding value are written to the output table. Subsequent peaks/troughs will only update these values if they are higher/lower (for max/min, respectively) than the previously found extremum.If the optional logical expression is defined, the extremum is updated only when the logical expression holds true.
peakreset: Thepeakreset(internalVariableName)function resets the internal state of thepeakfunction, causing it to start searching for a new extremum from that point forward. This is crucial for finding multiple peaks/troughs within a simulation. It is used in thesequenceblock.
Restrictions:
Use only in the main model block, not in
sequence,dobefore, ordoafterblocks.
Example (Finding Cmax and Tmax):
Tmax = peak(Cmax = C, max) // Find the maximum concentration (Cmax) and its time (Tmax)
Example (Finding Cmin and Tmin within a specific time window):
Tmin = peak(Cmin = C, min = (t >= 44 and t <= 52)) // Find minimum between t=44 and t=52
Example (Multiple peaks, using peakreset):
sequence { sleep(44) // Wait until the start of the interval of interest peakreset(Cmax) // Reset Cmax search peakreset(Cmin) // Reset Cmin search sleep(8) // Observe for 8 time units peakreset(Cmax) peakreset(Cmin) } Tmax = peak(Cmax = C, max) Tmin = peak(Cmin = C, min)Caution: The
peakfunction uses cubic spline interpolation, which can be sensitive to discontinuities (e.g., at the start/end of an IV infusion). Ensure sufficient simulation output density around potential extrema for accurate results.Manual Method (Using Conditional Logic):
Purpose: Provides more control over the extremum-finding process but requires more manual coding.
Method: Use assignment statements and the
maxandminfunctions within the main model block, combined with the ternary operator (? :) to track the highest/lowest values and corresponding times. All variables used with this method must be initialized appropriately.Initialize
Cmax1to a low value that is guaranteed to be exceeded, e.g. -1e6Initialize
Cmin1to a high value, e.g., 1E6.Use
max(current_value, previous_max)to update the maximum.Use
min(current_value, previous_min)to update the minimum.Use the ternary operator to update the time (
Tmax1,Tmin1) only when a new extremum is found.
Important: Unlike with the
peakfunction, you must initialize the variables used to store the maximum and minimum values.
Example (Finding Cmax and Tmax manually):
real(Cmax1, Tmax1, Cmin1, Tmin1) sequence{ Cmax1 = -1E6 Cmin1 = 1E6 } Cmax1 = max(C, Cmax1) Tmax1 = (C == Cmax1 ? t : Tmax1) // Update Tmax1 only when C equals the current Cmax1 Cmin1 = C > 0 ? min(C, Cmin1) : 1E6 // use some big value until initialization Tmin1 = C == Cmin1 ? t : Tmin1Advantages of Manual Method: Greater control, potentially more robust in some situations with discontinuities (if carefully implemented). Disadvantages of Manual Method: More code, requires careful initialization, no built-in interpolation.
Key Differences Summarized:
Feature |
|
Manual Method |
|---|---|---|
Initialization |
Automatic (blanks until first extremum) |
Manual (must initialize Cmax1/Cmin1 to appropriate low/high values) |
Interpolation |
Yes (Lagrange) |
No |
Resetting |
|
Manual logic
(typically
re-initializing in a
|
Code Complexity |
Less |
More |
Control |
Less |
More |
Discontinuities |
Potentially sensitive (ensure sufficient output density) |
Can be more robust if handled carefully |
Where to Use |
Main model block (not
in |
Main model block
(variables must be
declared with |
Output |
Table Only |
Table Only |
NONMEM Equivalent: There’s no direct equivalent to PML’s peak function in NONMEM. NONMEM users typically implement custom code in
$PRED or $PK to find extrema, similar to the “Manual Method” described above. NONMEM does not have built-in Lagrange interpolation
for this purpose.
Keywords: peak, trough, Cmax, Tmax, Cmin, Tmin, extremum, maximum, minimum, Lagrange interpolation, peakreset, table, output, simulation
Table Output¶
The table statement, defined in the column definition
file (or equivalent interface), specifies which variables and events
should be included in the output table(s) generated by a PML model run.
Purpose: To control the generation of output tables containing simulation results, observed data, and other model-related information.
Location: The
tablestatement is not part of the PML code itself. It’s defined in the column definition file (or equivalent interface) used by the modeling software (e.g., Phoenix NLME).Syntax:
table( [optionalFile] [optionalDosepoints] [optionalCovariates] [optionalObservations] [optionalTimes] variableList )
optionalFile: Specifies the output file name (e.g.,file="results.csv"). If omitted, a default file name is used.optionalDosepoints: Specifies that output should be generated at times when doses are administered to specific compartments (e.g.,dose(A1, Aa)).optionalCovariates: Specifies that output should be generated when the values of specified covariates change (e.g.,covr(Weight, Sex)).optionalObservations: Specifies that output should be generated at times when specific observations are made (e.g.,obs(CObs, EObs)).optionalTimes: Specifies explicit time points for output generation. Can include:Individual time points:
time(0, 1, 2.5, 5)Sequences:
time(seq(0, 10, 0.1))(generates 0, 0.1, 0.2, …, 9.9, 10)Combinations:
time(0, seq(1, 5, 0.5), 10)
variableList: A comma-separated list of variables to include in the table. This can include:Observed variables (
CObs,EObs, etc.)Predicted variables (
C,E, etc.)Covariates (
Weight,Sex, etc.)Model parameters (fixed effects, but not structural parameters directly)
Secondary parameters
User-defined variables for tracking extrema (e.g.,
Tmax,Cmaxfrom thepeakfunction, orTmax1,Cmax1from the manual method)Time (
tor mapped time variable)Other derived variables
Multiple Tables: You can define multiple
tablestatements to generate different output tables with different contents and time points.
Example:
table(file="results.csv",
time(0, seq(1, 24, 0.5), 48), // Output at t=0, every 0.5 from 1 to 24, and at t=48
dose(A1), // Output at dose times to compartment A1
obs(CObs), // Output at observation times for CObs
covr(Weight), // Output when Weight changes
C, CObs, Tmax, Cmax, Tmin, Cmin // Include these variables
)
table(file="covariates.csv",
time(seq(0,24, 4)),
covr(Weight, Sex),
Weight, Sex, Age
)
This defines two tables:
results.csv: Contains the predicted concentration (C), observed concentration (CObs), time of maximum concentration (Tmax), maximum concentration (Cmax), time of minimum concentration (Tmin) and minimum concentration (Cmin) , generated at specified times, dose times, observation times, and whenWeightchanges.covariates.csv: Contains Weight, Sex and Age generated at specified times and whenWeightorSexchanges.
Important Notes:
The
tablestatement controls output, not the model’s internal calculations.The order of variables in the
variableListdetermines the order of columns in the output table.Time points specified in
optionalTimesare automatically sorted.Variables like
Tmax,Cmax(frompeakfunction or manual method) should be added in output tables, not insecondarystatements.
Keywords: table, output, results, csv, data, time points, dosepoints, covariates, observations, seq
Delays¶
delay¶
The delay function introduces a time delay into a model. It can represent either a discrete delay (all signal mediators
have the same delay) or a distributed delay (delay times follow a distribution), modeling processes with a time lag.
Purpose: To model time delays, either discrete or distributed, in a dynamic system.
Syntax:
delay(S, MeanDelayTime [, shape = ShapeParam][, hist = HistExpression][, dist = NameOfDistribution])S: The signal (expression) to be delayed. Cannot directly depend on dose-related inputs.MeanDelayTime: The mean delay time.shape = ShapeParam: (Optional) Shape parameter for the distribution (distributed delay). Interpretation depends ondist:dist = Gammaordist = Weibull:ShapeParamis the shape parameter minus 1. Must be non-negative.dist = InverseGaussian:ShapeParamis the shape parameter itself.
hist = HistExpression: (Optional) The value ofSbefore time 0 (the “history function”). If not provided,Sis assumed to be 0 before time 0.dist = NameOfDistribution: (Optional) The distribution:Gamma(default),Weibull, orInverseGaussian.
Discrete vs. Distributed Delay:
If
shapeis not provided, a discrete delay is used:S(t - MeanDelayTime).If
shapeis provided, a distributed delay is used (convolution ofSwith the distribution’s PDF).
Limitations:
Cannot be used with closed-form solutions (
cfMicro,cfMacro) or matrix exponentiation.Scannot directly depend on dose-related inputs. UsedelayInfCptfor absorption delays.Should be used sparingly
Example (Discrete Delay):
deriv(A = -k * delay(A, 2, hist = 10)) // A is delayed by 2 time units, initial value 10
sequence {A=10}
Example (Distributed Delay - Gamma):
deriv(A = -k * delay(A, 5, shape = 2, hist = 0, dist = Gamma)) // Gamma-distributed delay
NONMEM Equivalent: There is no direct single-function equivalent in NONMEM. Delays, especially distributed delays, often require custom coding in NONMEM using differential equations or user-defined subroutines.
Keywords: delay, delay function, discrete delay, distributed delay, time delay, DDE, delay differential equation, gamma, Weibull, inverse Gaussian, hist
See also: gammaDelay, delayInfCpt, Differential Equations, Time Delays, Distributed Delays
gammaDelay¶
The gammaDelay function models a gamma-distributed
delay using an ODE approximation, which can be faster than the delay
function for complex models.
Purpose: Efficiently model a delay where the delay time follows a gamma distribution.
Syntax:
gammaDelay(S, MeanDelayTime, shape = ShapeParam, [, hist = HistExpression], numODE = NumberOfODEUsed)S: The signal to be delayed.MeanDelayTime: The mean of the gamma distribution.shape = ShapeParam: The shape parameter of the gamma distribution. Not shape parameter minus one, unlikedelay.hist = HistExpression: (Optional) Value ofSbefore time 0. Defaults to 0.numODE = NumberOfODEUsed: (Required) Number of ODEs for the approximation. Higher values are more accurate but slower. Maximum value is 400.
ODE Approximation:
gammaDelayapproximates the convolution integral with a system of ODEs.Accuracy and Performance: Accuracy depends on
numODE.ShapeParam > 1: Use at least 21 ODEs.ShapeParam <= 1: Use at least 101 ODEs.
Advantages over delay:
gammaDelaycan be significantly faster thandelay(..., dist=Gamma).Limitations:
Only for gamma-distributed delays.
The signal to be delayed cannot depend directly on dose inputs
Example:
deriv(A = -k * gammaDelay(A, 5, shape = 3, numODE = 30)) // Gamma-distributed delay
sequence {A=10}
NONMEM Equivalent: There’s no direct equivalent in NONMEM. You’d typically implement a gamma-distributed delay using a series of transit compartments, which approximates a gamma distribution.
Keywords: gammaDelay, distributed delay, gamma distribution, approximation, ODE approximation, shape parameter
See also: delay, Distributed Delays, Gamma Distribution, ODE Approximation
delayInfCpt¶
The delayInfCpt statement models a distributed delay
specifically for input into a compartment. This is the correct way to
model absorption delays with a distributed delay time.
Purpose: Model distributed delays in the absorption process (or any input into a compartment). Necessary because
delaycannot handle dose-related inputs directly.Syntax:
delayInfCpt(A, MeanDelayTime, ParamRelatedToShape [, in = inflow][, out = outflow][, dist = NameOfDistribution])A: The compartment receiving the delayed input. Can receive doses viadosepoint.MeanDelayTime: Mean of the delay time distribution.ParamRelatedToShape: Related to the shape parameter. Depends ondist:dist = InverseGaussian:ParamRelatedToShapeis the shape parameter.dist = Gammaordist = Weibull:ParamRelatedToShapeis the shape parameter minus 1. Must be non-negative.
in = inflow: (Optional) Additional inflow into compartmentAthat should also be delayed.out = outflow: (Optional) Outflow from compartmentAthat should not be delayed.dist = NameOfDistribution: (Optional) Delay time distribution:Gamma(default),Weibull, orInverseGaussian.
Relationship to dosepoint: Used with a
dosepointstatement for compartmentA.dosepointhandles dosing,delayInfCptmodels the delay.History function: is assumed to be zero
Example (One-compartment model with gamma-distributed absorption delay):
delayInfCpt(A1, MeanDelayTime, ShapeParamMinusOne, out = -Cl * C)
dosepoint(A1)
C = A1 / V
Example (Two-compartment model, two absorption pathways, each with gamma delay):
delayInfCpt(Ac1, MeanDelayTime1, ShapeParamMinusOne1, out = -Cl * C - Cl2 * (C - C2))
dosepoint(Ac1, bioavail = frac) // Fraction 'frac' goes through pathway 1
delayInfCpt(Ac2, MeanDelayTime2, ShapeParamMinusOne2)
dosepoint(Ac2, bioavail = 1 - frac) // Remaining fraction goes through pathway 2
deriv(A2 = Cl2 * (C - C2))
C = (Ac1 + Ac2) / V
C2 = A2 / V2
NONMEM Equivalent: There isn’t a single, direct equivalent in NONMEM. You would likely use a combination of:
An absorption compartment.
A series of transit compartments (to approximate a distributed delay, particularly a gamma distribution).
Potentially, user-defined subroutines (for more complex delay distributions).
Keywords: delayInfCpt, absorption delay, distributed delay, compartment, dosepoint, inflow, outflow, gamma, Weibull, inverse Gaussian
See also: delay, gammaDelay, dosepoint, Compartment Models, Absorption Delay, Distributed Delays
transit¶
The transit statement models the flow of material
through a series of linked compartments.
Purpose:
transitstatement can be used to model the flow of material through a series of linked compartmentsSyntax:
transit(dest, source, mtt, num [, in = inflow] [, out = outflow])dest: destination compartmentsource: source compartment, could be a dosepointmtt: mean transit timenum: number of transit compartmentsin = inflow: (Optional) Specifies any additional inflow into destination compartmentout = outflow: (Optional) Specifies any outflow from destination compartment
Restrictions: Cannot be used in a model with any closed-form statement
Example:
transit(A1, A0, mtt, num, out = - Cl * A1/V)
NONMEM Equivalent: The transit statement is conceptually similar to setting up a series of compartments with first-order transfer between them in NONMEM, but it handles the underlying equations more implicitly.
Keywords: transit compartment, absorption
See also: Compartment Models
Built-In Functions¶
Built-In Mathematical Functions¶
PML provides a wide range of standard mathematical functions that can be used in expressions within the model. These functions operate on double-precision floating-point numbers.
Common Functions:
sqrt(x): Square root ofx.exp(x): Exponential function (e^x).log(x): Natural logarithm (base e) ofx.log10(x): Base-10 logarithm ofx.pow(x, y):xraised to the power ofy(x^y).abs(x): Absolute value ofx.min(x, y): Minimum ofxandy.max(x, y): Maximum ofxandy.mod(x, y): Remainder ofxdivided byy(modulo operation).sin(x),cos(x),tan(x): Trigonometric functions (input in radians).asin(x),acos(x),atan(x): Inverse trigonometric functions (output in radians).sinh(x),cosh(x),tanh(x): Hyperbolic functions.asinh(x),acosh(x),atanh(x): Inverse hyperbolic functions.
Example:
stparm(
Cl = tvCl * exp(nCl),
V = tvV * exp(nV),
Ka = tvKa,
F = ilogit(tvF) // Example of using ilogit
)
C = A / V
Rate = Cl * C
Halflife = log(2) / (Cl / V) // Calculate half-life using log and division
Keywords: mathematical functions, sqrt, exp, log, log10, pow, abs, min, max, mod, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh
Link and Inverse Link Functions¶
Link and inverse link functions transform variables, often to constrain them to a specific range (e.g., probabilities between 0 and 1). They are important for modeling categorical/ordinal data.
Purpose: To relate a linear predictor (which can be any real number) to a parameter that has constraints (e.g., a probability).
Common Functions:
ilogit(x): Inverse logit function (sigmoid function). Calculatesexp(x) / (1 + exp(x)). Transforms any real numberxto a value between 0 and 1 (a probability).logit: Not directly available as a named function, uselog(p/(1-p)).probit(p): The probit function.iprobit(x): Inverse probit function. Equivalent tophi(x)(CDF of the standard normal distribution).iloglog(x): Inverse log-log link function.icloglog(x): Inverse complementary log-log link function.
Example (Logistic Regression):
stparm(p = ilogit(A * time + B)) // 'p' is a probability between 0 and 1
LL(Resp, Resp * log(p) + (1 - Resp) * log(1 - p))
Keywords: link function, inverse link function, ilogit, logit, probit, iprobit, iloglog, icloglog, logistic regression, categorical data
See also: multi, LL, Categorical Data, Ordinal Data, Logistic Regression
if and Ternary Operator¶
Conditional logic allows different calculations or actions
based on variable values. PML provides if/else within sequence
blocks and the ternary operator (? :) for expressions.
if/else(withinsequenceblocks ONLY):Syntax:
sequence { if (condition) { <statements> } else { <statements> } }condition: A logical expression (true or false). Use C++-style logical operators (&&,||,!) within the condition.Only usable within
sequenceblocks.
Restrictions
if/else could be used inside
sequenceblocks onlyThe observed variable in the
LLstatement cannot be used anywhere in the model outside of this statement.
Ternary Operator (for expressions):
Syntax:
condition ? value_if_true : value_if_falsecondition: A logical expression. Use nested ternary operators for complex conditions (cannot useand,or,not).value_if_true: Value ifconditionis true.value_if_false: Value ifconditionis false.Usable anywhere an expression is allowed (e.g.,
stparm,deriv,observe,LL).For complex conditions (more than one
else), nest ternary operators. You cannot useand,or, andnotkeywords. Use C++ style operators:&&,||and!.
Note
This is the only way to express conditional logic outside of a sequence block.
Example:
// CORRECT - Ternary operator outside of sequence block
EHC = SINE <= 0 ? 0 : SINE
// INCORRECT - if/else outside of sequence block
EHC = if (SINE <= 0) 0 else SINE
sequence {
// CORRECT - if/else inside sequence block
if (Time > 10)
{
DoseRate = 0
}
}
Example (Ternary Operator):
stparm(
Cl = tvCl * exp(dClSex * (Sex == 1 ? 1 : 0) + nCl) // Effect of Sex on Cl
)
This is equivalent to: If Sex equal to 1 then Cl = tvCl * exp(nClPeriod2 + nCl) otherwise Cl = tvCl * exp(nCl)
Keywords: if, else, ternary operator, conditional logic, sequence
See also: sequence, Expressions, Logical Operators
Logical Operators¶
Logical operators create logical expressions (conditions)
that evaluate to true or false. The allowed operators differ depending
on whether they are used inside or outside a sequence block.
Comparison Operators:
==: Equal to!=: Not equal to>: Greater than<: Less than>=: Greater than or equal to<=: Less than or equal to
Logical Operators:
Within
sequenceblocks: Use C++-style logical operators:&&(and),||(or),!(not).Outside
sequenceblocks: Nested ternary operators (? :) for conditional logic are preferred, but C++-style logical operators are also permitted.
Example:
sequence {
if (Time > 10 && DoseRate > 0) { // Correct
DoseRate = 0
}
}
stparm(
K21 = t < 6 ? 0 : t < 8 ? tvK21 : 0 // Correct: Nested ternary
)
stparm(
Cl = tvCl * exp(dClSex * (Sex == 1) + nCl) // Using == for comparison
)
stparm(
K21 = t >= 6 and t <= 8 ? tvK21 : 0 // INCORRECT: Cannot use 'and'
)
Keywords: logical operators, comparison operators, ==, !=, >, <, >=, <=, and, or, not
See also: if and Ternary Operator, sequence
sleep¶
The sleep function, used within a sequence block,
pauses execution for a specified duration, introducing time delays in
action code.
Purpose: To pause the execution of a
sequenceblock.Syntax:
sleep(duration)duration: Expression for the amount of time to sleep (in model time units).
sequenceBlock Only:sleepcan only be used within asequenceblock.Relative Time:
durationis relative to whensleepis encountered, not absolute time.Stability: sleep statement should be used to ensure the stability of the algorithms
Example:
sequence {
sleep(5) // Pause for 5 time units
A1 = A1 + 10 // Add 10 to compartment A1 after the delay
}
Keywords: sleep, sequence, time delay, action code
See also: sequence, Time Delays, Action Code
Statistical Distribution Functions¶
PML provides functions for calculating the PDF, CDF, log PDF, and log CDF of several common statistical distributions, often used within the LL statement.
Normal Distribution:
lnorm(x, std): Logarithm of the PDF of a normal distribution (mean 0, standard deviationstd).lphi(x, std): Logarithm of the CDF of a normal distribution (mean 0, standard deviationstd).phi(x): CDF of the standard normal distribution (mean 0, standard deviation 1).
Weibull Distribution:
dweibull(x, shape, scale): PDF.ldweibull(x, shape, scale): Log PDF.pweibull(x, shape, scale): CDF.lpweibull(x, shape, scale): Log CDF.
Inverse Gaussian Distribution:
dinvgauss(t, mean, shape): PDF.ldinvgauss(t, mean, shape): Log PDF.pinvgauss(t, mean, shape): CDF.lpinvgauss(t, mean, shape): Log CDF.
Poisson Distribution:
lpois(mean, n): Logarithm of the probability mass function.ppois(mean, n): Probability mass function.rpois(lambda): random numberunifToPoisson(mean, r): convert a uniform random number between 0 and 1 to a Poisson random number
Negative Binomial Distribution:
lnegbin_rp(r, p, y): logarithm of the probability mass function of a negative binomial, distribution parameterized by r and p
megnin_rp(r, p): generate a random sample from a negative binomial distribution parameterized by r and p
lnegbin(mean, beta, power, y): logarithm of the probability mass function of a negative binomial distribution parameterized by mean, beta (=log(alpha)), and power
pnegbin(mean, beta, power, y): probability mass function of a negative binomial distribution parameterized by mean, beta (= log(alpha)), and power
rnegbin(mean, beta, power): generate a random sample from a negative binomial distribution parameterized by mean beta (= log(alpha)), and power
Example (using lnorm in an LL statement):
LL(Obs, lnorm(Obs - Pred, ErrorSD)) // Custom log-likelihood for normal distribution
Keywords: statistical distributions, probability density function, PDF, cumulative distribution function, CDF, log-likelihood, lnorm, lphi, phi, dweibull, ldweibull, pweibull, lpweibull, dinvgauss, ldinvgauss, pinvgauss, lpinvgauss
See also: Probability Distributions, LL, Likelihood
Additional Statements and Features¶
Secondary Parameters¶
Secondary parameters are quantities calculated from primary (structural) parameters, fixed effects, and other variables. They are not directly estimated but are derived. They cannot depend directly on structural parameters.
Purpose: To calculate and report derived quantities of interest (e.g., half-life, AUC, Cmax), after the main model fitting is complete. These are not part of the core model dynamics.
Syntax:
secondary(parameterName = expression)parameterName: The name of the secondary parameter.expression: Defines how to calculateparameterName. Can include:Fixed effects (e.g.,
tvV,tvCl).Other secondary parameters (defined before this one).
Mathematical operators and functions.
Covariates
idosevar,infdosevar,infratevarvariables
Cannot Include: Structural parameters (parameters defined with
stparm), random effects, or variables defined by top-level assignment. Secondary parameters are functions of fixed effects, and other derived quantities, not the individual-specific parameter values or dynamic variables.
Calculation: Calculated once after all model fitting is completed.
Multiple
secondarystatements: can be included.
Note
There is generally no direct equivalent to secondary parameters in standard NONMEM code.
The secondary statement in PML is a convenience for post-processing and reporting,
not for defining relationships within the core model. Do not translate simple assignments from NONMEM’s $PK or $DES blocks
into secondary statements.
Example:
stparm(Cl = tvCl * exp(nCl))
stparm(V = tvV * exp(nV))
fixef(tvCl = c(,5,))
fixef(tvV = c(,50,))
secondary(
Halflife = log(2) / (tvCl / tvV) // Calculate half-life from fixed effects
)
Keywords: secondary parameter, derived parameter, calculated parameter, secondary, fixed effects
See also: stparm, Fixed Effects
Data Mapping (Column Definitions)¶
Data mapping links columns in your input dataset to variables and contexts within your PML model. This is done outside the PML code, using a column definition file or interface.
Purpose: To tell the execution engine how to interpret the data in your input file.
Column Definition File: A text file (or equivalent interface) with mapping statements.
Key Mapping Statements:
id(columnName): Maps a column to the subject ID. Example:id(SubjectID).time(columnName): Maps a column to the time variable (t). Example:time(Time).obs(observedVariable <- columnName): Maps a column to an observed variable. Example:obs(CObs <- Conc).amt(columnName): Maps a column to the dose amount (withdosepoint). Example:amt(DoseAmt).Can map a single column containing both bolus and infusion amounts.
rate(columnName): Maps a column to the infusion rate (withdosepoint). Example:rate(InfRate).covr(covariateName <- columnName): Maps a column to a covariate (covariateorfcovariate). Example:covr(Weight <- BW).Categorical Covariates:
With labels:
covr(Sex <- SexColumn("Male" = 0, "Female" = 1))Without labels:
covr(Sex <- SexColumn())(First unique value is the reference category).
fcovr(covariateName <- columnName): Same ascovr, but forfcovariate.censor(columnName): Maps a column to the BQL flag fromobserve. Example:censor(CObsBQL).loq(columnName): Maps a column to provide lower limits of quantification. Example:loq(LLOQ).mvd(columnName): Maps a column to indicate missing data values for observations. Example:mvd(MDV).evid(columnName): Maps a column to an event identifier. Example:evid(EVID).addl(columnName, doseCycleDescription): Maps a column to indicate additional doses. Example:addl(ADDL, 24 dt 10 bolus(A1)).ii(columnName): Maps a column to the interdose interval. Often derived fromaddl.dose(doseVariable <- columnName, cmt = compartmentNumber): Conditional mapping of doses. MapscolumnNametodoseVariableonly whencmtmatchescompartmentNumber. Essential for multiple dosepoints. Example:dose(AaDose <- AMT, cmt = 1).cmt(columnName): Maps a column specifying the compartment number (with conditionaldose). Example:cmt(CMT).ss(ssColumnName, doseCycleDescription): Maps a column that indicates steady-state dosing.reset(resetColumnName=c(lowValue, highValue)): Maps a column to reset time and compartmentsdate(dateColumnName[, formatString [, centuryBase]]): maps date columndvid(columnName): Maps a column to an observation type identifier (multiple observation types in same data column). Example:dvid(TYPE). Used with conditional observation mapping.table(...): (Not for input, defines output tables).
Conditional Mapping: Use conditional logic in mappings to handle rows differently (e.g., based on
CMT,DVID). Map the same data column to different variables, depending on context.
Example (Conceptual Column Mappings - PK/PD, Multiple Doses, BQL):
id(ID)
time(TIME)
amt(AMT)
addl(ADDL) // Additional doses
ii(II) // Interdose interval
mvd(MDV)
evid(EVID)
obs(CObs <- DV) // PK observations
obs(EObs <- DV) // PD observations
covr(Weight <- WEIGHT)
covr(Sex <- SEX("Male" = 0, "Female" = 1))
censor(CObsBQL) // BQL flag for PK observations
loq(LOQ)
dose(AaDose <- AMT, cmt = 1) // Map AMT to AaDose when CMT=1 (bolus)
dose(AaInfDose <- AMT, cmt = 1) // Map AMT to AaInfDose when CMT=1 (infusion)
rate(AaInfRate <- RATE) // Infusion rate
dvid(TYPE) // DVID for distinguishing PK/PD observations
Keywords: data mapping, column mapping, input data, dataset, id, time, obs, amt, evid, rate, covariate, censor, loq, dvid, dose, addl, ii, table, ss, reset, date, Input Data, Conditional Mapping
See also: observe, dosepoint, Covariates, BQL Handling
sequence¶
The sequence block allows for sequential execution of
statements, unlike the generally declarative nature of PML. Used for
initialization, time-dependent actions, and discontinuous events.
Purpose: Define statements executed in order, at specific times.
Syntax:
sequence { statements }Key Feature: Sequential Execution: Order of statements matters within a
sequenceblock.Statements Allowed Within sequence:
Assignment statements (for variables modifiable when the model is stopped – integrator variables,
real/doublevariables).if (test-expression) statement-or-block [else statement-or-block](Conditional execution). Use ternary operator (? :) within expressions, notif/else.while (test-expression) statement-or-block(Looping)sleep(duration-expression)(Pauses execution)Function calls
Execution Timing:
sequenceblocks start before model simulation (at time 0).Continue until
sleepor end of block.sleeppauses, resuming at the future time.
Multiple
sequencestatements can be in a model, and they are executed as if in parallel.Reset:
sequencestatement(s) are restarted when a reset is encountered in the dataRestrictions:
It is not strongly not recommended to modify inside
sequencefixed effects, random effects, residual erros, structural parameters. It is impossible to modify observable variables. Example (Initializing Compartments):
sequence {
A1 = 100 // Initialize A1 to 100 at time 0
A2 = 0 // Initialize A2 to 0 at time 0
}
Example (Time-Dependent Action):
sequence {
sleep(5) // Wait 5 time units
DoseRate = 0 // Turn off an infusion
}
Example (Loop and Conditional):
real(i)
sequence {
i = 0
while (i < 10) {
if (Concentration < Threshold) {
DoseRate = HighRate
} else {
DoseRate = LowRate
}
sleep(1)
i = i + 1
}
}
Keywords: sequence, action code, sequential execution, sleep, if, else, while, initialization, time-dependent actions
See also: Statements, Blocks, sleep, if and Ternary Operator, real
real and double¶
real and double are synonyms, declaring variables
as double-precision floating-point numbers modifiable within
sequence blocks.
Purpose: Declare variables to be modified in
sequenceblocks.Syntax:
real(variableName1, variableName2, ...)ordouble(variableName1, variableName2, ...)Double Precision:
real/doublevariables are double-precision.Modifiable in sequence: Can be assigned new values within
sequenceblocks (unlike top-level assignments).
Example:
real(Counter, Flag) // Declare Counter and Flag
sequence {
Counter = 0
while (Counter < 10) {
Counter = Counter + 1
sleep(1)
}
Flag = if (Concentration > Threshold) 1 else 0 //use ternary operator
}
Keywords: real, double, variable declaration, sequence, modifiable variable
See also: Variables, sequence, Assignment Statements
Model Generation Guidelines¶
Model Generation Guidelines¶
This checklist provides best practices and common pitfalls for generating correct PML models. It covers statement ordering, parameter definition, error models, covariate incorporation, and built-in functions, helping to avoid mistakes and improve identifiability.
Statement Ordering (Recommended Order):
Covariate Declarations:
covariate,fcovariate,interpolate.dosepointanddelayInfCpt: Define dosing. Beforederivreferencing the dosed compartment.Concentration Calculation: E.g.,
C = A1 / V. BeforederivusingC.deriv: Define differential equations.error: Define error variables (and their numeric standard deviations).observe: Link predictions to observations, specify error model.stparm: Define structural parameters. After covariate declarations,dosepoint, and calculations they depend on.fixef: Define fixed effects (population parameters).ranef: Define random effects (inter-individual variability).secondary: Define secondary (derived) parameters.sequence: For initialization (beginning) or time-dependent actions.
Structural Parameter Definition (stparm):
Common Styles:
Product * exp(eta):parameter = tvParameter * exp(nParameter)(Log-normal - for positive-only parameters like V, Cl, Ka).Sum * exp(eta):parameter = (tvParameter + covariate_effects) * exp(nParameter).exp(Sum + eta):parameter = exp(tvParameter + covariate_effects + nParameter).ilogit(Sum + eta):parameter = ilogit(tvParameter + covariate_effects + nParameter)(For parameters between 0 and 1, like bioavailability).Sum + eta:parameter = tvParameter + covariate_effects + nParameter(Normal - for parameters that can be positive or negative, like E0, Emax).
Choosing the Right Style:
Positive-Only (V, Cl, Ka, etc.):
Product * exp(eta)is generally preferred.Positive or Negative (E0, Emax, etc.):
Sum + etais appropriate.Between 0 and 1 (Bioavailability):
ilogit(Sum + eta)is ideal.
stparmRestrictions: Withinstparm, only use:Fixed effects (typically
tvprefix).Random effects (typically
nprefix).Declared covariates.
Mathematical operators and functions.
Cannot use variables defined by assignment (e.g.,
C = A1 / V).
Covariate Incorporation:
Declaration: Covariates must be declared (
covariate,fcovariate, orinterpolate).Centering (Continuous Covariates): Improves stability and reduces correlation.
Syntax:
(CovariateName / CenterValue)(multiplicative) or(CovariateName - CenterValue)(additive).Center Value: Mean, median, or clinically relevant value.
Categorical Covariates: Use
(CovariateName == CategoryValue)instparm. The first category is the reference. Mapping is done in the column definition file.Examples (stparm Styles with Covariates):
# Product * exp(eta) (most common for positive parameters) stparm(V = tvV * (Weight / 70)^dVdWt * exp(nV)) # Allometric scaling stparm(Cl = tvCl * exp(dCldSex * (Sex == 1)) * exp(nCl)) # Sex effect # Sum * exp(eta) stparm(V = (tvV + dVdWt * (Weight - 70)) * exp(nV)) # exp(Sum + eta) stparm(V = exp(tvV + dVdWt * (Weight - 70) + nV)) # ilogit(Sum + eta) (for parameters between 0 and 1) stparm(F = ilogit(tvF + dFdWt * (Weight - 70) + nF)) # Sum + eta (for parameters that can be positive or negative) stparm(E0 = tvE0 + dE0dWt * (Weight - 70) + nE0) fcovariate(Weight) fcovariate(Sex()) #categorical covariate
Error Model Specification:
error: Define error variables and their numeric standard deviations.observe: Link predictions to observations, specify error model (additive, proportional, combined, power, log-additive). Includes one error variable.LL: For custom likelihoods.Use prescribed error variable names (like
CEps)Select an error model appropriate for your data.
“Standard Deviation vs. Variance: Ensure the value provided in the
errorstatement is the standard deviation, not the variance. If translating from NONMEM, remember to take the square root of the$SIGMAvalue (if it represents variance).”
Initialization:
derivcompartments are initialized to 0.sequenceoverrides this.Only
real/double, integrator, andurinecptvariables are modifiable insequence.Do not include explicit compartment initializations within sequence blocks unless specifically required
Use of Built-In Functions:
Use functions for clarity and efficiency (e.g.,
exp,log,sqrt,ilogit,phi,lnorm).
Review and Validation:
Syntax Check: Double-check syntax.
Simulation Testing: Validate by simulating scenarios.
Parameter Estimates and SEs: Check for reasonableness. Large values or high correlations can indicate problems.
Documentation: Include comments.
Identifiability:
Be aware of identifiability. Can parameters be uniquely estimated? Overparameterization can lead to non-identifiability.
Example (Guideline Summary):
test() {
fcovariate(Weight) # Declare covariate early
fcovariate(Sex()) # Declare categorical covariate
dosepoint(Aa, tlag = Tlag) # Dosepoint (with structural lag time)
C = A1 / V # Define concentration *before* using it
deriv(Aa = -Ka * Aa) # Absorption compartment
deriv(A1 = Ka * Aa - Cl * C - Cl2 * (C - C2) - Cl3 * (C - C3)) # Central
deriv(A2 = Cl2 * (C - C2)) # Peripheral 2
deriv(A3 = Cl3 * (C - C3)) # Peripheral 3
C2 = A2/V2
C3 = A3/V3
error(CEps = 0.02) # Numeric SD value!
observe(CObs = C * (1 + CEps)) # Proportional error
# Structural parameters (log-normal, with covariate effects)
stparm(
Ka = tvKa * exp(nKa),
V = tvV * (Weight / 70)^dVdWt * exp(nV), # Allometric scaling
Cl = tvCl * exp(dCldSex * (Sex == 1)) * exp(nCl), # Sex effect
Cl2 = tvCl2 * exp(nCl2),
V2 = tvV2 * exp(nV2),
Cl3 = tvCl3 * exp(nCl3),
V3 = tvV3 * exp(nV3),
Tlag = tvTlag * exp(nTlag)
)
# Fixed effects
fixef(
tvKa = c(, 1, ),
tvV = c(, 50, ),
tvCl = c(, 5, ),
tvCl2 = c(, 2, ),
tvV2 = c(, 30, ),
tvCl3 = c(, 1, ),
tvV3 = c(, 20, ),
tvTlag = c(, 0.5, ),
dVdWt = c(, 0.75, ), # Fixed effect for Weight on V
dCldSex = c(, 0, ) # Fixed effect for Sex on Cl
)
# Random effects
ranef(diag(nV, nCl, nCl2, nV2, nCl3, nV3, nKa, nTlag) =
c(0.09, 0.04, 0.04, 0.09, 0.04, 0.09, 0.04, 0.01)
)
}
Keywords: best practices, checklist, troubleshooting, model generation, PML, common pitfalls, validation, syntax, ordering, parameterization, structural parameters, covariates, centering, error models
See also: PML Model Structure, Parameter Declarations, Covariates, Structural Model Definition, Observation and Error Models, Delays, Built-In Functions, Data Mapping
Modeling Complex Absorption Schemes¶
Demonstrates how to combine multiple dosepoint
statements and their options (bioavail, tlag, duration) to
model formulations with complex absorption kinetics, such as
simultaneous or sequential processes.
The dosepoint statement is highly flexible. By using multiple
dosepoint statements, you can model complex drug delivery systems
where a single dose (AMT from the data) is split into different
pathways or scheduled to occur sequentially.
A. Simultaneous (Parallel) Absorption (Zero-Order and First-Order)¶
This pattern models a formulation with both immediate and sustained
release components that begin absorbing at the same time. A single dose
is split between a first-order and a zero-order process using a
fractional bioavailability parameter (Fr).
Key Concept: Two
dosepointstatements are used. One (dosepoint(Aa, bioavail = Fr)) directs a fraction of the dose to a first-order process. The other (dosepoint(A1, bioavail = 1 - Fr, duration = Dur)) directs the remaining fraction to a zero-order process.
Example Code (Simultaneous ZO/FO):
test() {
# PARALLEL PATHWAYS:
# 1. First-order absorption for fraction 'Fr'.
dosepoint(Aa, bioavail = Fr)
# 2. Zero-order absorption for fraction '(1 - Fr)' over duration 'Dur'.
dosepoint(A1, bioavail = 1 - Fr, duration = Dur)
# Model structure
C = A1 / V
deriv(Aa = -Ka * Aa)
deriv(A1 = Ka * Aa - Cl * C)
# ... observe/error statements ...
# Structural parameters
stparm(Fr = ilogit(tvFr_logit + nFr_logit)) // Fraction for 1st-order
stparm(Dur = tvDur * exp(nDur)) // Duration for 0-order
# ... stparm for Cl, V, Ka ...
# ... fixef/ranef statements ...
}
B. Sequential Absorption (Zero-Order followed by First-Order - ZOFO)¶
This pattern models a formulation where an initial zero-order absorption phase is immediately followed by a first-order absorption phase.
Key Concept: The sequence is created by using the same parameter (
Din this example) to define thedurationof the zero-order process and thetlag(time lag) of the first-order process. This elegantly ensures the second process begins precisely when the first one ends. This method is compatible with closed-form (cfMicro) solutions.
Example Code (Sequential ZOFO):
test() {
# This model uses cfMicro because the time-dependency is handled by
# the event scheduler (dosepoint), not by changing the ODEs themselves.
cfMicro(A1, Cl / V, first = (Aa = Ka))
C = A1 / V
# SEQUENTIAL PATHWAYS:
# 1. First-Order Pathway:
# A fraction 'Fr' of the dose is sent to the absorption depot 'Aa',
# but its absorption is DELAYED by 'D' time units.
dosepoint(Aa, tlag = D, bioavail = Fr)
# 2. Zero-Order Pathway:
# The remaining fraction '(1-Fr)' is absorbed into the central
# compartment 'A1' over a DURATION of 'D' time units, starting at time 0.
dosepoint(A1, bioavail = 1 - Fr, duration = D)
# Define the fraction 'Fr' to be absorbed first-order (after the lag)
stparm(Fr = ilogit(tvFr_logit + nFr_logit))
fixef(tvFr_logit = c(, 0, ))
ranef(diag(nFr_logit) = c(0.09))
# Define the duration/tlag parameter 'D'
stparm(D = tvD * exp(nD))
fixef(tvD = c(, 1, ))
ranef(diag(nD) = c(0.09))
# ... observe/error statements ...
# ... stparm, fixef, ranef for Cl, V, Ka ...
}
C. Splitting a Single Dose into Different Administration Profiles (e.g., Bolus + Infusion)¶
This pattern is used for a single dose amount that is administered in two different ways simultaneously (e.g., a formulation that gives an initial bolus effect while also starting a slow-release infusion).
Key Concept: This is achieved by using
dosepointanddosepoint2on the same compartment. The crucialsplitargument is added to the firstdosepointstatement. This allows a single dose amount from one column in the data (e.g.,AMT) to be divided between the two statements using their respectivebioavailoptions.
Example (50% Bolus, 50% Infusion over 3 time units):
This model takes a single dose amount and administers 50% of it as an
instantaneous bolus into Aa, while simultaneously starting a 3-hour
infusion of the other 50% into the same Aa compartment.
PML Code:
test(){
# Define the central compartment and concentration
deriv(A1 = - (Cl * C) + (Aa * Ka))
deriv(Aa = - (Aa * Ka))
C = A1 / V
# DOSE SPLITTING:
# 1. Define the bolus part. 'bioavail=(.5)' takes 50% of the dose.
# 'split' is ESSENTIAL to allow the same data column to be used for dosepoint2.
dosepoint(Aa, bioavail = (.5), split)
# 2. Define the infusion part on the same compartment 'Aa'.
# 'bioavail=(.5)' takes the other 50% of the dose.
# 'duration=(3)' defines the zero-order infusion time.
dosepoint2(Aa, bioavail = (.5), duration = (3))
# ... observe/error and parameter definition statements ...
error(CEps = 30)
observe(CObs = C + CEps)
stparm(V = tvV * exp(nV))
stparm(Cl = tvCl * exp(nCl))
stparm(Ka = tvKa * exp(nKa))
# ... fixef/ranef statements ...
}
Corresponding Column Definition Mapping:
# Because 'split' is used, both dose() and dose2() can map to "AMT".
dose(Aa<-"AMT")
dose2(Aa<-"AMT")
D. Parallel Absorption with Independent Lag Times¶
For maximum flexibility, especially with complex formulations, you can assign independent lag times to each parallel absorption pathway. This allows for modeling scenarios where two different release mechanisms from a single dose not only have different profiles (e.g., zero-order vs. first-order) but also start at different times.
Key Concept: Use a separate
tlagargument in eachdosepointstatement. Eachtlagcan be linked to a different structural parameter, allowing them to be estimated independently.
Example (Simultaneous ZOFO with Independent Lags): This model splits a dose into a first-order pathway and a zero-order pathway, where each can start after its own unique delay.
PML Code Snippet (Focus on dosepoint):
# 1. First-Order Pathway with its own lag time 'Tlag_FO'
dosepoint(Aa, bioavail = Fr_FO, tlag = Tlag_FO)
# 2. Zero-Order Pathway with its own lag time 'Tlag_ZO'
dosepoint(A1, bioavail = 1 - Fr_FO, duration = Dur_ZO, tlag = Tlag_ZO)
# ... parameter definitions for Tlag_FO and Tlag_ZO would be separate ...
stparm(Tlag_FO = tvTlag_FO * exp(nTlag_FO))
stparm(Tlag_ZO = tvTlag_ZO * exp(nTlag_ZO))
Keywords: absorption, parallel absorption, sequential absorption, dose splitting, dual absorption, ZOFO, lag time, tlag, duration, bioavail
Modeling Multiple Elimination Pathways¶
Describes how to model drugs that are cleared by multiple
simultaneous mechanisms (e.g., a mix of linear and saturable pathways)
by summing their individual rate equations within the deriv
statement.
Pharmacokinetic models in PML are modular. If a drug is eliminated by
more than one process, the total rate of elimination is simply the sum
of the rates of each individual pathway. This is implemented by
subtracting each elimination rate term within the deriv statement
for the central compartment.
Total Elimination Rate = (Rate of Pathway 1) + (Rate of Pathway 2) + …
Example: Mixed Linear and Saturable (Hill-Type) Elimination
A common scenario is a drug cleared by both a first-order (linear) process and a capacity-limited (saturable) process.
Linear Pathway Rate:
Cl * CSaturable Pathway Rate (with Hill):
(Vmax * C^h) / (Km^h + C^h)
Implementation in deriv Statement: The deriv statement for
the central compartment (A1) would include the drug input minus both
elimination terms.
deriv(
A1 = (INPUT) - (Cl * C) - (Vmax * C^h) / (Km^h + C^h)
)
Full Example Model (from previous interaction): This code shows the
principle in a complex model that combines parallel absorption with
mixed-order elimination. Note the two separate subtraction terms in the
deriv(A1 = ...) line.
test() {
# Parallel absorption with independent lag times
dosepoint(Aa, bioavail = Fr_FO, tlag = Tlag_FO)
dosepoint(A1, bioavail = 1 - Fr_FO, duration = Dur_ZO, tlag = Tlag_ZO)
C = A1 / V
deriv(Aa = -Ka * Aa)
deriv(
# The two elimination pathways are summed here by subtracting each term
A1 = (Ka * Aa) - ( (Cl * C) + (Vmax * C^h) / (Km^h + C^h) )
)
# ... observe/error statements ...
# Structural parameters including Cl, Vmax, Km, and h
stparm(Cl = tvCl * exp(nCl)) // Linear clearance
stparm(Vmax = tvVmax * exp(nVmax)) // Saturable Vmax
stparm(Km = tvKm * exp(nKm)) // Saturable Km
stparm(h = tvh * exp(nh)) // Hill coefficient
# ... other stparm, fixef, ranef statements ...
}
Keywords: elimination, clearance, Michaelis-Menten, mixed-order, parallel pathways, deriv, Vmax, Km, Hill
See also: deriv, Compartment Models, Michaelis-Menten Kinetics
Introduction to Metamodels¶
The Metamodel Concept¶
A metamodel, distinguished by the .mmdl file extension,
is a self-contained text file that encapsulates all necessary components
for running a Pharmacometric Modeling Language (PML) model. It serves as
a comprehensive container, bundling the model code, data file reference,
column mappings, and engine instructions into a single, human-readable
file, analogous to a NONMEM control file.
The primary purpose of a metamodel is to simplify the
execution and management of PML models, particularly within the
Certara.RsNLME ecosystem. While PML is the core modeling language for
both Phoenix NLME and RsNLME, a basic PML model file (.mdl) only
contains the model’s structural equations and statements. It does not
specify which dataset to use, how to map data columns to model variables
(e.g., concentration, time), or what estimation engine settings to
apply.
A metamodel solves this by integrating these separate pieces of
information into a single, structured .mmdl file. This makes models
more portable, reproducible, and easier to run from both R (using
Certara.RsNLME) and command-line interfaces.
Comparison with Other Systems:
Phoenix NLME Project: In the Phoenix NLME graphical user interface, the project file (
.phxproj) holds the model, data, and mappings together in a binary format. A metamodel provides a text-based, human-readable equivalent of this integration.NONMEM Control Stream: The structure and function of a metamodel are conceptually very similar to a NONMEM control stream (
.ctlfile), which also combines model specification ($PK,$ERROR), data linkage ($DATA), and estimation instructions ($EST) into one file.
Metamodel Structure: Metamodels are organized into distinct blocks,
each beginning with a double number sign (##) followed by the
block’s name (e.g., ##DATA, ##MODEL). Comments within a
metamodel follow standard PML syntax: # or // for single-line
comments, and /* ... */ for multi-line comments.
Keywords: metamodel, .mmdl, container, PML, RsNLME, Phoenix NLME, NONMEM control file
See also: Metamodel Blocks, PML Model Structure
Metamodel Structure¶
Metamodel Blocks¶
A metamodel file is structured into distinct,
case-insensitive blocks, each starting with a double number sign
(##) followed by the block name. The most critical blocks are
##DATA, ##MODEL, and ##MAP, which define the dataset, the
PML code, and the column mappings, respectively. Other blocks like
##ESTARGS and ##TABLES provide instructions for model execution
and output generation.
The following blocks are used to organize the information
within a .mmdl file.
Informational Blocks:
##Author: Specifies the name of the model author (e.g.,##Author: A. User).##Description: Provides a text description of the model’s purpose. This is used by external tools like Pirana for display.##Based on: Indicates the filename of a parent or reference metamodel, used by Pirana to construct model development trees.These three blocks are optional and are not used by the NLME engine during estimation.
##DATARequired. This block specifies the path to the input dataset on a single line.
The path can be absolute or relative to the location of the
.mmdlfile.The
Certara.RsNLMEpackage uses thedata.table::fread()function to load the specified data file.Syntax:
##DATA path/to/your/data.csv
##MODELRequired. This block contains the complete Pharmacometric Modeling Language (PML) code for the model.
The syntax within this block must conform to standard PML rules.
Syntax:
##MODEL test() { // PML statements go here }
##MAPDefines the mappings between PML model variables and the columns in the dataset specified in
##DATA.Syntax: Mappings are defined using
variableName = columnName.If a mapping for a model variable is omitted, the engine assumes the data column has the exact same name as the model variable (e.g., a mapping for
CObsis equivalent to writingCObs = CObs).This block is also used to map special variables that control model behavior but may not be explicitly defined in the
##MODELblock:id: Required for population models. Maps up to five columns that uniquely identify each subject. If unmapped, the model is treated as an individual fit. (e.g.,id = SubjectID).time: Required for time-based models (any model with aderivstatement). Maps the time column. (e.g.,time = Time).dosingCompartmentName_Rate/..._Duration: Specifies that a dosing compartment receives an infusion, with the rate or duration defined in the mapped data column. (e.g.,A1_Duration = DUR).SS,ADDL,II: Map columns containing steady-state flags, additional dose flags, and the inter-dose interval, respectively.IIis required ifSSorADDLare used.MDV: Maps a column containing Missing Data Value flags. Rows with a non-zero value in this column are ignored during estimation.Reset: Maps a column that flags when to reset the model’s state (time and compartment amounts).
Categorical Covariates: Allows for on-the-fly definition of labels for character-based categorical data. (e.g.,
Sex = Gender(Male = 0, Female = 1)).
##COLDEFProvides an alternative and more powerful way to define column mappings using the full native syntax of the NLME engine’s column definition file.
This block is useful for complex mappings that cannot be expressed through the simpler
##MAPsyntax.Definitions from
##MAPand##COLDEFare combined. If there is a conflict, the##COLDEFdefinition may take precedence.Example Syntax:
##COLDEF id("id") obs(CObs<-"conc") covr(sex<-"sex"("male" = 0, "female" = 1))
##DOSING CYCLEAn alternative block specifically for defining
ADDLorSSdosing cycles without needing to addADDLorIIcolumns to the source dataset.Syntax Example:
SS = flag_col Dosepoint = A1 Amount = 100 Delta = 24
##ESTARGSSpecifies arguments for the estimation engine, using the syntax of the
engineParams()function in theCertara.RsNLMEpackage. Multiple arguments can be separated by commas or newlines.If this block is omitted, default engine parameters are used.
Sequential Estimation: Multiple
##ESTARGSblocks can be included. They will be executed sequentially, with each run starting from the final parameter estimates of the previous one.Example Syntax:
##ESTARGS method = QRPEM, numIterations = 50
##SIMARGSSpecifies arguments for model simulation runs. Key arguments include
numReplicatesandseed.Multiple
##SIMARGSand##ESTARGSblocks can be used. All estimation runs (##ESTARGS) are completed before any simulation runs (##SIMARGS) begin.Example Syntax:
##SIMARGS numReplicates = 200, seed = 1234
##TABLESDefines one or more custom output tables. By default, the engine already creates a
posthoc.csvtable.Uses the standard PML
table()syntax. Tables can also be defined in the##COLDEFblock.Example Syntax:
##TABLES table(file="conc_vs_time.csv", time(seq(0, 24, 0.5)), C, V)
Keywords: ##Author, ##Description, ##Based on, ##DATA, ##MAP, ##COLDEF, ##MODEL, ##ESTARGS, ##SIMARGS, ##TABLES, ##DOSING CYCLE, block, metamodel structure
See also: The Metamodel Concept, PML Model Structure.
Execution Control and Output¶
##ESTARGS: Estimation Engine Arguments¶
The ##ESTARGS block specifies arguments that control
the model estimation process, overriding the default behavior of the
NLME engine. Multiple ##ESTARGS blocks can be used to define a
sequence of estimation steps. Key arguments include method to select
the algorithm and numIterations to control its duration.
The ##ESTARGS block provides direct control over the
NLME estimation engine. Its arguments mirror those found in the
engineParams() function of the Certara.RsNLME R package.
Arguments can be separated by commas or newlines.
Common Use Case: Sequential Estimation A powerful feature is the
ability to define multiple ##ESTARGS blocks. They are executed in
the order they appear in the metamodel. A common strategy is to use a
fast but less precise method first to get good initial estimates, and
then use a more robust algorithm for the final estimation.
Example of a two-stage estimation:
##ESTARGS method = QRPEM, numIterations = 300
##ESTARGS method = FOCE-ELS, numIterations = 700
In this example, the engine first runs 300 iterations of the QRPEM algorithm. The resulting parameter estimates are then used as the starting point for a second run of 700 iterations using the FOCE-ELS algorithm.
Key Arguments:
method: Specifies the estimation algorithm. The choice of method is critical and depends on the model type.For population models, common options include:
"QRPEM": A robust stochastic algorithm, good for complex models and finding initial estimates."FOCE-ELS": A fast and widely used First-Order Conditional Estimation method. This is often the default."Laplacian": The default method for models with BQL data or count data."FOCE-LB": Similar to FOCE-ELS, the Lindstrom-Bates algorithm."IT2S-EM": An iterative two-stage algorithm.
For individual models:
"Naive-Pooled"is the only option.
numIterations: A non-negative integer specifying the maximum number of iterations for the chosen method.stdErr: Controls the method for calculating standard errors of the parameter estimates.Common options include
"Sandwich"(the default for FOCE-type methods),"Hessian", and"Fisher-Score".Setting
stdErr = "None"disables standard error calculation, which can speed up runs, especially during initial model development.
numIntegratePtsAGQ: Specifies the number of quadrature points for Adaptive Gaussian Quadrature, used to improve accuracy in"FOCE-ELS"or"Laplacian"methods, particularly for models with high shrinkage. A value greater than 1 enables AGQ.logTransform: A logical value (TRUE/FALSE) that controls how models with a log-additive error structure (e.g.,C*exp(eps)) are handled, typically by enabling or disabling a Log-Transform Both Sides (LTBS) approach during the fit.sort: A logical value. IfFALSE, it forces the engine to preserve the original sort order of the input dataset. This is critical for models with reset events or other sequence-dependent logic.
A full list of available arguments can be found in the
Certara.RsNLME::engineParams() documentation.
Keywords: ##ESTARGS, engine arguments, method, numIterations, stdErr, QRPEM, FOCE, Laplacian
See also: Metamodel Blocks, ##SIMARGS, ##TABLES
##SIMARGS: Simulation Arguments¶
The ##SIMARGS block configures simulation runs that are
executed after all model estimations are complete. Its key arguments,
numReplicates and seed, control the number of simulated datasets
to generate and ensure the reproducibility of the results.
The ##SIMARGS block is used to specify arguments for
running simulations based on the final parameter estimates obtained from
the ##ESTARGS blocks.
Execution Order: In a metamodel containing both ##ESTARGS and
##SIMARGS blocks, all estimation runs are performed first. The final
parameter estimates from the last completed ##ESTARGS block are
then used as the basis for all subsequent simulation runs defined by
##SIMARGS blocks.
Key Arguments:
numReplicates: An integer specifying the number of simulation replicates to generate. For example,numReplicates = 200will create 200 simulated datasets, which is common for a Visual Predictive Check (VPC). The default value is 100.seed: An integer used to initialize the random number generator. Setting a specific seed ensures that simulation results are reproducible; running the same model with the same seed will produce the exact same simulated output. The default value is 1234.Other Arguments: Technical arguments from the
engineParams()function can also be used here to control the simulation process, such as:sort: A logical value (TRUE/FALSE) to control data sorting during simulation.ODE,rtolODE,atolODE: Arguments to control the ODE solver settings for the simulation runs.
Example: This metamodel snippet first estimates the parameters and then uses those final estimates to run a reproducible simulation.
# First, estimate the model parameters
##ESTARGS method = FOCE-ELS, numIterations = 1000
# Then, using the final estimates, run a simulation of 500 replicates
##SIMARGS numReplicates = 500, seed = 42
Keywords: ##SIMARGS, simulation, numReplicates, seed
See also: Metamodel Blocks, ##ESTARGS, ##TABLES
##TABLES: Defining Output Tables¶
The ##TABLES block is used to define custom output
files containing model variables and calculated values from either
estimation or simulation runs. It is crucial to use the table()
statement for estimation outputs and the simtbl() statement for
simulation outputs to ensure the correct data is generated at the right
stage.
While the NLME engine produces a default posthoc.csv
file for estimation runs, the ##TABLES block provides complete
control over the content, structure, and timing of custom data tables.
table() vs. simtbl(): This distinction is critical and
determines when the table is generated:
table(...): Defines an output table generated during an estimation run (from an##ESTARGSblock). This is used for creating tables of predictions, residuals, individual parameters, etc., based on the original data.simtbl(...): Defines an output table generated during a simulation run (from a##SIMARGSblock). This is used for saving the results of simulations, such as data for a Visual Predictive Check (VPC).
Default Table: For estimation runs only, a posthoc.csv table is
created by default. It contains the structural parameters and covariates
for each subject at each time point present in the original input
dataset.
Table Syntax Components: Both table() and simtbl() share a
common syntax structure composed of several parts:
file="filename.csv": (Required) Specifies the name of the output CSV file.Triggers (When to write a row): These arguments specify the events that cause a row to be written to the output file. Multiple triggers can be used.
time(...): At specified time points. Can be a list of numbers or a sequence (e.g.,time(0, 10, seq(2, 8, 0.1))).dose(...): At each dosing event for the named compartment(s) (e.g.,dose(A1)).covr(...): Whenever a specified covariate’s value changes (e.g.,covr(BW)).obs(...): At each time point corresponding to an observation of the named variable(s) (e.g.,obs(CObs)).
Variables (What to write in the columns):
A comma-separated list of model variables (e.g., structural parameters, compartment amounts, concentrations) to include in the output (e.g.,
C, V, Cl, IPRED).
Special Variables (specvar):
Used to request special, calculated variables that are derived by the engine during the run.
Rule: The
specvar()clause can only be used in atable()definition that also contains anobs()trigger. The special variables are calculated specifically at the observation time points.Common
specvarvariables include:TAD: Time After Dose.IRES: Individual Residuals.IWRES: Individual Weighted Residuals.Weight: The statistical weight of the current observation.
Output Mode (mode):
mode = keep: This is a special mode that overrides other time-based triggers (time,dose, etc.). It forces the output table to contain the exact same number of rows as the input dataset, writing output only at the original time points.
Examples:
Post-hoc parameters at time zero:
##TABLES table(file="parameters_t0.csv", time(0), Ka, V, Cl)
Residuals and special variables at observation times:
##TABLES table(file="residuals.csv", obs(CObs), IPRED, specvar(TAD, IRES, IWRES))
A simulated dataset matching the original data structure:
##TABLES simtbl(file="simulated_data.csv", CObs, mode = keep)
A dense grid of simulated concentration profiles:
##TABLES simtbl(file="simulation_profiles.csv", time(seq(0, 48, 0.5)), C, V, Cl)
Keywords: ##TABLES, table, simtbl, output, file, time, dose, covr, obs, specvar, mode, keep
See also: Metamodel Blocks, ##ESTARGS, ##SIMARGS
Linking Data to the Model¶
Data Mapping: ##MAP and ##COLDEF¶
Data mapping is the crucial step of linking columns in your
dataset to variables within your PML model. Metamodels provide two
blocks for this purpose: ##MAP for simple, direct assignments and
##COLDEF for the full, native column definition syntax. Both can be
used together to define all necessary connections.
For the NLME engine to understand your dataset, you must tell it which column corresponds to subject IDs, which to time, which to observations, and so on.
The ##MAP Block¶
The ##MAP block is the primary and most convenient way to define
column mappings.
Basic Syntax: Mappings are specified as
variableName = columnName and are all placed on the same line as
the ##MAP keyword, separated by spaces or commas. If a model
variable is not explicitly mapped, the engine assumes the dataset
contains a column with the exact same name (e.g., CObs in the model
is implicitly mapped to a CObs column in the data).
##MAP C = CONC, Weight = BW, id = SubjectID
Special Mappings: The ##MAP block is also used to define
mappings for special variables that are essential for model execution
but are not part of the PML model code itself.
id = columnName: (Required for population models). Maps a column to be used as the subject identifier. Up to five comma-separated columns can be specified for composite keys. Ifidis not mapped, the data is treated as if from a single individual.time = columnName: (Required for time-based models). Maps the column containing the time of the records.Infusion Mappings: To specify an infusion dose, you map a rate or a duration to a specific dosing compartment from your model.
dosingCompartmentName_Rate = columnName: Maps an infusion rate for the specified dosepoint.dosingCompartmentName_Duration = columnName: Maps an infusion duration for the specified dosepoint.Example:
A1_Duration = DURmaps theDURcolumn to the duration of infusions into compartmentA1.
Dosing Event Mappings:
SS = columnName: Maps the column containing the steady-state dose flag.ADDL = columnName: Maps the column containing the “additional doses” flag.II = columnName: (Required if SS or ADDL are used). Maps the column containing the inter-dose interval.
Data Flag Mappings:
MDV = columnName: Maps the “Missing Data Value” column. Rows where this column contains a non-zero value are ignored for parameter estimation.Reset = columnName: Maps the column containing a reset flag. On rows with a non-zero value, the model’s state (time and compartment amounts) is reset.
Categorical Covariate Labeling:
For a categorical covariate (defined with
covariate(Sex())in PML), you can provide human-readable labels for the values in your data.Syntax:
ModelVariableName = ColumnName(Label1 = Value1, Label2 = Value2)Example:
Sex = Gender(Male = 0, Female = 1)maps theGenderdata column to theSexmodel variable, treating rows with0as “Male” and1as “Female”.
The ##COLDEF Block¶
The ##COLDEF block provides a more powerful and flexible alternative
for defining column mappings. It uses the full, native syntax that the
NLME engine uses internally, which is documented in the PML reference
guides.
When to Use ##COLDEF: You should use ##COLDEF when a mapping
is too complex for the simple ##MAP syntax. This is rare, but can
occur with advanced model structures.
Relationship with ##MAP: You can use both ##MAP and
##COLDEF in the same metamodel. The engine will combine the
definitions from both blocks. If a mapping is defined in both places,
the ##COLDEF definition may take precedence.
Example using native syntax:
##COLDEF
id("Subject")
time("Time")
dose(A1<-"Dose")
obs(CObs<-"Conc")
covr(wt<-"Weight")
Keywords: ##MAP, ##COLDEF, mapping, id, time, rate, duration, SS, ADDL, II, MDV, Reset, categorical covariate
See also: Metamodel Blocks, ##DATA, ##DOSING CYCLE
Advanced Dosing and a Complete Example¶
##DOSING CYCLE¶
The ##DOSING CYCLE block provides an alternative, powerful syntax for defining steady-state (SS) or additional
(ADDL) dosing events directly within the metamodel, without requiring pre-existing ADDL or II columns in the dataset. This
is particularly useful for simulations or when the dosing schedule is fixed. It defines the parameters of a dosing regimen that is triggered
by a flag in a specified data column.
While the ##MAP block can link existing ADDL and II data columns to the model, the ##DOSING CYCLE block allows
you to define the dosing cycle itself based on a trigger column.
Syntax: The block begins with either SS or ADDL, followed by
a series of key = value pairs.
For Steady-State Dosing (SS):
SS = [COL] Dosepoint = [CMT] Amount = [NUM/COL] Delta = [NUM/COL] Rate = [NUM/COL] Duration = [NUM/COL]For Additional Doses (ADDL):
ADDL = [COL] Delta = [NUM/COL] Dosepoint = [CMT] Amount = [NUM/COL] Rate = [NUM/COL] Duration = [NUM/COL]
Key-Value Pairs:
SS = [COL]orADDL = [COL]: (Required) Specifies the data column ([COL]) containing the flag that triggers the dosing cycle. The cycle is initiated on rows where this column has a non-zero value.Dosepoint = [CMT]: (Required) The name of the dosing compartment ([CMT]) in the PML model that will receive the doses.Amount = [NUM/COL]: The dose amount. Can be a fixed number ([NUM]) or sourced from a data column ([COL]).Delta = [NUM/COL]: The inter-dose interval (equivalent toII). Can be a fixed number or sourced from a data column.Rate = [NUM/COL]: The infusion rate for the doses.Duration = [NUM/COL]: The infusion duration for the doses.
Example: This example defines a steady-state dosing regimen
triggered by the ss_flag column. Each cycle consists of a 100-unit
dose administered as a 2-hour infusion into compartment A1 every 24
hours.
##DOSING CYCLE
SS = ss_flag Dosepoint = A1 Amount = 100 Duration = 2 Delta = 24
Keywords: ##DOSING CYCLE, SS, ADDL, steady-state, additional doses, Delta, Amount, Rate, Duration
Complete Metamodel Example¶
This section provides a complete, annotated example of a
.mmdl file for a one-compartment IV infusion model, demonstrating
how all the different blocks work together.
This example brings together all the concepts into a single
.mmdl file for a one-compartment IV infusion model for Warfarin.
##Description: 1-Cpt PK model with IV infusion for Warfarin
##Author: A. User
##Based on: Base_1Cpt.mmdl
##DATA ./warfarin_data.csv
##MAP id = Subject, time = Time, A1_Duration = DUR, A1 = Dose
##MODEL
test() {
# 1-compartment model, clearance parameterization
cfMicro(A1, Cl / V)
C = A1 / V
# Define dosepoint to receive infusions
dosepoint(A1, idosevar = A1Dose)
# Proportional residual error model
error(CEps = 0.1)
observe(CObs = C * (1 + CEps))
# Structural parameters with random effects
stparm(Cl = tvCl * exp(nCl))
stparm(V = tvV * exp(nV))
fixef(tvCl = c(, 0.1, ))
fixef(tvV = c(, 8, ))
ranef(diag(nCl, nV) = c(0.09, 0.09))
}
##ESTARGS
method = FOCE-ELS
numIterations = 1000
stdErr = "Sandwich"
##SIMARGS
numReplicates = 500
seed = 42
##TABLES
# Estimation table with residuals
table(file="residuals_output.csv", obs(CObs), specvar(IRES, IWRES))
# Simulation table matching original data structure
simtbl(file="simulation_output.csv", C, CObs, mode = keep)
Keywords: example, metamodel, .mmdl, complete
See also: Metamodel Blocks, ##MAP, PML Model Structure
Automated Model Search with pyDarwin¶
The Metamodel as a Template File¶
For a pyDarwin search, any valid metamodel (.mmdl)
file can serve as a template. It becomes a template by including one or
more special placeholders, called tokens, which are inserted at
locations where you want to test different model variations.
The template file provides the foundational structure for every model that will be generated during the automated search.
Token Syntax: The specific token syntax required is
{TOKEN_NAME[index]}.
Note on Comments: The rule to not use tokens in comments means that
the token syntax itself (e.g., {_COV_Cl[1]}) should not be placed
inside a comment line. Standard comments that describe the purpose of a
tokenized section are perfectly acceptable and good practice.
Correct:
# Token to modify the Cl equation stparm(Cl = tvCl {_COV_Cl[1]} * exp(nCl))Incorrect:
# The following token {_COV_Cl[1]} adds the covariate effect. stparm(Cl = tvCl {_COV_Cl[1]} * exp(nCl))TOKEN_NAME: This is the name of the token, which must correspond directly to a key in the accompanyingtokens.jsonfile. Token names often start with an underscore (e.g.,_COV,_nCl) to make them easily identifiable, but this is a convention, not a requirement.[index]: This is a 1-based integer. It specifies which piece of text to select from a chosen option in thetokens.jsonfile. Since options in thetokens.jsonfile are defined as an array of text strings, the index selects which string from that array to insert.
Example of Token Usage: A token in tokens.json might be defined
like this:
"_COV_Cl": [
[ "" , "" ],
[ " * (BW/70)^dBWdCl" , "\n\tfixef(dBWdCl = c(, 0.75, ))" ]
]
In the metamodel template, you would use two placeholders to insert this effect:
##MODEL
...
stparm(Cl = tvCl {_COV_Cl[1]} * exp(nCl))
...
fixef(tvCl = c(, 1,))
{_COV_Cl[2]}
...
If pyDarwin selects the second option (“on”), it will:
Replace
{_COV_Cl[1]}with the first string:* (BW/70)^dBWdCl.Replace
{_COV_Cl[2]}with the second string:\n\tfixef(dBWdCl = c(, 0.75, )).
This two-part substitution allows a single token option to modify
multiple parts of the model code simultaneously (e.g., both the
stparm and fixef blocks).
Placement of Tokens: Tokens can be placed anywhere within the metamodel file, including:
Inside the
##MODELblock (most common), to change structural equations, parameter definitions, or error models.Inside the
##MAPblock, to test different column mappings.Inside
##ESTARGSor##TABLESblocks, to test different engine parameters or output definitions.
The Golden Rule: The fundamental rule of creating a template is that
after pyDarwin performs all the token substitutions for any given
combination of choices, the resulting, fully-assembled .mmdl file
must be a syntactically valid metamodel that can be executed by the
NLME engine.
Keywords: template, token, placeholder, metamodel, pyDarwin, syntax, {TOKEN[index]}
See also: The tokens.json File
The tokens.json File¶
The tokens.json file is a required companion to the metamodel template. It is a JSON-formatted text file that defines all the possible code snippets
that can be substituted into the tokens in the template. The structure of this file dictates the dimensions and options of the model search.
File Structure: The tokens.json file has a specific structure:
It is a single JSON object, enclosed in curly braces
{}.The top-level keys of this object must exactly match the
TOKEN_NAMEs used in the metamodel template.The value for each
TOKEN_NAMEkey is an array of options.Each option is itself an array of one or more text strings.
Example Structure:
{
"TOKEN_NAME_1": [
[ "text for option 1, index 1" , "text for option 1, index 2" ],
[ "text for option 2, index 1" , "text for option 2, index 2" ]
],
"TOKEN_NAME_2": [
[ "text for option 1, index 1" ],
[ "text for option 2, index 1" ]
]
}
How it Works: When pyDarwin builds a model, it iterates through
each token name (e.g., "TOKEN_NAME_1"). It selects one of the option
arrays (e.g., the second option
[ "text for option 2, index 1" , "text for option 2, index 2" ]).
Then, when it encounters a placeholder in the template like
{TOKEN_NAME_1[index]}, it uses the index to pick the corresponding
string from the selected option array.
{TOKEN_NAME_1[1]}would be replaced with"text for option 2, index 1".{TOKEN_NAME_1[2]}would be replaced with"text for option 2, index 2".
The “Off” State: A crucial convention is to make the first option
for each token the “off” or “base” state. This is typically an array of
empty strings ([ "", "" ]). This ensures that there is always a
baseline model generated where none of the optional features are added.
Complete pyDarwin Search Example
This example demonstrates a search to test for the effect of Body Weight
(BW) on the clearance (Cl) of a one-compartment IV bolus model.
template.mmdl
##Description: 1-Cpt IV Bolus with BW on Cl search
##DATA {data_dir}/pk_data.csv
##MAP A1 = Dose CObs = DV BW = BW id = ID time = time
##MODEL
test() {
cfMicro(A1, Cl / V)
C = A1 / V
dosepoint(A1, idosevar = A1Dose)
error(CEps = 0.1)
observe(CObs = C * (1 + CEps))
fcovariate(BW)
# Token to modify the Cl equation
stparm(Cl = tvCl {_COV_Cl[1]} * exp(nCl))
stparm(V = tvV * exp(nV))
fixef(tvCl = c(, 1, ))
fixef(tvV = c(, 10, ))
# Token to add the fixef for the covariate effect
{_COV_Cl[2]}
ranef(diag(nCl, nV) = c(1, 1))
}
tokens.json
{
"_COV_Cl": [
[
"",
""
],
[
" * (BW/70)^dBWdCl",
"\n\tfixef(dBWdCl = c(, 0.75, ))"
]
]
}
Workflow Explanation: pyDarwin will generate two models from
these files:
Model 1 (Covariate Off):
It selects the first option from
_COV_Cl:["", ""].{_COV_Cl[1]}is replaced with an empty string. Thestparmbecomesstparm(Cl = tvCl * exp(nCl)).{_COV_Cl[2]}is replaced with an empty string. No extrafixefis added.
Model 2 (Covariate On):
It selects the second option from
_COV_Cl.{_COV_Cl[1]}is replaced with" * (BW/70)^dBWdCl". Thestparmbecomesstparm(Cl = tvCl * (BW/70)^dBWdCl * exp(nCl)).{_COV_Cl[2]}is replaced with"\n\tfixef(dBWdCl = c(, 0.75, ))", adding the necessary fixed effect definition.
The results of these two runs can then be compared to determine if including Body Weight as a covariate on Clearance significantly improves the model fit.
Keywords: tokens.json, JSON, key, search space, array of arrays, options, pyDarwin
See also: The Metamodel as a Template File
Directory Shortcuts in Templates¶
pyDarwin supports special directory shortcuts within
the metamodel template file. These shortcuts, such as {data_dir},
act as placeholders for directory paths. This allows templates to be
highly portable, as the actual paths are provided as arguments when the
pyDarwin search is executed, rather than being hard-coded into the
template itself.
Hard-coding the full path to a dataset within a metamodel
template (e.g., ##DATA C:/MyProject/data/pk_data.csv) makes the
template difficult to share and reuse. Directory shortcuts solve this
problem by parameterizing the paths.
Example: The most frequent use of these shortcuts is in the
##DATA block to specify the location of the input dataset.
Hard-coded (less portable) example:
##DATA C:/Users/Me/MyProject/data/warfarin_data.csv
Using a shortcut (recommended, portable):
##DATA {data_dir}/warfarin_data.csv
Keywords: data_dir, work_dir, output_dir, directory shortcut, path, parameterization, portable
See also: The Metamodel as a Template File, ##DATA, ##TABLES
The pyDarwin Execution Options File¶
To execute a pyDarwin search with the NLME engine, the options.json file must specify the correct engine_adapter and provide
the paths to the NLME installation and its required compiler. Other core options control the search algorithm, the degree of parallelism, and the penalties applied to model fitness scores.
The following options form the fundamental configuration for any pyDarwin run that uses the NLME engine:
- engine_adapter required: For NLME, it must be set to
"nlme". - nlme_dir required: The absolute path to the NLME Engine installation directory.Example:
"nlme_dir": "C:/Program Files/Certara/NLME_Engine" - gcc_dir required: The absolute path to the root directory of the GCC compiler used by the NLME Engine.Example:
"gcc_dir": "C:/Program Files/Certara/mingw64"
pyDarwin Best Practices¶
The Golden Rules of Metamodel Templates for pyDarwin¶
To ensure pyDarwin can correctly parse and process
metamodel templates, several strict formatting rules MUST be followed.
Violating these rules can lead to silent parsing errors or incorrect
model generation. This entry serves as a critical checklist.
The following formatting rules are not optional; they are required for creating valid and robust pyDarwin templates.
1. ##DATA Block MUST Be a Single Line The path to the data file
must be on the same line as the ##DATA keyword.
Correct:
##DATA {data_dir}/my_data.csvIncorrect:
##DATA {data_dir}/my_data.csv
2. ##MAP Block MUST Be a Single Line All column mappings in a
##MAP block must be on the same line as the ##MAP keyword,
separated by spaces or commas.
Correct:
##MAP id=Subject time=Time CObs=DVIncorrect:
##MAP id = Subject time = Time CObs = DV
3. No Comments Within ##DATA and ##MAP Blocks Because these
blocks must be single lines, do not attempt to add comments on the same
line.
Correct:
##Description: Model for study XYZ ##DATA ./data.csv ##MAP id=ID
Incorrect:
##DATA ./data.csv # Path to the dataset ##MAP id=ID # Subject identifier
4. Do NOT Place Token Syntax Inside Comments This is a
frequent source of errors. The pyDarwin parser may attempt to
substitute tokens found within comments, leading to unpredictable
results. A comment should describe the purpose of a tokenized section,
but the token syntax itself ({TOKEN[index]}) must never appear
inside the comment.
Correct: .. code:: pml
# Token for the structural model differential equations. {_STRUCT[1]}
Incorrect: .. code:: pml
# The {_STRUCT[1]} token defines the differential equations. {_STRUCT[1]}
Keywords: golden rules, best practices, checklist, template, syntax, ##DATA, ##MAP, comments, tokens, pyDarwin, formatting
See also: The Metamodel as a Template File
Automated Search of Omega Structure¶
Searching Omega Structure in NLME Models¶
pyDarwin can automatically search for the optimal
variance-covariance (Omega) matrix structure for random effects. It can
test models with a simple diagonal matrix against models with various
block-diagonal structures. This search is controlled by an option in
options.json and a special #search_block directive in the
metamodel template.
Instead of manually creating different models to test for
correlations between random effects, you can instruct pyDarwin to
explore these structures automatically.
Basic Omega Block Search¶
This is the most common use case, where you want to test if a set of random effects should be modeled as independent (diagonal matrix) or correlated (a single block matrix).
To enable a basic block search, you must do two things:
Step 1: Add the #search_block Directive to Your Template
In your metamodel template (.mmdl file), add a special comment line
with the syntax #search_block(randomEffect1, randomEffect2, ...).
This tells pyDarwin which random effects are candidates for the
block-diagonal search.
This directive can be placed anywhere in the
##MODELblock, but it is good practice to place it near theranefstatements.The random effects listed inside the parentheses must correspond to names used in a
ranefstatement that defines them with a diagonal structure.
Step 2: Enable the Search in options.json
In your options.json file, add the following key-value pair:
"search_omega_blocks": true
How It Works: An Example
Imagine your metamodel template has the following ranef statement
and search_block directive:
# template.mmdl snippet
...
ranef(diag(nCl, nV) = c(0.09, 0.09))
#search_block(nCl, nV)
...
And your options.json contains "search_omega_blocks": true.
pyDarwin will automatically generate and test two different
models:
Model 1 (Diagonal): The
ranefstatement is left as is, modeling no correlation.ranef(diag(nCl, nV) = c(0.09, 0.09))
Model 2 (Block):
pyDarwinautomatically creates a block-diagonal structure for the searched effects.ranef(block(nCl, nV) = c(0.09, 0, 0.09))
Important Rules and Restrictions:
The
#search_blockdirective must only contain a comma-separated list of random effect names. No other comments or syntax are allowed inside the parentheses.Only random effects defined in a
diag()context can be included in a#search_block. You cannot search effects that are already part of ablock(),same(), orfixedstructure.If a random effect is dependent on another (e.g.,
ranef(diag(nCl), same(nOther))), you cannot includenClin the search block.
Advanced Search with Submatrices¶
This feature expands the search beyond the simple “all diagonal vs. one
big block” paradigm. It allows pyDarwin to test various combinations
of smaller block matrices within the searched effects, which can be
useful for identifying specific correlation patterns without
over-parameterizing the model.
To enable a submatrix search, add two options to options.json
"search_omega_sub_matrix": true"max_omega_sub_matrix": N, whereNis an integer for the maximum size of any sub-block to be considered (e.g., 2 or 3).
How It Works: An Example
Consider a search on four random effects:
# template.mmdl snippet
ranef(diag(nCl, nV, nKa, nF) = c(1,1,1,1))
#search_block(nCl, nV, nKa, nF)
And your options.json contains:
"search_omega_blocks": true,
"search_omega_sub_matrix": true,
"max_omega_sub_matrix": 2
pyDarwin will now test a much larger set of possible Omega
structures. In addition to the full diagonal and the single 4x4 block,
it will also test patterns like:
Pattern A: One 2x2 block
ranef(block(nCl, nV) = c(1,0,1)) ranef(diag(nKa, nF) = c(1,1))
Pattern B: A different 2x2 block
ranef(diag(nCl) = c(1)) ranef(block(nV, nKa) = c(1,0,1)) ranef(diag(nF) = c(1))
Pattern C: Two separate 2x2 blocks
ranef(block(nCl, nV) = c(1,0,1)) ranef(block(nKa, nF) = c(1,0,1))
…and all other valid combinations. pyDarwin automatically
generates this entire search space of patterns based on the parameters
in the #search_block and the max_omega_sub_matrix setting.
Keywords: Omega search, search_omega_blocks, #search_block, ranef, block, diag, search_omega_sub_matrix, max_omega_sub_matrix, pyDarwin, NLME, variance-covariance
See also: The Metamodel as a Template File, The options.json File, ranef
Comments¶
Comments are used to document the PML code, explaining its purpose and functionality. PML supports three comment styles: R-style, C-style, and C++-style.
R-style:
# comment ... end-of-line(Everything after#on a line is a comment).C-style:
/* comment ... */(Multi-line comments, cannot be nested).C++-style:
// comment ... end-of-lineExample:
test() { # This is an R-style comment /* This is a multi-line C-style comment */ deriv(A = -k * A) // C++-style comment fixef(k = c(,3,)) x = 5 //valid statement y = 1 #valid statement }Important Note: While semicolons (;) are used as optional statement separators in PML, they are NOT reliable comment markers. Do not use a single semicolon to start a comment. The following examples demonstrate correct and incorrect comment usage:
test() { deriv(A1 = k) stparm(k = tvk) # This works because ‘stparm’ is valid. ; This is NOT a valid comment and will cause an error # This is a valid R-style comment. // This is a valid C++-style comment. /* This is valid C-style multi-line comment. */ fixef(tvk = c(0,1,2)) }
Keywords: comment, documentation, #, /* */, //
See also: PML Model Structure