Gyredocs
Depth Practice
API

Gyre/Examples

Inside OpenMDAO

In one minuteWrap the flow discipline as a matrix-free OpenMDAO component, in reduced space or at residual level, and let the framework assemble total derivatives in forward or reverse mode. No optimizer involved.

Written at L2 Practice goes to L3 MechanicsMDO integrators 657 words Markdown
python
import numpy as np
import openmdao.api as om

from gyre.cases import Case, preset
from gyre.integrations.openmdao import GyreFlowComp

flow = Case(preset("naca0012_euler", **{"solver.tol_rel": 1e-12})).discipline

prob = om.Problem()
prob.model.add_subsystem(
    "flow", GyreFlowComp(discipline=flow, inputs=("alpha", "mach"), outputs=("cl", "cd")),
    promotes=["*"])
prob.setup(mode="rev")
prob.set_val("alpha", np.radians(2.0))
prob.run_model()

totals = prob.compute_totals(of=["cl", "cd"], wrt=["alpha", "mach", "x_surface"])

examples/openmdao_wrapper.py is a runnable script built on this adapter (.venv/bin/python examples/openmdao_wrapper.py).

The adapter needs OpenMDAO, which the mdo extra installs (pip install -e ".[mdo]"); the all extra, which make bootstrap installs, includes it.

OpenMDAO calls Adapter does
compute set_inputs, warm-started solve (both skipped when the discipline already holds the converged solution for these inputs), outputs; raises om.AnalysisError if the flow does not converge or the inputs are outside the range of the flow model; after a failed point the next call always solves again, also for inputs that had converged before
compute_jacvec_product(mode="rev") one adjoint_product for all seeded outputs
compute_jacvec_product(mode="fwd") one tangent_product for the perturbed inputs

Options: inputs (scalar flow parameters), outputs (scalar functionals), shape=True (expose x_surface), loads=True (expose f_surface), linear_tol.

python
from gyre.integrations.openmdao import GyreFlowGroup

prob.model.add_subsystem(
    "flow", GyreFlowGroup(discipline=flow, inputs=("alpha", "mach"), outputs=("cl", "cd"),
                          loads=True),
    promotes=["*"])

GyreFlowGroup contains two components. The implicit one has the flow state as its output: OpenMDAO asks it for the residual (apply_nonlinear), for products with the linearised residual in either mode (apply_linear) and for linear solves (solve_linear), each about the state and inputs OpenMDAO holds; its solve_nonlinear is the flow solve. The second component evaluates the functionals and the load field from inputs and state. The framework then owns the coupled linear system, which is what a hierarchical or monolithic coupled adjoint needs.

OpenMDAO calls Adapter does
solve_nonlinear solve_nonlinear(inputs) of the discipline; the state becomes the component’s output
apply_nonlinear apply_nonlinear(inputs, state): the weak-form residual at OpenMDAO’s values
apply_linear (fwd / rev) jvp / vjp about that point
solve_linear (fwd / rev) solve_linear; a solve that misses linear_tol raises om.AnalysisError
OpenMDAO integrationDiscipline contract for MDO integration
quantityvalue
check_partials flow.solver: 4 (of, wrt) pairs, directional3.0315e-09
check_partials flow.functionals: 12 (of, wrt) pairs, directional5.6471e-09
totals through the implicit component (reverse) vs FlowDiscipline.totals4.1434e-16
coupled dcl/dα, OpenMDAO reverse mode7.54117
coupled dcl/dα, OpenMDAO forward mode7.54117
coupled dcl/dα, central difference of re-converged coupled analyses7.54117
relative difference, reverse mode vs finite difference1.2307e-07
dcl/dα of the rigid aerofoil7.51815
largest deflection [chords]0.00555541

gyre 0.4.0 · 39979fcf90 · · cpu · quick suite · record (JSON)Same code as this build

The table is from the discipline contract study: OpenMDAO’s own check_partials of both components, totals through the implicit component against the discipline’s adjoint totals, and the coupled derivative of a flow–structure cycle built on the group (examples/openmdao_wrapper.py) against a finite difference of re-converged coupled analyses.

tests/test_integrations.py::test_openmdao_component_totals checks that the framework’s reverse-mode totals equal Gyre’s own adjoint totals (including the shape sensitivity field), that forward mode agrees with reverse mode, and that a finite difference through the framework’s model evaluation matches. tests/test_integrations.py::test_openmdao_component_solves_again_after_a_failed_point checks that a point that does not converge raises AnalysisError and that a return to the previous, converged inputs solves again instead of reusing the cached point. For the implicit form, test_openmdao_implicit_component_passes_check_partials, test_openmdao_implicit_group_totals_match_the_reduced_space and test_openmdao_coupled_adjoint_through_the_implicit_component check the partial products, the totals mode for mode against the discipline’s own, and the coupled adjoint. The tests skip themselves when OpenMDAO is not installed; make bootstrap and the hosted CI workflow install all, so it runs there.

With loads=True and shape=True the component exposes the two coupling fields. Connected to a structural component in an OpenMDAO group with a nonlinear block solver, the framework’s linear solver assembles the coupled adjoint from the same compute_jacvec_product calls — the framework-owned version of the hand-written coupled adjoint.

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