Simulation

Maturity: stable — see Feature Maturity for what this means.

The optional [simulation] block defines a virtual clinical trial design for generating simulated data. This is useful for model validation and testing.

Syntax

[simulation]
  n_subjects = N
  dose_amt   = AMOUNT
  dose_cmt   = COMPARTMENT
  seed       = SEED
  times      = [t1, t2, t3, ...]
  horizon    = T          # TTE only — administrative censoring time
Key Aliases Description
n_subjects subjects Number of virtual subjects to simulate
dose_amt dose Dose amount administered to each subject
dose_cmt cmt Dosing compartment (1-indexed)
seed Random seed for reproducible simulations
times Continuous observation time points (Gaussian endpoints)
horizon Administrative censoring time for TTE endpoints (finite, > 0)

A block must define at least one of times or horizon, matched to the model’s endpoints:

  • a model with a continuous (residual-error) endpoint requires times;
  • a model with a time-to-event endpoint requires horizon (see TTE simulation below);
  • a joint PK + TTE model requires both.

Supplying only horizon for a model that has a continuous endpoint is an error (it would otherwise simulate zero continuous observations), as is supplying only times for a TTE model.

An unknown or malformed key in [simulation] is a parse error (not silently ignored), so a typo such as n_subject, or a line missing its = such as n_subjects 6, is reported rather than falling back to the default. A horizon that is not finite and strictly positive is likewise rejected.

Example

[simulation]
  n_subjects = 100
  dose_amt   = 100
  dose_cmt   = 1
  seed       = 12345
  times      = [0.5, 1, 2, 4, 8, 12, 24]

This simulates 100 subjects, each receiving a dose of 100 units into compartment 1, with observations at 0.5, 1, 2, 4, 8, 12, and 24 hours.

Usage

Run a simulation-estimation study from the CLI:

ferx model.ferx --simulate

This will: 1. Parse the model and [simulation] block 2. Generate simulated data using the model’s default parameters and random effects 3. Fit the model to the simulated data 4. Report parameter estimates and diagnostics

Simulation Process

  1. For each subject, random effects are sampled from \(\eta_i \sim N(0, \Omega)\)
  2. Individual parameters are computed using the [individual_parameters] equations
  3. Predictions are generated using the structural model
  4. Residual error is added: \(DV = IPRED + \epsilon\), where \(\epsilon \sim N(0, V)\)
  5. Observations below 0.001 are clipped to 0.001

TTE simulation

For a time-to-event model there are no continuous observations to schedule; instead each synthetic subject is followed until an event occurs or until the administrative horizon, at which point an event-free subject is right-censored. --simulate therefore generates, for each subject, one right-censored TTE row per event (cause) compartment, then draws the outcome:

  • a single [event_model] yields one row per subject — an event before the horizon, or right-censoring at the horizon;
  • competing risks (several [event_model] blocks on distinct compartments) yield one row per cause: the earliest latent event is observed and the remaining causes are right-censored at that same time, or — if no cause fires before the horizon — every cause is right-censored at the horizon.
[simulation]
  n_subjects = 200
  horizon    = 14     # follow each subject to t = 14, censoring survivors there
  seed       = 12345

Because a TTE design has no continuous times, the horizon key is required when the model has a TTE endpoint; omitting it is a parse error. See examples/tte_competing_risks.ferx.

Decoupled horizon and VPC

The horizon is an administrative censoring time decoupled from the observed event times. This matters when re-simulating an existing event-bearing dataset for a visual predictive check (VPC): without an explicit horizon a record that already carries an event imposes no censoring window (it would re-draw unbounded), which biases the simulated event-time distribution. Supplying horizon overrides each record’s per-record window so every simulated cause censors at the planned study end. The same control is available on the library API as SimulateOptions { horizon, .. }.

Covariates in Simulation

Covariates can be specified per subject in the [simulation] block. Currently, the simulated population uses default covariate values (covariates are not randomized).