|
| 1 | +--- |
| 2 | +layout: post.njk |
| 3 | +title: Landscape of ODE Solvers in R |
| 4 | +subtitle: A Practical Overview |
| 5 | +date: 2026-04-26 |
| 6 | +lastModified: 2026-04-26 |
| 7 | +description: 'An overview of ODE solvers in R, highlighting their capabilities, performance, and practical considerations.' |
| 8 | +author: Evgeny Metelkin |
| 9 | +authorURL: https://metelkin.me |
| 10 | +canonical: https://metelkin.me/landscape-of-ode-solvers-in-r/ |
| 11 | +openGraph: |
| 12 | + title: 'Landscape of ODE Solvers in R' |
| 13 | + description: 'An overview of ODE solvers in R, highlighting their capabilities, performance, and practical considerations.' |
| 14 | + url: https://metelkin.me/landscape-of-ode-solvers-in-r/ |
| 15 | + image: https://metelkin.me/landscape-of-ode-solvers-in-r/img/fig0-cover-520x270.png |
| 16 | + site_name: Evgeny Metelkin |
| 17 | + type: article |
| 18 | +tags: |
| 19 | + - post |
| 20 | +# - featured |
| 21 | + - SoftwareEngineering |
| 22 | + - modeling |
| 23 | + - software |
| 24 | + - r |
| 25 | +--- |
| 26 | + |
| 27 | + |
| 28 | +Solving **ordinary differential equations (ODEs)** is a common task in many fields, including systems biology, pharmacometrics, and engineering. The R ecosystem offers a collection of tools for this purpose: from lightweight numerical solvers to full modeling frameworks. |
| 29 | + |
| 30 | +This article provides **a practical overview of ODE solvers in R**, with a focus on helping users navigate the ecosystem and choose appropriate tools. |
| 31 | + |
| 32 | +All packages here was tested with simple examples and the code was published in the [GitHub repository](https://github.com/metelkin/ode-solvers-in-r). |
| 33 | + |
| 34 | +## What is included |
| 35 | + |
| 36 | +We include tools that: |
| 37 | + |
| 38 | +- Provide an API for R |
| 39 | +- Support solving general ODE systems |
| 40 | +- Are mentioned in literature, documentation, or community discussions |
| 41 | + |
| 42 | +We intentionally exclude: |
| 43 | + |
| 44 | +- Deprecated or archived packages (e.g., RxODE) |
| 45 | +- Thin wrappers around other frameworks without original solvers or formats (e.g., nlmixr2) |
| 46 | +- Small helper packages |
| 47 | +- Highly specialized tools limited to narrow domains |
| 48 | + |
| 49 | +Some tools included in this review go beyond ODE solving and provide additional capabilities such as parameter estimation or simulation workflows. We include them for completeness, without going into those advanced features. |
| 50 | + |
| 51 | +## Overview table |
| 52 | + |
| 53 | +| Package | Type | Engine | Algorithms | Model format | Stiff | DAE | DDE | Time events | Conditional events | CRAN downloads (2025) | |
| 54 | +|---------|------|---------|------------|--------------|-----------------|-----|-----|-------------|--------------------|------------------------| |
| 55 | +| [deSolve](https://cran.r-project.org/package=deSolve) | Compiled | [ODEPACK](http://www.netlib.org/odepack/); [DASPK](http://www.netlib.org/ode/) (in FORTRAN) | lsoda, lsode, radau, euler, rk4, ode23, ode45, etc. | R func (Interpreted); C / C++ / Fortran (Compiled) | Yes (via lsoda) | Yes (via daspk) | Yes (via dede) | Yes | Yes | 635628 | |
| 56 | + |
| 57 | +#### Type |
| 58 | + |
| 59 | +- **Pure R solvers**: Numerical algorithms implemented directly in R. These are easy to inspect and flexible, but typically slower due to interpreter overhead. |
| 60 | +- **Compiled solvers**: Implemented in C/C++/Fortran or wrapping established libraries (e.g., ODEPACK). These provide significantly better performance and are the default choice for most applications. |
| 61 | +- **External runtime interfaces**: Packages that delegate computation to external ecosystems such as Julia or Python. These act as bridges rather than standalone solvers. |
| 62 | + |
| 63 | +#### Engine / Algorithms |
| 64 | + |
| 65 | +- **Engine** refers to the underlying numerical implementation used by the package. This can be: |
| 66 | + - a well-known external library (e.g., ODEPACK), |
| 67 | + - a custom compiled implementation, |
| 68 | + - or an external runtime (e.g., Julia), |
| 69 | + - another R package. |
| 70 | + |
| 71 | +- **Algorithms** lists the available numerical methods (e.g., LSODA, Runge–Kutta, Radau). |
| 72 | + Some packages expose multiple algorithms, while others are limited to a specific class. |
| 73 | + |
| 74 | +#### Model format |
| 75 | + |
| 76 | +ODE models can be defined in different ways: as R functions, domain-specific languages (DSL), structured APIs, or even external code. |
| 77 | + |
| 78 | +The key distinction affecting performance is how the model is executed: |
| 79 | + |
| 80 | +- **Interpreted execution**: The model is defined as an R function and evaluated during each solver step. |
| 81 | +- **Compiled execution**: The model is translated into compiled code before simulation, avoiding interpreter overhead and improving performance. |
| 82 | + |
| 83 | +#### Stiff |
| 84 | + |
| 85 | +Indicates whether the solver can handle **stiff systems**. |
| 86 | + |
| 87 | +Stiffness arises when a system contains processes evolving on very different time scales. Solvers that support stiffness typically use implicit methods or adaptive switching (e.g., LSODA). |
| 88 | + |
| 89 | +#### DAE / DDE |
| 90 | + |
| 91 | +Support for these features is solver-dependent and often requires specific algorithms. |
| 92 | + |
| 93 | +- **DAE (Differential-Algebraic Equations)**: Systems that include algebraic constraints in addition to differential equations. |
| 94 | +- **DDE (Delay Differential Equations)**: Systems where derivatives depend on past states. |
| 95 | + |
| 96 | +#### Time events / Conditional events |
| 97 | + |
| 98 | +These features are important for modeling real-world systems with discontinuities. |
| 99 | + |
| 100 | +- **Time events**: Discrete changes applied at predefined time points (e.g., dosing events). |
| 101 | +- **Conditional events**: Events triggered when a condition is met during simulation (e.g., threshold crossing). |
| 102 | + |
| 103 | +#### CRAN downloads |
| 104 | + |
| 105 | +Total number of downloads in 2025, reflecting usage statistics. |
| 106 | + |
| 107 | +## Repository and test cases |
| 108 | + |
| 109 | +To ensure practical consistency, packages were tested on two simple ODE models. The full code for all packages is available in the companion [GitHub repository](https://github.com/metelkin/ode-solvers-in-r). |
| 110 | + |
| 111 | +We are providing here the code in `mrgsolve` format for two examples: a pharmacokinetic model with non-linear elimination, and the Robertson problem, which is a classic stiff ODE system. |
| 112 | + |
| 113 | +### Example 1: Pharmacokinetic model with non-linear elimination |
| 114 | + |
| 115 | +```r |
| 116 | +library(mrgsolve) |
| 117 | +library(magrittr) |
| 118 | + |
| 119 | +# load model as DLS (C++ like) |
| 120 | +mod <- mcode("pk_model", ' |
| 121 | +$PARAM |
| 122 | +kabs_Alc = 10.0 |
| 123 | +Vmax_ADH = 3 |
| 124 | +Km_ADH = 0.1 |
| 125 | +V_blood = 5.5 |
| 126 | +
|
| 127 | +$CMT |
| 128 | +Alc_g Alc_b_amt |
| 129 | +
|
| 130 | +$ODE |
| 131 | +double Alc_b = Alc_b_amt / V_blood; |
| 132 | +double vabs_Alc = kabs_Alc * Alc_g; |
| 133 | +double v_ADH = Vmax_ADH * Alc_b / (Km_ADH + Alc_b) * V_blood; |
| 134 | +
|
| 135 | +dxdt_Alc_g = -vabs_Alc; |
| 136 | +dxdt_Alc_b_amt = vabs_Alc - v_ADH; |
| 137 | +
|
| 138 | +$TABLE |
| 139 | +capture Alc_b; |
| 140 | +') |
| 141 | + |
| 142 | +# time event |
| 143 | +ev1 <- ev(time = 2, amt = 50, cmt = "Alc_g") |
| 144 | + |
| 145 | +# solve |
| 146 | +out <- mod %>% |
| 147 | + init(Alc_g = 50, Alc_b_amt = 0) %>% |
| 148 | + ev(ev1) %>% |
| 149 | + mrgsim(start = 0, end = 12, delta = 0.001) |
| 150 | + |
| 151 | +plot(out) |
| 152 | +``` |
| 153 | + |
| 154 | + |
| 155 | + |
| 156 | +### Example 2: Robertson problem stiff ODE |
| 157 | + |
| 158 | +```r |
| 159 | +library(mrgsolve) |
| 160 | +library(magrittr) |
| 161 | + |
| 162 | +# load model as DLS (C++ like) |
| 163 | +mod <- mcode("rob_model", ' |
| 164 | +$CMT |
| 165 | +A B C |
| 166 | +
|
| 167 | +$ODE |
| 168 | +dxdt_A = -0.04 * A + 1e4 * B * C; |
| 169 | +dxdt_B = 0.04 * A - 1e4 * B * C - 3e7 * pow(B, 2); |
| 170 | +dxdt_C = 3e7 * pow(B, 2); |
| 171 | +') |
| 172 | + |
| 173 | +# solve |
| 174 | +out <- mod %>% |
| 175 | + init(A = 1, B = 0, C = 0) %>% |
| 176 | + mrgsim(start = 0, end = 1, delta = 1e-2) |
| 177 | + |
| 178 | +plot(out) |
| 179 | +``` |
| 180 | + |
| 181 | + |
| 182 | + |
| 183 | +## Author's notes |
| 184 | + |
| 185 | +The goal of this review was to provide **a maximally complete and objective overview** of tools for solving ODEs in R. The packages included in this survey differ significantly in their purpose and functionality: from simple numerical solvers to full-featured frameworks and specialized domain-specific tools. They are presented here on equal footing, without going into detailed feature comparisons. |
| 186 | + |
| 187 | +Many aspects - such as computational performance, numerical accuracy, and advanced functionality - are intentionally not covered in this article. |
| 188 | + |
| 189 | +The table includes popularity metrics in the form of download counts. However, these numbers do not reflect the actual capabilities of the packages. Downloads may include one-time installations for educational purposes, CI/CD workflows, or other non-analytical uses. Therefore, they should not be considered a deciding factor when choosing a tool, but rather as a rough indicator of visibility within the community. |
| 190 | + |
| 191 | +Below is a subjective selection of packages that I would recommend paying attention to. |
| 192 | + |
| 193 | +#### General-purpose solution: deSolve |
| 194 | + |
| 195 | +[deSolve](https://cran.r-project.org/package=deSolve) is a robust and well-established package with broad functionality and support for multiple numerical methods. It provides advanced capabilities such as handling stiffness, DAEs, DDEs, and events, while maintaining good computational performance through compiled solvers and model interfaces. It also offers flexible ways to define models. |
| 196 | + |
| 197 | +With a large user base and extensive documentation, it is a reliable default choice. |
| 198 | + |
| 199 | +I would recommend it as a general-purpose tool for most ODE tasks in R. If you are new to ODE modeling in R, starting with deSolve is a safe and practical choice before exploring more specialized tools. |
| 200 | + |
| 201 | +#### Domain-specific tool: rxode2 |
| 202 | + |
| 203 | +[rxode2](https://cran.r-project.org/package=rxode2) is a powerful tool designed for pharmacokinetics and pharmacodynamics (PK/PD). |
| 204 | + |
| 205 | +Together with the [nlmixr2](https://nlmixr2.org/) toolkit, it extends beyond ODE solving to include parameter estimation from data and efficient Monte Carlo simulations in parallel and distributed environments. |
| 206 | + |
| 207 | +If your work is related to PK/PD modeling, this can be an excellent choice. |
| 208 | + |
| 209 | +#### Underappreciated tool: dMod |
| 210 | + |
| 211 | +[dMod](https://cran.r-project.org/package=dMod) provides a powerful framework for dynamic modeling, parameter estimation, and identifiability analysis. |
| 212 | + |
| 213 | +It combines symbolic model definition with efficient numerical solvers and supports gradient-based optimization workflows. While it has a steeper learning curve compared to simpler solvers, it offers a high level of flexibility and is particularly useful for more advanced modeling tasks. |
| 214 | + |
| 215 | +Despite its capabilities, it appears to be less widely used, making it an interesting but often overlooked option. |
| 216 | + |
| 217 | +#### Bridge to high-performance: diffeqr |
| 218 | + |
| 219 | +[diffeqr](https://cran.r-project.org/package=diffeqr) provides an interface to the Julia-based DifferentialEquations.jl ecosystem, exposing a large collection of state-of-the-art solvers directly in R. |
| 220 | + |
| 221 | +Rather than implementing its own numerical methods, it delegates computation to Julia, allowing access to advanced algorithms, GPU acceleration, and high-performance execution that are often beyond native R tools. |
0 commit comments