Gyredocs
Depth Practice
API

Gyre/User Guide

Python API

In one minuteUse Gyre as a library — build a case, drive the discipline, access the discretization and its differentiable kernels directly.

Written at L2 Practice goes to L3 Mechanics 1527 words Markdown

Cases and the discipline#

python
from gyre.cases import Case, preset, load_spec

case = Case(preset("naca0012_euler", **{"flow.mach": 0.55}))   # or load_spec("case.yaml")
flow = case.discipline                                         # FlowDiscipline

hist = flow.solve()                         # converged? hist.converged, hist.steps
out = flow.outputs()                        # scalars and "f_surface"
flow.set_inputs(alpha=0.05)                 # radians; keeps the state for a warm start
flow.solve()
tot = flow.totals(of=("cl", "cd"), wrt=("alpha", "mach", "x_surface"))

examples/quickstart.py is a runnable version of this block that also prints an error estimate (.venv/bin/python examples/quickstart.py). The in-process discipline takes the angle of attack as alpha in radians and knows no alpha_deg; what each output means is defined under Outputs and functionals.

Method Returns
describe() inputs, outputs, shapes, discretization summary
set_inputs(**inputs) — (flow parameters and/or x_surface)
solve(U0=None, callback=None) SolveHistory; on failure U keeps the last converged state, and history and failed_state describe the failed attempt
set_state(U, converged=True) — (restore a saved solution, or declare an initial guess with converged=False)
outputs(names=None, state=None) dict of floats / arrays, at the current state or at state, e.g. failed_state
totals(of, wrt, mode) {"derivatives": {of: {wrt: value}}, "info": …}
tangent_product(d_inputs, outputs) forward total product
adjoint_product(seeds, wrt) reverse total product
jvp(d_inputs, d_state), vjp(residual_bar, outputs_bar) partial products
solve_linear(rhs, mode) forward / transposed linear solve
surface_coordinates() baseline surface nodes
residual_norm() current nonlinear residual
require_solution(what) — (raises RuntimeError unless the state belongs to the current inputs)

solve() reports failure in the history (hist.converged, hist.reason); it does not raise. A failed solve never replaces a converged state: flow.U remains the last converged solution, which belongs to the inputs it was converged for, and is the warm start of the next solve. flow.failed_state holds the last iterate of the failed attempt (None after a converged solve). While nothing has converged yet, the iterate also becomes flow.U.

Until a solve converges again, linearize, solve_linear, jvp, vjp, tangent_product, adjoint_product and totals raise RuntimeError. Ways on:

python
hist = flow.solve()
if not hist.converged:
    flow.outputs(("cl",), state=flow.failed_state)   # where the failed attempt ended
    flow.solve(U0=flow.failed_state)                 # continue from there, or
    flow.set_inputs(alpha=0.03); flow.solve()        # take a smaller step from the kept state

A plain repeat of flow.solve() restarts from the same kept state and fails the same way. flow.set_state(flow.failed_state, converged=False) makes the failed iterate the state and lifts the refusal: the caller then takes responsibility for differentiating it. gyre.error.estimate_error reads the state directly and does not make this check; call flow.require_solution("the error estimate") before it.

Meshes from files#

python
from gyre import meshio
from gyre.cases import Case, CaseSpec

info = meshio.inspect_mesh("examples/meshes/cylinder_channel_p3.msh")   # reads and validates
info["boundaries"]            # faces per boundary group: cylinder, inflow, outflow, walls

spec = CaseSpec.model_validate({
    "name": "cylinder_channel",
    "mesh": {"generator": "file",
             "params": {"path": "examples/meshes/cylinder_channel_p3.msh", "scale": 1.0}},
    "flow": {"mach": 0.2, "reynolds": 40.0},
    "discretization": {"order": 2, "boundaries": {
        "cylinder": "noslip_adiabatic", "inflow": "farfield",
        "outflow": "farfield", "walls": "slip_wall"}},
    "reference": {"area": 1.0, "length": 1.0, "moment_center": [0.0, 0.0]},
})
case = Case(spec)             # raises ValueError if a boundary group has no condition
Object Purpose
meshio.read_mesh(path, scale=1.0) read .msh or .npz (by extension) into a Mesh; reading validates
meshio.read_gmsh(path, scale=1.0) Gmsh MSH 2.2 or 4.1, ASCII: quadrilaterals of degree 1 to 5, hexahedra of degree 1 to 4
meshio.write_gmsh(mesh, path, volume_name="fluid") write MSH 2.2 (ASCII), periodic node pairs included
meshio.save_mesh(mesh, path), meshio.load_mesh(path, scale=1.0) Gyre NPZ format: the arrays of a Mesh, at any geometry degree
meshio.inspect_mesh(path, scale=1.0, lines=False) summary dict: dim, elements, geometry_order, nodes, boundaries, quality numbers, bounding_box, sha256, bytes; with lines=True also lines, as gyre mesh info prints it
meshio.line_summary(mesh) the lines of the line smoother on a mesh and the anisotropy of its cells
meshio.validate_mesh(mesh) quality summary; raises for inverted elements and unmatched, untagged faces
meshio.file_digest(path) SHA-256 of the file’s contents (hex); the value CaseSpec.digest() uses for a mesh file
meshio.resolve_mesh_path(name) the file a case names in mesh.params.path
meshio.MeshFormatError a ValueError: the file is not a mesh Gyre can use; the message says why
CaseSpec.mesh_file() path of the mesh file of a generator: file case, None for generators
CaseSpec.reference area (default 1 in 2-D, the span of an extruded generator mesh in 3-D), length (default 1) and moment_center (default [0.25, 0, 0]) of the force and moment coefficients; groups, the wall groups they are integrated over (default: all walls), and components, named sets of wall groups whose coefficients are additional outputs cd_<name>, … (FlowDiscipline(..., groups=(...), components={...}) when a discipline is built by hand)

mesh.params of a file case accepts path and scale only. A relative path that does not exist as given is looked up in the directory named by the environment variable GYRE_MESH_DIR; with GYRE_MESH_RESTRICT=1, which the service sets for itself and its workers, only bare file names inside that directory are accepted. CaseSpec.digest() hashes the contents of the file, not its name. Reading an .npz file (load_mesh, read_mesh, inspect_mesh) raises MeshFormatError when its arrays take more than 1 GiB uncompressed; the environment variable GYRE_MESH_MAX_EXPANDED_BYTES (a positive integer, in bytes) sets another limit, see Meshes. The boundary groups of a mesh file have no default conditions: discretization.boundaries must name a condition for every group and no group the mesh does not have.

Order selection#

python
from gyre.adapt import adaptive_order
from gyre.runner import run_case

res = adaptive_order(spec, output="cd", tolerance=1e-3, max_order=5, min_order=None)
result = run_case(spec, adapt={"output": "cd", "tolerance": 1e-3, "max_order": 5})

adaptive_order(spec, output, tolerance, max_order=5, min_order=None, callback=None) raises the polynomial degree from spec.discretization.order (or min_order) until the estimated error of output is at or below tolerance, and returns output, tolerance, met, order, value, corrected, error, levels and case, the solved Case of the final degree. The arguments are checked before any flow is solved: a tolerance that is not a positive finite number, a degree that is not an integer, a starting degree below 1, a max_order below the starting degree or above 8 raise ValueError; an output that is not a scalar output of the case raises KeyError. Per degree the callback receives a case event, the iteration events of its solve (each with order), {"event": "adapt_retry", ...} when the degree is tried again from another start, and one {"event": "adapt", ...} record that closes it.

run_case(spec, adapt={...}) does the same inside the complete result record: the result is that of the final degree and gains adaptation (the entries above without case); timing["solve"] is the wall time of the whole selection and timing["setup"] is 0. max_order may be omitted (5). Keys other than output, tolerance and max_order, a missing output or tolerance, or adapt together with case= raise ValueError. See Gradients and error estimates.

One level down: the discretization#

python
import jax, jax.numpy as jnp
from gyre import meshgen, physics as ph
from gyre.dg import Discretization

mesh = meshgen.airfoil_omesh(16, 4, pg=3)       # 64 curved elements
disc = Discretization(mesh, p=2, phys=ph.Compressible(dim=2),
                      bcs={"wall": "slip_wall", "farfield": "farfield"})
th = ph.flow_params(mach=0.5, alpha_deg=2.0)
geom = disc.geometry()                          # differentiable in the node coordinates
U = disc.uniform_state(th)
Rw = disc.residual(U, geom, th, weak=True)      # (4, 64, 3, 3)

# Derivatives are matrix-free JAX calls on the same kernel.
# Forward mode: one Jacobian-vector product for one direction dU.
Jv = jax.jvp(lambda V: disc.residual(V, geom, th, weak=True), (U,), (jnp.ones_like(U),))[1]
# Reverse mode: the gradient of ONE scalar with respect to ALL mesh nodes in one sweep.
dJ_dX = jax.grad(lambda X: disc.residual(U, disc.geometry(X), th, weak=True).sum())(disc.X0)

This block is executed by tests/test_docs_content.py. Run on its own on a four-core CPU it took 40 s with a peak memory of 0.7 GB while the machine was heavily loaded (load average above 30); a single earlier run took 15 s, under a load that was not recorded. Most of the time is compilation.

Object Purpose
gyre.physics.flow_params build the differentiable parameter dict
Discretization.jitted("residual_weak" | "geometry" | "dt_local") compiled kernels with operator tables as runtime arguments
gyre.implicit.SteadySolver, SolverOptions nonlinear solver
gyre.implicit.Hierarchy p-multigrid: setup, solve(mode="fwd" | "adj")
gyre.functionals.SurfaceOps surface forces, loads, distributions
gyre.deform.Deformer surface displacement → volume nodes
gyre.error.estimate_error adjoint-weighted-residual estimates
gyre.adapt.adaptive_order, prolong_state output-driven order selection; interpolation of a state to another degree
gyre.meshio mesh files: Gmsh reader and writer, NPZ format, inspection
gyre.mms.manufactured_source forcing for manufactured solutions
gyre.runner.run_case the end-to-end record used by the service
gyre.io.write_vtu, save_checkpoint output

Import gyre before using JAX: it enables double precision and applies GYRE_DEVICE. To pin a process to the CPU set GYRE_DEVICE=cpu in the environment before the import.

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