Learn R Programming

rxode2

Overview

rxode2 is an R package for solving and simulating from ode-based models. These models are convert the rxode2 mini-language to C and create a compiled dll for fast solving. ODE solving using rxode2 has a few key parts:

Installation

You can install the released version of rxode2 from CRAN with:

install.packages("rxode2")

You can install the development version of rxode2 with

devtools::install_github("nlmixr2/rxode2")

To build models with rxode2, you need a working c compiler. To use parallel threaded solving in rxode2, this c compiler needs to support open-mp.

You can check to see if R has working c compiler you can check with:

## install.packages("pkgbuild")
pkgbuild::has_build_tools(debug = TRUE)

If you do not have the toolchain, you can set it up as described by the platform information below:

Windows

In windows you may simply use installr to install rtools:

install.packages("installr")
library(installr)
install.rtools()

Alternatively you can download and install rtools directly.

Mac OSX

To get the most speed you need OpenMP enabled and compile rxode2 with that compiler. There are various options and the most up to date discussion about this is likely the data.table installation faq for MacOS. The last thing to keep in mind is that rxode2 uses the code very similar to the original lsoda which requires the gfortran compiler to be setup as well as the OpenMP compilers.

If you are going to be using rxode2 and nlmixr together and have an older mac computer, I would suggest trying the following:

library(symengine)

If this crashes your R session then the binary does not work with your Mac machine. To be able to run nlmixr, you will need to compile this package manually. I will proceed assuming you have homebrew installed on your system.

On your system terminal you will need to install the dependencies to compile symengine:

brew install cmake gmp mpfr libmpc

After installing the dependencies, you need to reinstall symengine:

install.packages("symengine", type="source")
library(symengine)

Linux

To install on linux make sure you install gcc (with openmp support) and gfortran using your distribution's package manager.

Development Version

Since the development version of rxode2 uses StanHeaders, you will need to make sure your compiler is setup to support C++14, as described in the rstan setup page. For R 4.0, I do not believe this requires modifying the windows toolchain any longer (so it is much easier to setup).

Once the C++ toolchain is setup appropriately, you can install the development version from GitHub with:

# install.packages("devtools")
devtools::install_github("nlmixr2/rxode2")

Illustrated Example

The model equations can be specified through a text string, a model file or an R expression. Both differential and algebraic equations are permitted. Differential equations are specified by d/dt(var_name) = . Each equation can be separated by a semicolon.

To load rxode2 package and compile the model:

library(rxode2)
#> detected new version of rxode2, cleaning cache
#> rxode2 2.0.6 using 4 threads (see ?getRxThreads)

mod1 <- rxode2({
  C2 <- centr/V2;
  C3 <- peri/V3;
  d/dt(depot) <- -KA*depot;
  d/dt(centr) <- KA*depot - CL*C2 - Q*C2 + Q*C3;
  d/dt(peri)  <- Q*C2 - Q*C3;
  d/dt(eff)   <- Kin - Kout*(1-C2/(EC50+C2))*eff;
})
#> 
#> → creating rxode2 include directory
#> → getting R compile options
#> → precompiling headers
#> ✔ done

Specify ODE parameters and initial conditions

Model parameters can be defined as named vectors. Names of parameters in the vector must be a superset of parameters in the ODE model, and the order of parameters within the vector is not important.

theta <- 
   c(KA=2.94E-01, CL=1.86E+01, V2=4.02E+01, # central 
     Q=1.05E+01,  V3=2.97E+02,              # peripheral
     Kin=1, Kout=1, EC50=200)               # effects

Initial conditions (ICs) can be defined through a vector as well. If the elements are not specified, the initial condition for the compartment is assumed to be zero.

inits <- c(eff=1)

If you want to specify the initial conditions in the model you can add:

eff(0) = 1

Specify Dosing and sampling in rxode2

rxode2 provides a simple and very flexible way to specify dosing and sampling through functions that generate an event table. First, an empty event table is generated through the "eventTable()" function:

ev <- eventTable(amount.units='mg', time.units='hours')

Next, use the add.dosing() and add.sampling() functions of the EventTable object to specify the dosing (amounts, frequency and/or times, etc.) and observation times at which to sample the state of the system. These functions can be called multiple times to specify more complex dosing or sampling regiments. Here, these functions are used to specify 10mg BID dosing for 5 days, followed by 20mg QD dosing for 5 days:

ev$add.dosing(dose=10000, nbr.doses=10, dosing.interval=12)
ev$add.dosing(dose=20000, nbr.doses=5, start.time=120,
              dosing.interval=24)
ev$add.sampling(0:240)

If you wish you can also do this with the mattigr pipe operator %>%

ev <- eventTable(amount.units="mg", time.units="hours") %>%
  add.dosing(dose=10000, nbr.doses=10, dosing.interval=12) %>%
  add.dosing(dose=20000, nbr.doses=5, start.time=120,
             dosing.interval=24) %>%
  add.sampling(0:240)

The functions get.dosing() and get.sampling() can be used to retrieve information from the event table.

head(ev$get.dosing())
#>   id low time high       cmt   amt rate ii addl evid ss dur
#> 1  1  NA    0   NA (default) 10000    0 12    9    1  0   0
#> 2  1  NA  120   NA (default) 20000    0 24    4    1  0   0
head(ev$get.sampling())
#>   id low time high   cmt amt rate ii addl evid ss dur
#> 1  1  NA    0   NA (obs)  NA   NA NA   NA    0 NA  NA
#> 2  1  NA    1   NA (obs)  NA   NA NA   NA    0 NA  NA
#> 3  1  NA    2   NA (obs)  NA   NA NA   NA    0 NA  NA
#> 4  1  NA    3   NA (obs)  NA   NA NA   NA    0 NA  NA
#> 5  1  NA    4   NA (obs)  NA   NA NA   NA    0 NA  NA
#> 6  1  NA    5   NA (obs)  NA   NA NA   NA    0 NA  NA

You may notice that these are similar to NONMEM event tables; If you are more familiar with NONMEM data and events you could use them directly with the event table function et

ev  <- et(amountUnits="mg", timeUnits="hours") %>%
  et(amt=10000, addl=9,ii=12,cmt="depot") %>%
  et(time=120, amt=2000, addl=4, ii=14, cmt="depot") %>%
  et(0:240) # Add sampling 

You can see from the above code, you can dose to the compartment named in the rxode2 model. This slight deviation from NONMEM can reduce the need for compartment renumbering.

These events can also be combined and expanded (to multi-subject events and complex regimens) with rbind, c, seq, and rep. For more information about creating complex dosing regimens using rxode2 see the rxode2 events vignette.

Solving ODEs

The ODE can now be solved by calling the model object's run or solve function. Simulation results for all variables in the model are stored in the output matrix x.

x <- mod1$solve(theta, ev, inits);
knitr::kable(head(x))
timeC2C3depotcentrperieff
00.000000.000000010000.0000.0000.00001.000000
144.375550.91982987452.7651783.897273.18951.084664
254.882962.67298255554.3702206.295793.87581.180825
351.903434.45649274139.5422086.5181323.57831.228914
444.497385.98070763085.1031788.7951776.27021.234610
536.484347.17749812299.2551466.6702131.71691.214742

You can also solve this and create a rxode2 data frame:

x <- mod1 %>% rxSolve(theta, ev, inits);
x
#> ── Solved rxode2 object ──
#> ── Parameters (x$params): ──
#>      V2      V3      KA      CL       Q     Kin    Kout    EC50 
#>  40.200 297.000   0.294  18.600  10.500   1.000   1.000 200.000 
#> ── Initial Conditions (x$inits): ──
#> depot centr  peri   eff 
#>     0     0     0     1 
#> ── First part of data (object): ──
#> # A tibble: 241 × 7
#>   time    C2    C3  depot centr  peri   eff
#>    [h] <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl>
#> 1    0   0   0     10000     0     0   1   
#> 2    1  44.4 0.920  7453. 1784.  273.  1.08
#> 3    2  54.9 2.67   5554. 2206.  794.  1.18
#> 4    3  51.9 4.46   4140. 2087. 1324.  1.23
#> 5    4  44.5 5.98   3085. 1789. 1776.  1.23
#> 6    5  36.5 7.18   2299. 1467. 2132.  1.21
#> # … with 235 more rows

This returns a modified data frame. You can see the compartment values in the plot below:

library(ggplot2)
plot(x,C2) + ylab("Central Concentration")

Or,

plot(x,eff)  + ylab("Effect")

Note that the labels are automatically labeled with the units from the initial event table. rxode2 extracts units to label the plot (if they are present).

Related R Packages

ODE solving

This is a brief comparison of pharmacometric ODE solving R packages to rxode2.

There are several R packages for differential equations. The most popular is deSolve.

However for pharmacometrics-specific ODE solving, there are only 2 packages other than rxode2 released on CRAN. Each uses compiled code to have faster ODE solving.

  • mrgsolve, which uses C++ lsoda solver to solve ODE systems. The user is required to write hybrid R/C++ code to create a mrgsolve model which is translated to C++ for solving.

    In contrast, rxode2 has a R-like mini-language that is parsed into C code that solves the ODE system.

    Unlike rxode2, mrgsolve does not currently support symbolic manipulation of ODE systems, like automatic Jacobian calculation or forward sensitivity calculation (rxode2 currently supports this and this is the basis of nlmixr2's FOCEi algorithm)

  • dMod, which uses a unique syntax to create "reactions". These reactions create the underlying ODEs and then created c code for a compiled deSolve model.

    In contrast rxode2 defines ODE systems at a lower level. rxode2's parsing of the mini-language comes from C, whereas dMod's parsing comes from R.

    Like rxode2, dMod supports symbolic manipulation of ODE systems and calculates forward sensitivities and adjoint sensitivities of systems.

    Unlike rxode2, dMod is not thread-safe since deSolve is not yet thread-safe.

  • PKPDsim which defines models in an R-like syntax and converts the system to compiled code.

    Like mrgsolve, PKPDsim does not currently support symbolic manipulation of ODE systems.

    PKPDsim is not thread-safe.

The open pharmacometrics open source community is fairly friendly, and the rxode2 maintainers has had positive interactions with all of the ODE-solving pharmacometric projects listed.

PK Solved systems

rxode2 supports 1-3 compartment models with gradients (using stan math's auto-differentiation). This currently uses the same equations as PKADVAN to allow time-varying covariates.

rxode2 can mix ODEs and solved systems.

The following packages for solved PK systems are on CRAN

  • mrgsolve currently has 1-2 compartment (poly-exponential models) models built-in. The solved systems and ODEs cannot currently be mixed.

  • pmxTools currently have 1-3 compartment (super-positioning) models built-in. This is a R-only implementation.

  • PKPDsim uses 1-3 "ADVAN" solutions using non-superpositioning.

  • PKPDmodels has a one-compartment model with gradients.

Non-CRAN libraries:

  • PKADVAN Provides 1-3 compartment models using non-superpositioning. This allows time-varying covariates.

Copy Link

Version

Install

install.packages('rxode2')

Monthly Downloads

2,751

Version

2.0.7

License

GPL (>= 3)

Issues

Pull Requests

Stars

Forks

Maintainer

Matthew Fidler

Last Published

May 17th, 2022

Functions in rxode2 (2.0.7)

as.et

Coerce object to data.frame
.rxGetPredictionF

Get the prediction name
cvPost

Sample a covariance Matrix from the Posterior Inverse Wishart distribution.
coef.rxode2

Return the rxode2 coefficients
.getLastIdLvl

Get the last idLvl
.clearPipe

Clear/Set pipeline
.iniHandleFixOrUnfix

Handle Fix or Unfix an expression
assertRxUi

Assert properties of the rxUi models
.minfo

Internal messaging statements
.rxGetLowBoundaryPred1AndIni

Get the lower boundary condition when the transformation requires it
.handleSingleErrTypeNormOrTFoceiBase

Handle the single error for normal or t distributions
.quoteCallInfoLines

Returns quoted call information
.modelHandleModelLines

Handle model lines
.rxGetPredictionFTransform

Get the prediction transformation
.rxWithOptions

Temporarily set options then restore them while running code
.rxSens

Sensitivity for model
gammap

Gammap: normalized lower incomplete gamma function
gammapDer

gammapDer: derivative of gammap
etRbind

Combining event tables
.rxIsLinCmt

Internal function to tell if the linCmt() is the model variables
plot.rxSolve

Plot rxode2 objects
.rxJacobian

Internal function for calculating the Jacobian
+.rxSolve

Update Solved object with '+'
etExpand

Expand additional doses
.copyUi

This copies the rxode2 UI object so it can be modified
rinvchisq

Scaled Inverse Chi Squared distribution
.rxGetVarianceForErrorType

Get Variance for error type
rxCompile

Compile a model if needed
rxBlockZeros

Creates a logical matrix for block matrixes.
lowergamma

lowergamma: upper incomplete gamma function
rxAssignPtr

Assign pointer based on model variables
logit

logit and inverse logit (expit) functions
rxDfdy

Jacobian and parameter derivatives
rxDerived

Calculate derived parameters for the 1-, 2-, and 3- compartment linear models.
rxCombineErrorLines

Combine Error Lines and create rxode2 expression
add.dosing

Add dosing to eventTable
etRep

Repeat an rxode2 event table
add.sampling

Add sampling to eventTable
gammaqInv

gammaqInv and gammaqInva: Inverses of normalized gammaq function
etSeq

Sequence of event tables
genShinyApp.template

Generate an example (template) of a dosing regimen shiny app
et

Event Table Function
.rxWithWd

Temporarily set options then restore them while running code
.rxWithSink

With one sink, then release
erf

Error function
rxIsCurrent

Checks if the rxode2 object was built with the current build
rxGetLin

Get the linear compartment model true function
rxGetDistributionSimulationLines

This is a S3 method for getting the distribution lines for a rxode2 simulation
rxChain2

Second command in chaining commands
.s3register

Register a method for a suggested dependency
print.rxCoef

Print the rxCoef object
rxAllowUnload

Allow unloading of dlls
rxClean

Cleanup anonymous DLLs by unloading them
gammapInv

gammapInv and gammapInva: Inverses of normalized gammap function
print.rxDll

Print rxDll object
.rxGetLambdaFromPred1AndIni

Get the lambda value based on the pred information
.rxGetHiBoundaryPred1AndIni

Get the upper boundary condition when the transformation it
rxIsLoaded

Determine if the DLL associated with the rxode2 object is loaded
.rxPrune

Internal Pruning function
.rxLinCmtGen

Internal function to generate the model variables for a linCmt() model
rxAppendModel

Append two rxui models together
ini.rxUi

Ini block for rxode2/nlmixr models
rxControlUpdateSens

This updates the tolerances based on the sensitivity equations
etTrans

Event translation for rxode2
invWR1d

One correlation sample from the Inverse Wishart distribution
rxCondition

Current Condition for rxode2 object
rxFun

Add user function to rxode2
rxDelete

Delete the DLL for the model
eventTable

Create an event table object
rxGetControl

rxGetControl option from ui
rxExpandGrid

Faster expand.grid
rxPkg

Creates a package from compiled rxode2 models
rxRateDur

Creates a rxRateDur object
rxMd5

Return the md5 of an rxode2 object or file
rxExpandGrid_

Expand grid internal function
rxDemoteAddErr

Demote the error type
rxPp

Simulate a from a Poisson process
rxAssignControlValue

Assign Control Variable
rxGetrxode2

Get rxode2 model from object
rxCbindStudyIndividual

Bind the study parameters and individual parameters
rxModelVars

All model variables for a rxode2 object
.useUtf

Internal function to figure out if this session supports Unicode
rxReload

Reload rxode2 DLL
rxParams

Parameters specified by the model
rxOptExpr

Optimize rxode2 for computer evaluation
rxRmvn

Simulate from a (truncated) multivariate normal
rxReservedKeywords

A list and description of Rode supported reserved keywords
rxSetProd

Defunct setting of product
rxStack

Stack a solved object for things like ggplot
rxSetProgressBar

Set timing for progress bar
rxState

State variables
rxChain

rxChain Chain or add item to solved system of equations
findLhs

Find the assignments in R expression
rxSymInvCholCreate

Creates an object for calculating Omega/Omega^-1 and derivatives
model.function

Model block for rxode2/nlmixr models
phi

Cumulative distribution of standard normal
gammaq

Gammaq: normalized upper incomplete gamma function
rxSymInvCholN

Return the dimension of the built-in derivatives/inverses
rxShiny

Use Shiny to help develop an rxode2 model
rxTheme

rxTheme is the ggplot2 theme for rxode2 plots
rxTempDir

Get the rxode2 temporary directory
rxSetupScale

Setup the initial conditions.
rxWithSeed

Preserved seed and possibly set the seed
rxbeta

Simulate beta variable from threefry generator
getRxThreads

Get/Set the number of threads that rxode2 uses
rxHtml

Format rxSolve and related objects as html.
forderForceBase

Force using base order for rxode2 radix sorting
rxpois

Simulate random Poisson variable from threefry generator
rxParseErr

Prepare Error function for inclusion in rxode2
rLKJ1

One correlation sample from the LKJ distribution
rxParsePk

Parse PK function for inclusion in rxode2
rxbinom

Simulate Binomial variable from threefry generator
rxC

Return the C file associated with the rxode2 object
rxCreateCache

This will create the cache directory for rxode2 to save between sessions
rxD

Add to rxode2's derivative tables
reexports

Objects exported from other packages
rxCat

Use cat when rxode2.verbose is TRUE
is.rxEt

Check to see if this is an rxEt object.
guide_none

Empty Guide
is.rxSolve

Check to see if this is an rxSolve object.
print.rxode2

Print information about the rxode2 object.
rxExpandIfElse

Expand if/else clauses into multiple different types of lines.
rxt

Simulate student t variable from threefry generator
rxRandNV

Create a random "normal" matrix using vandercorput generator
rxPrune

Prune branches (ie if/else) from rxode2
probit

probit and inverse probit functions
rxExpandSens2_

Expand second order sensitivity
rxValidate

Validate rxode2 This allows easy validation/qualification of nlmixr by running the testing suite on your system.
rxSplitPlusQ

This function splits a function based on + or - terms
rxSymInvChol

Get Omega^-1 and derivatives
rxSetSum

Defunct setting of sum
rxSuppressMsg

Respect suppress messages
rxUse

Use model object in your package
rxSolveFree

Free the C solving/parsing information.
rxDynUnload

Unload rxode2 object
rxDll

Return the DLL associated with the rxode2 object
rxSetupIni

Setup the initial conditions.
rxDynLoad

Load rxode2 object
rxExpandSens_

Expand sensitivity
rxLhs

Left handed Variables
rxLock

Lock/unlocking of rxode2 dll file
rxGetSeed

Get the rxode2 seed
rxchisq

Simulate chi-squared variable from threefry generator
rxcauchy

Simulate Cauchy variable from threefry generator
rxErrTypeCombine

Combine transformations and error structures
rxForget

Clear memoise cache for rxode2
rxGetModel

Get model properties without compiling it.
rxEvid

EVID formatting for tibble and other places.
rxexp

Simulate exponential variable from threefry generator
rxExpandFEta_

Expand d(f)/d(eta)
stat_amt

Dosing/Amt geom/stat
rxIndLinState

Set the preferred factoring by state
rxWinSetup

Setup Windows components for rxode2
rxSetIni0

Set Initial conditions to time zero instead of the first observed/dosed time
rxPhysicalDrives

Returns a list of physical drives that have been or currently are mounted to the computer.
rxSetCovariateNamesForPiping

Assign covariates for piping
rxVersion

Version and repository for this dparser package.
rxParsePred

Prepare Pred function for inclusion in rxode2
rxord

Simulate ordinal value
stat_cens

Censoring geom/stat
rxode2

Create an ODE-based model specification
update.rxUi

Update for rxUi
rxnorm

Simulate random normal variable from threefry/vandercorput generator
rxgeom

Simulate geometric variable from threefry generator
rxModels_

Get the rxModels information
rxIndLin_

Inductive linearization solver
rxInits

Initial Values and State values for a rxode2 object
rxSetSeed

Set the parallel seed for rxode2 random number generation
rxRemoveControl

rxRemoveControl options for UI object
rxPreferredDistributionName

Change distribution name to the preferred distribution name term
rxProgress

rxode2 progress bar functions
rxRename

Rename items inside of a rxode2 ui model
rxNorm

Get the normalized model
rxunif

Simulate uniform variable from threefry generator
rxSetSilentErr

Silence some of rxode2's C/C++ messages
rxSumProdModel

Recast model in terms of sum/prod
uppergamma

uppergamma: upper incomplete gamma function
rxSupportedFuns

Get list of supported functions
rxIndLinStrategy

This sets the inductive linearization strategy for matrix building
rxInv

Invert matrix using RcppArmadillo.
rxRepR0_

Rep R0 for foce
rxS

Load a model into a symengine environment
rxSetControl

rxSetControl options for UI object
rxIs

Check the type of an object using Rcpp
rxReq

Require namespace, otherwise throw error.
rxSyncOptions

Sync options with rxode2 variables
rxweibull

Simulate Weibull variable from threefry generator
rxSolve

Solving & Simulation of a ODE/solved system (a options) equation
rxSimThetaOmega

Simulate Parameters from a Theta/Omega specification
rxgamma

Simulate gamma variable from threefry generator
rxSyntaxFunctions

A list and description of Rode supported syntax functions
rxf

Simulate F variable from threefry generator
rxToSE

rxode2 to symengine environment
rxTrans

Translate the model to C code if needed
rxUiGet.cmtLines

S3 for getting information from UI model
rxUnloadAll

Unloads all rxode2 compiled DLLs
summary.rxode2

Print expanded information about the rxode2 object.
summary.rxDll

Summary of rxDll object