Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# rxode2 Copilot Instructions

This file provides guidance to GitHub Copilot when working with code in this repository.

## Overview

**rxode2** is an R package for solving and simulating from ODE-based pharmacometric models. It parses a mini-language into C code, compiles it, and dynamically loads it into R. The package contains mixed R, C, C++, and Fortran code.

## Build and Development Commands

### Install/Build
```r
# Install development version (from within R)
devtools::install()

# Or from the shell
R CMD INSTALL .
```

### Document
```r
devtools::document()
```

### Run All Tests
```r
# From within R (sets up proper environment)
invisible(lapply(list.files("src", "\\.s?o$", full.names = TRUE), unlink));devtools::test()

# Or from shell
NOT_CRAN=true Rscript -e "invisible(lapply(list.files('src', '\\.s?o$', full.names = TRUE), unlink));devtools::test()"
```

### Run a Single Test File
```r
# Filter by test file name (without "test-" prefix and ".R" suffix)
invisible(lapply(list.files("src", "\\.s?o$", full.names = TRUE), unlink));devtools::test(filter="basic")
invisible(lapply(list.files("src", "\\.s?o$", full.names = TRUE), unlink));devtools::test(filter="ui")
invisible(lapply(list.files("src", "\\.s?o$", full.names = TRUE), unlink));devtools::test(filter="linCmt")
```

### Run Tests from Installed Package
```r
# Full test suite
rxValidate(TRUE) # or rxTest(TRUE)

# CRAN-level tests only
rxValidate(FALSE)
```

### R CMD Check
```r
invisible(lapply(list.files("src", "\\.s?o$", full.names = TRUE), unlink));devtools::check()
# Or: R CMD check .
```

### Get generated C code from model

When trying to get the underlying generated code from a model that
compiled successfully, you can have it echo to the console with:

```r
summary(rxC(rxode2model))
```

### Regenerate Grammar and Build Artifacts
When modifying `inst/tran.g` (the dparser grammar) or needing to regenerate generated files:
```r
# Regenerates: src/tran.g.d_parser.h, R/rxrandomui.R, inst/include/*.h
.rxodeBuildCode()
```

## Architecture

### ODE Solving Pipeline

1. **Model Definition**: Users define models as R functions with `ini({})` and `model({})` blocks, or as strings/files using the rxode2 mini-language.

2. **Parsing** (`src/tran.c`, `inst/tran.g`): The mini-language grammar is defined in `inst/tran.g` (a dparser grammar). `tran.c` uses this grammar (pre-compiled into `src/tran.g.d_parser.h`) to parse model equations into an AST.

3. **Code Generation** (`src/codegen.c`): The parsed AST is translated into C code with ODE functions, Jacobians, and sensitivity equations.

4. **Compilation**: The generated C code is compiled to a shared library (`inline` package) and dynamically loaded.

5. **Solving** (`src/par_solve.cpp`): `rxSolve()` drives the numerical ODE solving using the compiled model. Supports multiple backends:
- LSODA (`src/lsoda.c`, `src/dlsoda.f`) — stiff/non-stiff adaptive
- VODE (`src/call_dvode.c`) — stiff solver
- DOP853 (`src/dop853.c`) — non-stiff Runge-Kutta
- Parallel solving via OpenMP across subjects

### Key Subsystems

**UI/Model Interface** (`R/ui.R`, `R/rxUiGet.R`, `R/asRxui.R`):
- `rxUi` objects are R environments containing the fully parsed model
- `rxUiGet` is an S3 dispatch system: `ui$property` calls `rxUiGet.property(x)`
- Model piping (`R/piping*.R`) copies and modifies `rxUi` objects via `.copyUi()`

**Event Table** (`src/et.cpp`, `src/etTran.cpp`, `R/et.R`):
- `et()` constructs dosing/sampling event tables
- `etTran()` transforms event tables for the internal solver format
- Supports NONMEM-compatible event IDs (evid 0-4)

**Linear Compartment Models** (`src/linCmt.cpp`, `R/linCmt.R`):
- Analytical solutions for 1-3 compartment PK models
- Gradients computed via Stan math auto-differentiation (for FOCEi in nlmixr2)
- Can be mixed with ODEs in the same model

**Output Data Frame** (`src/rxode2_df.cpp`, `src/rxData.cpp`):
- The solved result is returned as a modified data frame with special classes
- Factor levels and units are preserved from the event table

**Testing** (`tests/testthat/`):
- Tests use testthat edition 3
- Most test files wrap tests in `rxTest({...})` which skips on CRAN (requires `NOT_CRAN=true`)
- Test files follow `test-*.R` naming convention

### Important Files

| File | Purpose |
|------|---------|
| `inst/tran.g` | dparser grammar for the rxode2 mini-language |
| `src/tran.c` | Grammar-based parser entry point |
| `src/codegen.c` | C code generation from parsed AST |
| `src/par_solve.cpp` | Main parallel ODE solver (OpenMP) |
| `src/rxData.cpp` | rx_solve data structure management |
| `src/linCmt.cpp` | Analytical PK compartment solutions |
| `src/et.cpp` | Event table construction |
| `R/rxode2.R` | `rxode2()` function entry point |
| `R/rxsolve.R` | `rxSolve()` function |
| `R/ui.R` | Model function parsing (ini/model blocks) |
| `R/rxUiGet.R` | S3 dispatch for `ui$property` access |
| `R/build.R` | Code generation helpers (run during development) |
| `inst/include/rxode2.h` | Main C header for generated model code |

## R Code Style

- **Exported functions**: `camelCase` (e.g., `rxSolve`, `rxControl`, `modelExtract`)
- **Internal/non-exported functions**: `.camelCase` with a leading dot (e.g., `.copyUi`, `.rxSolveFromUi`)
- **Local variables inside functions**: `.camelCase` with a leading dot (e.g., `.model`, `.iniDf`, `.ret`)
- **S3 methods**: follow `generic.class` R convention (e.g., `rxSolve.rxUi`, `modelExtract.function`)
- Avoid `snake_case` for new names; underscores only appear in S3 methods for snake_case generics from other packages (e.g., `drop_units.rxSolve`)

### C/C++ Conventions

- Use `(Rf_error)(...)` (wrapped in parens) rather than `Rf_error(...)` to prevent macro conflicts
- C files use `#define STRICT_R_HEADERS` and `#define USE_FC_LEN_T`
- C++ files use `#define R_NO_REMAP` to avoid R API name pollution
- OpenMP support is conditional via `rxomp.h`

### Generated Files (do not edit manually)

- `src/tran.g.d_parser.h` — generated from `inst/tran.g` via dparser
- `R/rxrandomui.R` — generated by `.generateRandomUiFuns()` in `R/build.R`
- `inst/include/rxode2parse_control.h` — generated by `genDefine()` in `R/build.R`
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. The same content is mirrored in `.github/copilot-instructions.md` for GitHub Copilot.

## Overview

Expand Down