Gyredocs
Depth Practice
API

Gyre/User Guide

Service API

In one minuteThe REST API offers two ways to compute — batch jobs for one-off analyses and warm sessions for design loops — plus a mesh store for uploaded mesh files, case validation, progress streams, artefacts and metrics. Responses are JSON, except /metrics (Prometheus text), artefact downloads and the ?stream=true event stream; every error carries a type, a message and a hint.

Written at L2 Practice goes to L3 Mechanics 2583 words Markdown

Base URL: http://HOST:8600/api/v1. The interactive explorer at /api/docs lists every endpoint with its schema.

Jobs: one-shot analyses#

bash
# submit
curl -s $API/jobs -H 'content-type: application/json' -d '{
  "case": "naca0012_euler",
  "overrides": {"flow.alpha_deg": 3.0},
  "gradients": {"of": ["cl", "cd"], "wrt": ["alpha", "mach"]},
  "error_estimate": ["cd"]
}'
# → 202 {"id": "job-20261110-121500-a1b2c3", "state": "queued", "device": "cpu", "digest": "…", …}

curl -s "$API/jobs/$ID?wait=60"          # long-poll until finished (or 60 s)
curl -s  $API/jobs/$ID/result            # full result record
curl -N "$API/jobs/$ID/events?stream=true"   # live Server-Sent Events
Method and path Purpose
POST /jobs queue an analysis (case, overrides, gradients, error_estimate, adapt, device, use_cache, label)
GET /jobs recent jobs (?state=, ?limit=)
GET /jobs/{id} state, progress, result summary (?wait= seconds)
GET /jobs/{id}/result outputs, history, gradients, error estimates, surface data, provenance
GET /jobs/{id}/events progress events (?since=N, ?stream=true)
GET /jobs/{id}/artifacts/{name} result.json, solution.npz, flow.vtu, field.json, events.jsonl, error.txt
DELETE /jobs/{id} cancel

With adapt the service chooses the polynomial degree: starting at discretization.order it raises the degree, warm-starting each level from the previous one, until the adjoint-based error estimate of one output is within a tolerance.

bash
curl -s $API/jobs -H 'content-type: application/json' -d '{
  "case": "bump_euler",
  "overrides": {"discretization.order": 1},
  "adapt": {"output": "cd", "tolerance": 1e-4, "max_order": 4}
}'
adapt
output required scalar output whose estimated error drives the selection
tolerance required, > 0 absolute tolerance on that estimate
max_order 1 to 8, default 5 highest degree to try

The result is that of the final degree (spec, case, outputs, gradients, error_estimate, artefacts) and has an adaptation section: {output, tolerance, met, order, value, corrected, error, levels}, where levels has one record per degree tried. Check met: a job whose tolerance was not reached at max_order is still succeeded, with the result of the highest degree that converged. The job record carries requested.adapt and result_summary.adaptation (without levels). The event log shows every degree in turn: a case event, the iteration events of its solve (each with order), and one adapt event (the level record) that closes it; the last case event is that of the result. timing.solve is the wall time of the whole selection and timing.setup is 0.

A degree whose solve fails from the interpolated lower-degree solution is tried again before it counts as failed: from the same guess with the full continuation (skipped when the guess is not an admissible state), then from the free stream. Each further attempt is an adapt_retry event with order, reason (why the previous attempt failed) and start (interpolated or free_stream).

Field of a level record Meaning
order polynomial degree of this level
degrees_of_freedom size of the discretization at this degree
converged whether the flow solve of this degree converged
value the output at this degree
estimate adjoint-based estimate of its discretization error
corrected the output corrected by the estimate
error the estimated error that is compared with tolerance (by absolute value)
adjoint_converged whether the adjoint solve of the estimate converged
newton_steps steps of the flow solve
wall_time seconds spent on this degree, estimate included

A degree whose flow solve did not converge has order, degrees_of_freedom, converged: false, the solver’s reason and wall_time only.

met is true only when the estimate is a finite number, its adjoint solve converged and its absolute value is at most tolerance; an estimate below the tolerance from an adjoint solve that did not converge does not end the selection. The selection also ends at the first degree whose flow solve does not converge, below max_order too: met is false and order, value, corrected and error are those of the last degree that converged. If the starting degree itself does not converge, these four are null and the result has converged: false.

Unknown keys and invalid values are a 422 ValidationError. A selection that cannot start — discretization.order is 0, or max_order is below discretization.order — is a 422 InvalidCase. An output that the case does not have is found by the worker: the job fails.

queued → running → succeeded | failed | cancelled, plus interrupted for jobs that were active when the service restarted. A job identical to an earlier successful one is answered immediately from the cache with cached_from naming the original; pass "use_cache": false to force a new run. The cache key is the specification, the gradient, error-estimate and adapt requests (an omitted max_order equals 5), the contents of a mesh file the case names, and the code (version, revision and a hash of the package sources): a result computed by different code, for example before an upgrade on the same data directory, is not reused, and replacing a stored mesh under the same name is a cache miss. On a cache hit device is the device of the original run. A job that ran but did not converge is succeeded with "converged": false — check the flag. Its outputs are those of the iterate the solve ended at, and it has no gradients or error_estimate: the result and the job’s result_summary carry skipped, which names each requested section that is missing with the reason. Non-finite values (a diverged solve) are reported as null in job records, results, events and session replies.

Sessions: warm solvers for design loops#

bash
SID=$(curl -s $API/sessions -H 'content-type: application/json' \
      -d '{"case": "naca0012_euler"}' | jq -r .id)

curl -s $API/sessions/$SID/evaluate -H 'content-type: application/json' \
     -d '{"inputs": {"alpha_deg": 2.5, "mach": 0.52}}'
# → {"converged": true, "nonlinear_iterations": 5, "outputs": {"cl": 0.36, …}, "wall_time": 0.8}

curl -s $API/sessions/$SID/gradients -H 'content-type: application/json' \
     -d '{"of": ["cl"], "wrt": ["alpha_deg", "x_surface"]}'

curl -s -X DELETE $API/sessions/$SID
Method and path Purpose
POST /sessions start a solver process for a case; returns its interface (inputs, outputs, shapes)
GET /sessions, GET /sessions/{id} list / describe
POST /sessions/{id}/evaluate set inputs, converge (warm start), return outputs
POST /sessions/{id}/gradients total derivatives at the current state
POST /sessions/{id}/adjoint reverse total product for output seeds (coupling)
POST /sessions/{id}/error-estimate adjoint-weighted-residual estimates
GET /sessions/{id}/surface surface-node coordinates and current displacement
GET /sessions/{id}/distributions wall cpc_p, cfc_f
GET /sessions/{id}/field field sample for visualisation
GET /sessions/{id}/state current inputs, solved, converged and residual norm
POST /sessions/{id}/tangent forward total product for input perturbations
POST /sessions/{id}/residual norms of the residual of the session’s state, at its inputs or at given ones
POST /sessions/{id}/revert after an evaluation that did not converge: back to the last converged point
POST /sessions/{id}/get-state, …/set-state download / upload the flow state (bytes, application/octet-stream)
POST /sessions/{id}/checkpoint, …/restore save / restore inputs, surface displacement and state as one blob; a checkpoint also rebuilds a session that was closed or reaped (GyreClient.restore_session)
DELETE /sessions/{id} close

The last five rows go through one generic route, POST /sessions/{id}/{command}, which passes any command of the solver process through (- and _ are the same in the name); GET /sessions/{id} lists the commands a session supports.

One command runs at a time per session. The first evaluate compiles kernels; later ones are warm-started and typically need a handful of Newton steps. Idle sessions close after 30 minutes; at most four are open at once by default.

alpha_deg is accepted wherever alpha (radians) is: in evaluate inputs and in the wrt of /gradients, /adjoint and job gradients. The derivative is returned under the name asked for, per degree for alpha_deg. With wrt omitted the result has alpha (radians) only, and /state and the session interface list alpha. Non-finite or non-numeric inputs are refused with 422.

If an evaluation does not converge, the reply has converged: false with the outputs and residual of the iterate the solve ended at, and the session keeps its last converged state as the warm start of the next evaluation. Until an evaluation converges again, /gradients, /adjoint, /error-estimate, /distributions and /field answer 422 RuntimeError; the last two do so before the first evaluation as well. converged of /state is true when a state exists and the last evaluation converged.

case is a preset name or a CaseSpec object, for jobs, sessions and validation alike; a string is never read as a file path (file specifications are a CLI and Python feature). A case on a mesh file names a mesh of the mesh store. A session reads that mesh once, when it starts: it keeps running on it, and reports the digest of that moment, if the mesh is replaced or deleted afterwards.

Meshes#

A case with mesh: {generator: file, params: {path: NAME}} refers to a mesh that was uploaded to the service’s mesh store under NAME. Mesh files has the formats, examples for curl, Python and MCP, and the limits.

bash
curl -s -X PUT --data-binary @wing.msh -H 'content-type: application/octet-stream' \
     $API/meshes/wing.msh
# → 201 {"name": "wing.msh", "bytes": …, "sha256": "…", "dim": 2, "elements": …,
#        "geometry_order": …, "nodes": …, "boundaries": {"wall": …, "farfield": …}, …}
Method and path Purpose
GET /meshes {"meshes": [summary, …]}, sorted by name
PUT /meshes/{name} store the request body (the file as it is on disk) under name; 201 with the summary; replaces an existing mesh of that name
GET /meshes/{name} summary: name, bytes, sha256, dim, elements, geometry_order, nodes, boundaries (faces per group), bounding_box, the reader’s quality numbers
DELETE /meshes/{name} remove; {"name": name, "deleted": true}
  • A name matches ^[A-Za-z0-9][A-Za-z0-9_.-]{0,79}\.(msh|npz)$. Anything else, including a name with directories, is a 422 InvalidMeshName on every mesh route and in mesh.params.path of a case; nothing is opened or written.
  • The upload is read and validated in full. A file the reader cannot use is a 422 InvalidMesh with the reader’s message and is not stored; a mesh already stored under the name is unchanged.
  • A case that names a mesh which is not stored is a 404 UnknownMesh, at validation, job submission and session creation; the hint names the PUT to make. A case whose discretization.boundaries does not give a condition for every boundary group of the mesh, or names a group the mesh does not have, is a 422 InvalidCase.
  • An upload above GYRE_MESH_MAX_BYTES (64 MiB by default) is a 413 MeshTooLarge; a full store is a 507 MeshStoreFull (limits).
  • A file that was placed in the store’s directory by hand and that the reader rejects is listed by GET /meshes as {"name", "error"} and answered 422 InvalidMesh by GET /meshes/{name}.

Pipelines#

Method and path Purpose
GET /pipelines pipeline definitions available to the deployment (stages, parameters); each entry has runnable and, when that is false, a reason
POST /pipelines/{name}/runs start a run (params, force); unchanged stages come from the cache. String params stay strings. Every stage’s case, after the run’s parameters are substituted, must be a preset name or a CaseSpec object (404 UnknownPreset otherwise, whatever the parameter is called). Runs of one pipeline are executed one after another
GET /pipelines/runs, GET /pipelines/runs/{id} list runs / status; record is the per-stage record this run wrote, or null if the run ended before the engine finished

Definitions with shell stages are not runnable through the service (403 PipelineNotRunnable) unless it was started with GYRE_PIPELINES_ALLOW_SHELL=1. A run leads its own process group: signals to the service’s process group do not reach it, and at service shutdown each run is terminated together with its stage processes.

See Pipelines for the YAML vocabulary.

Cases, metadata, metrics#

Method and path Purpose
GET /presets, GET /presets/{name} built-in cases
GET /schema/case, GET /schema/job JSON Schemas
POST /cases/validate dry run: resolved spec, size estimate, device, warnings
GET /info version, commit, process id (pid), deployment name, uptime, documentation build
GET /health liveness
GET /metrics Prometheus text format

Errors#

Every 4xx and 5xx answer under /api/v1 has the same shape, with type, message and hint always present:

json
{"error": {"type": "UnknownPreset",
           "message": "unknown preset 'naca0013'",
           "hint": "available presets: ['bump_euler', 'cylinder_re40', …]"}}
Status type Meaning
401 Unauthorized bearer token required (GYRE_API_TOKEN is set)
403 PipelineNotRunnable the pipeline has shell stages (or an invalid definition) and may not be started through the service
404 UnknownPreset, UnknownPipeline, NotFound no such preset, pipeline, job, session, artefact, pipeline run or API route; UnknownPreset is also the answer to a pipeline run in which a stage’s case is a string that is not a preset name
404 UnknownMesh no mesh of that name in the mesh store: on the mesh routes, and for a case that names it
405 MethodNotAllowed the path exists but not with this method; for GET, POST, PUT, PATCH and DELETE the Allow header lists the methods of the path (HEAD and OPTIONS are answered 405 on every API path, with an Allow header that is not complete)
409 NotReady no result: the job has not finished, or did not succeed
410 SessionDead the session process has terminated
422 ValidationError the request does not match the route’s schema (body, query, malformed JSON), or the case specification is invalid: mesh parameters the generator does not accept, order 0 for a viscous case, missing boundary data
413 MeshTooLarge the uploaded mesh file exceeds GYRE_MESH_MAX_BYTES
422 BadOverride, InvalidCase unknown override path; an empty case; a mesh-file case whose discretization.boundaries does not match the boundary groups of the mesh; an adapt request that cannot start
422 InvalidMeshName, InvalidMesh a mesh name that is not a bare store name (in a URL or in mesh.params.path); a mesh file the reader cannot use, with the reader’s message
422 KeyError, ValueError, RuntimeError a session command failed: unknown input or output name, bad input value, no flow state yet
429 TooManySessions session limit reached
500 InternalError unexpected fault of the service. The message is the service failed while handling this request (incident <8 hex digits>); the exception type and text are in the service log (logger gyre.service) under the same incident id and are not sent to the client
500 SessionStartFailed the session process died while building the case
503 MeshStoreInactive a case that names a stored mesh was sent to an application that was not started with its start-up phase (never the case under gyre serve); the name check (422 InvalidMeshName) and the store lookup (404 UnknownMesh) come first
504 Timeout a session command, or session start-up, exceeded its time limit
507 MeshStoreFull the mesh store holds GYRE_MESH_STORE_MAX_FILES meshes or would exceed GYRE_MESH_STORE_MAX_BYTES

Session command errors carry the exception class name as type; classes other than the three listed are answered with 500. An exception raised while a new session builds its case is a 422 with the class name as type; its hint, like that of a start-up Timeout, points to POST /cases/validate, since there is no session to inspect. Case errors are reported by POST /cases/validate and, identically, when a job or session is submitted. The error object inside a failed job record is {"type", "message"} without a hint; the traceback of a worker exception is the error.txt artefact.

gyre.client.GyreClient wraps the job, session, mesh, preset, validation and info endpoints (submit, job, wait, run, result, events, artifact, cancel, session, meshes, upload_mesh, mesh, delete_mesh, presets, validate, info) and raises GyreError carrying the status and the structured error. submit(..., adapt={"output": "cd", "tolerance": 1e-4}) and run(..., adapt=...) send an order selection; upload_mesh(path, name=None) stores a local file under its own name unless name is given. Pipelines, metrics and schemas are reached with plain HTTP. See MDO integration for RemoteDiscipline.

    ↑ ↓ moveEnter open[ ] change depthResults show section and depth level