---
title: Code structure
summary: A map of the package — which module owns which concept, how data is laid out in memory, and the conventions that the numerical kernels, the solver and the service share.
section: engineering
order: 4
level: 2
keywords: [modules, data layout, conventions, package]
---
<!-- gyre docs: expanded Markdown of engineering/code-structure.md (generated tables, verification numbers and chart data are written out). Base level of this page: L2 Practice; passages at another level are bracketed by 'begin level N' / 'end level N' comments and start with [LN Name]. Built from revision ceb1f4b0be. -->

<!-- begin level 1 (Essentials) -->
**[L1 Essentials] In brief**

One Python package, `src/gyre`, in five layers. **Numerics** (`basis`, `mesh`, `physics`,
`dg`, `implicit`, `linear`, `functionals`) are pure JAX kernels. **The discipline**
(`discipline`, `error`, `adapt`, `deform`) turns them into "solve, outputs, derivatives".
**Cases and execution** (`cases`, `runner`, `io`, `cli`) describe an analysis as data and run
it. **Delivery** (`service`, `agent`, `client`, `pipeline`) exposes the same execution path
over HTTP, MCP and YAML pipelines. **Evidence** (`verify`, `docsite`) produces the
verification records and this site. Everything above the discipline calls
`runner.run_case`, so the interfaces cannot drift apart.

<!-- end level 1 -->

## Module map

| Module | Owns |
|---|---|
| `gyre.basis` | LGL nodes and weights, derivative and interpolation matrices, SBP operators, pair tables, p-transfer operators (NumPy, set-up time) |
| `gyre.mesh` | `Mesh` (global high-order nodes, element→node map, boundary tags), structured-block builder, face connectivity and orientation |
| `gyre.meshgen` | generators: `airfoil_omesh`, `annulus`, `bump_channel`, `flat_plate`, `sphere_shell` (cubed sphere), `box`, spanwise extrusion |
| `gyre.meshio` | mesh files: Gmsh MSH reader (2.2 and 4.1, ASCII) and writer (2.2), the NPZ format, validation, inspection, lookup of the file a case names |
| `gyre.physics` | compressible Euler/Navier–Stokes kernels: variable sets, two-point and interface fluxes (classical and entropy stable), viscous flux, flow parameters |
| `gyre.turbulence` | negative Spalart–Allmaras closure: model functions, source terms, boundary values, wall distance to curved walls as differentiable foot-point tables, manufactured solutions |
| `gyre.dg` | `Discretization`, differentiable `geometry`, boundary conditions, the residual kernel |
| `gyre.timestep` | explicit low-storage Runge–Kutta (verification) |
| `gyre.linear` | flexible GMRES in JAX control flow |
| `gyre.implicit` | `Hierarchy` (p-multigrid, block inverses, line factors, forward/transposed solves), `SteadySolver` |
| `gyre.lines` | lines of strongly coupled elements of an anisotropic mesh and the colouring used to assemble their coupling blocks (NumPy only) |
| `gyre.functionals` | surface integration from residual fluxes, force/moment/heat functionals, nodal loads, distributions |
| `gyre.deform` | inverse-distance-weighted mesh deformation: a stored matrix for small meshes, matrix-free with a hand-written transpose above $2\times10^7$ entries |
| `gyre.discipline` | `FlowDiscipline`: the MDO contract |
| `gyre.error` | adjoint-weighted-residual error estimation |
| `gyre.adapt` | output-driven order selection: uniform p-enrichment until the error estimate of an output meets a tolerance |
| `gyre.mms` | manufactured solutions with AD-generated forcing |
| `gyre.cases` | `CaseSpec` (pydantic), presets, `Case` |
| `gyre.runner`, `gyre.io` | end-to-end execution record; checkpoints, VTK, browser payloads |
| `gyre.identity` | the one definition of "which code is this": version, commit, and what makes a tree dirty; stamps results, verification records, pipeline records and the documentation |
| `gyre.service.*` | API application, job manager, worker, sessions, mesh store, pipeline runs, limits, host validation, response schemas, store |
| `gyre.agent.*` | MCP server (mounted or standalone proxy) |
| `gyre.client` | HTTP client and `RemoteDiscipline` |
| `gyre.pipeline.*` | pipeline engine and compute tasks |
| `gyre.verify.*` | verification studies and suite runner |
| `gyre.docsite.*` | documentation site builder |
| `gyre.integrations.*` | optional framework adapters (OpenMDAO) |

## Data layout

<!-- begin level 3 (Mechanics) -->
**[L3 Mechanics] Arrays**

| Quantity | Shape | Notes |
|---|---|---|
| state `U` | `(nvar, K, n, n[, n])` | variables first; `n = p + 1` nodes per direction |
| geometry `x` | `(dim, K, n, …)` | solution nodes |
| metrics `Ja` | `(dim, dim, K, n, …)` | `Ja[a, b]` = component `b` of $J\mathbf a^a$ |
| face data | `(…, K, F, nq)` | `F = 2·dim` faces, `nq = n^(dim-1)` face nodes |
| neighbour gather `plus` | `(K, F, nq)` int | flat index into a `(K·F·nq)` face array |
| block inverses | `(K, bs, bs)` | `bs = nvar · n^dim` |

Local face `f = 2a + s` is the face normal to reference direction `a` at $\xi_a = -1$ (`s = 0`)
or $+1$ (`s = 1`). Face-node arrays keep the remaining reference directions in ascending order;
the neighbour's ordering is reconciled by a permutation encoded in `plus`.

<!-- end level 3 -->

<!-- begin level 3 (Mechanics) -->
**[L3 Mechanics] Connectivity without coordinate matching**

Neighbours are found topologically: each face is keyed by the sorted global ids of its corner
geometry nodes (after mapping periodic images to a common id). The relative orientation of two
matching faces — one of two in 2-D, one of eight in 3-D — is found by comparing their geometry
node ids under each index symmetry, and the result is stored as three booleans
`(transpose, flip, flip)`. The gather index for any polynomial degree is generated from those
booleans, so every p-multigrid level reuses one connectivity.

<!-- end level 3 -->

## Conventions

<!-- begin level 2 (Practice) -->
**[L2 Practice] Residual sign and scaling**

`residual(…)` returns $\mathbf R$ with $\mathrm d\mathbf U/\mathrm dt = -\mathbf R$; `weak=True` returns
$\mathbf R_w = (wJ)\,\mathbf R$. Solvers, adjoints and error estimates use $\mathbf R_w$ exclusively.

<!-- end level 2 -->

<!-- begin level 3 (Mechanics) -->
**[L3 Mechanics] Static versus traced**

Python-level structure (dimension, degree, boundary kinds, flux names) is fixed when a
`Discretization` is built and never appears inside traced code as data-dependent control flow.
Everything numerical — state, geometry, flow parameters, operator tables — is traced.

<!-- end level 3 -->

<!-- begin level 2 (Practice) -->
**[L2 Practice] Adding things**

* *A boundary condition*: write a function returning `(UL, UR, Ub)` and register it in
  `BOUNDARY_CONDITIONS`; add it to `WALL_KINDS` / viscous masks if applicable; add it to the
  linearisation study's configurations.
* *A functional*: return a `Functional` from a function of `(U, geom, th)`; build surface
  quantities with `SurfaceOps` so they reuse the residual's fluxes.
* *A mesh generator*: produce a geometry lattice and call `structured_mesh`; add a preset.
* *A pipeline stage*: add a task function in `gyre.pipeline.tasks` and its name to
  `COMPUTE_STAGES`.
* *An agent tool*: add a REST endpoint first, then a described wrapper in `gyre.agent.server`.

<!-- end level 2 -->
