---
title: Architecture at a glance
summary: A case specification flows through one pipeline — mesh, discretization, discipline — that every interface shares. The service wraps that pipeline in isolated worker processes and exposes it to programs, people and agents.
section: overview
order: 2
level: 1
keywords: [architecture, service, discipline, layers]
---
<!-- gyre docs: expanded Markdown of overview/architecture.md (generated tables, verification numbers and chart data are written out). Base level of this page: L1 Essentials; passages at another level are bracketed by 'begin level N' / 'end level N' comments and start with [LN Name]. Built from revision ceb1f4b0be. -->

Gyre has three layers. The **numerical core** turns a state and a geometry into residuals and
outputs, differentiably. The **discipline** adds solvers and exposes the contract an MDO
framework expects. The **service** makes the discipline reachable — by HTTP, by pipeline, by
agent — and serves the documentation you are reading.

<figure class="diagram">
<svg viewBox="0 0 880 430" role="img" aria-label="Layered architecture of Gyre" xmlns="http://www.w3.org/2000/svg" font-family="inherit" font-size="13">
  <defs>
    <marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
      <path d="M0 0L10 5L0 10z" fill="currentColor"/>
    </marker>
  </defs>
  <g fill="none" stroke="currentColor" stroke-width="1.2">
    <rect x="10" y="10" width="860" height="110" rx="8" opacity="0.55"/>
    <rect x="10" y="150" width="860" height="110" rx="8" opacity="0.55"/>
    <rect x="10" y="290" width="860" height="130" rx="8" opacity="0.55"/>
  </g>
  <g fill="currentColor">
    <text x="24" y="32" font-weight="700">Interfaces</text>
    <text x="24" y="172" font-weight="700">Discipline layer</text>
    <text x="24" y="312" font-weight="700">Numerical core (JAX)</text>
  </g>
  <g fill="none" stroke="currentColor" stroke-width="1.2">
    <rect x="30"  y="48" width="140" height="56" rx="6"/>
    <rect x="190" y="48" width="140" height="56" rx="6"/>
    <rect x="350" y="48" width="140" height="56" rx="6"/>
    <rect x="510" y="48" width="160" height="56" rx="6"/>
    <rect x="690" y="48" width="160" height="56" rx="6"/>
    <rect x="30"  y="188" width="200" height="56" rx="6"/>
    <rect x="250" y="188" width="180" height="56" rx="6"/>
    <rect x="450" y="188" width="190" height="56" rx="6"/>
    <rect x="660" y="188" width="190" height="56" rx="6"/>
    <rect x="30"  y="328" width="150" height="74" rx="6"/>
    <rect x="195" y="328" width="150" height="74" rx="6"/>
    <rect x="360" y="328" width="150" height="74" rx="6"/>
    <rect x="525" y="328" width="150" height="74" rx="6"/>
    <rect x="690" y="328" width="160" height="74" rx="6"/>
  </g>
  <g fill="currentColor" text-anchor="middle">
    <text x="100" y="72">CLI</text><text x="100" y="90" font-size="11" opacity="0.75">gyre run / verify</text>
    <text x="260" y="72">REST API</text><text x="260" y="90" font-size="11" opacity="0.75">jobs · sessions · SSE</text>
    <text x="420" y="72">MCP</text><text x="420" y="90" font-size="11" opacity="0.75">agent tools</text>
    <text x="590" y="72">Pipelines</text><text x="590" y="90" font-size="11" opacity="0.75">YAML DAG · cache</text>
    <text x="770" y="72">Documentation</text><text x="770" y="90" font-size="11" opacity="0.75">built per revision</text>
    <text x="130" y="212">CaseSpec → Case</text><text x="130" y="230" font-size="11" opacity="0.75">presets · overrides · digest</text>
    <text x="340" y="212">FlowDiscipline</text><text x="340" y="230" font-size="11" opacity="0.75">solve · outputs · products</text>
    <text x="545" y="212">Newton–Krylov</text><text x="545" y="230" font-size="11" opacity="0.75">FGMRES · p-multigrid</text>
    <text x="755" y="212">Adjoint / error</text><text x="755" y="230" font-size="11" opacity="0.75">totals · estimates</text>
    <text x="105" y="356">Mesh + metrics</text><text x="105" y="374" font-size="11" opacity="0.75">curved quads/hexes</text><text x="105" y="390" font-size="11" opacity="0.75">deformation</text>
    <text x="270" y="356">Split-form DGSEM</text><text x="270" y="374" font-size="11" opacity="0.75">flux differencing</text><text x="270" y="390" font-size="11" opacity="0.75">SBP operators</text>
    <text x="435" y="356">Physics kernels</text><text x="435" y="374" font-size="11" opacity="0.75">EC fluxes · Riemann</text><text x="435" y="390" font-size="11" opacity="0.75">BR2 viscous · RANS (SA)</text>
    <text x="600" y="356">Boundary conditions</text><text x="600" y="374" font-size="11" opacity="0.75">adjoint consistent</text>
    <text x="770" y="356">Functionals</text><text x="770" y="374" font-size="11" opacity="0.75">forces · loads</text><text x="770" y="390" font-size="11" opacity="0.75">from residual fluxes</text>
  </g>
  <g stroke="currentColor" stroke-width="1.2" fill="none" marker-end="url(#arr)" opacity="0.8">
    <path d="M100 104 V150"/><path d="M260 104 V150"/><path d="M420 104 V150"/><path d="M590 104 V150"/>
    <path d="M340 244 V290"/><path d="M545 244 V290"/><path d="M755 244 V290"/><path d="M130 244 V290"/>
  </g>
</svg>
<figcaption>Every interface reaches the numerical core through the same discipline layer.</figcaption>
</figure>

<!-- begin level 2 (Practice) -->
**[L2 Practice] From request to result**

1. A **case specification** (`CaseSpec`: mesh generator or mesh file, flow conditions,
   discretization, solver controls, reference quantities) arrives as a preset name with
   overrides or as a full document. Its canonical JSON is hashed (for a mesh file, with the
   file's contents in place of its name); the digest identifies the analysis in job, session
   and result records, and the service's result cache is keyed by that JSON together with the
   requested gradients, error estimates and order selection, the contents of a mesh file and a
   fingerprint of the code.
2. `Case` builds the curved mesh, or reads it from a file, the `Discretization` (operators, index tables, boundary
   conditions) and a `FlowDiscipline`.
3. The discipline converges the flow with the Newton–Krylov solver, evaluates outputs, and on
   request solves adjoint or direct linear systems for derivatives and error estimates.
   With an order selection (`adapt`) this step is repeated at rising polynomial degree until
   the estimated error of one output meets a tolerance.
4. The **runner** collects everything into one self-describing result record: outputs,
   convergence history, gradients, surface distributions, timings and provenance (version,
   commit, device).

<!-- end level 2 -->

<!-- begin level 3 (Mechanics) -->
**[L3 Mechanics] Process model of the service**

<figure class="diagram">
<svg viewBox="0 0 880 250" role="img" aria-label="Service process model" xmlns="http://www.w3.org/2000/svg" font-family="inherit" font-size="13">
  <g fill="none" stroke="currentColor" stroke-width="1.2">
    <rect x="10" y="20" width="170" height="210" rx="8" opacity="0.55"/>
    <rect x="250" y="20" width="250" height="210" rx="8"/>
    <rect x="580" y="20" width="290" height="60" rx="6"/>
    <rect x="580" y="95" width="290" height="60" rx="6"/>
    <rect x="580" y="170" width="290" height="60" rx="6"/>
    <rect x="30" y="50" width="130" height="38" rx="5"/><rect x="30" y="100" width="130" height="38" rx="5"/><rect x="30" y="150" width="130" height="38" rx="5"/>
  </g>
  <g fill="currentColor" text-anchor="middle">
    <text x="95" y="38" font-weight="700">Clients (tailnet)</text>
    <text x="95" y="74">programs</text><text x="95" y="124">browsers</text><text x="95" y="174">AI agents</text>
    <text x="375" y="44" font-weight="700">API process (asyncio, CPU only)</text>
    <text x="375" y="78">/api/v1  ·  /mcp  ·  /  (docs)</text>
    <text x="375" y="112">job queue · result cache</text>
    <text x="375" y="146">session registry · idle reaper</text>
    <text x="375" y="180">metrics · optional bearer token</text>
    <text x="375" y="214" font-size="11" opacity="0.75">never runs solver kernels</text>
    <text x="725" y="46">job worker process</text><text x="725" y="64" font-size="11" opacity="0.75">one per job · own JAX runtime · CPU or GPU</text>
    <text x="725" y="121">session process</text><text x="725" y="139" font-size="11" opacity="0.75">warm compiled solver · command loop over a pipe</text>
    <text x="725" y="196">on-disk store</text><text x="725" y="214" font-size="11" opacity="0.75">request · status · events · artefacts</text>
  </g>
  <g stroke="currentColor" stroke-width="1.2" fill="none" opacity="0.8">
    <path d="M180 125 H250"/><path d="M500 50 H580"/><path d="M500 125 H580"/><path d="M500 200 H580"/>
  </g>
</svg>
<figcaption>Solver work is isolated in subprocesses; the API process stays responsive and device-free.</figcaption>
</figure>

* **Jobs** are one-shot analyses. Each runs in a fresh subprocess, so a crash or an out-of-memory
  condition cannot take the service down, the device is chosen per job, and cancelling is a
  process termination. Progress is appended to an event log and streamed as Server-Sent Events.
* **Sessions** are long-lived solver processes for design loops: kernels stay compiled, every
  solve is warm-started from the last state, and derivative products are available between
  evaluations.
* The **store** is plain files — inspectable, restart-safe, and the source of truth for the
  result cache. Mesh files that clients upload are kept in the same data directory, in the
  **mesh store**; a case refers to them by name, and no request makes the service open a path
  of its own choosing.

<!-- end level 3 -->

<!-- begin level 3 (Mechanics) -->
**[L3 Mechanics] Deployments carry their own documentation**

`tools/deploy.py up` refuses a port that is already served, builds this site from the working
tree, stamps it with the commit, starts the service on all interfaces, and smoke-tests docs,
API, a real solver job (which bypasses the result cache) and the MCP endpoint through the
tailnet address. The footer of every page shows which deployment is serving it.

<!-- end level 3 -->
