flow5ctl 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flow5ctl-0.1.0/.editorconfig +21 -0
- flow5ctl-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +43 -0
- flow5ctl-0.1.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
- flow5ctl-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +28 -0
- flow5ctl-0.1.0/.github/ISSUE_TEMPLATE/physics_issue.yml +47 -0
- flow5ctl-0.1.0/.github/ISSUE_TEMPLATE/platform_report.yml +54 -0
- flow5ctl-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +33 -0
- flow5ctl-0.1.0/.github/workflows/ci.yml +48 -0
- flow5ctl-0.1.0/.github/workflows/release.yaml +96 -0
- flow5ctl-0.1.0/.gitignore +60 -0
- flow5ctl-0.1.0/AGENTS.md +191 -0
- flow5ctl-0.1.0/CHANGELOG.md +348 -0
- flow5ctl-0.1.0/CLAUDE.md +79 -0
- flow5ctl-0.1.0/CODE_OF_CONDUCT.md +43 -0
- flow5ctl-0.1.0/CONTRIBUTING.md +147 -0
- flow5ctl-0.1.0/LICENSE +202 -0
- flow5ctl-0.1.0/PKG-INFO +348 -0
- flow5ctl-0.1.0/README.ja.md +268 -0
- flow5ctl-0.1.0/README.md +326 -0
- flow5ctl-0.1.0/SECURITY.md +44 -0
- flow5ctl-0.1.0/docs/ARCHITECTURE-ja.md +188 -0
- flow5ctl-0.1.0/docs/ARCHITECTURE.md +197 -0
- flow5ctl-0.1.0/docs/DESIGN-GUIDE.md +461 -0
- flow5ctl-0.1.0/docs/DOMAIN-MODEL.md +216 -0
- flow5ctl-0.1.0/docs/FLOW5-INTERFACE.md +918 -0
- flow5ctl-0.1.0/docs/MCP-TOOLS.md +363 -0
- flow5ctl-0.1.0/docs/MCP.md +140 -0
- flow5ctl-0.1.0/docs/RELEASING.md +134 -0
- flow5ctl-0.1.0/docs/ROADMAP.md +222 -0
- flow5ctl-0.1.0/docs/adr/0001-drive-flow5-via-the-xml-script-interface.md +54 -0
- flow5ctl-0.1.0/docs/adr/0002-one-core-two-frontends.md +46 -0
- flow5ctl-0.1.0/docs/adr/0003-file-based-project-state.md +51 -0
- flow5ctl-0.1.0/docs/adr/0004-summarise-results-not-raw-data.md +40 -0
- flow5ctl-0.1.0/docs/adr/0005-compute-reference-dimensions-ourselves.md +48 -0
- flow5ctl-0.1.0/docs/adr/0006-licensing-and-the-gpl-boundary.md +46 -0
- flow5ctl-0.1.0/docs/adr/0007-flow5-version-compatibility.md +42 -0
- flow5ctl-0.1.0/docs/adr/0008-python-and-distribution.md +50 -0
- flow5ctl-0.1.0/docs/adr/0009-two-pass-solver-invocation.md +51 -0
- flow5ctl-0.1.0/docs/adr/0010-treat-solver-output-as-hostile.md +56 -0
- flow5ctl-0.1.0/docs/architecture.drawio +96 -0
- flow5ctl-0.1.0/docs/architecture.png +0 -0
- flow5ctl-0.1.0/docs/architecture.svg +3 -0
- flow5ctl-0.1.0/docs/ja/DESIGN-GUIDE.md +461 -0
- flow5ctl-0.1.0/docs/ja/QUICKSTART.md +174 -0
- flow5ctl-0.1.0/docs/log/2026-09-03-feasibility-spike.md +159 -0
- flow5ctl-0.1.0/docs/log/2026-09-03-poc-verification.md +209 -0
- flow5ctl-0.1.0/docs/log/2026-09-04-induced-drag-against-avl.md +200 -0
- flow5ctl-0.1.0/docs/log/2026-09-04-induced-drag-and-the-mesh.md +123 -0
- flow5ctl-0.1.0/examples/cg-sweep.yaml +27 -0
- flow5ctl-0.1.0/examples/hpa.yaml +61 -0
- flow5ctl-0.1.0/examples/rc-glider.yaml +56 -0
- flow5ctl-0.1.0/poc/README.md +80 -0
- flow5ctl-0.1.0/poc/case_a_geometry.py +29 -0
- flow5ctl-0.1.0/poc/case_b_foil.py +51 -0
- flow5ctl-0.1.0/poc/case_c_bisect.py +63 -0
- flow5ctl-0.1.0/poc/case_c_viscous.py +54 -0
- flow5ctl-0.1.0/poc/case_d_twopass.py +65 -0
- flow5ctl-0.1.0/poc/case_e_polartypes.py +60 -0
- flow5ctl-0.1.0/poc/case_f_glider2pass.py +97 -0
- flow5ctl-0.1.0/poc/case_g_wide.py +64 -0
- flow5ctl-0.1.0/poc/case_h_hpa.py +95 -0
- flow5ctl-0.1.0/poc/case_i_inertia_body_multi.py +114 -0
- flow5ctl-0.1.0/poc/case_j_project.py +49 -0
- flow5ctl-0.1.0/poc/lib/f5.py +87 -0
- flow5ctl-0.1.0/poc/lib/gen.py +155 -0
- flow5ctl-0.1.0/poc/lib/parse.py +128 -0
- flow5ctl-0.1.0/poc/verify_platform.py +191 -0
- flow5ctl-0.1.0/pyproject.toml +74 -0
- flow5ctl-0.1.0/src/flow5ctl/__init__.py +35 -0
- flow5ctl-0.1.0/src/flow5ctl/__main__.py +4 -0
- flow5ctl-0.1.0/src/flow5ctl/advisor/__init__.py +0 -0
- flow5ctl-0.1.0/src/flow5ctl/advisor/dragbudget.py +155 -0
- flow5ctl-0.1.0/src/flow5ctl/advisor/guardrails.py +451 -0
- flow5ctl-0.1.0/src/flow5ctl/advisor/stability.py +81 -0
- flow5ctl-0.1.0/src/flow5ctl/advisor/structure.py +159 -0
- flow5ctl-0.1.0/src/flow5ctl/cli.py +771 -0
- flow5ctl-0.1.0/src/flow5ctl/errors.py +55 -0
- flow5ctl-0.1.0/src/flow5ctl/flow5/__init__.py +0 -0
- flow5ctl-0.1.0/src/flow5ctl/flow5/airfoils.py +175 -0
- flow5ctl-0.1.0/src/flow5ctl/flow5/foilpolar.py +121 -0
- flow5ctl-0.1.0/src/flow5ctl/flow5/markers.py +293 -0
- flow5ctl-0.1.0/src/flow5ctl/flow5/probe.py +141 -0
- flow5ctl-0.1.0/src/flow5ctl/flow5/results.py +309 -0
- flow5ctl-0.1.0/src/flow5ctl/flow5/runner.py +149 -0
- flow5ctl-0.1.0/src/flow5ctl/flow5/summary.py +427 -0
- flow5ctl-0.1.0/src/flow5ctl/flow5/xmlgen.py +422 -0
- flow5ctl-0.1.0/src/flow5ctl/geometry/__init__.py +0 -0
- flow5ctl-0.1.0/src/flow5ctl/geometry/derived.py +279 -0
- flow5ctl-0.1.0/src/flow5ctl/geometry/massprops.py +87 -0
- flow5ctl-0.1.0/src/flow5ctl/geometry/planform.py +254 -0
- flow5ctl-0.1.0/src/flow5ctl/mcp_server.py +615 -0
- flow5ctl-0.1.0/src/flow5ctl/model/__init__.py +0 -0
- flow5ctl-0.1.0/src/flow5ctl/model/design.py +280 -0
- flow5ctl-0.1.0/src/flow5ctl/model/presets.py +74 -0
- flow5ctl-0.1.0/src/flow5ctl/presets/custom.yaml +34 -0
- flow5ctl-0.1.0/src/flow5ctl/presets/hpa.yaml +53 -0
- flow5ctl-0.1.0/src/flow5ctl/presets/rc-glider.yaml +42 -0
- flow5ctl-0.1.0/src/flow5ctl/presets/uav.yaml +40 -0
- flow5ctl-0.1.0/src/flow5ctl/project/__init__.py +0 -0
- flow5ctl-0.1.0/src/flow5ctl/project/store.py +262 -0
- flow5ctl-0.1.0/src/flow5ctl/units.py +53 -0
- flow5ctl-0.1.0/src/flow5ctl/usecases/__init__.py +0 -0
- flow5ctl-0.1.0/src/flow5ctl/usecases/analyze.py +634 -0
- flow5ctl-0.1.0/src/flow5ctl/usecases/define.py +124 -0
- flow5ctl-0.1.0/src/flow5ctl/usecases/edit.py +320 -0
- flow5ctl-0.1.0/src/flow5ctl/usecases/ground.py +129 -0
- flow5ctl-0.1.0/src/flow5ctl/usecases/gui.py +50 -0
- flow5ctl-0.1.0/src/flow5ctl/usecases/plot.py +95 -0
- flow5ctl-0.1.0/src/flow5ctl/usecases/sweep.py +385 -0
- flow5ctl-0.1.0/src/flow5ctl/usecases/trim.py +371 -0
- flow5ctl-0.1.0/src/flow5ctl/viz/__init__.py +0 -0
- flow5ctl-0.1.0/src/flow5ctl/viz/charts.py +374 -0
- flow5ctl-0.1.0/src/flow5ctl/viz/palette.py +71 -0
- flow5ctl-0.1.0/tests/conftest.py +29 -0
- flow5ctl-0.1.0/tests/fixtures/README.md +27 -0
- flow5ctl-0.1.0/tests/fixtures/foilpolar_csv.csv +47 -0
- flow5ctl-0.1.0/tests/fixtures/foilpolar_xfoil.txt +48 -0
- flow5ctl-0.1.0/tests/fixtures/oppoint_strips.csv +67 -0
- flow5ctl-0.1.0/tests/fixtures/polar_t1_rectwing.csv +36 -0
- flow5ctl-0.1.0/tests/fixtures/polar_t2_single_point.csv +28 -0
- flow5ctl-0.1.0/tests/fixtures/polar_t5_anhedral.csv +35 -0
- flow5ctl-0.1.0/tests/fixtures/polar_t5_beta.csv +37 -0
- flow5ctl-0.1.0/tests/fixtures/polar_t5_finaft.csv +35 -0
- flow5ctl-0.1.0/tests/fixtures/polar_t5_finfwd.csv +35 -0
- flow5ctl-0.1.0/tests/fixtures/polar_t7_inf.csv +37 -0
- flow5ctl-0.1.0/tests/test_charts.py +192 -0
- flow5ctl-0.1.0/tests/test_edit.py +368 -0
- flow5ctl-0.1.0/tests/test_end_to_end.py +264 -0
- flow5ctl-0.1.0/tests/test_geometry.py +331 -0
- flow5ctl-0.1.0/tests/test_guardrails.py +771 -0
- flow5ctl-0.1.0/tests/test_mcp_server.py +206 -0
- flow5ctl-0.1.0/tests/test_mcp_stdio.py +154 -0
- flow5ctl-0.1.0/tests/test_platform.py +42 -0
- flow5ctl-0.1.0/tests/test_project.py +177 -0
- flow5ctl-0.1.0/tests/test_results_parser.py +225 -0
- flow5ctl-0.1.0/tests/test_summary.py +245 -0
- flow5ctl-0.1.0/tests/test_trim_sweep.py +479 -0
- flow5ctl-0.1.0/tests/test_units.py +38 -0
- flow5ctl-0.1.0/tests/test_xmlgen.py +335 -0
- flow5ctl-0.1.0/tools/check_docs.py +132 -0
- flow5ctl-0.1.0/tools/gen_architecture.py +144 -0
- flow5ctl-0.1.0/uv.lock +1531 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
root = true
|
|
2
|
+
|
|
3
|
+
[*]
|
|
4
|
+
charset = utf-8
|
|
5
|
+
end_of_line = lf
|
|
6
|
+
insert_final_newline = true
|
|
7
|
+
trim_trailing_whitespace = true
|
|
8
|
+
indent_style = space
|
|
9
|
+
indent_size = 4
|
|
10
|
+
|
|
11
|
+
[*.{yml,yaml,json,toml}]
|
|
12
|
+
indent_size = 2
|
|
13
|
+
|
|
14
|
+
[*.md]
|
|
15
|
+
# trailing whitespace is significant for markdown line breaks
|
|
16
|
+
trim_trailing_whitespace = false
|
|
17
|
+
|
|
18
|
+
[*.{dat,csv,stl}]
|
|
19
|
+
# solver data files — leave exactly as produced
|
|
20
|
+
trim_trailing_whitespace = false
|
|
21
|
+
insert_final_newline = false
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: flow5ctl crashed, failed, or did something unexpected
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: what-happened
|
|
7
|
+
attributes:
|
|
8
|
+
label: What happened?
|
|
9
|
+
validations:
|
|
10
|
+
required: true
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: expected
|
|
13
|
+
attributes:
|
|
14
|
+
label: What did you expect?
|
|
15
|
+
validations:
|
|
16
|
+
required: true
|
|
17
|
+
- type: textarea
|
|
18
|
+
id: repro
|
|
19
|
+
attributes:
|
|
20
|
+
label: Reproduction
|
|
21
|
+
description: Commands or MCP calls, plus the smallest `design.yaml` that shows it.
|
|
22
|
+
validations:
|
|
23
|
+
required: true
|
|
24
|
+
- type: input
|
|
25
|
+
id: flow5-version
|
|
26
|
+
attributes:
|
|
27
|
+
label: flow5 version (`flow5 --version`)
|
|
28
|
+
placeholder: flow5 v7.57
|
|
29
|
+
validations:
|
|
30
|
+
required: true
|
|
31
|
+
- type: input
|
|
32
|
+
id: os
|
|
33
|
+
attributes:
|
|
34
|
+
label: Operating system
|
|
35
|
+
validations:
|
|
36
|
+
required: true
|
|
37
|
+
- type: dropdown
|
|
38
|
+
id: frontend
|
|
39
|
+
attributes:
|
|
40
|
+
label: How were you using it?
|
|
41
|
+
options: ["MCP (Claude Desktop or other)", "CLI", "Both", "Neither / library"]
|
|
42
|
+
validations:
|
|
43
|
+
required: true
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
blank_issues_enabled: true
|
|
2
|
+
contact_links:
|
|
3
|
+
- name: flow5 itself (the solver)
|
|
4
|
+
url: https://github.com/techwinder/flow5/issues
|
|
5
|
+
about: Bugs in flow5 belong upstream. Please also tell us, so we can record it in the compatibility matrix.
|
|
6
|
+
- name: Discuss the design
|
|
7
|
+
url: https://github.com/97kuek/flow5ctl/blob/main/docs/ARCHITECTURE.md
|
|
8
|
+
about: The project is in its design phase. If a decision looks wrong, open a normal issue and say why.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Something flow5ctl should be able to do
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: workflow
|
|
7
|
+
attributes:
|
|
8
|
+
label: What are you trying to do?
|
|
9
|
+
description: |
|
|
10
|
+
Describe the design task, not the feature. What is the loop you repeat by
|
|
11
|
+
hand today? That is what this project is trying to remove.
|
|
12
|
+
validations:
|
|
13
|
+
required: true
|
|
14
|
+
- type: textarea
|
|
15
|
+
id: proposal
|
|
16
|
+
attributes:
|
|
17
|
+
label: What would you like flow5ctl to do?
|
|
18
|
+
- type: dropdown
|
|
19
|
+
id: aircraft
|
|
20
|
+
attributes:
|
|
21
|
+
label: What do you design?
|
|
22
|
+
options:
|
|
23
|
+
- Human-powered aircraft (Birdman Rally / HPA)
|
|
24
|
+
- RC glider (F3B / F3F / F5J / DLG)
|
|
25
|
+
- Small UAV
|
|
26
|
+
- Other / several
|
|
27
|
+
validations:
|
|
28
|
+
required: true
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Physics / wrong result
|
|
2
|
+
description: A number flow5ctl produced is aerodynamically wrong or misleading
|
|
3
|
+
labels: ["physics"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: |
|
|
8
|
+
**These are our most serious bugs.** People build aircraft from this, and some
|
|
9
|
+
of those aircraft carry a pilot. A plausible wrong number is worse than a crash.
|
|
10
|
+
- type: textarea
|
|
11
|
+
id: what
|
|
12
|
+
attributes:
|
|
13
|
+
label: What was reported, and what should it be?
|
|
14
|
+
placeholder: |
|
|
15
|
+
flow5ctl reported a static margin of 29.8 %.
|
|
16
|
+
Hand calculation from the neutral point gives 8.2 %.
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
- type: textarea
|
|
20
|
+
id: how-you-know
|
|
21
|
+
attributes:
|
|
22
|
+
label: How do you know the correct value?
|
|
23
|
+
description: Hand calculation, a second solver, wind-tunnel or flight data, published results.
|
|
24
|
+
validations:
|
|
25
|
+
required: true
|
|
26
|
+
- type: textarea
|
|
27
|
+
id: setup
|
|
28
|
+
attributes:
|
|
29
|
+
label: Design and analysis setup
|
|
30
|
+
description: Your `design.yaml` (or the smallest version that reproduces it) and the analysis conditions.
|
|
31
|
+
render: yaml
|
|
32
|
+
validations:
|
|
33
|
+
required: true
|
|
34
|
+
- type: input
|
|
35
|
+
id: flow5-version
|
|
36
|
+
attributes:
|
|
37
|
+
label: flow5 version
|
|
38
|
+
description: Output of `flow5 --version` — not the version shown by the app bundle.
|
|
39
|
+
placeholder: flow5 v7.57
|
|
40
|
+
validations:
|
|
41
|
+
required: true
|
|
42
|
+
- type: input
|
|
43
|
+
id: os
|
|
44
|
+
attributes:
|
|
45
|
+
label: Operating system
|
|
46
|
+
validations:
|
|
47
|
+
required: true
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Platform report
|
|
2
|
+
description: Tell us whether flow5ctl works on your OS and flow5 version
|
|
3
|
+
labels: ["platform"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: |
|
|
8
|
+
Everything in [docs/FLOW5-INTERFACE.md](../blob/main/docs/FLOW5-INTERFACE.md)
|
|
9
|
+
was verified on macOS with flow5 7.57. Linux and Windows are untested, and
|
|
10
|
+
reports are how we find out what we cannot check ourselves.
|
|
11
|
+
|
|
12
|
+
The quickest useful report is one command:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
python3 poc/verify_platform.py
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
It runs flow5 a few times, checks each documented behaviour, and prints a
|
|
19
|
+
report to paste below. It needs only the standard library and an installed
|
|
20
|
+
flow5, and writes nothing outside a temporary directory.
|
|
21
|
+
- type: input
|
|
22
|
+
id: os
|
|
23
|
+
attributes:
|
|
24
|
+
label: Operating system and version
|
|
25
|
+
validations:
|
|
26
|
+
required: true
|
|
27
|
+
- type: input
|
|
28
|
+
id: flow5-version
|
|
29
|
+
attributes:
|
|
30
|
+
label: flow5 version (`flow5 --version`)
|
|
31
|
+
validations:
|
|
32
|
+
required: true
|
|
33
|
+
- type: input
|
|
34
|
+
id: install
|
|
35
|
+
attributes:
|
|
36
|
+
label: How did you install flow5?
|
|
37
|
+
placeholder: Homebrew cask / .deb / installer / built from source
|
|
38
|
+
- type: textarea
|
|
39
|
+
id: results
|
|
40
|
+
attributes:
|
|
41
|
+
label: Output of `python3 poc/verify_platform.py`
|
|
42
|
+
description: Paste it whole, including the header lines.
|
|
43
|
+
render: text
|
|
44
|
+
validations:
|
|
45
|
+
required: true
|
|
46
|
+
- type: textarea
|
|
47
|
+
id: differences
|
|
48
|
+
attributes:
|
|
49
|
+
label: Anything else worth knowing?
|
|
50
|
+
description: |
|
|
51
|
+
Where flow5 installed itself, whether it needed a display, and anything the
|
|
52
|
+
script did not cover. A `[ info ]` line saying this platform does NOT crash on
|
|
53
|
+
a combined script is especially interesting — it would mean flow5ctl's two-pass
|
|
54
|
+
design is unnecessary there.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
## What and why
|
|
2
|
+
|
|
3
|
+
<!-- What does this change, and what problem does it solve? Link the issue if there is one. -->
|
|
4
|
+
|
|
5
|
+
## Evidence
|
|
6
|
+
|
|
7
|
+
<!-- Required for any claim about flow5's behaviour or about a physical result. -->
|
|
8
|
+
|
|
9
|
+
- [ ] I verified this by **running** it (say which flow5 version and OS)
|
|
10
|
+
- [ ] I verified this by **reading upstream source** (cite the file)
|
|
11
|
+
- [ ] Not applicable — this change makes no claim about flow5 or about physics
|
|
12
|
+
|
|
13
|
+
flow5 version used: <!-- `flow5 --version` output -->
|
|
14
|
+
OS: <!-- -->
|
|
15
|
+
|
|
16
|
+
## Checklist
|
|
17
|
+
|
|
18
|
+
- [ ] No flow5 source code is copied into this repository
|
|
19
|
+
([ADR-0006](../docs/adr/0006-licensing-and-the-gpl-boundary.md))
|
|
20
|
+
- [ ] No solver output or generated artifacts are committed (`poc/work/`, `build/`)
|
|
21
|
+
- [ ] Any new fact in `docs/FLOW5-INTERFACE.md` is marked **[run]** or **[src]** and cited
|
|
22
|
+
- [ ] If this changes an accepted decision, a new ADR supersedes the old one
|
|
23
|
+
- [ ] If this touches geometry or the result parser, golden tests were updated
|
|
24
|
+
deliberately — and the PR says why the old values were wrong
|
|
25
|
+
- [ ] Both front-ends are in step (MCP and CLI), with `--json` matching the MCP payload
|
|
26
|
+
|
|
27
|
+
## Physics
|
|
28
|
+
|
|
29
|
+
<!-- Delete if this change cannot affect a number a user might act on. -->
|
|
30
|
+
|
|
31
|
+
- [ ] I have stated the limits that apply to any result this produces
|
|
32
|
+
- [ ] This does not let a stability figure come from a non-T7 polar
|
|
33
|
+
- [ ] This does not present a non-finite value as a result
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
checks:
|
|
13
|
+
name: Repository checks
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v5
|
|
17
|
+
- uses: actions/setup-python@v6
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
|
|
21
|
+
# Runs without flow5 installed.
|
|
22
|
+
- name: Docs, licence and artifact hygiene
|
|
23
|
+
run: python3 tools/check_docs.py
|
|
24
|
+
|
|
25
|
+
test:
|
|
26
|
+
name: Tests (Python ${{ matrix.python }})
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
strategy:
|
|
29
|
+
fail-fast: false
|
|
30
|
+
matrix:
|
|
31
|
+
python: ["3.11", "3.12", "3.13"]
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v5
|
|
34
|
+
- uses: astral-sh/setup-uv@v6
|
|
35
|
+
with:
|
|
36
|
+
enable-cache: true
|
|
37
|
+
- name: Install
|
|
38
|
+
run: uv sync --group dev --extra plot
|
|
39
|
+
|
|
40
|
+
- name: Lint
|
|
41
|
+
run: uv run ruff check src tests tools poc
|
|
42
|
+
|
|
43
|
+
# flow5 is not available on CI runners, so the solver-dependent tests are
|
|
44
|
+
# deselected. They run against a real flow5 on a developer machine — see
|
|
45
|
+
# poc/README.md. The rest of the suite must pass without it, which is what
|
|
46
|
+
# keeps the domain layer independent of the adapter (ADR-0002).
|
|
47
|
+
- name: Tests
|
|
48
|
+
run: uv run pytest -q -m "not needs_flow5"
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publishing is deliberately tag-triggered rather than automatic on main: a release
|
|
4
|
+
# is a decision, not a side effect of merging. See docs/RELEASING.md.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
tags: ["v*"]
|
|
8
|
+
# Validate everything a release runs, without cutting a tag. The publish and
|
|
9
|
+
# release jobs below are guarded to tags, so a dispatch builds and checks only.
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
build:
|
|
17
|
+
name: Build and check
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v5
|
|
21
|
+
- uses: astral-sh/setup-uv@v6
|
|
22
|
+
with:
|
|
23
|
+
enable-cache: true
|
|
24
|
+
|
|
25
|
+
# The tag and the packaged version must agree. Getting this wrong publishes
|
|
26
|
+
# something no tag points at, and PyPI will not let the filename be reused.
|
|
27
|
+
- name: Tag matches the packaged version
|
|
28
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
29
|
+
run: |
|
|
30
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
31
|
+
version="$(uv run --no-project --with hatchling python -c \
|
|
32
|
+
'import tomllib,pathlib;print(tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"])')"
|
|
33
|
+
echo "tag=$tag pyproject=$version"
|
|
34
|
+
[ "$tag" = "$version" ] || { echo "::error::tag $tag does not match version $version"; exit 1; }
|
|
35
|
+
|
|
36
|
+
- name: Install
|
|
37
|
+
run: uv sync --group dev --extra plot
|
|
38
|
+
|
|
39
|
+
- name: Lint
|
|
40
|
+
run: uv run ruff check src tests tools poc
|
|
41
|
+
|
|
42
|
+
# flow5 is not on CI runners, so the solver tests are deselected here exactly
|
|
43
|
+
# as in ci.yml. A release must not be cut on a red tree.
|
|
44
|
+
- name: Tests
|
|
45
|
+
run: uv run pytest -q -m "not needs_flow5"
|
|
46
|
+
|
|
47
|
+
- name: Repository checks
|
|
48
|
+
run: python3 tools/check_docs.py
|
|
49
|
+
|
|
50
|
+
- name: Build
|
|
51
|
+
run: uv build
|
|
52
|
+
|
|
53
|
+
# Catches a malformed README before PyPI rejects the upload.
|
|
54
|
+
- name: Check metadata
|
|
55
|
+
run: uvx twine check dist/*
|
|
56
|
+
|
|
57
|
+
- uses: actions/upload-artifact@v5
|
|
58
|
+
with:
|
|
59
|
+
name: dist
|
|
60
|
+
path: dist/
|
|
61
|
+
|
|
62
|
+
publish:
|
|
63
|
+
name: Publish to PyPI
|
|
64
|
+
needs: build
|
|
65
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
environment: pypi
|
|
68
|
+
permissions:
|
|
69
|
+
id-token: write # PyPI trusted publishing; no API token is stored anywhere
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/download-artifact@v5
|
|
72
|
+
with:
|
|
73
|
+
name: dist
|
|
74
|
+
path: dist/
|
|
75
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
76
|
+
|
|
77
|
+
github_release:
|
|
78
|
+
name: GitHub release
|
|
79
|
+
needs: publish
|
|
80
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
81
|
+
runs-on: ubuntu-latest
|
|
82
|
+
permissions:
|
|
83
|
+
contents: write
|
|
84
|
+
steps:
|
|
85
|
+
- uses: actions/checkout@v5
|
|
86
|
+
- uses: actions/download-artifact@v5
|
|
87
|
+
with:
|
|
88
|
+
name: dist
|
|
89
|
+
path: dist/
|
|
90
|
+
- name: Create the release
|
|
91
|
+
env:
|
|
92
|
+
GH_TOKEN: ${{ github.token }}
|
|
93
|
+
run: |
|
|
94
|
+
gh release create "$GITHUB_REF_NAME" dist/* \
|
|
95
|
+
--title "$GITHUB_REF_NAME" \
|
|
96
|
+
--notes "See [CHANGELOG.md](https://github.com/${GITHUB_REPOSITORY}/blob/main/CHANGELOG.md)."
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# ── flow5ctl generated artifacts ────────────────────────────────────
|
|
2
|
+
# design.yaml is the source of truth; everything below is regenerable.
|
|
3
|
+
build/
|
|
4
|
+
results/
|
|
5
|
+
.flow5ctl/
|
|
6
|
+
*.fl5
|
|
7
|
+
*.stl
|
|
8
|
+
|
|
9
|
+
# ── PoC verification workspace ──────────────────────────────────────
|
|
10
|
+
# poc/lib and poc/case_*.py are the reproducible harness and ARE tracked.
|
|
11
|
+
# Everything they produce is not.
|
|
12
|
+
poc/work/
|
|
13
|
+
poc/out/
|
|
14
|
+
poc/*_run.log
|
|
15
|
+
|
|
16
|
+
# ── Validation against real aircraft ────────────────────────────────
|
|
17
|
+
# Reconstructions of other teams' aircraft, and the logs comparing flow5ctl
|
|
18
|
+
# against their published data, stay out of the public repository. They are
|
|
19
|
+
# someone else's design work; publishing them is not ours to decide.
|
|
20
|
+
# Findings about flow5's own behaviour DO belong in docs/ — anonymised.
|
|
21
|
+
validation/
|
|
22
|
+
examples/*-real-*.yaml
|
|
23
|
+
|
|
24
|
+
# Real flow5 project files kept as references for what a practitioner's own model
|
|
25
|
+
# contains — mesh density, surface counts, airfoil choices. Reading one is how the
|
|
26
|
+
# Reynolds ladder default got fixed. They are someone's actual aircraft, so they
|
|
27
|
+
# stay local; what is learned from them goes into docs/ as a finding.
|
|
28
|
+
*.xfl
|
|
29
|
+
*.fl5
|
|
30
|
+
|
|
31
|
+
# Upstream flow5 source fetched for reference only.
|
|
32
|
+
# flow5 is GPL-3.0 and must never be committed here — see
|
|
33
|
+
# docs/adr/0006-licensing-and-the-gpl-boundary.md
|
|
34
|
+
poc/ref/
|
|
35
|
+
|
|
36
|
+
# ── Python ──────────────────────────────────────────────────────────
|
|
37
|
+
# uv.lock IS committed: it makes CI and a contributor's environment reproducible,
|
|
38
|
+
# and flow5ctl is installed as a tool more often than depended on as a library.
|
|
39
|
+
__pycache__/
|
|
40
|
+
*.py[cod]
|
|
41
|
+
*.egg-info/
|
|
42
|
+
.venv/
|
|
43
|
+
venv/
|
|
44
|
+
.env
|
|
45
|
+
dist/
|
|
46
|
+
build-py/
|
|
47
|
+
.pytest_cache/
|
|
48
|
+
.ruff_cache/
|
|
49
|
+
.mypy_cache/
|
|
50
|
+
.coverage
|
|
51
|
+
htmlcov/
|
|
52
|
+
|
|
53
|
+
# ── Editors / OS ────────────────────────────────────────────────────
|
|
54
|
+
.DS_Store
|
|
55
|
+
Thumbs.db
|
|
56
|
+
.idea/
|
|
57
|
+
.vscode/*
|
|
58
|
+
!.vscode/extensions.json
|
|
59
|
+
*.swp
|
|
60
|
+
*~
|
flow5ctl-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Instructions for AI agents working **on** this repository.
|
|
4
|
+
|
|
5
|
+
> If you are an agent **using** flow5ctl to design an aircraft, this is the wrong
|
|
6
|
+
> file. Read [docs/DESIGN-GUIDE.md](docs/DESIGN-GUIDE.md) instead.
|
|
7
|
+
|
|
8
|
+
## What this project is
|
|
9
|
+
|
|
10
|
+
`flow5ctl` lets AI agents design low-Reynolds-number aircraft by driving
|
|
11
|
+
[flow5](https://flow5.tech), a potential-flow solver, in its headless batch mode.
|
|
12
|
+
It ships as an MCP server and a CLI over one shared core.
|
|
13
|
+
|
|
14
|
+
The core, the CLI and the MCP server all work. See
|
|
15
|
+
[the roadmap](docs/ROADMAP.md) for what is left.
|
|
16
|
+
|
|
17
|
+
## Layout
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
src/flow5ctl/
|
|
21
|
+
model/ design.yaml schema, presets
|
|
22
|
+
geometry/ areas, span, MAC, mass properties ← no flow5 knowledge
|
|
23
|
+
advisor/ guardrails and thresholds ← no flow5 knowledge
|
|
24
|
+
viz/ chart rendering and the validated palette
|
|
25
|
+
flow5/ probe, xmlgen, runner, results, summary ← the ONLY flow5-aware package
|
|
26
|
+
usecases/ define, edit, analyze, trim, sweep, plot, gui ← the only orchestrators
|
|
27
|
+
presets/ *.yaml — data, so a new aircraft class needs no code
|
|
28
|
+
cli.py a thin adapter over the use cases
|
|
29
|
+
mcp_server.py the second thin adapter; no domain logic, no flow5 knowledge
|
|
30
|
+
poc/ the verification harness that produced the measured claims in docs/
|
|
31
|
+
examples/ worked designs and a study, used as documentation
|
|
32
|
+
tests/ golden values and real flow5 output; fixtures/ pins the parser traps
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The dependency rule is one way: `cli / mcp_server → usecases → geometry/advisor/model`,
|
|
36
|
+
with `flow5/` reachable only from `usecases/`. Nothing in `geometry/`, `advisor/` or
|
|
37
|
+
`model/` may import `flow5/`, so the aerodynamic model stays testable with flow5
|
|
38
|
+
absent — and CI relies on that.
|
|
39
|
+
|
|
40
|
+
The two front-ends must stay in step. A capability added to one and not the other is
|
|
41
|
+
an incomplete change ([ADR-0002](docs/adr/0002-one-core-two-frontends.md)).
|
|
42
|
+
|
|
43
|
+
## Working on it
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
uv sync --group dev --extra plot # the extra is what chart tests need
|
|
47
|
+
uv run pytest -q # everything, including the real flow5 runs
|
|
48
|
+
uv run pytest -q -m "not needs_flow5" # what CI runs
|
|
49
|
+
uv run ruff check src tests tools poc
|
|
50
|
+
python3 tools/check_docs.py
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The MCP server has two test layers, and they check different things:
|
|
54
|
+
`tests/test_mcp_server.py` runs it in process for the adapter's own contract, while
|
|
55
|
+
`tests/test_mcp_stdio.py` launches `flow5ctl mcp` as a subprocess and talks the
|
|
56
|
+
protocol — that is the only place a PNG surviving base64 transport, or a rejected
|
|
57
|
+
request coming back as an error result instead of killing the server, is actually
|
|
58
|
+
verified.
|
|
59
|
+
|
|
60
|
+
## Read before you change anything
|
|
61
|
+
|
|
62
|
+
In this order:
|
|
63
|
+
|
|
64
|
+
1. [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) — the layering and why it is that way
|
|
65
|
+
2. [docs/adr/](docs/adr/) — decisions already made, with their reasoning
|
|
66
|
+
3. [docs/FLOW5-INTERFACE.md](docs/FLOW5-INTERFACE.md) — verified facts about flow5
|
|
67
|
+
4. [docs/DOMAIN-MODEL.md](docs/DOMAIN-MODEL.md) — the vocabulary; use these words
|
|
68
|
+
|
|
69
|
+
## Rules
|
|
70
|
+
|
|
71
|
+
### Never invent facts about flow5
|
|
72
|
+
|
|
73
|
+
[docs/FLOW5-INTERFACE.md](docs/FLOW5-INTERFACE.md) marks every claim **[run]**
|
|
74
|
+
(verified by executing flow5) or **[src]** (read from a named upstream source file).
|
|
75
|
+
Anything not marked either way is not established.
|
|
76
|
+
|
|
77
|
+
If you need a fact that is not there:
|
|
78
|
+
|
|
79
|
+
- read it from the upstream source at [techwinder/flow5](https://github.com/techwinder/flow5), or
|
|
80
|
+
- verify it by running flow5, then
|
|
81
|
+
- add it to the reference with its marker and citation.
|
|
82
|
+
|
|
83
|
+
Do not guess an XML tag name. Unknown tags are silently ignored by flow5, so a
|
|
84
|
+
guess produces a subtly wrong analysis rather than an error.
|
|
85
|
+
|
|
86
|
+
### Never copy flow5 source code into this repository
|
|
87
|
+
|
|
88
|
+
flow5 is GPL-3.0; flow5ctl is Apache-2.0. Read the source to learn the interface and
|
|
89
|
+
describe it in your own words with a citation. See
|
|
90
|
+
[ADR-0006](docs/adr/0006-licensing-and-the-gpl-boundary.md).
|
|
91
|
+
|
|
92
|
+
### Physics correctness outranks everything
|
|
93
|
+
|
|
94
|
+
A crash is recoverable. A plausible wrong number that someone builds an aircraft
|
|
95
|
+
around is not. Specifically:
|
|
96
|
+
|
|
97
|
+
- Never let a code path produce stability results from a non-T7 polar. A T1 polar
|
|
98
|
+
will happily return an eigenvalue of `5.995e+51`.
|
|
99
|
+
- Never emit `PLANFORM` or `PROJECTED` reference dimensions
|
|
100
|
+
([ADR-0005](docs/adr/0005-compute-reference-dimensions-ourselves.md)).
|
|
101
|
+
- Never report an L/D from an inviscid run without labelling it. Measured: an
|
|
102
|
+
inviscid run omitted 93 % of the drag at α=0°.
|
|
103
|
+
- Never mix the two viscous methods inside one comparison; they disagree by 10–25 %.
|
|
104
|
+
- Never report Dutch-roll or short-period figures from flow5 7.57 — they are wrong.
|
|
105
|
+
- Never present a non-finite cell (`inf`, `nan`) as a result.
|
|
106
|
+
- Never treat `Static margin` from a flow5 file as a fraction; it is a percentage.
|
|
107
|
+
- Never attribute an operating-point file by its directory — flow5 duplicates those
|
|
108
|
+
files across every polar's directory with the wrong contents.
|
|
109
|
+
- Never widen an α sweep past the airfoil's stall to "get more data".
|
|
110
|
+
|
|
111
|
+
If you are unsure whether something is physically sound, say so in the PR rather than
|
|
112
|
+
guessing.
|
|
113
|
+
|
|
114
|
+
### Check both flow5's exit code and its stdout
|
|
115
|
+
|
|
116
|
+
`0` means nothing — flow5 exits 0 for a rejected script and for a run that failed
|
|
117
|
+
every operating point. Non-zero means it **crashed** (exit 139, SIGSEGV, observed
|
|
118
|
+
reproducibly). Success needs the stdout markers in
|
|
119
|
+
[docs/FLOW5-INTERFACE.md §6](docs/FLOW5-INTERFACE.md); crash detection needs the exit
|
|
120
|
+
code. Use both.
|
|
121
|
+
|
|
122
|
+
Scope marker matches carefully: `Made 0 valid analysis pairs (boat, polar) to run`
|
|
123
|
+
is printed on **every** run, so matching on `analysis pairs` alone reports failure on
|
|
124
|
+
every success.
|
|
125
|
+
|
|
126
|
+
### Never emit both script sections in one file
|
|
127
|
+
|
|
128
|
+
A script containing both `<foil_analysis>` and `<Plane_analysis>` segfaults flow5.
|
|
129
|
+
Two invocations, always — [ADR-0009](docs/adr/0009-two-pass-solver-invocation.md).
|
|
130
|
+
The generator must make the single-file form impossible to express.
|
|
131
|
+
|
|
132
|
+
### Never write a new output parser
|
|
133
|
+
|
|
134
|
+
flow5's output has at least seven traps that produce plausible wrong numbers rather
|
|
135
|
+
than errors ([docs/FLOW5-INTERFACE.md §5](docs/FLOW5-INTERFACE.md)). Extend
|
|
136
|
+
[poc/lib/parse.py](poc/lib/parse.py), keep its self-checks — especially validating
|
|
137
|
+
the row count against the file's own `Nbr. of data points` — and do not "simplify"
|
|
138
|
+
its oddities away. Every one of them is load-bearing
|
|
139
|
+
([ADR-0010](docs/adr/0010-treat-solver-output-as-hostile.md)).
|
|
140
|
+
|
|
141
|
+
### Respect the layering
|
|
142
|
+
|
|
143
|
+
`front-ends → use cases → domain → flow5 adapter`, one way only. Nothing in
|
|
144
|
+
`domain/` may import the adapter; the aerodynamic model must be testable with flow5
|
|
145
|
+
absent. Do not add flow5-specific knowledge outside the adapter, and do not add
|
|
146
|
+
domain logic to a front-end — see [ADR-0002](docs/adr/0002-one-core-two-frontends.md).
|
|
147
|
+
|
|
148
|
+
### Keep the two front-ends in step
|
|
149
|
+
|
|
150
|
+
Every capability appears in both the MCP server and the CLI, and
|
|
151
|
+
`flow5ctl <verb> --json` emits exactly the MCP tool payload. A change to one without
|
|
152
|
+
the other is incomplete.
|
|
153
|
+
|
|
154
|
+
### Changing a decision means writing an ADR
|
|
155
|
+
|
|
156
|
+
The ADRs record *why*. If you conclude one is wrong, add a new ADR that supersedes
|
|
157
|
+
it, with the new reasoning. Do not silently contradict an accepted decision in code.
|
|
158
|
+
|
|
159
|
+
## Conventions
|
|
160
|
+
|
|
161
|
+
- Use the vocabulary in [docs/DOMAIN-MODEL.md](docs/DOMAIN-MODEL.md). Say *section*,
|
|
162
|
+
*analysis*, *operating point*, *design*. Do not introduce synonyms.
|
|
163
|
+
- SI internally, always. Unit conversion happens at the edges only.
|
|
164
|
+
- Angles are degrees in `design.yaml` and in every user-facing value, radians inside
|
|
165
|
+
geometry code. Name variables accordingly (`twist_deg`, `alpha_rad`).
|
|
166
|
+
- `design.yaml` is the source of truth; XML is a build artifact. Never hand-author
|
|
167
|
+
XML in a fixture unless it is deliberately testing the parser.
|
|
168
|
+
- Prose in documentation is English. User-facing Japanese lives in `README.ja.md`
|
|
169
|
+
and `docs/ja/`.
|
|
170
|
+
|
|
171
|
+
## Testing
|
|
172
|
+
|
|
173
|
+
- Geometry has golden tests against hand-computed values. Changing geometry code
|
|
174
|
+
means changing them deliberately, never to make a failure go away.
|
|
175
|
+
- Solver-facing tests use recorded flow5 output where possible so the suite runs
|
|
176
|
+
without flow5 installed. Tests that need the real binary are marked and skipped
|
|
177
|
+
when it is absent.
|
|
178
|
+
- The spike design in [docs/log/2026-09-03-feasibility-spike.md](docs/log/2026-09-03-feasibility-spike.md)
|
|
179
|
+
is the canonical end-to-end fixture.
|
|
180
|
+
- [`poc/`](poc/) holds the verification harness that produced every measured claim in
|
|
181
|
+
the docs. If you change something the docs assert, re-run the relevant case and
|
|
182
|
+
update both. `poc/work/` and `poc/ref/` are gitignored — never commit solver output,
|
|
183
|
+
and never commit upstream flow5 source.
|
|
184
|
+
|
|
185
|
+
## When you finish
|
|
186
|
+
|
|
187
|
+
- Say what you verified by running versus what you reasoned about.
|
|
188
|
+
- If you changed anything in `docs/FLOW5-INTERFACE.md`, state which flow5 version you
|
|
189
|
+
verified against.
|
|
190
|
+
- If a task turned out to be blocked, finish everything else and say plainly what you
|
|
191
|
+
left and why.
|