flowchart TD
S([nmfe76 starts]) --> I0
I0["`ICALL`=0 · NM-TRAN preprocessing<br/><i>invisible to abbreviated code</i>"]
I0 --> I1
I1["`ICALL`=1 · Per-problem init via $INFN<br/>valid here: NPROB, IPROB, NREP, IREP, S1NUM, S2NUM<br/>per problem: NINDR, INDR1, INDR2"]
I1 --> C0
C0["ICALL=2, COMACT=0 · Estimation iterations<br/>$`PRED` / $PK / $DES / $ERROR called per record<br/>per-record counters update (NEWIND, NIREC, NDREC, LIREC)<br/><i>not observable via $TABLE</i>"]
C0 --> C1
C1["`ICALL`=2, COMACT=1 · Copy pass, ETA=0<br/>$TABLE source under FO"]
C1 --> COND{"conditional method?"}
COND -->|"COND / LAPLACE / FOCE"| C2
COND -->|FO| MIXQ
C2["ICALL=2, COMACT=2 · Copy pass, ETA=conditional<br/>$TABLE source under conditional methods"]
C2 --> MIXQ
MIXQ{"mixture model?"}
MIXQ -->|"NSPOP > 1"| MLOOP
MIXQ -->|no| I3
MLOOP["MIXNUM iterates 1..NSPOP"]
MLOOP --> I3
I3["`ICALL`=3 · Finalisation via $INFN (PREDPP) or IF (ICALL.EQ.3) in $`PRED`<br/>valid here: OBJECT, IERE, IERC<br/>final `OMEGA`(n,m), `SIGMA`(n,m)<br/>SETHET, SEOMEG, SESIGM if `$COV` succeeded"]
I3 --> NP{"more problems?"}
NP -->|"`IPROB` < NPROB"| I1
NP -->|"`IPROB` = NPROB"| E([run ends])
NONMEM Tips and Verified Behaviour
Small, empirically-verified observations about NONMEM that fill gaps in the official documentation, clarify behaviour across versions, or correct minor inaccuracies. All entries below were verified on NONMEM 7.6.0 (nmfe76 driver, Linux) unless otherwise noted. If you see a discrepancy on a different version, please open an issue.
Minimum viable control stream
NM-TRAN builds a working model from the records the user provides plus whatever it can infer. $OMEGA / $SIGMA are auto-created when ETA / EPS appear in code, $THETA is only demanded if THETA(n) is referenced, and $ESTIMATION onwards are entirely optional — NONMEM just evaluates at initial estimates if no estimation is requested. Two branches of minimum then follow naturally: the $PRED route (direct expression for Y) and the PREDPP route (ADVAN-driven integrator, requires a kinetic model definition). Part IV, Chapter III documents which records are required in isolation but not the smallest composition that actually runs; the minimum also depends on whether the model classifies as single-subject or population (see §Population vs single-subject inference).
$PRED route for population data: 7 records
$PROBLEM
$INPUT ID TIME DV
$DATA d
$PRED
Y = THETA(1) + ETA(1) + EPS(1)
$THETA 1
$OMEGA 0.1
$SIGMA 0.1Verified on a 3-subject × 3-record dataset. All three random-effect layers are load-bearing:
THETA(1)— typical value (fixed effect)ETA(1)— between-subject variability, variance held inOMEGAEPS(1)— within-subject residual error, variance held inSIGMA
Drop any of the three and NM-TRAN silently switches to single-subject inference — the run still completes but no longer has a population interpretation.
Other rules:
$PROBLEMmay have an empty title.$DATAdoes not needIGNORE=@if the file has no header row.- Auto-created
$OMEGA/$SIGMAappear in.lstasOMEGA HAS SIMPLE DIAGONAL FORM WITH DIMENSION: n. $ESTIMATION,$COVARIANCE,$TABLE,$SCATTERPLOTare truly optional. When$ESTIMATIONis omitted NONMEM evaluates once at initial estimates — same result as explicit$ESTIMATION MAXEVAL=0(same OBJ, same.ext).- Dropping any required record triggers a hard error, e.g. no
$PREDand no$SUBROUTINES→ERROR 94: $SUBROUTINES AND/OR $PRED IS REQUIRED FOR THE FIRST PROBLEM.
$PRED route for single-subject data: 4 records
Included for completeness — single-subject analysis is rarely used in practice. The floor is tighter because NM-TRAN only needs some random effect in Y:
$PROBLEM
$INPUT DV
$DATA d
$PRED
Y = EPS(1)$INPUTneed not includeID— NM-TRAN generates one for single-subject data.$THETAis only required ifTHETA(n)appears inY.Ymust involve at least one random effect.Y = EPS(1)alone orY = ETA(1)alone are both sufficient;Y = THETA(1)alone fails withERROR 560: ETA VARIABLES REQUIRED WITH SINGLE-SUBJECT DATA.
What NM-TRAN shows in the $PRED mode table:
Y |
NM-TRAN inference |
|---|---|
EPS(1) only |
SINGLE-SUBJECT |
ETA(1) only |
SINGLE-SUBJECT |
THETA(1) + EPS(1) |
SINGLE-SUBJECT |
THETA(1) + ETA(1) |
SINGLE-SUBJECT |
THETA(1) + ETA(1) + EPS(1) |
POPULATION |
THETA(1) only |
(rejected — error 560) |
ADVAN13 / PREDPP route: 7 records
$PROBLEM
$INPUT ID TIME AMT DV
$DATA d
$SUBROUTINES ADVAN13 TOL=6
$MODEL NCOMPARTMENTS=1
$DES
DADT(1) = -0.1 * A(1)
$ERROR
Y = A(1)/10 + EPS(1)Each extra record is empirically required:
| Dropped record | Error |
|---|---|
TOL= option |
145 $SUBROUTINES: TOL MUST BE SPECIFIED — mandatory for ADVAN13 |
$MODEL |
error on $DES line — NM-TRAN can’t resolve compartment indices without NCOMPARTMENTS |
$DES |
334 REQUIRED BLOCK OF ABBREVIATED CODE IS OMITTED |
$ERROR |
232 USER'S ERROR SUBROUTINE OR $ERROR RECORD IS REQUIRED |
Not required even with ADVAN13: $PK (PK parameters inlined as literals in $DES/$ERROR), $THETA (if no THETA(n) reference), $OMEGA/$SIGMA (auto-created), $ESTIMATION onwards. The PREDPP floor is three records taller than $PRED — $SUBROUTINES + $MODEL + $DES in place of the single $PRED.
Population vs single-subject inference
Classification is not a label NM-TRAN sticks on the data — it routes the control stream to a different estimation engine. Population mode invokes the mixed-effects likelihood (integrating over ETA per individual); single-subject mode takes a degenerate path where η collapses to an intra-subject residual and, under default FO with no user-supplied CONTR routine, the estimator reduces to extended least squares (Part IV Ch III line 1660). The classifier runs before any Fortran is generated, so its decision is what downstream NONMEM code actually executes.
The decision is made from the control stream (model code, records, $ESTIMATION options), not from how many distinct IDs are in the data. The same model on 1 subject vs 1000 subjects classifies the same way.
Doc rules vs 7.6 empirics
Part IV Ch II §C.4.2 lists seven sufficient conditions for POP inference. Updated 7.6 truth table — three doc rules are dead or context-specific, and one whole trigger family (EM / Bayesian methods) is omitted from the docs:
| # | Trigger | $PRED |
PREDPP $ERROR |
|---|---|---|---|
| 1 | EPS in abbreviated code |
✗ | ✅ (auto-creates $SIGMA DIAGONAL(n)) |
| 2 | ETA and ERR both in code |
✅ | ✅ |
| 3 | $SIGMA record declared, no EPS in code |
✗ | ✗ |
| 4 | $MSFI ... NPOPETAS=n, n > 0 |
✅ (sole trigger) | ✅ |
| 5 | no $PK + $OMEGA precedes $ERROR + ERR in code (per §V.C.6) |
n/a | ✗ dead in 7.6 |
| 6 | $ESTIMATION LIKELIHOOD / -2LL + ETA in code |
✅ | ✅ |
| 7 | $ESTIMATION LIKELIHOOD / -2LL + $OMEGA declared |
✅ | ✅ |
| — | $ESTIMATION METHOD ∈ {SAEM, IMP, IMPMAP, ITS, BAYES, CHAIN, NUTS, DIRECT} (undocumented) |
✅ | ✅ |
Three doc-vs-empirics gaps:
- Rule 1 is PREDPP-only.
EPSin$PREDalone stays SINGLE-SUBJECT withWARNING 132; in PREDPP$ERRORit auto-promotes and fabricates a$SIGMA DIAGONAL(n). The doc phrasing hides this asymmetry. - Rule 3 never fires alone in 7.6 —
$SIGMAwithoutEPSin code always falls back to SS +WARNING 132. - Rule 5 never fires in 7.6 either. We reproduced the exact PREDPP / no-
$PK/$OMEGA-first /ERRconfiguration from §V.C.6 — SINGLE-SUBJECT.
The EM / Bayesian method family is the biggest omission: any of those methods flips a bare Y = THETA(1) + ETA(1) to POP classification, then demands an ε side (error 687: WITH POPULATION METHOD, LIKELIHOOD OPTION OR $SIGMA REQUIRED).
Unified empirical rule for NONMEM 7.6:
POPULATION is inferred iff any of:
- η-side (
$OMEGAdeclared orETA/ERRin code) and ε-side (EPSin code or$SIGMAdeclared) — classical mixed-effects pairingETAandERRboth in code- PREDPP
$ERRORwithEPSpresent$MSFI ... NPOPETAS=n, n > 0$ESTIMATION LIKELIHOOD/-2LLcombined with any η-side hint$ESTIMATION METHODin the EM / Bayesian familyOtherwise SINGLE-SUBJECT. Classification is strictly model-driven — data cardinality does not flip the inference.
Non-triggers (commonly assumed but empirically SS on their own): $PRIOR NWPRI, $MIX, $NONPARAMETRIC, $CHAIN, bare $LEVEL, $SUPER.
Single-subject mode: variance collapses into $OMEGA
Part IV Ch II line 446: “With single-subject data there is no subject-to-subject variability, and the η variables represent intrasubject effects.” EPS / $SIGMA models are silently converted to η-form under the hood:
| Source code | Records declared | Final OMEGA(1,1) |
Final SIGMA(1,1) |
Final OBJ |
|---|---|---|---|---|
Y = THETA(1) + EPS(1) |
$SIGMA 0.1 only |
0.70897 | 0.00000 | 5.90349 |
Y = THETA(1) + ETA(1) |
$OMEGA 0.1 only |
0.70897 | 0.00000 | 5.90349 |
Identical estimator, identical objective — only the labels differ. WARNING 132 (SIGMA IS INTEPRETED AS RESIDUAL VARIANCE TO SINGLE SUBJECT ANALYSIS) announces the absorption.
Practical takeaway for single-subject work: write ETA(1) + $OMEGA. The EPS / $SIGMA variant estimates the same number but reports it under a confusing label (SIGMA=0, OMEGA=0.70897).
The polymorphic ERR(n) alias
Part IV Ch IV §A (lines 82–85): “With single-subject data the array element ERR(n) may be used instead of ETA(n)… With population data the array element ERR(n) may be used instead of EPS(n).”
So ERR(n) binds to whichever role is available in context:
| Model | NM-TRAN mode | ERR(1) binds as |
|---|---|---|
Y = ERR(1), $OMEGA only |
SS | ETA |
Y = ERR(1), $SIGMA only |
SS + W132 | EPS (absorbed into η) |
Y = ETA(1) + ERR(1), both records |
POP | EPS |
Why to avoid ERR(): adding a $SIGMA line to a working Y = ERR(1) silently flips between-subject → within-subject noise with no visible change to $PRED. Explicit ETA(n) / EPS(n) makes the statistical structure readable locally.
Control-record ordering rules
NM-TRAN parses the whole control stream into an abstract representation before generating any Fortran, so most records can appear in any order — NM-TRAN reconstructs meaning from the full stream, not from top-down position. The few ordering exceptions are either parser-boundary markers that NM-TRAN needs to see early ($PROBLEM, $SIZES, $SUPER) or cases where the position is the semantics (which $OMEGA block comes first). Part IV Ch III §A frames record order as immaterial except where noted; the exceptions:
| Rule | Outcome |
|---|---|
$PROBLEM must come first (only $SIZES and $SUPER may precede¹) |
Putting $DATA before $PROBLEM → NM-TRAN error on line 1. |
$INPUT must precede any record referring to data items |
If $PK/$DES/$ERROR reference only internal vars, $INPUT can be placed last. |
With standard ID label, $INPUT / $DATA order is free |
Confirmed: swap runs clean. |
PREDPP block order ($PK, $DES, $ERROR) is free |
Confirmed: swap runs clean. |
Multiple $OMEGA / $SIGMA are position-sensitive |
First record = first block, and so on (docs, not re-tested). |
Within $OMEGA / $SIGMA: BLOCK(n)/DIAGONAL(n) precede values; VALUES follows BLOCK; (v)xN has xN after () |
Docs, not re-tested. |
¹ Part IV Ch III calls this record $SUPERPROBLEM, but NM-TRAN 7.6 rejects that spelling (15 UNKNOWN CONTROL RECORD) — only $SUPER is accepted.
Variable visibility across PREDPP blocks
PREDPP compiles $PK, $DES, $ERROR (and $AES, $INFN, $MIX) into separate Fortran subroutines. User variables live in shared NMPRD4 module storage (visible in FSUBS.f90 as COM(n) pointers), but NM-TRAN enforces a compile-time scope check stricter than the shared storage would allow.
Direction of auto-propagation for regular (non-COM) variables:
| From → To | Auto-visible? | Test outcome |
|---|---|---|
$PK → $DES |
✅ | K = ... in $PK used as -K*A(1) in $DES works |
$PK → $ERROR |
✅ | MYX = 42 in $PK, Y = ... + MYX + ... in $ERROR gives 42 |
$DES → $ERROR |
❌ | DES_VAR in $DES, read in $ERROR → ERROR 208 UNDEFINED VARIABLE |
$ERROR → $PK |
❌ | Same error 208, regardless of control-stream order |
Rule: $PK is the “source of truth” for parameters; its variables flow forward to $DES and $ERROR. $DES and $ERROR are leaves — they read from $PK but can’t share with each other or feed back. Consistent with Part IV, Chapter IV §E.1: “PRED-defined items are stored in variables defined in PRED (PK or ERROR if PREDPP is used)” — $DES is explicitly excluded.
The check uses PREDPP execution order, not lexical order in the .mod file. Putting $ERROR physically before $PK in the file does not help — NM-TRAN still knows $PK runs first.
Escape hatch: COM(n) and $ABBREVIATED COMRES / COMSAV
NMPRD4 is a Fortran MODULE (pre-NM7 it was a FORTRAN COMMON block — hence the COM prefix) containing a single double-precision array called VRBL. Every abbreviated-code variable is a pointer into one position of VRBL.
! generated into FSUBS.f90
MODULE NMPRD4P
USE NMPRD4, ONLY: VRBL
REAL, DIMENSION(:), POINTER :: COM ! alias to VRBL
REAL, POINTER :: K, V, Y, ...
CONTAINS
SUBROUTINE ASSOCNMPRD4
COM => VRBL
K => COM(2); V => COM(3); Y => COM(4) ! packed from position 1
END SUBROUTINE
END MODULEMental model — a shared cabinet of numbered drawers:
- Every variable = label stuck on one drawer. NM-TRAN picks the numbers.
- Regular variables are bound to one block — that’s the “one-block LHS rule” that kills reverse flow.
COMRES=nleaves the firstndrawers un-labelled so you can address them directly asCOM(1)..COM(n). No compile-time owner → any block can LHS-assign, bypassing the scope check.- NONMEM re-walks the data during copying passes (
COMACT=1,COMACT=2, …). Each walk re-runs abbreviated code and can overwrite drawer values. COMSAV=ngives the firstnreserved drawers a per-record shelf; record R’s value is restored when R is re-processed in a later pass.- Assignments inside
IF (COMACT>0) ... ENDIFcopying blocks auto-save regardless ofCOMSAV:COM(i)is an explicit SAVE variable (needsCOMRESonly); a regular variable is an implicit SAVE variable (auto-creates the region). Both flavours can’t coexist in one model.
When you actually need COM / COMRES / COMSAV
Per-record diagnostic for
$TABLE(canonical Part VIII §VII example):$ABBREV COMRES=1 COMSAV=1 $PRED COM(1) = LOG(DV) $TABLE COM(1)=LDVCOM(n)=labelin$TABLElabels the column; without a label it appears as:001,:002, etc.Record bookkeeping in a copying block:
$ABBR COMRES=2 IF (COMACT.EQ.1) THEN COM(1) = IIDX(NIREC) COM(2) = CNTID(NIREC) ENDIFMixture models where
MIXNUM-specific computations inCOMACT=1must survive toCOMACT=2.Interfacing with user-supplied Fortran routines that write into
NMPRD4’s firstnpositions.Cross-block state machine —
$PKinitialises,$DESupdates,$ERRORreads, all through reservedCOMslots. The only way three subroutines can share per-record state, since the one-block LHS rule forbids named variables from crossing.
COM() as a TTE state machine
Nyberg et al.’s TTE simulator (PAGE 2014, poster 404) uses $ABB COMRES=7 to hold per-individual state: event time (COM(1), -1 = none yet), current random draw (COM(2)), survival at event (COM(3)), censoring time (COM(4)), counters (COM(6), COM(7)). $PK initialises at NEWIND.NE.2 and calls CALL RANDOM(2, R); $DES updates COM(1) / COM(3) when COM(2) > SUR(T) (event detected mid-integration); $ERROR reads the slots and writes per-event rows to fort.99 via verbatim WRITE. Pairs with the MTIME trick above to avoid dense grids entirely.
NONMEM state variables
NONMEM’s main driver calls the generated Fortran subroutines at different points in execution (preprocessing, per-problem init, per-record integration, copy passes, finalisation). State variables are read-only reserved names the driver maintains to tell user code where it is — right-hand-only, homed in specific Fortran modules that govern visibility. Authoritative list: Part IV Ch IV §E.4–E.5.
Variable reference
| Variable | Scope | Tracks | Readable in | Typical idiom |
|---|---|---|---|---|
ICALL |
Run phase | 0 pre · 1 per-problem init · 2 estimation + copy passes · 3 finalisation · 4–6 simulation | $INFN: 0/1/3 · $PRED: all · PREDPP per-record blocks: only 2 |
IF (ICALL.EQ.3) ... in $INFN or $PRED to extract final state |
NPROB / IPROB |
Run/problem | Total / current problem index | Everywhere | Multi-problem control streams |
NREP / IREP |
Simulation | Total / current replicate; 0 without $SIMULATION |
Everywhere | Replicate-indexed code under $SIMULATION |
S1NUM / S2NUM |
Superproblem | Scope-1 / scope-2 numbers | Everywhere | Under $SUPER only |
COMACT |
Pass sub-phase | 0 estimation iter · 1 first copy pass (ETA=0) · 2 second copy pass (ETA=cond) · 3 nonparametric | Per-record blocks ($PRED, $PK, $ERROR, $MIX, $AES); $DES stale; not $INFN |
IF (COMACT.EQ.1) COM(i) = VAL |
MIXNUM |
Pass-level | Mixture subpop (1..NSPOP); per-individual after classification (verified: IDs partitioned, constant within each) |
Per-record blocks; requires $MIX else error 574 |
IF (MIXNUM.EQ.1) ... ELSE ... |
NEWIND |
Record | 0 dataset’s first record · 1 new-individual boundary · 2 continuation | Per-record blocks; $DES stale on dose rows |
IF (NEWIND.NE.2) ACC=0 to reset per-individual accumulators |
NIREC |
Record | Current individual index (1..NINDR); constant within an individual |
Per-record blocks | Rarely referenced directly |
NDREC |
Record | Record index within current individual (1..LIREC); resets per individual |
Per-record blocks | IF (NDREC.EQ.1) ... = first record of individual |
LIREC |
Record | Total records in current individual; varies per individual | Per-record blocks | Loop bounds |
NINDR / INDR1 / INDR2 |
Problem-wide | Total / first / last individual index with observations | Everywhere after ICALL=1 |
Denominators, individual-level loop bounds |
NEWL2 |
Record | 1 on first record of an L2 record, 2 on continuation — defaults to 1 always; needs an L2 data item in $INPUT to vary (not $LEVEL) |
Per-record blocks | Per-record grouping within individuals |
IFIRSTEM / FIRSTEM |
Derivative flag | 1 on first derivative pass of EM iter, 0 otherwise; FIRSTEM is the local user copy (verified under METHOD=IMP) |
$PK / $ERROR / $DES during EM / Laplace only |
Skip derivative-independent work after first pass |
OBJECT |
ICALL=3 only |
Final minimum objective function value | $INFN or $PRED at ICALL=3 |
IF (ICALL.EQ.3) EXT=OBJECT (verified: matches .lst minimum) |
IERE |
ICALL=3 only |
Estimation return code; 0 = normal termination |
$INFN or $PRED at ICALL=3 |
IF (IERE.GT.0) ... to detect estimation failure |
IERC |
ICALL=3 only |
Covariance return code; 0 means “not attempted” OR “succeeded” |
$INFN or $PRED at ICALL=3 |
Cross-check with .ext -1000000001 row before branching |
OMEGA(n,m) / SIGMA(n,m) |
Current estimates | Variance estimates at the current point | Everywhere | Parameter transformations (e.g. log-normal η) |
SETHET / SEOMEG / SESIGM |
ICALL=3 + $COV |
Standard errors of θ / Ω / Σ; meaningful only after successful $COV |
$INFN or $PRED at ICALL=3 |
Extract SEs for downstream formatting |
MSEC |
(not user-accessible) | Internal derivative-mode indicator | — (read raises error 208) | Use FIRSTEM instead |
$TABLE cannot display these directly — route through COM(n) in a per-record block, or WRITE to fort.N in $INFN / $PRED at ICALL=3. ICALL = 4, 5, 6 (simulation / sub-problem contexts) are reachable via $INFN + $SIMULATION but not tested here.
Execution lifecycle
State variables change at three nested scopes — problem, pass, and record:
Block × ICALL frequency:
| Block | Fires at | Granularity at ICALL=2 |
Notes |
|---|---|---|---|
$PRED |
ICALL=0, 1, 2, 3 (+ 4/5/6 in SIM) |
per event / observation record | Non-PREDPP; IF (ICALL.EQ.N) guards reach per-problem hooks directly |
$PK |
ICALL=2 |
per event record (or per individual with CALLFL=1) |
IF (ICALL.EQ.0/1/3) bodies auto-promote to $INFN |
$ERROR |
ICALL=2 |
per observation record | Same auto-promotion as $PK |
$DES |
ICALL=2 |
per integrator substep within an observation interval | Not called on dose-only rows; rejects ICALL guards outright |
$AES |
ICALL=2 |
per event record | Analytical-solution ADVANs |
$MIX |
ICALL=2 |
per individual, not per record | Fires once per subject; MIXNUM is the assigned subpop |
$INFN |
ICALL=0, 1, 3 — three times per problem |
— | PREDPP-only (error 237 with $PRED) |
Verified fort-file evidence (ADVAN13 + METHOD=0 MAXEVAL=0, verbatim WRITE in three blocks):
| File | Source | Rows | ICALL seen |
Notes |
|---|---|---|---|---|
fort.51 |
$INFN |
3 | 0, 1, 3 | Once each: preprocessing, init, finalisation |
fort.52 |
$PK |
10 | 2 | Per event record |
fort.53 |
$ERROR |
10 | 2 | Per observation record |
Under conditional methods (COND, LAPLACE, FOCE), the second copy pass (COMACT=2) doubles per-record call count versus FO — expensive $DES ODEs feel this. $TABLE takes the last value written per record, which is COMACT=1 under FO and COMACT=2 under conditional methods (second copy overwrites the first); that’s why FOCE residuals “see” conditional ETAs and FO residuals don’t.
Per-record counters (NEWIND, NIREC, NDREC, LIREC, NEWL2) update on every abbreviated-code call inside each ICALL=2 box; derivative flags (IFIRSTEM / FIRSTEM) set only on EM / Laplace passes. NDREC resets at each individual boundary — no running global record counter.
Observation pattern
No direct print mechanism exists — route through $TABLE via COM(n):
$ABBREVIATED COMRES=1
$`PRED`
COM(1) = `NEWIND`
Y = `THETA`(1) + ETA(1) + EPS(1)
`$TABLE` ID TIME COM(1)=NI FILE=m.tab$TABLE shows the last value written per record (the final copy pass).
Empirical snapshot: per-record counters verified
Probe on 4 subjects with varying records per individual (2, 3, 1, 4), Y = THETA + ETA + EPS, MAXEVAL=0; state vars routed through COM(n) into $TABLE.
Invariants on every row: ICALL=2, NPROB=IPROB=1, NREP=IREP=0, NINDR=4. COMACT=1 under FO, COMACT=2 under COND POSTHOC (second copy overwrites first); all other columns identical between the two runs.
Varying per record:
| ID | TIME | NEWIND |
NIREC |
NDREC |
LIREC |
|---|---|---|---|---|---|
| 1 | 0 | 0 | 1 | 1 | 2 |
| 1 | 1 | 2 | 1 | 2 | 2 |
| 2 | 0 | 1 | 2 | 1 | 3 |
| 2 | 1 | 2 | 2 | 2 | 3 |
| 2 | 2 | 2 | 2 | 3 | 3 |
| 3 | 0 | 1 | 3 | 1 | 1 |
| 4 | 0 | 1 | 4 | 1 | 4 |
| 4 | 1 | 2 | 4 | 2 | 4 |
| 4 | 2 | 2 | 4 | 3 | 4 |
| 4 | 3 | 2 | 4 | 4 | 4 |
Idiom pitfall: IF (NEWIND.NE.2) = “first record of an individual” (matches both 0 and 1); IF (NEWIND.EQ.1) excludes the dataset’s very first record.
Accessing ICALL=3 state (OBJECT, IERE, IERC, SEs)
OBJECT, IERE, IERC, SETHET, SEOMEG, SESIGM are valid only at ICALL=3. They are accessible in either $INFN (PREDPP) or IF (ICALL.EQ.3) inside $PRED (non-PREDPP) — the $INFN record itself is PREDPP-only, but the ICALL=3 hook exists in both paths.
`$INFN` ; PREDPP
IF (`ICALL`.EQ.3) THEN
WRITE (50, *) OBJECT, IERE, IERC
ENDIFFor $PRED models, the same IF (ICALL.EQ.3) block goes directly inside $PRED (no $INFN). See §$INFN and initialization / finalization for structure and further use cases.
Integrator-substep probe
Useful handle on $DES cost:
$ABBREVIATED COMRES=1
$PK
COM(1) = 0 ; reset at each record
$DES
DADT(1) = -K*A(1)
COM(1) = COM(1) + 1
$TABLE COM(1)=NDESEmpirical baseline: single-subject -K·A(1) ODE, TOL=6, dose at 0 and observations at [0.5, 1, 2, 4, 8, 12, 24] gave N_DES = [14, 14, 18, 22, 28, 28, 56] per record. The per-record cost scales with interval length, not a fixed per-observation count.
Reset the counter at each individual boundary instead of each record to get cumulative substep counts per subject:
$ABBREVIATED COMRES=2
$PK
IF (`NEWIND`.LE.1) THEN
COM(1) = 0 ; record index within individual
COM(2) = 0 ; cumulative DES calls for this individual
ENDIF
COM(1) = COM(1) + 1
$DES
DADT(1) = -K*A(1)
COM(2) = COM(2) + 1
$TABLE ID TIME COM(1)=N_REC COM(2)=N_DES ONEHEADER FILE=des.tabWithin each ID block, the last row’s N_DES is that subject’s total.
Stiffness detection: a sudden jump in substep count between adjacent records flags integrator struggle. Probing the baseline model with a narrow spike in K at T=10 (FMOD = 1 + 100*EXP(-((T-10)**2)/0.01)) and observations at [1, 5, 8, 11, 14, 20] gave N_DES = [18, 28, 26, 231, 26, 34] — the record spanning T=10 cost ~9× its neighbours. Scan tables with awk 'prev && $(NF) > prev*3 {print} {prev=$(NF)}' des.tab to surface offenders.
Tolerance sweeps: pair the counter with varying TOL= on $SUBROUTINES (TOL=3 loose / TOL=6 default-ish / TOL=9 tight; each n is relative accuracy ~10^(-n)). Same baseline model:
TOL= |
Total N_DES |
Per-record pattern |
|---|---|---|
| 3 | 76 | 8, 8, 8, 10, 12, 12, 18 |
| 6 | 180 | 14, 14, 18, 22, 28, 28, 56 |
| 9 | 348 | 38, 38, 42, 48, 54, 54, 74 |
Cost roughly doubles per 3 decades of accuracy — adaptive step-size integrators amortise tighter tolerance rather than scaling linearly with it. Compare N_DES totals against the -1000000000 objective-function row in .ext to find the knee of the accuracy/cost curve.
MTIME / MTDIFF / MNOW / MPAST — $PK hooks at arbitrary times
MTIME(i) is not a precision knob. PREDPP stops the integrator at each MTIME(i), calls $PK with MNOW=i, then resumes. $DES substep counts don’t change materially — the adaptive stepper already crosses those times.
What MTIME buys: extra $PK calls inside an integration interval, which enables:
- Discontinuities in PK parameters — turn a parameter on/off at specific times (circadian rhythms, enterohepatic recycling, piecewise infusion rates, time-varying
KA). Cleaner thanIF (T > X) ...inside$DES, which createsDADTdiscontinuities that adaptive steppers handle badly. - Time-shifted observables — capture a compartment amount at
TIME − Δfor moving-window AUC (seeaucdelay2S.ctlin the NONMEM examples directory). - Dense
$PKgrids for custom output — force extra$PKcalls so verbatimWRITEinside$PKcan sample continuous quantities at arbitrary times.$TABLEdoes not emit rows at MTIME points — use verbatimWRITEfrom$PKto capture them (as in Part VIII’sstep_circexa.ctlexample).
Indicator variables:
MNOW—iduring advance toMTIME(i),0otherwiseMPAST(i)—0until the advance pastMTIME(i)completes, then1; reset whenMTIME(i)is redefinedMTDIFF— set to1in$PKto tell PREDPP “MTIME values have changed this call” (set automatically on the first record of an individual; save run time by leaving at0otherwise)
Empirical trace. Dataset: dose at TIME=0, observation at TIME=10. Model: MTIME(1)=5 MTDIFF=0 in $PK; verbatim WRITE(50,*) TIME, MNOW, MPAST(1), NDREC dumps per-call state to fort.50:
TIME MNOW MPAST(1) NDREC
0.0 0 0 1 ← record 1 (dose)
10.0 1 0 2 ← advance to MTIME(1)=5, MNOW=1
10.0 0 1 2 ← advance to record 2 (TIME=10)
Three $PK calls for two data records. The MNOW=1 row reports the destination record’s TIME and NDREC — read TSTATE to see the physical time of the state vector (here 0 and 5 respectively).
Nyberg et al.’s TTE simulator (PAGE 2014, poster 404) applies this dense-hook pattern to RTTE hazard integration: a two-row-per-subject dataset plus many MTIMEs gives a fine hazard grid stored in COM() / fort.N, shrinking dataset size by ~420× vs the naive dense-grid approach.
Gotchas
MSECis not user-accessible. Reading it in abbreviated code raises error 208. UseFIRSTEM(the local copy ofIFIRSTEMNM-TRAN generates inFSUBS).COMSAVdefeats a global counter. Each record sees its savedCOM(i)restored, soCOM(1) = COM(1) + 1counts per-record. For a global counter, omitCOMSAVfor that slot.- Can’t directly observe
COMACT=0via$TABLE— it’s always overwritten by the subsequent copy pass. UseIF (COMACT.EQ.0) ...+COMSAVto capture it.
$INFN and initialization / finalization blocks
Mental model: the per-problem layer
NONMEM’s abbreviated code executes in two distinct layers:
- Per-record layer (
$PK/$DES/$ERROR/$PRED) — called once per data row duringICALL=2. This is where the likelihood contribution for each record is computed. - Per-problem layer (
$INFN) — called three times total per problem: once each atICALL=0(pre-problem),ICALL=1(post-init),ICALL=3(post-estimation). Thinksetup()/teardown().
$INFN is where problem-scope logic lives. It is PREDPP-only (NM-TRAN error 237 if paired with $PRED). For $PRED models the same hooks exist by placing IF (ICALL.EQ.N) guards inside $PRED itself — NM-TRAN emits identical Fortran either way (see auto-promotion below).
Practical uses
- Dump run artefacts at
ICALL=3—WRITE(50, *) OBJECT, IERE, IERCor individual SEs tofort.Nfor custom parsing. The PsN / xpose / nlmixr toolchain reads the same values from.ext/.cov/.phi; explicitWRITEis for non-standard fields or custom formats. - Cross-record aggregation —
DOWHILE(DATA)atICALL=3walks the dataset for quantities that don’t fit per-record (individual AUCs, convergence-gated summaries, bootstrap post-hocs). New data items created here can be tabled since$TABLEruns after finalisation. - Pre-problem setup — at
ICALL=1, read an auxiliary file or precompute a lookup table into aPRINFNvariable once; every subsequent$PKcall reuses it. - Persistent accumulators — counters and caches declared in
$INFN, mutated in$PK/$ERRORacross the estimation. - Superproblem control — set
SKIP_or use phrase macros (END PROBLEM,END FIRST-LEVEL SUPERPROBLEM ITERATION) atICALL=3to abort nested iteration.
Understanding when $INFN is called illuminates the per-record / per-problem split that shapes every NM-TRAN program — the same module design underlies why $PK variables feed forward but $ERROR variables don’t (§Variable visibility) and why COMRES slots exist at all.
Architectural “why”
NM-TRAN compiles the abbreviated code into Fortran subroutines and NONMEM’s main driver calls them at different points:
PK,ERROR,DES,AES,MIX,PREDare called per record duringICALL=2.INFNis called once perICALLtransition into 0, 1, and 3.
Variables emitted by each block land in a specific Fortran module, which governs persistence and visibility:
| Module (FSUBS name) | First defined in | Lifetime | Visible in |
|---|---|---|---|
NMPRD4P |
$PK, $ERROR, $DES, $PRED … |
Persists across calls; re-assigned each call | Every generated routine (via USE); routable to $TABLE |
INFNP |
$INFN, auto-promoted $PK-INFN / $ERROR-INFN |
Persistent; never re-assigned unless user code does | Every generated routine; not directly tablable |
Docs call these NMPRD4 and PRINFN; the generated 7.6 FSUBS.f90 names them NMPRD4P and INFNP. The split is the architectural reason $INFN exists: a single module couldn’t distinguish per-record scratch from cross-record state. INFNP values aren’t in NMPRD4P and so can’t appear in $TABLE directly — shuttle through a COM(n) slot in $PK / $ERROR, or WRITE to fort.N.
$PK-INFN / $ERROR-INFN auto-promotion
Code wrapped in IF (ICALL.EQ.0/1/3) ... ENDIF inside $PK or $ERROR can never fire during the per-record ICALL=2 loop. NM-TRAN moves the branch body into the generated INFN subroutine (Part IV Ch V §C.11 line 1083); the IF test becomes a no-op for the user’s benefit. Verified on 7.6:
IF (ICALL.EQ.3) WRITE(51,*) OBJECTinside either$PKor$ERRORproduces a one-linefort.51at finalisation;WRITE(51,*)appears insideSUBROUTINE INFNinFSUBS.f90, well beforeSUBROUTINE PK/ERROR.- Variables defined in the promoted body land in
MODULE INFNP(notNMPRD4P) — the INFN-defined scope rule applies to auto-promoted code too. DOWHILE (DATA)inside the promoted guard works exactly as in an explicit$INFNblock.$DESactively rejectsIF (ICALL.EQ.N)guards (exit 107) rather than compiling them as dead code — auto-promotion is only for$PKand$ERROR.
Style preference: use explicit $INFN. $PK should read as per-record parameter setup; burying a once-per-problem WRITE inside it confuses scope. Auto-promotion is a compatibility shim — the only legitimate inline use is $PRED models where $INFN is forbidden outright.
Block layout when you do use $INFN
Six ordered sections in the generated INFN subroutine:
"FIRST— verbatim Fortran for declarations (e.g." DOUBLE PRECISION ACC). Fortran forbids declarations after executable statements, so any typed variable must go here."MAIN— delimiter ending theFIRSTblock. Required only when both aFIRSTblock and top-of-third verbatim code coexist.- Abbreviated code from
$INFN. - Auto-promoted
$PK-INFN(invisible to the user). - Auto-promoted
$ERROR-INFN(invisible to the user). "LAST— verbatim Fortran just beforeRETURN. Typical use:CLOSEa file opened in"FIRST.
Template:
`$INFN`
"FIRST
" DOUBLE PRECISION ACC
"MAIN
IF (`ICALL`.EQ.0) THEN
" OPEN(51, FILE='myfile.txt')
ENDIF
IF (`ICALL`.EQ.3) THEN
" WRITE(51,*) OBJECT, IERE, IERC
ENDIF
"LAST
" IF (`ICALL`.EQ.3) CLOSE(51)Other verified ICALL-3 and state-var findings
IFIRSTEM/FIRSTEMflip together during EM methods:METHOD=IMPprobe logged both(0, 0)and(1, 1)pairs, confirmingFIRSTEMis the user-accessible copy ofIFIRSTEM.MSECerrors on read (208); onlyFIRSTEMis visible.SETHET/SEOMEG/SESIGMmirror the-1000000001row of.ext:0.0when$COVwasn’t attempted,NaNon singular info matrix, real numbers on success. Access compiles in both$INFNand$PREDatICALL=3.IERC = 0does not mean “covariance succeeded” — also reads0when$COVis omitted or wasn’t attempted. Combine withIEREand the-1000000001row in.extbefore trusting.MIXNUMis per-individual under$MIX: a 20-subject run withNSPOP=2/P(1)=0.5classified IDs 1–10 toMIXNUM=1and IDs 11–20 toMIXNUM=2; constant within each individual.$TABLEcaptures the assigned subpop, not the per-pass cycling.NEWL2defaults to1on every row; alternating between1and2requires anL2column in$INPUT.$LEVELsets up hierarchical-level ETAs above individuals — a different mechanism from per-record L2 grouping.$LEVELrequiresLEVCENTER=0orLEVCENTER=1on$ESTIMATION(error 714 otherwise).SKIP_ = 1in a single-problemICALL=3block compiles cleanly with no effect to observe. Multi-problem / superproblem testing of phrase macros (END PROBLEM, etc.) requires$SUBROUTINE LIBRARY=...in problem 1 (error 329) — deferred.
Caveats
- Apostrophe-quoted Fortran strings (
WRITE(unit,*) 'text') get mangled by shell heredoc quoting when the control stream is built viassh <<EOF. Write bare numerics, or edit the.modlocally. $INFNand$PREDare mutually exclusive (error 237, verified).INFNP(docs: PRINFN) values are silent in$TABLE— shuttle throughCOM(n)if you need them in a table column.$DESrejectsIF (ICALL.EQ.N)guards outright; any ICALL-0/1/3 work must live in$PK,$ERROR, or an explicit$INFN.
Verbatim code and WRITE / PRINT for debugging
The two layers
Abbreviated code is a constrained DSL (assignments, IF, DOWHILE, NONMEM functions, reserved variables). Verbatim code — lines prefixed with " — is the escape hatch to raw Fortran: NM-TRAN strips the " and copies the rest into the generated subroutine (columns 1–72). Both layers compile into the same subroutine; the abbreviated-to-Fortran translation and the verbatim pass-through interleave. Verbatim placement is controlled by three markers:
| Marker | Position in generated subroutine | Typical use |
|---|---|---|
"FIRST |
Header, before any executable code | Fortran declarations (" DOUBLE PRECISION ACC, " INTEGER K) |
"MAIN (default) |
Body, interleaved with abbreviated code | " WRITE(51, *) X, " CALL MYROUTINE(...) |
"LAST |
Just before RETURN |
File cleanup (" CLOSE(51)) |
WRITE / PRINT works in both layers
Both abbreviated and verbatim accept WRITE / PRINT, with different capabilities:
| Statement | Abbreviated (no ") |
Verbatim (" prefix) |
|---|---|---|
WRITE (N, *) X, Y |
✅ NM-TRAN translates | ✅ literal Fortran |
WRITE (N, *) OMEGA(BLOCK) |
✅ NM-TRAN generates whole-matrix loop | ❌ BLOCK undeclared → Fortran compile error |
WRITE (N, *) X + Y |
❌ expressions not allowed in list | ✅ valid Fortran |
PRINT *, 'label:', X |
✅ character constants allowed with PRINT |
✅ valid Fortran |
Array-whole forms (abbreviated only): THETA, ETA, THETAFR, SETHET, SETHETR, THSIMP, THSIMPR, OMEGA(BLOCK), OMEGA(DIAG), SEOMEG(BLOCK), SEOMEG(DIAG), SIGMA(BLOCK), SIGMA(DIAG), SESIGM(BLOCK), SESIGM(DIAG), IIDX, CNTID. Format must be * for entire-array writes; NONMEM builds the format internally. Missing SE elements print as 0.1000000E+11 (1E10 sentinel); after a failed $COV all SEs are zero.
Unit convention: use 40 < N < 2000 for fort.N debug logs. Unit 6 is NONMEM’s own .lst output — don’t interleave debug with report. Format * is list-directed, safe for debugging; avoid 1 / 2 / 991 / 992 as your own labels (they collide with NONMEM’s built-in (35F14.4) and (35E15.7)).
Gotcha 1: the local-mirror cache
For any $INPUT data-item label, NM-TRAN inserts a local Fortran mirror at the top of the generated subroutine and initialises it from the corresponding DATREC slot on every call. All abbreviated-code references to that label compile to reads of the local mirror, not live DATREC reads:
! FSUBS.f90, generated from `$PRED`
WT = `DATREC`(005) ! NM-TRAN-inserted mirror init, per-call
COM(1) = WT ! reads local mirror
`DATREC`(005) = 888.0 ! verbatim " WT = 888.0 (label-replaced)
COM(2) = WT ! still reads mirror — original value, not 888Empirically on 7.6:
" WT = 888.0in a per-record block modifiesDATREC(005)but doesn’t update the mirror; subsequent abbreviatedWTreads return the original, and$TABLEshows the original.- Abbreviated
WT = 999.0atICALL=2is rejected by NM-TRAN (error 236) — the safety check fires only for abbreviated code, not verbatim. " @WT = 777.0escapes the replacement: generatesWT = 777.0as a plain local variable shadowing the mirror. Data record untouched.- For persistent transgeneration, wrap an abbreviated assignment inside
IF (ICALL.EQ.1) DOWHILE (DATA) ... ENDDOin$PREDor$INFN. AtICALL=0/1/3the walk writes through to the data store and$TABLEreflects the change.
Practical rule: per-record verbatim " label = X is nearly always inert. For local overrides use a different name (WT_EFF = 70.0); for data modification use DOWHILE (DATA) in an init / final block.
Gotcha 2: undefined-RHS relaxation
A single verbatim line in a block — even a pure comment — disables NM-TRAN’s undefined-variable check for the whole block:
`$PRED`
"! any verbatim line
Y = `THETA`(1) + FOOBAR + ETA(1) + EPS(1) ; compiles; FOOBAR = 0 at runtimeWithout the " line, NM-TRAN raises error 208 on FOOBAR. With it, the name flows into the generated Fortran literally; IMPLICIT REAL(KIND=DPSIZE) then declares it as a zero-valued variable. The model silently computes with garbage. If a block mixes verbatim and abbreviated code, read FSUBS.f90 to catch typos NM-TRAN would otherwise have flagged.
Debug recipe
Per-record tracing (no " needed — WRITE works in abbreviated code):
`$PK`
CL = `THETA`(1) * EXP(ETA(1))
WRITE (51, *) ID, NIREC, CL, ETA(1)Finalisation dump with array-whole forms:
`$INFN`
IF (`ICALL`.EQ.3) THEN
WRITE (99, *) OBJECT
WRITE (99, *) `OMEGA`(BLOCK), SEOMEG(BLOCK)
WRITE (99, *) IERE, IERC
ENDIFVerbatim for file lifecycle and Fortran-only constructs:
`$INFN`
"FIRST
" OPEN(51, FILE='debug.log')
IF (`ICALL`.EQ.3) WRITE (51, *) OBJECT
"LAST
" CLOSE(51)Last-resort debugging: read FSUBS.f90 directly — it reveals local-mirror initialisations, auto-promoted $PK-INFN code relocated into SUBROUTINE INFN, and exactly how NM-TRAN translated each line.
Dataset columns: character data and DROP
NONMEM reads NM-TRAN data through a fixed Fortran format; every column that reaches FDATA must parse as numeric. A non-numeric value in a non-dropped column is a hard error:
(DATA ERROR) RECORD 1, DATA ITEM 4, CONTENTS: A
NM-TRAN TERMINATED ABNORMALLY BECAUSE OF DATA ERRORS.
However, DROP (and its synonym SKIP) causes NM-TRAN to strip the column during translation before the fixed-format write. Dropped columns are never numerically parsed:
| Character column position | $INPUT has DROP for it? |
Result |
|---|---|---|
| End / Middle / Beginning | yes | ✅ PASS; FDATA has only numerics |
| Anywhere | no | ❌ FAIL with DATA ERROR |
You can keep textual subject-code / flag columns in a NONMEM-ready dataset without stripping them first — just DROP the position in $INPUT. Column position is irrelevant as long as the DROP lines up with the character field. (Per doc line 218, DROP may not be used when $DATA includes an explicit (format) specification.)
Anatomy of a NONMEM run: files left behind
nmfe76 is not a compiled NONMEM binary — it is a driver script that translates your control stream to Fortran, compiles it against NONMEM’s libraries into a one-shot executable, then runs that executable on your data. That translate-compile-run pipeline is why each run produces ~30–40 files in the working directory: NM-TRAN intermediates (FCON, FDATA, FSUBS.f90, FMSG, …), compile-time artefacts (LINK.LNK, compile.lnk, object files in temp_dir/), the generated nonmem binary, and the estimation outputs. Understanding which files are intermediates, which are machine-parseable mirrors of .lst, and which are extra detail for downstream tools makes it easier to debug when a run surprises you. Per-file schemas are documented in Running NONMEM and Output File Formats; the entries below focus on non-obvious behaviour.
.lst is the primary output
The second nmfe76 argument (mycontrol.lst, the report file) is the authoritative human-readable record. Every other artifact is an intermediate, a machine-parseable mirror of part of .lst, or extra detail for a specific downstream tool. A practical reading order: .lst first, then dip into .ext / .cov / .phi for what isn’t already shown.
The .lst reads top-to-bottom as a transcript:
- Date stamp.
- Verbatim echo of the submitted control stream.
NM-TRAN MESSAGES(same content asFMSG).- License banner.
- Problem setup: data-set summary,
LABELS FOR DATA ITEMS(including any NM-TRAN synthesized, see below), Fortran format forFDATA. - Initial estimates for
THETA/OMEGA/SIGMAwith a block-form visualisation. - Full settings dump for
$ESTand$COV— every option and its value, useful for defaults. - Iteration history (per
PRINT=n). - Minimum / termination block (minimum OBJ, estimates, standard errors).
- Covariance-step output (if requested).
$TABLEoutput blocks.$SCATASCII-art plots.- Elapsed / CPU times and
Stop Time:.
Pipeline stages
- Preprocess:
nmtran_presort+nmtran→FCON,FDATA,FSUBS.f90,FSIZES,FSTREAM,FMSG,FREPORT,PRSIZES.f90. - Compile:
FSUBS.f90+ PREDPP sources + pre-compiled NONMEM objects → a one-shotnonmembinary in the working directory, specific to this model’s$SIZES+ ADVAN. - Run: that binary is executed against
FCON/FDATA.
A Fortran compiler (gfortran or ifort) is a hard dependency; first-run latency is dominated by stage 2 (tens of seconds even for MAXEVAL=0).
Fixed-name vs root-based artifacts
Output files come in three naming families:
| Family | Named after | Examples |
|---|---|---|
| Root-based | control stream basename (arg 1) | root.ext, root.cov, root.cor, root.coi, root.phi, root.shm, root.clt, root.grd, root.cpu, root.xml |
| Report | the 2nd CLI argument | <yourname>.lst |
| Fixed-name | (same every run) | FCON, FDATA, FDATA.csv, FMSG, FREPORT, FSIZES, FSTREAM, FSUBS*, PRSIZES.f90, LINK.LNK, LINKC.LNK, compile.lnk, gfortran.txt, INTER, nmprd4p.mod, nonmem, temp_dir/ |
Running two control streams back-to-back in the same directory clobbers the fixed-name family — only the last run’s FDATA / FSUBS.f90 / nonmem remain. Per-run subdirectories or nmfe76 ... -rundir=... are the usual workaround (what PsN and Pirana do by default).
FDATA, FCON, FSUBS.f90 — what they are
FDATAis what NONMEM actually read. NM-TRAN preprocesses the input (IGNORE=,ACCEPT=,TRANSLATE=, column reordering, date-to-TIME) and emitsFDATAin a fixed Fortran format.FDATA.csvis a CSV mirror with headers. DiffFDATAagainst your source when residuals surprise you or rows silently drop.EVID/MDVcan be auto-generated. If$INPUTdoesn’t declare them, NM-TRAN synthesizes them fromAMT/DVand appends them toFDATA. The.lstLABELS FOR DATA ITEMSline shows the final column list.FCONis NONMEM’s actual configuration input — a fixed-column Fortran file with ~40 four-letter record codes (FILE,PROB,FMTN,DATA,ITEM,INDX,LABL,LBW1,FORM,STRC,THCN,THTA,LOWR,UPPR,BLST,DIAG,ESTM,BEST,ORDR,COVR,COVT,CPAR,SPAR, …), most undocumented. Tools parsing it rely on an informal schema. For an opaque NM-TRAN error,FCONoften shows the problem more directly than the.lst.FSUBS.f90is your model as Fortran.$PK/$ERROR/$DES/$PREDtranslate to subroutines inside it. NM-TRAN inserts commented breadcrumbs for analytic derivatives it generated (A000031 = DERIVATIVE OF KA W.R.T. ETA(001)). You can hand-editFSUBS.f90and re-run withnmfe76 ... -trskip -prsamefor surgical debugging.LBW1inFCONenumerates 38 reserved$TABLEdiagnostic item names NM-TRAN understands without definition:IWRS,IPRD,IRS,NPRED,NRES,NWRES,NIWRES,NIPRED,NIRES,CPRED,CRES,CWRES,CIWRES,CIPRED,CIRES,PREDI,RESI,WRESI,IWRESI,IPREDI,IRESI,CPREDI,CRESI,CWRESI,CIWRESI,CIPREDI,CIRESI,EPRED,ERES,EWRES,EIWRES,EIPRED,EIRES,NPDE,ECWRES,NPD,OBJI. These supplement the always-availablePRED,RES,WRES,IPRED,IRES,IWRES.
Magic negative iteration numbers in .ext
After the positive iteration history, the tail of .ext contains special rows:
| Iteration | Meaning |
|---|---|
-1000000000 |
Final parameter estimates (or Bayesian posterior mean) |
-1000000001 |
Standard errors (only if $COV step succeeded) |
-1000000002 |
Eigenvalues of the correlation matrix ($COV PRINT=E + R/S matrices) |
-1000000003 |
Condition number + lowest / highest eigenvalues |
-1000000004 |
OMEGA / SIGMA in SD / correlation form |
-1000000005 |
SE of the SD / correlation form |
-1000000006 |
1 if fixed, 0 otherwise |
-1000000007 |
Termination status and codes |
-1000000008 |
Gradient of the log-likelihood at the final estimate |
Not every row appears in every run; conditional on method / $COV success. If $COV fails, .cov / .cor / .coi are also not written — an estimation-only run.
temp_dir/ is a compile cache
Holds PREDPP objects compiled for the current $SIZES + ADVAN selection. nmfe76 reuses it to skip recompilation on the next run (why the second run is an order of magnitude faster). Delete it only when switching ADVAN, PREDPP sources, or $SIZES. Force rebuild with -prcompile, skip staleness check with -prsame.
A typical temp_dir/ holds ~180 files, ~150 of which are Fortran .mod module-interface files (cmnm1_int.mod, cmnm1_real.mod, cmnm1_char.mod, cmnm1_log.mod, …, cmnm7_*; cmnm = “COMMON NONMEM”). The extension collision with NONMEM control streams is cosmetic on case-insensitive filesystems but confusing on Linux: find . -name '*.mod' returns a flood.
Other quirks
FSUBS,FSUBS2,FSUBS.f90are byte-identical copies. OnlyFSUBS.f90is compiled; the other two are legacy mirrors.compile.lnkvsLINK.LNKdiffer by a few entries.compile.lnkis what got recompiled this run;LINK.LNKis what gets linked (includes pre-built.oobjects from/opt/nm760/pr/for size-insensitive PREDPP routines). Diff them to see what was rebuilt vs reused..xmlis produced unconditionally. EvenMAXEVAL=0emits ~200 KB of XML mirror. Disable withnmfe76 ... -xmlofffor simulations / bootstraps.INTERandgfortran.txtare typically zero-byte markers, not errors.nonmembinary is not stripped —file nonmemreportswith debug_info. Segfault → symbolic backtrace;gdb ./nonmem coreworks out of the box.PRECONDTIONING(missing secondI) appears 4× in every.lst. A bug in NONMEM’s own output, not in user code. Grepping forPRECONDITIONINGmisses four of the five occurrences..shmencodes exclusion flags in a binary-decimal formatx.abcdef(each digit is 0 or 1), not a real number. Leftmostx=1means excluded; positionsa..frecord which rule fired. An FO run typically shows1.010000for every eta.
What to keep, what to delete
For archival, the minimal sufficient set is:
- Control stream (
.mod) - Data file referenced by
$DATA root.lst,root.ext,root.cov,root.cor,root.phi,root.xml,root.shm,root.grd
Everything else (FCON, FDATA*, FSUBS*, PRSIZES.f90, LINK.LNK, LINKC.LNK, compile.lnk, gfortran.txt, INTER, nmprd4p.mod, nonmem, temp_dir/) can be regenerated from the .mod + data. PsN and Pirana delete them by default after a successful run.
Multiple $PROBLEM records and $SUPER
Mental model
A $PROBLEM record defines one unit of work (fit, simulate, evaluate). Multiple $PROBLEM records in one control stream run sequentially, sharing abbreviated code but separate in their $DATA, $ESTIMATION, and tables. $SUPER wraps a sequence of problems into an iteration — SCOPE=n ITERATIONS=m means “the next n problems, repeated m times”.
The canonical two-problem pattern: estimate in problem 1, simulate from those estimates in problem 2.
Practical uses, ranked
- Post-estimation simulation — estimate once, then generate virtual populations, VPC datasets, or what-if scenarios using the fitted parameters.
MSF(Model Specification File) is the handoff medium. - Sequential sub-models — fit PK in problem 1, freeze PK estimates with
$MSFI+ fit PD in problem 2. - Bootstrap / bias correction via
$SUPER— iterate a sim + fit loop to study estimator properties (Part VIII Superproblem 1 example). - Two-stage analysis — fit individuals in problem 1, summarise across individuals in problem 2 (Part VIII Superproblem 2 example).
PsN automates most common cases (execute, simulate, bootstrap); native multi-$PROBLEM is useful when the workflow doesn’t fit a pre-made PsN command, or when you want everything self-contained in one run.
Worked example: estimate then simulate
Verified end-to-end on NM 7.6.0:
$PROBLEM ESTIMATE
$INPUT ID TIME AMT DV `EVID`
$DATA d
$SUBROUTINES ADVAN1 TRANS2
$PK
CL = THETA(1) * EXP(ETA(1))
V = THETA(2) * EXP(ETA(2))
S1 = V
$ERROR
IPRED = F
Y = F + F*EPS(1) + EPS(2)
$THETA (0, 5) (0, 20)
$`OMEGA` 0.1 0.1
$SIGMA 0.01 0.1
$ESTIMATION METHOD=COND `MAXEVAL`=9990 `MSFO`=msf
$PROBLEM SIMULATE-FROM-MSF
$INPUT ID TIME AMT DV `EVID`
$DATA d REWIND
$`MSFI` msf
$SIMULATION (12345) ONLY TRUE=FINAL
$TABLE ID TIME AMT DV `EVID` IPRED NOAPPEND NOHEADER NOPRINT FILE=sim.tabHandoff mechanisms:
MSFO=msfin problem 1 writes final estimates to a model specification file.$MSFI msfin problem 2 reads them back as starting estimates.TRUE=FINALon$SIMULATIONtells the simulator to use the MSFI estimates (not the$THETA/$OMEGA/$SIGMAshells).$DATA d REWINDis mandatory when the same data file is reused — without it, problem 2 aborts with error 31 (INPUT DATA FILE IS AT EOF).ONLY(short forONLYSIMULATION) suppresses estimation in problem 2;$EST/$COVare not allowed whenONLYSIMULATIONis in effect.
For the dataset used in verification (3 subjects × 7 records, 21 rows), problem 1 converged at OBJ = 34.52 with CL = 1.05, V = 11.96. Problem 2’s sim.tab produced new DV realisations from those estimates (e.g. DV for ID 1 became 10.52, 5.96, 4.92, ... vs observed 2.5, 5.1, 7.2, ...).
State variables for tracking position
Inside abbreviated code (readable via NMPRD_INT):
NPROB— total number of problems in the runIPROB— index of the current problem (1-based)S1NUM/S2NUM— which first- / second-level superproblem is active (0if none)S1IT/S1NIT— current / total iterations of first-level superS2IT/S2NIT— same for second-level super
Branch logic via IF (IPROB.EQ.2) ... to do per-problem things inside the single shared abbreviated block. These variables are “read-only” in the docs’ phrasing but only reliably populated in initialization / finalization blocks (ICALL=0/1/3) — per-record reads are ambiguous across the copy passes.
$SUPER — iterating problem sequences
`$PROB` STANDALONE
... abbreviated code, data, estimation ...
$SUPER SCOPE=2 ITERATIONS=2
$PROB IN-SUPER-A
... data, estimation (abbreviated code inherited) ...
`$PROB` IN-SUPER-B
... data, estimation (abbreviated code inherited) ...Execution sequence, verified from m.lst problem headers:
| NONMEM header | IPROB |
Super iter |
|---|---|---|
PROBLEM NO.: 1 |
1 | — |
PROBLEM NO.: 2 ITERATION NO.: 1 |
2 | 1 |
PROBLEM NO.: 3 ITERATION NO.: 1 |
3 | 1 |
PROBLEM NO.: 2 ITERATION NO.: 2 |
2 | 2 |
PROBLEM NO.: 3 ITERATION NO.: 2 |
3 | 2 |
SCOPE=n includes the next n problems in the super. Nesting depth is at most 2; the inner super must fit entirely inside the outer (s1 ≥ n+s2).
For early termination (convergence-based stop, bias threshold, etc.), set SKIP_ in a finalization block or use phrase macros (END PROBLEM, END FIRST-LEVEL SUPERPROBLEM ITERATION) per §SKIP_.
Gotchas
- Shared abbreviated code. NM-TRAN generates one
FSUBS.f90with onePRED/PK/DES/ERRORsubroutine. All$PROBLEMrecords use the same block — redefining$PREDin a later problem raises error 329 ($SUBR LIBRARY REQUIRED IN PROBLEM 1 IF LATER PROBLEM HAS ABBR.CODE). Branch viaIF (IPROB.EQ.N) ...inside the single shared block. - Data file must be rewound. Each problem reads the data file from its current position; without
$DATA d REWIND, the second problem hits error 31. $TABLEoverwrites across super iterations unlessFILE=varies per-iter. For per-iteration diagnostics, includeS1ITin the filename via verbatimWRITEto a customfort.Ninstead, or use$TABLE ... APPEND.- Verbatim use of
S1NUM,S1IT, etc. requires explicitUSE NMPRD_INT, ONLY: S1NUM => IDXSUP(1), ...— abbreviated code handles the import automatically but raw Fortran verbatim does not. $SIM TRUE=FINAL(notTRUE=PRIOR) is the option that pulls$MSFIestimates into the simulator. Verified on NM 7.6.0; older guides sometimes suggestTRUE=PRIORfor this role.- Compilation speedup. If later problems don’t need second derivatives, add
$ABBR DERIV2=NOafter the first$PROBto trimFSUBSconstruction time (FMSG emits this as a suggestion for multi-problem runs).
Sending signals to a running NONMEM: polling cadence is $EST PRINT
NONMEM’s next.sig / stop.sig / print.sig / paraprint.sig files are listed in Running NONMEM as a way to advance or terminate a running estimation cleanly — create an empty file with one of those names in the working directory, NONMEM notices and reacts. What is not documented is how often NONMEM checks for them.
Empirically (NM 7.6.0): NONMEM polls its working directory at every $EST PRINT event. Latency between touch <signal> and NONMEM’s Ending Mode / Ending Program response equals the PRINT value, in iterations:
$EST PRINT |
Touch iteration | Ending Mode after |
Latency |
|---|---|---|---|
| 1 | -198 | -197 | 1 iter |
| 10 | -160 | -150 | 10 iter |
| 50 | -350 | -300 | 50 iter |
Probes were run on small SAEM models with CTYPE=0 to suppress automatic convergence; next.sig was injected mid burn-in via touch and the iteration shown is the last data row in .ext before Ending Mode appeared in the iteration trace.
Wallclock implication. PRINT is in iterations, not seconds. For a slow per-iter model with PRINT=50, fifty iterations may take many minutes — so a stop.sig sent expecting “instant” termination can sit unconsumed for a non-trivial wallclock window. When rapid response matters, reduce PRINT to a small value (the .ext / .lst grow more, but signal latency drops to ~1 iteration).
stop.sig semantics on chained $EST. Empirically, stop.sig skips ALL remaining $EST records and goes straight to $COVARIANCE (when defined). The common SAEM → IMP EONLY=1 OFV-refinement chain is therefore broken by stop.sig mid-SAEM: the IMP refinement does not run, and the reported OFV stays the stochastic SAEM value rather than the cleaned-up importance-sampling value. Use next.sig to advance from SAEM to the IMP refinement cleanly when the chain matters.
$EST option defaults per method
Every $ESTIMATION option has a method-specific default that NONMEM applies silently. The .lst echoes a human-readable subset; the .xml carries the exhaustive list in <nm:estimation_options nm:knob='value' ... /> (one element per chained $EST step). A $EST METHOD=X probe with the minimum extra options NONMEM accepts is the cleanest empirical way to know “what does NONMEM use if I don’t set it?”
Common across every method
These attributes have the same value across FOCE / FOCE-INTER / ITS / IMP / IMP-EONLY / SAEM (NM 7.6.0):
| key | default |
|---|---|
analysis_type |
pop |
atol |
0 |
centered_eta |
no |
cond_estim |
yes |
ctype |
0 (no convergence test) |
dercont |
0 |
etader / etastype / optmap / mceta |
0 |
evalshrink / noprior / nocov / notitle / nolabel |
0 |
format |
s1pe12.5 |
laplace / msfo / objsort |
no |
maxfn |
528 |
nsig |
3 |
order |
tsol |
sigl / siglo |
100 |
slow_gradient |
noslow |
The file attribute defaults to <basename>.ext (per-run); seed / clockseed are NONMEM-derived per-run for stochastic methods — not user-meaningful as “defaults”.
Method-specific differences
Only the differentiating knobs shown; -- means the attribute is absent (NONMEM omits inapplicable knobs from the XML entirely):
| key | FOCE | FOCE-INTER | ITS | IMP | IMP EONLY=1 | SAEM |
|---|---|---|---|---|---|---|
epseta_interaction |
no |
yes |
yes |
yes |
yes |
yes |
eonly |
– | – | – | 0 |
1 |
0 |
iaccept |
– | – | – | 0.4 |
0.4 |
0.4 |
iscale_min |
– | – | – | 0.1 |
0.1 |
1E-06 |
iscale_max |
– | – | – | 10 |
10 |
1000000 |
iacceptl / df / grdq |
– | – | – | 0 |
0 |
– |
mapiter |
– | – | – | 1 |
1 |
– |
mapinter / mapcov |
– | – | – | 0 / 1 |
0 / 1 |
– |
mapiters |
– | – | – | – | – | 0 |
massreset |
– | – | – | – | – | -1 |
ikappa |
– | – | – | – | – | 1 |
isample_m1 / _m1b / _m2 / _m3 |
– | – | – | – | – | 2 |
isample_m1a |
– | – | – | – | – | 0 |
ranmethod |
– | – | – | 3u |
3u |
3u |
anneal / mum / grd |
– | – | BLANK |
BLANK |
BLANK |
BLANK |
auto / constrain |
– | – | 0 / 1 |
0 / 1 |
0 / 1 |
0 / 1 |
Two methodological asymmetries worth knowing:
iscale_min/iscale_maxdiffer wildly between IMP (0.1/10) and SAEM (1E-06/1000000). IMP’s importance-sampling proposal density is scaled around 1, so its rescale window is tight; SAEM’s stochastic-approximation kernel scales more aggressively to explore.- The
mapiter*family is IMP-only (controls the MAP-Bayes refinement of subjectphivalues inside each importance sample); SAEM uses its own kernel scheme (isample_m1…m3,ikappa,massreset).
Inherently user-set knobs
These are NOT method defaults — NONMEM rejects the run if you omit them, so any value you see in an actual run is a value you typed:
niter— ITS / IMP / SAEM all require it.nburn,isample— SAEM / IMP require these too.
BAYES not in the table
Probing BAYES cleanly requires a $PRIOR record that the minimal probe model lacks; deferred until needed.
Verifying on your own host
Run the minimal probe yourself:
nmfe76 model.mod model.lst # model has just $EST METHOD=X
grep -A 30 estimation_options model.xml | head -40A self-closing element with one nm:knob='value' attribute per knob NONMEM applied — attribute order is alphabetical-ish but not guaranteed stable across NM patches.
Verified 2026-05-09 against NONMEM 7.6.0 via probes ~/positron-nonmem/probe-defaults/{foce_inter,foce,its,imp,imp_eonly,saem}/run001.xml.
Accepted aliases for infinity in $THETA bounds
NONMEM does not actually represent unbounded bounds — every $THETA lower / upper slot is a concrete number by the time FCON is written. NM-TRAN’s lexer recognises a small set of tokens as “unbounded” and rewrites them to the sentinel ±1,000,000 before emission. The recognition appears to be prefix-matching on INF: Part IV, Chapter III documents three tokens (INF, INFINITY, INFIN), but INFTY also works on NMTRAN 7.6.0. All four resolve to the same ±1,000,000 bound:
`$THETA`
(-INF, 1, INF ) ; all four triples
(-INFINITY, 1, INFINITY) ; read as
(-INFIN, 1, INFIN ) ; (-1000000, 1, 1000000)
(-INFTY, 1, INFTY ) ; after NMTRAN translationThese tokens are lexical — recognised only inside $THETA bound triples. Using them in abbreviated code raises ERROR 208: UNDEFINED VARIABLE. For a large value in abbreviated code use a literal like 1.0D+30, or the machine-overflow constant INFNTY (≈ 1.0E+154) via verbatim USE NMPRD_REAL — see the global variables table.