<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Evgeny Metelkin - Featured posts</title>
  <subtitle>Personal website of Evgeny Metelkin, a Computational Biologist and Systems Pharmacology Architect</subtitle>
  <link href="https://metelkin.me/feed/featured.xml" rel="self" />
  <link href="https://metelkin.me/" />
  <updated>2026-05-04T00:00:00Z</updated>
  <id>https://metelkin.me/</id>
  <author>
    <name>Evgeny Metelkin</name>
  </author>
  <entry>
    <title>Model Formats in Systems Pharmacology. Part 1</title>
    <link href="https://metelkin.me/model-formats-for-systems-pharmacology-1/" />
    <updated>2025-08-22T00:00:00Z</updated>
    <id>https://metelkin.me/model-formats-for-systems-pharmacology-1/</id>
    <content type="html">&lt;h4&gt;Part 1, &lt;a href=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-2/&quot;&gt;Part 2&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-1/img/fig0-cover.jpg&quot; alt=&quot;Cover&quot;&gt;&lt;/p&gt;
&lt;h2&gt;1. Intro&lt;/h2&gt;
&lt;p&gt;Imagine &lt;strong&gt;web development&lt;/strong&gt; where every framework has its &lt;strong&gt;own version of HTML&lt;/strong&gt;, CSS, and JavaScript. &lt;strong&gt;Git is almost useless&lt;/strong&gt;: the project is a mix of binary files and settings hidden in a GUI. &lt;strong&gt;Code can&#39;t be reused&lt;/strong&gt;: each tool has its own syntax, its own logic, and a closed project file.&lt;/p&gt;
&lt;p&gt;Sounds like a bad alternate reality, but this is still how &lt;strong&gt;data storage and exchange&lt;/strong&gt; often look in &lt;strong&gt;drug modeling&lt;/strong&gt;. Tools solve similar problems, but &lt;strong&gt;model formats&lt;/strong&gt; are &lt;strong&gt;incompatible&lt;/strong&gt;; project structure is a &amp;quot;black box&amp;quot;; &lt;strong&gt;reproducibility is fragile&lt;/strong&gt;; &lt;strong&gt;exchange is painful&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In this article, we propose looking at a &lt;strong&gt;pharmacological model as code&lt;/strong&gt;, and at the model format as an interface between people and tools.&lt;/p&gt;
&lt;p&gt;We&#39;ll explore:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what &lt;strong&gt;QSP&lt;/strong&gt; is and the role &lt;strong&gt;modeling&lt;/strong&gt; plays in &lt;strong&gt;pharmacology&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;what makes up a QSP model and how &lt;strong&gt;mathematics&lt;/strong&gt; turns into &lt;strong&gt;structure&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;the main approaches to &lt;strong&gt;model description&lt;/strong&gt; (ODE scripts, process-based DSLs, tables, visual editors);&lt;/li&gt;
&lt;li&gt;how popular tools &lt;strong&gt;store projects&lt;/strong&gt; and where &lt;strong&gt;collaboration&lt;/strong&gt; breaks down;&lt;/li&gt;
&lt;li&gt;which software &lt;strong&gt;engineering practices&lt;/strong&gt; (layered architecture, testing, CI/CD, semantic diffs) can actually work in QSP;&lt;/li&gt;
&lt;li&gt;how we can improve the situation with &lt;strong&gt;model formats&lt;/strong&gt; in QSP.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. Why QSP Matters&lt;/h2&gt;
&lt;p&gt;Today, &lt;strong&gt;computational biology&lt;/strong&gt; and &lt;strong&gt;mathematical modeling&lt;/strong&gt; are essential in &lt;strong&gt;drug development&lt;/strong&gt; - from early &lt;strong&gt;preclinical experiments&lt;/strong&gt; in a test tube to full-scale &lt;strong&gt;clinical trials&lt;/strong&gt; in humans. Major &lt;strong&gt;pharmaceutical companies&lt;/strong&gt; are investing heavily in this field, hiring top experts in computational biology to streamline the process and make it more effective (&lt;a href=&quot;https://doi.org/10.1007/s10928-024-09905-y&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Industry Perspective, JPKPD, 2024&lt;/a&gt;; &lt;a href=&quot;https://doi.org/10.1124/jpet.123.001842&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Promotional Submission of QSP, JPET, 2024&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;One of the fastest-growing areas is &lt;strong&gt;Quantitative Systems Pharmacology (QSP)&lt;/strong&gt;. In short, QSP builds detailed mathematical models that describe how &lt;strong&gt;drugs&lt;/strong&gt; interact with the &lt;strong&gt;human body&lt;/strong&gt; and how biological systems respond in return (&lt;a href=&quot;https://customsitesmedia.usc.edu/wp-content/uploads/sites/106/2012/12/17062522/NIH-White-Papaer-2011.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;NIH QSP White Paper, 2011&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Think of the human body as a &lt;strong&gt;complex engineered system&lt;/strong&gt; - full of components, feedback loops, and regulators. QSP turns this system into math: models that allow pharmacologists, engineers, and statisticians to test drug behavior &lt;strong&gt;virtually&lt;/strong&gt; before moving to costly and risky experiments in the lab or clinic. Much like an &lt;strong&gt;aerospace engineer&lt;/strong&gt; tests a new airplane in a simulator long before the &lt;strong&gt;first real flight&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;How QSP Relates to Other Modeling Fields&lt;/h3&gt;
&lt;p&gt;Long before QSP became a term, &lt;strong&gt;modelers&lt;/strong&gt; were already working with related approaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Systems Biology (SB)&lt;/strong&gt; - models networks of &lt;strong&gt;molecules and cells&lt;/strong&gt; (typically non-related to drugs or clinical endpoints).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Physiologically Based Pharmacokinetics (PBPK)&lt;/strong&gt; - models how drugs move through &lt;strong&gt;organs and tissues&lt;/strong&gt;, and eliminates from the &lt;strong&gt;body&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pharmacokinetics/Pharmacodynamics (PK/PD)&lt;/strong&gt; - links drug concentration to its &lt;strong&gt;therapeutic effect&lt;/strong&gt;, typically &lt;strong&gt;empirically&lt;/strong&gt; or by compartmental approach.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;QSP didn&#39;t appear out of nowhere - it grew out of all three. But it raised the level of detail, scaled up from molecules to &lt;strong&gt;whole-body systems&lt;/strong&gt;, and aimed to bridge the gap between mechanism and clinic.&lt;/p&gt;
&lt;h3&gt;Why Software Engineers Should Care&lt;/h3&gt;
&lt;p&gt;From a &lt;strong&gt;developer&#39;s perspective&lt;/strong&gt;, QSP projects look surprisingly familiar:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;there is &lt;strong&gt;code&lt;/strong&gt; (equations, parameters, model structure),&lt;/li&gt;
&lt;li&gt;there are &lt;strong&gt;tests&lt;/strong&gt; (validation against clinical or experimental data),&lt;/li&gt;
&lt;li&gt;there are &lt;strong&gt;versions&lt;/strong&gt; (models evolve as new data arrives),&lt;/li&gt;
&lt;li&gt;there are &lt;strong&gt;teams&lt;/strong&gt; working together on the same project,&lt;/li&gt;
&lt;li&gt;and there are real concerns about &lt;strong&gt;readability&lt;/strong&gt;, &lt;strong&gt;reproducibility&lt;/strong&gt;, and &lt;strong&gt;maintainability&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Drug modeling&lt;/strong&gt; is starting to resemble &lt;strong&gt;software engineering&lt;/strong&gt; but without the benefit of decades of best practices. That&#39;s why ideas like &lt;strong&gt;modularity&lt;/strong&gt;, &lt;strong&gt;version control&lt;/strong&gt;, &lt;strong&gt;open formats&lt;/strong&gt;, and &lt;strong&gt;CI/CD&lt;/strong&gt; are so relevant here.&lt;/p&gt;
&lt;h3&gt;Current Challenges&lt;/h3&gt;
&lt;p&gt;For all its &lt;strong&gt;promise&lt;/strong&gt;, QSP is still far from smooth sailing. In practice, many QSP teams still run into &lt;strong&gt;systemic challenges&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Teamwork is hard.&lt;/strong&gt; Models are often locked inside proprietary tools, making collaboration feel more like passing around &lt;strong&gt;black boxes&lt;/strong&gt; than working on shared code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interpretation is tricky.&lt;/strong&gt; Results can be meaningful to insiders, but remain &lt;strong&gt;opaque&lt;/strong&gt; to biologists, clinicians, or &lt;strong&gt;decision-makers&lt;/strong&gt; who actually need to use them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reproducibility is fragile.&lt;/strong&gt; The same model may give slightly &lt;strong&gt;different results&lt;/strong&gt; depending on the tool, environment, or even the person running it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Standards are missing.&lt;/strong&gt; Each group develops its own &lt;strong&gt;formats and workflows&lt;/strong&gt;, so models &lt;strong&gt;don&#39;t travel&lt;/strong&gt; well between teams or platforms.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintenance is painful.&lt;/strong&gt; Every new dataset or scientific insight often means heavy, &lt;strong&gt;manual re-work&lt;/strong&gt; of existing models.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In short, QSP has the ambition of being an &lt;strong&gt;&amp;quot;engineering discipline for pharmacology&amp;quot;&lt;/strong&gt;, but today it still operates with the patchiness and friction of a young science.&lt;/p&gt;
&lt;h2&gt;3. Anatomy of QSP models&lt;/h2&gt;
&lt;p&gt;In Quantitative Systems Pharmacology, the body is usually represented as a &lt;strong&gt;network of reactions&lt;/strong&gt; and interactions across scales—from organs to cells to molecules. Regardless of the level of detail, most models can be broken down into a few common &lt;strong&gt;building blocks&lt;/strong&gt;. If we borrow concepts from software engineering, the parallels look like this:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Modeling&lt;/th&gt;
&lt;th&gt;Software analogy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;States&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Molecule concentrations, cell counts, organ volumes, biomarkers&lt;/td&gt;
&lt;td&gt;Domain states, stored fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Processes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reactions, transport mechanisms, discrete events&lt;/td&gt;
&lt;td&gt;Business logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Parameters&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Organ sizes, blood flows, interaction constants, conditions&lt;/td&gt;
&lt;td&gt;Configuration settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Equations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rate laws, transport equations, empirical rules&lt;/td&gt;
&lt;td&gt;Algorithms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Solver&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ODE integrator running the system dynamics&lt;/td&gt;
&lt;td&gt;Runtime / execution engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Datasets&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Experimental, clinical, literature data&lt;/td&gt;
&lt;td&gt;Test data, fixtures, databases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tasks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dose optimization, prediction, parameter estimation, validation&lt;/td&gt;
&lt;td&gt;Use cases, automated tests&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This mapping isn&#39;t perfect, but it highlights a useful perspective: QSP models aren&#39;t just collections of equations—they&#39;re systems with states, logic, data, and tasks.&lt;/p&gt;
&lt;h3&gt;A Simple Example&lt;/h3&gt;
&lt;p&gt;To make this more concrete, let&#39;s look at an example - far smaller than what modelers deal with in practice, but enough to illustrate the &lt;strong&gt;key principles&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-1/img/fig1-scheme.png&quot; alt=&quot;Alcohol metabolism model&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fig 1. A toy QSP model of alcohol metabolism.&lt;/strong&gt; This minimal example is used to illustrate model structure; real QSP models can include thousands of components and interactions.&lt;/p&gt;
&lt;p&gt;In this example, ethanol is consumed twice (state &lt;strong&gt;&lt;em&gt;Alc_g&lt;/em&gt;&lt;/strong&gt;), absorbed into the bloodstream (&lt;strong&gt;&lt;em&gt;Alc_b&lt;/em&gt;&lt;/strong&gt;), metabolized into acetaldehyde (&lt;strong&gt;&lt;em&gt;AcHc&lt;/em&gt;&lt;/strong&gt;), and then further converted into acetate (&lt;strong&gt;&lt;em&gt;Acet&lt;/em&gt;&lt;/strong&gt;).&lt;/p&gt;
&lt;p&gt;In ODE form, the model&#39;s core dynamics are:&lt;br&gt;
$$&lt;br&gt;
&#92;begin{align}&lt;br&gt;
&#92;frac{d(Alc_g)}{dt} &amp;amp; = -vabs_{Alc},&#92;&#92;&lt;br&gt;
&#92;frac{d(Alc_b &#92;cdot blood)}{dt} &amp;amp; = vabs_{Alc} - v_{ADH},&#92;&#92;&lt;br&gt;
&#92;frac{d(AcHc &#92;cdot blood)}{dt} &amp;amp; = v_{ADH} - v_{ALDH}.&lt;br&gt;
&#92;end{align}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;where&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;$$&lt;br&gt;
&#92;begin{aligned}&lt;br&gt;
vabs_{Alc} &amp;amp; = kabs_{Alc} &#92;cdot Alc_g,&#92;&#92;&lt;br&gt;
v_{ADH} &amp;amp; =  &#92;frac{Vmax_{ADH} &#92;cdot Alc_b}{Km_{ADH} + Alc_b} &#92;cdot blood,&#92;&#92;&lt;br&gt;
v_{ALDH} &amp;amp; = &#92;frac{Vmax_{ALDH} &#92;cdot AcHc}{Km_{ALDH} + AcHc} &#92;cdot blood.&lt;br&gt;
&#92;end{aligned}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-1/img/fig2-dynamics.png&quot; alt=&quot;Alcohol metabolism simulation results&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fig 2. Demo run of the toy alcohol model.&lt;/strong&gt; Results are illustrative and qualitative, not quantitative. Definitely not medical advice (or bartending advice).&lt;/p&gt;
&lt;h3&gt;Beyond the Core Equations&lt;/h3&gt;
&lt;p&gt;In &lt;strong&gt;real-world projects&lt;/strong&gt;, the mathematical core is important but just the part of the model. A complete, reproducible modeling package also needs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Annotations&lt;/strong&gt; - units, explanations, and key assumptions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parameter sets &amp;amp; datasets&lt;/strong&gt; - conditions, doses, patient characteristics, and data for calibration and validation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Solver settings &amp;amp; metadata&lt;/strong&gt; - numerical settings and project history.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without these and other &lt;strong&gt;metadata&lt;/strong&gt;, results are not reproducible, and the model cannot be properly evaluated or reused. Here, we&#39;ll keep our focus on the core structure of models rather than all the surrounding layers.&lt;/p&gt;
&lt;h2&gt;4. Popular Tools and How They Store Models&lt;/h2&gt;
&lt;p&gt;At their core, QSP models are systems of algebraic and differential equations. But the moment we try to implement such a model on a computer, things get trickier. Equations need to be expressed in a very specific way - as functions, arguments, and expressions tailored for a particular programming language or solver.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-1/img/fig3-raw-matlab.png&quot; alt=&quot;Raw scripting Matlab code&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fig 3. Example of raw scripting in Matlab.&lt;/strong&gt; Model logic (right) and simulation script (left) live in separate files but are still entangled. The model carries redundant syntax just to satisfy MATLAB and cannot be directly reused in another language or tool. Updating the model code may result in changes in the simulation script.&lt;/p&gt;
&lt;p&gt;To make life easier, many tools introduce their own &lt;strong&gt;Domain Specific Languages (DSL)&lt;/strong&gt; or rely on &lt;strong&gt;macros&lt;/strong&gt;. In essence, we&#39;re still writing ODEs, but now they are expressed in a more compact, structured form. This makes the model easier to read and sometimes separates the model description from the execution code - a small but important step toward better clarity and maintainability.&lt;/p&gt;
&lt;p&gt;However, this approach still has its drawbacks. As models grow larger, ODE-based code becomes &lt;strong&gt;difficult to maintain&lt;/strong&gt;: it resists modularization, and even small changes may require edits in multiple places or restructuring the equations themselves. On top of that, the notation is far from intuitive for biologists or pharmacologists, who tend to think in terms of reactions, metabolites, and pathways rather than ODEs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-1/img/fig4-mrgsolve.png&quot; alt=&quot;ODE based DSL, mrgsolve&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fig 4. Example of ODE based DSL in mrgsolve.&lt;/strong&gt; The model is expressed as a macro-like C++ dialect, which makes it close to the solver and mathematically transparent. The syntax is tool-specific and difficult to parse or convert, limiting portability beyond mrgsolve (and partially NONMEM).&lt;/p&gt;
&lt;p&gt;One way around this is the &lt;strong&gt;process-based approach&lt;/strong&gt; describing the model in terms of processes that involve metabolites, reactions, compartments and other entities. The software then &lt;strong&gt;generates the ODE&lt;/strong&gt; system automatically (e.g., from tables or a custom DSL) right before simulation. This approach reduces manual edits, enables modularity, and lowers the risk of mistakes.&lt;br&gt;
The trade-off? You need to adjust your modeling mindset. The equation-level view is hidden behind generation, and you need an explicit build pipeline. Still, this method has been influential &lt;a href=&quot;https://sbml.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SBML&lt;/a&gt; is one example born from such thinking.&lt;/p&gt;
&lt;p&gt;To make models even more approachable, some tools offer &lt;strong&gt;visual modeling&lt;/strong&gt; showing the model as a map or process graph. Great for accessibility, less so for large-scale version control.&lt;/p&gt;
&lt;p&gt;In practice, different tools offer different interaction styles. For small models, it doesn&#39;t matter much which you choose. As complexity grows, the differences become critical-impacting &lt;strong&gt;usability, versioning, and collaboration&lt;/strong&gt;. For example, comparing two versions, reusing components, or organizing a modular model is often much easier in a process-based DSL than in raw ODE code.&lt;/p&gt;
&lt;p&gt;Broadly, user–model interaction falls into these categories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Raw scripting&lt;/strong&gt; - Pure code in a &lt;strong&gt;general-purpose language&lt;/strong&gt; (MATLAB, Julia, R, Python). Maximum flexibility, minimal standardization. Equations are coded directly; solvers may also be custom-built.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visual modeling&lt;/strong&gt; - The user &lt;strong&gt;draws diagrams&lt;/strong&gt;, with equations and parameters hidden in annotations (e.g., SimBiology, JDesigner). Great for visualization, poor for Git diffs and mass editing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DSL-based modeling (ODE- or process-based)&lt;/strong&gt; - A dedicated &lt;strong&gt;intermediate language&lt;/strong&gt;. Balances readability, structure, and flexibility (e.g., mrgsolve, Heta); plus separating the &lt;strong&gt;model layer&lt;/strong&gt; from the &lt;strong&gt;execution layer&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Table-based modeling&lt;/strong&gt; - The model is defined via &lt;strong&gt;series of spreadsheets&lt;/strong&gt;. The idea is similar to a DSL but avoids the need to memorize syntax. Readable, but limited in expressing complex logic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mixed modeling&lt;/strong&gt; - Combinations such as tables + DSL, or tables + diagrams, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-1/img/fig5-simbio.png&quot; alt=&quot;Visual modeling SimBiology code snippet&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fig 5. Example of visual modeling in SimBiology.&lt;/strong&gt; Visual diagrams make it easier for beginners and biologists to start modeling, but serious work still requires editing mathematical details hidden in tables and formulas. The binary storage format complicates version control and collaboration, while large models quickly become cumbersome as diagrams grow too complex to manage effectively.&lt;/p&gt;
&lt;h3&gt;What Ends Up on Disk: Project Storage Formats&lt;/h3&gt;
&lt;p&gt;Beyond the way models are written (authoring approach), &lt;strong&gt;storage format&lt;/strong&gt; matters, especially for collaboration, exchange, and version control. Some platforms store everything in a single file; others use multiple files in different formats. In QSP, storage formats often differ across tools and are rarely interoperable.&lt;/p&gt;
&lt;p&gt;Broad storage format categories for QSP:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Binary formats&lt;/strong&gt; - Not human-readable, hard to diff.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Proprietary text formats&lt;/strong&gt; - Can be opened in a text editor, but structure is obscure and not meant for manual editing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Structured formats&lt;/strong&gt; - Based on open standards (XML, SBML, JSON, YAML). Easier to parse and transform.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Human-readable text&lt;/strong&gt; - Best for Git and team workflows, but still needs a parser.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;strong&gt;way you write a model&lt;/strong&gt; affects how easy it is to work with, how accessible it is for collaborators, and how reusable the code becomes. The &lt;strong&gt;way you store&lt;/strong&gt; a project determines whether it is Git-friendly, easy to compare across versions, and possible to translate into other tools and formats. Both layers matter — and problems often come from confusing or conflating the two.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-1/img/fig6-heta.png&quot; alt=&quot;HetaSimulator code snippet&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fig 6. Example for process-based DSL. Code for HetaSimulator.&lt;/strong&gt; Heta offers a solver- and scripting-independent way to describe models and works well with version control. Support modules and namespaces. However, the format is niche, less familiar to those used to ODEs, and may feel harder to adopt outside its community. Requires a specific &lt;a href=&quot;https://hetalang.github.io/hetacompiler&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;translator&lt;/a&gt; for use across applications.&lt;/p&gt;
&lt;h3&gt;Tool Matrix: Authoring, Storage, and Interfaces&lt;/h3&gt;
&lt;p&gt;QSP has been steadily evolving since &lt;strong&gt;around 2010&lt;/strong&gt;. Many of the tools used today originated in neighboring fields such as Systems Biology, PBPK, and PK/PD. Some of them were &lt;strong&gt;adapted&lt;/strong&gt; to serve QSP needs, while others were &lt;strong&gt;developed&lt;/strong&gt; from scratch.&lt;/p&gt;
&lt;p&gt;The table below summarizes key tools in the QSP landscape. Tools are included based on three criteria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mentioned&lt;/strong&gt; in QSP software reviews (i.e. &lt;a href=&quot;https://doi.org/10.1002/psp4.12373&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;QSP tools review, CPT-PSP, 2018&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Positioned&lt;/strong&gt; as QSP tools in docs or case studies&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Designed&lt;/strong&gt; for solving dynamics, not just auxiliary tasks&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Software&lt;/th&gt;
&lt;th&gt;Interaction mode&lt;/th&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Model/project file format&lt;/th&gt;
&lt;th&gt;Interface&lt;/th&gt;
&lt;th&gt;Initial scope&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.mathworks.com/help/simbio/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SimBiology&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Visual, Tables&lt;/td&gt;
&lt;td&gt;Process-based&lt;/td&gt;
&lt;td&gt;Binary (.sbproj)&lt;/td&gt;
&lt;td&gt;GUI, Scripting (Matlab)&lt;/td&gt;
&lt;td&gt;SB, QSP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw MATLAB&lt;/td&gt;
&lt;td&gt;Raw scripting&lt;/td&gt;
&lt;td&gt;ODE-based&lt;/td&gt;
&lt;td&gt;Human-readable (.m)&lt;/td&gt;
&lt;td&gt;Scripting (Matlab)&lt;/td&gt;
&lt;td&gt;General purpose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://hetalang.github.io&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;HetaSimulator&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;DSL, Tables&lt;/td&gt;
&lt;td&gt;Process-based&lt;/td&gt;
&lt;td&gt;Human-readable (.heta)&lt;/td&gt;
&lt;td&gt;Scripting (Julia)&lt;/td&gt;
&lt;td&gt;QSP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://docs.pumas.ai/stable/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Pumas&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;DSL&lt;/td&gt;
&lt;td&gt;ODE-based&lt;/td&gt;
&lt;td&gt;Human-readable (.jl)&lt;/td&gt;
&lt;td&gt;Scripting (Julia)&lt;/td&gt;
&lt;td&gt;PK/PD, QSP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.open-systems-pharmacology.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PK-Sim/MoBi&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Tables, Visual&lt;/td&gt;
&lt;td&gt;ODE-based&lt;/td&gt;
&lt;td&gt;Structured (.xml, .pkml)&lt;/td&gt;
&lt;td&gt;Scripting (R)&lt;/td&gt;
&lt;td&gt;PBPK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://iqrtools.intiquan.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;IQR Tools&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;DSL&lt;/td&gt;
&lt;td&gt;ODE-based&lt;/td&gt;
&lt;td&gt;Human-readable (.txt)&lt;/td&gt;
&lt;td&gt;Scripting (R)&lt;/td&gt;
&lt;td&gt;PK/PD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://mrgsolve.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mrgsolve&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;DSL&lt;/td&gt;
&lt;td&gt;ODE-based&lt;/td&gt;
&lt;td&gt;Human-readable (.mod)&lt;/td&gt;
&lt;td&gt;Scripting (R)&lt;/td&gt;
&lt;td&gt;PK/PD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://sourceforge.net/projects/dbsolve/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;DBSolve&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Mixed: DSL + Tables&lt;/td&gt;
&lt;td&gt;ODE-based&lt;/td&gt;
&lt;td&gt;Proprietary text (.slv)&lt;/td&gt;
&lt;td&gt;GUI&lt;/td&gt;
&lt;td&gt;SB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.berkeleymadonna.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Berkeley Madonna&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Mixed: DSL + Tables&lt;/td&gt;
&lt;td&gt;ODE-based, Process-based&lt;/td&gt;
&lt;td&gt;Proprietary text (.mmd)&lt;/td&gt;
&lt;td&gt;GUI&lt;/td&gt;
&lt;td&gt;General purpose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.certara.com/software/simcyp-pbpk/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SimCYP&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Tables&lt;/td&gt;
&lt;td&gt;ODE-based (*restricted)&lt;/td&gt;
&lt;td&gt;Proprietary text (.wksz)&lt;/td&gt;
&lt;td&gt;GUI, Scripting (R)&lt;/td&gt;
&lt;td&gt;PBPK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.simulations-plus.com/software/gastroplus/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;GastroPlus&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Tables&lt;/td&gt;
&lt;td&gt;ODE-based (*restricted)&lt;/td&gt;
&lt;td&gt;Binary (.gpj)&lt;/td&gt;
&lt;td&gt;GUI&lt;/td&gt;
&lt;td&gt;PBPK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://monolixsuite.slp-software.com/monolix/2024R1/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Monolix&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Mixed: DSL, Tables&lt;/td&gt;
&lt;td&gt;ODE-based&lt;/td&gt;
&lt;td&gt;Human-readable (.mlxtran, .txt)&lt;/td&gt;
&lt;td&gt;GUI, Scripting (R, Python)&lt;/td&gt;
&lt;td&gt;PK/PD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://www.iconplc.com/solutions/technologies/nonmem&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;NONMEM&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Mixed: DSL, Tables&lt;/td&gt;
&lt;td&gt;ODE-based&lt;/td&gt;
&lt;td&gt;Human-readable (.ctl, .mod)&lt;/td&gt;
&lt;td&gt;CLI, Scripting (PsN/Pirana)&lt;/td&gt;
&lt;td&gt;PK/PD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://jdesigner.sourceforge.net/Site/JDesigner.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;JDesigner&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Visual&lt;/td&gt;
&lt;td&gt;Process-based&lt;/td&gt;
&lt;td&gt;Binary (.jdes)&lt;/td&gt;
&lt;td&gt;GUI&lt;/td&gt;
&lt;td&gt;SB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;* restricted - limited access to the ODE structure; pre-generated for drug distribution models.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;What this comparison actually tells us? Looking across tools, a few patterns stand out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Interoperability gap.&lt;/strong&gt; Most ecosystems define their own project and model formats; moving between them usually means partial &lt;strong&gt;rewrites or lossy conversions&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authoring ≠ storage.&lt;/strong&gt; A convenient way to write a model (visual editor, macro DSL) doesn&#39;t guarantee a format that&#39;s easy to diff, review, or translate. Many tools couple authoring and storage tightly, which &lt;strong&gt;locks projects&lt;/strong&gt; to a runtime.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Opacity hurts teamwork.&lt;/strong&gt; Binary or proprietary text bundles are hard to compare, branch, and merge; they don&#39;t play well with &lt;strong&gt;Git, code review, or CI&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scaling pain&lt;/strong&gt;. ODE-centric code &lt;strong&gt;scales poorly&lt;/strong&gt;: small structural tweaks (e.g., making a volume dynamic) ripple across many equations. Visual maps don&#39;t scale either—large diagrams become navigation mazes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Skill barrier and mental models.&lt;/strong&gt; Biologists think in terms of reactions, metabolites, pathways; engineers and solvers consume equations. Many tools &lt;strong&gt;force one side&lt;/strong&gt; to work in the other side&#39;s mental model.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For teams, this creates a mix of problems: onboarding and &lt;strong&gt;knowledge transfer become slower&lt;/strong&gt;, &lt;strong&gt;reproducibility suffers&lt;/strong&gt; when parts of the logic are hidden in GUIs or project files, every new dataset or mechanism &lt;strong&gt;increases the cost of changes&lt;/strong&gt;, and projects gradually become &lt;strong&gt;locked into&lt;/strong&gt; a single vendor&#39;s toolset.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;bigger issue&lt;/strong&gt; isn’t the formats themselves, but the &lt;strong&gt;isolation of the communities&lt;/strong&gt; that use them. QSP modeling is already demanding: you need biology, math, data, statistics. Few people have the energy to master more than one tool, so most pick one and stay there. &lt;strong&gt;The circle closes&lt;/strong&gt;: groups can&#39;t share because formats don&#39;t match, and formats don&#39;t match because each tool is tuned to its own little world. The way out doesn&#39;t have to come only from research groups or pharma companies—software &lt;strong&gt;developers can play a big role&lt;/strong&gt; in breaking these walls too.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Next: &lt;a href=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-2/&quot;&gt;Part 2: Engineering Practices We Can Borrow&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Model Formats in Systems Pharmacology. Part 2</title>
    <link href="https://metelkin.me/model-formats-for-systems-pharmacology-2/" />
    <updated>2025-09-06T00:00:00Z</updated>
    <id>https://metelkin.me/model-formats-for-systems-pharmacology-2/</id>
    <content type="html">&lt;h4&gt;&lt;a href=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-1/&quot;&gt;Part 1&lt;/a&gt;, Part 2&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-2/img/fig0-cover.jpg&quot; alt=&quot;Cover&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;In &lt;a href=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-1/&quot;&gt;Part 1&lt;/a&gt;, we looked at the landscape of QSP model formats-their origins, strengths, and limitations. In this follow-up, I want to step back and explore the problem from a software engineering perspective: what practices and design principles could make QSP modeling more transparent, modular, and reproducible, and how model formats can evolve to support that shift.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;5. Model as code&lt;/h2&gt;
&lt;p&gt;In software, the &amp;quot;X as code&amp;quot; idea has proven itself many times over: &lt;strong&gt;Infrastructure as Code&lt;/strong&gt; (&lt;a href=&quot;https://developer.hashicorp.com/terraform/tutorials/aws-get-started/infrastructure-as-code&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Terraform&lt;/a&gt;), &lt;strong&gt;Configuration as Code&lt;/strong&gt; (&lt;a href=&quot;https://www.redhat.com/en/blog/ansible-automation-platform-2.3-configuration-as-code-improvements&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Ansible&lt;/a&gt;), and &lt;strong&gt;Pipeline as Code&lt;/strong&gt; (&lt;a href=&quot;https://www.jenkins.io/doc/book/pipeline/jenkinsfile/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Jenkins&lt;/a&gt;), among others. The core idea is simple: we don&#39;t just manage &lt;strong&gt;artifacts&lt;/strong&gt; (infrastructure, configuration, pipelines) directly, we manage their &lt;strong&gt;textual representation&lt;/strong&gt; under version control, even if those artifacts were never treated as &amp;quot;code&amp;quot; before. In all cases, the &lt;strong&gt;authoritative source&lt;/strong&gt; (canonical source for other forms) is human-readable text. This doesn&#39;t exclude working with diagrams or tables-authoring can stay visual or interactive, but the canonical format must still be code. This shift brought massive gains in transparency, modularity, and reproducibility, and accelerated progress in software engineering.&lt;/p&gt;
&lt;p&gt;If we look back at the popular formats discussed earlier, not all of them can be considered model as code. Some are well-suited; others are not. QSP formats that are stored in binary or tool-specific project files cannot be treated as code. Formats that are formally &amp;quot;text-based&amp;quot; but too complex or unstructured for a human to read or write also only partially fit this concept.&lt;/p&gt;
&lt;p&gt;When this principle is in place, modelers gain access to an entire ecosystem of tools: version control, diff and merge utilities, code completion, automated testing, and CI/CD pipelines. Nothing needs to be reinvented-these are the same practices that software engineers already use. It means versioned, testable, modular, and reusable models.&lt;/p&gt;
&lt;p&gt;In practice, a model becomes more than a loose collection of files: it turns into a full engineering project with a clear folder structure, documentation, and automation. This approach improves reproducibility, makes validation and review more systematic, and simplifies onboarding new team members.&lt;/p&gt;
&lt;h3&gt;Code Transparency&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Code transparency&lt;/strong&gt; means that every aspect of a model is visible, reviewable, and understandable &lt;strong&gt;at a glance&lt;/strong&gt;. This covers not only the model structure, but also the equations, parameters, and the assumptions embedded within it. A transparent format allows collaborators to see what exactly the model does without needing the original author or a proprietary tool to &amp;quot;explain&amp;quot; it.&lt;/p&gt;
&lt;p&gt;A second requirement is &lt;strong&gt;traceable change&lt;/strong&gt;. This is not only about being able to read the code, but also about being able to see what changed and why between versions. With text-based formats, differences are captured by standard tools (e.g., Git diff), making version control, peer review, and collaboration far more effective.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-2/img/fig1-nonmem.png&quot; alt=&quot;Traceable change for NONMEM file&quot;&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;Fig. 1. Traceable change for NONMEM model file.&lt;/strong&gt; Files comparison in VSCode: old vs. new. Can see changes clearly.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A desirable (not strictly mandatory) requirement is &lt;strong&gt;self-explanatory code&lt;/strong&gt;. This means that the model description carries enough context-through clear naming, annotations, and units-that a new reader can understand the intent without constantly referring back to external notes or publications.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-2/img/fig2-antimony.png&quot; alt=&quot;Self-explanatory code trace in Antimony modeling language&quot;&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;Fig. 2. Self-explanatory code trace in Antimony modeling language.&lt;/strong&gt; File comparison in VSCode: old vs. new. Model diff meaning easy to understand at a glance because of clear syntax.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;By contrast, QSP environments that store models in binary, closed, or otherwise non-readable formats cannot ensure transparency. They block the very practices-review, versioning, collaboration-that modern scientific software relies on. Even when dedicated comparison tools exist, they are typically ad-hoc, tied to a single platform, and rarely integrate smoothly into a team&#39;s normal project workflow. As a result, they are used sporadically and do not replace true text-level transparency.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-2/img/fig3-simbio.png&quot; alt=&quot;Non traceable change for SimBiology project&quot;&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;Fig. 3. Non traceable change for SimBiology project (.sbproj).&lt;/strong&gt; File comparison in VSCode: old vs. new. Model change cannot be tracked because of binary format.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Modularity&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Modularity&lt;/strong&gt; is the ability to divide a project into independent parts that can be developed, tested, and reused separately. It comes from having clear interfaces and well-defined dependencies between components.&lt;/p&gt;
&lt;p&gt;Here we use the term broadly. It includes both the &lt;strong&gt;separation of different project layers&lt;/strong&gt;: model, data, scripts (covered in more detail in the &amp;quot;Separation of Concerns&amp;quot; section), and the internal modularization of the model itself into subcomponents that are easier to manage and understand.&lt;/p&gt;
&lt;p&gt;What modularity brings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Code reuse.&lt;/strong&gt; Modules can be packaged and reused across projects or systems, avoiding copy-paste and enabling larger blocks to become shared dependencies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clarity and maintainability.&lt;/strong&gt; Smaller, self-contained pieces are easier to read, document, and hand over to new collaborators.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scalability.&lt;/strong&gt; Adding a new drug, pathway, or dataset means extending the project with a module rather than rewriting existing code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Experimentation and flexibility.&lt;/strong&gt; Alternative implementations (e.g., two different PK models) can be swapped or compared without touching the rest of the system.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The more logically a QSP platform is divided into modules, the easier it becomes to manage the work and distribute responsibilities. A single monolithic file forces the entire team to work sequentially, rather than in parallel.&lt;/p&gt;
&lt;h3&gt;Longevity&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Longevity&lt;/strong&gt; means that a model remains usable and trustworthy years after it was first created. Instead of &amp;quot;remembering which buttons we clicked,&amp;quot; the project can be rebuilt, rerun, and revalidated from its source (&lt;a href=&quot;https://dx.doi.org/10.1038/sdata.2016.18&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;FAIR Principles, Scientific Data, 2016&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-2/img/fig4-git.png&quot; alt=&quot;Git commit history in QSP project.&quot;&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;Fig. 4. Git commit history in QSP project.&lt;/strong&gt;: Commit history provides a timeline of changes in branches, enabling easy rollback and review.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;What makes longevity possible:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Version history.&lt;/strong&gt; With Git or similar systems, projects can be saved as a sequence of working snapshots. Each commit captures the state of parameters, equations, and scenarios at a given moment, creating a project timeline that can be revisited or rolled back when needed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Environment capture.&lt;/strong&gt; Pinned dependencies and solver settings (e.g., Project.toml, requirements.txt, renv.lock) ensure the same model can be executed in the future, regardless of local updates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Readable formats.&lt;/strong&gt; Text-based models outlive specific tools; even if a GUI disappears, the core code can still be read, parsed, and converted.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Re-validation.&lt;/strong&gt; As new data or regulatory requirements arise, archived models can be rerun and checked against updated knowledge.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Knowledge preservation.&lt;/strong&gt; Annotations, comments, and documentation carry context, so understanding does not depend on the original author&#39;s memory.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Regulatory and audit needs.&lt;/strong&gt; Long-term reproducibility is critical for submissions: teams must show exactly what model was used to support a decision at a given time (&lt;a href=&quot;https://dx.doi.org/10.1002/psp4.12049&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;MID3 Good Practices, CPT: PSP, 2016&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Longevity turns a QSP model from a one-off experiment into a &lt;strong&gt;sustainable scientific asset&lt;/strong&gt;, something that can be reliably shared, revisited, and built upon.&lt;/p&gt;
&lt;h3&gt;Automation&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Automation&lt;/strong&gt; means that models are executed and tested through scripts instead of manual clicks. Once a model is code, it can be integrated into pipelines that ensure consistent, repeatable runs for testing, simulations, parameter estimation, or sensitivity analyses.&lt;/p&gt;
&lt;p&gt;With &lt;strong&gt;CI/CD&lt;/strong&gt;, every commit can automatically launch validation tasks, generate reports, or even dispatch heavy computations to a more powerful server or cluster (&lt;a href=&quot;https://www.oreilly.com/library/view/continuous-delivery-reliable/9780321670250/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Continuous Delivery, Addison-Wesley, 2010&lt;/a&gt;). This reduces human error, scales effortlessly, and makes QSP projects more reliable and collaborative.&lt;/p&gt;
&lt;h3&gt;LLM &amp;amp; AI&lt;/h3&gt;
&lt;p&gt;When models are stored as readable code, they become accessible not only to people but also to &lt;strong&gt;AI assistants&lt;/strong&gt;. This opens new opportunities for collaboration between modelers and &lt;strong&gt;Large Language Models&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-2/img/fig5-heta-autocompletion.png&quot; alt=&quot;Autocompletion by GitHub Copilot for Heta&quot;&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;Fig.5. Autocompletion for Heta code.&lt;/strong&gt; GitHub Copilot can assist with code suggestions and completions if the model has unified and clear structure. Red arrow points to the AI generated suggestion.&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Assisted modeling.&lt;/strong&gt; Existing tools like GitHub Copilot or Cursor can already be used with QSP models stored as code-offering chat-based assistance, code generation, or autocompletion in Domain-Specific Language (DSL)/Macros/JSON, just as they do today for Python or R scripts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Report integration.&lt;/strong&gt; Because the model is code, the model updates and results can be automatically pulled into human-readable reports, presentations, or dashboards. LLMs can help generate narratives that explain changes, assumptions, or results based on the current model state.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Model as knowledge base.&lt;/strong&gt; A structured repository can serve as a living knowledge base for LLMs. By indexing models and metadata, teams can query them conversationally (&amp;quot;how is clearance modeled here?&amp;quot;) or connect them with external literature for reasoning.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automated generation.&lt;/strong&gt; Beyond code completion, LLMs could be used to generate entire model fragments or even full models from structured descriptions, project specifications, or natural-language prompts. For example, a developer might design a tool that takes a pathway description or a clinical protocol and produces a ready-to-run QSP model.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Used carefully--with validation, tests, and human review--AI support can turn QSP models into more &lt;strong&gt;interactive, transparent, and productive assets&lt;/strong&gt;, while preserving scientific rigor.&lt;/p&gt;
&lt;p&gt;By contrast, if the model exists only as a binary project and the GUI is the only interface, none of these benefits are available: assistants cannot read or suggest code, generation and reporting are manual, and the project remains a &lt;strong&gt;closed box&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;6. Separation of Concerns&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Separation of Concerns&lt;/strong&gt; is a principle borrowed from software engineering. Applied to QSP, it means treating each aspect of a project as its own &lt;strong&gt;layer with clear boundaries&lt;/strong&gt; and contracts. The model is not the data. The runtime is not the source. When these lines blur, projects become harder to read, validate, and reuse. When they are explicit, projects stay modular, and easier to evolve.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Layers (at a glance):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Model&lt;/strong&gt; - description of system, biological processes: structure, equations, annotation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scenarios&lt;/strong&gt; - experiment conditions: dosing, schedules, observables, simulation horizons, parameter variations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data&lt;/strong&gt; - experimental/clinical datasets, raw or statistical data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tasks&lt;/strong&gt; - computational actions (simulate/fit/sensitivity/identifiability) referencing Model/Scenarios/Data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Solver config &amp;amp; Environment&lt;/strong&gt; - numerical settings plus lockfiles/containers (library and OS versions).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Runtime/Engines&lt;/strong&gt; - applications, solvers and interfaces.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pipelines &amp;amp; Notebooks&lt;/strong&gt; - automation (CLI/CI/CD) and interactive analysis (Jupyter/Quarto).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Results/Reports&lt;/strong&gt; - outputs, logs, and report artifacts (generated by pipelines; not hand-edited).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docs/Metadata&lt;/strong&gt; - general annotations, assumptions, references, ontologies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is a fairly fine-grained breakdown for illustration. In real projects, separation can take different forms: dedicated folders, individual files, or clear structural sections inside one file in some cases. The key is that layers stay distinct, not mixed.&lt;/p&gt;
&lt;p&gt;Another point is that each layer carries &lt;strong&gt;its own logic and conventions&lt;/strong&gt;. A sensitivity-analysis script has very different requirements than a model structure. Automation may rely on technologies with its own style of configuration. Forcing everything into a monolithic structure only makes projects fragile.&lt;/p&gt;
&lt;p&gt;The idea is familiar from software engineering: web applications separate frontend, backend, and database; frameworks use MVC or MVVM patterns. QSP projects benefit from the same discipline: each layer does its job, connected by explicit interfaces rather than hidden assumptions.&lt;/p&gt;
&lt;h3&gt;Project Structure&lt;/h3&gt;
&lt;p&gt;A practical way to implement &amp;quot;Separation of Concerns&amp;quot; is through a &lt;strong&gt;clear and consistent project structure&lt;/strong&gt;. When each layer has its own dedicated place, the project becomes easier to navigate, test, and extend. In other words, the folder layout itself enforces modularity and discipline:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;qsp-project/
  model/          # core structure: states, reactions, equations
  data/           # measurements for calibration/validation
  scenarios/      # description of experimental conditions and parameters
  docs/           # General purpose annotations, assumptions for the whole project
  results/        # outputs, logs, reports (generated, not hand-edited)
  julia/          # Julia code for simulation and analysis
  R/              # R code for simulation and analysis
  project.yml     # machine-readable top-level metadata, dependencies, configuration
  README.txt      # human-readable overview and instructions
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Such a layout makes a QSP project behave like any other modern &lt;strong&gt;software repository&lt;/strong&gt;: modular, reviewable, and easy to share. The exact structure may vary depending on how well the project can be split into layers, which tools are in use, and the team&#39;s workflow. The key is not the specific folder names, but that the structure is &lt;strong&gt;consistent, agreed upon, and understood&lt;/strong&gt; by everyone on the team. This shared and long-term convention reduces friction, improves collaboration, and ensures that new members can quickly find their way around the project.&lt;/p&gt;
&lt;p&gt;These additional principles make project structures more robust:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Keep the repository clean.&lt;/strong&gt; The project folder should only contain what is essential: model code, data, scenarios, tasks, configuration, and documentation. Avoid bundling software installers, outdated model versions, or random drafts and unused scripts. Such clutter makes navigation harder and undermines clarity. Version control (Git) and artifact registries are the right place for history and distributions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Single source of truth.&lt;/strong&gt; Everything required to run or reproduce the project must either be part of the repository or referenced in a reliable, long-term way. No ambiguous dependencies on &amp;quot;files somewhere on a laptop&amp;quot; or &amp;quot;datasets hidden in a presentation.&amp;quot; If a script was written for the project, it should live in the project. If external data cannot be included directly, it must be referenced with a stable identifier or DOI and clear provenance. This ensures reproducibility and integrity over time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stable naming and references.&lt;/strong&gt; Avoid renaming files, folders, or key identifiers without a strong reason. Frequent or unnecessary renames break links, confuse collaborators, and make version history harder to follow. Stable names act as anchors for scripts, pipelines, and external references, ensuring continuity and trust in the project&#39;s structure.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Documentation as code.&lt;/strong&gt; Documentation should live alongside the project and evolve together with it. Assumptions, references, and instructions belong under version control, not in scattered slides or personal notes. Keeping docs close to the source makes them reviewable, traceable, and always in sync with the current state of the project.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Model Layout&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Why this matters.&lt;/strong&gt; Data scientists have long kept data in tabular, diff-friendly formats (CSV/TAB/Parquet) and code in versioned scripts (R/Julia/Matlab/Python). The persistent gap in QSP is the model layer: when a model has no canonical, human-readable source, you can&#39;t reliably diff or review changes, and you can&#39;t automate validation. Everything downstream-reproducibility, CI, and collaboration-degrades.&lt;/p&gt;
&lt;p&gt;&amp;quot;Model as code&amp;quot; principle treats the model as a &lt;strong&gt;declarative artifact&lt;/strong&gt; with its own lifecycle. The source of truth is text. GUIs, notebooks, and binaries are authoring or execution surfaces, not the canonical store. This simple rule unlocks code review, versioning, composition, and automation for the model layer exactly as it does for code and data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Contracts: how layers talk.&lt;/strong&gt; When the model is kept separate from the rest of the project, it needs a clear way for other layers to reference it. Scenarios, tasks, and datasets should point to model elements through explicit identifiers-states, parameters, events, or observables in namespaces-rather than by position, file order, or ad-hoc labels. These references must be validated automatically, with pipelines configured to fail fast if an ID is missing or renamed. Stability here does not mean immobility, but transparent, predictable evolution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Modularity at the model level.&lt;/strong&gt; A practical layout, if your tools support a DSL or modular model files, might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;model/
  core.dsl          # base states/params/eqs
  pk.dsl            # PK submodule (imports core)
  pd.dsl            # PD submodule
  cell-dynamics.dsl # cell dynamics submodule
  index.dsl         # top-level file that composes/exports modules
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When available, this approach has clear advantages: each file has a narrow, stable responsibility; imports and namespaces let you compose submodels cleanly; and a single &lt;strong&gt;index&lt;/strong&gt; provides the surface for downstream layers to import. This makes reuse straightforward, modules can be copied or version-pinned across repositories instead of duplicating whole projects. Even if your current environment doesn&#39;t provide such modularity, aiming for stable paths, explicit IDs, and a clear separation of responsibilities will bring similar benefits and reduce churn that breaks links or history.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tooling for the model layer.&lt;/strong&gt; The model layer benefits from having its own basic checks, just like data or code. At minimum, tools should confirm that equations are dimensionally consistent, that all references point to something that exists, and that every state or parameter is defined only once. It helps to run quick &amp;quot;sanity&amp;quot; simulations on each update to confirm the model still produces reproducible results. These lightweight checks make errors visible early and keep the model trustworthy as it grows.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Authoring &amp;amp; round-trip workflow.&lt;/strong&gt; You can build or edit a model wherever it&#39;s convenient, through a GUI, a notebook, or even a helper script. What matters is that the final result is always saved back into a clear, text-based format. That text is what goes under version control, what reviewers look at, and what other tools check for errors. This way modelers stay free to use familiar interfaces, while the project still keeps a consistent, transparent record.&lt;/p&gt;
&lt;h3&gt;Roles &amp;amp; Handoffs (who edits which layer)&lt;/h3&gt;
&lt;p&gt;Different specialists can work productively when boundaries are explicit.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Model Engineer (biology focused).&lt;/strong&gt; Owns Model + Scenarios. Delivers: validated model modules, annotations, units.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Biostatistician / Data Manager (data analysis focused).&lt;/strong&gt; Owns Data and results. Delivers: cleaned datasets with units and provenance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simulation Engineer (computational methods focused).&lt;/strong&gt; Owns Tasks + Runtime + Solver config. Delivers: task specs, algorithms, tolerances, acceptance criteria.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Runtime &amp;amp; DevOps Engineer (automation focused).&lt;/strong&gt; Owns Pipelines &amp;amp; Notebooks + Environment. Delivers: containers/lockfiles, solver configs, CI/CD configs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Depending on the project&#39;s goals and team size, some roles may be combined, have other titles, or be unnecessary (for example, a dedicated DevOps engineer may not be required). In other cases, additional roles can emerge, such as a technical/medical writer, visualization scientist, or a platform architect/manager. These specialists may work full time, contribute periodically or only at specific stages, but in all cases, a well-structured layered project plus clear conventions makes the whole work more effective.&lt;/p&gt;
&lt;h3&gt;Anti-patterns&lt;/h3&gt;
&lt;p&gt;In software engineering, &lt;strong&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_software_anti-patterns&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;anti-patterns&lt;/a&gt;&lt;/strong&gt; are recurring practices that may seem convenient but ultimately hinder maintainability, scalability, and collaboration. QSP projects face similar risks. Identifying such anti-patterns is important because it helps teams recognize fragile structures early, and avoid repeating common mistakes. Here is the examples of anti-patterns that often appear in QSP projects:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Classic Anti-Pattern&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Manifestation in QSP projects&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Why it&#39;s harmful&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Big Ball of Mud&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Entire model, data, protocols, tasks, and results stored in a single monolithic file or notebook.&lt;/td&gt;
&lt;td&gt;No modularity, hard to review, impossible to scale or work in parallel.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Swiss Army Knife&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One application tries to do everything-modeling, simulation, analysis, data management, and reporting-without clear separation of roles.&lt;/td&gt;
&lt;td&gt;Lacks flexibility, becomes overly complex, and prevents use of specialized tools.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Copy-and-Paste Programming&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Equations and parameters duplicated or hard-coded into scripts and notebooks instead of being managed in a structured model source.&lt;/td&gt;
&lt;td&gt;Leads to inconsistencies, errors, and maintenance headaches.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Magic Numbers / Hidden Dependencies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Defaults for model structure, doses, solver tolerances, or units applied implicitly in GUIs, with no audit trail or explicit configuration.&lt;/td&gt;
&lt;td&gt;Non-transparent, hard to reproduce, fragile under review.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stovepipe System&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A model format or tool originally designed for smaller systems or narrow use cases, stretched to large-scale systems without modularity or namespaces.&lt;/td&gt;
&lt;td&gt;Doesn&#39;t scale, encourages hacks, and blocks extension or reuse.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inner-Platform Effect&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Forcing all roles (modelers, data scientists, analysts) into a single rigid workflow, instead of allowing different business logics and affordances.&lt;/td&gt;
&lt;td&gt;Stifles productivity, mismatches workflows, and limits adaptability.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;7. Evaluating Your Workflow&lt;/h2&gt;
&lt;p&gt;A couple of checklists is a simple way how you can check how well your current tools and workflows align with the principles of &lt;strong&gt;&amp;quot;model as code&amp;quot;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Mark ☑ for each practice that is true for your current workflow.&lt;/p&gt;
&lt;h3&gt;Model-as-Code Maturity Checklist&lt;/h3&gt;
&lt;p&gt;This test is not about scoring &amp;quot;perfect&amp;quot; compliance. Instead, it highlights areas where improvements may bring the biggest benefits, such as versioning, automation, or separation of layers.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;☐ &lt;strong&gt;Text-based authoritative source.&lt;/strong&gt; Every part of the project can be expressed in a human-readable text format (DSL/Macros/JSON/YAML).&lt;/li&gt;
&lt;li&gt;☐ &lt;strong&gt;Model layer separation.&lt;/strong&gt; The model is stored separately from data, code, and results. Model files contain only model structure, equations, parameters, and annotations.&lt;/li&gt;
&lt;li&gt;☐ &lt;strong&gt;Extensibility.&lt;/strong&gt; You can add new components, pathways, drugs, and scripts without rewriting existing code. The time you spend on this is proportional to the size of updates, not exponential.&lt;/li&gt;
&lt;li&gt;☐ &lt;strong&gt;Annotated &amp;amp; self-explanatory.&lt;/strong&gt; Your model includes clear names, units, and comments that explain its structure and assumptions. Annotations are machine-readable and will be preserved across tools.&lt;/li&gt;
&lt;li&gt;☐ &lt;strong&gt;Tool-agnostic.&lt;/strong&gt; You can open, edit and execute the model and other project parts using more or one tool. Or at least export/import to/from other formats without loss of information.&lt;/li&gt;
&lt;li&gt;☐ &lt;strong&gt;Meaningful diffs.&lt;/strong&gt; When a file changes, you can review and understand the difference (both technically and semantically) easily for any part of the project.&lt;/li&gt;
&lt;li&gt;☐ &lt;strong&gt;Scriptable execution.&lt;/strong&gt; You are able to run and repeat simulations, fits, tests, and analyses from the command line or scripts, not only through a GUI.&lt;/li&gt;
&lt;li&gt;☐ &lt;strong&gt;Reproducible environment.&lt;/strong&gt; Dependencies, solver versions, and configuration are pinned (e.g., lockfiles, containers).&lt;/li&gt;
&lt;li&gt;☐ &lt;strong&gt;Clear structure.&lt;/strong&gt; The project follows a consistent folder/file layout that separates model, data, tasks, and results. All members of your project understand where to find and edit each part of the project.&lt;/li&gt;
&lt;li&gt;☐ &lt;strong&gt;Project conventions.&lt;/strong&gt; Your team has agreed on conventions for naming, versioning, and structuring the project. These conventions are documented and followed consistently.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Modern Practices Checklist&lt;/h3&gt;
&lt;p&gt;This checklist helps assess how much your project or workflow utilizes modern engineering practices.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;☐ &lt;strong&gt;Version control.&lt;/strong&gt; The project is under Version Control Tools (Git or equivalent), with meaningful commit history.&lt;/li&gt;
&lt;li&gt;☐ &lt;strong&gt;Continuous Integration &amp;amp; Delivery (CI/CD).&lt;/strong&gt; Substantial change triggers automated builds or checks (e.g., GitHub Actions, GitLab CI, Jenkins).&lt;/li&gt;
&lt;li&gt;☐ &lt;strong&gt;Automated reporting.&lt;/strong&gt; Reports, figures, and logs are generated directly from code and kept in sync with model versions.&lt;/li&gt;
&lt;li&gt;☐ &lt;strong&gt;Automated testing.&lt;/strong&gt; Unit or reproducibility tests verify correctness of model components and pipelines.&lt;/li&gt;
&lt;li&gt;☐ &lt;strong&gt;Automation of routine tasks.&lt;/strong&gt; Data preprocessing, parameter fitting are scripted, not manual. Routine operations can be autocompleted or assisted by AI tools.&lt;/li&gt;
&lt;li&gt;☐ &lt;strong&gt;Remote execution.&lt;/strong&gt; Workflows can seamlessly run on HPC clusters (like SLURM) or cloud resources when needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Previous: &lt;a href=&quot;https://metelkin.me/model-formats-for-systems-pharmacology-1/&quot;&gt;Part 1: The Missing Link Between Biology and Software Engineering&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Preventing System Sleep During Long Computations</title>
    <link href="https://metelkin.me/preventing-system-sleep-during-long-computations/" />
    <updated>2026-01-16T00:00:00Z</updated>
    <id>https://metelkin.me/preventing-system-sleep-during-long-computations/</id>
    <content type="html">&lt;p&gt;&lt;img src=&quot;https://metelkin.me/preventing-system-sleep-during-long-computations/img/fig0-cover.jpg&quot; alt=&quot;Cover&quot;&gt;&lt;/p&gt;
&lt;p&gt;In scientific and engineering modeling it is very common to run computations that take hours: overnight simulations, parameter fitting, Monte Carlo experiments, or long-running machine learning jobs. You start the script, make sure everything looks fine, and walk away.&lt;/p&gt;
&lt;p&gt;Then the operating system decides to go to sleep.&lt;/p&gt;
&lt;p&gt;When that happens, the computation is paused or effectively stopped. In the morning you come back to an unfinished run and lost time. This is not a rare edge case - it is a very common and very frustrating failure mode when working on laptops or workstations.&lt;/p&gt;
&lt;h2&gt;Why disabling sleep globally is not a great solution&lt;/h2&gt;
&lt;p&gt;There are obvious ways to deal with this problem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;disable sleep in system settings,&lt;/li&gt;
&lt;li&gt;run &lt;code&gt;caffeinate&lt;/code&gt; on macOS,&lt;/li&gt;
&lt;li&gt;enable an &amp;quot;awake&amp;quot; mode using system tools or third-party utilities.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These approaches work, but only at a very coarse level. They keep the system awake indefinitely and have no idea when your computation has actually finished. As a result, the machine may stay awake long after the job is done, wasting power and requiring manual cleanup.&lt;/p&gt;
&lt;p&gt;In practice, this often turns into a trade-off between reliability and convenience: either risk losing the computation, or remember to manually manage system sleep every time.&lt;/p&gt;
&lt;h2&gt;The problem is better solved at the script level&lt;/h2&gt;
&lt;p&gt;A key observation is that this is not really an operating system problem, it is a scripting problem.&lt;/p&gt;
&lt;p&gt;Your script knows exactly when the computation starts and when it ends. That means the script itself can request sleep prevention at the beginning of the run and release it as soon as the work is complete. If something goes wrong and the script exits early, it can still clean up properly.&lt;/p&gt;
&lt;p&gt;This leads to a much more precise and reliable solution: keep the system awake &lt;strong&gt;only while the computation is actually running&lt;/strong&gt;, and automatically restore normal system behavior afterward.&lt;/p&gt;
&lt;h2&gt;One idea, implemented for three languages&lt;/h2&gt;
&lt;p&gt;Based on this idea, we implemented the same solution for three languages that are commonly used in scientific and systems modeling: &lt;strong&gt;R&lt;/strong&gt;, &lt;strong&gt;Julia&lt;/strong&gt;, and &lt;strong&gt;MATLAB&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;All three implementations are called &lt;strong&gt;NoSleep&lt;/strong&gt;. They follow the same conceptual design and expose a very similar interface, even though the languages and ecosystems are different.&lt;/p&gt;
&lt;p&gt;The goal was not to create three unrelated utilities, but to provide a consistent solution that works across environments often used in the same modeling workflows.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/preventing-system-sleep-during-long-computations/img/fig1-sleep.jpg&quot; alt=&quot;Fig1. Computations that take hours on user laptops are often interrupted by system sleep&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Design principles behind NoSleep&lt;/h2&gt;
&lt;p&gt;All NoSleep implementations share a few core principles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They rely exclusively on &lt;strong&gt;native operating system mechanisms&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Power Request&lt;/code&gt; API on Windows,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;caffeinate&lt;/code&gt; on macOS,&lt;/li&gt;
&lt;li&gt;&lt;code&gt;systemd-inhibit&lt;/code&gt; on Linux.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;No hacks, no simulated mouse movement, no background tricks.&lt;/li&gt;
&lt;li&gt;Fully &lt;strong&gt;cross-platform&lt;/strong&gt;: Windows, macOS, and Linux.&lt;/li&gt;
&lt;li&gt;Automatic cleanup: sleep prevention is released when the computation finishes or fails.&lt;/li&gt;
&lt;li&gt;Open source and published through standard channels.&lt;/li&gt;
&lt;li&gt;No external runtime dependencies.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The intention is to cooperate with the operating system rather than fight it, and to keep the behavior explicit and predictable.&lt;/p&gt;
&lt;h2&gt;Using NoSleep inside your scripts&lt;/h2&gt;
&lt;p&gt;NoSleep is designed to be used directly inside your code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can wrap a block of code and keep the system awake only for the duration of that block.&lt;/li&gt;
&lt;li&gt;You can manually enable and disable sleep prevention around long-running sections.&lt;/li&gt;
&lt;li&gt;The behavior is deterministic and reproducible, which makes scripts easier to share and reuse on other machines.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because sleep control lives inside the script, it does not depend on external tools, system settings, or manual intervention.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/preventing-system-sleep-during-long-computations/img/fig2-nosleep.jpg&quot; alt=&quot;Fig2. NoSleep helps keep your system awake only while your long computations are running&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Cross-language framework&lt;/h2&gt;
&lt;p&gt;All three implementations of NoSleep share the same basic API design, making it easy to switch between languages while keeping the same approach to sleep prevention.&lt;/p&gt;
&lt;h2&gt;R&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt;&lt;br&gt;
&lt;a href=&quot;https://github.com/hetalang/NoSleepR&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/hetalang/NoSleepR&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CRAN:&lt;/strong&gt;&lt;br&gt;
&lt;a href=&quot;https://cran.r-project.org/web/packages/NoSleepR/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://cran.r-project.org/web/packages/NoSleepR/index.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;install.packages&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;NoSleepR&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
library&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;NoSleepR&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Simple usage:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;nosleep_on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# nosleep_on(TRUE) sets display activity&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# Long computation here&lt;/span&gt;
nosleep_off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Block usage:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;with_nosleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# Long computation here&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Julia&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt;&lt;br&gt;
&lt;a href=&quot;https://github.com/hetalang/NoSleep.jl&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/hetalang/NoSleep.jl&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Julia Registry:&lt;/strong&gt;&lt;br&gt;
&lt;a href=&quot;https://juliahub.com/ui/Packages/General/NoSleep&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://juliahub.com/ui/Packages/General/NoSleep&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-julia&quot;&gt;&lt;code class=&quot;language-julia&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;add NoSleep
&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; NoSleep&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Simple usage:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-julia&quot;&gt;&lt;code class=&quot;language-julia&quot;&gt;nosleep_on&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# nosleep_on(keep_display=true) sets display activity&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# Long computation here&lt;/span&gt;
nosleep_off&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Block usage:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-julia&quot;&gt;&lt;code class=&quot;language-julia&quot;&gt;with_nosleep&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# Long computation here&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;MATLAB&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt;&lt;br&gt;
&lt;a href=&quot;https://github.com/hetalang/NoSleepMatlab&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/hetalang/NoSleepMatlab&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;File Exchange:&lt;/strong&gt;&lt;br&gt;
&lt;a href=&quot;https://www.mathworks.com/matlabcentral/fileexchange/183008-nosleep&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://www.mathworks.com/matlabcentral/fileexchange/183008-nosleep&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Find in File Exchange and follow instructions.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Simple usage:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-matlab&quot;&gt;&lt;code class=&quot;language-matlab&quot;&gt;import NoSleep&lt;span class=&quot;token operator&quot;&gt;.*&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;nosleep_on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;% nosleep_on(true) sets display activity&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;% Long computation here&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;nosleep_off&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Block usage:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-matlab&quot;&gt;&lt;code class=&quot;language-matlab&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;myLongComputation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;% Long computation here&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;with_nosleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;myLongComputation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;When NoSleep is useful, and when it isn&#39;t&lt;/h2&gt;
&lt;p&gt;NoSleep does not make your code faster, and it does not replace proper job schedulers or cluster infrastructure. It is not meant for large distributed systems or remote compute environments.&lt;/p&gt;
&lt;p&gt;What it does is eliminate a very common and very annoying failure mode in long local computations. In practice, small tools that quietly do one thing well often end up being used far more often than expected, simply because they remove friction from everyday work.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Good Fit Is Not Enough</title>
    <link href="https://metelkin.me/practical-identifiability-why-a-good-fit-is-not-enough/" />
    <updated>2026-02-21T00:00:00Z</updated>
    <id>https://metelkin.me/practical-identifiability-why-a-good-fit-is-not-enough/</id>
    <content type="html">&lt;p&gt;&lt;img src=&quot;https://metelkin.me/practical-identifiability-why-a-good-fit-is-not-enough/img/fig0-cover.png&quot; alt=&quot;Cover&quot;&gt;&lt;/p&gt;
&lt;h2&gt;The Illusion of Certainty&lt;/h2&gt;
&lt;p&gt;✓  The optimizer stopped without warnings.&lt;br&gt;
✓  The simulated curves match the experimental data.&lt;br&gt;
✓  Residuals look acceptable.&lt;br&gt;
✓  A final set of parameter values is reported.&lt;br&gt;
✓  Predictions are generated and plotted.&lt;/p&gt;
&lt;p&gt;From a workflow perspective, everything seems complete. The calibration step is done, the numbers are fixed, and the model appears ready for interpretation or decision-making. It feels deterministic: one model, one parameter set, one set of predictions.&lt;/p&gt;
&lt;p&gt;This is the point where many modeling projects move forward.&lt;/p&gt;
&lt;p&gt;The optimizer may settle at one acceptable solution among many equivalent ones. A good fit does not prove that the parameters are well constrained by the data. The reported parameter values represent one possible explanation of the data, not necessarily a unique explanation.&lt;/p&gt;
&lt;p&gt;The final parameter table may give a false impression of precision. What looks like certainty may, in fact, be an illusion.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/practical-identifiability-why-a-good-fit-is-not-enough/img/fig1-point-estimates.png&quot; alt=&quot;Fig 1. Point estimates&quot;&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;Point estimates workflow&lt;/strong&gt;. Calibration produces a stable fit and a single set of parameter values. However, without identifiability analysis, the uniqueness and robustness of this solution remain unknown.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Why Optimization Hides the Problem&lt;/h2&gt;
&lt;p&gt;Parameter estimation in nonlinear models is an inverse problem: we try to estimate parameter values from observed outcomes. In general, inverse problems do not guarantee a unique solution. Different parameter values, or different combinations of parameters, can reproduce the data equally well, or very close to it.&lt;/p&gt;
&lt;p&gt;Parameters may be correlated, partially redundant, or only weakly influential within the range supported by the data. As a result, several distinct parameter sets can produce nearly identical fits. An optimizer will return one of them, depending on initialization, algorithmic details, and numerical tolerances.&lt;/p&gt;
&lt;p&gt;If we report only that single solution, we implicitly assume that the parameters are well determined. But without assessing the width of the admissible region (whether a finite confidence interval exists at all) this assumption may be false. The true parameter values consistent with the data may lie far from the reported point estimate.&lt;/p&gt;
&lt;p&gt;Because nonlinear models often contain many interacting parameters, it is rarely possible to anticipate in advance which ones are tightly constrained and which are not. Optimization alone does not answer that question.&lt;/p&gt;
&lt;h2&gt;What Is Practical Identifiability?&lt;/h2&gt;
&lt;p&gt;Practical identifiability is not about finding a single best-fit parameter set. It is about understanding the range of parameter values that are still consistent with the observed data.&lt;/p&gt;
&lt;p&gt;Instead of asking, &amp;quot;What are the optimal parameters?&amp;quot;, we ask a different questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How far can each parameter move before the model no longer agrees with the data?&lt;/li&gt;
&lt;li&gt;What are the extreme parameter values that still provide an acceptable fit?&lt;/li&gt;
&lt;li&gt;What are the limits of the admissible parameter space?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this sense, identifiability analysis explores the geometry of the admissible parameter space. For each parameter, it reveals whether a finite confidence interval exists, how wide it is, and whether it remains bounded or extends indefinitely. A parameter is considered practically identifiable if its admissible region is compact and well constrained by the data.&lt;/p&gt;
&lt;p&gt;Beyond individual intervals, identifiability analysis also exposes correlations and compensatory relationships between parameters. Directions in parameter space where changes can offset each other without degrading the fit.&lt;/p&gt;
&lt;p&gt;Importantly, these properties reflect the structure of the data-model combination. They are far less dependent on the specific optimization algorithm or on which local minimum was found. Identifiability analysis characterizes the landscape around solutions, not just a single point.&lt;/p&gt;
&lt;h2&gt;What Does Identifiability Analysis Give Us?&lt;/h2&gt;
&lt;p&gt;Once identifiability analysis is performed, the model stops being just a calibrated object and becomes a quantified one.&lt;/p&gt;
&lt;p&gt;If all parameters turn out to be practically identifiable - meaning their confidence intervals are finite and reasonably compact - this tells us:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The available data constrain the parameters sufficiently.&lt;/li&gt;
&lt;li&gt;The chosen parametrization is appropriate.&lt;/li&gt;
&lt;li&gt;The calibration result is stable, not accidental.&lt;/li&gt;
&lt;li&gt;Predictions derived from the model can be accompanied by meaningful uncertainty bounds.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the best-case scenario. It does not mean the model is &amp;quot;true&amp;quot; but it means the parameter estimates are genuinely supported by the data.&lt;/p&gt;
&lt;p&gt;If some parameters are weakly identifiable or non-identifiable, the analysis still provides actionable information. Several strategies become available:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fix and document&lt;/strong&gt; poorly identifiable parameters, reducing the model to a reliably constrained core.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reparametrize or group&lt;/strong&gt; parameters, replacing unstable individual quantities with more robust composite ones.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Augment the data&lt;/strong&gt;, either by incorporating information from the literature or by designing additional experiments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use model-driven experimental design&lt;/strong&gt;, targeting conditions that improve identifiability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Assess impact on predictions&lt;/strong&gt;, determining whether weakly identifiable parameters actually influence the outputs of interest.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In any case, identifiability analysis eliminates false confidence. It clarifies what is supported by data and what is not, reducing the risk of unpleasant surprises when predictions fail.&lt;/p&gt;
&lt;p&gt;Despite being conceptually well established, identifiability analysis is still not routinely included in many modeling workflows. Time pressure, computational cost, and lack of accessible tools often push it aside in practice.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/practical-identifiability-why-a-good-fit-is-not-enough/img/fig2-interval-estimate.png&quot; alt=&quot;Fig 2. Interval estimate&quot;&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;Identifiability-aware workflow.&lt;/strong&gt; By estimating parameter confidence intervals and propagating uncertainty to model outputs, we distinguish well-identified parameters from weakly identifiable ones. This leads to prediction bands that are narrow where the data constrain the model and wide where they do not.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Fisher Information Matrix and Its Limitations&lt;/h2&gt;
&lt;p&gt;A widely used approach to assessing identifiability is the &lt;strong&gt;Fisher Information Matrix (FIM)&lt;/strong&gt;. It evaluates local sensitivity of the model output to parameter changes near the optimum and is often available as part of standard maximum likelihood estimation. Because it is computationally efficient, it is commonly used as a quick diagnostic of parameter identifiability.&lt;/p&gt;
&lt;p&gt;However, the FIM is inherently local. It relies on a quadratic approximation of the likelihood surface around the optimum and may not capture flat directions, strong nonlinearities, or extended regions of near-equivalent solutions. As a result, it can miss important aspects of parameter uncertainty in complex nonlinear models.&lt;/p&gt;
&lt;p&gt;To overcome these limitations, &lt;strong&gt;likelihood-based methods&lt;/strong&gt; that explore the objective function beyond the immediate neighborhood of the optimum provide a more informative assessment. By examining how far parameters can vary while maintaining agreement with the data, these approaches offer a more complete view of practical identifiability.&lt;/p&gt;
&lt;h2&gt;Profile Likelihood Methods&lt;/h2&gt;
&lt;p&gt;Likelihood-based approaches provide a principled way to assess practical identifiability. Instead of relying on local curvature near the optimum, they examine how the objective function changes as individual parameters are systematically varied. This reveals whether confidence intervals are finite and how strongly parameters are constrained by the data.&lt;/p&gt;
&lt;p&gt;In practice, these methods are more computationally demanding and require careful implementation, which has limited their routine use in many modeling workflows.&lt;/p&gt;
&lt;p&gt;To make such analyses more accessible, we developed an open-source Julia package, &lt;a href=&quot;https://github.com/insysbio/LikelihoodProfiler.jl&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;LikelihoodProfiler.jl&lt;/a&gt;. It implements several profile-likelihood strategies within a unified workflow, including optimization-based, integration-based, and constrained-optimization (CICO) approaches. A detailed description of the methodology and software is available in our recent JOSS publication (&lt;a href=&quot;https://doi.org/10.21105/joss.09501&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://doi.org/10.21105/joss.09501&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/practical-identifiability-why-a-good-fit-is-not-enough/img/fig3-profile-likelihood.png&quot; alt=&quot;Fig 3. Profile likelihood methods&quot;&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;Profile likelihood examples generated with &lt;a href=&quot;https://github.com/insysbio/LikelihoodProfiler.jl&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;LikelihoodProfiler.jl&lt;/a&gt;.&lt;/strong&gt; By tracing the objective function beyond the optimum, these profiles expose the curvature and extent of the admissible parameter region. The threshold crossing determines finite or non-finite confidence intervals.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion: A Shift in Mindset&lt;/h2&gt;
&lt;p&gt;Nonlinear modeling is inherently challenging, especially when it involves solving inverse problems and estimating multiple interacting parameters. Once a model fits the data well, there is a natural temptation to move directly to interpretation and prediction.&lt;/p&gt;
&lt;p&gt;However, optimal parameter values represent only part of the information contained in the data. Identifiability analysis reveals how strongly those values are supported and what constraints the data truly impose on the model. This distinction is essential for building trust in model-based conclusions and for making informed decisions.&lt;/p&gt;
&lt;p&gt;Importantly, even weakly identifiable parameters do not automatically invalidate a model. When handled carefully, models with partial ambiguity can still provide useful insights. The key is to understand what is well constrained and what is not.&lt;/p&gt;
&lt;p&gt;Today, practical tools make it possible to include identifiability analysis as a routine part of the modeling workflow rather than an afterthought.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Landscape of ODE Solvers in R</title>
    <link href="https://metelkin.me/landscape-of-ode-solvers-in-r/" />
    <updated>2026-05-04T00:00:00Z</updated>
    <id>https://metelkin.me/landscape-of-ode-solvers-in-r/</id>
    <content type="html">&lt;p&gt;&lt;img src=&quot;https://metelkin.me/landscape-of-ode-solvers-in-r/img/fig0-cover.png&quot; alt=&quot;Cover&quot;&gt;&lt;/p&gt;
&lt;p&gt;Solving &lt;strong&gt;ordinary differential equations (ODEs)&lt;/strong&gt; is a common task in many fields, including systems biology, pharmacometrics, physics, and engineering where dynamic systems are studied. An ODE describes how a variables evolve over time, typically written as&lt;br&gt;
$$&lt;br&gt;
&#92;begin{align}&lt;br&gt;
&#92;frac{dx_1}{dt} &amp;amp; = f_1&#92;left(t, x_1, x_2, &#92;ldots &#92;right), &#92;&#92;&lt;br&gt;
&#92;frac{dx_2}{dt} &amp;amp; = f_2&#92;left(t, x_1, x_2, &#92;ldots &#92;right), &#92;&#92;&lt;br&gt;
&#92;vdots &amp;amp; &#92;&#92;&lt;br&gt;
&#92;end{align}&lt;br&gt;
$$&lt;/p&gt;
&lt;p&gt;The R ecosystem offers a collection of tools for this purpose: from lightweight numerical solvers to full modeling frameworks. This article provides &lt;strong&gt;a practical overview of ODE solvers in R&lt;/strong&gt;, with a focus on helping users navigate the ecosystem and choose appropriate tools.&lt;/p&gt;
&lt;p&gt;All packages included here were tested with simple examples and the code is available in the &lt;a href=&quot;https://github.com/metelkin/ode-solvers-in-r&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What is included&lt;/h2&gt;
&lt;p&gt;We include R packages that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Support solving general ODE systems&lt;/li&gt;
&lt;li&gt;Are mentioned in literature, documentation, or community discussions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We intentionally exclude:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Deprecated or archived packages&lt;/li&gt;
&lt;li&gt;Thin wrappers around other frameworks without original solvers or formats&lt;/li&gt;
&lt;li&gt;Small helper packages&lt;/li&gt;
&lt;li&gt;Proprietary tools with limited public documentation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h2&gt;Packages overview table&lt;/h2&gt;
&lt;div class=&quot;table-h-scroll&quot;&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;Solver type&lt;/th&gt;
&lt;th&gt;Algorithms&lt;/th&gt;
&lt;th&gt;Model format&lt;/th&gt;
&lt;th&gt;Stiff&lt;/th&gt;
&lt;th&gt;DAE&lt;/th&gt;
&lt;th&gt;DDE&lt;/th&gt;
&lt;th&gt;Time events&lt;/th&gt;
&lt;th&gt;Conditional events&lt;/th&gt;
&lt;th&gt;Downloads (2025)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cran.r-project.org/package=deSolve&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;deSolve&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;http://www.netlib.org/odepack/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ODEPACK&lt;/a&gt;; &lt;a href=&quot;http://www.netlib.org/ode/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;DASPK&lt;/a&gt; (Fortran)&lt;/td&gt;
&lt;td&gt;Compiled&lt;/td&gt;
&lt;td&gt;lsoda, lsode, lsodes, lsodar, vode, daspk, bdf, adams, euler, rk4, ode23, ode45,&lt;/td&gt;
&lt;td&gt;R func (interpreted); C/C++/Fortran (compiled)&lt;/td&gt;
&lt;td&gt;Yes (lsoda)&lt;/td&gt;
&lt;td&gt;Yes (daspk)&lt;/td&gt;
&lt;td&gt;Yes (dede)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (root finding)&lt;/td&gt;
&lt;td&gt;635628&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cran.r-project.org/package=diffeqr&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;diffeqr&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://docs.sciml.ai/DiffEqDocs/stable/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;DifferentialEquations.jl&lt;/a&gt; SciML ecosystem&lt;/td&gt;
&lt;td&gt;External runtime (Julia)&lt;/td&gt;
&lt;td&gt;&amp;gt; 200 algorithms &lt;a href=&quot;https://docs.sciml.ai/DiffEqDocs/stable/solvers/ode_solve/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;see docs&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;R func (interpreted); Julia func (JIT/compiled)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (root finding)&lt;/td&gt;
&lt;td&gt;4688&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cran.r-project.org/package=dMod&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;dMod&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;deSolve&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Compiled&lt;/td&gt;
&lt;td&gt;&lt;em&gt;depends on deSolve&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;DSL (cOde, compiled), API (compiled)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;4947&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cran.r-project.org/package=EpiModel&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;EpiModel&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;deSolve&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Compiled&lt;/td&gt;
&lt;td&gt;&lt;em&gt;depends on deSolve&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;R func (interpreted)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Yes (dede)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;23088&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;* &lt;a href=&quot;https://iqrtools.intiquan.com/doc/book/license-and-availability.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;IQRTools&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://sundials.readthedocs.io/en/latest/cvodes/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;CVODES&lt;/a&gt; (SUNDIALS) (C)&lt;/td&gt;
&lt;td&gt;Compiled&lt;/td&gt;
&lt;td&gt;BDF, Adams&lt;/td&gt;
&lt;td&gt;DSL (Compiled)&lt;/td&gt;
&lt;td&gt;Yes (BDF)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;&lt;em&gt;NA&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cran.r-project.org/package=mrgsolve&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mrgsolve&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;http://www.netlib.org/odepack/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;DLSODA&lt;/a&gt; (C++ translation)&lt;/td&gt;
&lt;td&gt;Compiled&lt;/td&gt;
&lt;td&gt;lsoda&lt;/td&gt;
&lt;td&gt;DSL (C++-like, compiled)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;33544&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cran.r-project.org/package=odin&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;odin&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;deSolve&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Compiled&lt;/td&gt;
&lt;td&gt;&lt;em&gt;depends on deSolve&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;DSL (R-like, compiled)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Yes (dede)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;17252&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cran.r-project.org/package=PBSddesolve&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PBSddesolve&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://webhomes.maths.ed.ac.uk/~swood34/simon/dde.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;solv95&lt;/a&gt; (C)&lt;/td&gt;
&lt;td&gt;Compiled&lt;/td&gt;
&lt;td&gt;dde&lt;/td&gt;
&lt;td&gt;R func (interpreted)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;10341&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cran.r-project.org/package=PKPDsim&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PKPDsim&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/boostorg/odeint&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Boost::odeint&lt;/a&gt; (C++)&lt;/td&gt;
&lt;td&gt;Compiled&lt;/td&gt;
&lt;td&gt;Adaptive RK (RKCK54)&lt;/td&gt;
&lt;td&gt;DSL (compiled)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;10319&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cran.r-project.org/package=pracma&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;pracma&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Matlab-inspired implementation&lt;/td&gt;
&lt;td&gt;Pure R&lt;/td&gt;
&lt;td&gt;ode23, ode23s, ode45, ode78&lt;/td&gt;
&lt;td&gt;R func (interpreted)&lt;/td&gt;
&lt;td&gt;Yes (ode23s)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;1059146&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cran.r-project.org/package=r2sundials&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;r2sundials&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://computing.llnl.gov/projects/sundials&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SUNDIALS&lt;/a&gt; (C)&lt;/td&gt;
&lt;td&gt;Compiled&lt;/td&gt;
&lt;td&gt;BDF, Adams&lt;/td&gt;
&lt;td&gt;R func (interpreted); C++ (compiled)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (via IDA)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Yes (root finding)&lt;/td&gt;
&lt;td&gt;3439&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cran.r-project.org/package=reticulate&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;reticulate&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.solve_ivp.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SciPy solve_ivp&lt;/a&gt; (Python)&lt;/td&gt;
&lt;td&gt;External runtime (Python)&lt;/td&gt;
&lt;td&gt;RK45, RK23, DOP853, Radau, BDF, LSODA&lt;/td&gt;
&lt;td&gt;R func (interpreted); Python func (interpreted)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Yes (root detection only)&lt;/td&gt;
&lt;td&gt;3566483&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cran.r-project.org/package=rodeo&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;rodeo&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;deSolve&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Compiled&lt;/td&gt;
&lt;td&gt;&lt;em&gt;depends on deSolve&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Table format (interpreted / compiled)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (root finding)&lt;/td&gt;
&lt;td&gt;3386&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cran.r-project.org/package=rstan&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;rstan&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://mc-stan.org/docs/2_27/functions-reference/functions-ode-solver.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Stan Math Library&lt;/a&gt; (C++)&lt;/td&gt;
&lt;td&gt;Compiled&lt;/td&gt;
&lt;td&gt;rk45, bdf, adams, ckrk&lt;/td&gt;
&lt;td&gt;DSL (Stan language, compiled)&lt;/td&gt;
&lt;td&gt;Yes (bdf)&lt;/td&gt;
&lt;td&gt;Yes (limited, index-1)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;1107167&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cran.r-project.org/package=rxode2&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;rxode2&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://github.com/sdwfrost/liblsoda&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;LIBLSODA&lt;/a&gt; + custom (C)&lt;/td&gt;
&lt;td&gt;Compiled&lt;/td&gt;
&lt;td&gt;liblsoda, lsoda, dop853, indLin&lt;/td&gt;
&lt;td&gt;DSL (R-like, compiled)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;42872&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cran.r-project.org/package=sundialr&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;sundialr&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;https://computing.llnl.gov/projects/sundials&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SUNDIALS&lt;/a&gt; (C)&lt;/td&gt;
&lt;td&gt;Compiled&lt;/td&gt;
&lt;td&gt;BDF, Adams&lt;/td&gt;
&lt;td&gt;R func (interpreted); C++ (compiled)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (via IDA)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;* IQRTools is proprietary; version 99.0.0 is available under AGPL-3.0 and was used in this review.&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;Engine&lt;/h4&gt;
&lt;p&gt;This refers to the underlying numerical implementation used by the package. This can be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a well-known external library (e.g., ODEPACK)&lt;/li&gt;
&lt;li&gt;a custom compiled implementation&lt;/li&gt;
&lt;li&gt;another R package&lt;/li&gt;
&lt;li&gt;or an external runtime (e.g., another language)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Solver type&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pure R solvers&lt;/strong&gt;: Numerical algorithms implemented directly in R. These are easy to inspect and flexible, but typically slower due to interpreter overhead.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compiled solvers&lt;/strong&gt;: 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.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;External runtime interfaces&lt;/strong&gt;: Packages that delegate computation to external ecosystems such as Julia or Python. These act as bridges rather than standalone solvers.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Algorithms&lt;/h4&gt;
&lt;p&gt;This is the list of available numerical methods as documented by the package.&lt;/p&gt;
&lt;h4&gt;Model format&lt;/h4&gt;
&lt;p&gt;ODE models can be defined in different ways: as R functions, domain-specific languages (DSL), structured APIs, or even external code.&lt;/p&gt;
&lt;p&gt;The key distinction affecting performance is how the model is executed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Interpreted execution&lt;/strong&gt;: The model is defined as an R function and evaluated during each solver step.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compiled execution&lt;/strong&gt;: The model is translated into compiled code before simulation, avoiding interpreter overhead and improving performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Stiff&lt;/h4&gt;
&lt;p&gt;Indicates whether the solver can handle &lt;strong&gt;stiff systems&lt;/strong&gt;. 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).&lt;/p&gt;
&lt;h4&gt;DAE / DDE&lt;/h4&gt;
&lt;p&gt;Support for these features is solver-dependent and often requires specific algorithms.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DAE (Differential-Algebraic Equations)&lt;/strong&gt;: Systems that include algebraic constraints in addition to differential equations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DDE (Delay Differential Equations)&lt;/strong&gt;: Systems where derivatives depend on past states.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Time events / Conditional events&lt;/h4&gt;
&lt;p&gt;These features are important for modeling real-world systems with discontinuities.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Time events&lt;/strong&gt;: Discrete changes applied at predefined time points (e.g., dosing events).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conditional events&lt;/strong&gt;: Events triggered when a condition is met during simulation (e.g., threshold crossing).&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Downloads 2025&lt;/h4&gt;
&lt;p&gt;Total number of downloads in 2025 from CRAN, reflecting usage statistics. Calculated with &lt;a href=&quot;https://cranlogs.r-pkg.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;CRAN logs&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Repository and test cases&lt;/h2&gt;
&lt;p&gt;To ensure practical consistency, packages were tested on two simple ODE models. The full code for all packages is available in the companion &lt;a href=&quot;https://github.com/metelkin/ode-solvers-in-r&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We are providing here the code in &lt;code&gt;mrgsolve&lt;/code&gt; format for two examples: a pharmacokinetic model with non-linear elimination, and the Robertson problem, which is a classic stiff ODE system.&lt;/p&gt;
&lt;h3&gt;Example 1: Pharmacokinetic model with non-linear elimination&lt;/h3&gt;
&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;library&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mrgsolve&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
library&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;magrittr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# load model as DLS (C++ like)&lt;/span&gt;
mod &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; mcode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pk_model&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &#39;
&lt;span class=&quot;token operator&quot;&gt;$&lt;/span&gt;PARAM
kabs_Alc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10.0&lt;/span&gt;
Vmax_ADH &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
Km_ADH &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.1&lt;/span&gt;
V_blood &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5.5&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;$&lt;/span&gt;CMT
Alc_g Alc_b_amt

&lt;span class=&quot;token operator&quot;&gt;$&lt;/span&gt;ODE
double Alc_b &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Alc_b_amt &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; V_blood&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
double vabs_Alc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; kabs_Alc &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; Alc_g&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
double v_ADH &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Vmax_ADH &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; Alc_b &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Km_ADH &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; Alc_b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; V_blood&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

dxdt_Alc_g &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;vabs_Alc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
dxdt_Alc_b_amt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; vabs_Alc &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; v_ADH&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;$&lt;/span&gt;TABLE
capture Alc_b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&#39;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# time event&lt;/span&gt;
ev1 &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; ev&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;time &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; amt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cmt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Alc_g&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# solve&lt;/span&gt;
out &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; mod &lt;span class=&quot;token percent-operator operator&quot;&gt;%&gt;%&lt;/span&gt;
  init&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Alc_g &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Alc_b_amt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token percent-operator operator&quot;&gt;%&gt;%&lt;/span&gt;
  ev&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ev1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token percent-operator operator&quot;&gt;%&gt;%&lt;/span&gt;
  mrgsim&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;start &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; delta &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.001&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

plot&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/landscape-of-ode-solvers-in-r/img/fig1-example-1-sim.png&quot; alt=&quot;example-1-sim&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Example 2: Robertson problem stiff ODE&lt;/h3&gt;
&lt;pre class=&quot;language-r&quot;&gt;&lt;code class=&quot;language-r&quot;&gt;library&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mrgsolve&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
library&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;magrittr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# load model as DLS (C++ like)&lt;/span&gt;
mod &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; mcode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rob_model&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &#39;
&lt;span class=&quot;token operator&quot;&gt;$&lt;/span&gt;CMT
A B C

&lt;span class=&quot;token operator&quot;&gt;$&lt;/span&gt;ODE
dxdt_A &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.04&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; A &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1e4&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; B &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; C&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
dxdt_B &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;0.04&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; A &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1e4&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; B &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; C &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3e7&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; pow&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;B&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
dxdt_C &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;token number&quot;&gt;3e7&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; pow&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;B&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&#39;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# solve&lt;/span&gt;
out &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; mod &lt;span class=&quot;token percent-operator operator&quot;&gt;%&gt;%&lt;/span&gt;
  init&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;A &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; B &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; C &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token percent-operator operator&quot;&gt;%&gt;%&lt;/span&gt;
  mrgsim&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;start &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; end &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; delta &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1e-2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

plot&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://metelkin.me/landscape-of-ode-solvers-in-r/img/fig2-example-2-sim.png&quot; alt=&quot;example-2-sim&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Author&#39;s notes&lt;/h2&gt;
&lt;p&gt;The goal of this review was to provide &lt;strong&gt;a maximally complete and objective overview&lt;/strong&gt; of tools for solving ODEs in R. The packages included in this survey differ in their purpose and functionality: from simple numerical solvers to full-featured frameworks and specialized domain-specific tools. Many aspects, such as computational performance, numerical accuracy, and advanced functionality are intentionally not covered in this article.&lt;/p&gt;
&lt;p&gt;Many packages designed for specific application areas (e.g., PK/PD) can also be used to solve general ODE systems. These are included here on equal footing, without focusing on their domain-specific features.&lt;/p&gt;
&lt;p&gt;Due to the nature of the R ecosystem, most tools that could be systematically reviewed, tested, and compared in a reproducible way are open-source packages.  While several &lt;strong&gt;proprietary engines&lt;/strong&gt; also provide ODE-solving capabilities, their internal implementations, numerical methods, and usage are not always publicly documented or accessible without licenses. As a result, they are not included in the main comparison table.&lt;/p&gt;
&lt;p&gt;For a broader overview, see the &lt;a href=&quot;https://cran.r-project.org/web/views/DifferentialEquations.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;CRAN Task View: Differential Equations&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Download metrics&lt;/h3&gt;
&lt;p&gt;The table includes popularity metrics as 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 usage of a package for non-ODE problems. Therefore, they should not be considered a deciding factor when choosing a tool, but rather as a rough indicator of visibility within the community.&lt;/p&gt;
&lt;h3&gt;deSolve&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://cran.r-project.org/package=deSolve&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;deSolve&lt;/a&gt; is one of the most widely used and established ODE packages in R. Its central role in the ecosystem is reflected not only in its large user base, but also in the number of other packages that build on top of it.&lt;/p&gt;
&lt;p&gt;It provides a broad set of numerical methods and supports a wide range of problem types, including stiff systems, DAEs, DDEs, and event handling. At the same time, it offers flexible model definitions, from simple R functions to compiled code.&lt;/p&gt;
&lt;p&gt;Overall, deSolve can be seen as a foundational tool in the R ecosystem for differential equations. Its popularity is well justified by its versatility, stability, and long-term development. If you are new to ODE modeling in R, starting with deSolve is a safe and practical choice before exploring more specialized tools.&lt;/p&gt;
&lt;h3&gt;Model formats&lt;/h3&gt;
&lt;p&gt;An important factor when choosing a tool is the model definition format. The R ecosystem offers a wide range of approaches: from simple R functions (deSolve, pracma, PBSddesolve) and tabular specifications (rodeo) to domain-specific languages (PKPDsim, rstan) and low-level compiled code (rxode2, mrgsolve, deSolve).&lt;/p&gt;
&lt;p&gt;These differences affect both performance and usability, and often determine how easily a model can be developed, modified, and integrated into a workflow.&lt;/p&gt;
&lt;h3&gt;Limitations and gaps&lt;/h3&gt;
&lt;p&gt;Despite the variety of available tools, support for some important features remains limited across the R ecosystem. In particular, capabilities such as delay differential equations (DDE), differential-algebraic equations (DAE), and advanced event handling are only partially implemented and are available in relatively few packages. As a result, modeling tasks that rely on these capabilities may require additional effort, careful selection of tools, or even custom implementations.&lt;/p&gt;
&lt;h2&gt;Disclaimer&lt;/h2&gt;
&lt;p&gt;This article will be continuously updated. If you find any inaccuracies or have suggestions, feel free to open an issue:&lt;br&gt;
&lt;a href=&quot;https://github.com/metelkin/ode-solvers-in-r/issues&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;https://github.com/metelkin/ode-solvers-in-r/issues&lt;/a&gt;&lt;/p&gt;
</content>
  </entry>
</feed>