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.1

Verified 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 in OMEGA
  • EPS(1) — within-subject residual error, variance held in SIGMA

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:

  • $PROBLEM may have an empty title.
  • $DATA does not need IGNORE=@ if the file has no header row.
  • Auto-created $OMEGA / $SIGMA appear in .lst as OMEGA HAS SIMPLE DIAGONAL FORM WITH DIMENSION: n.
  • $ESTIMATION, $COVARIANCE, $TABLE, $SCATTERPLOT are truly optional. When $ESTIMATION is 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 $PRED and 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)
  • $INPUT need not include ID — NM-TRAN generates one for single-subject data.
  • $THETA is only required if THETA(n) appears in Y.
  • Y must involve at least one random effect. Y = EPS(1) alone or Y = ETA(1) alone are both sufficient; Y = THETA(1) alone fails with ERROR 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. EPS in $PRED alone stays SINGLE-SUBJECT with WARNING 132; in PREDPP $ERROR it auto-promotes and fabricates a $SIGMA DIAGONAL(n). The doc phrasing hides this asymmetry.
  • Rule 3 never fires alone in 7.6 — $SIGMA without EPS in 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 / ERR configuration 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:

  1. η-side ($OMEGA declared or ETA/ERR in code) and ε-side (EPS in code or $SIGMA declared) — classical mixed-effects pairing
  2. ETA and ERR both in code
  3. PREDPP $ERROR with EPS present
  4. $MSFI ... NPOPETAS=n, n > 0
  5. $ESTIMATION LIKELIHOOD / -2LL combined with any η-side hint
  6. $ESTIMATION METHOD in the EM / Bayesian family

Otherwise 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 MODULE

Mental model — a shared cabinet of numbered drawers:

  1. Every variable = label stuck on one drawer. NM-TRAN picks the numbers.
  2. Regular variables are bound to one block — that’s the “one-block LHS rule” that kills reverse flow.
  3. COMRES=n leaves the first n drawers un-labelled so you can address them directly as COM(1)..COM(n). No compile-time owner → any block can LHS-assign, bypassing the scope check.
  4. NONMEM re-walks the data during copying passes (COMACT=1, COMACT=2, …). Each walk re-runs abbreviated code and can overwrite drawer values.
  5. COMSAV=n gives the first n reserved drawers a per-record shelf; record R’s value is restored when R is re-processed in a later pass.
  6. Assignments inside IF (COMACT>0) ... ENDIF copying blocks auto-save regardless of COMSAV: COM(i) is an explicit SAVE variable (needs COMRES only); 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

  1. Per-record diagnostic for $TABLE (canonical Part VIII §VII example):

    $ABBREV COMRES=1 COMSAV=1
    $PRED
     COM(1) = LOG(DV)
    $TABLE COM(1)=LDV

    COM(n)=label in $TABLE labels the column; without a label it appears as :001, :002, etc.

  2. Record bookkeeping in a copying block:

    $ABBR COMRES=2
    IF (COMACT.EQ.1) THEN
      COM(1) = IIDX(NIREC)
      COM(2) = CNTID(NIREC)
    ENDIF
  3. Mixture models where MIXNUM-specific computations in COMACT=1 must survive to COMACT=2.

  4. Interfacing with user-supplied Fortran routines that write into NMPRD4’s first n positions.

  5. Cross-block state machine — $PK initialises, $DES updates, $ERROR reads, all through reserved COM slots. The only way three subroutines can share per-record state, since the one-block LHS rule forbids named variables from crossing.

TipWorked example: 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:

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])

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
 ENDIF

For $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)=NDES

Empirical 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.

TipPer-individual totals and stiffness detection

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.tab

Within 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.

TipMTIME / 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:

  1. Discontinuities in PK parameters — turn a parameter on/off at specific times (circadian rhythms, enterohepatic recycling, piecewise infusion rates, time-varying KA). Cleaner than IF (T > X) ... inside $DES, which creates DADT discontinuities that adaptive steppers handle badly.
  2. Time-shifted observables — capture a compartment amount at TIME − Δ for moving-window AUC (see aucdelay2S.ctl in the NONMEM examples directory).
  3. Dense $PK grids for custom output — force extra $PK calls so verbatim WRITE inside $PK can sample continuous quantities at arbitrary times. $TABLE does not emit rows at MTIME points — use verbatim WRITE from $PK to capture them (as in Part VIII’s step_circexa.ctl example).

Indicator variables:

  • MNOW — i during advance to MTIME(i), 0 otherwise
  • MPAST(i) — 0 until the advance past MTIME(i) completes, then 1; reset when MTIME(i) is redefined
  • MTDIFF — set to 1 in $PK to tell PREDPP “MTIME values have changed this call” (set automatically on the first record of an individual; save run time by leaving at 0 otherwise)

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

  • MSEC is not user-accessible. Reading it in abbreviated code raises error 208. Use FIRSTEM (the local copy of IFIRSTEM NM-TRAN generates in FSUBS).
  • COMSAV defeats a global counter. Each record sees its saved COM(i) restored, so COM(1) = COM(1) + 1 counts per-record. For a global counter, omit COMSAV for that slot.
  • Can’t directly observe COMACT=0 via $TABLE — it’s always overwritten by the subsequent copy pass. Use IF (COMACT.EQ.0) ... + COMSAV to 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 during ICALL=2. This is where the likelihood contribution for each record is computed.
  • Per-problem layer ($INFN) — called three times total per problem: once each at ICALL=0 (pre-problem), ICALL=1 (post-init), ICALL=3 (post-estimation). Think setup() / 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

  1. Dump run artefacts at ICALL=3 — WRITE(50, *) OBJECT, IERE, IERC or individual SEs to fort.N for custom parsing. The PsN / xpose / nlmixr toolchain reads the same values from .ext / .cov / .phi; explicit WRITE is for non-standard fields or custom formats.
  2. Cross-record aggregation — DOWHILE(DATA) at ICALL=3 walks 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 $TABLE runs after finalisation.
  3. Pre-problem setup — at ICALL=1, read an auxiliary file or precompute a lookup table into a PRINFN variable once; every subsequent $PK call reuses it.
  4. Persistent accumulators — counters and caches declared in $INFN, mutated in $PK / $ERROR across the estimation.
  5. Superproblem control — set SKIP_ or use phrase macros (END PROBLEM, END FIRST-LEVEL SUPERPROBLEM ITERATION) at ICALL=3 to 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, PRED are called per record during ICALL=2.
  • INFN is called once per ICALL transition 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,*) OBJECT inside either $PK or $ERROR produces a one-line fort.51 at finalisation; WRITE(51,*) appears inside SUBROUTINE INFN in FSUBS.f90, well before SUBROUTINE PK / ERROR.
  • Variables defined in the promoted body land in MODULE INFNP (not NMPRD4P) — the INFN-defined scope rule applies to auto-promoted code too.
  • DOWHILE (DATA) inside the promoted guard works exactly as in an explicit $INFN block.
  • $DES actively rejects IF (ICALL.EQ.N) guards (exit 107) rather than compiling them as dead code — auto-promotion is only for $PK and $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:

  1. "FIRST — verbatim Fortran for declarations (e.g. " DOUBLE PRECISION ACC). Fortran forbids declarations after executable statements, so any typed variable must go here.
  2. "MAIN — delimiter ending the FIRST block. Required only when both a FIRST block and top-of-third verbatim code coexist.
  3. Abbreviated code from $INFN.
  4. Auto-promoted $PK-INFN (invisible to the user).
  5. Auto-promoted $ERROR-INFN (invisible to the user).
  6. "LAST — verbatim Fortran just before RETURN. Typical use: CLOSE a 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 / FIRSTEM flip together during EM methods: METHOD=IMP probe logged both (0, 0) and (1, 1) pairs, confirming FIRSTEM is the user-accessible copy of IFIRSTEM. MSEC errors on read (208); only FIRSTEM is visible.
  • SETHET / SEOMEG / SESIGM mirror the -1000000001 row of .ext: 0.0 when $COV wasn’t attempted, NaN on singular info matrix, real numbers on success. Access compiles in both $INFN and $PRED at ICALL=3.
  • IERC = 0 does not mean “covariance succeeded” — also reads 0 when $COV is omitted or wasn’t attempted. Combine with IERE and the -1000000001 row in .ext before trusting.
  • MIXNUM is per-individual under $MIX: a 20-subject run with NSPOP=2 / P(1)=0.5 classified IDs 1–10 to MIXNUM=1 and IDs 11–20 to MIXNUM=2; constant within each individual. $TABLE captures the assigned subpop, not the per-pass cycling.
  • NEWL2 defaults to 1 on every row; alternating between 1 and 2 requires an L2 column in $INPUT. $LEVEL sets up hierarchical-level ETAs above individuals — a different mechanism from per-record L2 grouping.
  • $LEVEL requires LEVCENTER=0 or LEVCENTER=1 on $ESTIMATION (error 714 otherwise).
  • SKIP_ = 1 in a single-problem ICALL=3 block 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 via ssh <<EOF. Write bare numerics, or edit the .mod locally.
  • $INFN and $PRED are mutually exclusive (error 237, verified).
  • INFNP (docs: PRINFN) values are silent in $TABLE — shuttle through COM(n) if you need them in a table column.
  • $DES rejects IF (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 888

Empirically on 7.6:

  • " WT = 888.0 in a per-record block modifies DATREC(005) but doesn’t update the mirror; subsequent abbreviated WT reads return the original, and $TABLE shows the original.
  • Abbreviated WT = 999.0 at ICALL=2 is rejected by NM-TRAN (error 236) — the safety check fires only for abbreviated code, not verbatim.
  • " @WT = 777.0 escapes the replacement: generates WT = 777.0 as a plain local variable shadowing the mirror. Data record untouched.
  • For persistent transgeneration, wrap an abbreviated assignment inside IF (ICALL.EQ.1) DOWHILE (DATA) ... ENDDO in $PRED or $INFN. At ICALL=0/1/3 the walk writes through to the data store and $TABLE reflects 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 runtime

Without 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
 ENDIF

Verbatim 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:

  1. Date stamp.
  2. Verbatim echo of the submitted control stream.
  3. NM-TRAN MESSAGES (same content as FMSG).
  4. License banner.
  5. Problem setup: data-set summary, LABELS FOR DATA ITEMS (including any NM-TRAN synthesized, see below), Fortran format for FDATA.
  6. Initial estimates for THETA / OMEGA / SIGMA with a block-form visualisation.
  7. Full settings dump for $EST and $COV — every option and its value, useful for defaults.
  8. Iteration history (per PRINT=n).
  9. Minimum / termination block (minimum OBJ, estimates, standard errors).
  10. Covariance-step output (if requested).
  11. $TABLE output blocks.
  12. $SCAT ASCII-art plots.
  13. Elapsed / CPU times and Stop Time:.

Pipeline stages

  1. Preprocess: nmtran_presort + nmtran → FCON, FDATA, FSUBS.f90, FSIZES, FSTREAM, FMSG, FREPORT, PRSIZES.f90.
  2. Compile: FSUBS.f90 + PREDPP sources + pre-compiled NONMEM objects → a one-shot nonmem binary in the working directory, specific to this model’s $SIZES + ADVAN.
  3. 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

  • FDATA is what NONMEM actually read. NM-TRAN preprocesses the input (IGNORE=, ACCEPT=, TRANSLATE=, column reordering, date-to-TIME) and emits FDATA in a fixed Fortran format. FDATA.csv is a CSV mirror with headers. Diff FDATA against your source when residuals surprise you or rows silently drop.
  • EVID / MDV can be auto-generated. If $INPUT doesn’t declare them, NM-TRAN synthesizes them from AMT / DV and appends them to FDATA. The .lst LABELS FOR DATA ITEMS line shows the final column list.
  • FCON is 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, FCON often shows the problem more directly than the .lst.
  • FSUBS.f90 is your model as Fortran. $PK / $ERROR / $DES / $PRED translate 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-edit FSUBS.f90 and re-run with nmfe76 ... -trskip -prsame for surgical debugging.
  • LBW1 in FCON enumerates 38 reserved $TABLE diagnostic 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-available PRED, 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.f90 are byte-identical copies. Only FSUBS.f90 is compiled; the other two are legacy mirrors.
  • compile.lnk vs LINK.LNK differ by a few entries. compile.lnk is what got recompiled this run; LINK.LNK is what gets linked (includes pre-built .o objects from /opt/nm760/pr/ for size-insensitive PREDPP routines). Diff them to see what was rebuilt vs reused.
  • .xml is produced unconditionally. Even MAXEVAL=0 emits ~200 KB of XML mirror. Disable with nmfe76 ... -xmloff for simulations / bootstraps.
  • INTER and gfortran.txt are typically zero-byte markers, not errors.
  • nonmem binary is not stripped — file nonmem reports with debug_info. Segfault → symbolic backtrace; gdb ./nonmem core works out of the box.
  • PRECONDTIONING (missing second I) appears 4× in every .lst. A bug in NONMEM’s own output, not in user code. Grepping for PRECONDITIONING misses four of the five occurrences.
  • .shm encodes exclusion flags in a binary-decimal format x.abcdef (each digit is 0 or 1), not a real number. Leftmost x=1 means excluded; positions a..f record which rule fired. An FO run typically shows 1.010000 for 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

  1. 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.
  2. Sequential sub-models — fit PK in problem 1, freeze PK estimates with $MSFI + fit PD in problem 2.
  3. Bootstrap / bias correction via $SUPER — iterate a sim + fit loop to study estimator properties (Part VIII Superproblem 1 example).
  4. 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.tab

Handoff mechanisms:

  • MSFO=msf in problem 1 writes final estimates to a model specification file.
  • $MSFI msf in problem 2 reads them back as starting estimates.
  • TRUE=FINAL on $SIMULATION tells the simulator to use the MSFI estimates (not the $THETA / $OMEGA / $SIGMA shells).
  • $DATA d REWIND is mandatory when the same data file is reused — without it, problem 2 aborts with error 31 (INPUT DATA FILE IS AT EOF).
  • ONLY (short for ONLYSIMULATION) suppresses estimation in problem 2; $EST / $COV are not allowed when ONLYSIMULATION is 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 run
  • IPROB — index of the current problem (1-based)
  • S1NUM / S2NUM — which first- / second-level superproblem is active (0 if none)
  • S1IT / S1NIT — current / total iterations of first-level super
  • S2IT / 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.f90 with one PRED / PK / DES / ERROR subroutine. All $PROBLEM records use the same block — redefining $PRED in a later problem raises error 329 ($SUBR LIBRARY REQUIRED IN PROBLEM 1 IF LATER PROBLEM HAS ABBR.CODE). Branch via IF (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.
  • $TABLE overwrites across super iterations unless FILE= varies per-iter. For per-iteration diagnostics, include S1IT in the filename via verbatim WRITE to a custom fort.N instead, or use $TABLE ... APPEND.
  • Verbatim use of S1NUM, S1IT, etc. requires explicit USE NMPRD_INT, ONLY: S1NUM => IDXSUP(1), ... — abbreviated code handles the import automatically but raw Fortran verbatim does not.
  • $SIM TRUE=FINAL (not TRUE=PRIOR) is the option that pulls $MSFI estimates into the simulator. Verified on NM 7.6.0; older guides sometimes suggest TRUE=PRIOR for this role.
  • Compilation speedup. If later problems don’t need second derivatives, add $ABBR DERIV2=NO after the first $PROB to trim FSUBS construction 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:

  1. iscale_min / iscale_max differ 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.
  2. The mapiter* family is IMP-only (controls the MAP-Bayes refinement of subject phi values 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 -40

A 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 translation

These 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.