Metadata-Version: 2.5
Name: flow5ctl
Version: 0.1.3
Summary: AI-driven aircraft design with flow5 — MCP server and CLI for low-Reynolds-number aircraft
Project-URL: Homepage, https://github.com/97kuek/flow5ctl
Project-URL: Issues, https://github.com/97kuek/flow5ctl/issues
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: aerodynamics,aircraft-design,flow5,glider,mcp,uav,xflr5
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3.11
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.11
Requires-Dist: mcp>=2.1.1
Requires-Dist: pydantic>=2.7
Requires-Dist: pyyaml>=6.0
Provides-Extra: plot
Requires-Dist: matplotlib>=3.8; extra == 'plot'
Description-Content-Type: text/markdown

# flow5ctl

**AI-driven aircraft design with [flow5](https://flow5.tech).**

`flow5ctl` lets an AI agent — Claude Desktop, Claude Code, Codex, or any MCP client —
design and analyse low-Reynolds-number aircraft by driving flow5's headless batch engine.

It ships as one Python package with two front-ends:

| Front-end | Command | For |
|---|---|---|
| **MCP server** | `flow5ctl mcp` | Claude Desktop, and any MCP-capable client |
| **CLI** | `flow5ctl <verb>` | Claude Code, Codex, humans, CI |

13 tools, 7 resources and 4 prompts over MCP; the same capabilities as CLI verbs.
Both are thin adapters over one core, so neither can drift ahead of the other.

> **Status: the CLI and the MCP server both work.**
>
> Claude Desktop can design an aircraft with this today — see
> [docs/MCP.md](docs/MCP.md) for the one-line install. flow5ctl computes the geometry,
> generates and validates flow5's XML, computes 2D airfoil polars and caches them,
> drives the solver through the two passes it requires, solves trim conditions, runs
> parameter studies, and draws charts. 276 tests, 19 of them against a real flow5 7.57.
> Phases 1–3 of the [roadmap](docs/ROADMAP.md) are done.
>
> **macOS only.** Verified against **flow5 7.57** on macOS, and that is the only
> platform it is offered for. Nothing in the package is platform-specific, but every
> measured claim in `docs/` was made on macOS, and this project's failure mode is
> confident wrong numbers — so Linux and Windows are stated as unverified rather
> than assumed to work. The [verification log](docs/log/2026-09-03-poc-verification.md)
> records what was found on the way, including a reproducible flow5 crash and seven
> ways its output misleads a naive reader; re-run any of it from [`poc/`](poc).

日本語版 README: [README.ja.md](README.ja.md) ／ はじめかた: [docs/ja/QUICKSTART.md](docs/ja/QUICKSTART.md)

---

## Why

flow5 is an excellent potential-flow solver, but designing an aircraft with it is a
long loop of manual GUI work: draw a planform, pick airfoils, set up a polar, run,
read graphs, adjust, repeat. That loop is exactly what an AI agent is good at —
*if* it can drive the solver reliably.

It can. flow5 has a headless batch mode (`flow5 -s script.xml`) that runs a full
plane analysis in well under a second. What it lacks is a surface an agent can
actually use: the XML schemas are large, some required fields are silently fatal if
omitted, and the results come back as wide Unicode-headed CSVs.

`flow5ctl` is that missing surface. It is **not** a thin wrapper around the flow5
binary — that would add nothing over a shell command. It is a domain layer that:

- accepts a **high-level design description** (span, taper, airfoil, mass, CG) instead of raw XML
- computes the geometry flow5 needs but does not derive in batch mode (reference area, span, MAC)
- generates and validates every XML artifact
- runs the solver, diagnoses failures in plain language
- returns **summaries an agent can reason about** (CL slope, best L/D and where, Cm_α, static margin, neutral point) rather than raw data dumps
- keeps the whole design in a **git-friendly project directory** so humans can inspect, diff and review it

How much of that is real work rather than plumbing: flow5 **segfaults** if one script
asks for both 2D and 3D work, its polar `.csv` files contain no commas, the first row
of data is welded onto the header line, `Static margin` is a percentage that looks
like a fraction, operating-point files are duplicated into every polar's directory
carrying *another* polar's contents, and a stability request on the wrong polar type
returns eigenvalues of `5.995e+51` with a straight face. Each of those is verified,
documented, and handled.

## Who it is for

`flow5ctl` targets the whole low-Re community that already uses flow5 / XFLR5:

- **Human-powered aircraft** (鳥人間コンテスト, Daedalus-class): 30 m+ span, AR ≈ 30, Re ≈ 5×10⁵–1×10⁶, ground effect, spanwise loading, structural mass budget
- **RC gliders** (F3B / F3F / F5J, DLG): 1.5–4 m span, Re ≈ 5×10⁴–3×10⁵, camber-changing flaps, ballast, wide speed range
- **Small UAVs and model aircraft** in the same regime

Presets encode the defaults each of these needs; the underlying model is general.

## Quickstart — Claude Desktop

Install flow5 from [flow5.tech](https://flow5.tech), then add one entry to Claude
Desktop's config:

```json
{
  "mcpServers": {
    "flow5": {
      "command": "uvx",
      "args": ["--from", "flow5ctl[plot]", "flow5ctl", "mcp"]
    }
  }
}
```

Restart, and ask: *"Design a 3 m F5J glider for minimum sink, then show me what moving
the CG from 30 % to 40 % MAC does."* Full setup notes, including where designs are
kept and how to point flow5ctl at an unusual flow5 install, are in
[docs/MCP.md](docs/MCP.md).

## Quickstart — command line

Install flow5 first, from [flow5.tech](https://flow5.tech). Then:

```bash
git clone https://github.com/97kuek/flow5ctl && cd flow5ctl
uv sync                          # or: pip install -e .
uv run flow5ctl doctor           # check the flow5 installation
```

```
flow5ctl      0.1.0.dev0
flow5         7.57  /Applications/flow5.app/Contents/MacOS/flow5
              verified
workspace     ~/flow5ctl  (writable)
presets       custom, hpa, rc-glider, uav
```

Describe an aircraft, then analyse it:

```yaml
# glider.yaml
preset: rc-glider
requirements: {cruise_speed: 12.0, objective: min_sink}
mass:
  components:
    - {tag: fuselage,   mass: 0.40, at: [ 0.12,  0.00, 0.00]}
    - {tag: wing_left,  mass: 0.10, at: [ 0.05, -0.75, 0.02]}
    - {tag: wing_right, mass: 0.10, at: [ 0.05,  0.75, 0.02]}
airfoils:
  - {name: AG35, source: 'naca:2409'}
wing:
  airfoil: AG35
  planform: {span: 3.0, root_chord: 0.24, taper: 0.55, dihedral: 3.0, washout: -1.5}
```

```bash
flow5ctl init Glider --file examples/rc-glider.yaml
flow5ctl analyze Glider --type T1 --speed 12 --alpha=-2,8,2
```

The 2D airfoil polars it needs are computed automatically the first time and cached
afterwards, so the first run takes about twenty seconds and later ones under a second.

Every report says what its lift-to-drag figure **excludes** — a VLM run of a wing and
a tail returns the drag of a wing and a tail, and on a human-powered aircraft the
rigging and the fairing are a fifth to two fifths of the aeroplane again. It also
reports the wing root bending moment, which is the number a spar is sized from, with
a closed-form cross-check beside it — and says when the operating point it came from
is not level flight, because a fixed-speed polar's best-L/D point usually is not.

For an aircraft that flies in ground effect, one call does both:

```bash
flow5ctl analyze Albatross --compare-ground --ground-height 2.0
```

Run on [`examples/hpa.yaml`](examples/hpa.yaml), so you can reproduce it:

```
                      free air   in ground    change
  best L/D               41.18       50.44   +22.5 %
  min sink m/s          0.1729      0.1337   -22.7 %
  CL_alpha /deg        0.10748     0.11128    +3.5 %
```

### Solve, don't sweep

```bash
flow5ctl trim Glider --target level --speed 11          # α for level flight
flow5ctl trim Glider --target static-margin --value 0.10  # CG for a 10 % margin
flow5ctl trim Glider --target pitch --speed 11          # elevator incidence for Cm = 0
```

```
Solved
  cg_x                    0.07571
  static_margin           0.1006
  neutral_point_x         0.09476
  shift_from_current      0.02821

  A static margin of +10.0% needs the CG at x = 0.0757 m (39.7 % MAC), which is
  28 mm aft of the current CG. The neutral point is at x = 0.0948 m.
```

That one takes two solver runs rather than a bisection, because the neutral point does
not move with the CG — verified, so the second run only confirms the answer.

### Compare

```bash
flow5ctl sweep Glider --parameter cg_x --values 0.04:0.09:6 \
    --metrics static_margin,trim_alpha,ld_at_trim
```

```
         cg_x  static_margin   trim_alpha   ld_at_trim
  -----------  -------------  -----------  -----------
         0.04         0.3448       -0.368        4.466
         0.06           0.24       -0.001        6.485
         0.08         0.1351        0.924        11.14
         0.09         0.0827        2.286       17.297
```

`ld_at_trim`, not best L/D: moving the CG does not change the drag polar at all, only
where the aircraft trims. Ask for `best_LD` in a CG sweep and flow5ctl will tell you
the column is blind to the parameter you varied.

Studies are files, so a question survives a design change:

```bash
flow5ctl sweep Glider --study examples/cg-sweep.yaml
```

> Released: `pip install flow5ctl` or `uvx --from "flow5ctl[plot]" flow5ctl` —
> [pypi.org/project/flow5ctl](https://pypi.org/project/flow5ctl/). macOS only.

Claude Desktop — add to your MCP config:

```json
{
  "mcpServers": {
    "flow5": { "command": "flow5ctl", "args": ["mcp"] }
  }
}
```

Then ask: *"Design a 3 m F5J glider for minimum sink, and show me the effect of
moving the CG from 30% to 40% MAC."*

## How it works

```
                design.yaml  ← source of truth, human- and LLM-readable, in git
                     │
        flow5ctl     │  geometry solve → XML generation → validation
                     ▼
            plane.xml + polar.xml + script.xml   ← build artifacts, disposable
                     │
                     ▼
            flow5 -s script.xml                  ← headless, ~0.5 s per sweep
                     │
                     ▼
            polars.csv + oppoints/ + project.fl5
                     │
        flow5ctl     │  parse → normalise → summarise
                     ▼
            structured result + warnings  → agent
                                          → `flow5ctl open` hands the .fl5 to the GUI for a human
```

The YAML is the source; the XML is a build artifact. See
[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).

![Architecture](docs/architecture.png)

Drawn from the import graph, not from these notes — the editable source is
[docs/architecture.drawio](docs/architecture.drawio), regenerated by
[tools/gen_architecture.py](tools/gen_architecture.py). A Japanese walkthrough of the
same picture is in [docs/ARCHITECTURE-ja.md](docs/ARCHITECTURE-ja.md).

## Documentation

| Document | What it covers |
|---|---|
| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | Layers, data flow, why one core with two front-ends |
| [docs/ARCHITECTURE-ja.md](docs/ARCHITECTURE-ja.md) | 日本語の全体像。アーキテクチャ図つき |
| [docs/ja/QUICKSTART.md](docs/ja/QUICKSTART.md) | 日本語のはじめかた。ターミナル未経験者向け |
| [docs/ja/DESIGN-GUIDE.md](docs/ja/DESIGN-GUIDE.md) | 日本語の設計ガイド（DESIGN-GUIDE.md の全訳） |
| [docs/DOMAIN-MODEL.md](docs/DOMAIN-MODEL.md) | Vocabulary and the `design.yaml` schema |
| [docs/MCP.md](docs/MCP.md) | Setting up Claude Desktop, and how to read what comes back |
| [docs/MCP-TOOLS.md](docs/MCP-TOOLS.md) | The tool surface exposed to agents |
| [docs/FLOW5-INTERFACE.md](docs/FLOW5-INTERFACE.md) | Verified reference for flow5's batch/XML interface |
| [docs/DESIGN-GUIDE.md](docs/DESIGN-GUIDE.md) | Aerodynamic guardrails agents must respect |
| [docs/ROADMAP.md](docs/ROADMAP.md) | Phases and milestones |
| [docs/adr/](docs/adr/) | Architecture decision records |
| [docs/log/](docs/log/) | Investigation and verification log |
| [poc/](poc/) | The verification harness — reproduce every measured claim |
| [examples/](examples/) | Worked designs: an RC glider, an HPA, and a study |

Source layout: `src/flow5ctl/{model,geometry,advisor}` is the domain and never imports
`src/flow5ctl/flow5`, which is the only code that knows flow5 exists. `usecases/`
orchestrates; `cli.py` is a thin adapter over it, and the MCP server will be a second
one.

Contributing: [CONTRIBUTING.md](CONTRIBUTING.md) · Working with AI agents in this repo: [AGENTS.md](AGENTS.md)

## Known limitations

- **Induced drag depends on the wake length, and flow5's default is too short for a
  slender wing.** flow5 carries its wake 30 × MAC downstream, which is `30 / AR`
  spans — 0.75 spans at AR 40 — and the induced drag comes out low as a result.
  flow5ctl sets the wake in **spans** instead (20 by default), which brings an
  elliptic wing to within 0.23 % of its exact span efficiency of 1.0 at every aspect
  ratio from 10 to 50, and matches AVL to 0.2 %. **0.1.0 shipped this the other way
  round**, as a claim that flow5's induced drag is systematically wrong; it is not,
  and [the correction is recorded](docs/log/2026-09-04-the-wake-was-too-short.md).
- **Flaps and control surfaces are not supported, and cannot be.** flow5 has no flap
  or hinge elements in its plane XML — a flap belongs to flow5's Foil object, which a
  `.dat` file cannot carry, and planes loaded from a GUI-made project cannot be paired
  with new analyses. So T6 control polars are out of reach through this interface.
  This matters if you fly camber-changing RC gliders; see
  [the verification log](docs/log/2026-09-03-poc-verification.md), findings 9 and 10.
- **macOS only.** Linux and Windows are unverified and not claimed. The package is
  pure Python and has no platform-specific code, so it may well work — but flow5's
  behaviour is what this tool encodes, and none of it has been measured on another
  platform. [`poc/verify_platform.py`](poc/verify_platform.py) checks every
  documented behaviour in one command and prints a pasteable report; a run from a
  Linux or Windows user is what would change this.
- **flow5's own defects are inherited.** Dutch-roll and short-period frequencies are
  unreliable in 7.57 and are deliberately not reported.

## Relationship to flow5

flow5 is a separate project by André Deperrois, released under GPL-3.0 at
[techwinder/flow5](https://github.com/techwinder/flow5). `flow5ctl` is an
independent tool that invokes the flow5 executable as a subprocess. It does not
link flow5 code and does not redistribute it — you install flow5 yourself.
See [ADR-0006](docs/adr/0006-licensing-and-the-gpl-boundary.md).

`flow5ctl` is not affiliated with or endorsed by the flow5 project.

## License

Apache-2.0 (proposed — see [ADR-0006](docs/adr/0006-licensing-and-the-gpl-boundary.md)).
