Editing ferx model files programmatically
ferx model files (.ferx) are plain text, so you can create and modify them entirely from R without opening an editor. This article covers the three functions that support that workflow: ferx_model_new(), ferx_model_section(), and ferx_model_set_section().
See also Chapter 3 (Model DSL) in the ferx book for a complete reference with examples.
Starting from scratch
ferx_model_new() writes a skeleton .ferx file for one of five built-in templates: "1cpt_oral" (default), "1cpt_iv", "2cpt_oral", "2cpt_iv", and "ode".
Pass print = TRUE to preview a template in the console without writing any file — useful when you are exploring or copy-pasting a starting point:
ferx_model_new(print = TRUE)ferx_model_new(template = "2cpt_iv", print = TRUE)To write a file, supply a path. By default the file is also opened in your editor (edit = TRUE); pass edit = FALSE to skip that:
ferx_model_new("my_model.ferx", edit = FALSE)Inspecting a section
ferx_model_section() extracts the body of a named section and prints it. Start with the bundled warfarin example:
ex <- ferx_example("warfarin")
ferx_model_section(ex$model, "parameters")The return value is the character vector of lines, returned invisibly, so you can capture it:
params <- ferx_model_section(ex$model, "parameters")
paramsScripted read-modify-write
The extract → modify → write-back pattern lets you change a model programmatically without an editor. Here we raise the initial CL estimate in the parameters section and write the result to a temporary copy:
# work in a fresh temp file so re-knitting never reuses stale state
model_path <- tempfile(fileext = ".ferx")
stopifnot(file.copy(ex$model, model_path))
# read the section
lines <- ferx_model_section(model_path, "parameters")
# modify: change the TVCL initial value from 0.134 to 0.5
lines <- sub("TVCL\\([^,]+,", "TVCL(0.5,", lines)
# write back
ferx_model_set_section(model_path, "parameters", lines)
# verify
ferx_model_section(model_path, "parameters")ferx_model_set_section() replaces only the targeted section; all other sections are left untouched.
Console-only workflow: create, tweak, fit
You can go from nothing to an estimated model without ever opening a text editor. The steps below create a one-compartment oral model file, switch the estimation method to FOCEI, and run ferx_fit():
model_path <- file.path(tempdir(), "run1.ferx")
# 1. Write a skeleton
ferx_model_new(model_path, template = "1cpt_oral", edit = FALSE)
# 2. Switch method to focei
ferx_model_set_section(model_path, "fit_options", c(
" method = focei",
" maxiter = 300",
" covariance = true"
))
# 3. Fit (uses the bundled warfarin data for illustration)
ex <- ferx_example("warfarin")
fit <- ferx_fit(model_path, ex$data)
print(fit)Opening a model in an editor
ferx_model_edit() copies a bundled example to a destination directory (or leaves a user-owned file in place) and opens it in your editor. It is the interactive companion to the scripted functions above.
# Copy the bundled warfarin model to tempdir() and open it
my_model <- ferx_model_edit(ex$model, dest = tempdir())
# After editing, save the result under a new name for version control
ferx_model_edit("run1.ferx", save_as = "run2.ferx")The dest argument controls where the copy lands. If the source file is already outside the installed package directory, ferx_model_edit() edits it in place (no copy) unless dest is set explicitly.
Validating a model file
ferx_model_validate() checks that all required sections are present and reports the result. Use it after creating or editing a model to catch missing sections before running the optimizer:
ferx_model_validate(ex$model)A model missing required sections produces a clear error list:
bad <- tempfile(fileext = ".ferx")
writeLines(c(
"[parameters]",
" theta TVCL(1.0, 0.001, 100.0)",
"[structural_model]",
" pk one_cpt_oral(cl=CL, v=V, ka=KA)"
), bad)
ferx_model_validate(bad)Overwrite guards
By default ferx_model_new() refuses to overwrite an existing file:
model_path <- tempfile(fileext = ".ferx")
ferx_model_new(model_path, edit = FALSE) # creates the file
tryCatch(
ferx_model_new(model_path, edit = FALSE), # would overwrite — error
error = function(e) message(conditionMessage(e))
)Opt in explicitly with overwrite = TRUE:
ferx_model_new(model_path, template = "1cpt_iv", overwrite = TRUE, edit = FALSE)
ferx_model_section(model_path, "structural_model")Function reference
| Function | Purpose |
|---|---|
ferx_model_new() |
Write a skeleton .ferx file from a template |
ferx_model_section() |
Extract the body of a named section |
ferx_model_set_section() |
Replace the body of a named section |
ferx_get_section() |
Print a section mid-pipe (returns object unchanged) |
ferx_set_section() |
Rewrite a section mid-pipe (returns object unchanged) |
ferx_model_edit() |
Copy + open a model in your editor |
ferx_model_validate() |
Check that all required sections are present |