Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -462,8 +462,7 @@ and `cargo xtask publish <study> [--run <UTC>] [--dry-run]` copies one run's bun
geometry), pages generated whole by `cargo xtask coverage` (coverage.md), `cargo xtask steps` (branch-tests.md) and
`generate_subroutine_validation` (subroutines/). Every published number is derived from a fixture directory
(`tests/fixtures/xfoil/<case>/` or `target/fixtures/<case>/` for `--big` cases) or from `tests/fixtures/subroutines/`.
Only Markdown, ironlab figures (`.fig`, embedded live in the site) and the branch-coverage study's two SVGs are
committed under `docs/`. No XFOIL dump, `.dat`, `.pol`, `DUMP`/`CPWR` output or other reference data is ever
Only Markdown and ironlab figures (`.fig`, embedded live in the site) are committed under `docs/`. No XFOIL dump, `.dat`, `.pol`, `DUMP`/`CPWR` output or other reference data is ever
committed there — `.gitignore` enforces it — and a table that cannot be regenerated from tracked inputs plus
`cargo xtask fixtures` is not evidence. The studies: `cargo run -p aerofoil-series` (the validation selection, one
figure per generator family, the naca456 comparison), `cargo run -p series-cases`, `cargo run -p known-issues`,
Expand All @@ -474,11 +473,10 @@ comparison, `studies/study-support/records.rs` — study evidence beside the tes
user guide's figures (`docs/user-guide/figures/`) are the output of the commands the guide prints, regenerated by
`scripts/guide-figures.sh`.
Figures are ironlab figures (`docs/development/conventions/figures.md`), built in Rust by each study from the numbers it
computes, through `src/output/figures.rs` (colours, ordered-family colours, parameters and labels the ironlab viewer
filters by, linked axes, validated saving); run folders also hold a PDF of each. The exception is the
branch-coverage study's branch map and case matrix, still drawn by matplotlib (`studies/branch-coverage/plot.py`
through `studies/figures/render.sh`, which needs **`uv` installed**) until ironlab has an occupancy-matrix figure
type. The analysis report (BL-distribution difference tables and
computes, through `src/output/figures.rs` (colours, ordered-family colours, parameters and tags the ironlab viewer
filters by, linked axes, saving that refuses a figure which does not validate or lay out); run folders also hold a
PDF of each. The branch-coverage study's branch map and case matrix are ironlab occupancy and cell figures too. The
analysis report (BL-distribution difference tables and
plots for the ±15° N=160 polar) is generated from the `--big` fixture case and is the remaining validation stage.

## Conventions
Expand Down
29 changes: 15 additions & 14 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ env_logger = "0.11"

# Optional figures (behind feature flag): ironlab figure files, pinned exactly because the
# figure model is still moving between minor versions
ironlab = { version = "=0.12.0", optional = true }
ironlab = { version = "=0.16.0", optional = true }

[dev-dependencies]
approx = "0.5"
Expand Down
44 changes: 32 additions & 12 deletions docs/development/conventions/figures.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ icon: lucide/image
Every yFoil figure is an [ironlab](https://ironlab.org) figure: a file holding the figure's own
data structure (`.fig`, Protocol Buffers; `.fig.json`, the same model as JSON), not a picture of it. It is opened
in the ironlab viewer, embedded live in this site, and exported to PDF for publication. The shared conventions
are implemented once, in `src/output/figures.rs` (feature `plotting`), and every figure is built through it.

The exception is the branch-coverage study's two occupancy figures (`docs/validation/branch-coverage/`): the branch map
and the case × subroutine matrix are not charts, and stay SVG drawn by matplotlib (`studies/branch-coverage/plot.py`
through `studies/figures/render.sh`) until ironlab has a figure type for them.
are implemented once, in `src/output/figures.rs` (feature `plotting`), and every figure is built through it. That
includes the branch-coverage study's occupancy figures (`docs/validation/branch-coverage/`): the branch map is an
ironlab occupancy matrix (`ax.occupancy` under `ax.ycategory_groups`) and the case × subroutine matrix is `ax.cells`
on categorical axes.

## Who computes what

Expand All @@ -30,9 +29,9 @@ axis extents. There is no presentation layer that transforms data.
colourmap for line colours. The family's default or base case is black and drawn on top.
- **Unordered families of up to four** use `figures::CATEGORY`.

## Parameters and labels
## Parameters and tags

The viewer sorts, filters and groups figures by their **parameters** (name → value) and **labels** (plain words).
The viewer sorts, filters and groups figures by their **parameters** (name → value) and **tags** (plain words).
Every figure carries both, so that all the figures every study produces can be loaded into one viewer and sliced.

**Parameters** say *which* figure this is and *what went in and came out*:
Expand All @@ -47,13 +46,26 @@ Every figure carries both, so that all the figures every study produces can be l
A name that exists as a key in yFoil's own JSON output is spelled exactly as that key. A new value at a place
is named `<quantity>_<place>_<side>`.

**Labels** are for tags a reader filters on, not for values:
**Tags** (`fig.tag`) are words a reader filters on, not values:

- the documentation section a figure appears in: `validation`;
- `xfoil-comparison` for a figure that plots the reference against yFoil;
- regime tags taken from the inputs: `inviscid`, `compressible` (M > 0), `forced-transition`, `fixed-cl`,
`low-re` (Re < 1e5).

## Axes

- **Geometry** (an aerofoil section, offsets drawn normal to its surface) is drawn at `Aspect::Equal`, which the
layout, the viewer and export all keep. Limits are never widened by hand to fake it.
- **Pressure coefficient** is plotted as \(C_p\) on a reversed axis (`yreverse(true)`), suction up, never as
\(-C_p\).
- **Rows of names** (cases, subroutines) are categorical axes (`ycategories`, first at the top), set in monospace
with `\texttt{…}` (escape `_`).
- **Reference lines** (a tolerance, interval ends) are `hline` / `vline`, which span the axes at any zoom.
- **Non-finite values** are marked by `Marker::VLine` ticks at the top edge, at data x and axes-fraction y with
clipping off, so they stay at the edge while the axes autoscale, pan and zoom.
- **Stacks sharing x** show x tick labels on the bottom row only (`xtick_labels_hidden()` above).

## Linked axes

Panels that share an independent variable (α, x/c) have that axis linked across the whole figure. Panels that plot
Expand All @@ -62,14 +74,22 @@ dependent axis linked too. Two axes with different quantities are never linked,

## Legends

Where a figure has one key for many panels, the legend goes in a **panel legend** (`fig.panel_legend(row, col)` +
`ax.legend_in(key)`) in a tile of its own. This replaces the hand-placed legend strips of the matplotlib figures.
Where a figure has one key for many panels, the legend goes in a **panel legend** (`fig.panel(row, col).legend()` +
`ax.legend_in(key)`) in a tile of its own, whose row is sized to it (`fit_row`), never by a calibrated weight.
A legend that belongs to one panel stays in that panel.

- **A reference and its variants**: the default section or base case is drawn last, so it is on top, and listed
first, on a row of its own (`full_row`), the members after it in order.
- **Meanings shared by many artists** (a marker shape for a kind of point, a colour for a side) are explained once by
`custom_item` entries, in a key of their own if the figure also names curves, never by dummy artists or one entry
per combination.
- Columns are `LegendEntryColumns::Auto` or a fixed count, never chosen by trial export.

## Saving

- `figures::save(fig, path)` validates the figure and refuses to write one with validation errors. The encoding
follows the extension.
- `figures::save(fig, path)` validates the figure and refuses to write one with validation errors, or whose layout
(`Figure::layout_warnings`) overflows a legend, clashes two legend entries or puts text beyond the page. The
encoding follows the extension.
- `figures::save_with_pdf(fig, stem)` also writes the PDF, for the run folders' LaTeX reports.
- Study run folders hold `.fig` and `.pdf`. The documentation holds `.fig` only.
- The user guide's figures (`docs/user-guide/figures/`) are the output of the very commands the guide prints, run by
Expand Down
6 changes: 2 additions & 4 deletions docs/development/testing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ measure the reference and compare whole runs, and add nothing the tests rely on.

Figures are [ironlab](https://ironlab.org) figures, built in Rust by each study from the numbers it
computes (`docs/development/conventions/figures.md`). They are embedded live in these pages: pan, zoom and toggle lines from the
legend. Each carries parameters (case, conditions, outputs) and labels, so every study's figures can be loaded
together into the ironlab viewer and sorted, filtered and grouped. A study's run folder also holds a PDF of each. The branch-coverage study's branch map and case matrix are the exception: they are SVGs drawn by
matplotlib (`studies/branch-coverage/plot.py` through `studies/figures/render.sh`, which needs
[`uv`](https://docs.astral.sh/uv/) installed).
legend. Each carries parameters (case, conditions, outputs) and tags, so every study's figures can be loaded
together into the ironlab viewer and sorted, filtered and grouped. A study's run folder also holds a PDF of each.

| Page | What it shows | Regenerate with |
|---|---|---|
Expand Down
16 changes: 8 additions & 8 deletions docs/user-guide/commands/plotting.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Open a figure in the ironlab viewer, a desktop application that pans, zooms, rea
the curves and hides series from the key:

``` sh
cargo install ironlab-viewer --version 0.12.0
cargo install ironlab-viewer --version 0.16.0
ironlab-viewer naca0012.fig
ironlab-viewer *.fig # several: the figure browser lists them
```
Expand Down Expand Up @@ -65,12 +65,11 @@ yfoil plot analysis a5.json # writes a5.fig

For the NACA 4412 at α = 5°, Re = 10⁶:

<ironlab-figure src="../figures/a5.fig" format="fig" name="a5" alt="NACA 4412 at alpha 5 degrees, Re 1e6: -Cp and Ue over x/c, upper and lower surfaces, above the aerofoil outline">NACA 4412 at alpha 5 degrees, Re 1e6: -Cp and Ue over x/c, upper and lower surfaces, above the aerofoil outline</ironlab-figure>
<ironlab-figure src="../figures/a5.fig" format="fig" name="a5" alt="NACA 4412 at alpha 5 degrees, Re 1e6: Cp and Ue over x/c, upper and lower surfaces, above the aerofoil outline">NACA 4412 at alpha 5 degrees, Re 1e6: Cp and Ue over x/c, upper and lower surfaces, above the aerofoil outline</ironlab-figure>

\(-C_p\) and \(U_e/U_\infty\) over \(x/c\) from a single-point analysis, upper surface
\(C_p\) and \(U_e/U_\infty\) over \(x/c\) from a single-point analysis, upper surface
blue and lower red, above a strip with the aerofoil outline; the three share a linked
\(x\) axis. \(C_p\) is plotted negated, with suction up, because ironlab has no reversed
axis. A point whose numbers were withheld (an invalid point analysed without
\(x\) axis. The \(C_p\) axis is reversed, so suction is up. A point whose numbers were withheld (an invalid point analysed without
`--allow-invalid`) has no distributions to draw, and is refused.

## The `plot foil` command { #foil }
Expand Down Expand Up @@ -110,6 +109,7 @@ polar JSON written with `--distributions` (one design point per α).
Each quantity gets its own hue and its own scale; each design point gets a tone
and dash pattern, so several angles overlay legibly. The markers are open shapes, one per
kind (○ stagnation, □ transition, ◇ forced transition, △ separation, ▽ reattachment),
yellow on the upper surface and green on the lower, and the key below the axes names each
kind and side drawn. The axes are scaled equally in \(x\) and \(y\) at the figure's own
size, so the section is undistorted; zooming in the viewer changes the two independently.
yellow on the upper surface and green on the lower. Below the axes are two keys: the curves,
and the markers, which explain each shape drawn once in grey and each side's colour once.
The axes are kept at equal aspect, in the viewer while panning and zooming too, so the
section and the offsets normal to it are undistorted.
Binary file modified docs/user-guide/figures/a5.fig
Binary file not shown.
Binary file modified docs/user-guide/figures/compare.fig
Binary file not shown.
Binary file modified docs/user-guide/figures/foil.fig
Binary file not shown.
Binary file modified docs/user-guide/figures/intro.fig
Binary file not shown.
Binary file modified docs/user-guide/figures/naca0012_a5.fig
Binary file not shown.
Binary file modified docs/user-guide/figures/naca0012_polar.fig
Binary file not shown.
Binary file modified docs/user-guide/figures/naca4412_foil.fig
Binary file not shown.
Binary file modified docs/user-guide/figures/naca4412_polar.fig
Binary file not shown.
2 changes: 1 addition & 1 deletion docs/user-guide/get-started/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Plotting is behind a cargo feature; build with it enabled if you want

``` sh
cargo install --path . --features plotting
cargo install ironlab-viewer --version 0.12.0
cargo install ironlab-viewer --version 0.16.0
```

## Check it works
Expand Down
Binary file modified docs/validation/aerofoil-series/figures/karman-trefftz.fig
Binary file not shown.
Binary file modified docs/validation/aerofoil-series/figures/naca-16.fig
Binary file not shown.
Binary file modified docs/validation/aerofoil-series/figures/naca-4-digit-modified.fig
Binary file not shown.
Binary file modified docs/validation/aerofoil-series/figures/naca-4-digit.fig
Binary file not shown.
Binary file modified docs/validation/aerofoil-series/figures/naca-5-digit.fig
Binary file not shown.
Binary file modified docs/validation/aerofoil-series/figures/naca-6.fig
Binary file not shown.
Binary file modified docs/validation/aerofoil-series/figures/naca-6a.fig
Binary file not shown.
Loading
Loading